This site requires JavaScript, please enable it in your browser!
Greenfoot back
Jax
Jax wrote ...

2015/4/20

Need help With Jump n' run game (jumping/falling)

1
2
Jax Jax

2015/4/20

#
Hello everybody, I need a bit help with my jump n' run game. I managed to make the main actor jump and fall, but somehow the actor still falls inside the ground pretty often. Sometimes it works and it stays on the ground but most of the time the actor goes inside the ground. Actor and Blocks are all 60 px height. Also there is a problem where the actor jumps the whole time while pressing space. Is it possible to make the actor only jump one time and then it's only possible to jump again when the actor is on the ground again? I would really appreciate help :) Thanks!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import greenfoot.*;
 
public class Peach extends Actor
{
    private int vSpeed = 0;
    private int acceleration = 2;
     
    public void act()
    {
        checkKeys();
        checkFall();
    }
     
    public void checkKeys() {
        if (Greenfoot.isKeyDown("left")) {
            setLocation(getX()-2, getY());
        }
        else if (Greenfoot.isKeyDown("right")) {
            setLocation(getX()+2, getY());
        }
        else {
            setLocation(getX()+1, getY());
        }
        if (Greenfoot.isKeyDown("space")){
            jump();
        }
         
    }
     
    public void checkFall(){
        if(onGround()){
        }
        else{
            fall();
        }    
    }
     
    public boolean onGround(){
        Actor unter = getOneObjectAtOffset( 0, 30, Ground.class );
        return unter != null;
    }
 
     
    public void jump(){
        vSpeed = -11;
        fall();
    }
     
    public void fall(){
        setLocation( getX(), getY() + vSpeed);
        vSpeed = vSpeed + acceleration;
    }
 
}
Jax Jax

2015/4/20

#
Nevermind, it seems that i fixed the issue with just including the following condition for jump
1
2
3
if (Greenfoot.isKeyDown("space") && onGround()){
    jump();
}
Thanks anyways, I will probably need help with other problems haha
Jax Jax

2015/4/20

#
It somehow only works with the bottom 2 rows of blocks. If I place other blocks (via world, not shift + click), it seems to bug again. Any solutions for that maybe? :) (here is an image of the bottom 2 rows, works fine)
Super_Hippo Super_Hippo

2015/4/20

#
Are these blocks Ground objects as well? You could change line 51 to this:
1
2
if (isTouching(Ground.class)) setLocation(getX(), getY()-vSpeed);
else vSpeed = vSpeed + acceleration;
Or you have 'onGround' as a boolean, then it could look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private boolean onGround;
private int vSpeed = 0;
 
public void act()
{
    int dx=0;
    if (Greenfoot.isKeyDown("right")) dx+=2;
    if (Greenfoot.isKeyDown("left") dx-=2;
    if (dx==0) dx++;
    setLocation(getX()+dx, getY());
    if (isTouching(Ground.class)) setLocation(getX()-dx, getY());
     
    if (Greenfoot.isKeyDown("space") && onGround) jump();
 
    if (!onGround) fall();
}
 
public void jump()
{
    vSpeed = -11;
    onGround = false;
}
 
public void fall()
{
    setLocation(getX(), getY()+vSpeed);
    if (isTouching(Ground.class))
    {
        setLocation(getX(), getY()-vSpeed);
        onGround = true;
    }
    else vSpeed += acceleration;
}
Jax Jax

2015/4/20

#
Hello, thanks for the answer! Could you maybe add a little explanation on what which part does ? Since I'm having a bit of a hard time understanding everything :) The top one doesn't work by the way, the actor can't jump anymore then :) Thanks!
Super_Hippo Super_Hippo

2015/4/20

#
Changed line 1, maybe it works now?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private boolean onGround = true; //actor on ground? Starting on ground
private int vSpeed = 0; //current vertical speed
 
public void act()
{
    int dx=0; //declare dx
    if (Greenfoot.isKeyDown("right")) dx+=2; //increase dx by 2 if the right key is pressed
    if (Greenfoot.isKeyDown("left") dx-=2; //decrease dx by 2 if the left key is pressed
    if (dx==0) dx++; //increase dx by one if none or both or pressed
    setLocation(getX()+dx, getY()); //move
    if (isTouching(Ground.class)) setLocation(getX()-dx, getY()); //move back if it touches a Ground object
     
    if (Greenfoot.isKeyDown("space") && onGround) jump(); //jump if space is pressed and actor is on ground
 
    if (!onGround) fall(); //fall if not on ground
}
 
public void jump()
{
    vSpeed = -11; //set vertical speed to -11 to make it move up in the fall method
    onGround = false; //not on ground anymore because it jumped
}
 
public void fall()
{
    setLocation(getX(), getY()+vSpeed); //move up or down
    if (isTouching(Ground.class)) //if touching Ground object
    {
        setLocation(getX(), getY()-vSpeed); //move back
        onGround = true; //reached bottom, on ground is true
    }
    else vSpeed += acceleration; //if not, increase vertical speed by acceleration (which is 2)
}
danpost danpost

2015/4/20

#
The code given by Super_Hippo will most probably work (however, I did not test it or even look it over too much). What caught my attention was the use of the 'boolean onGround' field; and how it is used on line 15 to regulate falling. I feel that the code does not represent the true nature of falling; and because of this, the code is made more complicated than it needs to be. Just because the actor is on the ground, that does not preclude gravity from effecting the actor (which, in essence, is what the line says). What, then, is missing is setting the vertical speed to zero when an obstacle (ground or otherwise) is contacted. The 'onGround' state can be determined at that time and used in the jump code later in the method using a local variable (instead of an instance field). The 'onGround' state is determined by the direction of vertical movement when intersection occurs.
1
2
3
4
5
6
7
8
9
10
11
12
13
// vertical movement
vSpeed += acceleration; // gravity
setLocation(getX(), getY()+vSpeed); // move (fall or rise)
boolean onGround = false; // for current 'on ground' state
Actor ground = getOneIntersectingObject(Ground.class); // get ground intersector
if (ground != null) // if intersects ground
{
    int direction = (int)Math.signum(vSpeed); // get vertical direction
    if (direction == 1) onGround = true; // set on ground state
    vSpeed = 0; // vertical movement is stopped
    while (isTouching(Ground.class)) setLocation(getX(), getY()-direction); // move off of ground actor
}
if (onGround && Greenfoot.isKeyDown("space") vSpeed -= 11; // jump
The 'fall' method in the code Super_Hippo supplied does not appear to take into account which direction the actor is moving (up or down) when a Ground object is contacted. If the actor is moving up and hits its head on the underside of a Ground object, the 'onGround' field will be set to 'true' and the actor will stop, suspended underneath the Ground object because 'fall' is not called when 'onGround' is 'true'.
Jax Jax

2015/4/20

#
danpost wrote...
The code.
Thank you very much! :) There are two problems now though hehe: 1) The actor is jumping a bit too fast. Is it maybe possible to slow down the jump? 2) I limited the movement of the actor so it's not possible to go through objects. However, depending on where the actor lands on a block, it can happen that the actor gets "teleported" onto the block, instead of staying left/right of it. Here is what I mean: The actor was right of the block and then get's teleported onto it. I use the infinite scrolling engine you made so the game scrolls from left to right. All things are moved 1px to the right with every act then. Could that maybe cause the bug? Like if the actor is normally besides the block, but then everything moves and the actor bugs onto the block? Movement limitation:
1
2
3
4
5
6
7
8
9
10
if (Greenfoot.isKeyDown("left") && getOneObjectAtOffset( -25, 0, Ground.class ) == null) {
    animleft();
    setLocation(getX()-2, getY());
    anileft = anileft + 2;
}
else if (Greenfoot.isKeyDown("right") && getOneObjectAtOffset( 25, 0, Ground.class ) == null) {
    animright();
    setLocation(getX()+3, getY());
    aniright = aniright + 2;
}
(animleft/right and anileft/right are animation methods/variables)
Jax Jax

2015/4/20

#
Okay nevermind, I fixed the issue. I forgot to remove the other fall method and because of that the actor jumped really fast. *facepalm* Another issue though: The actor can get suddenly removed when touching blocks: I don't really know how that happens to be honest haha gifv of it It was with the fast/bugged fall/jump. But still happens with the updated, slower one :)
Super_Hippo Super_Hippo

2015/4/20

#
I can't open this 'gifv' (no program on my computer). The only thing I see which could cause a "removing" (and is here in this thread) is this line:
1
while (isTouching(Ground.class)) setLocation(getX(), getY()-direction);
It says "as long as it touches a Ground object, move one pixel up or down depending on the vSpeed". If it moves up, it is teleported onto the block. If it moves down, it is teleported below the block (not visible anymore). Unfortunately I can't tell you to 100% why this happens. I would only suggest to move right and left like I described instead of using 'getOneObjectAtOffset'. It could be that this method doesn't return a block although the actor is touching one after it moved there. This could especially be true because you check for ±25 to right/left side but move 2 to the left and 3 to the right. So it would move there and is touching the block afterwards. If you go into the block when falling, you will be set onto the block. If you walk into a block and try to jump then or you jump into a block, you will "disappear".
danpost danpost

2015/4/20

#
You should probably post you revised class code so we know what we are now working with.
Jax Jax

2015/4/20

#
Here is the new code. Somehow the jump animation bugs... :/ after the jump animation is over, the image stays at peach_jump6.png and is not changed while moving left, right. Only changed when jumping. Long code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import greenfoot.*;
 
public class Peach extends Actor
{
    private int vSpeed = 0; //fall speed
    private int acceleration = 1; //gravity
    private int anileft = 0; //animation variable left
    private int aniright = 0; //animation variable right
    private int anijump = 0; //animation variable jump
     
    public void act()
    {
        checkKeys(); //checks if keyse are pressed
        movement();
        checkDeath();
    }
     
    public void checkKeys() { //checks different keys and performs the called actions
        setLocation(getX()+1, getY());
        if (Greenfoot.isKeyDown("left") && getOneObjectAtOffset( -25, 0, Ground.class ) == null) {
            animleft();
            setLocation(getX()-3, getY());
            anileft = anileft + 2;
        }
        else if (Greenfoot.isKeyDown("right") && getOneObjectAtOffset( 25, 0, Ground.class ) == null) {
            animright();
            setLocation(getX()+3, getY());
            aniright = aniright + 2;
        }
        else if(onGround()) {
            setImage("peach_stand.png");
            anileft = -10;
            aniright = -10;
        }
         
    }
     
    public void movement(){
        // vertical movement
        vSpeed += acceleration; // gravity
        setLocation(getX(), getY()+vSpeed); // move (fall or jump)
        boolean onGround = false; // for current on ground state
        Actor ground = getOneIntersectingObject(Ground.class); // get ground intersector
        if (ground != null){ //if actor is inside ground
            int direction = (int)Math.signum(vSpeed); // get vertical direction
            if (direction == 1) onGround = true; // actor is on ground
            vSpeed = 0; // vertical movement is stopped
            while (isTouching(Ground.class)) setLocation(getX(), getY()-direction); // move off of ground
        }
        if (onGround && Greenfoot.isKeyDown("space")) {   
            vSpeed -= 18; // jump
        }
        animjump();
    }
    
    public void animleft(){ //animation for left movement
        if (anileft == -10 && onGround()){
            setImage("peach_startrun.png");
        }
        else if (anileft == 0 && onGround()){
            setImage("peach_1.png");
        }
        else if (anileft == 10 && onGround()){
            setImage("peach_2.png");
        }
        else if (anileft == 20 && onGround()){
            setImage("peach_3.png");
        }
        else if (anileft == 30 && onGround()){
            setImage("peach_4.png");
        }
        else if (anileft == 40 && onGround()){
            setImage("peach_5.png");
        }
        else if (anileft == 50 && onGround()){
            setImage("peach_6.png");
        }
        else if (anileft == 60 && onGround()){
            setImage("peach_7.png");
        }
        else if (anileft == 70 && onGround()){
            setImage("peach_8.png");
            anileft = 0;
        }
    }
     
    public void animright(){ //animation for right movement
        if (aniright == -10 && onGround()){
            setImage("peach_rstartrun.png");
        }
        else if (aniright == 0 && onGround()){
            setImage("peach_r1.png");
        }
        else if (aniright == 10 && onGround()){
            setImage("peach_r2.png");
        }
        else if (aniright == 20 && onGround()){
            setImage("peach_r3.png");
        }
        else if (aniright == 30 && onGround()){
            setImage("peach_r4.png");
        }
        else if (aniright == 40 && onGround()){
            setImage("peach_r5.png");
        }
        else if (aniright == 50 && onGround()){
            setImage("peach_r6.png");
        }
        else if (aniright == 60 && onGround()){
            setImage("peach_r7.png");
        }
        else if (aniright == 70 && onGround()){
            setImage("peach_r8.png");
            aniright = 0;
        }
    }
     
    public void animjump(){ //animation for jump movement
        if (vSpeed <= -10 && !onGround()){
            setImage("peach_jump1.png");
        }
        else if (vSpeed < -4 && vSpeed > -10 && !onGround()){
            setImage("peach_jump2.png");
        }
        else if (vSpeed > -4 && vSpeed < 0 && !onGround()){
            setImage("peach_jump3.png");
        }
        else if (vSpeed > 0 && vSpeed < 4 && !onGround()){
            setImage("peach_jump4.png");
        }
        else if (vSpeed > 4 && vSpeed < 10 && !onGround()){
            setImage("peach_jump5.png");
        }
        else if (vSpeed > 10 && !onGround()){
            setImage("peach_jump6.png");
        }
    }
     
    public boolean onGround(){ //checks if there is Ground under the actor
        Actor unter = getOneObjectAtOffset( 0, 30, Ground.class );
        return unter != null;
    }
     
    public void checkDeath(){
       Actor lava = getOneIntersectingObject(Lava.class); // get Lava intersector
        if (lava != null){ //if actor is inside lava
            Greenfoot.stop();
        }
    }
 
}
Super_Hippo Super_Hippo

2015/4/20

#
Does it change anything if you use 'onGround' instead of 'onGround()'? You have this boolean, so you don't need the method anymore. For example the 'animLeft' method can look like this:
1
2
3
4
5
6
7
8
9
10
11
public void animleft()
{
    if (!onGround) return;
     
    if (anileft == -10) setImage("peach_rstartrun.png");
    else if (anileft % 10 == 0)
    {
        setImage("peach_"+ (anileft/10+1) +".png");
        if (anileft == 70) anileft = 0;
    }
}
Jax Jax

2015/4/20

#
Thank you! :) Now everything works again (I had to put the vertical movement method before the checkKey method too, so now the image gets replaced on Ground :) Only the bug with the actor dissappearing is left. It happens when I jump against a block. Is it maybe possible to limit a range of pixels for the movement ? I mean if it's possible to code, if a block is in 0-30px range of the actor, moving in that direction is not possible. currently the movement commands are like that :
1
2
3
4
5
6
7
8
9
10
if (Greenfoot.isKeyDown("left") && getOneObjectAtOffset( -30, 0, Ground.class ) == null) {
    animleft();
    setLocation(getX()-5, getY());
    anileft = anileft + 2;
}
else if (Greenfoot.isKeyDown("right") && getOneObjectAtOffset( 30, 0, Ground.class ) == null) {
    animright();
    setLocation(getX()+5, getY());
    aniright = aniright + 2;
}
So if it would be possible to have a range of 0-30 instead of just the fixed 30, it should probably work? Thanks! :)
Super_Hippo Super_Hippo

2015/4/21

#
I already stated it. If 'getOneObjectAtOffset' returns null, it doesn't mean you don't touch the object when you are moving.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int dx = 0;
if (Greenfoot.isKeyDown("right")) dx += 5;
if (Greenfoot.isKeyDown("left") dx -= 5;
if (dx != 0)
{
    setLocation(getX()+dx, getY());
    if (isTouching(Ground.class)) setLocation(getX()-dx, getY());
    else
    {
        if (dx>0)
        {
            animright();
            aniright += 2;
        }
        else
        {
            animleft();
            anileft += 2;
        }
    }
}
There are more replies on the next page.
1
2