Physics Engine vs. Runtime enterFrame for object movement

OK, I pretty much use the Box2D physics engine to do all my heavy lifting so I’ll often make physics objects out of objects that don’t require physics, just for the ease of moving them around.  I’ll make them a sensor and weld them to something else so essentially they move because the object they are pinned to moves.  I use this for glows, explosions, force fields and also visual add-ons to physics objects.  I got in the habit of doing this so Box2D calculated all the rotational changes.

This technique hasn’t been detrimental to game performance but lately, I’ve been experimenting with using Runtime enterFrame listeners to do the same work.  I’m using trig analysis to rotate everything and it works just fine but I can’t tell if it is saving me any bandwidth.

I have thousands of physics bodies so a little gain here or there might add up to something.  I know Box2D is a lean and powerful tool so maybe it is outperforming my methods.  Should I just stick with Box2D?

Has anyone done any benchmarking of these approaches?  Hunches are welcome too!

of the thousands of physics objects, how many would be “candidates” for the frame/trig approach?  (vs how many still remain as truly physics-needing bodies)  if just some trivial number, then probably doesn’t matter either way.  if all of them, then might matter.

box2d runs native, so potential performance gain there; but it does things the “hard way”, so potential perf loss there.  iow, if you’re talking about using weld joints to achieve equivalent of “obj1.x, obj1.y = obj2.x, obj2.y” in a frame loop, then the extra work to actually solve the joint constraint (even though via native) might actually amount to more overhead than the equiv interpreted frame code.

you’ll want to do your own benchmark - load/stress your system with these candidates until you can detect a frame drop, then compare to the other method - ie, which one drops frames earlier?

Good idea @davebollinger

I could prune a lot of physics objects, maybe 50%.  I’ve just never bothered before because box2d seems so efficient - especially with sensors with a light collision filter (or would that be heavy?).

Unless, someone else has run these benchmarks, I’ll have to add this to my project list.

I ran tests on this issue over a year ago, but I regrettably don’t have the exact results written down anymore or I can’t find them at least.

When I compared attaching some images to physics objects by welding them together to just updating their position via a runtime listener, the results varied.

The only case in which Box2D performed better was when I tried updating series of different types of objects in a loop and I had to resort to using conditional statements, such as

local function update() for i = 1, #objects do if objects[i].hasImage then -- update image location elseif objects[i].hasSomething then -- update something, etc. end end end

However, even in this instance, Box2D outperformed the runtime listener only after the number of objects reached a high number.

I found that a simple runtime listener performed much better than welding the images to the physics objects, i.e. something like:

local function update() for i = 1, #objects do objects[i].image.x, objects[i].image.y, objects[i].image.rotation = objects[i].x, objects[i].y, objects[i].rotation end end

I’m also not sure, but I think that using :translate() was even better than just updating x and y. 

Thanks @Xedur, that’s good to know!

OK, another idea.  If I make a display group and pin it to a physics object with a Runtime enterFrame function and then fill that display group with other visuals I would only have to make one update for the whole group. . . but how expensive is the group.  I’m looking for hunches to inform how I set up my tests.

so…

1000 separate objects, moved independently (via frame code)

vs

10 display groups of 100 objects, moved as groups (via same frame code)

…??

if so, the latter will outperform the former

(ymmv depending on how “dense” the grouping is, e.g: 2 groups of 500 vs 500 groups of 2?)

aside:  may or may not apply to your usage, but are you aware you can define a group as a physics body?

use case:  one body with (say) 10 welded children.  put the body AND children in group, properly relative to each other, then add group as physics body (with custom shape to match intended body item)

better than 10 actual welds between 11 bodies, or manually moving a group or 10 to follow separate body. physics would do it all, for the “cost” of one body.

I would expect that Dave’s suggestion to give each display group a physics body to be the best performing option out of everything that has been discussed so for.

What?  A display group as a physics body, that’s crazy talk. . . I love it! 

That’s a serious hack if I can get it to work right for my objects.  Thanks!

As for the number of groups, I currently have about 300 objects - each with multiple welds - many of which don’t need to be physics objects so I would be talking about 300 display groups for 2,000 objects.

just be aware that the caveat is:  if you also need these elements to be in diff groups to achieve global “layering” then won’t work.

e.g., imagine a “sprite” with a “health bar” under it, you require all health bars to appear “above” all sprites, so you have

spriteLayer = display.newGroup() healthLayer = display.newGroup()

with paired elements appropriately assigned to the layering groups.  but you can’t achieve that if both sprite and health bar are in a single “physics body” group - eventually one of your sprites will occlude a neighbors health bar (based on their local layering within the physics group)

also, make sure main body object is positioned at 0,0 within group, else the “physics doesn’t match display” snafu:

circle = display.newCircle(group, 100, 100, 20, 20) physics.addBody(circle, "dynamic", { radius=20 }) -- body and display both at 100,100 = ok group = display.newGroup() group.x, group.y = 100,100 circle = display.newCircle(group, 0, 0, 20, 20) physics.addBody(group, "dynamic", { radius=20 }) -- body and display both at 100,100 = ok group = display.newGroup() group.x, group.y = 90,90 circle = display.newCircle(group, 10, 10, 20, 20) physics.addBody(group, "dynamic", { radius=20 }) -- body at 90,90 but display at 100,100 = OOPS!!

Ah ha, so the group is the “canvas” and I can pass on vertices to describe the shape of the group when it becomes a physics object.  One physics body to rule them all ; joints need not apply.  Nifty!

Oh, I see, if the objects are also physics bodies they aren’t held together by group, they get passed onto the physics engine and thus still need joints.  I’m going to play around with all of this, thanks @davebollinger and @ Xedur