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

2013/6/22

creating buffer to stop sinking when falling

davemib123 davemib123

2013/6/22

#
Hi, I have attempted to make some sort of gravity in my game. But the characters keeps sinking into the tile. I know where the error is within the checkGround() method but I am unsure on how to fix it. Scenario is located here: http://www.greenfoot.org/scenarios/8881
Zamoht Zamoht

2013/6/22

#
Okay first of all you have to change private int marioHeight = getImage().getHeight() / 2; to private int marioHeight; Or well you don't have to, but the first statement doesn't do anything since the program sets marioHeight before the actors image so that getImage() will be equal null. So instead i made a little change to:
public Mario()
    {
        setImage(RMidle);
        walking = false;
        facingRight = true;

        RMidle.scale(38,50);
        RMjump.scale(51,50);
        RMwalk1.scale(48,50);
        RMwalk2.scale(36,50);
        RMwalk3.scale(44,50);

        LMidle.scale(38,50);
        LMjump.scale(51,50);
        LMwalk1.scale(48,50);
        LMwalk2.scale(36,50);
        LMwalk3.scale(44,50);

        LMidle.mirrorHorizontally();
        LMjump.mirrorHorizontally();     
        LMwalk1.mirrorHorizontally();     
        LMwalk2.mirrorHorizontally();     
        LMwalk3.mirrorHorizontally();   
        
        marioHeight = getImage().getHeight() / 2;
    }
Here the image is set and will therefore not be null. Okay that was the minor part. So here comes the real fix:
public void act() 
    {
        checkKeys();
        if(!checkGround())
        {
            fall();
        }
    }
public void fall()
    {
        setLocation (getX(), getY()+vSpeed); 
        vSpeed = vSpeed + acceleration;
        checkGround();
    }
public boolean checkGround()
    {
        Actor tile = getOneObjectAtOffset(0, marioHeight - 1, Tile.class);  
        if(tile == null) 
        {
            tile = getOneObjectAtOffset(0, marioHeight, Tile.class);
            if(tile == null)
            {
                return false;
            }
            return true;
        }
        else
        {
            boolean fixed = false;
            while(!fixed)
            {
                tile = getOneObjectAtOffset(0, marioHeight - 1, Tile.class);
                if(tile == null)
                {
                    fixed = true;
                }
                else
                {
                    setLocation(getX(), getY() - 1);
                }
            }
            vSpeed = 0;
            return true;
        }
    }
I don't feel like explaining the code since I have a feeling that you are pretty well known with coding in Greenfoot, if I'm wrong please ask about the code.
Zamoht Zamoht

2013/6/22

#
Now you have another problem to deal with. When Mario goes to the edge he will fall when half of his body is off the edge, this problem occurs because you only check for tiles right under Mario. A way to fix this would be to check for tiles twice, first tiles under his right foot and then tiles under his left foot. Okay I got excited and wrote the code :)
public boolean checkGround()  
    {  
        Actor tileLeft = getOneObjectAtOffset(-18, marioHeight - 1, Tile.class);
        Actor tileRight = getOneObjectAtOffset(18, marioHeight - 1, Tile.class);
        if(tileLeft == null && tileRight == null)   
        {  
            Actor tile = getOneIntersectingObject(Tile.class);  
            if(tile == null)  
            {  
                return false;  
            }  
            return true;  
        }  
        else  
        {  
            boolean fixed = false;  
            while(!fixed)  
            {  
                tileLeft = getOneObjectAtOffset(-18, marioHeight - 1, Tile.class);
                tileRight = getOneObjectAtOffset(18, marioHeight - 1, Tile.class);
                if(tileLeft == null && tileRight == null)  
                {  
                    fixed = true;  
                }  
                else  
                {  
                    setLocation(getX(), getY() - 1);  
                }  
            }  
            vSpeed = 0;  
            return true;  
        }  
    } 
Only use it if you want to.
danpost danpost

2013/6/22

#
That seems like a lot of coding for something so straight-forward.
public boolean checkGround()
{
    if (getOneObjectAtOffset(0, marioHeight, Tile.class) == null) return false;
    while (getOneObjectAtOffset(0, marioHeight-1, Tile.class) != null) setLocation(getX(), getY()-1);
    vSpeed = 0;
    return true;
}
This should do the same thing, but much more efficiently. Please note that this does not check for Left and Right side tiles; but, you can expand the conditions to include them, if you wish (both in the 'if' and the 'while' statements).
Zamoht Zamoht

2013/6/22

#
My bad sorry for the amount of code. To defend myself I want to say that I was more focused on writing some code that worked than also making it efficient, but thanks for cleaning my code danpost :)
davemib123 davemib123

2013/6/22

#
Guys many thanks for the posts. Danpost, If i understood your code correctly does it mean: if Mario is at a y-location that is half its height and at the centre of the tile class then move its y-location up by 1 and set its vSpeed to 0. if false then do nothing.
danpost danpost

2013/6/22

#
davemib123 wrote...
Guys many thanks for the posts. Danpost, If i understood your code correctly does it mean: if Mario is at a y-location that is half its height and at the centre of the tile class then move its y-location up by 1 and set its vSpeed to 0. if false then do nothing.
Yes. But, to state it more clearly, 'First, if not on ground, do nothing; else (understood, due to return statement), while intersecting excess ground, move up; and set vSpeed to 0.'
davemib123 davemib123

2013/6/23

#
Thanks for the reply danpost. Could you explain why these two statements give me different results: statement 1:
           Actor marioLeftFoot = getOneObjectAtOffset(-19, marioHeight - 1, Tile.class);
        Actor marioRightFoot = getOneObjectAtOffset(19, marioHeight - 1, Tile.class);
        
        while (marioLeftFoot != null || marioRightFoot != null)
        {
            setLocation(getX(), getY()-1);  
        }
statement 2:
     while (getOneObjectAtOffset(-19, marioHeight - 1, Tile.class) != null || (getOneObjectAtOffset(19, marioHeight - 1, Tile.class) != null))
        {
            setLocation(getX(), getY()-1);  
        }
I would have assumed they both do the same? But the first keeps the character up by about 25 pixels whereas the second keeps it as it should be
davemib123 davemib123

2013/6/23

#
also i noticed that if I use statement 1 Greenfoot compiles but gives me a blank screen. Whereas, if I use statement 2 Greenfoot compiles and shows the actors on screen.
davmac davmac

2013/6/23

#
In the first version, marioLeftFoot and marioRightFoot are assigned before the 'while' loop begins. They are either null or not. During the loop, they are not modified, so if one of them isn't null then it will remain non-null and the loop will be an infinite loop. That's why the world doesn't display.
davemib123 davemib123

2013/6/23

#
davmac does that mean that I can not use a reference as shown in statement 1?
Zamoht Zamoht

2013/6/23

#
It means that statement 1 have to look like this instead:
Actor marioLeftFoot = getOneObjectAtOffset(-19, marioHeight - 1, Tile.class);  
Actor marioRightFoot = getOneObjectAtOffset(19, marioHeight - 1, Tile.class);  
  
while (marioLeftFoot != null || marioRightFoot != null)  
{  
    setLocation(getX(), getY()-1);   
    marioLeftFoot = getOneObjectAtOffset(-19, marioHeight - 1, Tile.class);  
     marioRightFoot = getOneObjectAtOffset(19, marioHeight - 1, Tile.class); 
}  
So it updates wether Mario is free from the ground or not.
davemib123 davemib123

2013/6/23

#
thanks Zamoht. I was trying to create a reference where both the if and while statement could use.
danpost danpost

2013/6/23

#
It is not a problem, but it does not make much sense to keep a reference to the actor since it is always being modified. The extra unnecessary actions performed by the instructions include creating two references each act cycle and modifying those references multiple times; flagging those references for garbage collection will also need to be executed at the end of the method each act cycle. By using the following, all this is avoided:
public boolean checkGround()
{
    if (getOneObjectAtOffset(0, marioHeight, Tile.class) == null &&
        getOneObjectAtOffset(19, marioHeight, Tile.class) == null &&
        getOneObjectAtOffset(-19, marioHeight, Tile.class) == null)
            return false;
    while (getOneObjectAtOffset(0, marioHeight-1, Tile.class) != null ||
           getOneObjectAtOffset(19, marioHeight-1, Tile.class) != null ||
           getOneObjectAtOffset(-19, marioHeight-1, Tile.class) != null))
               setLocation(getX(), getY()-1);
    vSpeed = 0;
    return true;
}
If you were to go ahead and create references to the intersecting Tile objects, you could just use the following:
public boolean checkGround()
{
    Actor tileLeft = getOneObjectAtOffset(-19, marioHeight, Tile.class);
    Actor tileDown = getOneObjectAtOffset(0, marioHeight, Tile.class);
    Actor tileRight = getOneObjectAtOffset(19, marioHeight, Tile.class);
    if (tileDown == null && tileRight == null && tileLeft == null) return false;
    Actor highTile = tileLeft;
    if (highTile == null || (!tileDown == null && highTile.getY() > tileDown.getY())) highTile = tileDown;
    if (highTile == null || (!tileRight == null && highTile.getY() > tileRight.getY())) highTile = tileRight;
    setLocation (getX(), highTile.getY()-highTile.getImage().getHeight()/2-marioHeight);
    vSpeed = 0;
    return true;
}
davemib123 davemib123

2013/6/25

#
Thanks for the comment danpost. Point noted :)
You need to login to post a reply.