Moving object toward another object in a while loop [solved]

Started by WHAM, Sat 12/03/2011 19:15:52

Previous topic - Next topic

WHAM

Is there a way to do this (I haven't been able to get this kind of behavior with the move command, so I thought I'd ask):

Code: ags

//PSEUDOCODE
String hupu;
int tupu = 0;

while (tupu < 500 && hupu != "taa") {
  // move an object toward another object's coordinates by ONE PIXEL
  if (GetLocationName(object.x, object.y) == "target") {
    hupu = "poo";
  }
  wait(1);
}

Wrongthinker and anticitizen one. Utterly untrustworthy. Pending removal to memory hole.

Wyz

you can directly change the X and Y properties if an object isn't moving. You can check if it is moving, or let it stop moving before you run your code, but yes it's possible. :)

edit:
I just realized you were probably also wondering about the maths so here goes:
Code: ags

float vx = IntToFloat(targer.X - object.X);
float vy = IntToFloat(targer.Y - object.Y);
float n = Maths.Sqrt(vx*vx + vy*vy);
vx = vx / n;
vy = vy / n;

object.X += FloatToInt(vx, eRoundNearest);
object.Y += FloatToInt(vy, eRoundNearest);


edit2:
You're right Khris, corrected it.
Life is like an adventure without the pixel hunts.

Khris

FloatToInt's default rounding is eRoundDown; it should be eRoundNearest in that code, if I'm not mistaken.

WHAM

Thanks for the quick replies! I've got some nasty crap going on in my head and throat ("ooh! Gooooo!"), but I'll get to testing some time in the upcoming week.
Wrongthinker and anticitizen one. Utterly untrustworthy. Pending removal to memory hole.

WHAM


Am I doing something wrong? I decided to use a character instead of an object (cBullet). Below is the script I blatantly copy-pasted from here. For some reason the bullet first travels in a straight line for a while and only then begins to curve towards the target...

You can download the InDev game from www.whamgames.com/downloads/success/POC.rar

To test the bullet script press F, move cursor over target area and press space to shoot. The bullet travels according to the below script and stops if it encounters a wall.

Code: ags

        cBullet.ChangeRoom(player.Room, player.x, player.y-7);
        
        String hupu = "x";
        int tupu = 0;
        Display("SHOT FIRED");
        while (tupu < 500 && hupu != "BLOCK") {
        
          float vx = IntToFloat(GetViewportX() + 160 - cBullet.x);
          float vy = IntToFloat(GetViewportY() + 78 - cBullet.y);
          float n = Maths.Sqrt(vx*vx + vy*vy);
          
          if (vx == 0.00 || vy == 0.00) {
            tupu = 501;
          } else {
            vx = vx / n;
            vy = vy / n;  
          }
          
          cBullet.x += FloatToInt(vx, eRoundNearest);
          cBullet.y += FloatToInt(vy, eRoundNearest);
          
          hupu = Game.GetLocationName(cBullet.x - GetViewportX(), cBullet.y - GetViewportY());
          
          tupu++;
         
          Wait(2);
        }
Wrongthinker and anticitizen one. Utterly untrustworthy. Pending removal to memory hole.

Khris

I guess the initial code is already flawed.

In fact, usually this is done by doing all calculations with floats and only using rounded versions of those to place the object.

Basically:
Code: ags
  float bx = IntToFloat(cBullet.x);
  float by = IntToFloat(cBullet.y);

  while(...) {
    // calculate path, add vx/vy to bx/by
    ...
    cBullet.x = FloatToInt(bx, eRoundNearest);
    cBullet.y = FloatToInt(by, eRoundNearest);
    ...
  }


That way, rounding errors don't accumulate; or, as shown here, supposed sums of small values don't remain 0.

WHAM

I was suddenly reminded why I suck at math... I'll try to see if I can get it working, but at the moment I'm having a hard time even grasping the math and understanding how the script I was provided works.

I'll post back if I make progress!
Wrongthinker and anticitizen one. Utterly untrustworthy. Pending removal to memory hole.

Khris

The math isn't that hard; the first step is to calculate the 2D vector from bullet to target:

x = target.x - bullet.x;
y = target.y - bullet.y;

In other words, calculate the x;y which you have to add to bullet's position to get to target.

A vector's length is square root of (x*x + y*y) since that equals length*length, according to Pythagoras.

In order to shrink the vector to desired length dl, divide both x and y by length (l), then multiply by dl.
dl can be thought of as speed in pixels per frame.
Wyz' code uses a speed of 1, so:

x = x / l;
y = y / l;

The resulting vector points from bullet to target and in this case has a length of one.

The final step is to add it to bullet's position every loop, thus moving it in the direction of target.

WHAM

As far as I can understand this should be correct:
Code: ags

    function Shooty() {
      // First we move the bullet graphic to the player's location and reset variables
      String hupu = "x";
      int tupu = 0;

      cBullet.ChangeRoom(player.Room, player.x, player.y-7);
      
      // Then we reset "hupu", which keeps track of what the bullet is currently on top of
      // And we reset tupu, which makes sure the bullet does not travel indefinitely
      // DEBUG Display("SHOT FIRED");
      // WHILE tupu is less than 500 ie. for a long enough time
      while (tupu < 500) {
        
        // vectorX and vectorY
        float vx = IntToFloat(GetViewportX() + 160 - cBullet.x);
        float vy = IntToFloat(GetViewportY() + 78 - cBullet.y);
        
        float length = Maths.Sqrt((vx*vx) + (vy*vy));
        // Make sure there is no division by zero
        if (vx == 0.00 || vy == 0.00) {
          tupu = 501;
        } else {
          vx = vx / length;
          vy = vy / length;  
        }
        
        cBullet.x += FloatToInt(vx, eRoundNearest);
        cBullet.y += FloatToInt(vy, eRoundNearest);
        
        hupu = Game.GetLocationName(cBullet.x - GetViewportX(), cBullet.y - GetViewportY());
        Display("%s", hupu);
        tupu++;
       
        Wait(2);
        
      }


But: Nope, the bullet still travels in a straight line first and only as it gets closer to its target does it begin to curve towards the correct coordinate.

I stooped so low as to try this:
Code: ags

      cBullet.ChangeRoom(player.Room, player.x, player.y-7);
      cBullet.Move(GetViewportX() + 160, GetViewportY() + 78, eNoBlock, eAnywhere);
      while (cBullet.Moving == true) {
        if (tupu >= 12) {
          hupu = Game.GetLocationName(cBullet.x - GetViewportX(), cBullet.y - GetViewportY());
          Display("%s", hupu); 
        }
        Wait(1);
        tupu++;
      // Add a check to stop the cBullet from moving if it encounters a character or an object with a certain name
      }


That actually works just as needed, save for the fact that I cannot track the bullets location on each pixel of movement unless the movement speed of the bullet is set as "1", which in turn looks too on-screen. If I set the movementspeed higher, the bullet can skip "through" some of the most narrow objects if they meet at an angle.
Wrongthinker and anticitizen one. Utterly untrustworthy. Pending removal to memory hole.

Khris

First of all, avoiding a division by zero is done by making sure that length isn't 0.

Secondly, we're back at the original problem. Like I explained, you have to store the bullet's position in two floats, add vx, vy to those and then put cBullet at the FloatToInts of them. Otherwise, rounding errors accumulate.

WHAM

Ooookay... So far I've just made the variables go haywire.  :o

Code: ags

      // First we move the bullet graphic to the player's location and reset variables
      String hupu = "x";
      int tupu = 0;
     
      cBullet.ChangeRoom(player.Room, player.x, player.y-7);
      
      // VERSION 1 - PYTHAGORAS

      // WHILE tupu is less than 500 ie. for a long enough time
      while (tupu < 500) {
        
        // VX and VY
        float vx = IntToFloat(GetViewportX() + 160 - cBullet.x);
        float vy = IntToFloat(GetViewportY() + 78 - cBullet.y);
        
        // Bullet's location X and Y
        float bx = IntToFloat(cBullet.x);
        float by = IntToFloat(cBullet.y);

        float length = Maths.Sqrt((vx*vx) + (vy*vy));
        // Make sure there is no division by zero
        if (length == 0.00) {
          tupu = 501;
        } else {
          vx = vx / length;
          vy = vy / length;  
        }

        // Addition
        bx = bx + vx;
        by = by + vy;
       
        cBullet.x += FloatToInt(bx, eRoundNearest);
        cBullet.y += FloatToInt(by, eRoundNearest);
        Display("%d %d", cBullet.x, cBullet.y);
        
        if (tupu > 15) {
          hupu = Game.GetLocationName(cBullet.x - GetViewportX(), cBullet.y - GetViewportY());
          Display("%s", hupu);
        }
        tupu++;
       
        Wait(2);
      } 
Wrongthinker and anticitizen one. Utterly untrustworthy. Pending removal to memory hole.

Khris

You added bx/by to the bullet's position; bx/by IS the bullet's position.

Code: ags
        cBullet.x = FloatToInt(bx, eRoundNearest);
        cBullet.y = FloatToInt(by, eRoundNearest);



WHAM

Whoops, remnants of previous attemps I had failed to clean up.
However, even with these corrections the issue remains: if the target coordinates are, lets say 60 pixels to the right and 15 pixels down from the starting point, the bullet travels directly to the right for about 35-40 pixels and only then begins to curve towards its final target.
Wrongthinker and anticitizen one. Utterly untrustworthy. Pending removal to memory hole.

Khris

Just saw the other error:

Move this above the while loop:

Code: ags
 // Bullet's location X and Y
        float bx = IntToFloat(cBullet.x);
        float by = IntToFloat(cBullet.y);


What's the point of having separate floats to avoid rounding errors if you set them to the rounded values every loop...? ;)

WHAM

Holy crap, it seems to work!   ;D

You people have once again saved my day and future games! Now that I have a working example I can study it and see if I can refine it further.

Thank you!
Wrongthinker and anticitizen one. Utterly untrustworthy. Pending removal to memory hole.

SMF spam blocked by CleanTalk