Instantiating bodies from a prototype in a RUBE document

General discussion about the R.U.B.E editor
benjohn
Posts: 2
Joined: Wed May 01, 2013 11:17 pm

Instantiating bodies from a prototype in a RUBE document

Post by benjohn »

Hi,

I'd like to use RUBE to design scenes for a game, and load the scenes with the b2djson c++ library.

The game features a number of game bodies that I will instantiate, or spawn, multiple times. Each time the body is spawned, the player will interact with it for a bit, moving it about in the process, and it'll then disappear. At some later point it will be recreated at the original location for further interaction. It would be really handy to have these bodies defined in the RUBE scene description and be able to instantiate them multiple times, but I can't see a way to do this.

A partial solution, I think, would be to add the bodies to the RUBE scene, mark them as not being "active" and tag them with a custom property. I can use b2djson to locate these bodies when I load the scene, then generate a fragment of JSON for them with "b2j(b2Body* body)". I can then use this JSON to recreate fresh body instances with "j2b2Body". I think this would work, but I don't think it will correctly hook up any joints that the bodies have attached to them. I've looked at the joint creation code and it doesn't seem like it would easily support this usage.

Is there something neat that I'm missing? Does b2d already have a way of achieving this that I'm not aware of? Should I just knuckle down and write some code that can directly clone a body and it's joints?

Suggestions would be very welcome :-)

Thanks!
iforce2d
Site Admin
Posts: 861
Joined: Sat Dec 22, 2012 7:20 pm

Re: Instantiating bodies from a prototype in a RUBE document

Post by iforce2d »

I am doing something similar by having a separate file for the bodies that should be loaded repeatedly. You can slightly modify the loading functions to take an existing world parameter, to add the contents of the file to instead of creating a new world:

Code: Select all

// b2dJson.h
b2World* readFromFile(const char* filename, string& errorMsg, b2World* world = NULL);
b2World* j2b2World(Json::Value worldValue, b2World* world = NULL);


// b2dJson.cpp
b2World* b2dJson::readFromFile(const char* filename, string& errorMsg, b2World* world)
{
    ...
    //change just the last line
    return j2b2World(worldValue, world);
}

b2World* b2dJson::j2b2World(Json::Value worldValue, b2World* world)
{
    m_bodies.clear();

    //only create world if the passed-in world was null
    if ( ! world )
        world = new b2World( jsonToVec("gravity", worldValue) );
    ...
}
This way you will also get the joints that you need. To avoid opening the file every time, you could also modify the readFromString function instead to use a string in memory, and just read that string from the file once.

If you only ever have one instance at a time of this collection of bodies though, it would be more efficient to just make a note of the starting positions and angles when you load them the first time, then to 'remove' them you can just move them way off screen and make them inactive, and to re'spawn' them you can set their positions to the starting positions and angles and make them active. Remember to initialize the linear and angular velocities each time too :)
benjohn
Posts: 2
Joined: Wed May 01, 2013 11:17 pm

Re: Instantiating bodies from a prototype in a RUBE document

Post by benjohn »

Thanks very much. I think I'll probably use a combination of approaches. If some usefully reusable code comes out of it, I'll throw it your way.

I just bought the full editor. I love your hot of the build approach :-)

Thanks,
Benjohn
Eddy8555
Posts: 42
Joined: Sat Apr 27, 2013 3:01 pm

Re: Instantiating bodies from a prototype in a RUBE document

Post by Eddy8555 »

So I followed the steps for modifying the readFromFile and the j2b2World methods, then went to my scene -- where I create my layers -- initialized a b2World there, and set it to my layers (so I can use the same b2World in different layers.) The images are loading OK, but they are not connected to their bodies.

Going back to the world created by the readFromFile -- and the bodies and sprites are connected again.

I looked everywhere I can in the code but I'm not understanding what is the issue. There seems to be nothing in the b2World created by readFromFile, that is different than my simple code for creating it:

Code: Select all

   b2Vec2 gravity = b2Vec2(0.0f, 0.0f);
    _world = new b2World(gravity);
    
    _world->SetAutoClearForces(true);
    _world->SetWarmStarting(true);
    _world->SetContinuousPhysics(true);
    _world->SetSubStepping(true);


What am I missing?
iforce2d
Site Admin
Posts: 861
Joined: Sat Dec 22, 2012 7:20 pm

Re: Instantiating bodies from a prototype in a RUBE document

Post by iforce2d »

How are you setting the image positions based on the body positions when they move?

Since you mention layers and scenes, can we assume you are using cocos2d here? In the RUBE/cocos2d sample project there is a method called setImagePositionsFromPhysicsBodies which does this. You would need to make sure that is being called every frame. If you are overriding some methods, eg. 'step', make sure you are also calling the superclass method as well.
Eddy8555
Posts: 42
Joined: Sat Apr 27, 2013 3:01 pm

Re: Instantiating bodies from a prototype in a RUBE document

Post by Eddy8555 »

Yes, it is cocos2d and I did put the setImagePositionsFromPhysicsBodies in my tick method. The strange thing is that if I let the RubeLayer use it's own m_world everything is good, but when I'm setting my own world only the image is showing up, but no body.
iforce2d
Site Admin
Posts: 861
Joined: Sat Dec 22, 2012 7:20 pm

Re: Instantiating bodies from a prototype in a RUBE document

Post by iforce2d »

I'm not really sure what you mean by setting your own world... it's pretty hard for anyone to diagnose the problem if you have changed things around.
Eddy8555
Posts: 42
Joined: Sat Apr 27, 2013 3:01 pm

Re: Instantiating bodies from a prototype in a RUBE document

Post by Eddy8555 »

OK if I understand correctly, normally the world is created by the json.readFromFile method. But following your advice from above I have the option to give readFromFile my own world, so that if I'm adding bodies after the world is created they go into the same one -- the same world. At least that's what I thought your advice meant.

So I created a b2World at the scene level, and I'm handing it to the couple of layers I have. Is that not the correct approach?
iforce2d
Site Admin
Posts: 861
Joined: Sat Dec 22, 2012 7:20 pm

Re: Instantiating bodies from a prototype in a RUBE document

Post by iforce2d »

Yes, that's what I meant :)

There isn't really any definitive correct approach, the important thing is make sure the image positions are being updated every frame, from the body positions in the world. But since you ask, couldn't you create the world in one of the layers, and hand it to the other layer? All the sample code is set up to work with the world being a class member of a layer, so making it a member of the scene means you would have to put all those methods in the scene as well. See BasicRUBELayer to see what I mean by "all those methods".

Anyway... sounds like a little debugging is necessary to follow the program flow and check what happens in the tick, setImagePositionsFromPhysicsBodies etc methods. You could also log the pointer of the world being worked with each time you load a .json, or step, or update the image positions. This would let you check if you're working with a single world, or many.
Eddy8555
Posts: 42
Joined: Sat Apr 27, 2013 3:01 pm

Re: Instantiating bodies from a prototype in a RUBE document

Post by Eddy8555 »

I took your advice and left the world as is (defined in the .h of BasicRubeLayer) and run it, reading my Grass1.json file, which is an export of a rube scene with just one image connected to body.

The image and body are there, connected, working perfectly. First step done.

Now I need to save this, so that my users can save the state of their game. I created my own writeToFile method, wrapping the b2dJson one. Like this:

Code: Select all

-(void) writeWorldToFile: (id) sender {
    NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
    NSString *fileName2 = [NSString stringWithFormat:@"%@/newWorld3.json", documentsDirectory];
    const char *fn = [fileName2 cStringUsingEncoding:NSASCIIStringEncoding];
    
    b2dJson json(true); // enable human readable floats
    bool wroteFile = json.writeToFile(m_world, fn);
    printf("wrote %i \n", wroteFile);    
}
The file is written to the location, but when I look at it I can see that the body is written well, but not the image! I compared it to the Grass1.json document (the one generated by Rube on export) and indeed Grass1.json has and entry to the image, but my own "newWorld3.json" doesn't.

I started debugging, and I'm seeing that the size of m_imageToNameMap in Json::Value b2dJson::b2j(b2World* world) is zero.

I can continue on from here, but maybe you have an insight to help put me on the right track?

I tried to add my generated json file for reference, but neither .json, .txt, or no extension are allowed by the form.
Post Reply