Next we need to decide what z value would be the ground. This is fairly arbitrary, you can pick any value. I initially used 1, then changed it to 100, mainly to simplify the math with the perspective scaling. We also need to decide what direction z goes, whether into the screen or out of it. It doesn't matter which, but it does affect the values you'd use for vz and g.
Let's be simple and say z=0 is where the ground is. Let's also say that into the screen is positive z, and for our physics lets consider positive z as down.
So if gravity is down then we can make g = 10. So the object will accelerate down into the screen.
To make a nice arc instead of a freefall we'd make vz=-10 so it goes up first.
Here's a nice diagram of our setup showing where the ground is, and the directions of gravity and the initial velocity:
Next we can figure out when and how to bounce.
Well it makes sense that a ball would bounce when it collides with the ground. So a collision would be when z=0? Yes, but the calculations can make the z move past 0. That would make a collision when the z is greater than or equal to 0. So viola we now can detect when the object is on the ground, aka this can be our condition.
Here's a visual of the z position frame by frame and a possible example when a collision is detected:
Notice the object can have moved past the ground before the collision is detected. Let's fix it by moving it to be on the ground.
sprite: z>=0
-- sprite: set z to 0
Note: this pretty much does what I used the max() expression for in my op.
Now that the object is safely resting on the ground, even when it tries to move past, we can now consider how to bounce.
Bouncing is really simple, just reverse the direction of the velocity. We do that by making the velocity negative.
sprite: set vz to -self.vz
It will bounce to about the same height as it was dropped from. To make it bounce less we can just multiply the velocity by a number between 0 and 1 when we reverse it. Let's make each bounce go only half as high, so we'll use 0.5.
sprite: set vz to -0.5*self.vz
Sweet. So that would make the whole collision detection and bounce event this:
sprite: z>=0
-- sprite: set z to 0
-- sprite: set vz to -0.5*self.vz
All that takes care of the motion and bouncing, but it gives us no visuals. We want the sprite to scale as if it was getting closer or further from us as the z changes. So first lets look at how such scaling is done. A diagram is great for this:
We see an eye, an object and the screen. The size of the object's shadow on the screen is the size we want to scale the object to to make it seem that close.
Notice that the triangle from the eye to the object is a scaled version of the triangle from the eye to the screen. For those interested you can dirive the the equation for z scaling from this. I'll keep this simpler by just giving the equation:
sprite: set scale to eyeDistance/(self.z + eyeDistance)
In similar fashion you can move the xy position with something about the same for more effect.
eyeDistance is the z distance from the eye to the screen. We just choose a value for this. 100 works well for our purposes, but we can change that to adjust the strength of the effect.
Anyways here is the whole example:
{Editor setup:}
Sprite:
z=0
vz=-10
{Events:}
global number g = 10
global number eyeDistance = 100
every tick:
-- sprite: add g*dt to vz
-- sprite: add self.vz*dt to z
sprite: z>=0
-- sprite: set z to 0
-- sprite: set vz to -0.5*self.vz
every tick:
-- sprite: set scale to eyeDistance/(self.z + eyeDistance)