Bacon2D/Bacon2D

Suggestion to change the way Box2D bindings are integrated

bjorn opened this issue · 13 comments

The current way of integrating qml-box2d is quick and easy, and somewhat convenient in terms of API because users don't have to write import Box2D in addition to import Bacon2D and certain public classes are avoided because they're automatically part of some Bacon2D classes.

However, it is less ideal in terms of cooperating on making the best QML Box2D bindings possible and it is less modular than it could be.

So a few questions I'm wondering about are:

  • What is the advantage of embedding World into Scene, and could there be alternative approaches that are still convenient but more flexible?
  • What is the advantage of deriving Entity directly from Body and couldn't there be a more flexible approach?

In both cases I think the answer is convenience. And maybe also a bit that the qml-box2d API is limited in some ways.

Some alternatives that come to mind:

Scene {
    id: scene

    property World world: World { root: scene }

    Entity {
        id: entity

        property Body body: Body { target: entity }
    }
}

The above example relies on two new properties that could be added to the World and Body objects, which would allow them to be directed at the Bacon2D classes independently. In advantage is, that they can still be used with non-Bacon2D classes as well, for example for driving a simple Image item by physics or using a basic Item as the root of the world. In addition unlike is currently the case, World and Body could derive from QObject instead of being a full QQuickItem.

But of course, it comes at a level of verbosity that's painful to deal with. I haven't figured out a way to reduce this without relying on qml-box2d directly yet, so here's my proposal by still having a hard dependency on the qml-box2d plugin:

Scene {
    id: scene

    world.gravity: Qt.point(0, 10)

    Entity {
        body.bullet: true
        body.fixtures: [ ... ]
    }
}

This example uses aggregation in both cases. The changes to qml-box2d would be the same as described above (so World would have a root property that Scene automatically sets to itself, and Body has a target property that Entity automatically sets to itself). And, the World and Body instances don't need to be created as long as they're not accessed, so scenes or entities without physical properties can automatically avoid the overhead.

What do you think about the last proposed API changes? What are your own thoughts about this issue in general? Does it make sense for me to try finding some time for working towards making this possible?

Hi @bjorn,

About embedding World into Scene and making Entity a derive from Body, you're correct, the answer is convenience. We were trying the Scene and Entity easy to use by making World a member of the scene and making Entity derive from Box2DBody so anyone using Bacon2D elements won't need to manually declare World or Body elements.

I really like the idea of the root and target properties you are describing. People would be able to create less coupled elements and increase code reuse (Like what we are trying to do with our Behavior/ScriptBehavior elements - we have an "entity" property that works like your target property). I think creating those properties is a good move for the qml-box2d api.

Also, good for us: We can provide some custom elements for certain game types. Example: Imagine a platform game; We can create an Element representing the player, containing all update logic/settings to make the player walk, jump, etc, and a property named sprite. Then, the user of our API only needs to set the sprite property, that we can use to set the target property of the Body, and he is ready to go. Am I making sense?

Thank you @bjorn, and I want to see @rodrigogolive and @kenvandine opinions on this :)

Thanks @bjorn for making this suggestion, you've done a great job with qml-box2d. The way we integrated this was to simplify the API without needing to modify qml-box2d. Along with simplifying the API, it also reduces the terminology developers need to be grok. No deciding when you need a Body instead of an Entity, and when to add a World inside of a Scene.

I really like your idea for the root and target properties, you could probably do that without really breaking the current qml-box2d API right? It wouldn't require a huge change to our API, but we are anxious to stabilize the Bacon2D API.

@bjorn did you see what I did with our Scene.debug property and DebugDraw? If debug is enabled, we automatically add a DebugDraw that fills the Scene. Would you be interested in something like that in qml-box2d? If so I could work on a proposing a branch to qml-box2d. I think it's much more convenient to be able to toggle a property for debugging instead of needing to add a DebugDraw component in the code :-)

+1 with this change @bjorn, of course if it is easy and don't break things on qml-box2d.

This kind of change will give an easy and strong way to use your project on ours (and, of course, on other projects as well).

Thanks!

Alright, thanks for the positive responses! Now, one of the challenges will be to figure out how this World.root property is going to work in practice, since for example only the QQuickItem itself gets notified about when children get added and World currently relies on that for registering dynamically created bodies. However, I think we can solve that by reversing the relationship and have the body search its ancestors for the world instead.

@kenvandine About the way DebugDraw is instantiated, first of all I think it makes complete sense to aggregate an instance from your Scene object that is automatically placed at the top of the scene. The V-Play guys did that too. I considered adding DebugDraw to the World object as well, but I'm not sure if this is still a good idea when we introduce World.root because in this case the world does not necessarily need to derive from QQuickItem and it would basically need to add the DebugDraw instance as a child of the World.root item instead. Maybe this becomes a little hacky?

I really like the proposed solution. We're very anxious to release a 1.0 API that developers can rely on, with hopes of getting it included in the Ubuntu touch platform making it easy to create pure QML games. @bjorn, do you think this is something that could be done soon, or is this something more long term and needs more thought? I'm trying to decide if we should block 1.0 on this, or wait for the eventual 2.0.

@kenvandine Ah, it's not something that would take a huge amount of time I think, but the problem in my case is that I've been very busy already and in addition I'm trying to get Tiled to another stable release. However, I will pause that for a bit to look into making these changes to qml-box2d. Let's give this one week, maybe?

That would be excellent! We'll block 1.0 for that then, thanks! We love tiled too, we have plans for creating scenes based on maps loaded by tiled, a 2.0 feature. @rogerzanoni has a branch that uses libtiled to read the maps, but nothing in place to render them yet :)

So I'm first of all trying to add the Body.target property. It's not really working yet but I'm pushing my progress to https://github.com/bjorn/qml-box2d/tree/body-target for those who want to have a look. My current problem to solve is that the Body may no longer be a direct child of the World and will thus not get initialized. I'm considering to apply a rule that the Body.target needs to be a direct child of the World or, later, a direct child of an item that is set as World.root. If anybody has a better idea, feedback is welcome.

@kenvandine @rogerzanoni Would be awesome if we get to nice Tiled integration later! After Tiled 0.10 I plan to start work on a mobile/tablet version of Tiled, which will also need QML bindings of the base Tiled data structures as well as scene-graph based rendering. :-)

@bjorn great, maybe we can work together on the qml bindings for tiled instead of doing it directly in

So, I've opened a pull request for adding the Body.target property, cause that patch is almost done. I'll try to also get to World.root, but unfortunately it looks like I won't be able to continue until Sunday evening.

So, while Body.target addition is in, I'm a bit stuck on World.root. Nobody but the item itself gets proper notifications when children get added dynamically. The only thing the World could use is QQuickItem::childrenChanged, but this would require iterating over all the children and keeping track to be able to see if any got added, so that one could see if they are Body targets or contain nested Body targets. I'm somewhat inclined to think such functionality simply does not belong in the World object at all. And without this functionality, there is no use for a World.root either.

Comments welcome.

Check out qml-box2d/qml-box2d#70 for the approach I'm going for now. This should be quite easy to integrate into Canvas2D, right?