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

2014/10/29

Having difficulties with randomly moving objects

cha0204 cha0204

2014/10/29

#
Hey guys. I'm currently doing an assignment for uni, where we are asked to imitate a PacMan game using the given vocabulary (the relevant vocab necessary to help answer my question):
treeFront(): Checks if there is a tree infront of the ghost. treeAbove(): Checks if there is a tree above the ghost. (Regardless of the direction of the ghost and relative to the screen). treeBelow: Checks if there is a tree below the ghost. (Regardless of the direction of the ghost and relative to the screen). treeToLeft: Checks if there is a tree to the left of the ghost. (Regardless of the direction of the ghost and relative to the screen). treeToRight: Checks if there is a tree to the right of the ghost. (Regardless of the direction of the ghost and relative to the screen). setDirection(string): Makes the ghost face a specific direction. "up" - face toward the top of the screen, "down" - face toward the bottom of the screen, "right" - face toward the right, "left" - face toward the left.
I've had some difficulties coding the ghosts as randomly moving objects. The issues were: 1. They were glitching at intersections (which are sections where they can move top/left/right/down): I fixed this by creating a turning counter, so that they could only turn once every so often.
//a variable to help keep the ghost from turning too much
    private boolean turning = false;
    //a counter for how much the ghost turns
    private int turningCounter = 25;
    //how much the ghost will turn
    private int turningSpeed = 25;
2. Even though I set the turning counter, they still seem to get stuck in random areas (bouncing left and right repeatedly). To be completely honest, I'm unsure as to what the '25' stands for, as initially I thought it symbolised how many squares (?) but I appear to be wrong. So the number I chose is completely random and based on how well the ghosts were running. So my main question is how can I change my code to make the ghosts run normally like the ghosts in class Pacman? Below is the code currently in the ghost class. To give a better idea on what the program looks like heres a print screen of the world: http://i.imgur.com/EYcV99h.png An example of the issues I'm dealing with is for example, in the ghost base they seem to be unable to exit the cave and instead continuously bounce between the trees. By the way, I'm relatively bad at java.
import greenfoot.*;  // (World, Actor, GreenfootImage, and Greenfoot)
import java.util.Random;

/**
 * Ghost Class
 * 
 * Available functions (see Assignment document for explanations on what each function does):
 * treeFront, treeAbove, treeBelow, treeToLeft, treeToRight,
 * getDirection, setDirection,
 * move,
 * isScared,
 * animate, animateDead, animateScared,
 * getClara, getGhostHealer,
 * isAboveMe, isBelowMe, isToMyLeft, isToMyRight,
 * makeClaraDead,
 * playGhostEatenSound,
 * isPacmanIntroStillPlaying,
 * wrapAroundWorld
 */
public class Ghost extends Character
{
    //Add and initialise Ghost variables here

    //Part5: Speed of Ghost's movement. 
    public int speedGhosts =1;

    //Part5: Ghost's animation
    private int imageCounter=0;

    private int moveConter=0;

    //Part5: Intro music
    public boolean hasIntroPlayed;

    //Part1: Has the actor reached the edge of the world. 
    public boolean isAtEdge=false;  

    public int number;

    //The direction Ghost is moving. 
    private boolean left=true;
    private boolean right=true;
    private boolean down=false;
    private boolean up=false; 

    //Stuck in the ghost base
    private boolean out;

   //a variable to help keep the ghost from turning too much
    private boolean turning = false;
    //a counter for how much the ghost turns
    private int turningCounter = 25;
    //how much the ghost will turn
    privateint turningSpeed = 25;

    /**
     * Act method, runs on every frame
     */
    public void act()
    {
        //Make the Ghost do things here

        if ( isPacmanIntroStillPlaying()==false )
        {
            //if ( treeFront() )
            //{
                ai();
                ghostMove();
            //}
            //else if ( !treeFront() )
            //{ 
                move ( speedGhosts );
                animateGhost();
                port();
                ghostMoveConditions();
            //}

        } 
    }
    //Give the Ghost functions here
    /**
     * Part 5: Ghost's animation
     */
    private void animateGhost()
    {
        if (imageCounter==10)    //Every 10 frames, she will animate. 
        {   
            animate();
            imageCounter=0;
        }
        else imageCounter++;      
    }

    /**
     * Part 5: The ghost's will wrap around.
     */
    private void port() 
    {
        int x=getX();    //Get the X location of Clara.
        int y=getY();    //Get the Y location of Clara.

        if ( x<=0 || x>=370 || y<=0 || y>=430 )
        {
            isAtEdge=true;                  
        }
        else isAtEdge=false;

        if ( isAtEdge==true )
        {
            wrapAroundWorld();
            isAtEdge=false;
        }
    }

    /**
     * Intro music
     */
    private void introMusic()
    {
        hasIntroPlayed=false;

        if ( isPacmanIntroStillPlaying()== false && hasIntroPlayed==false )
        {
            hasIntroPlayed=true;
        }

    }

    /**
     * If there is a tree infront, choose a random direction,
     */
    private void ai()
    {
        Random rand = new Random();
        int randomNumber = rand.nextInt(4)+1;

        if ( randomNumber == 1 && !turning )
        {
            left=true;
        }
        else if ( randomNumber == 2 && !turning )
        {
            right=true;
        }
        else if ( randomNumber == 3 && !turning )
        {
            up=true;
        }
        else if ( randomNumber == 4 && !turning )
        {
            down=true;
        }

    }

    private void ghostMove()
    {
        if ( left && !treeToLeft() && turning == false )
        {
            setDirection("left");
            turning = true;
        } 

        else if ( right && !treeToRight() && turning == false )
        {
            setDirection("right");
            turning = true;
        } 

        else if ( up && !treeAbove() && turning == false )
        {
            setDirection("up");
            turning = true;
        } 

        else if ( down && !treeBelow()  && turning == false )
        {
            setDirection("down");
            turning = true;
        }

        turningCounter();
    }

    private void ghostMoveConditions()
    {
        if ( left )
        {
            //80% chance to go up if able to
            if ( !treeAbove() && Greenfoot.getRandomNumber(100)>80 )
            {
                up = true;
                left = false;
            }
            
            //75% chance to go down if able to
            else if ( !treeBelow()&& Greenfoot.getRandomNumber(100)>75 ) 
            {
                down = true;
                left = false; 
            }
            
            //99%
            else  if ( !treeToRight() && Greenfoot.getRandomNumber(100)>50 )
            { 
                right = true;
                left = false;
            }
            //To ensure the ghost does not walk backward
            //Otherwise, left remains true and the direction will be set to left. 
        } 
        //No tree to the right
        else if ( right )
        {
            //80% chance to go up if able to
            if ( !treeAbove() && Greenfoot.getRandomNumber(100)>80 )
            {
                up = true;
                right = false;
            } 
            //75% chance to go down if able to
            else if ( !treeBelow() && Greenfoot.getRandomNumber(100)>75 )
            {
                down = true;
                right = false;
            }

            //99% chance to go left if able to
            else  if ( !treeToLeft() && Greenfoot.getRandomNumber(100)>50 )
            { 
                left = true;
                right = false;
            } 

            //Otherwise right remains and true and the direction wil be set to right            
        } 
        else if ( up )
        {           
            //80% chance to go left
            if ( !treeToLeft() && Greenfoot.getRandomNumber(1000)>80 )
            {
                left = true;
                up = false;
            } 
            //75% chance to go right
            else if ( !treeToRight() && Greenfoot.getRandomNumber(100)>75 )
            {
                right = true;
                up = false;
            }
            //99% chance to go down 
            else  if ( !treeBelow() && Greenfoot.getRandomNumber(100)>50 )
            { 
                down = true;
                up = false;
            }

        } 
        else if ( down )
        {
            //80% chance to go left
            if ( !treeToLeft() && Greenfoot.getRandomNumber(100)>80 )
            {
                left = true;
                down = false;
            } 
            //75% chance to go right
            else if ( !treeToRight() && Greenfoot.getRandomNumber(100)>75  )
            {
                right = true;
                down = false;
            }
            //99% chance to go up 
            else  if ( !treeAbove() && Greenfoot.getRandomNumber(100)>50)
            { 
                up = true;
                down = false;
            }
        }  

        ghostMove();
        
 
}

    /**
     * a counter that makes sure that the object does not turn serveral times in the same second
     */
    private void turningCounter()
    {
        if( turning == true && turningCounter < 0)
        {
            turning = false;
            turningCounter = turningSpeed;
        }
        else if( turning == true && turningCounter >= 0)
        {
            turningCounter --;
        }
    }
}
danpost danpost

2014/10/29

#
It appears your ghosts move one pixel at a time. Therefore, your 'turningCounter should probably be equal to the size of one 'grid-square' of your map (I think it might be '20', not 25'). Make sure you are placing your ghosts at a turning point to begin with or the counter will not be set properly with respect to its position.
Super_Hippo Super_Hippo

2014/10/29

#
I don't think that there is a reason for the counter or even the Boolean. As I did it in my Bomberman game, the AI does the following: They check the four directions to see if these directions are free to move (ground enemies other than air enemies). Every direction has a Boolean to save whether or not moving would be possible (or in my case whether or not moving is blocked). And then it decides the direction to where it should go. If only one direction is possible, it will move in this direction. If there is more than one possible, it will decide based on the last step and if this is not enough, randomly. I don't know how you check for trees. I use a modified 'canSee' method for it which checks for three points. This way it is made sure that another direction is only seen as a possible walking direction if the enemy is exactly in the middle of a crossroad. So if one step is made, the walls are detected and it won't turn more than one time. This makes the counter and Boolean unnecessary. You do it the wrong way around in my opinion. You get a random number in lines 134+135 (for some reason without the 'getRandomNumber' method) and choose a direction with it. You don't set the last one to false, so you have two of the four directions as true (or one if it was the same) And then, if there isn't a tree, you move to that direction (you first check to the left direction and so on (ll. 158ff). Then you move to the decided direction. At the end, you have this 'ghostMovingConditions' method (in which you have a '0' too many in line 240). Still remembering you (might) have move than one of the direction as 'true' right now, you (might) set the current direction to 'false' and one of the other three to 'true'. After that, you call 'ghostMove' again (which can set another direction if there isn't a tree. Still two moving directions can be true and in the next act-cycle, the random number might set another one to true. (So actually even all 4 can be true which doesn't make sense since you would only use the 'left' anyway.) I think you should change the order. What you have it right now:
  • set a random direction to true (without setting the others to false)
  • if possible, move to that direction
  • change the direction
Instead, try this:
  • check for the four directions and set a Boolean each
  • based on those Booleans, the last step (save this, too) and a random number if needed (only when having 3 or 4 possible directions), move to the chosen direction (and set the last-step variable)
danpost danpost

2014/10/29

#
It may be true that the boolean is not needed (as the value of the counter being zero indicates a turn); however, the counter does two things: (1) it allows the ghost to move freely from one crossroads to another without having to check each time if it can turn; and (2) it prevents the ghosts from "turning too much" (see line 49); I think what is meant here is that the ghosts, when arriving at a crossroads, will 'bounce' around within the intersection, seemingly indecisively (glitchy behavior). This behavior is caused by the a combination of image size (both of the ghosts and of the trees) and how the intersections are otherwise detected (and of the speed of the ghosts). Without the 'turningCounter' field, everything would have to be extremely precise to avoid glitching.
Super_Hippo Super_Hippo

2014/10/29

#
As long as they will move from crossroad to crossroad, it may be an addition, so the checking doesn't have to be every act-cycle. (Not for my scenario though, because bombs can make the enemy have to turn any time.) If the moving wants to be not so random anymore, for example, avoid the player after he ate a mushroom in this case. So the ghost would also decide the turning based on this state. The problem would be, if the ghost would turn 180 degree when the mushroom was eaten. Then the timer wouldn't be at the right value when reaching the crossroad. Maybe the speed is also changed when the ghosts are scared. This probably could be solved, too, but I am not sure if it will be easier this way. Still, try to follow my two points above and you can add the counter to it, if you want, so you only check every 20 (or how big your cells are there) act-cycles.
danpost danpost

2014/10/29

#
You can always subtract the counter value from the grid size when reversing to compensate. As an alternative, the x and y coordinates could be used to determine when turning may be possible. Let us say the world size (380, 580) and grid size is 20. Turning (reversing allowed anytime) would be allowed when '(getX()-10)%20 == 0 && (getY()-10)%20 == 0'.
cha0204 cha0204

2014/10/30

#
Hey guys thanks for the replies, really appreciated. I've been looking at the problem for so long I've just been randomly moving the methods around to see if they achieve anything. But anyways, I followed hippos advice and I came up with the following code:
/**
     * Act method, runs on every frame
     */
    public void act()
    {
        //Make the Ghost do things here

        //if ( !treeFront() )
        //{
        checkDirections();
        changeDirections();
        move(speedGhosts);
        resetBoolean();
        //}

    }
    //Give the Ghost functions here

    public void checkDirections()
    {
        if ( !treeToLeft() )
        {
            left=true;
        }

        if ( !treeToRight() ) 
        {
            right=true;
        }

        if ( !treeAbove() )
        {
            up=true;
        }

        if ( !treeBelow() ) 
        {
            down=true;
        }    

    }

    public void changeDirections()
    {
        //if there are 3+ possible directions

        if ( left && right && up || up && right && down || up && left && down )
        {
            if (Greenfoot.getRandomNumber(100)<30 && left )
            {
                setDirection("left");
            }
            else if (Greenfoot.getRandomNumber(100)<30 && right )
            {
                setDirection("right"); 
            }
            else if (Greenfoot.getRandomNumber(100)<30 && up )
            {
                setDirection("up"); 
            }
            else if (Greenfoot.getRandomNumber(100)<30 && down )
            {
                setDirection("down"); 
            }
            turning = true;
            turningCounter();

        }
        else if ( left )
        {
            if ( Greenfoot.getRandomNumber(100)<20 && up )
            {
                setDirection("up");
            }
            else if ( Greenfoot.getRandomNumber(100)<20 && down )
            {
                setDirection("down");
            }
            else if (Greenfoot.getRandomNumber(100)<20 && right )
            {
                setDirection("right");
            }

            else setDirection("left");
            turning = true;
            turningCounter();

        } 

        else if ( right && !turning )
        {
            if ( Greenfoot.getRandomNumber(100)<20 && up )
            {
                setDirection("up");
            }
            else if ( Greenfoot.getRandomNumber(100)<20 && down )
            {
                setDirection("down");
            }
            else if (Greenfoot.getRandomNumber(100)<20 && left )
            {
                setDirection("left");
            }

            else setDirection("right");
            turning = true;
            turningCounter();

        } 

        else if ( up && !turning )
        {
            if ( Greenfoot.getRandomNumber(100)<20 && left )
            {
                setDirection("left");
            }
            else if ( Greenfoot.getRandomNumber(100)<20 && right )
            {
                setDirection("right");
            }
            else if (Greenfoot.getRandomNumber(100)<20 && down )
            {
                setDirection("down");
            }

            else setDirection("up");
            turning = true;
            turningCounter();

        } 

        else if ( down && !turning )
        {
            if ( Greenfoot.getRandomNumber(100)<20 && up )
            {
                setDirection("up");
            }
            else if ( Greenfoot.getRandomNumber(100)<20 && left )
            {
                setDirection("left");
            }
            else if (Greenfoot.getRandomNumber(100)<20 && right )
            {
                setDirection("right");
            }

            else setDirection("down");
            turning = true;
            turningCounter();

        }

    }
   
    //Was unsure if this was necessary 
    public void resetBoolean()
    {
        left=false;
        up=false;
        right=false;
        down=false;
    }

    private void turningCounter()
    {
        if( turning == true && turningCounter < 0)
        {
            turning = false;
            turningCounter = turningSpeed;
        }
        else if( turning == true && turningCounter >= 0)
        {
            turningCounter --;
        }
    }
So basically the ghosts check what directions they can turn (checkDirections() ), then from the true boolean values they decide which way to turn? I'm not sure if I did it right, however, the ghosts still seem to be glitching... To give you an idea on what I mean: http://imgur.com/cypDVxN Also @dan about the turning counter, how do I make sure that the turning counter increments only when the ghosts are at an intersection? Once again, I'm pretty bad at java, so if I completely misread everything you said I'm really sorry! Thank you so much for the help
danpost danpost

2014/10/30

#
It is not that you increment it only when the ghosts are at an intersection. You increment it every act cycle and when the value is divisible by 20 then it can turn and the counter can be reset to zero.
Super_Hippo Super_Hippo

2014/10/30

#
The 'resetBoolean' method is needed as you do it. (In my scenario I just set them to 'true' or 'false' every time, but you can also do it like this. The 'changeDirections' method... well, I was thinking of something like this:
/** one possible direction **/
if (right && !left && !up && !down) setDirection("right");
else if (!right && left && !up && !down) setDirection("left");
else if (!right && !left && up && !down) setDirection("up");
else if (!right && !left && !up && down) setDirection("down");

/** two possible directions **/
else if (right && left && !up && !down)
{
    if (lastStep != 1) setDirection("right");
    else setDirection("left");
}
//...


/** three possible directions **/
else if (right && left && up && !down)
{
    switch(lastStep)
    {
        case 0: switch(r(3))
                    {
                        case 0: setDirection("left"); break;
                        case 1: setDirection("right"); break;
                        case 2: setDirection("up"); break;
                    } break;
        case 1: if (r(2)==0) setDirection("left");
                    else setDirection("up"); break;
        case 2: if (r(2)==0) setDirection("left");
                    else setDirection("right"); break;
        case 3: if (r(2)==0) setDirection("up");
                    else setDirection("right"); break;
    }
}
//...

/** four possible directions **/
else if (right && left && up && down)
{
    switch (lastStep)
    {
        case 0: switch(r(4))
                    {
                        case 0: case 1: setDirection("up"); break;
                        case 2: setDirection("right"); break;
                        case 3: setDirection("left"); break;
                    } break;
        case 1: //...
        case 2: /...
        case 3: //...
    }
}


//another method
public int r(int s)
{
    return Greenfoot.getRandomNumber(s);
}

//in your "setDirection" method, set the 'lastStep' to the correct value. You can also save the last step as a string if you prefer that
//you probably have something like this:
public void setDirection(String direction)
{
    switch(direction)
    {
        case "up": setLocation(getX(),getY()-1); lastStep=0; break;
        case "right": setLocation(getX()+1,getY()); lastStep=1; break;
        case "down": setLocation(getX(),getY()+1); lastStep=2; break;
        case "left": setLocation(getX()-1,getY()); lastStep=3; break;
    }
}
This is not complete, but I hope you will understand what I mean. It is a bit different from what I did, but quite similar.
You need to login to post a reply.