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
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  2D movement: How do I do that?  (Read 4126 times)
0 Members and 1 Guest are viewing this topic.
Offline 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.
Can someone please help me out?
Thanks!
Offline 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;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.
Offline 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  Grin
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline 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  Grin
Delta time is the difference between the current time and the time the last update was made.

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

Offline 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_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 Sad
Offline 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.
Offline 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.
Offline 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 Smiley

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.
Offline 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.
Offline 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 JavaFX
by philfrei
2019-12-19 19:35:12

Java Gaming Resources
by philfrei
2019-05-14 16:15:13

Deployment and Packaging
by philfrei
2019-05-08 15:15:36

Deployment and Packaging
by philfrei
2019-05-08 15:13:34

Deployment and Packaging
by philfrei
2019-02-17 20:25:53

Deployment and Packaging
by mudlee
2018-08-22 18:09:50

Java Gaming Resources
by gouessej
2018-08-22 08:19:41

Deployment and Packaging
by gouessej
2018-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
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!