Bullet projectiles

In an early version of my game i always used projectiles while firing, never weapon tracing.
But is this this method acceptable for performance and multiplayer bandwidth?
Keeping in mind that the game will support 16-32 players with full auto weapons, so will be shooting multiple bullets per seconds.

The main reasson i prefer using projectiles instead of tracing is for the ballistics

I would want to say no but Battlefield made it work somehow. That’s my two cents on the situation.

If you had asked me about 5 or so years ago I would have thought the same as you. But now I know traces work just as well. All you need to do in order to simulate the ballistics is to split the trace into multiple segments (the more segments the closer the approximation is to the actual flightpath of the bullets) and calculate the start/end point pairs for the traces on an arc.

I’m using Projectiles for almost all of my ordnance in my Hovertank game, which is all centralized around Multiplayer. It’s definitely possible to pull it off, but it really depends on your situation.

A few bits of advice I can give from my experience:

  • Spawning Projectiles is expensive. I strongly suggest pooling the projectiles and just reactivating them instead of spawning new ones. It’s slightly more difficult to manage, but there are huge performance and network gains to be made from that. A lot of Actor properties replicate their initial state when the object is spawned even if they aren’t used. It’s a good practice to get into anyway IMO - it’s benefited me massively. My weapon system is based loosely on ShooterGames, and it’s relatively easy to calculate how many pooled objects you need based on the weapons fire rate and the projectiles lifespan.

  • Write your own version of FRepMovement. The engines stock version is massively inefficient for things like bullets. My version only replicates a Quantized world-location and Velocity now for movement - everything else can be determined clientside from that information so long as it’s rotation follows velocity.

  • Use Variables instead of RPC’s where possible. Although RPC’s are reliable (if you make them so) and will execute in order - replicated variables cost less bandwidth, and the client will always mirror their state eventually. The engine can also decide how often to update the clients based on current bandwidth usage, whereas RPC’s are sent regardless.

  • Unreal Tournament has a complex but very good system for projectile replication, whereby they spawn Fake projectiles on the client and match them up to the servers versions to minimize the gameplay effects of latency. I still haven’t been able to make total sense of it, but it’s worth looking into if you don’t mind being overwhelmed initially. I want to try and get 10+ people into a custom UT match just firing link guns off into the distance to see how well it really performs.

If you don’t want ballistics or shot-leading to be huge contributors to your game, I would move to traces and fake the ‘projectile’ with a fast-moving bullet particle system. Believe it or not, the new Battlefront game, Gears of War and numerous other titles use this approach and when they marry up properly it can be virtually seamless. But of course, it means that bullets will hit a target 1km away as soon as they hit a target 1m away, so it’s situation-dependent.

You can even simulate ‘shot lead’ to an extent by modifying the end-position of the trace, and there are methods for doing arc-traces using relatively simple parabolic equations, good if you want to simulate bullet drop.

I say a better approach is to call a multicast RPC on trigger pull and trigger release that sets weapon states to firing or stop firing.

Rule 1 for good replication : Use replication only when absolutely needed

If your projectile is too fast and won’t really be visible to the player you can just spawn it on server and play fire effects on client

If your projectile is slow you can spawn it on both server and clients WITHOUT replication using the Tigger pull RPC so the projectile will spawn on both client and server but there would be no further network replication needed as ballistics projectiles use predictable physics which can be simulated with decent accuracy on all clients.

if your weapon is a continuous fire weapon you can just call a timer loop on trigger pull RPC that’ll spawn projectile and fire effects on both server and clients without calling fire multiple times until trigger release RPC is called.

Sweet and simple I think

Not if you take that into account. Say that you’re already performing multiple traces to approximate bullet drop (of course you can also offset by wind, Coriolis effect, etc.) you might as well perform them across several ticks.

Any amount of traces would not give as accurate ballistics as launching an actual physics body and simulate it

Depends on how accurate you want it to be. You could literally perform millions of traces if you wanted to, for example each with a length of just one centimeter between start and endpoint. Meaning a single bullet requires 100 000 traces per traveled kilometer, multiply that by 100 bullets flying around at any given time. And still it would always be an approximation and never perfect. It’s a game after all so you’re almost always going to compromise. In many cases you simply do not need that much accuracy so reducing things to 100 traces per traveled kilometer could still be close enough to the actual flight path. In fact you may be able to reduce this even further because most changes happen near the apex and much less before or after that (see attached image).


The above was created with a simple blueprint, each of the line segments between the red dots is a single trace starting in the bottom right corner fired at an angle. Only one trace is performed each tick with the start equal to the end of the last tick (except for the first trace which starts at the weapon). The bullets obey gravity therefore making their flight path arc which is taken into account when calculating the endpoint for each trace. With 100 bullets flying around at any given time you’d only need just as many traces each tick. A problem is that it depends on the general performance, a slower machine would have fewer ticks with longer delta times and therefore a less accurate arc.

In the end it’s up to Fallonsnest to decide which one is the most appropriate approach.

Problem with that is that all clients end up firing at different times, and therefore each one sees different results. For a competitive shooter, that’s not really acceptable - you have to ensure that each player sees the same thing, or at least that the local player doesn’t feel disjointed. My approach has two RPC’s, the first is to start and stop firing, while the second is called ONLY from the local client and tells the server to actually create the projectile. Projectiles are then managed both locally and on the server, if they explode on a client they do everything they usually would aside from dealing damage, which only ever occurs on the Server. If the projectile explodes on the server first, it forces all clients to explode via replicated variable.

Remember that reliable RPC’s are bi-directional, the client has to tell the server that it was carried out, so they can actually consume more bandwidth than variables - at the cost of some precision.

There are also other situations where traces aren’t applicable. If you projectile can be affected by some outside force, such as other projectiles or physics volumes, syncing up the traces on Server and Client can get difficult unless you’re using a deterministic system. bNetTemporary is a godsend for Projectiles, so long as you can ensure all players stay in sync.

EDIT: Having thought this over some more, I actually fail to see the benefit of using traces to work out the flight paths of projectiles. The problem is, you either need to sync up every subsequent trace with the Server to ensure that the client is simulating correctly - or you need to just accept that the client may simulate differently to the server - at which point you can either allow the client to deal the damage on their end (which opens up the possibility for hacking / cheating), or you allow the client to simulate differently and accept that they may start hitting objects on their end but not on the server, which lets players feel cheated.

If you sync up the traces, you have to send RPC’s which is much more expensive than projectile movement replication. If you just want to allow the client to show different results, then you may as well just turn on bNetTemporary on the projectile and only send the initial replication byte, or not replicate movement at all and send an RPC to tell all clients to ‘fire’ a round.