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

2011/6/16

Help with carrying information between worlds

MrCohen MrCohen

2011/6/16

#
I have been working on a scenario which involves two worlds: A main game (GameWorld) world and an "item shop" (StoreWorld) where the user can buy upgrades and ammo between levels. What would be an effective way to pass information back and forth? For example, I want to bring the player's current gold and ammo counts with me to the store, and bring back the same info when I'm done. I don't want anything else about the state of the main game to change. I have a few ideas floating around my head on how to go about this, but I wanted to check in for some guidance before I start coding blindly. Here are my ideas: 1. Construct a StoreWorld owned by GameWorld. Create a constructor in StoreWorld to allow passing of values into StoreWorld. The problem - I have no idea how to pass information back. Worlds don't have return types! I thought about passing an entire StoreWorld into the GameWorld, which would allow it to call methods on StoreWorld's actors, but still no way to go back... 2. Create a MasterWorld and then sub worlds of it. The MasterWorld would own the GameWorld, StoreWorld, and all the Actors. I have no idea if this is even possible - it's just something that popped into my mind. I have read the limited documentation and studied the example, but I just can not seem to figure out how to create this more complex scenario.
davmac davmac

2011/6/17

#
As usual when programming, there is more than one way to go about it. Regardless of the specifics, there are two discrete events which I can see: 1) StoreWorld becomes the world (replacing the GameWorld) 2) GameWorld becomes the world (replacing the StoreWorld) Both of these happen programmatically, and so you have the option of passing information to whichever world you are setting as the current world, in the usual way - that is, by calling methods on it. I'm assuming that the game starts in the GameWorld (but the techniques I'm talking about here will apply regardless). At some point you want to transition to the StoreWorld, at which point you either construct a new StoreWorld and set it as the world, or you use "one you prepared earlier" :), that is, your GameWorld "owns" a StoreWorld instance and sets it as the active world. In the first case it can pass information to the StoreWorld via the constructor; in the second case it can do so via instance method calls (the myStoreWorld.xyzMethod() syntax). At some point the StoreWorld needs to set the GameWorld as the world again. To do this, it will need a reference to the game world (or otherwise it will need to create a new GameWorld instance each time, which might also be feasible). Typically, when the GameWorld creates the StoreWorld, it would pass a reference to itself to the StoreWorld constructor, and the StoreWorld can store this in an instance variable, and use it for two purposes: 1) to pass information back to the GameWorld, via method calls 2) to set, at the appropriate time, the GameWorld as the active world again. Another option is to have an object which holds the shared data, and have both the GameWorld and StoreWorld have references to it. This decouples the two world implementations to some degree. The object with the shared data could be an instance of a custom class (it wouldn't necessarily be either an Actor or a World, though it could be). I'd be cautious about your second idea - is MasterWorld really a world (I mean: isn't it really just a controller)? What do you mean by saying the MasterWorld "owns" the Actors? If the MasterWorld is going to respond to events in the scenario, how is it going to get notification of those events (who will call its methods?)
mik mik

2011/6/17

#
One thing to note (elaborating on davmac's second idea): If you use a shared object, there is no need to pass the data back. For example: GameWorld creates an external object of, say, class GameStatus (which holds all important state: the player object, inventory, score, ... - maybe even the two world objects). If GameWorld passes the object to StoreWorld, then StoreWorld can make changes to it. There is no need to pass it back - GameWorld (if you install the same world object) still holds a reference to the GameStatus object and can see all the changes made to it. I have (just yesterday) updated the Multi-Level-Demo scenario to show how to transition an object (in that case: the game character) between two worlds. See Multi Level Demo.
MrCohen MrCohen

2011/6/17

#
Thank you so much for the detailed reply. I used the method Davmac discussed in his fourth paragraph. I just want to make sure I'm doing this correctly/efficiently. Here is the relevant code (chopped from different parts of the program):
  
    // Create a new Store object and send it the inventory information it needs, along with a reference to
    // this world
    public void callStore ()
    {
        Store s = new Store (inventory, this);
        Greenfoot.setWorld(s);
    }

    //Constructor of Store, stores a copy of GameWorld, the exact state of the current game
    public Store(Inventory i, GameWorld mw)
    {
        this();
        this.inventory = i;
        this.storedWorld = mw;
    }

    //send control back to the GameWorld
    if ("space".equals(Greenfoot.getKey()))
        {
            Greenfoot.setWorld(storedWorld);
        }
I just want to make sure this is efficient. Just to clarify - I believe I am creating references between objects and not new objects. Is this correct? I don't want to inadvertently create a memory leak..
mik mik

2011/6/17

#
The idea and overall structure looks good. It's fine as long as you go into the store only once. Every time you use callStore() you will create a new store. If it is possible to go into the store several times, and you want it to be the same store, then you should create the Store outside callStore() (possible in the constructor) and only set it using setWorld in callStore().
davmac davmac

2011/6/18

#
MrCohen, just a few hints: first, in Java, if you use "new" then you're creating an object; otherwise you're just dealing with references (so as mik says, every time you call callStore() a new Store object is created - because callStore creates a new Store). Second, you generally don't need to worry about memory leaks; if there's no way to get to an object via references, Java's garbage collection system will reclaim the memory. Finally, don't worry about efficiency until it becomes a problem :) Creating a new store each time probably isn't going to be a problem, but if you want to avoid it for any reason, you can instead create a new instance of StoreWorld from within the GameWorld constructor, store a reference to it in a GameWorld instance variable, and use that object within callStore() instead of creating a new one then.
You need to login to post a reply.