Josh Wallace
Josh Wallace

Reputation: 63

C++ how to make velocity equal in all directions?

I feel this is a difficult question to articulate, so I have illustrated on this graph (I am using SDL in C++). Each square represents a pixel on the screen, I want the red pixel to move at the same speed regardless of direction.

If the speed is 8 pixels/sec then after 1 second:

pixels

In both cases the pixel has been displaced by 8 pixels, however.. The euclidean distance between red and blue = 8.00 and red and green = 11.31. I want the pixel to arrive at yellow instead.

So I have tried to correct this by declaring a constant speed, then I divide this by the actual displacement, giving me a number I use to multiple the X and Y coordinates and travel back along the trajectory, limiting my speed.

The code looks sorta like this (I have commented the area of interest):

float velX = 0, velY = 0, currentX, currentY;
int time = 0, speed = 300;

//Events
void handleInput(){
    if( event.type == SDL_KEYDOWN ){
        switch( event.key.keysym.sym ){
            case SDLK_UP: {velY -= speed;} break;
            case SDLK_DOWN: {velY += speed;} break;
            case SDLK_LEFT: {velX -= speed;} break;
            case SDLK_RIGHT: {velX += speed;} break;
        }
    }
    else if( event.type == SDL_KEYUP ){
        //do the opposite
    }
}

//Logic
void move(){

    //float dist = sqrt( (velX*velX) + (velY*velY) );
    //
    //if(dist > 0){
    //        velX *= speed / dist;
    //        velY *= speed / dist;
    //}

    currentX += velX * (get_delta_ticks(&time) / 1000.f);
    currentY += velY * (get_delta_ticks(&time) / 1000.f);
    set_delta_ticks(&time);
}

//Render
void Player::render(){
    apply_surface(currentX, currentY, spriteSheet, screen, &currentClip);
}

So here is my question, I am new to programming games and I'm unsure if this is the CORRECT way to be doing movement.. It seems a bit inefficient in ways, should I be trying to deduce the position based on an angle and the length of the hypotenuse instead? I don't know very much about trigonometry but of course I am keen to learn.

Upvotes: 3

Views: 1854

Answers (5)

SigTerm
SigTerm

Reputation: 26429

Well, it looks like most people forgot about analog input...

Anyway, It should work like this:

velX, velY are floats within [-1.0..1.0] range. In case of digital input (keyboard, dpad), pressing "left" sets velX to -1, pressing "right" sets velX to 1, etc.

However, if you use analog stick, you put floating point values, where velX == 1.0 corresponds to rightmost position of analog stick, velX == -1.0 corresponds to leftmost position, and so on.

maxSpeed is maximum game movement speed, also float.

With all this in mind, you could calculate next position of object like this:

void move(){
    float curVelX = velX, curVelY = velY;
    float moveSquared = (curVelX*curVelX + curVelY*curVelY);
    if (moveSquared > 1.0f){
         float d = sqrtf(moveSquared);
         curVelX /= d;
         curVelY /= d;
    }
    currentX += curVelX * maxSpeed * (get_delta_ticks(&time) / 1000.f);
    currentY += curVelY * maxSpeed * (get_delta_ticks(&time) / 1000.f);
    set_delta_ticks(&time);
}

It seems a bit inefficient in ways,

Look, you have ONE object. When you'll have few hundreds of thousands of them, then you can start worrying about efficiency.

should I be trying to deduce the position based on an angle and the length of the hypotenuse instead?

If your object is torpedo-like and can slowly turn left/right and accelerate/decelerate (you can steer it a bit and make it go faster/slower), then you probably need to store movement direction and linear movement speed.

If your object is some kind of flying orb or rolling ball that can go in any direction it wants, then you should use method similar to the one I described. Have separate velocity for x/y and limit maximum linear velocity using sqrtf.

Upvotes: 0

Useless
Useless

Reputation: 67772

Separate the logical position from the display position.

The logical position will probably need to use floating-point coordinates, and you'll round them to integer pixel coordinates for the display position. You can even do anti-aliasing with this if you want to smooth the movement.

So:

  • right would have logical unit vector (x,y)=(1.0,0.0)
  • down would have logical unit vector (x,y)=(0.0,-1.0)
  • down+right would have logical unit vector (x,y)=(1/sqrt(2),-1/sqrt(2))

every 1/8th of a second, you add the unit vector to your current logical location, and select which pixel to draw. Obviously you can choose different units and update frequencies, but this will give the numbers you asked for.

Upvotes: 1

TemplateRex
TemplateRex

Reputation: 70536

I would start with different user controls: namely absolute speed and direction.

Given speed velAbs and the angle theta, you have

velX = velAbs * cos(theta);
velY = velAbs * sin(theta);

When updating the position, it is typically most convenient to decompose the absolute speed in its X and Y components, update the X and Y positions for the given time interval dt

currentX = velX * dt;
currentY = velY * dt;

whereas for collision impact computations the absolute speed is more relevant.

This will avoid your yellow/green problem because maximum throttle in both the X and Y directions will get you to green. Just let the user set the throttle from 0 to 8 and also set a direction, then you will get to yellow or blue.

Upvotes: 0

ogni42
ogni42

Reputation: 1276

The point is: You counted 8-x and 8-y key press events, which lead to a shortest distance from the origin of v=sqrt(8*8+8*8)=11.31, exactly as you observed.

You should be aware, that, within the time you are measuring, either 8 (only x OR y) or 16 (x plus y) key press events might be sampled, resulting in different "speeds", where speed=number_of_key_events/period_of_time

If you want to travel to the "yellow" spot, there should be only 6 X key press events plus 6 Y key press events in the same period of time in which you sampled the 8 key presses in one of the basic directions.

So there is nothing wrong with your code, and, as the other posters pointed out, your euclidian speed can be calculated using the euclidian distance divided by the sampling period, resulting in v=8 or v=11.31, respectively.

Upvotes: 0

superarce
superarce

Reputation: 403

You need to get the speed in a 2D Space. To get it you have to do a sqrt with both speeds.

curSpeed = sqrt( ( velX * velX ) + (velY * velY ) );

Upvotes: 0

Related Questions