2D movement: How do I do that?

I’m working on a 2D RTS game for my final project in college, and I’ve run into a major problem with the movement algorithm.
I’ve been looking all around the web and I can’t seem to find something as trivial as moving a label in a certain vector rather than moving it using arrows or in a 45 degree vector.

I managed to find this code, which calculates the angle and velocity correctly:

public void setPath(Point p){
		destination=p;
        if(this.destination == null){
            return;
        } 

        double delta_x = (double) (this.destination.x-this.getX());
        double delta_y = (double) (this.destination.y-this.getY());
        int sign_x = (int)Math.signum(delta_x);
        int sign_y = (int)Math.signum(delta_y);
        double radian_angle = Math.atan2(delta_y, delta_x);
        if(sign_x > 0 && sign_y > 0)
            radian_angle += Math.PI;
        else if(sign_x > 0 && sign_y < 0)
            radian_angle += Math.PI/2;
        else if(sign_x < 0 && sign_y > 0)
            radian_angle += 3*Math.PI/2;

        System.out.println("Delta X: "+delta_x);
        System.out.println("Delta Y: "+delta_y);
        System.out.println("Angle: "+radian_angle);

        this.x_velocity = this.max_velocity*(float)Math.cos(radian_angle);
        this.y_velocity = this.max_velocity*(float)Math.sin(radian_angle);

        System.out.println("X vel: "+x_velocity);
        System.out.println("Y vel: "+y_velocity);
    }

The problem is that I have no idea how to actually move it using these parameters. Everything I tried ended up in either the label not moving forward (when label’s current x and y < destination x and y), or the label passing by the destination point and missing the stop condition.

I’ve tried everything I could find and I’m absolutely certain there’s a quick and easy solution I’m missing.
Can someone please help me out?
Thanks!

In order to do movement using that code you all you need to do is access the x_velocity and y_velocity variables which should be stored locally in the class I assume. All that class does is print them out to console.
Note I prefer to work in Vector2s, you can do this in x, y if you want.

Then every game loop, do the following:


private Vector2 target;
private Vector2 position;s

public boolean moveObject(float deltaTime){

//Work out the distance to travel
float distance = Math.sqrt(Math.pow(target.x - postion.x, 2) + Math.pow(target.y - postion.y, 2))

//Adjust the position of the game object, factoring in deltaTime for time spent on the game loop
position.x += x_velocity * deltaTime;
position.y += y_velocity * deltaTime;

//Check if you have moved past the target by comparing new distance with the old distance
if((Math.sqrt(Math.pow(target.x - postion.x, 2) + Math.pow(target.y - postion.y, 2))) >= distance){
//Move the position to the target to compensate for overshooting the target
position.x = target.x;
position.y = target.y;
return true;
} else {
return false
}


Run moveObject recursively, when it returns true you should have reached your target. You will need to adjust this by adding a list if your RTS system has waypoints.

Would you care to explain what should deltaTime be? On first glance I thought it’s calculated with “time = section / velocity” but the comment about the game loop confused me…

Oh and thanks for helping ;D

Delta time is the difference between the current time and the time the last update was made.

deltaTime = (System.currentTimeMillis() - lastUpdateTime);

Ok so I’ve tried that in a recursive way, changing the “return false” with a call of the same function. In some cases it simply jumps there, in other cases it prints a StackOverflowError (it goes on an infinite loop, not moving the object for some reason).

That’s the edited code:

private void moveOneStep()
	{
		float deltaTime = (System.currentTimeMillis() - lastUpdate);
		
		//Work out the distance to travel
		float distance = (float) Math.sqrt(Math.pow(destination.x - this.getX(), 2) + Math.pow(destination.y - this.getY(), 2));
		System.out.println(distance);
		
		//Adjust the position of the game object, factoring in deltaTime for time spent on the game loop
		this.setLocation((int)(this.getX()+x_velocity*deltaTime), (int)(this.getY()+x_velocity*deltaTime));
		System.out.println(this.getLocation());

		//Check if you have moved past the destination by comparing new distance with the old distance
		if((Math.sqrt(Math.pow(destination.x - this.getX(), 2) + Math.pow(destination.y - this.getY(), 2))) >= distance){
		//Move the position to the destination to compensate for overshooting the destination
		this.setLocation(destination.x, destination.y);
		return;
		} else {
		moveOneStep();
		}
	}

I’ve tried making a different function for motion, and it works very well in most cases, except in some where it decides to move only in one axis then jump into the destination when it reaches a greater distance than the initial.

private void moveOneStep(double distance)
	{
		double magnitude=1;
		//velocity ratio
		double x_offset=(double)(this.getX()), y_offset=(double)(this.getY());
		//offset is the distance that the ship has already moved. it adds up to see, for example, for how much traveled x should it travel y.
		int sign_x = (int) Math.signum(destination.getX()-this.getX()), sign_y = (int) Math.signum(destination.getY()-this.getY());
		//signs, in order to move both forward and backwards and not only backwards
		boolean isMagniX=false;
		//is the magnitude applied on X? else, apply on Y.
		if (x_velocity>y_velocity)
		{
			magnitude = y_velocity/(x_velocity==0?y_velocity:x_velocity);
			//calculate magnitude for y
		}
		else if (x_velocity<y_velocity)
		{
			magnitude = x_velocity/(y_velocity==0?x_velocity:y_velocity);
			isMagniX=true;
			//calculate magnitude for x
		}
		for (int i=0; i<10; i++)//for number of frames per 100 milisec
		{
			if (x_velocity>max_velocity)
				System.err.println(x_velocity);
			if (y_velocity>max_velocity)
				System.err.println(y_velocity);
			if (isMagniX)
			{
				y_offset+=1.0D*sign_y;
				x_offset+=magnitude*sign_x;
				//add up the offsets with magnitude for x
				System.out.println("offset X:"+x_offset+"; offset Y: "+y_offset);
			}
			else
			{
				x_offset+=1.0D*sign_x;
				y_offset+=magnitude*sign_y;
				//add up the offsets with magnitude for y (if not needed, in which case x_velocity=y_velocity [45 degrees]: magnitude = 1 and won't affect the motion)
				System.out.println("offset X: "+x_offset+"; offset Y: "+y_offset);
			}
			this.setLocation((int)(x_offset), (int)(y_offset));
			System.out.println("not in range: "+this.getLocation());
			//finally, update the object's location
			if((Math.sqrt(Math.pow(destination.getX() - this.getX(), 2) + Math.pow(destination.getY() - this.getY(), 2))) >= distance)
				break;
			try {
				Thread.sleep(10);
				//wait for 10ms (10ms*10=100ms)
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		setPath(destination);
		//reset path in case of drift
		double newdistance = Math.sqrt(Math.pow(destination.getX() - this.getX(), 2) + Math.pow(destination.getY() - this.getY(), 2));
		//calculate current distance
		if((Math.sqrt(Math.pow(destination.getX() - this.getX(), 2) + Math.pow(destination.getY() - this.getY(), 2))) >= distance)
		{
			//Move the position to the destination to compensate for overshooting the destination
			System.out.println((Math.sqrt(Math.pow(destination.getX() - this.getX(), 2) + Math.pow(destination.getY() - this.getY(), 2)))+"   "+distance);
			this.setLocation(destination.x, destination.y);
			return;
		}
		//if it arrived, stop recursion
		moveOneStep(newdistance);
		//recursion
	}

Neither of them works properly, so again I’m calling for help :frowning:

You don’t want to do this recursively unless you want the unit to instantaneously move to the target. You want only call the method once per game loop for each unit. Otherwise it will do all the movement before anything renders to the screen.

Don’t use pow(…, 2). It’s slow as hell. Just multiply the value by itself.

Ok I get it, so each unit will move a certain amount per update loop.
Regarding my method, I wanted to apply course correction since I noticed there’s always a slight drift as I cast to int. I have lots of miscalculations though… I suppose I’ll continue experimenting with your method once I’ve done working on the update loop.
Thanks a lot for the help :slight_smile:

As for the usage of pow, I absolutely aggree, even though it really doesn’t matter since the game won’t require massive processing power.

Hmm, turns out pow(x, 2) is actually converted to x*x, even with strictfp enabled. o_O Looks like there’s a fast path for squaring specifically:

Comparing pow(x, power) with xxx*…*x:


	public strictfp static long testSqr() {
		long startTime = System.nanoTime();

		for(int i = 0; i < n; i++){
			float input = x[i];
			float result = input;
			for(int j = 1; j < power; j++){
				result *= input;
			}
			y[i] = result;
		}
		
		return System.nanoTime() - startTime;
	}

	public strictfp static long testPow() {
		long startTime = System.nanoTime();

		for(int i = 0; i < n; i++){
			double input = x[i];
			y[i] = (float)Math.pow(input, power);
		}
		
		return System.nanoTime() - startTime;
	}

power 1:
Sqr: 0.515 ms
Pow: 70.034 ms

power 2:
Sqr: 0.884 ms
Pow: 2.274 ms

power 3:
Sqr: 1.507 ms
Pow: 70.202 ms

Pow stays constant after that.

No idea what’s going on there. It doesn’t seem like it’s a hard-coded thing either, since as soon as the exponent is exactly 2 the function speeds up massively. So it’s actually doing something like


if(power == 2){
    return value*value;
}else{
    //do proper calculation
}

Regardless, it is a very good practice to keep in mind to only use pow() when you really need it. Even calculating pow() with an integer power can be done in log(exponent) time, which in pretty much every single realistic case will be faster than Math.pow(). Only ever use it if you have a float/double exponent which actually isn’t an integer.

Huh, never knew about the speed of that. I usually get libgdx to handle a lot of those math functions automaticly. Cheers for the pointer.