Sign in to follow this  
CaseyHardman

My Mathematical Crisis: A Move Towards Point Function

Recommended Posts

CaseyHardman    2765
Sorry, but this is going to be a detail-heavy and cumbersome post...but trust me, there has actually been some code written for it already...
I am and have been trying to make a Move Towards Point function for a while. It's for a 2D game, so we're dealing with only X and Y. I'm using ActionScript 3 with the program FlashDevelop. So yes, it's a Flash game.


I got it to work, but not perfectly. I made it so that when the function was called it continued moving you towards the specified point (which would be arguments in the function called pointx and pointy) until you reached that point, and at the speed you specified (also an argument).

So, declaring the function looks like this:

public function moveTowardsPoint(affected:Object, pointx:int, pointy:int, speed:Number):void

Affected is the object that will move. pointx and pointy are the x and y positions that the object will be moving towards. Speed is roughly how much x/y you'll 'move' per frame. By the way, the framerate is 30 FPS, if it matters.

So, the main problem right now is the moving. It doesn't move perfectly towards that point. If I called: moveTowardsPoint(object, 28, 124, 4); then I would have a problem. The problem is pointx (28) is lower than pointy (124). This means the object would finish moving it's X to 28 before it finished moving its Y to 124. This would be criminal to leave be for the kind of game I'm making, as it would cause players to cross places they wouldn't want to if they move diagonally.

Before I show you the code, let me tell you what some of the variables mean. First, yDistanceToTravel and xDistanceToTravel. This is pretty much the object that will be moving's X/Y - or + the pointx, or the other way around. A set of 'Ifs' make sure it's always how much x (or y) it needs to either gain or lose. So, they're both how much Y or X the object will need to gain or lose to finish reaching its point. The name itself is pretty explanatory, actually.

Second, the variable 'TotalFramesToTravel'. Before I set this variable, I figured out which y/xDistanceToTravel is higher than the other, if any. The higher one is the total amount of distance we'll need to move. Another set of 'Ifs' assures we know which one is the highest. We then set the TotalFramesToTravel to the highest distance to travel divided by the speed.

Ok, time for code. There are lots of Ifs involved, so stay with me...here's the part of the function that makes you move (the other parts work fine, so it's easier to explain them than to list their code). Also, everything you see will be happening once per frame until you reach the specified point, where it will then discontinue and the function is finished.

[code]
if (yDistanceToTravel > xDistanceToTravel) //In other words, if you need to move up/down further than you need to move left/right.
{
if (affected.x > pointx) //affected.x or affected.y means the affected object's X or Y. This specific 'if' means if 'affected' needs to move left.
{

for (var i:Number = 0; i < speed && affected.x != pointx; i += 0.10) //Assuming the speed is 4, the code in the braces will happen 40 times.
{
affected.x -= (0.10 - (((yDistanceToTravel - xDistanceToTravel) / TotalFramesToTravel) / (speed * 10))); //Yeah, I'll have to explain that later...

}
}

//Note: the reason for breaking it up like this and using a 'for' instead of just moving it by the speed is to make sure you don't skip over your destination and therefore get caught in continuous movement.

if (affected.x < pointx) //If the object needs to move right to reach pointx.
{
for (var i:Number = 0; i < speed && affected.x != pointx; i += 0.10) //That same for again.
{
affected.x += (0.10 - (((yDistanceToTravel - xDistanceToTravel) / TotalFramesToTravel) / (speed * 10))); //Once again, I'll explain this in a bit.
}
}
}
else //This else pretty much moves you regularly. It's only used in the situation where xDistanceToTravel is higher than yDistanceToTravel, or they're both precisely the same.
{
if (affected.x > pointx)
{
for (var i:Number = 0; i < speed && affected.x != pointx; i += 0.10)
{
affected.x -= 0.10;
}
}
if (affected.x < pointx)
{
for (var i:Number = 0; i < speed && affected.x != pointx; i += 0.10)
{
affected.x += 0.10;
}
}
}

if (xDistanceToTravel > yDistanceToTravel) //This is the same as the If we saw at the start of the code. It means "if you need to move further right/left than you do up/down".
{
if (affected.y > pointy) //So from this point on, the rest of the code is pretty much the same but with every X either replaced or switched positions with a Y.
{
for (var i:Number = 0; i < speed && affected.y != pointy; i += 0.10)
{
affected.y -= (0.10 - (((xDistanceToTravel - yDistanceToTravel) / TotalFramesToTravel) / (speed * 10)))
}
}
if (affected.y < pointy)
{
for (var i:Number = 0; i < speed && affected.y != pointy; i += 0.10)
{
affected.y += (0.10 - (((xDistanceToTravel - yDistanceToTravel) / TotalFramesToTravel) / (speed * 10)))
}
}
}
else
{
if (affected.y > pointy)
{
for (var i:Number = 0; i < speed && affected.y != pointy; i += 0.10)
{
affected.y -= 0.10;
}
}
if (affected.y < pointy)
{
for (var i:Number= 0; i < speed && affected.y != pointy; i += 0.10)
{
affected.y += 0.10;
}
}


}
[/code]


Phew, what a mess. Who knew I'd have to make a huge monster of a function to move this there over this amount of time, right?

So basically, I use 'for's instead of just saying 'affected.x += speed' to make sure it doesn't skip over its destination and get caught moving forever. For example, if I'm moving 4 X per second and I'm at 128 X, trying to reach 129 X, then I'll go from 128 to 132...and skip right over the promised land. Forever caught in motion. That's why I move the object gradually.

Now, this little dread of an equation:

affected.x += (0.10 - (((yDistanceToTravel - xDistanceToTravel) / TotalFramesToTravel) / (speed * 10)));

Parenthesis EVERYWHERE!! And yes, that's just one of 4 of those lines with different letters. There's one for moving left, moving right, moving up and moving down, all with slight differences that you'll find throughout the code...

Okay, here's what led me to using that formula.
Assuming yDistanceToTravel is 128 and xDistanceToTravel is 48. This means we're moving for a total of 20 frames until we get to our point (yDist - xDist / speed). So Y needs to move more than X, that's already known. My solution to this is making X move slower than Y.
So: yDist -xDist is how much more Y needs to move than X. This also means it's how much we must divisively reduce from the speed of the X movement. For a while, I had each movement 'for' like this:


for (var i:int = 0; i < speed && affected.x != pointx; i ++)
{
affected.x += (1 - (((yDistanceToTravel - xDistanceToTravel) / TotalFramesToTravel) / speed));
}
But I thought it would be better to move it even more so gradually than 1 at a time...maybe that was a mistake? Can you even be at X position "1.5" or "1.2" or something similar? Maybe I'm...an...an...an idiot?

Pretty much what I mean by this is that the movement you make with X needs to be reduced by however much MORE Y is traveling. But since you move 4 (in other words, 'speed') per frame, you need to do it by 'however much more / TotalFramesToTravel'. But since I'm also divisively moving the speed so it doesn't skip over and continue moving, it must be 'however much more / TotalFramesToTravel / speed'. By however much more I mean yDist - xDist.
But it didn't work, not when I had it moving 1 or when I had it moving 0.10. There was still a slight bit of cutoff, of extra distance the X needed to move or not move. I figured this was because I was dividing one value by another value and then dividing that value by another..which could cause many openings for decimals, such as 1.1, 1.2, etc. So that's why I went from moving 1 at a time to 0.10 at a time (therefore moving from 'gradually' to 'super-gradually'...also known as 'The Twilight Zone').


I bet I just made the first non-spam thread on GameDev with this many words in it...sorry, but I'm really stumped.

If you can suggest a better way to move in the first place, go ahead, but I'm pretty much trying to find out what's wrong with that formula. That is, this one:
affected.x += (1 - (((yDistanceToTravel - xDistanceToTravel) / TotalFramesToTravel) / speed));

and/or this one, assuming on whether you're doing it gradually or super-gradually...:

affected.x += (0.10 - (((yDistanceToTravel - xDistanceToTravel) / TotalFramesToTravel) / (speed * 10)));


Alright, there you have it...I would laugh if there was some supremely easy way to do this that I overlooked (other than switching to Game Maker and using their perfect one moveTowardsPoint function, of course...)

If I missed something, committed a bannable crime, etc. please let me know.
And finally, thank you very much for any efforts you've put out to helping me, whether it's just reading through this or reading and replying.

Share this post


Link to post
Share on other sites
SuperVGA    1132
Hi GHMP,

I think you're making the problem somewhat harder than you have to. I know, I've been doing that a lot earlier. ;)
How about [url="http://snipd.net/2d-and-3d-vector-normalization-and-angle-calculation-in-c"]normalizing[/url] a movement direction vector from the vector between the player's current position, and the target position.
Given the player's position in origo: P=[0.0,0.0]
and the target position: T=[[color="#1C2837"][size="2"]28.0, 124.0][/size][/color]
[color="#1C2837"][size="2"]Also, the step size for your velocity Vp = s / t : s = 4.0[/size][/color]
[color="#1C2837"][size="2"]We normalize the vector TP like this: Pdir = [/size][/color][color="#1C2837"][size="2"]T-P / |TP|, here: [/size][/color][[color="#1C2837"][size="2"]28.0, 124.0] / [/size][/color][color="#1C2837"][size="2"]127.12 = [0.22, 0.97][/size][/color]

[color="#1C2837"][size="2"]Now for every t, the player should move so that P = P + Vp * Pdir, [[/size][/color][color="#1C2837"][size="2"]0.88, 3.90], where t is the time taken to progress s units.[/size][/color]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this