I have the following code for a game that manages display patterns for items by randomly deciding on one, creating the items based on the pattern chosen and then animating them.
The problem is that cleaning code and managing changes can be cumbersome due to the sheer size, I have been studying design patterns lately and I was wondering which would be the best one to apply here, so far I have considered strategy and command as possible options
"The strategy pattern is used to create an interchangeable family of algorithms from which the required process is chosen at run-time." Seems like something I could use to apply the positioning of the items depending on the pattern selected.
And by looking at previous questions here when asked about reducing the amount of if/elses the command pattern came up quite a bit.
" The command pattern is used to express a request, including the call to be made and all of its required parameters, in a command object. The command may then be executed immediately or held for later use."
Still, I don't know if I may be finding relevance where there is not, so I thought I'd ask if such patterns can be applied to the following scenarios.
Below is the relevant code, I'm specially interested in learning about this because almost the same code is repeated for enemies.
/**
* Set items pattern.
*
*/
private function setItemsPattern():void
{
// Change until enough flight distance has been accumulated.
if (patternChange > 0)
{
patternChange -= playerSpeed * elapsed;
}
else
{
// As the player moves, change item patterns.
if ( Math.random() < 0.7 )
{
// If < normal item chance (0.7), get a random pattern.
pattern = Math.ceil(Math.random() * 4);
}
else
{
// If random number is > normal item chance (0.3), create special item.
pattern = Math.ceil(Math.random() * 2) + 9;
}
if (pattern == GameConstants.ITEM_PATTERN_VERTICAL)
{
// Vertical
patternStep = 15;
patternChange = Math.random() * 500 + 500;
}
else if (pattern == GameConstants.ITEM_PATTERN_HORIZONTAL)
{
// Horizontal
patternOnce = true;
patternStep = 40;
patternChange = patternGap * Math.random() * 3 + 5;
}
else if (pattern == GameConstants.ITEM_PATTERN_ZIGZAG)
{
// ZigZag
patternStep = Math.round(Math.random() * 2 + 2) * 10;
if ( Math.random() > 0.5 )
{
patternDirection *= -1;
}
patternChange = Math.random() * 800 + 800;
}
else if (pattern == GameConstants.ITEM_PATTERN_RANDOM)
{
// Random
patternStep = Math.round(Math.random() * 3 + 2) * 50;
patternChange = Math.random() * 400 + 400;
}
else
{
patternChange = 0;
}
}
}
/**
* Creates items - called by createPattern()
*
*/
private function createItems():void
{
var itemToTrack:Item;
switch (pattern)
{
case GameConstants.ITEM_PATTERN_HORIZONTAL:
// Horizontal.
if (Math.random() > 0.9)
{
// Asignes items not too close to border.
patternPosY = Math.floor(Math.random() * (gameArea.bottom - gameArea.top + 1)) + gameArea.top;
}
itemToTrack = itemFactory.getItem(GameConstants.ITEM_TYPE_1);
this.addChild(itemToTrack);
// Sets pos
itemToTrack.x = stage.stageWidth + itemToTrack.width ;
itemToTrack.y = patternPosY;
// Marks item for animation
itemsToAnimate.push(itemToTrack);
break;
case GameConstants.ITEM_PATTERN_VERTICAL:
// Vertical
if (patternOnce == true)
{
patternOnce = false;
patternPosY = Math.floor(Math.random() * (gameArea.bottom - gameArea.top + 1)) + gameArea.top;
patternLength = (Math.random() * 0.4 + 0.4) * stage.stageHeight;
}
patternPosYstart = patternPosY;
while (patternPosYstart + patternStep < patternPosY + patternLength && patternPosYstart + patternStep < stage.stageHeight * 0.8)
{
itemToTrack = itemFactory.getItem(GameConstants.ITEM_TYPE_1);
this.addChild(itemToTrack);
itemToTrack.x = stage.stageWidth + itemToTrack.width;
itemToTrack.y = patternPosYstart;
itemsToAnimate.push(itemToTrack)
patternPosYstart += patternStep;
}
break;
case GameConstants.ITEM_PATTERN_ZIGZAG:
// ZigZag
if (patternDirection == 1 && patternPosY > gameArea.bottom - 50)
{
patternDirection = -1;
}
else if ( patternDirection == -1 && patternPosY < gameArea.top )
{
patternDirection = 1;
}
if (patternPosY >= gameArea.top && patternPosY <= gameArea.bottom)
{
itemToTrack = itemFactory.getItem(GameConstants.ITEM_TYPE_1);
this.addChild(itemToTrack);
itemToTrack.x = stage.stageWidth + itemToTrack.width;
itemToTrack.y = patternPosY;
itemsToAnimate.push(itemToTrack)
patternPosY += patternStep * patternDirection;
}
else
{
patternPosY = gameArea.top;
}
break;
case GameConstants.ITEM_PATTERN_RANDOM:
// Random, creates N amount of items on screen.
if (Math.random() > 0.3)
{
patternPosY = Math.floor(Math.random() * (gameArea.bottom - gameArea.top + 1)) + gameArea.top;
while (patternPosY + patternStep < gameArea.bottom)
{
itemToTrack = itemFactory.getItem(GameConstants.ITEM_TYPE_1);
this.addChild(itemToTrack);
itemToTrack.x = stage.stageWidth + itemToTrack.width;
itemToTrack.y = patternPosY;
itemsToAnimate.push(itemToTrack)
patternPosY += Math.round(Math.random() * 100 + 100);
}
}
break;
case GameConstants.ITEM_PATTERN_SPEED:
// special item
patternPosY = Math.floor(Math.random() * (gameArea.bottom - gameArea.top + 1)) + gameArea.top;
itemToTrack = itemFactory.getItem(GameConstants.ITEM_TYPE_MANA);
this.addChild(itemToTrack);
itemToTrack.x = stage.stageWidth + itemToTrack.width;
itemToTrack.y = patternPosY;
itemsToAnimate.push(itemToTrack);
break;
case GameConstants.ITEM_PATTERN_STR:
// special item
patternPosY = Math.floor(Math.random() * (gameArea.bottom - gameArea.top + 1)) + gameArea.top;
itemToTrack = itemFactory.getItem(GameConstants.ITEM_TYPE_REFERENCIA);
this.addChild(itemToTrack);
itemToTrack.x = stage.stageWidth + itemToTrack.width;
itemToTrack.y = patternPosY;
itemsToAnimate.push(itemToTrack);
break;
}
}
/**
* Animates the vector itemsToAnimate.
*
*/
private function animateItems():void
{
var itemToTrack:Item;
for(var i:uint = 0;i<itemsToAnimate.length;i++)
{
itemToTrack = itemsToAnimate[i];
if (itemToTrack != null)
{
if (referencia > 0 && itemToTrack.itemType <= GameConstants.ITEM_TYPE_REFERENCIA)
{
itemToTrack.x -= (itemToTrack.x - brujaX) * 0.2;
itemToTrack.y -= (itemToTrack.y - brujaY) * 0.2;
}
else
{
itemToTrack.x -= playerSpeed * elapsed;
}
if (itemToTrack.x < -80 || gameState == GameConstants.GAME_STATE_OVER)
{
disposeItemTemporarily(i, itemToTrack);
}
else
{
brujaItem_xDist = itemToTrack.x - brujaX;
brujaItem_yDist = itemToTrack.y - brujaY;
brujaItem_sqDist = brujaItem_xDist * brujaItem_xDist + brujaItem_yDist * brujaItem_yDist;
if (brujaItem_sqDist < 5000)
{
if (itemToTrack.itemType == GameConstants.ITEM_TYPE_1)
{
scoreItems += itemToTrack.itemType;
hud.itemScore = scoreItems;
if (!Sounds.muted) Sounds.sndPag.play();
}
else if (itemToTrack.itemType == GameConstants.ITEM_TYPE_MANA)
{
scoreItems += 1;
mana = 5;
if (isHardwareRendering) particleMana.start(mana);
if (!Sounds.muted) Sounds.sndMana.play();
if (!Sounds.muted) Sounds.sndRisa.play();
}
else if (itemToTrack.itemType == GameConstants.ITEM_TYPE_REFERENCIA)
{
scoreItems += 1;
referencia = 20;
partRef = 0.5;
if (isHardwareRendering) particleRef.start(partRef);
playRandomRef();
}
if(referencia > 0){referencia--;}
disposeItemTemporarily(i, itemToTrack);
}
}
}
}
}
Your function setItemsPattern and createItems both contain a switch-case statement, so you could create a base class contains two functions hanlde the switch-case work.
For example, you get the base class like this
Class BaseBehavior
{
//if the variable shouldn't be accessed by other class, change public to protected
public var patternOnce:Boolean;
public var patternStep:int;
public var patternChange:int;
public var patternDirection:int;
public var itemToTrack:Object;
public var gameArea:Object;
//used in setItemsPattern function
public function initPatternData():void {};
//used in createItems function
public function createItems():void {};
public function dispose():void {};
}
And here is the vertical class
Class VerticalBehavior extends BaseBehavior
{
override public function initPatternData():void
{
patternStep = 15;
patternChange = Math.random() * 500 + 500;
}
override public function createItems():void
{
if (Math.random() > 0.9)
{
// Asignes items not too close to border.
patternPosY = Math.floor(Math.random() * (gameArea.bottom - gameArea.top + 1)) + gameArea.top;
}
itemToTrack = itemFactory.getItem(GameConstants.ITEM_TYPE_1);
// Sets pos
itemToTrack.x = stage.stageWidth + itemToTrack.width ;
itemToTrack.y = patternPosY;
}
}
Other sub classes are most same.
Now you need a factory class to create the sub class
Class BehaviorFactory
{
public static function create(type:int):BaseBehavior
{
switch(type)
{
case 1://vertical
return new VerticalBehavior();
case 2:
return ...
...
}
}
}
After these work, you can use them in your old logic code
private var behavior:BaseBehavior;
private function setItemsPattern():void
{
if (behavior && behavior.patternChange > 0)
{
behavior.patternChange -= playerSpeed * elapsed;
}
else
{
// As the player moves, change item patterns.
if ( Math.random() < 0.7 )
{
// If < normal item chance (0.7), get a random pattern.
pattern = Math.ceil(Math.random() * 4);
}
else
{
// If random number is > normal item chance (0.3), create special item.
pattern = Math.ceil(Math.random() * 2) + 9;
}
//here to create the sub class
//dispose old behavior
if (behavior)
{
behavior.dispose();
}
behavior = BehaviorFactory.create(pattern);
}
private function createItems():voidh
{
//you may check behavior is null here
var itemToTrack:Item = behavior.createItems();
this.addChild(itemToTrack);
// Marks item for animation
itemsToAnimate.push(itemToTrack);
}
At last, if you want add a new type, you just need to create a sub behavior class and add it to the factory class.But be careful if the variables in the behavior increase too many, you may need to use composition class.
Related
I'm currently using this particle system and it work fine. Particle's should be destroy after cross boundry's or alpha's smaller then 0. But however sometimes when i use this code like pereatly 4 it fails and particles cant destory.
function init():void
{
particleArray = [];
addEventListener(Event.ENTER_FRAME, onEnterFrameLoop);
createParticle(s3.whell.x,s3.whell.y);
}
function onEnterFrameLoop(event:Event):void
{
updateParticle();
}
/**
* createParticle(target X position, target Y position)
*/
function createParticle(targetX:Number, targetY:Number):void
{
//run for loop based on particleTotal
for (var i:Number = 0; i < particleTotal; i++)
{
var particle_mc:MovieClip = new Particle();
//set position & rotation, alpha
particle_mc.x = targetX
particle_mc.y = targetY
particle_mc.rotation = Math.random() * 360;
particle_mc.alpha = Math.random() * 1.1;
//set particle boundry
particle_mc.boundyLeft = targetX - particleRange;
particle_mc.boundyTop = targetY - particleRange;
particle_mc.boundyRight = targetX + particleRange;
particle_mc.boundyBottom = targetY + particleRange;
//set speed/direction of fragment
particle_mc.speedX = Math.random() * particleMaxSpeed - Math.random() * particleMaxSpeed;
particle_mc.speedY = Math.random() * particleMaxSpeed - Math.random() * particleMaxSpeed;
particle_mc.speedX *= particleMaxSpeed
particle_mc.speedY *= particleMaxSpeed
//set fade out speed
particle_mc.fadeSpeed = Math.random()*particleFadeSpeed;
//just a visual particle counter
particleCurrentAmount++;
// add to array
particleArray.push(particle_mc);
// add to display list
addChild(particle_mc);
}
}
function updateParticle():void
{
for (var i = 0; i < particleArray.length; i++)
{
var tempParticle:MovieClip = particleArray[i];
//update alpha, x, y
tempParticle.alpha -= tempParticle.fadeSpeed;
tempParticle.x += tempParticle.speedX;
tempParticle.y += tempParticle.speedY;
// if fragment is invisible remove it
if (tempParticle.alpha <= 0)
{
destroyParticle(tempParticle);
}
// if fragment is out of bounds, increase fade out speed
else if (tempParticle.x < tempParticle.boundyLeft ||
tempParticle.x > tempParticle.boundyRight ||
tempParticle.y < tempParticle.boundyTop ||
tempParticle.y > tempParticle.boundyBottom)
{
tempParticle.fadeSpeed += 8;
destroyParticle(tempParticle);
}
}
}
function destroyParticle(particle:MovieClip):void
{
for (var i = 0; i < particleArray.length; i++)
{
var tempParticle:MovieClip = particleArray[i];
if (tempParticle == particle)
{
particleCurrentAmount--;
particleArray.splice(i,1);
removeChild(tempParticle);
}
}
}
I'm trying to make a character able to move within the "world" movieclip, but not go through the walls.
So here is the character class, pretty basic. The moving variable dictates whether the character will move or not.
The issue is, when I move the character left or right against the "world" the character will move upwards or downwards as well.
// Code to move character
package
{
import flash.display.*;
import flash.events.*;
import flash.ui.*;
public class Character extends MovieClip
{
public var Moving:Boolean = true;
public var maxSpeed = 10;
public var dx = 0;
public var dy = 0;
public var rot = 0;
public var rof = 30;
private var keysDown:Array = new Array ;
// Declare variables:
//var bullet:Bullet;
var reload:int = 10;
private var bullets:Array;
public function Character()
{
//bullets = bulletList;
// Initialize variables:
addEventListener(Event.ADDED_TO_STAGE,init);
}
function init(e:Event):void
{
stage.addEventListener(KeyboardEvent.KEY_DOWN,keyPressed);
stage.addEventListener(KeyboardEvent.KEY_UP,keyReleased);
this.addEventListener(Event.ENTER_FRAME,processInput);
this.addEventListener(Event.ENTER_FRAME,moveMe);
removeEventListener(Event.ADDED_TO_STAGE,init);
}
public function removeT():void
{
stage.removeEventListener(KeyboardEvent.KEY_DOWN,keyPressed);
stage.removeEventListener(KeyboardEvent.KEY_UP,keyReleased);
this.removeEventListener(Event.ENTER_FRAME,processInput);
this.removeEventListener(Event.ENTER_FRAME,moveMe);
}
private function keyPressed(e:KeyboardEvent):void
{
keysDown[e.keyCode] = true;
// Handle keys pressed:
}
private function keyReleased(e:KeyboardEvent):void
{
keysDown[e.keyCode] = false;
}
private function processInput(e:Event)
{
// Handle keys held:
dx = 0;
dy = 0;
if (keysDown[Keyboard.LEFT])
{
dx = - maxSpeed;
dy = 0;
rot = -180;
this.gotoAndStop(4);
}
if (keysDown[Keyboard.RIGHT])
{
dx = maxSpeed;
dy = 0;
rot = 0;
this.gotoAndStop(1);
}
if (keysDown[Keyboard.UP])
{
dx = 0;
dy = - maxSpeed;
rot = -90;
this.gotoAndStop(3);
}
if (keysDown[Keyboard.DOWN])
{
this.gotoAndStop(2);
dx = 0;
dy = maxSpeed;
rot = 90;
}
}
private function moveMe(e:Event)
{
if(Moving){
this.x += dx;
this.y += dy;
this.rotation = rot;
}
//stop if it tries to go off the screen
if (this.x < 0)
{
this.x = 0;
}
if (this.x > stage.stageWidth)
{
this.x = stage.stageWidth;
}
if (this.y < 0)
{
this.y = 0;
}
if (this.y > stage.stageHeight)
{
this.y = stage.stageHeight;
}
}
}
}
Here is the code for the collision checking of the object and the world. I use the functions checkFrom and checkFromX to check the points around the movieclip. So when one of those functions are checked, it iterates through all the points on that side of the movieclip character
picture of the movieclip character if anyone is confused. http://prntscr.com/5fqu69
function checkFrom(x1:int, x2:int, y:int )
{
if (x2<x1) {
trace("x2<x1");
return false;
}
for (var i=x1; i<=x2; i++)
{
if (world.hitTestPoint(i,y,true))
{
return true;
}
}
return false;
}
//made this to check for right and left
function checkFromX(y1:int, y2:int, x:int )
{//y1 at top
if (y2<y1) {
trace("y2<y1");
return false;
}
for (var a=y1; a<=y2; a++)
{
if (world.hitTestPoint(x,a,true))
{
return true;
}
}
return false;
}
function moveDude(e:Event):void
{
var obj:Object = e.target;
if (obj.hitTestObject(world.lv1))
{
cleanup();
gotoAndStop("donkeyKong");
}
else
{
obj.Moving = true;
while (checkFrom(obj.x - obj.width / 2 + obj.width / 8,obj.x + obj.width / 2 - obj.width / 8,obj.y + obj.height / 2 - obj.height / 9))
{//bot
obj.y--;
obj.Moving = false;
}
while (checkFrom(obj.x - obj.width / 2 + obj.width / 8,obj.x + obj.width / 2 - obj.width / 8,obj.y - obj.height / 2 + obj.height / 9))
{
obj.y++;
obj.Moving = false;
}
while (checkFromX(obj.y - obj.height / 2 + obj.height / 7,obj.y - obj.height / 7 + obj.height / 2,obj.x - obj.width / 2 + obj.width / 9))
{//left
obj.x++;
obj.Moving = false;
}
while (checkFromX(obj.y - obj.height / 2 + obj.height / 7,obj.y + obj.height / 2 - obj.height / 7,obj.x + obj.width / 2 - obj.width / 9))
{
obj.x--;
obj.Moving = false;
}
I'm trying to add the maths to control a panorama with the gyroscope and struggling. This is a mobile app built in AS3.
I've got the data coming through from the gyroscope (x and y), and I've got the current angle (pan and tilt). What I want to do is update the cameraController with the new angle based on the data from the gyro.
I've been attempting to convert the Javascript I found on https://github.com/fieldOfView/krpano_fovplugins/blob/master/gyro/source/gyro.source.js into Actionscript 3, and it kind of works - but not really.
EDIT
Thanks I tried those changes and I added camera roll back in because the euler maths needed it, it runs but there is something wrong with the Maths.
The panorama only seems to drift up and left, after a while of moving the phone the other way it drifts down, and then moves right.
Can you see anything important I'm missing from the Javascript?
import com.adobe.nativeExtensions.GyroscopeEvent;
import flash.events.Event;
import flash.geom.Orientation3D;
public class GyroscopeMaths
{
public function GyroscopeMaths()
{
super();
}
private var isTopAccessible:Boolean = false;
private var isDeviceAvailable:Boolean;
private var isEnabled:Boolean = false;
private var vElasticity:Number = 0;
private var isVRelative:Boolean = false;
private var isCamRoll:Boolean = false;
private var friction:Number = 0.5;
private var isTouching:Boolean = false;
private var validSample:Boolean = false;
private var firstSample:* = null;
private var hOffset:Number = 0;
private var vOffset:Number = 0;
private var hLookAt:Number = 0;
private var vLookAt:Number = 0;
private var camRoll:Number = 0;
private var vLookAtNow:Number = 0;
private var hLookAtNow:Number = 0;
private var hSpeed:Number = 0;
private var vSpeed:Number = 0;
private var vElasticSpeed:Number = 0;
private var camRollNow:Number;
private var pitch:Number;
private var yaw:Number;
private var altYaw:Number;
private var factor:Number;
private var degRad:Number = Math.PI / 180;
public function handleDeviceOrientation(x:Number, y:Number, z:Number):void {
// Process event.alpha, event.beta and event.gamma
var orientation:* = rotateEuler({
"yaw":y * degRad,
"pitch":x * degRad,
"roll": z * degRad
});
yaw = wrapAngle(orientation.yaw / degRad);
pitch = orientation.pitch / degRad;
altYaw = yaw, factor;
hLookAtNow = Pano.instance.pan;
vLookAtNow = Pano.instance.tilt;
hSpeed = hLookAtNow - hLookAt,
vSpeed = vLookAtNow - vLookAt;
// Ignore all sample until we get a sample that is different from the first sample
if (!validSample) {
if (firstSample == null) {
firstSample = orientation;
} else {
if (orientation.yaw != firstSample.yaw || orientation.pitch != firstSample.pitch || orientation.roll != firstSample.roll) {
firstSample = null;
validSample = true;
if (isVRelative) {
vOffset = -pitch;
}
}
}
return;
}
// Fix gimbal lock
if (Math.abs(pitch) > 70) {
altYaw = y;
var altYaw:Number = wrapAngle(altYaw);
if (Math.abs(altYaw - yaw) > 180) {
altYaw += (altYaw < yaw) ? 360 :-360;
}
var factor:Number = Math.min(1, (Math.abs(pitch) - 70) / 10);
yaw = yaw * (1 - factor) + altYaw * factor;
//camRoll *= (1 - factor);
}
// Track view change since last orientation event
// ie:user has manually panned, or krpano has altered lookat
hOffset += hSpeed;
vOffset += vSpeed;
// Clamp vOffset
if (Math.abs(pitch + vOffset) > 90) {
vOffset = (pitch + vOffset > 0) ? (90 - pitch) :(-90 - pitch)
}
hLookAt = wrapAngle(-yaw - 180 + hOffset);
vLookAt = Math.max(Math.min((pitch + vOffset), 90), -90);
// Dampen lookat
if (Math.abs(hLookAt - hLookAtNow) > 180) {
hLookAtNow += (hLookAt > hLookAtNow) ? 360 :-360;
}
hLookAt = (1 - friction) * hLookAt + friction * hLookAtNow;
vLookAt = (1 - friction) * vLookAt + friction * vLookAtNow;
if (Math.abs(camRoll - camRollNow) > 180) {
camRollNow += (camRoll > camRollNow) ? 360 :-360;
}
camRoll = (1 - friction) * camRoll + friction * camRollNow;
var wAh:Number = wrapAngle(hLookAt);
Pano.instance.panoGyroChange(wAh, vLookAt);
//krpano.view.camroll = wrapAngle(camRoll);
if (vOffset != 0 && vElasticity > 0) {
if (vSpeed == 0) {
if (vElasticity == 1) {
vOffset = 0;
vElasticSpeed = 0;
} else {
// vElasticSpeed = 1 - ((1 - vElasticSpeed) * krpano.control.touchfriction);
vOffset *= 1 - (Math.pow(vElasticity, 2) * vElasticSpeed); // use Math.pow to be able to use saner values
if (Math.abs(vOffset) < 0.1) {
vOffset = 0;
vElasticSpeed = 0;
}
}
} else {
vElasticSpeed = 0;
}
}
}
private function rotateEuler(euler:Object):Object {
// This function is based on http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm
// and http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToEuler/index.htm
trace(euler);
var heading:Number;
var bank:Number;
var attitude:Number;
var ch:Number = Math.cos(euler.yaw);
var sh:Number = Math.sin(euler.yaw);
var ca:Number = Math.cos(euler.pitch);
var sa:Number = Math.sin(euler.pitch);
var cb:Number = Math.cos(euler.roll);
var sb:Number = Math.sin(euler.roll);
var matrix:Array = [
sh * sb - ch * sa * cb, -ch * ca, ch * sa * sb + sh * cb,
ca * cb, -sa, -ca * sb,
sh * sa * cb + ch * sb, sh * ca, -sh * sa * sb + ch * cb
]; // Note:Includes 90 degree rotation around z axis
/* [m00 m01 m02] 0 1 2
* [m10 m11 m12] 3 4 5
* [m20 m21 m22] 6 7 8 */
if (matrix[3] > 0.9999) {
// Deal with singularity at north pole
heading = Math.atan2(matrix[2], matrix[8]);
attitude = Math.PI / 2;
bank = 0;
} else if (matrix[3] < -0.9999) {
// Deal with singularity at south pole
heading = Math.atan2(matrix[2], matrix[8]);
attitude = -Math.PI / 2;
bank = 0;
} else {
heading = Math.atan2(-matrix[6], matrix[0]);
bank = Math.atan2(-matrix[5], matrix[4]);
attitude = Math.asin(matrix[3]);
}
return {
yaw:heading,
pitch:attitude,
roll:bank
};
}
private function wrapAngle(value:Number):Number {
value = value % 360;
return (value <= 180) ? value :value - 360;
} // wrap a value between -180 and 180
//function stringToBoolean(value:Number):String
//{ return (String("yesontrue1").indexOf( String(value:Number) ) >= 0) };
}
Without the rest of your program, I won't be able to compile this, but I've corrected what syntactical errors I could find. That said, if you can program in either JS or As3, you should be able to follow the logic and write your own class (they're both EMCAScript). Continue pursuing that until you've arrived at a more solid problem than "it doesn't work" (which is generally a question no one wants to answer).
private var isTopAccessible:Boolean = false;
private var isDeviceAvailable:Boolean;
private var isEnabled:Boolean = false;
private var vElasticity:Number = 0;
private var isVRelative:Boolean = false;
private var isCamRoll:Boolean = false;
private var friction:Number = 0.5;
private var isTouching:Boolean = false;
private var validSample:Boolean = false;
private var firstSample:* = null;
private var hOffset:Number = 0;
private var vOffset:Number = 0;
private var hLookAt:Number = 0;
private var vLookAt:Number = 0;
private var camRoll:Number = 0;
private var vLookAtNow:Number = 0;
private var hLookAtNow:Number = 0;
private var hSpeed:Number = 0;
private var vSpeed:Number = 0;
private var vElasticSpeed:Number = 0;
private var camRollNow:Number;
private var pitch:Number;
private var yaw:Number;
private var altYaw:Number;
private var factor:Number;
private var degRad:Number = Math.PI / 180;
public function handleDeviceOrientation(x:Number, y:Number):void {
// Process event.alpha, event.beta and event.gamma
var orientation:* = rotateEuler({
"yaw":y * degRad,
"pitch":x * degRad,
"roll":0
});
yaw = wrapAngle(orientation.yaw / degRad);
pitch = orientation.pitch / degRad;
altYaw = yaw, factor;
hLookAtNow = Pano.instance.pan;
vLookAtNow = Pano.instance.tilt;
hSpeed = hLookAtNow - hLookAt,
vSpeed = vLookAtNow - vLookAt;
// Ignore all sample until we get a sample that is different from the first sample
if (!validSample) {
if (firstSample == null) {
firstSample = orientation;
} else {
if (orientation.yaw != firstSample.yaw || orientation.pitch != firstSample.pitch || orientation.roll != firstSample.roll) {
firstSample = null;
validSample = true;
if (isVRelative) {
vOffset = -pitch;
}
}
}
return;
}
// Fix gimbal lock
if (Math.abs(pitch) > 70) {
altYaw = y;
/*switch(deviceOrientation) {
case 0:
if ( pitch>0 )
altYaw += 180;
break;
case 90:
altYaw += 90;
break;
case -90:
altYaw += -90;
break;
case 180:
if ( pitch<0 )
altYaw += 180;
break;
}*/
var altYaw:Number = wrapAngle(altYaw);
if (Math.abs(altYaw - yaw) > 180) {
altYaw += (altYaw < yaw) ? 360 :-360;
}
var factor:Number = Math.min(1, (Math.abs(pitch) - 70) / 10);
yaw = yaw * (1 - factor) + altYaw * factor;
//camRoll *= (1 - factor);
}
// Track view change since last orientation event
// ie:user has manually panned, or krpano has altered lookat
hOffset += hSpeed;
vOffset += vSpeed;
// Clamp vOffset
if (Math.abs(pitch + vOffset) > 90) {
vOffset = (pitch + vOffset > 0) ? (90 - pitch) :(-90 - pitch)
}
hLookAt = wrapAngle(-yaw - 180 + hOffset);
vLookAt = Math.max(Math.min((pitch + vOffset), 90), -90);
// Dampen lookat
if (Math.abs(hLookAt - hLookAtNow) > 180) {
hLookAtNow += (hLookAt > hLookAtNow) ? 360 :-360;
}
hLookAt = (1 - friction) * hLookAt + friction * hLookAtNow;
vLookAt = (1 - friction) * vLookAt + friction * vLookAtNow;
if (Math.abs(camRoll - camRollNow) > 180) {
camRollNow += (camRoll > camRollNow) ? 360 :-360;
}
camRoll = (1 - friction) * camRoll + friction * camRollNow;
var wAh:Number = wrapAngle(hLookAt);
Pano.instance.panoGyroChange(wAh, vLookAt);
//krpano.view.camroll = wrapAngle(camRoll);
if (vOffset != 0 && vElasticity > 0) {
if (vSpeed == 0) {
if (vElasticity == 1) {
vOffset = 0;
vElasticSpeed = 0;
} else {
// vElasticSpeed = 1 - ((1 - vElasticSpeed) * krpano.control.touchfriction);
vOffset *= 1 - (Math.pow(vElasticity, 2) * vElasticSpeed); // use Math.pow to be able to use saner values
if (Math.abs(vOffset) < 0.1) {
vOffset = 0;
vElasticSpeed = 0;
}
}
} else {
vElasticSpeed = 0;
}
}
}
private function rotateEuler(euler:Object):Object {
// This function is based on http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm
// and http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToEuler/index.htm
trace(euler);
var heading:Number;
var bank:Number;
var attitude:Number;
var ch:Number = Math.cos(euler.yaw);
var sh:Number = Math.sin(euler.yaw);
var ca:Number = Math.cos(euler.pitch);
var sa:Number = Math.sin(euler.pitch);
var cb:Number = Math.cos(euler.roll);
var sb:Number = Math.sin(euler.roll);
var matrix:Array = [
sh * sb - ch * sa * cb, -ch * ca, ch * sa * sb + sh * cb,
ca * cb, -sa, -ca * sb,
sh * sa * cb + ch * sb, sh * ca, -sh * sa * sb + ch * cb
]; // Note:Includes 90 degree rotation around z axis
/* [m00 m01 m02] 0 1 2
* [m10 m11 m12] 3 4 5
* [m20 m21 m22] 6 7 8 */
if (matrix[3] > 0.9999) {
// Deal with singularity at north pole
heading = Math.atan2(matrix[2], matrix[8]);
attitude = Math.PI / 2;
bank = 0;
} else if (matrix[3] < -0.9999) {
// Deal with singularity at south pole
heading = Math.atan2(matrix[2], matrix[8]);
attitude = -Math.PI / 2;
bank = 0;
} else {
heading = Math.atan2(-matrix[6], matrix[0]);
bank = Math.atan2(-matrix[5], matrix[4]);
attitude = Math.asin(matrix[3]);
}
return {
yaw:heading,
pitch:attitude,
roll:bank
};
}
private function wrapAngle(value:Number):Number {
value = value % 360;
return (value <= 180) ? value :value - 360;
} // wrap a value between -180 and 180
//function stringToBoolean(value:Number):String
//{ return (String("yesontrue1").indexOf( String(value:Number) ) >= 0) };
so I'm working on this simple game, in which you dodge falling boulders. Every time a boulder doesn't hit you (reaches a y coordinate below) you get 30 pts. And when a boulder does hit you you lose a life. Unfortunately, it seems to glitch out seemingly unpredictable.
LINK TO TEST OUT THE GAME: http://fozgamez.com/a
(only the 1p mouse works)
I do not know how to fix the problem, since I cannot figure out how/ when the problem happens.
My code for the 2nd scene (the one with the rules):
import flash.events.MouseEvent;
stop();
var livesSelected:Number;
m1Select.addEventListener(MouseEvent.MOUSE_UP, m1Selected)
function m1Selected (e:MouseEvent)
{
livesSelected = 01;
gotoAndStop(3);
}
m3Select.addEventListener(MouseEvent.MOUSE_UP, m3Selected)
function m3Selected (e:MouseEvent)
{
livesSelected = 03;
gotoAndStop(3);
}
m5Select.addEventListener(MouseEvent.MOUSE_UP, m5Selected)
function m5Selected (e:MouseEvent)
{
livesSelected = 05;
gotoAndStop(3);
}
m9Select.addEventListener(MouseEvent.MOUSE_UP, m9Selected)
function m9Selected (e:MouseEvent)
{
livesSelected = 09;
gotoAndStop(3);
}
CODE FOR THE 3RD SCENE (where you actually play the game):
import flash.events.Event;
import flash.events.TouchEvent;
import flash.events.MouseEvent;
import flash.utils.Timer;
var points:int = 0;
var lifeTimer:Timer = new Timer(1000, 1)
var lives:Number = livesSelected;
livesText.text = lives.toString();
pointsText.text = points.toString();
lifeTimer.stop()
stage.addEventListener(Event.ENTER_FRAME, enterFrame);
function enterFrame(e:Event){
mChar.x = mouseX;
mChar.y = mouseY;
b1.y += 20;
b2.y += 40;
b3.y += 15;
b4.y += 25;
b5.y += 20;
bFast.y += 50;
if(mChar.y <= 20)
{
mChar.y = 20;
}
if(mChar.x >= 700)
{
mChar.x = 700;
}
if(mChar.y <= 0)
{
mChar.y = 700;
}
if(b1.y >= 730) {
b1.y = (Math.random() + .001) * -200;
b1.x = (Math.random() + .001) * 700;
points += 15;
}
if(b2.y >= 730) {
b2.y = (Math.random() + .001) * -200;
b2.x = (Math.random() + .001) * 700;
points += 30;
}
if(b3.y >= 730) {
b3.y = (Math.random() + .001) * -200;
b3.x = (Math.random() + .001) * 700;
points += 15;
}
if(b4.y >= 730) {
b4.y = (Math.random() + .001) * -200;
b4.x = (Math.random() + .001) * 700;
points += 15;
}
if(b5.y >= 730) {
b5.y = (Math.random() + .001) * -200;
b5.x = (Math.random() + .001) * 700;
points += 15;
}
if(bFast.y >= 730) {
bFast.y = (Math.random() + .001) * -200;
bFast.x = (Math.random() + .001) * 700;
points += 15;
}
if(!lifeTimer.running) {
livesText.text = lives.toString();
mInvin.x = -66;
mInvin.y = 560;
pointsText.text = points.toString();
if(mChar.hitTestObject(b1)) {
lives--;
livesText.text = lives.toString();
lifeTimer.start();
}
if(mChar.hitTestObject(b2)) {
lives--;
livesText.text = lives.toString();
lifeTimer.start();
}
if(mChar.hitTestObject(b3)) {
lives--;
livesText.text = lives.toString();
lifeTimer.start();
}
if(mChar.hitTestObject(b4)) {
lives--;
livesText.text = lives.toString();
lifeTimer.start();
}
if(mChar.hitTestObject(b5)) {
lives--;
livesText.text = lives.toString();
lifeTimer.start();
}
if(mChar.hitTestObject(bFast)) {
lives--;
livesText.text = lives.toString();
lifeTimer.start();
}
if(lives <= 0)
{
gotoAndStop(7);
}
}
if(lifeTimer.running)
{
mInvin.x = mChar.x;
mInvin.y = mChar.y;
}
}
Thanks for reading: I know this is kind of a tough problem to figure out, so thanks for the help!
Your problem here, from what I can derive from your code, is your event listeners. I don't know what code you have on frame 7, but unless you remove the event listeners, they will keep on listening and running code, even though you have moved the playhead forward on the timeline (e.g. when calling gotoAndStop())
I'm trying to create an honeycomb with as3 but I have some problem on cells positioning.
I've already created the cells (not with code) and for cycled them to a funcion and send to it the parameters which what I thought was need (the honeycomb cell is allready on a sprite container in the center of the stage).
to see the structure of the cycle and which parameters passes, please see the example below, the only thing i calculate in placeCell is the angle which I should obtain directly inside tha called function
alt text http://img293.imageshack.us/img293/1064/honeycomb.png
Note: the angle is reversed but it isn't important, and the color are useful in example only for visually divide cases.
My for cycle calls placeCell and passes cell, current_case, counter (index) and the honeycomb cell_lv (cell level).
I thought it was what i needed but I'm not skilled in geometry and trigonometry, so I don't know how to position cells correctly:
import flash.display.Sprite;
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
function createHoneycomb (cells:int):void {
var honeycomb:Sprite = new Sprite ();
addChild (honeycomb);
var cell_lv:int = 1;
var increment:int = 6;
var tot_cur_cells:int = 1;
var current_case:int = 0;
var case_counter:int = 1;
var case_length:int = 1;
var max_cases:int = 6;
var nucleus:Sprite = new cell (); // hexagon from library
honeycomb.addChild (nucleus);
for (var i:int = 1; i <= cells; i ++) {
if (case_counter < case_length) {
case_counter ++;
} else {
current_case ++;
if (current_case > max_cases) current_case = 1;
case_counter = 1;
}
if (i == tot_cur_cells) { // if i reach the current level
if (i > 1) cell_lv ++;
case_length = cell_lv;
tot_cur_cells += (increment * cell_lv);
}
var current_cell:Sprite = new cell (); // hexagon from library
honeycomb.addChild (current_cell);
placeCell (current_cell, case_counter, current_case, cell_lv);
}
function centerHoneycomb (e:Event):void {
honeycomb.x = stage.stageWidth / 2
honeycomb.y = Math.round (stage.stageHeight / 2);
}
stage.addEventListener (Event.RESIZE, centerHoneycomb)
stage.dispatchEvent (new Event (Event.RESIZE));
}
function placeCell (cell:Sprite, counter:int, current_case:int, cell_lv:int):void {
var margin:int = 2;
// THIS IS MY PROBLEM
var start_degree:Number = (360 / (cell_lv * 6));
var angle:Number = (start_degree * ((current_case - 1) + counter) - start_degree);
var radius:Number = (cell.width + margin) * cell_lv;
cell.x = radius * Math.cos (angle);
cell.y = radius * Math.sin (angle);
// end of the problem
if (angle != 0) trace ("LV " + cell_lv + " current_case " + current_case + " counter " + counter + " angle " + angle + " radius " + radius);
else trace ("LV " + cell_lv + " current_case " + current_case + " counter " + counter + " angle " + angle + " radius " + radius);
}
createHoneycomb (64);
if you copy and paste this code, it works but you need to create an hexagon and call it in the actionscript library as cell
how can I do to solve it?
I've also thought to use a switch with the cases to align it, but i think is a little bit buggy doing this
Okay, I really loved this question. It was interesting, and challenging, and I got a working result. I didn’t use any of your code as the base though, but started from scratch, so depending on your final use, you might need to change a bit.
I did however created similar variables to those inside of your cells (in the picture). For each cell you have the following properties:
the iterating variable i is equally to your cell number
the radius r equals your level, and expresses the distance from the center (with 0 being the center)
the position p expresses the position in the current radius
the sector s equals your case, but starts with zero
p % r equals your index
I don’t have an angle, simply because I don’t position the individual hexagons using an angle. Instead I base the position of the (fixed) positions of the hexagons at radius 1 and calculate the missing ones in between.
The following code shows my implementation with 61 (60 + the center; but it’s configurable). You can also see the code in action on Wonderfl.
package
{
import flash.display.Sprite;
public class Comb extends Sprite
{
public function Comb ()
{
Hexagon.scale = 0.5;
this.x = stage.stageWidth / 2;
this.y = stage.stageHeight / 2;
// draw honeycomb with 60 cells
drawComb( 60 );
}
private function drawComb ( n:uint ):void
{
var colors:Array = new Array( 0x33CC33, 0x006699, 0xCC3300, 0x663399, 0xFF9900, 0x336666 );
var sectors:Array = new Array(
new Array( 2, 0 ),
new Array( 1, 1 ),
new Array( -1, 1 ),
new Array( -2, 0 ),
new Array( -1, -1 ),
new Array( 1, -1 ) );
var w:Number = 0.50 * Hexagon.hxWidth;
var h:Number = 0.75 * Hexagon.hxHeight;
var r:uint, p:uint, s:uint;
var hx:Hexagon;
for ( var i:uint = 0; i <= n; i++ )
{
r = getRadius( i );
p = getPosition( i, r );
s = getSector( i, r, p );
// create hexagon
if ( r == 0 )
hx = new Hexagon( 0xCCCCCC );
else
hx = new Hexagon( colors[s] );
hx.x = w * ( r * sectors[s][0] - ( p % r ) * ( sectors[s][0] - sectors[ ( s + 1 ) % 6 ][0] ) );
hx.y = h * ( r * sectors[s][1] - ( p % r ) * ( sectors[s][1] - sectors[ ( s + 1 ) % 6 ][1] ) );
addChild( hx );
}
}
private function getRadius ( i:uint ):uint
{
var r:uint = 0;
while ( i > r * 6 )
i -= r++ * 6;
return r;
}
private function getPosition ( i:uint, r:uint ):uint
{
if ( r == 0 )
return i;
while ( r-- > 0 )
i -= r * 6;
return i - 1;
}
private function getSector ( i:uint, r:uint, s:uint ):uint
{
return Math.floor( s / r );
}
}
}
import flash.display.Shape;
class Hexagon extends Shape
{
public static var hxWidth:Number = 90;
public static var hxHeight:Number = 100;
private static var _scale:Number = 1;
public function Hexagon ( color:uint )
{
graphics.beginFill( color );
graphics.lineStyle( 3, 0xFFFFFF );
graphics.moveTo( 0, -50 );
graphics.lineTo( 45, -25 );
graphics.lineTo( 45, 25 );
graphics.lineTo( 0, 50 ),
graphics.lineTo( -45, 25 );
graphics.lineTo( -45, -25 );
graphics.lineTo( 0, -50 );
this.scaleX = this.scaleY = _scale;
}
public static function set scale ( value:Number ):void
{
_scale = value;
hxWidth = value * 90;
hxHeight = value * 100;
}
public static function get scale ():Number
{
return _scale;
}
}