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

2017/12/17

Need for removedFromWorld(World world) callback method for Actors

MrBradley MrBradley

2017/12/17

#
Can we get a removedFromWorld callback method added to Actors similar to the addedToWorld method? I need to perform clean up tasks when Actors are being removed. I currently make use of threads for actor animations that I would like to stop. The reset button in the GF UI could then call the remove objects method on the Actors and everything can be cleaned up nicely. Thanks, Mr Bradley
danpost danpost

2017/12/17

#
You can override the current removeObject method of the world class:
public void removeObject(Actor actor)
{
    super.removeObject(actor); // remove the actor
    // clean up code goes here
}
MrBradley MrBradley

2017/12/17

#
Thanks, I will give that a try. For others thinking this may be useful the cleanup code should appear before the call to super.removeObject(actor).
MrBradley MrBradley

2017/12/17

#
This works, but I still need to ensure that all Actors have a default removedFromWorld() method (similar to addedToWorld). I could create my own intermediate class that has this, then extend from it, but it seems like it should be in GF. Also this does not solve the problem of the reset button in the GF not calling the removeObjects(). Actually it is unclear what the reset button actually does. It also doesn't reset static variables in Worlds or Actors. Thanks Dan!
danpost danpost

2017/12/17

#
MrBradley wrote...
This works, but I still need to ensure that all Actors have a default removedFromWorld() method (similar to addedToWorld). I could create my own intermediate class that has this, then extend from it, but it seems like it should be in GF.
You can determine the type of actor is being removed and work with each type individually in the overriding method.
this does not solve the problem of the reset button in the GF not calling the removeObjects().
I am not sure what the problem is here. Why would you want there to be some correlation between the reset button and the 'removeObjects' method?
it is unclear what the reset button actually does. It also doesn't reset static variables in Worlds or Actors.
'static' field are only reset upon compilation. Resetting does not recompile; it just removes the currently active world and activates a new initial world object.
MrBradley MrBradley

2017/12/17

#
Good comments here: Yes, I could check each class and do the appropriate thing, but that is what polymorphism is for, right? ;) My students (and GF users in general) commonly use the reset button to 'reset' their scenarios. Using this button does not call the removeActor method, therefore my actor animation threads keep running. static fields are also reset when the JVM is reset - which is one of the things I thought the reset button did...
danpost danpost

2017/12/17

#
MrBradley wrote...
I could check each class and do the appropriate thing, but that is what polymorphism is for, right?
True. Unfortunately, we only have that which we have to work with. Maybe creating an intermediate actor class would be best, if you want to teach about polymorphism.
My students (and GF users in general) commonly use the reset button to 'reset' their scenarios. Using this button does not call the removeActor method, therefore my actor animation threads keep running.
Maybe you can create a static list of all threads you create and have them cleared from your initial world constructor before building the new world. However, on the other hand, using threads for animation is probably not the best way to go. If you were building the projects using some other IDE (like Eclipse, for example) then maybe threads might be more likely the way to go; but, with greenfoot, which implements act steps automatically, not so.
Super_Hippo Super_Hippo

2017/12/17

#
If you don't want to upload your scenario here, then you can change Greenfoot itself because it is open source. You can implement the 'removedFromWorld' method similar to the 'addedToWorld' method. What reset basically does is, as danpost described: remove the current world and set a new instance of the original first world class active:
[...]
WorldHandler.getInstance().discardWorld();
WorldHandler.getInstance().instantiateNewWorld();
[...]
davmac davmac

2017/12/18

#
I have a simple solution: have you animation thread periodically check (eg it could check on each animation frame) the return of the actor's getWorld() method. If it returns null, the actor has been removed from the world. Your animation thread can then stop.
MrBradley MrBradley

2017/12/18

#
Davin, Thanks for the tip. I have implemented it as a matter of good practice in my AnimationThread base class. Just a FYI, resetting the world loses all handles to actors and any executing threads are not stopped. Is there a reference to the previous world being held somewhere, or are the actor threads preventing the world from being released? That reset button is a mysterious thing. ;) I have added cleanup methods directly to our class' world, and have instructed my students to use them. I have also added a removedFromWorld method to my Actors in the hopes of having something implemented in GF in a future release. Thanks to all for your replies. Happy Holidays. Mr Bradley
davmac davmac

2017/12/18

#
Ah yes. Resetting the world does not remove the actors from the world they are in, they continue to exist in the previous world. (In fact, all that "reset" does is instantiate a new world and make it active. Normally, the previous world would get 'garbage collected' in due course, but if you have created active threads that continue to reference it - even indirectly, via actors within it - then this will not happen). I think, currently, you need to keep some static state if you want to detect when a reset has happened. For example, you could keep a static "currentWorld" member of the world, and set it in the world constructor, as well as use it to remove any objects from the previous world:
public MyWorld()
{
    if (currentWorld != null) {
        currentWorld.removeObjects(currentWorld.getObjects(null));
    }
    currentWorld = this;
}
However, this still won't work for the case of compilation, since that creates a new version of each class and so "resets" static variables. Probably the safest option would be to override the "stopped" method, which is called on reset or on compilation (but also when the scenario is paused), and then you can go through each actor in the world and stop the animation thread:
public void stopped()
{
    // Do whatever you need to do here
}

public void started()
{
    // You can start the animation threads here
}
There's no super-simple solution; unfortunately this just isn't a use-case that Greenfoot was really designed for.
MrBradley MrBradley

2017/12/18

#
Thanks, I think I'll use this approach. Have you seen my other post about retrieving stacked objects being non-deterministic? I have trying to come up with visualization scenarios for a Stacks using actors and these guys appear to be randomly selected from the list of objects at the same location... I'm wondering what performance improvements are gained by hashing (?) the actors into the set/list vs adding them either FIFO or FILO orders. Any way to specify a different collect class at runtime? Thanks again, Mr Bradley
You need to login to post a reply.