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

2019/4/27

New Greenfoot Broke My Game (supposedly)

trianburner trianburner

2019/4/27

#
Some friends and I created a rather large game replicating the agar.io game during our high school senior year in AP Comp Sci. The computers had Greenfoot 2.4.2 installed, so that's what we used. I am now trying to revive it as a little side project and perfect it, using the latest and greatest: Greenfoot 3.5.3. As far as I know, the Greenfoot.Color class was the only major change between the versions, and the game used it quite a bit but I've changed all the code accordingly. The problem occurs when I try to add an object to the world from an Actor class. I'm gonna focus on one thing, but it seems to happen with anything I add from the Actor class. During the World creation, I make a bunch of objects like this:
1
2
3
4
5
6
7
8
9
10
11
public void spawnProteins(int amount) {
        for (int i = 0; i < amount; i++) {
            int x = (int)(Math.random() * (getFullWidth() - getWidth()) + (getWidth() / 2));
            int y = (int)(Math.random() * (getFullHeight() - getHeight()) + (getHeight() / 2));
            int c = (int)(Math.random() * 5);
            addObject(new Protein(c), x, y);
             
            ProteinPackage pPackage = new ProteinPackage(x, y, c);
            p.add(pPackage);
        }
    }
I call this to make 500 "proteins" at random places around the map. Works flawlessly. However, when my "Cell" actor runs into one and "eats" it, then Cell removes the offending "Protein" and then calls:
1
2
3
4
5
public void hitProtein() {
        removeTouching(Protein.class);
        addMass(1);
        ((Agar)getWorld()).spawnProteins(1);
    }
It crashes and returns:
1
2
3
4
5
6
7
8
9
10
11
12
13
java.lang.NullPointerException
    at greenfoot.collision.ibsp.Rect.contains(Rect.java:91)
    at greenfoot.collision.ibsp.IBSPColChecker.addObject(IBSPColChecker.java:98)
    at greenfoot.collision.ColManager.addObject(ColManager.java:123)
    at greenfoot.World.addObject(World.java:438)
    at ScrollWorld.addObject(ScrollWorld.java:160) //highlighted red
    at Agar.spawnProteins(Agar.java:144) //highlighted red
    at Cell.hitProtein(Cell.java:286) //highlighted red
    at Cell.act(Cell.java:81) //highlighted red
    at greenfoot.core.Simulation.actActor(Simulation.java:567)
    at greenfoot.core.Simulation.runOneLoop(Simulation.java:530)
    at greenfoot.core.Simulation.runContent(Simulation.java:193)
    at greenfoot.core.Simulation.run(Simulation.java:183)
I should also add that this game uses ScrollWorld and ScrollActors. Now I installed 2.4.2 on my computer, and using the same code it works perfectly fine, maybe even runs smoother. I've compared the World and Actor classes between the versions and they're basically the same. I don't know what I'm missing.
nccb nccb

2019/4/27

#
Do you do anything involving resizing images in your scenario? There is a potential issue in the Greenfoot collision checker that if you call e.g. getImage().scale(10, 10) after adding the actor, the collision checker can end up in an inconsistent state because it doesn't realise the actor has changed size. The simplest fix is to call setImage(getImage()); after finishing the resizing of an actor.
trianburner trianburner

2019/4/27

#
I don't do any scaling, instead I just create a new image each act that is the new updated size for the Cell, once it's "eaten" the "Protein". It increases in size by 1 in both x and y.
1
2
3
4
5
6
7
8
9
/**
     * Sets the image of the cell based on size and mass when called
     */
    public void setImage() {
        cell = new GreenfootImage(size, size);
        cell.setColor(color);
        cell.fillOval(0, 0, size, size);
        setImage(cell);
    }
The protein class is incredibly simple, and just sets the image to a pre-made .png of a colored circle. I can't remember, but I think this was easier on Greenfoot with how many instances of them they are in a game, instead of creating an image.
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
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
 
/**
 * The protein blobs that appear randomly throughout the game.
 * Cells can eat proteins to gain mass.
 *
 * @author Wayde Gilliam, Brian Turner, Cecilia Martin, Ethan Harris
 */
public class Protein extends ScrollActor
{
    String[] colors = {"blueProtein.png", "redProtein.png", "orangeProtein.png", "pinkProtein.png", "greenProtein.png"};
    String color;
     
    /**
     * Act - do whatever the protein wants to do. This method is called whenever
     * the 'Act' or 'Run' button gets pressed in the environment.
     */
    public void act()
    {
        Actor cell;
        cell = getOneIntersectingObject(Cell.class);
    }
     
    /**
     * Constructor for the Protein class
     *
     * @param num The color of the protein
     */
    public Protein(int num) {
        super();
        color = colors[num];
        setImage(color);
    }
}
danpost danpost

2019/4/27

#
The error trace shows that the error was detected from your ScrollWorld class, in its addObject method, at line 160, of which you have not provided the code.
trianburner trianburner

2019/4/28

#
I did not create the ScrollWorld class, and it hasn't been changed at all. The curious part is that the code works with a previous Greenfoot API. This is the addObject method of the ScrollWorld class which extends World.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void addObject(Actor object, int x, int y)
    {
        if (object instanceof ScrollActor) {
            if (x >= fullWidth)
                x = fullWidth -1;
            else if (x < 0)
                x = 0;
            if (y >= fullHeight)
                y = fullHeight -1;
            else if (y < 0)
                y = 0;
            ScrollActor sa = (ScrollActor) object;
            super.addObject(sa, x -(camX -getWidth() /2), y -(camY -getHeight() /2));  //error at this line
            objects.add(sa);
            sa.setIsCameraFollower(false);
        } else
            super.addObject(object,x,y);
    }
sa isn't null at the point before this line, and I don't know what could've changed to make this not work. The addObject method in World hasn't changed between the two versions.
danpost danpost

2019/4/28

#
Looks like it might be an area bounds issue in greenfoot itself; but cannot be sure. I mean, it appears that a Rect object is being returned as null from the getArea method in IBSPColChecker.
trianburner trianburner

2019/4/28

#
I download Greenfoot 3.0.4 (the one just before the API update) and it works just as fine as 2.4.2, so I think I'll just stick with that version.
nccb nccb

2019/4/29

#
I've looked into our code and it appears this exception looks hard to trigger. The way that I could see it happening is if your ScrollActor has overridden the setLocation method and in there, it is possible that the object is being removed from the world. Greenfoot's World.addObject calls setWorld then setLocation then calculates the actor bounds, which are coming out null -- the only way this should be possible is if setLocation removes the object from the world while we are in the middle of adding it. We can add a check for this in future, but it would be useful for us to know if this is the issue. Could you see if ScrollActor does override setLocation?
trianburner trianburner

2019/4/29

#
Seems to override it. Would've never guessed.
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
/**
 * Sets your location seen from the world
 * (regular location).
 * That means that a negative location is off
 * screen, and bigger than the world's size is
 * also off screen.
 */
public void setLocation(int x, int y)
{
    if (world == null) return;
    super.setLocation(x,y);
    int halfWorldWidth = world.getWidth() /2;
    int halfWorldHeight = world.getHeight() /2;
    camX = x -halfWorldWidth;
    camY = y -halfWorldHeight;
    globalX = x +(world.getCameraX() -halfWorldWidth);
    globalY = y +(world.getCameraY() -halfWorldHeight);
}
 
/**
 * Sets your location seen from the camera.
 */
public void setLocationFromCamera(int x, int y)
{
    setLocation(x +world.getCameraX(), y +world.getCameraY());
}
 
/**
 * Sets your location in the big space (the
 * space where the camera is moving over).
 */
public void setGlobalLocation(int x, int y)
{
    int subX = world.getCameraX() -world.getWidth() /2;
    int subY = world.getCameraY() -world.getHeight() /2;
    setLocation(x -subX, y -subY);
}
nccb nccb

2019/4/30

#
Hmmm I can't see a way that setLocation can remove the actor from the world, though. Sorry for all the hassle, but would you be willing to send us the scenario for us to try it out? This looks like a bug in Greenfoot as that exception shouldn't be thrown, but I'm struggling to see exactly how it happens at the moment. If you are happy to email the zipped scenario to support@greenfoot.org then we will take a look, fix anything on our end, and also advise you on a work-around.
You need to login to post a reply.