Amiga.org

Coffee House => Coffee House Boards => CH / Science and Technology => Topic started by: motorollin on August 27, 2007, 08:03:16 PM

Title: Moving a sprite in a specified angle
Post by: motorollin on August 27, 2007, 08:03:16 PM
In C++/SDL, how can I tell a sprite to move in a specific angle, in degrees? Currently I only know how to move it a certain number of pixels on the x or y axis, which doesn't really help when I want to move it at an angle of, say, 30 degrees.

Note this is moving in a straight line at that angle, not rotating the sprite.

--
moto
Title: Re: Moving a sprite in a specified angle
Post by: Karlos on August 27, 2007, 08:42:51 PM
Two possible approaches:

1) Use component velocities (velocities in X and Y axes). You can work this out from taking the sine and cosine of the angle and using them to factor your sprite's overall speed into the vX and vY components (which will allow you to position the sprite at any time along the trajectory).

2) Use a bresenham style line drawing algorithm to calculate your sprite position over successive iterations. This is only really useful if your sprite moves a fixed distance every frame.

Since you know how to move the sprite in X and Y, you could use either of the above. I'd go for (1) myself.
Title: Re: Moving a sprite in a specified angle
Post by: motorollin on August 27, 2007, 09:14:00 PM
Thanks Karlos! Here's what I've knocked together in the move() function:

if ( x <= 20 ) if ( xVel < 0 ) xVel = -xVel;
if ( y <= 20 ) if ( yVel < 0 ) yVel = -yVel;
       
if ( x >= 620 ) if ( xVel > 0 ) xVel = -xVel;
if ( y >= 440 ) if ( yVel > 0 ) yVel = -yVel;

x+=xVel*cos(angle);
y+=yVel*sin(angle);



This does work, and makes the sprite move in the angle specified by int angle, and bounce off the edges of the screen if it reaches the edge of the playfield. However, if I have lots of the same sprite (a vector of the same class) lots of the sprites seem to go off the edge of the screen instead of bouncing back in to the playfield :-?
Title: Re: Moving a sprite in a specified angle
Post by: Karlos on August 27, 2007, 09:33:07 PM
Strange. You're not using threads or anything like that are you?

Incidentally, I had something like this in mind for the components:

vX = sprite->speed*cos(sprite->angle);
vY = sprite->speed*sin(sprite->angle);

ie, you only have speed and angle and calculate vX amd vY as required.

I'd probably go for a basic lookup table or something for the sin/cos too if my angle is an integer with a small enough range. Or cache the cos/sin angle values and recalculate only when angle changes. Or both :lol:
Title: Re: Moving a sprite in a specified angle
Post by: motorollin on August 27, 2007, 09:50:13 PM
No threads. It just runs through the vector of sprites and moves/checks/recalculates/shows them one at a time. I'm wondering if my variables are declared as public when they should be private, or vice versa? I'm not sure I fully understand the difference, but if a variable were public might that mean the velocity for all of the sprites was being adjusted the same instead of them each having their own velocity?

--
moto
Title: Re: Moving a sprite in a specified angle
Post by: motorollin on August 27, 2007, 10:06:40 PM
Looks like I had some of the < and > signs the wrong way round. Also I expanded the nested if statements out, and it seems to be working now.

    if ( x <= 20 )
    {
         if ( xVel > 0 )
         {
              xVel = -xVel;
         }
    }
    if ( y <= 20 )
    {
         if ( yVel < 0 )
         {
              yVel = -yVel;
         }
    }
    if ( x >= 620 )
    {
         if ( xVel < 0 )
         {
              xVel = -xVel;
         }
    }
    if ( y >= 440 )
    {
         if ( yVel > 0 )
         {
              yVel = -yVel;
         }
    }

    x+=xVel*cos(angle);
    y+=yVel*sin(angle);



Not sure how this is different to your last example Karlos, but at least it works now :-D

Cheers!

--
moto
Title: Re: Moving a sprite in a specified angle
Post by: motorollin on August 27, 2007, 10:10:51 PM
Hmmm, I borked it again :lol: I found out what is causing it, but I don't know why.

When the enemy is created its initial angle is set. If I set this to, e.g., 40 degrees, then the enemies all stay within the playfield. If I set it to rand() % 350 + 10 (should be an angle betwen 10 and 350 degrees) then they start flying off the screen :-?

--
moto
Title: Re: Moving a sprite in a specified angle
Post by: Karlos on August 27, 2007, 10:13:23 PM
Right. I was looking at the if statements and was pretty sure something weird must be happening, but as it worked in the single sprite case and failed in the many sprite case, simple boundary detection didn't seem the obvious cause ;-)
 
With respect to the calculation stuff, it's different in that you have have already separated the velocities into xVel and yVel which you then multiply by the corresponding component factor (cos(angle) / sin(angle) in the respective direction.

Or in English, you already have separate xVel / yVel which you then multiply by the cos/sin, which is quite different from having single speed which is then used to derive xVel / yVel.

In other words, I'm saying:

xVel = speed*cos(angle)

where as you have

xVel*cos(angle)

...what was xVel initialised with?
Title: Re: Moving a sprite in a specified angle
Post by: motorollin on August 27, 2007, 10:22:08 PM
OIC. xVel and yVel are initialised with enemyspeed, which is in turn initialised at the top of my code, just to make it easy to tweak sprite speeds and have the changes cascade through the code. xVel and yVel end up (currently) with a value of 3.

--
moto
Title: Re: Moving a sprite in a specified angle
Post by: Karlos on August 27, 2007, 10:32:12 PM
Right.

I'd suggest it's more numerically correct to stick with either totally separate 'vectorized' style xVel / yVel (ie no angle) or have speed and angle and derive xVel/yVel from the speed and angle.

In the latter case, the component velocities xVel and yVel, at any time, will always be:

xVel = speed*cos(angle);
yVel = speed*sin(angle);

which you derive just as you need them. For example, you can say, at any time:

sprite->posX += sprite->speed*cos(sprite->angle);
sprite->posY += sprite->speed*sin(sprite->angle);

to reposition the sprite. As an optimisation, if angle doesn't vary much, I'd consider storing the result of cos(angle) and sin(angle), but not xVel/yVel, eg:

sprite->scaleVelX = cos(sprite->angle);
sprite->scaleVelY = sin(sprite->angle);

...

sprite->posX += sprite->speed*sprite->scaleVelX;
sprite->posY += sprite->speed*sprite->scaleVelY;

I'm obviously not taking edge rebound into consideration here, but you get the general idea.


Having just one speed/angle pair per sprite makes things a bit easier to understand. It also allows you to alter the speed and angle (to simulate acceleration etc) with relative ease. The correct coordinates will always be found by the expressions above for any given value of speed/angle.

Also, unless your sprites always all move with the same speed, you'd want to make the speed/angle properties part of the sprite structure.
Title: Re: Moving a sprite in a specified angle
Post by: motorollin on August 27, 2007, 10:42:06 PM
The reason for having two separate velocity variables (xVel and yVel) is for the edge rebound code. If the sprite hits the top or bottom then yVel is made negative, and if it hits the left or right then xVel is made negative.

If I only use one variable for the velocity then I can't do this, since changing the velocity variable would affect the direction on both the X and Y axes.

--
moto
Title: Re: Moving a sprite in a specified angle
Post by: Karlos on August 27, 2007, 10:58:52 PM
True, but remember, you could alter the angle instead.
Title: Re: Moving a sprite in a specified angle
Post by: motorollin on August 27, 2007, 11:01:58 PM
The problem there is that sometimes the sprite crosses a couple of pixels over the edge of the playfield boundary. This is not a problem aesthetically, but it does mean that it keeps having its angle reversed over and over again so gets stuck. At least if I check whether the velocity is going to take the sprite further out of the playfield before changing it then I can stop this from happening.

--
moto
Title: Re: Moving a sprite in a specified angle
Post by: Karlos on August 27, 2007, 11:24:46 PM
Hey, all is fair in love, warfare and game physics ;-)

I remember doing trajectory stuff for objects bouncing over a polygon mesh. Not much fun at all :lol:
Title: Re: Moving a sprite in a specified angle
Post by: motorollin on August 27, 2007, 11:29:13 PM
Ok this works:

    if ( x <= 20 )
    {
        x=21;
        angle=(180-angle);
    }
    if ( y <= 20 )
    {
        y=21;
        angle=(180-angle)-180;
    }
    if ( x >= 620 )
    {
        x=619;
        angle=(180-angle);
    }
    if ( y >= 440 )
    {
        y=439;
        angle=(180-angle)-180;
    }

    x+=pinwheelspeed*cos(angle);
    y+=pinwheelspeed*sin(angle);




But every so often the sprite hits the wall at an angle which means it can't escape for some reason. It's probably due to the fact that certain angle can't be represented precisely at such a low resolution. I think it might be better to have tables of x and y velocities to give a variety of "angles" rather than trying to calculate actual angles.

--
moto
Title: Re: Moving a sprite in a specified angle
Post by: Karlos on August 27, 2007, 11:41:04 PM
As sin / cos use doubles and return doubles, that's unlikely. It's more likely that the conversion to whatever type x and y are discards fractional data for results that would otherwise be less than one, giving a zero x or y velocity.

Strange that you are using degrees as an angular measure for sin and cos. I thought it only worked in radians?

Anyway, if you are going for strict x / y velocities and only ever inverting their signs on collision with a wall, you might as well do away with angles except when firing them off initially.

That is to say, each sprite is initialised with

xVel = speed*cos(angle);
yVel = speed*sin(angle);

and then basically the values dont change for the sprite except for sign flipping on hitting a wall.

Avoid narrow angles by only allowing say a 5 degree granularity for the starting angle.
Title: Re: Moving a sprite in a specified angle
Post by: motorollin on August 27, 2007, 11:50:16 PM
Quote
Karlos wrote:
Strange that you are using degrees as an angular measure for sin and cos. I thought it only worked in radians?

Ohhhhh, that could be my mistake - I was treating the result as degrees :roll: However, this seems to work better anyway:

Pinwheel::Pinwheel()
{
...
xVel=4;
yVel-2;
...
}

Pinwheel::move()
{
    if ( x <= 0 )
    {
        x=1;
        xVel = -xVel;
    }
    if ( y <= 0 )
    {
        y=1;
        yVel = -yVel;
    }
    if ( x >= LEVEL_WIDTH-PINWHEEL_WIDTH )
    {
        x=(LEVEL_WIDTH-PINWHEEL_WIDTH)-1;
        xVel = -xVel;
    }
    if ( y >= LEVEL_HEIGHT-PINWHEEL_HEIGHT )
    {
        y=(LEVEL_HEIGHT-PINWHEEL_HEIGHT)-1;
        yVel = -yVel;
    }
   
    x+=xVel;
    y+=yVel;
}



This works perfectly even with 1000 pinwheels on screen. Only problem is, when I tried changing the values of xVel and yVel to "rand() % 10 + 2" to make them a random number, the pinwheels all moved in the same direction every time. Did I get the rand() syntax wrong?

--
moto
Title: Re: Moving a sprite in a specified angle
Post by: whabang on August 28, 2007, 12:03:45 AM
My mind hurts...
Title: Re: Moving a sprite in a specified angle
Post by: Karlos on August 28, 2007, 12:17:24 AM
C(++) stdlib rand() returns a pseudorandom number between 0 and RAND_MAX, which is implementation defined (from as low as 32767 or as high as (2^32)-1, depending on the implementation).

To convert the output to the range 0 ... N use ((N*rand())/RAND_MAX)
Title: Re: Moving a sprite in a specified angle
Post by: Karlos on August 28, 2007, 12:20:15 AM
Quote

whabang wrote:
My mind hurts...


Wrong forum, really :lol:
Title: Re: Moving a sprite in a specified angle
Post by: motorollin on August 28, 2007, 08:00:34 AM
Hmmm, it doesn't work...

int xv=(3*(rand()/RAND_MAX))+1;
int yv=(3*(rand()/RAND_MAX))+1;
int r=1*(rand()/RAND_MAX);
if ( r == 1 ) xv = -xv;
r=1*(rand()/RAND_MAX);
if ( r == 1 ) yv = -yv;
xVel=xv;
yVel=yv;

Lines 1 and 2 should (I think) generate two random numbers between 1 and 4 inclusive. Lines 3-6 then give each velocity a 50/50 chance of becoming negative (IOW the sprite has a 50/50 chance of having its direction reversed in each axis). Lines 7 and 8 obviously then assign the result to the velocities.

When I run this over and over again, the sprite always moves in the same direction. I then added "if ( xv == 1 && yv == 1 ) quit=true;", and my game quits every single time. That suggests to me that the result of the rand() functions are always returning 0 :-?

--
moto
Title: Re: Moving a sprite in a specified angle
Post by: Karlos on August 28, 2007, 01:28:35 PM
As I said, you need

(N*rand())/RAND_MAX

What you are doing is this:

N*(rand()/RAND_MAX)

Since you are dealing with integer arithmetic here, you will always get zero for that except in the rare (about 1 in RAND_MAX ;-) ) chance that rand() returns RAND_MAX.

Order of evaluation is very important here. You want to multiply rand() by your scaling factor *before* dividing it, not the other way around.
Title: Re: Moving a sprite in a specified angle
Post by: motorollin on August 28, 2007, 05:34:12 PM
Ok I changed my code to this:

        int xv=((3*rand())/RAND_MAX)+1;
        int yv=((3*rand())/RAND_MAX)+1;
        int r1=((1*rand())/RAND_MAX)+1;
        if ( r1 == 1 ) xv = -xv;
        int r2=((1*rand())/RAND_MAX)+1;
        if ( r2 == 1 ) yv = -yv;
        pinwheels.xVel=xv;
        pinwheels.yVel=yv;

The enemy still moves in the same direction each time the game is launched (though a different one to the one it went in before I changed the rand() lines)
Title: Re: Moving a sprite in a specified angle
Post by: Karlos on August 28, 2007, 07:23:57 PM
Well, you didn't expect rand() to be actually random did you? ;-)

Unsurprisingly, rand() returns the next number in a series of pseudorandom numbers that are in fact entirely predictable if you know the algorithm in use and the seed that it started with.

Try initialising the random number generator at the start with something reasonably likely to be different each time your program runs. The current system time is a favourite random seed of old.
Title: Re: Moving a sprite in a specified angle
Post by: motorollin on August 28, 2007, 08:37:40 PM
Well actually I do have "srand( time(NULL) );" at the top of my main function, before the rand() commands :-?

--
moto