Physics or Manual Position Update

Hi Defold experts, I am new to Defold and Lua (moving from C#).
I am creating a game that will have 300-500 (est) bouncing bullets alive at a time.
Imagine an environment of walls and objects with players and enemies shooting machine gun of bullets that bounce.
Should I use physics to do the bounce for me? or should I better off handling the position manually?

Some other things to consider:

  • Bullets may have different speed and life over distance traveled.
  • I want to be able to control bullets life by number of bounces.
  • Some bullets have homing capabilities on top of bouncing.
  • I want to be able to control collision group at runtime because some bullets may acquire ability to penetrate through walls, or enemies can activate skill to evade certain bullets.
  • This is a multiplayer game where bullets position have to be sync-ed across clients. I am using Nakama based on the tutorial available on Defold’s website.

In the previous game engine I implemented this by managing bullets behavior and position manually and didn’t have any performance issues. Any advice on this would be greatly appreciated.

Thank you!

The simplest solution is probably to use dynamic collision objects to get bouncing for free. Also it might be tempting to put a script on each bullet, but if you plan to have several hundred bullets at a time it will be a bit of an overhead to run all those scripts every frame. Instead I’d put a script on the level geometry and detect bounces there. I would track the bullets using a lookup table, keyed on bullet id, with data about the bullets as values.

You can probably do this in Defold as well, but again I’d recommend that you control the bullets from a central script which iterates over all bullets to move them and also where collisions are handled.

1 Like

The reason why I implemented manual approach previously was because the physics engine performance sucks. I think it has too much overhead and a lot of features I don’t use, it is a 3d physics engine after all.

Thanks for your input!
I’ll create a small benchmark based on your suggestion and familiarize myself with Defold.

1 Like

Hi, I have done some tests on this. And I do have some questions.
Method 1: Using built-in Physics
I set bullet type to dynamic so built-in physics calculate the bounce automatically for me.
I noticed that this method is the fastest in performance and have the smallest memory footprint.
However the challenge of using this is to set the bullet velocity. I am using this method:

local vel = BULLET_SPEEED * bullet.dir
go.set(bullet.id, "linear_velocity", vel)

However it seems like the velocity has a limit. I don’t see any difference when BULLET_SPEED is over 150. Any advice here?

Method 2: Manual Calculation
I am using fixed_update to move the bullet and using contact_point_response message to process collisions to calculate bounce. However processing contact points is not easy because it may produce multiple calls. I am following this tutorial for this. But it is quite complicated to incorporate bounce into the picture.

The 2nd challenge with this approach is communication between 2 scripts. I am following your guide to create a bullet_manager script that controls all bullets behavior. But how do I handle collision effectively from this bullet_manager script? What I am doing now is to attach bullet.script to the bullet.go which relay the contact_point_response message to bullet_manager script using msg.post. Is this the right way to do it?

Method 3: Predict Collision using Raycast
With this approach, every time the bullet is created I also predict its path using raycasts upfront. And then use go.animate to move the bullet to the raycast hit position and use complete_function to calculate bounce, and so on. I am using kinematic so I can still listen for collisions to enemy group.
I am expecting this to have better performance than the 2nd method. However it seems like it is either the same or slighly slower than the 2nd method.
I like this option the most because bullet’s path is the most predictable out of the 3. But the question I have is how do I change the flight path for homing when I am using go.animate?

Hmm, not sure really. linear_velocity is in units/s (pixels/s):

Does it matter what physics scale you have in game.project? See here:

My suggestion to use a bullet manager was made to avoid having to have a script on each bullet. Depending on the number of bullets it may add too much of an overhead to do the transition from C to Lua for each callback. But I guess most bullets won’t bounce every frame so it’s probably going to be alright.

Personally I’d add scripts to my level geometry instead and let this (or these) script(s) detect the bullet collisions and deal with bouncing etc. I do this with the assumption that you have more bullets than you have level geometry to bounce on.

But both approaches might work well. I suggest that you measure the performance of both and make a decision based on the results.

Yes, this is a clean solution. Ray casts and go.animate() is a good option. Could you use this method for bullets which travel in a straight path and option 2 for homing bullets?