as3 move towards function jitters on Y axis - actionscript-3

As the title suggests, I've written a move towards function that works perfectly fine, except that it jitters on the Y axis.
The problem
It happens when the function is operating on the Y axis and the difference between the starting value and the target value is less than that between the starting value and the value to return - meaning that the value has gone past its target. as this point, it's supposed to set the returning value to the target value, but for the most part it's not. Except when it's operating on the x axis, in which case it works fine. It's really strange.
The code
here's the code I'm using for the function:
public static function LookAt(thisX:Number, thisY:Number, targetX:Number, targetY:Number, speed:Number = 0, startRot:Number = 0):Number
{
// Get the distances between the two parsed points
var xDif:Number = targetX - thisX;
var yDif:Number = targetY - thisY;
// Use a tangent formula to get the rotation to return in radians, then convert to degrees
var rot:Number = Math.atan2(xDif, yDif) * 180/Math.PI * -1 - 180;
// If a speed has been parsed
if (speed != 0)
{
// Ensure the parsed starting rotation is between -180 and 180
while (startRot > 180)
{startRot -= 360;}
while (startRot < -180)
{startRot += 360;}
// If the rotation previously calculated is less than the parsed starting rotation,
// return the starting rotation minus the speed. Otherwise, return the starting rotation
// plus the speed
return (rot > startRot) ? startRot + speed : startRot - speed;
}
else
{
return rot;
}
}
public static function PointAround (axisPos:Number, angle:Number, speed:Number, axis:String = "x"):Number
{
// Convert the parsed angle into radians
var fixedRot = angle * Math.PI / 180;
// Return the parsed position plus speed multiplied by the sine of the angle in radians for the x axis,
// or the cosine of the angle in radians for the y axis
return (axis == "x") ? axisPos + speed * Math.sin(fixedRot) : axisPos + speed * Math.cos(fixedRot) * -1;
}
public static function PointTowards(thisX:Number, thisY:Number, targetX:Number, targetY:Number, speed:Number, axis:String = "x"):Number
{
// Use the LookAt function to calculate a rotation for later use in this function
var workingAngle:Number = ExtraMath.LookAt (thisX, thisY, targetX, targetY);
var toReturn;
var thisVar:Number = (axis == "x") ? thisX : thisY;
var targetVar:Number = (axis == "x") ? thisX : thisY;
toReturn = ExtraMath.PointAround (thisVar, workingAngle, speed);
// BUGGY LINE
toReturn = (thisVar >= targetVar && toReturn <= targetVar
|| thisVar <= targetVar && toReturn >= targetVar)
? targetVar
: toReturn;
return toReturn;
}
and here's the code I'm using to test it:
public var c:Sprite;
public function TestZone()
{
// constructor code
stage.addEventListener(Event.ENTER_FRAME, Update);
}
private function Update (e:Event):void
{
c.x = ExtraMath.PointTowards(c.x, c.y, stage.mouseX, stage.mouseY, 5);
c.y = ExtraMath.PointTowards(c.x, c.y, stage.mouseX, stage.mouseY, 5, "y");
}
things I've tried already
turning the line into regular a regular if statement with curly brackets and all, and tracing the variables thisY, targetY and toReturn after it has been operated on. the really annoying thing is that it turns out it sometimes actually returns the right number, but then proceeds to bug out again
Using an absolute value instead of stage.mouseY in testing. bug occurs as usual
Performing the function on the Y axis before the X axis. no difference
Changing the condition for setting the variables thisVar and targetVar to (axis != x) and switching the if/else values. do difference

A few things may be causing the problem:
[LookAt]
return (rot > startRot) ? startRot + speed : startRot - speed;
This may cause overshooting. If rot is only very slightly different from startRot, you are still adding (or subtracting) a full speed increment. If the absolute distance between rot and startRot is less than speed, it should return rot regardless.
This reduces angular jittering.
[PointAround]
return (axis == "x") ? axisPos + speed * Math.sin(fixedRot) : axisPos + speed * Math.cos(fixedRot) * -1;
Watch out for operator precedence. You are expecting this line to be parsed as
(axis == "x") ?
(axisPos + speed * Math.sin(fixedRot)) :
(axisPos + speed * Math.cos(fixedRot) * -1);
But that may not be the case. The line may instead be interpreted as
(
(axis == "x") ?
(axisPos + speed * Math.sin(fixedRot)) :
axisPos
)
+ speed * Math.cos(fixedRot) * -1;
You can either memorize all precedence rules and make sure you never mistake them, or put parenthesis around to ensure it's doing the right thing. In this case you can simplify the expression to
axisPos + speed * (axis == "x" ? Math.sin(fixedRot) ? -Math.cos(fixedRot))
var thisVar:Number = (axis == "x") ? thisX : thisY;
var targetVar:Number = (axis == "x") ? thisX : thisY;
So thisVar and targetVar always have the same value? I don't understand what was supposed to happen here, and nobody seems to reassign those variables later.
c.x = ExtraMath.PointTowards(c.x, c.y, stage.mouseX, stage.mouseY, 5);
c.y = ExtraMath.PointTowards(c.x, c.y, stage.mouseX, stage.mouseY, 5, "y");
You are changing the position separately in each axis. It may work, but it's harder and error prone. For example
c.x = ...PointTowards(c.x ...);
c.y = ...PointTowards(c.x ...);
You are changing the value of c.x between the calls, so the first call to PointTowards see a different point from the second call. That may be the reason why the jittering only happens on the y axis. I suggest making a function that deals with box axis at once, or at the very least storing the old values of c.x and c.y:
var oldX:Number = c.x;
var oldY:Number = c.y;
c.x = ExtraMath.PointTowards(oldX, oldY, stage.mouseX, stage.mouseY, 5);
c.y = ExtraMath.PointTowards(oldX, oldY, stage.mouseX, stage.mouseY, 5, "y");

I fixed it! I changed the function to return a point object instead of a number and now it works perfectly

Related

Making objects chase another (AS3)

So i have an object that i want to chase another object. Everything is working fine, however i tried to implement an easeTo function to make it so that the object doesn't take the most direct route to the x,y locations of the object it is chasing. This also works but the thing that is really pissing that i can't fix is the fact that the objects speed changes depending how far away from the object it is chasing is. For example, if the object being chased is on the far right of the stage and the chaser spawns on the far left, it will go really fast towards the object on the right and slow down as it gets closer. I would love for it to go at a consistent speed. Any suggestions?
Thanks
Here is my code
private function easeTo(cur:Point, target:Point, ease:Number):Point{
var dx:Number = target.x - cur.x;
var dy:Number = target.y - cur.y;
var finalResult:Point = new Point(); //create a var to hold the result
finalResult.x = cur.x + (dx * ease);
finalResult.y = cur.y + (dy * ease);
return finalResult;
}
public function chase(xValue:Number, yValue:Number):void{
//store current x and y in a point var
var curPos:Point = new Point (x,y);
//store the mouse x and y in a var
var targetPos:Point = new Point(xValue, yValue);
var nextPos:Point = easeTo(curPos, targetPos, 0.001);
this.x = nextPos.x;
this.y = nextPos.y;
}
So the (xValue:Number, yValue:Number) is the x and y values of the object it is chasing being passed into the chase function.
I would suggest using Math.min (when dx or dy is negative) and Math.max (when dx or dy is positive) with a value you define to be your max speed
ie:
var maxSpeed:Number = 20;
if (dx >= 0){
dx = Math.max(dx,maxSpeed)
}else{
dx = Math.min(dx,maxSpeed)
}
repeat for dy

Function to convert numbers over 1000 to 1k etc. AS3

G'day,
The function is coded, but it's on the stage frame. I'm looking to get it converted into a more dynamic function so I can just call it on all my textfields.
Here's the code:
function numtolet():void
{
output.text = String(int(earner * 100) / 100);
if (earner >= 1000 && earner < 1000000)
{
output.text = String(int((earner/1000) * 100) / 100 + "k");
}
else if (earner >=1000000 && earner < 1000000000)
{
output.text = String(int((earner/ 1000000) * 100 ) / 100 + " M");
}
}
I'm looking to turn the 'output.text' portion into a variable that changes based on the text field calling the function and 'earner' to the variable the textfield reads.
Cheers,
-Aidan.
You'd better write your function as proper function that can return a String value to assign to a text property or use elsewhere. Also, you should use a pattern that is easily extendable to bigger prefixes, should you need them. Say, I have found a game with a W prefix being used, which is one beyond the common "yotta" prefix, and there was a set of subsequent prefixes as well. So, this is how you should devise such a function:
function numtolet(x:Number):String {
const prefixes:Vector.<String> = Vector.<String>(["","k","m","g","t"]);
// add more to taste. Empty prefix is used if the number is less than 1000
var y:Number=x;
var i:int=1;
// provided x>0, if not, store a minus somewhere and attach later
while((y>=1000) && (i<prefixes.length)) {
y=y/1000;
i++;
}
// there, you have just divided X by 1000 a couple of times and selected the prefix
var s:String = y.toFixed(2)+prefixes[i-1];
// if there was a minus, add it here: s="-"+s;
return s;
}
Then you just call it like this:
output.text=numtolet(earner);
You can do this using the CHANGE event:
output.addEventListener(Event.CHANGE, numtolet);
function numtolet(e:Event):void
{
output.text = String(int(earner * 100) / 100);
if (earner >= 1000 && earner < 1000000)
{
output.text = String(int((earner/1000) * 100) / 100 + "k");
}
else if (earner >=1000000 && earner < 1000000000)
{
output.text = String(int((earner/ 1000000) * 100 ) / 100 + " M");
}
}
This will make the function run every time the text is changed by a user, but you'd probably want to add a few conditional (if)'s to the function, or use a variable to keep track of the curent number. When the number converts to 1k, how does it know what do to at 1000k?
Feel free to ask if you need help on this.
When you use the CHANGE event like Neguido said, and add listeners to different text fields, you can use e.target.text = to change the text in the calling text field.
To target a different variable for each text field is more difficult, because you cant pass extra arguments into the event handlers, and you cant add your own variables/properties to textFields. You could stick each textField into a parent MovieClip and then create variables in there like MovieClip1.earner = 0 and retrieve the values with e.target.parent.earner. You could also write a dynamic extension to the TextField class where you add custom variables. Alternatively you could use a switch statement in your event handler to use different variables for different callers.
Here's a quick function I wrote up for something else. It can easily be adapted to larger numbers by adding another if statement and adding 000 to the number. It also doesn't include the output display, but that can very easily be added as well. Hope it helps!
Call the function via numToLet(earner).
function numToLet(x) {
if (x > 1000000000000000000) {
x = x / 1000000000000000000
x = Number(x.toFixed(2));
return x + "Quin";
}
if (x > 1000000000000000) {
x = x / 1000000000000000
x = Number(x.toFixed(2));
return x + "Quad";
}
if (x > 1000000000000) {
x = x / 1000000000000
x = Number(x.toFixed(2));
return x + "Tril";
}
if (x > 1000000000) {
x = x / 1000000000
x = Number(x.toFixed(2));
return x + "Bil";
}
if (x > 1000000) {
x = x / 1000000
x = Number(x.toFixed(2));
return x + "Mil";
}
if (x < 1000000) {
x = Number(x.toFixed(2));
return x;
}
}

Breakout with Flash: I need help to improve my Brick n Ball collision

I've been stuck on this problem for a very long time now, I've searched around alot and tried stuff, but nothing works. Some explanations are just very hard for me to understand as Im pretty new to programming overall and got alot to learn.
I have two problems
1: The ball wont collide with the bricks sometimes when the speed is too fast.
2: The ball is capable of hitting 2 bricks.
Both problems is related to the fact that 60 fps isnt enough for my type of collision detection to work properly.
I just need someone to explain in a simple way as possible what I need to do to make a collision detection that will prevent this from happen.
Here's my current collision code:
private function checkCollision(): void {
grdx = Math.floor((ball.x) / 28);
grdy = Math.floor((ball.y) / 14);
ngrdx = Math.floor((ball.x + dx) / 28);
ngrdy = Math.floor((ball.y + dy) / 14);
var flipX: Boolean = false;
var flipY: Boolean = false;
if ((grdy <= level.length - 1) &&
(ngrdy <= level.length - 1) &&
(grdy >= 0 && ngrdy >= 0)) {
if (testBlock(grdx, ngrdy)) {
flipY = true;
paddleFlag = 1;
}
if (testBlock(ngrdx, grdy)) {
flipX = true;
paddleFlag = 1;
}
if (testBlock(ngrdx, ngrdy)) {
flipX = true;
flipY = true;
paddleFlag = 1;
}
dx *= flipX ? -1 : 1;
dy *= flipY ? -1 : 1;
}
}
private function testBlock(xPos: int, yPos: int): Boolean {
if (level[yPos][xPos] > 0 && level[yPos][xPos] != 13) {
trace("hit on X,Y");
level[yPos][xPos] = 0;
breakBlock("Block_" + yPos + "_" + xPos);
trace("Block: " + totalBreaks + " / " + totalBlocks);
return true;
}
return false;
}
private function breakBlock(blockName: String): void {
if (this.getChildByName(blockName)) {
this.removeChild(this.getChildByName(blockName));
totalBreaks++;
}
}
Thank you and sorry for my bad english, its not my motherlanguage.
One solution is to move the ball in smaller iterations, multiple times in a given frame.
For example, and I am giving this solution assuming that you are moving the ball based on the time elapsed from the last frame.
Suppose that 30 milliseconds have elapsed since the last frame update. In that case you would update the movement/collision twice in that frame using 15 millisecond as your time elapsed.
The higher resolution of collision you want, the more iterations you would do.
Here's an example :
// class declarations
var lastFrame:Number;
var iterationsPerFrame:int;
function startGame():void
{
// lets specify 3 updates per frame
iterationsPerFrame = 3;
// save initial time
lastFrame = getTimer();
// create your listener
addEventListener(Event.ENTER_FRAME, update);
}
function update(e:Event):void
{
var currentFrame:Number = getTimer();
var deltaTime:Number = (currentFrame - lastFrame)/1000;
var iterationDelta:Number = deltaTime/iterationsPerFrame;
for (var index:int = 0;index < iterationsPerFrame;index++)
{
// I'm assuming dx,dy are the velocity of the ball, in pixels per second
ball.x += dx * iterationDelta;
ball.y += dy * iterationDelta;
// check collision
}
// set lastFrame to the currentFrame time, preparing for next frame
lastFrame = currentFrame;
// after this, your frame is going to render
}
You could work out how far the ball travels each frame (A) based on its speed, how far the ball is from the paddle (B) and if A > B manually trigger a collision that frame.
You're essentially checking every bricks X and Y coordinate to the balls X and Y coordinate, so if the bricks are stored in an array this becomes: Sqrt( Sqrd(p2.x - p1.x) + Sqrd(p2.y - p1.y))
for(var i=0; i<brickArray.length; i++)
{
var distance:Number = Math.sqrt((brickArray[i].x - ball.x) * (brickArray[i].x - ball.x) +
(brickArray[i].y - ball.y) * (brickArray[i].y - ball.y));
}
This is a very good tutorial on high speed collison detection:
http://www.newgrounds.com/bbs/topic/1072673

How to have an object hover back and forth constrained within a specific radius?

I have a sprite in a movie symbol that I would like to hover back and forth within a 360 radius. I was hoping to make it smooth and random. Never really venturing from its original xy cordinates.
I've tried to create some stipulations with if statements and a starting momentum. Like this:
var num = 2;
stage.addEventListener(Event.ENTER_FRAME, hover);
function hover(evt:Event):void{
//start it moving
cloudWhite.y += num;
cloudWhite.x += num;
//declare these variables
var cX = cloudWhite.x;
var cY = cloudWhite.y;
// object travels 10 pixels
var cXP = cX + 10;
var cXN = cX - 10;
var cYP = cY + 10;
var cYN = cY - 10;
// if object goes 10 pixels reverse direction of momentum (maybe)
if (cX >= cXP) {
num = -2;
}
if (cX <= cXN){
num = 2;
}
if (cY >= cYP) {
num = 2;
}
if (cY <= cYN){
num = 2;
}
Clearly this is super wrong because when it runs the object just either goes to 0,0 or to some place that only the math gods know of.
I am clearly a noob at this kind of math so i apologize but I am very excited to learn the trig behind this.
Thank you for your help and thank you for reading.
You are setting all your variables inside the ENTER_FRAME loop, so none of your conditions ever evaluates to true. On every single frame you are doing this:
cloudWhite.x += 2;
cX = cloudWhite.x;
cXP = cX + 10; // Must == cloudWhite's previous x + 10 + 2;
cXN = cX - 10; // Must == cloudWite's previous x -10 + 2;
if(cX > cXP)... // Can never be true.
if(cX < cXN)... // Can never be true.
What you need to do is:
1) Store the original position of cloudWhite somewhere outside the loop, and store it before the loop begins.
2) Define your bounds relative to the original position of cloudWhite, again before your loop begins. Also define the amount you are going to change the position with each iteration.
3) Start your loop.
4) Increment the current position of cloudWhite on each iteration. Add a little random in here if you want the shape to move in a random manner.
5) Check if the new position of cW is outside your bounds and adjust the direction if it is.
The sample below is crude and jerky but I don't know exactly what effect you're looking for. If you want smoother, longer movements in each direction, consider using the Tween class or a Tween library such as the popular Greensock one, instead of incrementing / decrementing the position manually. There's a useful discussion of this here: http://www.actionscript.org/forums/archive/index.php3/t-163836.html
import flash.display.MovieClip;
import flash.events.Event;
// Set up your variables
var original_x:Number = 100; // Original x
var original_y:Number = 100; // Original y
var x_inc:Number = 5; // X Movement
var y_inc:Number = 5; // Y Movenent
var bounds:Number = 50; // Distance from origin allowed
// Doesn't take into account width of object so is distance to nearest point.
// Create an MC to show the bounds:
var display:MovieClip = addChild(new MovieClip()) as MovieClip;
display.graphics.lineStyle(1, 0x0000FF);
display.graphics.beginFill(0x0000FF, 0.5);
display.graphics.drawRect(0-bounds, 0-bounds, bounds * 2, bounds *2);
display.x = original_x;
display.y = original_y;
addChild(display);
// Create our moving mc:
var mc:MovieClip = addChild(new MovieClip()) as MovieClip;
mc.graphics.beginFill(0xFF0000, 1);
mc.graphics.drawCircle(-10, -10, 20);
// Position it:
mc.x = original_x;
mc.y = original_y;
addChild(mc);
// Loop:
function iterate($e:Event = null):void
{
// Move the mc by a random amount related to x/y inc
mc.x += (Math.random() * (2 * x_inc))/2;
mc.y += (Math.random() * (2 * y_inc))/2;
// If the distance from the origin is greater than bounds:
if((Math.abs(mc.x - original_x)) > bounds)
{
// Reverse the direction of travel:
x_inc == 5 ? x_inc = -5 : x_inc = 5;
}
// Ditto on the y axis:
if((Math.abs(mc.y - original_y)) > bounds)
{
y_inc == 5 ? y_inc = -5 : y_inc = 5;
}
}
// Start the loop:
addEventListener(Event.ENTER_FRAME, iterate);
This should get you started. I'm sure there are any number of other ways to do this with formal trig, but this has the benefit of being very simple, and just an extension of your existing method.

Zooming an object based on mouse position

I've got a large large Sprite, and I want to zoom in and out keeping mouse position as the pivot point, exactly how google maps or the photoshop zoom works, when you rotate the mouse wheel.
To archieve the zoom effect I tween the scaleX and scaleY properties, but the fixed point is (0,0), while i need to be the mouse position (that changes everytime, of course).
How can I change this?
thanks.
I just did a Google search for "zoom about an arbitrary point" (minus the quotes) and the first result looks promising.
You have to take the original offset out the scale and then reapply it at the new scale. I've done this myself but don't have the code to hand right now, so I'll see if I can dig it out and post it later.
The pseudo code for this is something like this (from memory):
float dir = UP ? 1 : -1;
float oldXscale = Xscale;
float oldYscale = Yscale;
Xscale += dir * increment;
Yscale += dir * increment;
newX = (oldX - Xoffset) / Xscale;
newY = (oldY - Yoffset) / Yscale;
Xoffset += (newX * oldXscale) - (newX * Xscale);
Yoffset += (newY * oldYscale) - (newY * Yscale);
Anything not declared is a "global"
Found out by searching the right words on google ...
This link explains the Affine Transformations http://gasi.ch/blog/zooming-in-flash-flex/
and so I can Tween the transformation Matrix:
var affineTransform:Matrix = board.transform.matrix;
affineTransform.translate( -mouseX, -mouseY );
affineTransform.scale( 0.8, 0.8 );
affineTransform.translate( mouseX, mouseY );
var originalMatrix:Matrix = board.transform.matrix;
TweenLite.to(originalMatrix, 0.7, {a:affineTransform.a, b:affineTransform.b, c:affineTransform.c, d:affineTransform.d, tx:affineTransform.tx, ty:affineTransform.ty, onUpdate:applyMatrix, onUpdateParams:[originalMatrix]});
:-)
private static function onMouseWheel(event:MouseEvent):void {
var zoomAmount:Number = 0.03;
if (event.delta < 0)
zoomAmount *= -1;
var x:int = largeLargeSprite.mouseX;
var y:int = largeLargeSprite.mouseY;
largeLargeSprite.scaleX += zoomAmount;
largeLargeSprite.scaleY += zoomAmount;
largeLargeSprite.x -= x * zoomAmount;
largeLargeSprite.y -= y * zoomAmount;
}