"Dipping" physics: flying object swooping and then returning to previous height

Started by
13 comments, last by PiCroft 12 years, 5 months ago
Hello,

I'm trying to create a simple 2D game where an object is flying at a particular height at constant velocity. When I press a button, the object starts to dive, the dive getting steeper the longer you hold the button. When you release the button, the object levels out, then starts to climb back up to its original height.

Could someone give me some math pointers for making this mechanic?
Advertisement

Hello,

I'm trying to create a simple 2D game where an object is flying at a particular height at constant velocity. When I press a button, the object starts to dive, the dive getting steeper the longer you hold the button. When you release the button, the object levels out, then starts to climb back up to its original height.

Could someone give me some math pointers for making this mechanic?


Have your object have 2 velocities, horizontal and vertical, that do not interact with each other (as in real life).

Have the horizontal velocity stay the same. When you press the button accelerate the vertical velocity so that it starts going down. That will need to be incremented each frame the button is held down. When the button is released, just have the vertical velocity decelerate to 0.

if (button.ispresssed())
{
vertical.velocity += 10; // this could be -= all depends on how to set it up
}
else
{
vertical.velocity -= 10; // this is opposite of the first one
if (vertical.velocity < 0) vertical.velocity = 0;
}

Edit: Not sure if you wanted to decrease horizontal velocity during "diving". Doing so would make the dive even steeper. if so, it would change to something like...
if (button.ispresssed())
{
vertical.velocity += 10; // this could be -= all depends on how to set it up
horizontal.velocity -= 5; //this could be += all depends on set up
if (horizontal.velocity < 0) horizontal.velocity = 0;
}
else
{
vertical.velocity -= 10; // this is opposite of the first one
if (vertical.velocity < 0) vertical.velocity = 0;
horizontal.velocity += 5;
if (horizontal.velocity > MAX_HORIZONTAL_VELOCITY) horizontal.velocity = MAX_HORIZONTAL_VELOCITY;
}
my blog contains ramblings and what I am up to programming wise.
Thanks, that's useful!

I need to make the object rotate correctly too, I'm using a second object hovering slightly in front of the main object thats invisible, so the object always rotates to face it. It doesn't look right atm, but Its a good stop-gap until I can make a movement algorithm that correctly applies physics to it.

Thanks, that's useful!

I need to make the object rotate correctly too, I'm using a second object hovering slightly in front of the main object thats invisible, so the object always rotates to face it. It doesn't look right atm, but Its a good stop-gap until I can make a movement algorithm that correctly applies physics to it.


Not sure if you are doing this already but it will be a smoother curve if instead of using constants for acceleration, the += -= 5's and 10's, is to make it time based. Like so...

float VERTICAL_ACCELERATION_PER_SECOND 10
if (button.ispresssed())
{
vertical.velocity += (int)( VERTICAL_ACCELERATION_PER_SECOND * Timer.MillisecondsElapsed / 1000.0f);
}
where Timer.MillisecondsElapsed is the amount of time taken since the last frame.

This is the method I use in my 2D tile engine testing to give my character the ability to jump in a smooth curve which can be seen here. He is a bit "floaty" but that is because I haven't gotten the gravity number down just right.

Basically, there is a constant gravity pulling down on him adding to his velocity, 32 pixels per second per frame. When he stands on a block, the block is pushing him back up and resetting his vertical velocity to 0 pixels per second. To jump I add a force to him by setting his vertical acceleration to -1000 pixels per second and let the gravity that is applied every frame do the work for me.
Horizontal movement is just adding/subtraction a horizontal acceleration and when left/right is not pressed, the acceleration rapidly returns to 0.

I know it is not exactly what you are trying to achieve, but maybe it will give ya some ideas.
my blog contains ramblings and what I am up to programming wise.
How about using an upside bell curve for the y-coordinate of the object?

Hello,

I'm trying to create a simple 2D game where an object is flying at a particular height at constant velocity. When I press a button, the object starts to dive, the dive getting steeper the longer you hold the button. When you release the button, the object levels out, then starts to climb back up to its original height.

Could someone give me some math pointers for making this mechanic?


I wouldn't go with splitting the velocity into horizontal and vertical. In fact, if you keep horizontal as it is and increse the vertical part, the total velocity will have higher magnitude, which probably won't be what you want.

I would solve your problem by having a velocity vector v (which will be horizontal during the initial flying at a constant height).
When you hold a dive button, I would simply rotate the vector down. The longer you hold the button, the bigger the overall rotation, but with a limit at some defined angle (for example 70 degrees to not let it dive too steep)
When you don't hold the button, I would gradually rotate the vector back to the initial horizontal orientation, then compare the current height with the target height and if it is smaller, rotate the vector further upwards to make the object ascend to the required target height.

Your actual position is then simply updated every frame as
p = p + v * dt;
where dt is the timestep between frames, p is a position vector (2D) and v is the velocity vector (2D).

If you always change the rotation gradually (so not for example from 0 to 40 degrees in one step), the resulting movement will be nicely smooth (the object's central point would draw a smooth curve).

Please let me know if this verbal explanation isn't clear and/or if you would prefer some equations.

Btw, it of course all depends on what kind of movement are you trying to get, what kind of object it is etc. Maybe you really want it to dive at higher overall velocity than when it flies horizontal?The solution with rotating velocity vector would suit something like an airplane, where the rotation even can be nicely visualised (the whole airplane rotates).

[quote name='PiCroft' timestamp='1319728613' post='4877578']
Hello,

I'm trying to create a simple 2D game where an object is flying at a particular height at constant velocity. When I press a button, the object starts to dive, the dive getting steeper the longer you hold the button. When you release the button, the object levels out, then starts to climb back up to its original height.

Could someone give me some math pointers for making this mechanic?


I wouldn't go with splitting the velocity into horizontal and vertical. In fact, if you keep horizontal as it is and increse the vertical part, the total velocity will have higher magnitude, which probably won't be what you want.

I would solve your problem by having a velocity vector v (which will be horizontal during the initial flying at a constant height).
When you hold a dive button, I would simply rotate the vector down. The longer you hold the button, the bigger the overall rotation, but with a limit at some defined angle (for example 70 degrees to not let it dive too steep)
When you don't hold the button, I would gradually rotate the vector back to the initial horizontal orientation, then compare the current height with the target height and if it is smaller, rotate the vector further upwards to make the object ascend to the required target height.

Your actual position is then simply updated every frame as
p = p + v * dt;
where dt is the timestep between frames, p is a position vector (2D) and v is the velocity vector (2D).

If you always change the rotation gradually (so not for example from 0 to 40 degrees in one step), the resulting movement will be nicely smooth (the object's central point would draw a smooth curve).

Please let me know if this verbal explanation isn't clear and/or if you would prefer some equations.

Btw, it of course all depends on what kind of movement are you trying to get, what kind of object it is etc. Maybe you really want it to dive at higher overall velocity than when it flies horizontal?The solution with rotating velocity vector would suit something like an airplane, where the rotation even can be nicely visualised (the whole airplane rotates).
[/quote]

Thatnks, this is closer to what I want.

The idea is that the object will have 2 heights: at default, the object will move towards the upper height (default behaviour) and when diving, it will move towards the lower height.

I have the velocity and I'm adding it to the position as you suggested, and I am getting movement forward (because the default velocity is forwards, but I'm having difficulty correctly calculating the rotation.

//if diving, set target y to lower limit
zVec2f target;

target.x = getPosition().x;
target.x += 5.0f;

if(diving)
target.y = baseline_y_lower;
else
target.y = baseline_y_upper;

zVec2f delta = (getPosition()-target);

float TurnDir = delta.getAngle();

float rotDif = zRadRelative(TurnDir-physOrientatiation.xa.getAngle());
physOrientatiation.xa.rotate(rotDif);

//set final velocity
zVec2f velocity = physOrientatiation.xa;
velocity.normalise();
setPosition(getPosition() + velocity * ev.getTime().dt);

//rotate sprite
mainr->setRotation(physOrientatiation.xa.getAngle());


The above code makes my object fly in weird directions. I'm not entirely sure why though :/

Edit: physOrientatiation.xa.rotate(rotDif); rotates to an absolute value in radians I believe, so when it calculates the difference in angle between the current heading and the target point, the value returned is the total angle needed to turn, not the angle needed to turn this frame.

That being said, I'm wondering why this is causing such odd behaviour since, if the object turns directly towards the target in 1 frame, the next frame the difference should be tiny or non-existant, meaning that it should snap to facing the right direction immediately and then continue correctly, but instead it floats off in odd directions.

I wouldn't go with splitting the velocity into horizontal and vertical. In fact, if you keep horizontal as it is and increse the vertical part, the total velocity will have higher magnitude, which probably won't be what you want.


Actually, that's exactly what I would expect. By dipping you convert potential energy into kinetic energy, which is why you get faster.

Kestrels use this to their advantage when hunting: They hover above the ground at some height and, when they figure out what they want to catch, they take advantage of the extra speed that the drop gives them.

[quote name='Tom KQT' timestamp='1320671819' post='4881384']
I wouldn't go with splitting the velocity into horizontal and vertical. In fact, if you keep horizontal as it is and increse the vertical part, the total velocity will have higher magnitude, which probably won't be what you want.


Actually, that's exactly what I would expect. By dipping you convert potential energy into kinetic energy, which is why you get faster.

Kestrels use this to their advantage when hunting: They hover above the ground at some height and, when they figure out what they want to catch, they take advantage of the extra speed that the drop gives them.
[/quote]

Of course, but for a proper simulation of this you would need to use gravity vector (acceleration vector) and increase the velocity using this acceleration. Simply increasing the vertical velocity component by a constant number would lead to a weird effect. We don't know what's PiCroft really doing, but he didn't mention gravity.
The code doesn't seem right to me, provided I understand well what you really want to achieve.
Try something like this. I don't know what physOrientation.xa is, so you'll need to adapt this code a bit, it is partly a pseudo-code. In fact I didn't use physOrienation.xa at all, I don't see when (and if at all) it should be used:


float angle = 0;
if (diving && getPosition().y > baseline_y_lower) {
// we are diving and we are still above the desired depth => we must descend
angle = descend_angle_value; // descend_angle_value can be a constant now to make it easier to get it working
}
else if (!diving && getPosition().y < baseline_y_upper) {
// we are not diving and we are still below the desired depth => we must ascend
angle = ascend_angle_value; // ascend_angle_value can be a constant now to make it easier to get it working
}

// Now give your object the 'angle' absolute orientation, dunno how your engine works so I'll guess:
mainr->setRotation(angle);

// Calculate the velocity vector anyhow, given its orientation ('angle') and magnitude (depends on how fast you want it to move).
// For example even something as simple as this could work too:
zVec2f velocity;
velocity.x = magnitude * cos(angle);
velocity.y = magnitude * sin(angle);
// ... but a care must be taken about signs (in which direction you measure the angle, which direction does y have etc.)
// Note that you don't want to normalize the velocity, in general case the velocity magnitude won't be 1.

setPosition(getPosition() + velocity * ev.getTime().dt);


Warning - it's just a code to get started, it has some simplifications - the rotation changes abruptly, the object will move in lines, not in curves. The final code should change the angle gradually until it reaches the desired ascend/descent value. And when approaching the target height, you would have to start leveling your object in advance to make smooth transition again.

This topic is closed to new replies.

Advertisement