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

2024/3/28

Help with weird greenfoot.ActorRemovedFromWorld error

Bennylit Bennylit

2024/3/28

#
I'm trying to write some code that will go through the world and remove all instances of a "Spotlight" object. I accomplish this via this method:
public static void reset() {
        List theList = MyWorld.w.getObjects(Spotlight.class);
        for (Object m: theList) {
            MyWorld.w.removeObject((Actor)m);
        }
    }
The problem is that this throws the greenfoot.ActorRemovedFromWorld exception. Unlike other community posts I found, my method references the World as a static World object ("w", "MyWorld.w"), meaning that since I know it's can't be an actor trying to reference getWorld() because it's static, I'm completely at a loss. Any help would be much appreciated.
Vrogram Vrogram

2024/3/28

#
After You have removed the Objects of Spotlight Class, Some code of your project is trying to access the methods of those instances but they have been removed so you need to check the code to stop the access of those instances
Spock47 Spock47

2024/3/28

#
This exception is thrown if one of the actor methods is called that need a world reference to work. Examples for these methods are: getX, getY, isAtEdge, getNeighbours, getObjectsAtOffset, getOneObjectAtOffset, getObjectsInRange, getIntersectingObjects, getOneIntersectingObject, isTouching, removeTouching, containsPoint. The fact that you get the exception therefore indicates that one of the mentioned methods is called AFTER the reset method was executed. Please check the stack trace. It will have a line "at greenfoot.Actor.failIfNotInWorld(Actor.java:716)" and in the next line of the stacktrace one of the methods from the list above will be shown, e.g. "at greenfoot.Actor.getX(Actor.java:169)". If you look at that line, you will know which method call is the actual problem, i.e. if the line is "at greenfoot.Actor.getX(Actor.java:169)" then you have a call to getX after the reset method is completed. Feel free to post the stacktrace of the error here if you like to look at it together. Additional note for simplification: - World::getObjects returns a List<T> where T is the given type, i.e. getObjects(Spotlight.class); returns a List<Spotlight>; you can use this: List<Spotlight> theList = MyWorld.w.getObjects(Spotlight.class); - World also has a removeObjects method that takes a list of actor subtype, so you don't have to make the for loop:
List<Spotlight> theList = MyWorld.w.getObjects(Spotlight.class);
MyWorld.w.removeObjects(theList);
or even shorter:
MyWorld.w.removeObjects(MyWorld.w.getObjects(Spotlight.class));
Live long and prosper, Spock47
Bennylit Bennylit

2024/3/28

#
Unfortunately, between then and now I changed my code to remove objects in an entirely different way, and I can't seem to replicate the issue I first encountered. The closest I can get is purposefully calling isTouching on something that has been removed from the world, which shows this stacktrace upon exception:
java.lang.IllegalStateException: Actor has been removed from the world.
	at greenfoot.Actor.failIfNotInWorld(Actor.java:722)
	at greenfoot.Actor.isTouching(Actor.java:987)
	at Spotlight.minTouching(Spotlight.java:35)
	at MyWorld.act(MyWorld.java:43)
	at greenfoot.core.Simulation.actWorld(Simulation.java:573)
	at greenfoot.core.Simulation.runOneLoop(Simulation.java:506)
	at greenfoot.core.Simulation.runContent(Simulation.java:193)
	at greenfoot.core.Simulation.run(Simulation.java:183)
Caused by: greenfoot.ActorRemovedFromWorld
	at greenfoot.World.removeObject(World.java:466)
	at greenfoot.World.removeObjects(World.java:479)
	at Flashlight.reset(Flashlight.java:34)
	at Guard.renderVitals(Guard.java:43)
	at Player.act(Player.java:97)
	at greenfoot.core.Simulation.actActor(Simulation.java:567)
	at greenfoot.core.Simulation.runOneLoop(Simulation.java:530)
	... 2 more
where Greenfoot will incorrectly tell you where the greenfoot.ActorRemovedFromWorld error happened. So for anyone else having a similar issue, it may be that Greenfoot is incorrectly telling you where the issue is happening, and you just need to check your code very carefully to make sure that you are not calling methods on anything that is not in the world. Thanks for your help; if I can manage to repeat the original issue I will come back.
Spock47 Spock47

2024/3/28

#
Hi Bennylit, from the stacktrace you gave, it says that the method Spotlight.minTouching called Actor.isTouching after that spotlight object had been removed in Flashlight.reset. That matches your description how you reproduced this stacktrace, so I would say that Greenfoot correctly tells where the problem is. I think the most important information about the stacktrace for "ActorRemovedFromWorld" is that it has two parts: 1. When an actor gets removed from the world, Greenfoot will save this information in the actor (this is lines 10-18 of your stacktrace). So, this part tells us when the actor was removed from the world. 2. As soon as a method is called that needs the world on this actor, an error is thrown that gives us a stacktrace of this call (lines 1-9) and attaches the stacktrace from the previous stored info about the removal (lines 10-18, see point 1). => Therefore, we can deduce from lines 10-18 of the stacktrace that Flashlight.reset removed the spotlight from the world and from lines 1-9 of the stacktrace that afterwards the Spotlight.minTouching called isTouching. Anyhow, you solved the problem and that is great. Well done! Live long and prosper, Spock47
danpost danpost

2024/3/29

#
Bennylit, I think you should try to keep static content to a minimum. The world does not need to be held in a static variable in the MyWorld class. The minTouching method sounds like it should be non-static -- applying to a particular Spotlight object when called. Basically, if it is created anew or is to be reset during resetting of the project, then it should not be kept in a static field; and as for methods, if it is a behavior of an instance of the class, it should not be a static method. Please provide the code to the minTouching method in your Spotlight class so I can explain with an example.
Bennylit Bennylit

2024/3/29

#
minTouching is simply a way for me to call isTouching on a spotlight object without the JVM getting mad at me because it has protected access when I'm trying to reference it somewhere else:
public boolean minTouching(java.lang.Class c) {
        return isTouching(c);
    }
As for the static world, I understand that stylistically you really shouldn't do that, but my project only involves one singular world, no resetting or recreating, so I just make it static so that I can more easily access it.
You need to login to post a reply.