
JUMPING
Most beginners believe that the code needed to make an actor jump cannot be very difficult. There is actually quite a lot involved, however. Not only must the jump be initiated, but it must be regulated throughout the rising and falling process. Let us try to understand each individual step that is involved with jumping. In order for the actor to slow down while rising and speed up while falling, the vertical speed must increase at a regular rate. This would be the constant acceleration due to gravity.
​
/* with constant field */
private static final int GRAVITY = 1;
/* and with instance field */
private int vSpeed; // the current vertical speed of the actor
/* in act */
vSpeed += GRAVITY; // applying gravity
​
If the actor always returns to the same ground level, then its final speed would be the exact negative value of its initial jumping speed (jump strength). This fact can be used to regulate when a jump is initiated also.
​
/* with constant field */
private static final int GRAVITY = 1;
private static final int JUMP_STRENGTH = 15;
​
/* and still with instance field (here assigned an initial value) */
private int vSpeed = JUMP_STRENGTH;
​
/* in act (to initiate a jump using "space" key) */
if (vSpeed == JUMP_STRENGH && Greenfoot.isKeyDown("space"))
{
vSpeed = -JUMP_STRENGTH;
}
​
/* in act (for rising and falling) */
if (vSpeed < JUMP_STRENGTH)
{
setLocation(getX(), getY()+vSpeed);
vSpeed += GRAVITY;
}
​
Without the second condition for initiating a jump, the actor will "fly" and then not return to ground level, but stop in the air somewhere. That is because the code would then be initiating jumps while the actor was already in the air.
​
For a simple avoider where the actor jumps over approaching objects, the code above would be sufficient (using additional code to check for collisions, losing life points and ending the game). However, the code is inadequate for a platformer type game, where the actor might land on a platform (or some other object) or hit its head on something. Once more than one ground level is introduced or the jump is interfered with in any way, using JUMP_STRENGTH for the condition to jump and for determining if the actor is to move vertically (rise or fall) is useless. Another mechanism must be introduced for these. It is probably a good thing, anyway, as the value of vSpeed should not be JUMP_STRENGTH when at ground level -- it should be zero. Unfortunately, it being zero cannot be used for jumping because it will also be zero at the apex of a jump. Something is needed to give the state of the actor being on "ground" (at ground level or standing on something) or not. A boolean value would be sufficient in this regard. Too often, it is seen in the form of a returned value from a method that checks the current state of the actor (checks where the actor is and looks for an object below it that the actor might be standing on). Another thing too often seen is a boolean field that carries the current on ground state across to the following act step. By allowing vertical movement regardless of jumping (which just changes the current vertical speed), we can acquire the current on ground state within the execution of the method, eliminating the need for a boolean method or field (using a local boolean variable within the method). This would look like the following:
​
/* with constant fields */
private static final int GRAVITY = 1;
private static final int JUMP_STRENGTH = 15;
​
/* and with instance field */
private int vSpeed;
​
/* in act (code for vertical movement */
// get needed values
int halfHeight = getImage().getHeight()/2;
int groundLevel = getWorld().getHeight()-halfHeight;
// variable for "on ground" state
boolean onGround = false; // assume not on ground
// vertical movement
vSpeed += GRAVITY;
setLocation(getX(), getY()+vSpeed);
// ground check
if (getY() > groundLevel)
{
onGround = true;
setLocation(getX(), groundLevel);
vSpeed = 0;
}
// obstacle check
else
{
Actor obj = getOneIntersectingObject(Obstacle.class);
if (obj != null)
{
int dir = (int)Math.signum(vSpeed); // 1 : falling; -1 : rising
if (dir > 0) onGround = true;
int objHalfHeight = obj.getImage().getHeight()/2;
setLocation(getX(), obj.getY()-dir*(objHalfHeight+halfHeight));
vSpeed = 0;
}
}
// jump initiation
if (onGround && Greenfoot.isKeyDown("space"))
{
vSpeed = -JUMP_STRENGTH;
}
​
Notice that both 'if' blocks for ground and obstacle checking do essentially the same thing -- that is (1) set 'onGround' to true (if needed); (2) adjust the location of the actor (so as not to be below ground level or inside an obstacle); and (3) kills the vertical speed of the actor.
​
This final coding (for the act method) can be placed in a method called 'moveVertically' and called from the act method of the Actor subclass. Horizontal movement can be done in a separate method.
​