Preventing object from getting stuck on walls and rapidly bouncing between two points - actionscript-3

I currently have objects populating the screen randomly and bouncing around the stage. The problem is, some of the objects are getting stuck and bouncing back and forth rapidly between two points. How can I prevent this?
Here's how I have the EnterFrame setup:
var ballT = e.target
ballT.x += ballT.vx;
ballT.y += ballT.vy;
if (ballT.x + ballT.width / 2 >= sWidth || ballT.x - ballT.width / 2 <= 0) {
ballT.vx = - ballT.vx;
} else if (ballT.y + ballT.height / 2 >= sHeight || ballT.y - ballT.height / 2 <= 0) {
ballT.vy = - ballT.vy;
}
Any ideas or any good reads worth checking out?

Most likely, your objects are going a bit "too much out of bounds", so that when you turn them around, they don't make it inside the bounds before you turn them around again. You may want to change the code to be a bit more clear on when to turn things around;
var ballT = e.target
ballT.x += ballT.vx;
ballT.y += ballT.vy;
// Outside to the right and heading right - turn around
if ( ballT.x + ballT.width / 2 >= sWidth && ballT.vx > 0 )
ballT.vx = -ballT.vx;
// Outside to the left and heading left - turn around
if ( ballT.x - ballT.width / 2 <= 0 && ballT.vx < 0 )
ballT.vx = -ballT.vx;
// Outside at the bottom and heading down - turn around
if ( ballT.y + ballT.height / 2 >= sHeight && ballT.vy > 0 )
ballT.vy = -ballT.vy;
// Outside at the top and heading up - turn around
if ( ballT.y - ballT.height / 2 <= 0 && ballT.vy < 0 )
ballT.vy = -ballT.vy;

Related

Control timeline with pinchzoom in Actionscript 3.0

I'm using Actionscript 3.0 in Flash. Is there a way to control the timeline with pinching (so instead of zooming out/in you are moving the timeline back/forth)? I'm working on an story app where the player is in control of the story.
You could do something like the following:
import flash.events.TransformGestureEvent;
Multitouch.inputMode = MultitouchInputMode.GESTURE;
stop();
//listen on whatever object you want to be able zoom on
stage.addEventListener(TransformGestureEvent.GESTURE_ZOOM , zoomGestureHandler);
function zoomGestureHandler(e:TransformGestureEvent):void{
//get the zoom amount (since the last event fired)
//we average the two dimensions (x/y). 1 would mean no change, .5 would be half the size as before, 2 would twice the size etc.
var scaleAmount:Number = (e.scaleX + e.scaleY) * 0.5;
//set the value (how many frames) to skip ahead/back
//we want the value to be at least 1, so we use Math.max - which returns whichever value is hight
//we need a whole number, so we use Math.round to round the value of scaleAmount
//I'm multiplying scaleAmount by 1.25 to make the output potentially go a bit higher, tweak that until you get a good feel.
var val:int = Math.max(1, Math.round(scaleAmount * 1.25));
//determine if the zoom is actually backwards (smaller than before)
if(scaleAmount < 1){
val *= -1; //times the value by -1 to make it a negative number
}
//now assign val to the actual target frame
val = this.currentFrame + val;
//check if the target frame is out of range (less than 0 or more than the total)
if(val < 1) val = this.totalFrames + val; //if less than one, add (the negative number) to the totalFrames value to loop backwards
if(val > this.totalFrames) val = val - this.totalFrames; //if more than total, loop back to the start by the difference
//OR
if(val < 1) val = 0; //hard stop at the first frame (don't loop)
if(val > this.totalFrames) val = this.totalFrames; //hard stop at the last frame (don't loop)
//now move the playhead to the desired frame
gotoAndStop(val);
}

hexagon grid drawing issue

I seem to be having a bit of trouble drawing a proper hex grid:
As you can see, the hexagons are just slightly misaligned, though I believe my math is correct (some of which is verfied via http://www.redblobgames.com/grids/hexagons/).
My method of drawing is to start at the top left hexagon (first tile in the first row) and draw that row of tiles. Then for the next row, there is a negative X offset and positive Y offset, etc until it reaches the middle row, at which the X offsets increase until 0:
private function drawHexGrid(inputGraphics:Graphics, inputPos:Point, inputGrid:HexGrid, inputTileSize:int):void {
var rootThree:Number = Math.sqrt(3); // seems like this will be used a lot
// total number of rows and also size of largest row (in tiles)
var totalRows:int = (2 * inputGrid.size) - 1;
// the other useful dimension of a hex tile
var triSize:Number = rootThree * 0.5 * inputTileSize;
var topLeft:Point = new Point(-(inputGrid.size - 1) * triSize, -(1.5 * (inputGrid.size - 1) * inputTileSize));
topLeft.x += inputPos.x;
topLeft.y += inputPos.y;
var currentPos:Point = topLeft.clone();
// step size between each tile and row
var xStep:Number = rootThree * inputTileSize;
var yStep:Number = triSize * rootThree;
var offsetDirection:int = -1;
var rowLimit:int = inputGrid.size;
var offsetAmount:int = 0; // offsetAmount goes 1 to n, then back to 0 again, used for calculating thw row offsets
var mazeTiles:Vector.<Tile> = inputGrid.getTiles();
var tileCounter:int = 0; // index to cycle through mazeTiles
for(var rowCount:int = 0; rowCount < totalRows; rowCount++){
currentPos.x = topLeft.x + (offsetAmount * rootThree / -2 * inputTileSize);
for(var counter:int = 0; counter < rowLimit; counter++){
drawHexTile(inputGraphics, currentPos.x, currentPos.y, inputTileSize, mazeTiles[tileCounter++]);
currentPos.x += xStep;
}
currentPos.y += yStep;
if(rowCount == (inputGrid.size - 1)){
offsetDirection *= -1;
}
rowLimit += offsetDirection * -1;
offsetAmount -= offsetDirection;
} // end of for loop
} // end of drawHexGrid()
The actual drawing of each hexagon is in this loop:
private function drawHexTile(inputGraphics:Graphics, inputX:int, inputY:int, inputSize:int, inputTile:Tile):void {
inputGraphics.lineStyle(0.1, 0, 1);
var convertToRadians:Number = Math.PI / 180;
// easier to draw by wall, since need to look up each wall and can set a starting degree without having to worry about the 'end degree'
// (since the end may not be 360 degrees if the starting degree is in the negatives or a high degree)
var degrees:int = -150; // starting wall is the top left wall of the hexagon tile
for(var counter:int = 0; counter < 6; counter++){
if(inputTile.walls[counter] == true){
inputGraphics.moveTo(inputX + (Math.cos(degrees * convertToRadians) * inputSize),
inputY + (Math.sin(degrees * convertToRadians) * inputSize));
inputGraphics.lineTo(inputX + (Math.cos((degrees + 60) * convertToRadians) * inputSize),
inputY + (Math.sin((degrees + 60) * convertToRadians) * inputSize));
}
degrees += 60;
}
} // end of drawHexTile() method
At first glance, I think the issue is due to floating point math accuracy, though I'm not sure how to go about fixing/improving this.
Any ideas?
I am aware that my drawing method would end up with many overlapping lines, which is fine for the moment. If it helps, the overall code is meant for a hexagon maze generator, which is working fine and thus can be ignored as it isn't part of the problem.
And if it helps, how I am storing the hexagon tiles is just in one long array, indexed like this:
where n=4
0 1 2 3
4 5 6 7 8
9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27
28 29 30 31 32
33 34 35 36
While not preferable for your final product, try changing all of your int values to Num values. See what happens. Also focus on your code that deals with the X position of each new tile.
from your comments
...uh.....this worked. I don't know why. =| I just did a global replace for ints to Numbers and it worked. Hm, will go back and manually replace to see exactly what int variable did it.
Oh! I figured it out. In drawHexTile() I had set inputX and inputY as int types. But in actuality, the positions being calculated were floating values, so they were being rounded to ints implicitly causing the misalignment. Simple mistake (by reflex), no magic involved after all in the fix.
Looks like a floating point problem in your drawHexTile. have you tried to round down your drawing coordinates so you always have a round pixel coordinates ? Something like
inputGraphics.moveTo(Math.floor(inputX + (Math.cos(degrees * convertToRadians) * inputSize)), Math.floor(inputY + (Math.sin(degrees * convertToRadians) * inputSize)));
inputGraphics.lineTo(Math.floor(inputX + (Math.cos((degrees + 60) * convertToRadians) * inputSize)), Math.floor(inputY + (Math.sin((degrees + 60) * convertToRadians) * inputSize)));

How can I fix the wrong timing of my fade/scroll animation?

Currently I'm busy with Actionscript. I have a map with diverse worlds. If you click on an arrow icon you will scroll to the other world. The code is:
street.street_market.addEventListener(MouseEvent.CLICK, straat_actie2);
function straat_actie2(evt:MouseEvent) {
market.bringToFront();
MovieClip(root).worldmap.targetX = marktx;
MovieClip(root).worldmap.targetY = markty;
old = "street";
new = "market";
addEventListener(Event.ENTER_FRAME, fade);
}
The worlds are sliding to each other and the other one is fading out. It works like this:
addEventListener(Event.ENTER_FRAME, ballEnterFrame)
function ballEnterFrame (pEvent):void
{
var b = pEvent.currentTarget;
b.x += (b.targetX - b.x) / 16;
b.y += (b.targetY - b.y) / 16;
}
function fade(e:Event)
{
if(new != "")
{
this[new].alpha+=0.03;
if(this[old].alpha >= 0.2)
{
this[old].alpha-=0.05;
}
}
}
It all works fine. Except one thing. The longer you stay on a world map the longer it will take to let the other world fade out. So if I stay on the street map for 10 secs and I scroll to the next one it takes around 10 seconds before the old map fades out.
Does someone know how I can solve this problem?
Thanks.
Try setting up your function like this...
function fade(e:Event)
{
if( new != "")
{
//# add : ONLY if less than 1 (full opacity)
if ( this[new].alpha < 1 )
{ this[new].alpha += 0.03; } //# fade in (increase)
//# minus : if NOT 0 -OR- is NOT less than 0 (transparent)
if ( this[old].alpha != 0 || !( this[old].alpha < 0 ) )
{ this[old].alpha -= 0.05; } //# fade out (reduce)
//# clamp within limits of 0 to 1 range
if ( this[new].alpha > 1 ) { this[new].alpha = 1; } //# never higher than 1
if ( this[old].alpha < 0 ) { this[old].alpha = 0; } //# never less than 0
}
}//# end function fade
Explained :
...if I stay on the street map for 10 secs and I scroll to the next
one it takes around 10 seconds before the old map fades out.
Do you realise that ENTER_FRAME is something that happens every frame according to your SWF's frame rate (FPS)?. If you set 30 FPS in your Document settings, by your code you are adding 0.03 x 30 every second for 10 seconds. The .alpha amount rises higher than 1, now the delay is waiting for it to reduce from 9.0 back to 1 then it fades out as expected. There is no visual benefit to allowing alpha values to become anything higher than 1 or less than 0.

I have an array of blasts and an array of zombies, how can I efficiently detect collisions?

I have an array of blasts, and an array of zombies, but I'm struggling to find a way to efficiently detect collisions and remove both the blasts and zombies that have collided. Any suggestions?
Iterator<Rectangle> blastIter = blasts.iterator();
while(blastIter.hasNext()) {
Rectangle blast = blastIter.next();
blast.x += 200 * Gdx.graphics.getDeltaTime();
if(blast.x + 16 > 800) blastIter.remove();
}
Iterator<Rectangle> zombieIter = zombies.iterator();
while(zombieIter.hasNext()) {
Rectangle zombie = zombieIter.next();
zombie.x -= 150 * Gdx.graphics.getDeltaTime();
//if(zombie.overlaps(blast)) zombieIter.remove();
}
You need to put the other iterator inside of the other iterator, try this:
Iterator<Rectangle> zombieIter = zombies.iterator();
while(zombieIter.hasNext()) {
Rectangle zombie = zombieIter.next();
zombie.x -= 150 * Gdx.graphics.getDeltaTime();
Iterator<Rectangle> blastIter = blasts.iterator();
while(blastIter.hasNext()) {
Rectangle blast = blastIter.next();
blast.x += 200 * Gdx.graphics.getDeltaTime();
if(blast.x + 16 > 800) blastIter.remove();
if(zombie.overlaps(blast)) zombieIter.remove();
}
}
Make nested loops, just like you have there, but one inside other (which is inside which is not important), so you'll have all combinations of objecst from those 2 lists.

AS3 Continuous value from rotation slider

I have a dial which I drag around a circle to give me a reading between 0 and 1.
Something like this:
dx = mouseX-centerX;
dy = mouseY-centerY;
rad = Math.atan2(dy,dx);
rad += offset;
Tweener.addTween(knob,{y:centerY - Math.cos(rad)*radius, time:.1, transition:"easeOutSine"});
Tweener.addTween(knob,{x:centerX + Math.sin(rad)*radius, time:.1, transition:"easeOutSine"});
knob.rotation = rad * 180 / Math.PI;
This work's great, except when the slider goes from 359 degrees to 1 degree, my value between 0 and 1 returns to zero. (Which makes sense, as the value is based on the angle of my slider)
I'm trying to find a way for the dial to move from 359 degrees to 361 and onwards basically.
In my head: I need to check if the next value of my mouse drag goes past the 360 degree point and add 360 to the total, to stop it returning to zero and continue to 361 degrees.
I just cant work out how to put this into code...
On each frame when you are rotating the knob, check the change in angular distance instead of direct angle.
Save the previous frames angle and see if the difference is positive or negative.
var rad = Math.atan2(dy, dx);
var diff = rad - oldRad;
oldRad = rad;
if( diff > Math.PI )
diff -= Math.PI * 2;
if( shortestAngle < -Math.PI )
diff += Math.PI * 2;
diff should contain a value that if it's been rotated to the right, is positive (or negative if rotated left). Simply add that to the total angle.
There might be some errors in the code (took it from an old project), but that's the gist of it :)
Hope that helps!