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

2023/10/16

I need help with multiple worlds.

DumpsterFire DumpsterFire

2023/10/16

#
I'm making a semi-horror game called Ollie's Ordinary Orchard. It's about collecting apples and completing orders. But as time goes on, things get creepier. I'm having trouble keeping a counter on the screen, because I'm using multiple worlds. The reason I'm using multiple worlds is because I'm using Danpost's portal code. The counter is keeping track of how many apples you have collected, and it keeps disappearing whenever you go to another world/screen. It also gives me an error as well. I don't know what to do and I would like help please. Here is some code that I think would be important:
/**
 * 
 * This is the "player class". You control Ollie and move around.
 * 
 */
public class Ollie extends Actor
{
    private int timer;
    private GreenfootImage walk1;
    private GreenfootImage walk2;
    
    private static GreenfootSound collect = new GreenfootSound("collect.mp3");
    
    public Ollie()
    {
        walk1 = new GreenfootImage("ollieWalk.png");
        walk2 = new GreenfootImage("ollieWalk2.png");
        setImage(walk1);
        
    }
    /**
     * The movement code here is all of @Danpost's code, it is not mine. 
     * I have just copied it. But the rest is my code.
     */
    public void act() 
    {
        int dx = 0, dy = 0;
        if(Greenfoot.isKeyDown("W")) dy = -4;
        if(Greenfoot.isKeyDown("A")) dx = -4;
        if(Greenfoot.isKeyDown("S")) dy = 4;
        if(Greenfoot.isKeyDown("D")) dx = 4;
        setLocation(getX()+dx, getY()+dy);
        
        if(isTouching(Tree.class))
        {
            setLocation(getX()-dx, getY()-dy);
        }
        if(isTouching(HouseHitbox.class))
        {
            setLocation(getX()-dx, getY()-dy);
        }
        if(isTouching(BridgeHitBox.class))
        {
            setLocation(getX()-dx, getY()-dy);
        }
        if(isTouching(PondHitBox.class))
        {
            setLocation(getX()-dx, getY()-dy);
        }
        
        animation();
        pickApples();
    }    
    public void pickApples()
    {
         Screen1 s1 = (Screen1) getWorld();
         if(isTouching(apple.class))
         {
             collect.play();
             removeTouching(apple.class);           
             s1.applesPicked.add(1);
         }
        
    }
    public void animation()
    {
        timer = timer + 1;
        
        if(timer == 10)
        {
            setImage(walk1);
        }
        if(timer == 20)
        {
            setImage(walk2);
            timer = 0;
        }
    }
}
/**
 * Write a description of class MyWorld here.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class Screen1 extends World
{
    private GreenfootImage test;
    private static GreenfootSound daytime = new GreenfootSound("music.mp3");
    
    Counter applesPicked = new Counter("Apples Picked: ");
    
    /**
     * The code to put the trees in a row is this:
     * for(int i = 0; i < 8; i++)
        {
            addObject(new Tree(), i*100+50, 43);
        }
     * The number at the end, 43, is the number that needs to be changed to 
     * go down. The increasing numbers are: 
     * 129, 215, 301, 387, 473, 559, 645 
     */
    public Screen1()
    {    
        super(800, 700, 1); 
        //daytime.play();
        
        Hitbox hitbox = new Hitbox();
        
        hitbox = new Hitbox();
        addObject(hitbox, 800, 384);
        hitbox = new Hitbox(hitbox);
        Screen2 s2 = new Screen2();
        s2.addObject(hitbox, 0, 384);
        
        hitbox = new Hitbox();
        addObject(hitbox, 0, 384);
        hitbox = new Hitbox(hitbox);
        Screen8 s8 = new Screen8();
        s8.addObject(hitbox, 800, 384);
        
        
        for(int i = 0; i < 8; i++)
        {
            addObject(new Tree(), i*100+50, 43);
        }
        for(int i = 0; i < 6; i++)
        {            
             if(i != 3)
             {
                 addObject(new Tree(), 750, i*86+129);
             }             
        }
        for(int i = 0; i < 8; i++)
        {            
             addObject(new Tree(), i*100+50, 645);
        }
        
        
        addObject(new House(), 360, 342);
        addObject(new HouseHitbox(), 359, 310);
        
        
        
        addObject(new Ollie(), 563, 391);
        test = new GreenfootImage("screen1.png");
        setBackground(test);
        
        
        addObject(applesPicked, 94, 660);
        applesPicked.setValue(0);
        
    }
}
/**
 * A Counter class that allows you to display a numerical value on screen.
 * 
 * The Counter is an actor, so you will need to create it, and then add it to
 * the world in Greenfoot.  If you keep a reference to the Counter then you
 * can adjust its value.  Here's an example of a world class that
 * displays a counter with the number of act cycles that have occurred:
 * 
 * <pre>
 * class CountingWorld
 * {
 *     private Counter actCounter;
 *     
 *     public CountingWorld()
 *     {
 *         super(600, 400, 1);
 *         actCounter = new Counter("Act Cycles: ");
 *         addObject(actCounter, 100, 100);
 *     }
 *     
 *     public void act()
 *     {
 *         actCounter.setValue(actCounter.getValue() + 1);
 *     }
 * }
 * </pre>
 * 
 * @author Neil Brown and Michael Kölling 
 * @version 1.0
 */
public class Counter extends Actor
{
    private static final Color transparent = new Color(0,0,0,0);
    private GreenfootImage background;
    private int value;
    private int target;
    private String prefix;
    
    public Counter()
    {
        this(new String());
    }

    /**
     * Create a new counter, initialised to 0.
     */
    public Counter(String prefix)
    {
        background = getImage();  // get image from class
        value = 0;
        target = 0;
        this.prefix = prefix;
        updateImage();
    }
    
    /**
     * Animate the display to count up (or down) to the current target value.
     */
    public void act() 
    {
        if (value < target) {
            value++;
            updateImage();
        }
        else if (value > target) {
            value--;
            updateImage();
        }
    }

    /**
     * Add a new score to the current counter value.  This will animate
     * the counter over consecutive frames until it reaches the new value.
     */
    public void add(int score)
    {
        target += score;
    }

    /**
     * Return the current counter value.
     */
    public int getValue()
    {
        return target;
    }

    /**
     * Set a new counter value.  This will not animate the counter.
     */
    public void setValue(int newValue)
    {
        target = newValue;
        value = newValue;
        updateImage();
    }
    
    /**
     * Sets a text prefix that should be displayed before
     * the counter value (e.g. "Score: ").
     */
    public void setPrefix(String prefix)
    {
        this.prefix = prefix;
        updateImage();
    }

    /**
     * Update the image on screen to show the current value.
     */
    private void updateImage()
    {
        GreenfootImage image = new GreenfootImage(background);
        GreenfootImage text = new GreenfootImage(prefix + value, 22, Color.BLACK, transparent);
        
        if (text.getWidth() > image.getWidth() - 20)
        {
            image.scale(text.getWidth() + 20, image.getHeight());
        }
        
        image.drawImage(text, (image.getWidth()-text.getWidth())/2, 
                        (image.getHeight()-text.getHeight())/2);
        setImage(image);
    }
}
danpost danpost

2023/10/16

#
DumpsterFire wrote...
I'm having trouble keeping a counter on the screen, because I'm using multiple worlds. The counter is keeping track of how many apples you have collected, and it keeps disappearing whenever you go to another world/screen. It also gives me an error as well.
The applesPicked counter was created in, referenced by and added into the Screen1 world. The counter is referenced by the instance variable/field (defined on line 12 in your Screen1 code), plus it is kept in a list (of objects currently in the world) by its World superclass. This list belongs to that same Screen1 world instance. So, once all references to that Screen1 world instance is lost, all references to that counter are lost as well. You could pass the Counter object from world to world; but, that is a hassle. Better might be to add another class to hold it in a static field. This could be a header screen, a menu world or just a simple stats world. I prefer using a header screen as it is only used once; so, it can create the counter when the header world is initialized and maintain a class reference to it. All your "Screen#" worlds can access it simply enough. You would basically have:
import greenfoot.*;

// title screen; proceeds to Screen1 world when the 'Run' button is clicked
public class Header extends World
{
    public static Counter applesPicked;
    
    public Header() {
        super(800, 600);
        // add title (a default background image for the class could be used to show title if desired)
        String text = "Ollie's\nOrdinary\nOrchard";
        Color trans = new Color(0, 0, 0, 0);
        GreenfootImage image = new GreenfootImage(text, 72, Color.GREEN, trans);
        GreenfootImage bg = getBackground();
        bg.drawImage(image, 400-image.getWidth()/2, 300-image.getHeight()/2);
        
        // create counter
        applesPicked = new Counter("Apple Picked: ");
    }
    
    public void started() {
        Greenfoot.setWorld(new Screen1());
    }
}
In your Screen1 class code, remove line 12 and replace line 72 with:
addObject(Header.applesPicked, 94, 660);
Line 73 is unnecessary as all Counter objects created are initialized with a 0 value. Even line 50 of the Counter class is not required to create a Counter object with an initial value of 0.
DumpsterFire DumpsterFire

2023/10/16

#
So I've added a world class named Header and implemented the code, just like you suggested, but the counter still doesn't show up on any other worlds. And i did add the "addObject(Header.applesPicked, 94, 660);" to the other screen worlds. I'm not sure if I missed something or didn't add a line of code somewhere. Here is my new Screen1 code and some other "Screen#" codes.
/**
 * This is Screen1. 
*
 */
public class Screen1 extends World
{
    private GreenfootImage test;
    private static GreenfootSound daytime = new GreenfootSound("music.mp3");
    
    
    
    /**
     * The code to put the trees in a row is this:
     * for(int i = 0; i < 8; i++)
        {
            addObject(new Tree(), i*100+50, 43);
        }
     * The number at the end, 43, is the number that needs to be changed to 
     * go down. The increasing numbers are: 
     * 129, 215, 301, 387, 473, 559, 645 
     */
    public Screen1()
    {    
        super(800, 700, 1); 
        //daytime.play();
        
        Hitbox hitbox = new Hitbox();
        
        hitbox = new Hitbox();
        addObject(hitbox, 800, 384);
        hitbox = new Hitbox(hitbox);
        Screen2 s2 = new Screen2();
        s2.addObject(hitbox, 0, 384);
        
        hitbox = new Hitbox();
        addObject(hitbox, 0, 384);
        hitbox = new Hitbox(hitbox);
        Screen8 s8 = new Screen8();
        s8.addObject(hitbox, 800, 384);
        
        
        for(int i = 0; i < 8; i++)
        {
            addObject(new Tree(), i*100+50, 43);
        }
        for(int i = 0; i < 6; i++)
        {            
             if(i != 3)
             {
                 addObject(new Tree(), 750, i*86+129);
             }             
        }
        for(int i = 0; i < 8; i++)
        {            
             addObject(new Tree(), i*100+50, 645);
        }
        
        
        addObject(new House(), 360, 342);
        addObject(new HouseHitbox(), 359, 310);
         
        addObject(new Ollie(), 563, 391);
        test = new GreenfootImage("screen1.png");
        setBackground(test);
        
        
        addObject(Header.applesPicked, 94, 660);
    }
}
/**
 * This is Screen2
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class Screen2 extends World
{
    private GreenfootImage screen2;
    /**
     * Constructor for objects of class Screen2.
     * 
     */
    public Screen2()
    {    
        super(800, 700, 1); 
        screen2 = new GreenfootImage("screen2.png");
        setBackground(screen2);
        
        
        Hitbox hitbox = new Hitbox();
        
        hitbox = new Hitbox();
        addObject(hitbox, 800, 384);
        hitbox = new Hitbox(hitbox);
        Screen3 s3 = new Screen3();
        s3.addObject(hitbox, 0, 384);
        
        for(int i = 0; i < 8; i++)
        {
            addObject(new Tree(), i*100+50, 43);
        }
        for(int i = 0; i < 8; i++)
        {
            addObject(new Tree(), i*100+50, 129);
        }
        for(int i = 0; i < 8; i++)
        {
            addObject(new Tree(), i*100+50, 215);
        }
        for(int i = 0; i < 8; i++)
        {
            addObject(new Tree(), i*100+50, 301);
        }
        for(int i = 0; i < 8; i++)
        {
            addObject(new Tree(), i*100+50, 473);
        }
        for(int i = 0; i < 8; i++)
        {
            addObject(new Tree(), i*100+50, 559);
        }
        for(int i = 0; i < 8; i++)
        {
            addObject(new Tree(), i*100+50, 641);
        }
        addObject(Header.applesPicked, 95, 660);
    }
}
/**
 * This is Screen3.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class Screen3 extends World
{
    private GreenfootImage screen3;
   
    /**
     * Constructor for objects of class Screen3.
     * 
     */
    public Screen3()
    {    
        super(800, 700, 1); 
        screen3 = new GreenfootImage("screen3.png");
        setBackground(screen3);
        addObject(Header.applesPicked, 94, 660);
        
        Hitbox hitbox = new Hitbox();
        
        hitbox = new Hitbox();
        addObject(hitbox, 800, 384);
        hitbox = new Hitbox(hitbox);
        Screen4 s4 = new Screen4();
        s4.addObject(hitbox, 0, 384);
        
        for(int i = 0; i < 8; i++)
        {
            addObject(new Tree(), i*100+50, 43);
        }
        for(int i = 0; i < 8; i++)
        {
            addObject(new Tree(), i*100+50, 645);
        }
        addObject(new Tree(), 50, 129);
        addObject(new Tree(), 750, 129);
        addObject(new Tree(), 50, 215);
        addObject(new Tree(), 750, 215);
        addObject(new Tree(), 50, 301);
        addObject(new Tree(), 750, 301);
        addObject(new Tree(), 50, 473);
        addObject(new Tree(), 750, 473);
        addObject(new Tree(), 50, 559);
        addObject(new Tree(), 750, 559);
        addObject(new Tree(), 515, 217);
        addObject(new Tree(), 331, 380);
        addObject(new Tree(), 414, 435);
    }
}
I can also upload more code if needed
danpost danpost

2023/10/17

#
DumpsterFire wrote...
So I've added a world class named Header and implemented the code, just like you suggested, but the counter still doesn't show up on any other worlds. And i did add the "addObject(Header.applesPicked, 94, 660);" to the other screen worlds. I'm not sure if I missed something or didn't add a line of code somewhere. Here is my new Screen1 code and some other "Screen#" codes. << Codes Omitted >> I can also upload more code if needed
When you reset the project, do you see the title screen? If not, manually create a Header world object (right click on the Header class icon and select "new Header()". Then click on the 'Run' button. If it is possible that some other actor obscures the counter's view, add the following line to all your world constructors:
setPaintOrder(Counter.class);
DumpsterFire DumpsterFire

2023/10/17

#
Yes, I do see the title screen. I added the "setPaintOrder(Counter.class);" code to the other constructors, but the counter didn't appear. So what I did, was I put this line of code into every Screen# constructor, and I got the counter to show up on every screen.
public void act()
    {
       addObject(Header.applesPicked, 94, 660);
    }
I have another question though. I can't update the counter properly because the game freezes and gives me an error when i hit the "Run" button. I'm trying to update/add to the counter in the Ollie class by casting out to the Header world when i touch an apple. What should I do? This is the ollie class and Header class.
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
 * 
 * This is the "player class". You control Ollie and move around.
 * 
 */
public class Ollie extends Actor
{
    private int timer;
    private GreenfootImage walk1;
    private GreenfootImage walk2;

    private static GreenfootSound collect = new GreenfootSound("collect.mp3");

    public Ollie()
    {
        walk1 = new GreenfootImage("ollieWalk.png");
        walk2 = new GreenfootImage("ollieWalk2.png");
        setImage(walk1);

    }

    /**
     * The movement code here is all of @Danpost's code, it is not mine. 
     * I have just copied it. But the rest is my code.
     */
    public void act() 
    {
        int dx = 0, dy = 0;
        if(Greenfoot.isKeyDown("W")) dy = -4;
        if(Greenfoot.isKeyDown("A")) dx = -4;
        if(Greenfoot.isKeyDown("S")) dy = 4;
        if(Greenfoot.isKeyDown("D")) dx = 4;
        setLocation(getX()+dx, getY()+dy);

        if(isTouching(Tree.class))
        {
            setLocation(getX()-dx, getY()-dy);
        }
        if(isTouching(HouseHitbox.class))
        {
            setLocation(getX()-dx, getY()-dy);
        }
        if(isTouching(BridgeHitBox.class))
        {
            setLocation(getX()-dx, getY()-dy);
        }
        if(isTouching(PondHitBox.class))
        {
            setLocation(getX()-dx, getY()-dy);
        }

        animation();
        pickApples();
    }    

    public void pickApples()
    {
        
        Header h = (Header) getWorld();
        if(isTouching(apple.class))
        {
            collect.play();
            removeTouching(apple.class);           
            h.applesPicked.add(1);
        }

    }

public class Header extends World
{
    public static Counter applesPicked;
    public int apple;
    /**
     * Constructor for objects of class Header.
     * 
     */
    public Header()
    {    
        super(800,700,1);
        String text = "Ollie's\nOrdinary\nOrchard";
        Color trans = new Color (0,0,0,0);
        GreenfootImage image = new GreenfootImage(text, 72, Color.GREEN, trans);
        GreenfootImage bg = getBackground();
        bg.drawImage(image, 400-image.getWidth()/2, 300-image.getHeight()/2);
         
        // create counter
        applesPicked = new Counter("Apple Picked: ");
        apple = 0;
    }
    public void started()
    {
        Greenfoot.setWorld(new Screen1());
    }
}

DumpsterFire DumpsterFire

2023/10/19

#
I got it working! The casting now works properly! Thanks for the help Danpost! I really appreciate it! :)
You need to login to post a reply.