Scenario
I am working on a top-down view game in which enemies move towards certain positions. The destination being moved towards will often change drastically - sometimes while the enemy is still in motion towards the previous destination...
I want to achieve movement which is more realistic than linear movement, so there should be some acceleration and deceleration as enemies switch between the destinations.
Steering (direction) is not a factor. You may assume the sprites will move much like a hovercraft, drifting between the destinations as quickly as it can manage with respect to acceleration and deceleration.
For simplicity - lets assume there is only 1 dimension (Y) instead of X and Y... the movement should emulate a car which can only move north or south.
Since we are considering realistic movement in this scenario, you might not be surprised that a maximum speed over time is also a consideration. The enemy should never exceed it's own maximum speed; enemies store their own maximum speed in a variable.
One final consideration is that enemies will not only have a 'maximum speed' value, but it will also have a 'maximum acceleration' value - this would be the thing which guides how quickly each enemy can respond to moving in the opposite direction.
For simplicity, assume that the enemy does not have any movement friction... when it stops accelerating, it just keeps cruising at the current velocity forever.
Example
For context, lets elaborate on the car example. A particular car has:
Maximum speed: 10 meters per second
Maximum acceleration: can reach top speed in 2 seconds
(Other factors like destination y-pos, position y-pos, current velocity, etc)
Just like when I'm driving a car, I imagine all of these values are present, but I can't change them. All I can really change is how much I'm putting my foot on the acceleration/brake. Let's call this the 'throttle'. Like the acceleration pedal in a car, I can change this value to any amount at any time, in no time at all.
I can plant my foot down (throttle=1), let go of the pedal immediately (throttle=0), and even change into reverse and plant my foot down again (throttle=-1). Lets assume these changes in throttle are INSTANT (unlike speed or acceleration, which grow/shrink over TIME)
All that said, I imagine the only value that I really need to calculate is what the throttle should be, since thats the only thing I can control in the vehicle.
So, how then do I know how much throttle to use at any given moment, to arrive as quickly as possible to a destination (like some traffic lights) without overshooting my destination? I will need to know how much to push down the accelerator, briefly not accelerate at all, and then how hard decelerate as I'm closing in on the destination.
Preempted movement
This game will likely have an online component. That said, players will be transmitting their positions over a socket connection... however even the best connection could never achieve sending positions frequently enough to achieve smooth movement - you need interpolation. You need to send the 'velocity' (speed) along with the co-ordinates so that I can assume future positions in the time between packets being received.
For that reason, Tweens are a no-go. There would be no way to send a tween, then accurately inform the other parties at what point along each tween each entity currently is (I mean, I guess it is possible, but horrendously overcomplicated and probably involves rather large packet sends, and is probably also very exploitable in terms of the online component), then you need to consider aborting Tweens as the destinations change, etc.
Don't get me wrong, I could probably already model some very realistic movement with the ease-in/ease-out functionality of Tweens, and it would look great, but in an online setting that would be.. very messy.
How's it look so far?
So, essentially I have established that the primary aspect which needs to be calculated at any time is how much throttle to use. Let's work through my logic...
Imagine a very basic, linear movement over time formula... it would look like this:
Example 1 - position over time
currentDY = 5; // Current 'velocity' (also called Delta Y or 'DY')
currentY += currentDY * time // Current Y pos is increased by movement speed over time.
As you can see, at any given moment, the Y position increases over time due to the 'velocity' or DY (over time). Time is only a factor ONCE, so once we reach the destination we just set the DY to zero. Very sharp unrealistic movement. To smoothen the movement, the velocity ALSO needs to change over time...
Example 2 - velocity over time
throttle = -1
currentDY += throttle * time;
currentY += (currentDY * time);
//Throttle being -1 eventually decelerates the DY over time...
Here, the throttle is '-1' (maximum reverse!), so over time this will reduce the velocity. This works great for realistic acceleration, but provides no realistic anticipation or deceleration.
If we reach the destination this time, we can set the throttle to '0'... but it's too late to brake, so the resulting enemy will just keep moving past the target forever. We could throttle = '1' to go back, but we'll end up swinging back and forth forever.
(Also note that the maximum acceleration and speed isn't even a factor yet - it definately needs to be! The enemy cannot keep ramping up their speed forever; velocity delta AND ALSO acceleration delta need to have limits).
All that said, it's not enough to simply change velocity over time, but we also need be able to anticipate how much to decelerate (i.e. 'backwards throttle') BEFORE IT HAPPENS. Here's what I've got so far, and I'm practically certain this is the wrong approach...
Example 3 - throttle over time?? (I'm stuck)
guideY = currentY + (currentDY * (timeScale * 3000));
dist = endY - guideY;
throttle = Math.max(-1, Math.min(1, dist / 200));
currentDY += throttle * time;
currentY += (currentDY * time);
As you can see, this time we attempt to anticipate how much throttle to use by guessing where the enemies position will be in an arbitrary time in the future (i.e. 3 seconds). If the guideY went past the destination, we know that we have to start BRAKING (i.e. reducing speed to stop on top of the destination). By how much - depends on how far away the enemies future position is (i.e. throttle = dist / 200;)
Here's is where I gave up. Testing this code and changing the values to see if they scale properly, the enemy always swings way over the destination, or takes far too long to 'close in' on the destination.
I feel like this is the wrong approach, and I need something more accurate. It feels like I need an intersection to correctly anticipate future positions.
Am I simply using the wrong values for the 3 seconds and dist / 200, or am I not implementing a fully working solution here?
Presently, compared to the linear movement, it always seems to take 8 times longer to arrive at the target position. I haven't even reached the point of implementing maximum values for DeltaVelocity or DeltaAcceleration yet - the accepted solution MUST consider these values despite not being present in my JSFiddle below...
Test my logic
I've put all my examples in a working JSFiddle.
JSFiddle working testbed
(Click the 'ms' buttons below the canvas to simulate the passing of time. Click a button then press+hold Return for very fast repetition)
The sprite is initially moving in the 'wrong' direction - this is intended for testing robustness - It assumes an imaginary scenario where we just finished moving as fast as possible toward an old destination far lower on the screen, and now we need to suddenly start moving up...
As you can see, my third example (see the update function), the time for the sprite to 'settle' at the desired location takes far longer than it should. My math is wrong. I can't get my head around what is needed here.
What should the throttle be at any given time? Is using throttle even a correct approach? Your assistance is very appreciated.
Tiebreaker
Alright, I've been stuck on this for days. This question is going up for some phat bounty.
If a tiebreaker is required, the winner will need to prove the math is legit enough to be tested in reverse. Here's why:
Since the game also comprises of a multiplayer component, enemies will be transmitting their positions and velocities.
As hacking protection, I will eventually need a way to remotely 'check' if the velocity and position at between any two sample times is possible
If the movement was too fast based on maximum velocity and acceleration, the account will be investigated etc. You may assume that the game will know the true maximum acceleration and velocity values of the enemies ahead of time.
So, as well as bounty, you can also take satisfaction in knowing your answer will contribute to ruining the lives of filthy video game cheaters!
Edit 2: Fiddle added by answer author; adding into his answer in case anyone else finds this question: http://jsfiddle.net/Inexorably/cstxLjqf/. Usage/math explained further below.
Edit 1: Rewritten for clarification received in comments.
You should really change your implementation style.
Lets say that we have the following variables: currentX, currentY, currentVX, currentVY, currentAX, currentAY, jerk.
currentVX is what would have been your currentDX. Likewise, currentAX is the x component of your delta velocity value (accel is derivative of velocity).
Now, following your style, we're going to have a guideX and a guideY. However, there is another problem with how you're doing this: You are finding guideY by predicting the target's position in three seconds. While this is a good idea, you're using three seconds no matter how close you are to the target (no matter how small dist is). So when the sprite is 0.5 seconds from the target, it's going to still be moving towards the target's estimated position (three seconds into the future). This means it won't actually be able to hit the target, which seems to be the problem that you implied.
Moving on, recall the previous variables that I mentioned. Those are the current variables -- ie, they will be updated at every call after some seconds have been passed (like you have been doing before). You also mentioned a desire to have maxV, and maxA.
Note that if you have a currentVX of 5 and a currentVY of 7, the velocity is (5^2+7^2)^0.5. So, what you're going to want to do each time you're updating the 'current' archetype of variables is before updating the value, see if the magnitude (so sqrt(x^2+y^2) of those variables like I showed with velocity) will exceed the respective maxV, maxA, or jmax values that you have set as constants.
I'd also like to improve how you generate your guide values. I'm going to assume that the guide can be moving. In this case, the target will have the values listed above: x, y, vx, vy, ax, ay, jx, jy. You can name these however you'd like, I'm going to use targetX, targetY... etc to better illustrate my point.
From here you should be finding your guide values. While the sprite is more than three seconds away from the target, you may use the target's position in three seconds (note: I recommend setting this as a variable so it is simple to modify). For this case:
predictionTime = 3000*timescale; //You can set this to however many seconds you want to be predicting the position from.
If you really wanted to, you could smooth the curve using integration functions or loops for the guide values to get a more accurate guide value from the target values. However, this is not a good idea because if you ever implement multiple targets / etc, it could have a negative impact on performance. Thus, we will use a very simple estimation that is pretty accurate for being such low cost.
if (sprite is more than predictionTime seconds away from the target){
guideX = targetX + predictionTime * targetVX;
guideY = targetY + predictionTime * targetVY;
}
Note that we didn't account for the acceleration and jerk of the target in this, it's not needed for such a simple approximation.
However, what if the sprite is lese than predictionTime seconds away from the target? In this case we want to start increasingly lessening our predictionTime values.
else{
guideX = targetX + remainingTime * targetVX;
guideY = targetY + remainingTime * targetVY;.
}
Here you have three choices on finding the value of remaining time. You can set remainingTime to zero, making the guide coordinates the same as the targets. You can set remainingTime to be sqrt((targetX-currentX)^2+(targetY-currentY))/(sqrt(currentVX)^2+(currentVY)^2), which is basically distance / time in 2d, a cheap and decent approximation. Or you can use for loops as mentioned before to simulate an integral to account for the changing velocity values (if they are deviating from maxV). However, generally you will be remaining at or close to maxV, so this is not really worth the expense. Edit: Also, I'd also recommend setting remainingTime to be 0 if it is less than some value (perhaps about 0.5 or so. This is because you don't want hitbox issues because your sprite is following guide coordinates that have a slight offset (as well as the target moving in circles would give it a larger velocity value while changing direction / essentially a strong evasion tactic. Maybe you should add something in specifically for that.
We now have the guideX and guideY values, and account for getting very close to a moving target having an effect on how far from the target the guide coordinates should be placed. We will now do the 'current' value archetype.
We will update the lowest derivative values first, check to see if they are within bounds of our maximum values, and then update the next lowest and etc. Note that JX and JY are as mentioned before to allow for non constant acceleration.
//You will have to choose the jerk factor -- the rate at which acceleration changes with respect to time.
//We need to figure out what direction we're going in first. Note that the arc tangent function could be atan or whatever your library uses.
dir = arctan((guideY-currentY)/(guideX-currentX));
This will return the direction as an angle, either in radians or degree depending on your trig library. This is the angle that your sprite needs to take to go in the direction of guide.
t = time; //For ease of writing.
newAX = currentAX + jerk*t*cos(dir);
newAY = currentAY + jerk*t*sin(dir);
You may be wondering how the newAx value will ever decrease. If so, note that cos(dir) will return negative if guide is to the left of the target, and likewise sin(dir) will return negative if the sprite needs to go down. Thus, also note that if the guide is directly below the sprite, then newAx will be 0 and newAY will be a negative value because it's going down, but the magnitude of acceleration, in other words what you compare to maxA, will be positive -- as even if the sprite is moving downwards, it's not moving at negative speed.
Note that because cos and sin are of the same library as atan presumably, so the units will be the same (all degrees or all radians). We have a maximum acceleration value. So, we will check to make sure we haven't exceeded that.
magnitudeA = sqrt(newAX^2+newAY^2);
if (magnitudeA > maxA){
currentAX = maxA * cos(dir);
currentAY = maxA * sin(dir);
}
So at this point, we have either capped our acceleration or have satisfactory acceleration components with a magnitude less than maxA. Let us do the same for velocity.
newVX = currentVX + currentAX*t;
newVY = currentVY + magnitudeA*t*sin(dir);
Note that I have included two ways to find the velocity components here. Either one works, I'd recommend choosing one and using it for both x and y velocity values for simplicity. I just wanted to highlight the concept of magnitude of acceleration.
magnitudeV = sqrt(newVX^2+newVY^2);
if (magnitudeV > maxV){
currentVX = maxV * cos(dir);
currentVY = maxV * sin(dir);
}
We'd also like to stop boomeranging around our target. However, we don't want to just say slow down alot like you did in your JSFiddle, because then if the target is moving it will get away (lol). Thus, I suggest checking how close you are, and if you are in a certain proximity, reduce your speed linearly with distance with an offset of the target's speed. So set closeTime to something small like 0.3 or what ever feels good in your game.
if (remainingTime < closeTime){
//We are very close to the target and will stop the boomerang effect. Note we add the target velocity so we don't stall while it's moving.
//Edit: We should have a min speed for the enemy so that it doesn't slow down too much as it gets close. Lets call this min speed, relative to the target.
currentVX = targetVX + currentVX * (closeTime - remainingTime);
currentVY = targetVY + currentVY * (closeTime - remainingTime);
if (minSpeed > sqrt(currentVX^2+currentVY^2) - sqqrt(targetVX^2-targetVY^2)){
currentVX = minSpeed * cos(dir);
currentVY = minSpeed * sin(dir);
}
}
magnitudeV = sqrt(currentVX^2+currentVY^2);
At this point we have good values for velocity too. If you going to put in a speedometer or check the speed, you're interested in magnitudeV.
Now we do the same for position. Note you should include checks that the position is good.
newX = currentX + currentVX*t;
newY = currentY + currentVY*t;
//Check if x, y values are good.
current X = newX; currentY = newY;
Now everything has been updated with the good values, and you may write to the screen.
Related
In my mobile game, the main character needs to jump to a specific y point before starting to fall down again due to gravity. How could I calculate the y velocity I would need to reach (at peak) a given y position with a given gravity?
I tried to do this with a tween instead of arcade physics because it was exactly what I needed (distance traveled over time with a gravity-like effect), but the performance was not good and was constantly sputtering.
I'm currently using Phaser 2.6.2. Thanks!
There are 2 kinds of time in games, cycle based time and deltaTime.
In a cycle based scenario, the physics don't shift to account for lag so the best solution is this:
You start off with number sequence where you compound gravity with the previous number in the sequence until your sum total equals your desired height.
For an example, I will use height = 75, gravity = 5, and jumpForce=unknown
(5+10+15+20+25)=75
The jump force you need to reach this height will be the last number in the sequence which is "25".
In a deltaTime Scenario (as used in this case), the physics are abstracted to try to adjust to time.
So, if you have 5fps, you'll have 5 cycles/second. if you have 20 fps, you'll have 20 cycles per second; so, this abstraction makes the above irrelevant.
Instead use the standard gravity formula of height = velocity^2 / (2 * gravity)., because this will better model to the formulas used by delta time engines.
I'm trying to create an autonomous agent with arriving behaviour based on Reynolds classic Boids paper and more specifically Dan Shiffman's implementation on natureofcode.com. Both of these approaches are easy to understand (and work) but they both assume a fixed nominal update step rather than querying the elapsed time since the last frame update and applying this time delta during the Euler integration step. Ie they suggest simply:
//pseudocode
acceleration.add(force);
velocity.add(acceleration);
location.add(velocity);
however if I want to include a time delta then various physics papers suggest I should rewrite this as:
//pseudocode
delta = timeElpasedSinceLastFrameUpdate();
acceleration.add(force);
deltaAcceleration = acceleration.mult(delta);
velocity.add(deltaAcceleration );
deltaVelocity = velocity.mult(delta);
location.add(deltaVelocity);
This also works until I try to implement the steering behaviours and specifically the arriving behaviour. For reference to the methodology see example 6.2 in the link here:
Nature of Code Steering Behaviours
The problematic step (in pseudocode) is steeringForce = desired - velocity;
Without a time delta applied the contribution of '- velocity' to the steering force correctly counteracts the currently held velocity vector and brings the object to a decelerating stop at the arrival destination.
With a time delta applied however the steeringForce is effectively moderated by the fractional time delta when I multiply acceleration by delta and hence doesn't apply sufficient deceleration to the accumulated velocity to bring the object to a halt in time and I get oscillating behaviour.
I can 'fix' this by not multiplying accumulated forces (i.e. acceleration) by time delta but this doesn't seem correct.
So, my question:
How should a steeringForce for arrival behaviour be generated to account for variable delta time?
Any help gratefully appreciated!
EDIT: I missed the main part of your question. There are many ways to get the result you want, but they do so by different means. The easiest I can come up with is probably to skip the steeringForce for now all together and instead just modify the velocity.
We want a vector such that
desired_velocity = velocity + correction
This is the same as you wrote previously (but for 'steeringForce'):
correction = desired_velocity - velocity
By doing velocity + correction you will immediately get to the desired velocity, which is usually not what you want as it leads to a very jerky motion. Instead, you could clamp the correction to some value which would lead to a smoother motion:
len = length(correction)
corr_length = len > max_correction ? max_correction : len
correction = correction/len*corr_length
And now you can do
velocity += correction
This should lead to a somewhat dynamic motion, without oscillation.
Explicit time step integration (e.g. an forward Euler in your case) is usually written as
new_acceleration = old_acceleration + (delta_t/mass)*force
^^ note
And similarly,
new_velocity = old_velocity + delta_t*acceleration
new_position = old_position + delta_t*velocity
So to answer your question, you need to multiply by the time step before accumulating:
acceleration.add(force.mult(delta_t/mass));
velocity.add(acceleration.mult(delta_t));
location.add(velocity.mult(delta_t));
I'm developing an HTML5 3D fps-like engine that already looks quite nice, but as this might be one of the worst language choices to make 3D there's noticeable lag sometimes.
I programmed movements (WASD) to be independent of rendering speed, so sometimes it's quite jerky, but other times is working at an acceptable 30+ fps (depending on CPU of course).
The only thing I can't wrap my mind around is jumping: currently the jumping is done by adding a positive constant to the falling variable (gravity is always negative and then corrected by collision detection) and then subtracting a constant, this is called every time a new frame is rendered, the thing is that when fps go low I feel like I'm on the moon. I prefer jerkiness to slow-mo effect.
If I use the same method like I do for moving (calculate time between current and last frame) the deducted variable gets too big sometimes and the jumping apex changes (to half of the value compared to high fps) - this is unacceptable as jumping height must be always the same.
Here's some pseudo-code to help understanding the problem (called during one rendering routine):
// when clicked on spacebar:
if(spacebar)
{
// this defines jumping apex
jump = 0.5
}
// constant added to y (vertical position) later in the code
cy += jump;
// terminal velocity = -2
if(jump > -2)
{
// gravity (apex multiple to get maximum height)
jump -= 0.05;
}
if(collision_with_floor)
{
// stop falling
cy = 0;
if(jump < 0)
{
jump = 0;
}
}
player.position.y += cy;
Now with time dependent jumping (replace in the code above):
// terminal velocity = -2
if(jump > -2)
{
// gravity, 0.4 is an arbitrary constant
jump -= (now - last_frame)*0.4;
last_frame = now;
}
To illustrate even better here's an image of what's going on:
Blue dots indicate frame renders.
I'm not even sure of this is the right way to program jumping routine. Basically jerkiness and constant jumping height is better than smoothness and slow-mo effect.
If the frame updates are coming too slowly to get accurate physics, then maybe you can hack in the jump apex so that the player always hits it. The cue here might be when the y velocity changes from positive to negative. If I'm reading your pseudocode right, then it looks like:
old_cy = cy;
cy += jump;
if(old_cy > 0 && cy <= 0)
player.position.y = jump_apex_height;
In terms of your graph, the idea is that you want to identify the blue dot that reaches the orange line, then bump it up to the dotted line.
And now that I'm thinking about it, if the player really has to reach the jump apex every time, then this might help even for high-rate updates.
I am writing a fairly simple script in JavaScript using the canvas. It draws a central node which pulls all of the surrounding nodes towards it. This works great, however I need each node to repel each other.
I am going to do this by increasing each nodes velocity away from each other so eventually they should level out and end up looking something like a flower. It needs to be enough force to stop them from hitting each other or sinking into the center node without flying off into the distance.
I just can not work out how I can have a higher number the closer they get.
So if two nodes where 10px away from each other it would add 5 in force to one of their x velocities. But if they where 1000px away from each other then it would add almost nothing to the force of one of the nodes.
Does anyone know of a mathematical equation I can use to work this kind of thing out, or maybe a nudge in the right direction?
TL;DR: Depending on how close two x values are, I need to increment the x velocity of one node so they move apart but eventually level out. It is just the maths I can not crack, I have pretty much all of the JavaScript done, including the implementation of velocity.
Thanks, and sorry it is a bit wordy.
You just need an inverse (or inverse square) relationship:
var increment = k / distance;
or:
var increment = k / (distance * distance);
You can determine k based on the actual values you want, for example, in the first case, if you wanted an increment of 5 for a distance of 10, you would set k = increment * distance = 50.
Look into the equations governing electrical point charges, have the velocity be based on the "force" each "charge" would feel based on its proximity.
I'm looking to create a basic Javascript implementation of a projectile that follows a parabolic arc (or something close to one) to arrive at a specific point. I'm not particularly well versed when it comes to complex mathematics and have spent days reading material on the problem. Unfortunately, seeing mathematical solutions is fairly useless to me. I'm ideally looking for pseudo code (or even existing example code) to try to get my head around it. Everything I find seems to only offer partial solutions to the problem.
In practical terms, I'm looking to simulate the flight of an arrow from one location (the location of the bow) to another. I have already simulated the effects of gravity on my projectile by updating its velocity at each logic interval. What I'm now looking to figure out is exactly how I figure out the correct trajectory/angle to fire my arrow at in order to reach my target in the shortest time possible.
Any help would be greatly appreciated.
Pointy's answer is a good summary of how to simulate the movement of an object given an initial trajectory (where a trajectory is considered to be a direction, and a speed, or in combination a vector).
However you've said in the question (if I've read you correctly) that you want to determine the initial trajectory knowing only the point of origin O and the intended point of target P.
The bad news is that in practise for any particular P there's an infinite number of parabolic trajectories that will get you there from O. The angle and speed are interdependent.
If we translate everything so that O is at the origin (i.e. [0, 0]) then:
T_x = P_x - O_x // the X distance to travel
T_y = P_y - O_y // the Y distance to travel
s_x = speed * cos(angle) // the X speed
s_y = speed * sin(angle) // the Y speed
Then the position (x, y) at any point in time (t) is:
x = s_x * t
y = s_y * t - 0.5 * g * (t ^ 2)
so at impact you've got
T_x = s_x * t
T_y = -0.5 * g * (t ^ 2) + s_y * t
but you have three unknowns (t, s_x and s_y) and two simultaneous equations. If you fix one of those, that should be sufficient to solve the equations.
FWIW, fixing s_x or s_y is equivalent to fixing either speed or angle, that bit is just simple trigonometry.
Some combinations are of course impossible - if the speed is too low or the angle too high the projectile will hit the ground before reaching the target.
NB: this assumes that position is evaluated continuously. It doesn't quite match what happens when time passes in discrete increments, per Pointy's answer and your own description of how you're simulating motion. If you recalculate the position sufficiently frequently (i.e. 10s of times per second) it should be sufficiently accurate, though.
I'm not a physicist so all I can do is tell you an approach based on really simple process.
Your "arrow" has an "x" and "y" coordinate, and "vx" and "vy" velocities. The initial position of the arrow is the initial "x" and "y". The initial "vx" is the horizontal speed of the arrow, and the initial "vy" is the vertical speed (well velocity really but those are just words). The values of those two, conceptually, depend on the angle your bowman will use when shooting the arrow off.
You're going to be simulating the progression of time with discrete computations at discrete time intervals. You don't have to worry about the equations for "smooth" trajectory arcs. Thus, you'll run a timer and compute updated positions every 100 milliseconds (or whatever interval you want).
At each time interval, you're going to add "vx" to "x" and "vy" to "y". (Thus, note that the initial choice of "vx" and "vy" is bound up with your choice of time interval.) You'll also update "vx" and "vy" to reflect the effect of gravity and (if you feel like it) wind. If "vx" doesn't change, you're basically simulating shooting an arrow on the moon :-) But "vy" will change because of gravity. That change should be a constant amount subtracted on each time interval. Call that "delta vy", and you'll have to tinker with things to get the values right based on the effect you want. (Math-wise, "vy" is like the "y" component of the first derivative, and the "delta vy" value is the second derivative.)
Because you're adding a small amount to "vy" every time, the incremental change will add up, correctly simulating "gravity's rainbow" as your arrow moves across the screen.
Now a nuance you'll need to work out is the sign of "vy". The initial sign of "vy" should be the opposite of "delta vy". Which should be positive and which should be negative depends on how the coordinate grid relates to the screen.
edit — See #Alnitak's answer for something actually germane to your question.