Java-Gaming.org Hi !
 Featured games (91) games approved by the League of Dukes Games in Showcase (803) Games in Android Showcase (237) games submitted by our members Games in WIP (867) games currently in development
 News: Read the Java Gaming Resources, or peek at the official Java tutorials
Pages: [1]
 ignore  |  Print
 2D movement: How do I do that?  (Read 4126 times) 0 Members and 1 Guest are viewing this topic.
PolarBearOC

Junior Newbie

 « Posted 2015-12-11 16:16:54 »

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:
 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 `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.
Thanks!
longshorts

Senior Newbie

Medals: 2

 « Reply #1 - Posted 2015-12-11 20:32:52 »

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:
 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21 `private Vector2 target;private Vector2 position;spublic boolean moveObject(float deltaTime){//Work out the distance to travelfloat 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 loopposition.x += x_velocity * deltaTime;position.y += y_velocity * deltaTime;//Check if you have moved past the target by comparing new distance with the old distanceif((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 targetposition.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.
PolarBearOC

Junior Newbie

 « Reply #2 - Posted 2015-12-11 21:22:38 »

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
Archive
 « Reply #3 - Posted 2015-12-12 00:17:17 »

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
Delta time is the difference between the current time and the time the last update was made.

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

PolarBearOC

Junior Newbie

 « Reply #4 - Posted 2015-12-12 12:55:15 »

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:
 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21 `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.

 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  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63  64  65  66  67  68 `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_velocitymax_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
longshorts

Senior Newbie

Medals: 2

 « Reply #5 - Posted 2015-12-12 14:41:57 »

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.
theagentd
 « Reply #6 - Posted 2015-12-12 15:31:05 »

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

Myomyomyo.
PolarBearOC

Junior Newbie

 « Reply #7 - Posted 2015-12-12 17:17:20 »

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.

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

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.
theagentd
 « Reply #8 - Posted 2015-12-12 21:08:27 »

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 x*x*x*.....*x:
 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 `   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
 1  2  3  4  5 `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.

Myomyomyo.
longshorts

Senior Newbie

Medals: 2

 « Reply #9 - Posted 2015-12-13 13:40:23 »

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.
Pages: [1]
 ignore  |  Print

 Riven (397 views) 2019-09-04 15:33:17 hadezbladez (5280 views) 2018-11-16 13:46:03 hadezbladez (2204 views) 2018-11-16 13:41:33 hadezbladez (5544 views) 2018-11-16 13:35:35 hadezbladez (1150 views) 2018-11-16 13:32:03 EgonOlsen (4584 views) 2018-06-10 19:43:48 EgonOlsen (5462 views) 2018-06-10 19:43:44 EgonOlsen (3119 views) 2018-06-10 19:43:20 DesertCoockie (4015 views) 2018-05-13 18:23:11 nelsongames (4708 views) 2018-04-24 18:15:36
 A NON-ideal modular configuration for Eclipse with JavaFXby philfrei2019-12-19 19:35:12Java Gaming Resourcesby philfrei2019-05-14 16:15:13Deployment and Packagingby philfrei2019-05-08 15:15:36Deployment and Packagingby philfrei2019-05-08 15:13:34Deployment and Packagingby philfrei2019-02-17 20:25:53Deployment and Packagingby mudlee2018-08-22 18:09:50Java Gaming Resourcesby gouessej2018-08-22 08:19:41Deployment and Packagingby gouessej2018-08-22 08:04:08
 java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org