Moving an object based on the direction it is facing:

Started by
6 comments, last by Aztral 15 years, 1 month ago
Hi, I'm working on a space game similar in style to Escape Velocity if anyone is familiar with that. I have a question regarding how to properly move an object based on the direction it is facing. Right now I am moving objects by simply modifying their x,y by setting a dx and dy and adding this to the current x and y values. I currently have an array of ship images, call it shipArray. shipArray[0] is the image of a ship facing straight up, that is it is pointed at an angle of 90 degrees. shipArray[1] simply subtracts 10 degrees to the ships facing angle, so shipArray[1] is a ship facing at an angle of 80 degrees. My code for dealing with this looks like this: (I'm not sure if you even need this to answer my question) when _rotating == 1, rotate clockwise when _rotating == 0, no rotation when _rotating == -1, rotate counter-clockwise

void Ship::draw(SDL_Surface* s){
  if(!_rotating){
    if(_animate){
      if(_currentFrame >= _imgs->getNumImgs()){
	_currentFrame = 0;
	if(!_cycle)
	  _animate = 0;
      }
      setCurrentFrame(_currentFrame);
    }
  }
  
  if(_rotating == 1){
    if(_animate){
      _currentFrame++;

      if(_currAngle != 0)
	_currAngle -= 10;
      else
	_currAngle = 350;
	
      _currAngle = _currAngle%360;

      if(_currentFrame >= _imgs->getNumImgs()){
	_currentFrame = 0;
	if(!_cycle)
	  _animate = 0;
      }
      setCurrentFrame(_currentFrame);
    }
  }

  if(_rotating == -1){
    if(_animate){
      if(_currentFrame == 0)
	_currentFrame = _imgs->getNumImgs()-1;
      else
	_currentFrame--;

      _currAngle += 10;
      _currAngle = _currAngle%360;

      if(_currentFrame >= _imgs->getNumImgs()){
	_currentFrame = 0;
	if(!_cycle)
	  _animate = 0;
      }
      setCurrentFrame(_currentFrame);
    }
  }
  
  SDL_BlitSurface(_imgs->getImage(_currentFrame), &_src, s, &_dest);
}


So, on to actually moving the ship, the way I came up with to do this (well, I'm sure I didn't actually come up with it, but I thought I was being fairly creative =)) is by setting dx to the cosine of _currAngle and dy to the sine of _currAngle, and then multiplying each by whatever speed I want the ship to move at. I've found that this method works fairly well when the ship speed is sufficiently large - 10+ seems to make it work, but if I want a ship to be slow, for example with a speed of only one or two, then the ship movement does not quite appear to be what it should. I assume this is because of some rounding or somewhere in my code where I'm doing integer + double arithmetic (in which case I apologize for being so dumb!). This is my function for calculating and fetching the DXDY. I made the if statements because it seems cos() and sin() don't return actual values of zero rather a number very close to zero, which I found when multiplied by larger numbers can skew the travel of the object.

void Ship::calcDXDY(double* currDX, double* currDY){
  double PI = 3.14159265;
  if(_currAngle == 90 || _currAngle == 270){
    *currDX = 0;
    *currDY = (sin(_currAngle*PI/180))*SHIP_SPEED;
  }
  else if(_currAngle == 0 || _currAngle == 180){
    *currDX = (cos(_currAngle*PI/180))*SHIP_SPEED;
    *currDY = 0;
  }
  else{
    *currDX = (cos(_currAngle*PI/180))*SHIP_SPEED;
    *currDY = (sin(_currAngle*PI/180))*SHIP_SPEED;
  }
}


So, I guess my question is whether or not there is a better method to go about determining dx and dy, or if there is something that I can do so that when objects are traveling slowly their trajectories don't appear quite as skewed. - Thanks.
Advertisement
Im not sure what you mean by "skewed" so i cant really help with the main problem. However, i do have a few suggestions to clean up your code a little if you dont mind me sharing them with you.

You have made a PI a local variable to a single function, this is generally a global constant, maybe you would like to change it to that?
instead of ...( double *currDX, ... ) perhaps you could make it a referance instead, it looks like your only using the value of what currDX points to and dont actually need the address of what it points to ( then you wont have to keep typing an asterisk each time you want to set the value of whatever currDX/DY points to )
Why are you using fixed angles for your ship? can you not rotate a single image by the current direction? this would reduce code and the amount of assets needed, if you still want a fixed set of angles ( 0, 10, 20, 40, 50,.. n ) i think there is a way to make such a restriction using the modulus operator.
In calcDX( ... ) if you have a restricted angle then you should only need to write:
currDX = (cos(_currAngle*PI/180))*SHIP_SPEED;
currDY = (sin(_currAngle*PI/180))*SHIP_SPEED;

once, eliminating the need for if statements and repetition.
Is SHIP_SPEED a global constant? if so, shouldnt it be a part of the Ship class? and also, why not make it non-constant so you can implement acceleration for the ship which is more natural.

Well, theres a few suggestions there for you. You dont have to take onboard what ive said, but its there if you feel like it.

~Reegan :)
Quote:Original post by Reegan
Im not sure what you mean by "skewed" so i cant really help with the main problem. However, i do have a few suggestions to clean up your code a little if you dont mind me sharing them with you.

You have made a PI a local variable to a single function, this is generally a global constant, maybe you would like to change it to that?
instead of ...( double *currDX, ... ) perhaps you could make it a referance instead, it looks like your only using the value of what currDX points to and dont actually need the address of what it points to ( then you wont have to keep typing an asterisk each time you want to set the value of whatever currDX/DY points to )
Why are you using fixed angles for your ship? can you not rotate a single image by the current direction? this would reduce code and the amount of assets needed, if you still want a fixed set of angles ( 0, 10, 20, 40, 50,.. n ) i think there is a way to make such a restriction using the modulus operator.
In calcDX( ... ) if you have a restricted angle then you should only need to write:
currDX = (cos(_currAngle*PI/180))*SHIP_SPEED;
currDY = (sin(_currAngle*PI/180))*SHIP_SPEED;

once, eliminating the need for if statements and repetition.
Is SHIP_SPEED a global constant? if so, shouldnt it be a part of the Ship class? and also, why not make it non-constant so you can implement acceleration for the ship which is more natural.

Well, theres a few suggestions there for you. You dont have to take onboard what ive said, but its there if you feel like it.

~Reegan :)


Thanks for the response!

By skewed I mean that with a low SHIP_SPEED, certain angle values seem to behave strangely. For example with a ship speed of 2, when the ship is facing at a 90 degree angle and I move it, it move up and to the right rather than straight up, whereas when I use a ship speed of 10, it moves straight up.

I should indeed make PI a global constant - consider it done.

I wrote my calcDXDY function like that because it allows me to retrieve those values from different objects. For example (I think) in the future should I be writing a different ship's AI and how it behaves is based on my own ship's current trajectory, it can retrieve my current trajectory by doing:

int playerdx, playerdy;
_playerShip->calcDXDY(&playerdx, &playerdy)
---then playerdx and playerdy will contain my ship's dx dy.

Similarly I am currently moving via a main game loop not in the Ship object, and I retrieve my ship's dx and dy that way. I realize my code might not be as optimized as it could be at the moment but I'm just trying to get some of these basics down at the moment.

As far as why I am using fixed angles for my ship, I'm not entirely sure what you mean by "rotating a single image by the current direction." I don't necessarily want to use fixed angles, but I'm not sure how to go about simply rotating an image. I simply pre-generated a number of ship images at rotations of 10 degrees.

Regarding the if statements in calcDX, you are right. I'm not sure why I was having issues before and put them in - but taking them out works fine.

I also plan to implement things like acceleration, different rates of ship rotation, etc. but again I'm just trying to get the basics down for now.

Thanks again for the response.
Hey,

I think you are complicating it a lot. This is how I would do it if I were doing something similar to what you are doing.

#define RADIANS 3.14/180.0f;if(currAngle < -180.0f)    currAngle += 360.0f;else if(currAngle > 180.0f)   currAngle -= 360.0f;float rad = currAngle * RADIANS;float dx = cos(currAngle) * 5.0f;float dy = sin(currAngle) * 5.0f;


There is an another way to
Vector3 forward = GetDirection();newPosition = forward * speed;SetPosition(newPosition);In this case you define up, forward and right vector somehow and work your way around. It is possible to get these vectors from the matrix transforms of the ship.


[/source]
The more applications I write, more I find out how less I know
Thanks I think I'll try rewriting the code to be a bit simpler.
Well, I have a new question, and I guess that this thread is as good a place as any to ask it... so here goes.

The way I am drawing the background is by essentially keeping the ship and the background in the same location, and just moving the stars and planets relative to the ship DX, DY, thus making it look like the ship is moving through space. I don't know if this is at all a good way to do it, but it is what I came up with. It does seem rather inefficient so I'm open to suggestions there.

Here is where my question comes in. I was stumped for a long, long time as to why my stars seemed to clump whenever they reached the edge of my screen window. My screen window is bounded by an SDL_Rect _bounds, where _bounds.w is equal to the width of my window, and _bounds.h is height of my window. The way I have created the system in space is to create a 20,000 by 20,000 grid of stars that move. I think that my problem is with my _bounds window, because with that window my ship is *always* being drawn within a grid that has a top left corner of (0, 0). Whenever a star is being drawn below the top of the window, it's current y coordinate is greater than zero, and therefore when I add my dy value to it (which is a double, and thus part of the problem) it adds the coordinate and then rounds the new y coord value down. So if my current y coord was 200 and I add a dy to it of -.8, it rounds to 203 and the star appears to be moving correctly. However, if a star is on the top of the window and thus has a current y coordinate of 0, and -.8 is added to it, 0 + -.8 results in an integer value of 0, and the star will not properly move. I cannot convert my x,y coordinates to doubles because SDL_Rect takes integer parameters.

So, my question: First, is there a better way to go about drawing a simple star background so that this problem doesn't occur at all? Second, if this is a reasonable way to do it, how can I go about fixing this problem, because I'm kind of out of ideas at this point. Is there a way to make an integer value round UP rather than down?

Thanks again.
Hi Aztral,

Quote:The way I am drawing the background is by essentially keeping the ship and the background in the same location, and just moving the stars and planets relative to the ship DX, DY, thus making it look like the ship is moving through space. I don't know if this is at all a good way to do it, but it is what I came up with. It does seem rather inefficient so I'm open to suggestions there.


Store the fixed positions of the stars and then calculate the screen positions each frame, depending on the ship's position.

Quote:Is there a way to make an integer value round UP rather than down?


Pick one system and stick to it. Either store all the positions using floats, and calculate all movement in floats, or do everything with integers. I would recommend using floating point numbers for everything if your ship movement involves rotation. Store the star positions, ship position and ship movement with floats. Forget integers until the moment you need to draw sprites on screen.

If you want to make floats round to integers correctly (i.e. for x < 1.5 --> x == 1 and for x>= 1.5 --> x == 2) then add 0.5 to the floating point number before you round it.

Hope this is of some use!
Thank you very much Shaolinspin - words cannot express my contentness having finally fixed this. Turns out I needed to change the x,y coordinates for a number of objects in my drawable object hierarchy from ints to floats and that did the trick!

Thanks again!

Dang learning is fun.

This topic is closed to new replies.

Advertisement