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

2014/5/10

Snake game issue

gamer121 gamer121

2014/5/10

#
Hi, I am making a snake/worm game but I have no idea how to keep on adding Tails to the worm so it gets larger after it eats a Food object. My code for the Worm class is shown below (I have added a tail every time the worm eats the food). What do I need to put in the Tail class to make this work?
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
public class Worm extends Actor

{   
   private static final int EAST = 0;
    private static final int SOUTH = 90;
    private static final int WEST = 180;
    private static final int NORTH = 270;
    private int counter = 0;
    public Worm() 
    {
        GreenfootImage img = new GreenfootImage(Ground.CELL_SIZE,Ground.CELL_SIZE);
        img.drawRect(0,0,Ground.CELL_SIZE,Ground.CELL_SIZE);
        img.fillRect(0,0,Ground.CELL_SIZE,Ground.CELL_SIZE);
        setImage(img);
    }  

    public void act()

    {
        checkKeys();
        moveForward();
        if( atEdgeOfWorld() ) {
            getWorld().removeObject(this);
            Greenfoot.stop();
        }else{
            Actor food = getOneIntersectingObject(Food.class);
            if(food!=null) {
                getWorld().addObject(new Tail(),getX(),getY());
                getWorld().addObject(new Food(),Greenfoot.getRandomNumber(40),Greenfoot.getRandomNumber(30));
                getWorld().removeObject(food);
            }
        }
    }

    public void moveForward()

    {
        counter++;
        if( counter == 3 ) {
            move(1);
            counter = 0;
        }
    }


    public void checkKeys()

    {
        if( Greenfoot.isKeyDown("right") ) {
            setRotation(EAST);
        }
        if( Greenfoot.isKeyDown("down") ) {
            setRotation(SOUTH);
        }
        if( Greenfoot.isKeyDown("left") ) {
            setRotation(WEST);
        }
        if( Greenfoot.isKeyDown("up") ) {
            setRotation(NORTH);
        } 
    }

    private boolean atEdgeOfWorld()    
    {
        return getX()<=-1 || getY()<=-1 || getX()>=getWorld().getWidth()+0|| getY()>=getWorld().getHeight()+0;
    }
}
danpost danpost

2014/5/10

#
You should probably post the Tail class also.
gamer121 gamer121

2014/5/10

#
This is all I have:
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
public class Tail extends Actor
{   private int trans = 350
    public void act() 
    {
        GreenfootImage img = new GreenfootImage(20,20);
        img.drawRect(0,0,20,20);
        img.fillRect(0,0,20,20);
        setImage(img);
        trans = trans - 5;
        if(trans<5) {
            getWorld().removeObject(this);
        }
    }   
    }

danpost danpost

2014/5/10

#
Ok. I see that the tail actors are stationary and remove themselves after a little time. This will shorten the size of the snake. Unless you continuously add Tail actors back into the world, your worm/snake will be reduced to just the head. What do you have to compensate for this? Should the Tail actors really be temporary? If so, then you need to add another tail when one is removed to maintain the same length (or relocate them, instead of removing them). Another problem I foresee is the length of time that they are in a specific location (whether being removed or relocated). The longer the worm/snake is, the longer they need to remain in place (provided the speed of the worm/snake is constant). The difference in time from one length to the next is the time it takes for one segment to move to the location of the one preceeding it (in act cycles).
gamer121 gamer121

2014/5/10

#
Ok. I was hoping to make it so that every time the worm eats a Food object, it grows by 1 cell and the worm's body follows the worm's head by creating an illusion to drop body parts (black squares) into the world where the worm's head is. Then remove the body part after it has been in the world a certain length of time. That way, as the worm's head moves, the body part remains for a period of time. The longer it remains in the world, the longer the worm's body appears to be.
gamer121 gamer121

2014/5/10

#
That was what I was trying to achieve in my Tail class. What do you suggest I put in that class?
danpost danpost

2014/5/10

#
A consequence of doing it that way is that the growth of the worm will not be realized until the worm has moved its total length in distance. The effect will be most noticeable when the snake is relatively short in length. The only way to avoid this, without re-writing the entire movement code, is to add a small amount to the lifespan of ALL Tail objects when food is eaten by the Worm object. It might be easier to use a 'static int' field in the Tail class to hold the current maximum lifespan and have it be the target value of the counters in each of the Tail objects. That way, just changing the one value, the lifespan of all the Tail objects are automatically increased. What values did you use to create your world (width, height, and cellsize)? Really, I just need the cellsize. Actually, I think I already know what I need to know. Try this for a Tail class.
import greenfoot.*;

public class Tail extends Actor
{
    public static int maxLife = Ground.CELL_SIZE;

    private int lifespan;

    public Tail()
    {
        GreenfootImage img = new GreenfootImage(20,20);
        img.drawRect(0,0,20,20);
        img.fillRect(0,0,20,20);
        setImage(img);
    }

    public void act()
    {
        lifespan++;
        if (lifespan == maxLife) {
            getWorld().removeObject(this);
        }
    }   
}
I moved the code creating the image of the Tail objects into a constructor block since you only need to do it once (when the object is created). Also, with what you had, the Tail objects would be in the world for 70 act cycles; and your worm is only moving 1 cell per act; so, I adjusted the lifespan value to something more reasonable. Now, in the Worm class, in the block where food is being eaten, just add this line:
Tail.maxLife += Ground.CELL_SIZE;
You will need a counter in the Worm class to count act cycles; and after every so many (maybe the value of 'Ground.CELL_SIZE'), add a Tail object into the world.
gamer121 gamer121

2014/5/10

#
Ok, so here is what I have so far for both classes. Unfortunately, the tail doesn't follow the head of the worm and disappears afterwards.
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
public class Worm extends Actor

{   
    private static final int EAST = 0;
    private static final int SOUTH = 90;
    private static final int WEST = 180;
    private static final int NORTH = 270;
    private int counter = 0;
    public Worm() 
    {
        switch( Greenfoot.getRandomNumber(4) ) {
            case 0:
            setRotation( EAST );
            break;
            case 1:
            setRotation(WEST);
            break;
            case 2:
            setRotation(SOUTH);
            break;
            case 3:
            setRotation(NORTH);
        }
        GreenfootImage img = new GreenfootImage(Ground.CELL_SIZE,Ground.CELL_SIZE);
        img.drawRect(0,0,Ground.CELL_SIZE,Ground.CELL_SIZE);
        img.fillRect(0,0,Ground.CELL_SIZE,Ground.CELL_SIZE);
        setImage(img);
    }  

    public void act()

    {
        checkKeys();
        moveForward();
        if( atEdgeOfWorld() ) {
            getWorld().removeObject(this);
            Greenfoot.stop();
        }else{
            Actor food = getOneIntersectingObject(Food.class);
            if(food!=null) {
                Tail.maxLife += Ground.CELL_SIZE;
                getWorld().addObject(new Tail(),getX(),getY());
                getWorld().addObject(new Food(),Greenfoot.getRandomNumber(40),Greenfoot.getRandomNumber(30));
                getWorld().removeObject(food);
            }
        }
    }

    public void moveForward()

    {
        counter++;
        if( counter == 3 ) {
            move(1);
            counter = 0;
        }
    }

    public void checkKeys()
    {
        if( Greenfoot.isKeyDown("right") ) {
            setRotation(EAST);
        }
        if( Greenfoot.isKeyDown("down") ) {
            setRotation(SOUTH);
        }
        if( Greenfoot.isKeyDown("left") ) {
            setRotation(WEST);
        }
        if( Greenfoot.isKeyDown("up") ) {
            setRotation(NORTH);
        }
    }

    private boolean atEdgeOfWorld()    
    {
        return getX()<=-1 || getY()<=-1 || getX()>=getWorld().getWidth()+0|| getY()>=getWorld().getHeight()+0;
    }
}
import greenfoot.*;  
public class Tail extends Actor  
{  
    public static int maxLife = Ground.CELL_SIZE;  
    private int lifespan;  
    public Tail()  
    {  
        GreenfootImage img = new GreenfootImage(20,20);  
        img.drawRect(0,0,20,20);  
        img.fillRect(0,0,20,20);  
        setImage(img);  
    }  
  
    public void act()  
    {  
        lifespan++;  
        if (lifespan == maxLife) {  
            getWorld().removeObject(this);  
        }  
    }     
} 
danpost danpost

2014/5/10

#
You need to either modify the counter in your Worm class so that you can use it to spawn a new Tail every so often or add a new counter to do that.
gamer121 gamer121

2014/5/11

#
My code does not work but is this on the right track? I only want to add a tail whenever it eats a food object. import greenfoot.*; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo) public class Worm extends Actor { private static final int EAST = 0; private static final int SOUTH = 90; private static final int WEST = 180; private static final int NORTH = 270; private int counter = 0; private int tailCounter = 0; public Worm() { switch( Greenfoot.getRandomNumber(4) ) { case 0: setRotation( EAST ); break; case 1: setRotation(WEST); break; case 2: setRotation(SOUTH); break; case 3: setRotation(NORTH); } GreenfootImage img = new GreenfootImage(Ground.CELL_SIZE,Ground.CELL_SIZE); img.drawRect(0,0,Ground.CELL_SIZE,Ground.CELL_SIZE); img.fillRect(0,0,Ground.CELL_SIZE,Ground.CELL_SIZE); setImage(img); } public void act() { checkKeys(); moveForward(); if( atEdgeOfWorld() ) { getWorld().removeObject(this); Greenfoot.stop(); }else{ Actor food = getOneIntersectingObject(Food.class); if(food!=null) { Tail.maxLife += Ground.CELL_SIZE; getWorld().addObject(new Food(),Greenfoot.getRandomNumber(40),Greenfoot.getRandomNumber(30)); getWorld().removeObject(food); tailCounter++; if(tailCounter==10) { getWorld().addObject(new Tail(),getX(),getY()); tailCounter=0; } } } } public void moveForward() { counter++; if( counter == 3 ) { move(1); counter = 0; } } public void checkKeys() { if( Greenfoot.isKeyDown("right") ) { setRotation(EAST); } if( Greenfoot.isKeyDown("down") ) { setRotation(SOUTH); } if( Greenfoot.isKeyDown("left") ) { setRotation(WEST); } if( Greenfoot.isKeyDown("up") ) { setRotation(NORTH); } } private boolean atEdgeOfWorld() { return getX()<=-1 || getY()<=-1 || getX()>=getWorld().getWidth()+0|| getY()>=getWorld().getHeight()+0; } }
danpost danpost

2014/5/11

#
I think if you move the code starting from the line 'tailCounter++;' and move it outside the 'if' block it is in, you should be ok. You need to be constantly adding tail objects (but, not necessarily every act cycle, so the counter would be advisable); and you do not need to add one when food is eaten, just increasing the 'maxLife' field is enough.
gamer121 gamer121

2014/5/11

#
Ok. Now, whenever the game starts the worm has already several tails. I want the worm to start with just the head and add one tail whenever it eats a food.
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
public class Worm extends Actor

{   
    private static final int EAST = 0;
    private static final int SOUTH = 90;
    private static final int WEST = 180;
    private static final int NORTH = 270;
    private int counter = 0;
    private int tailCounter = 0;
    public Worm() 
    {
        switch( Greenfoot.getRandomNumber(4) ) {
            case 0:
            setRotation( EAST );
            break;
            case 1:
            setRotation(WEST);
            break;
            case 2:
            setRotation(SOUTH);
            break;
            case 3:
            setRotation(NORTH);
        }
        GreenfootImage img = new GreenfootImage(Ground.CELL_SIZE,Ground.CELL_SIZE);
        img.drawRect(0,0,Ground.CELL_SIZE,Ground.CELL_SIZE);
        img.fillRect(0,0,Ground.CELL_SIZE,Ground.CELL_SIZE);
        setImage(img);
    }  

    public void act()

    {
        checkKeys();
        moveForward();
        if( atEdgeOfWorld() ) {
            getWorld().removeObject(this);
            Greenfoot.stop();
        }else{
            Actor food = getOneIntersectingObject(Food.class);
            if(food!=null) {
                Tail.maxLife += Ground.CELL_SIZE;
                getWorld().addObject(new Food(),Greenfoot.getRandomNumber(40),Greenfoot.getRandomNumber(30));
                getWorld().removeObject(food);
                
            }
        }
        tailCounter++;
        if(tailCounter==3) {
                    getWorld().addObject(new Tail(),getX(),getY());
                    tailCounter=0;
            }
    }
    
    public void eatFood()

    public void moveForward()

    {
        counter++;
        if( counter == 3 ) {
            move(1);
            counter = 0;
        }
    }

    public void checkKeys()
    {
        if( Greenfoot.isKeyDown("right") ) {
            setRotation(EAST);
        }
        if( Greenfoot.isKeyDown("down") ) {
            setRotation(SOUTH);
        }
        if( Greenfoot.isKeyDown("left") ) {
            setRotation(WEST);
        }
        if( Greenfoot.isKeyDown("up") ) {
            setRotation(NORTH);
        }
    }

    private boolean atEdgeOfWorld()    
    {
        return getX()<=-1 || getY()<=-1 || getX()>=getWorld().getWidth()+0|| getY()>=getWorld().getHeight()+0;
    }
}
danpost danpost

2014/5/11

#
If you are using 3 for the tailCounter limit, then change the value of 'maxLife' in the Tail class to 3. Also, change the amount added to in in line 43 in the Worm class to 3.
gamer121 gamer121

2014/5/11

#
Works like a charm. Thanks! However, I get an error in line 51 stating that the actor is not in the world every time the worm reaches an edge of the world. I have tried different placements of code but it has not worked. Is there a way to fix this error message?
{   
    private static final int EAST = 0;
    private static final int SOUTH = 90;
    private static final int WEST = 180;
    private static final int NORTH = 270;
    private int counter = 0;
    private int tailCounter = 0;
    public Worm() 
    {
        switch( Greenfoot.getRandomNumber(4) ) {
            case 0:
            setRotation( EAST );
            break;
            case 1:
            setRotation(WEST);
            break;
            case 2:
            setRotation(SOUTH);
            break;
            case 3:
            setRotation(NORTH);
        }
        GreenfootImage img = new GreenfootImage(Ground.CELL_SIZE,Ground.CELL_SIZE);
        img.drawRect(0,0,Ground.CELL_SIZE,Ground.CELL_SIZE);
        img.fillRect(0,0,Ground.CELL_SIZE,Ground.CELL_SIZE);
        setImage(img);
    }  

    public void act()

    {
        checkKeys();
        moveForward();
        tailCounter++;
        if( atEdgeOfWorld() ) {
            getWorld().removeObject(this);
            Greenfoot.stop();
        }else{
            Actor food = getOneIntersectingObject(Food.class);
            if(food!=null) {
                Tail.maxLife += 3;
                getWorld().addObject(new Food(),Greenfoot.getRandomNumber(40),Greenfoot.getRandomNumber(30));
                getWorld().removeObject(food);
                
            }
        }
        if(tailCounter==3) {
                    getWorld().addObject(new Tail(),getX(),getY());
                    tailCounter=0;
            }
    }
   

    public void moveForward()

    {
        counter++;
        if( counter == 3 ) {
            move(1);
            counter = 0;
        }
    }

    public void checkKeys()
    {
        if( Greenfoot.isKeyDown("right") ) {
            setRotation(EAST);
        }
        if( Greenfoot.isKeyDown("down") ) {
            setRotation(SOUTH);
        }
        if( Greenfoot.isKeyDown("left") ) {
            setRotation(WEST);
        }
        if( Greenfoot.isKeyDown("up") ) {
            setRotation(NORTH);
        }
    }

    private boolean atEdgeOfWorld()    
    {
        return getX()<=-1 || getY()<=-1 || getX()>=getWorld().getWidth()+0|| getY()>=getWorld().getHeight()+0;
    }
}
danpost danpost

2014/5/11

#
Place a 'return;' statement after the 'Greenfoot.stop();' line in the atEdgeOfWorld 'if' block.
You need to login to post a reply.