65
\$\begingroup\$

I came across this question when I was designing a video game in C#.

If we consider games such as Battlefield or Call of Duty, hundreds or even thousands of bullets are flying at the same time. Events are triggered constantly, and from what I know, this sucks a lot of processing power … or does it? I want to know how various game developers manage (2D and 3D) bullets and what the most efficient method for each is.

I read the question How are bullets simulated in video games? but it doesn't touch on how bullets work from a program design perspective.

I had a couple ideas, but each have their drawbacks:


Most efficient method I could think of (for 2D games):

Say I was to create a class called Bullet, and for however long the user holds down a button, every 0.01 seconds a Bullet object would be made. This Bullet has:

  • 1 Velocity

  • 2 Starting position of where it is being shot from

  • 3 Sprite texture

  • 4 An on-hit effect

Since the bullet would be its own class, it could manage the drawing, moving, and action listeners itself.

Wouldn't it be hard on the processor to process thousands of these objects being instantiated, then destroyed (when on-hit effect is triggered)? RAM space?


Efficient method for 3D games - Another thought I had was:

Lets say I create a weapon class. This weapon has various features, some of which:

  • 1 Detect where the weapon is aiming, and determine if it's looking at a target

  • 2 Trigger an animation of the gun shooting

  • 3 Has a doDamage() method that indicates something to subtract health from whatever the gun is pointed at

  • 4 Notifies a bullet animation class when button pressed

I could then create a static class, say BulletAnimation, that could get notification from where the gun that triggered it is, where that gun is pointed at (for the bullet destination), and information on an appropriate sprite and velocity to use for the bullet. This class then draws sprites (on a new thread maybe, idk) based on both positions and desired sprite, to simulate a bullet being fired from a gun.


The latter seems much harder to code, and wouldn't it take lots of processing power to constantly call the static to do this for thousands of bullets at a time? Getting constant updates on both starting and ending positions would be hard as well.

My question is, what is the most efficient way game creators do it? Does this method change from 2D to 3D games?

\$\endgroup\$
  • 16
    \$\begingroup\$ Note that even today, most games don't simulate the flight of bullets. For anything considered "fast enough", a simple hitscan is performed instead - basically, it's assumed the impact occurs at the same time as you press the trigger. In any case "hundreds or thousands of bullets" isn't really a big amount - that's something games had since very early consoles (various bullet-hell games), thousands of times less powerful than today's machines. You just need to make sure to only do as little work per bullet as possible :) \$\endgroup\$ – Luaan Jul 1 '16 at 8:36
  • 42
    \$\begingroup\$ Bullets usually work via pew-pew-pew technology :) \$\endgroup\$ – MonkeyZeus Jul 1 '16 at 13:23
  • 5
    \$\begingroup\$ There are never hundreds or thousands of bullets flying around at the same time. There's no gun that fires them that fast. Even the mighty Phalanx tops out at 75 bullets per second. Based on the "Effective firing range" listed on Wikipedia, the bullets fly for around 3 seconds at most, so a Phalanx can put 225 bullets in the air at one time. A M16 tops out at around 12 rounds/sec and cannot sustain that rate (max for sustained fire is 0.25rounds/sec). There's just not that many guns firing at any one time! \$\endgroup\$ – Cort Ammon - Reinstate Monica Jul 2 '16 at 1:33
  • 3
    \$\begingroup\$ Just to point this out, it is never good to make objects individual classes when they are so simple. It is far better to have one bulletField instance for each type of bullet. The slight overhead in code length and whatnot will save you an extra 4 byte word per bullet (if type is an integer). Plus, one object can easily scan a list. \$\endgroup\$ – The Great Duck Jul 2 '16 at 4:37
  • 4
    \$\begingroup\$ @Cort - that's true supposing there's only one firearm in the game space. The OP mentioned games like Battlefield and CoD, where dozens of players could be firing automatic guns simultaneously. It's not unreasonable for there to be a ridiculous number if each round was actually accounted for physically in space. \$\endgroup\$ – Jesse Williams Jul 3 '16 at 5:14
79
\$\begingroup\$

I can certainly see why you would think that it would be hard to simulate those, but there are enough constraints on bullets (all projectiles, really) to make them easier.

  1. They are generally simulated as a single point, instead of as something with volume. This makes collision detection significantly easier, as now I only need to do collisions against very simple surfaces, such as a line against a circle.

  2. We know how they will move, so there isn't much information we need to store or calculate for them. Your list was reasonably accurate, we'll generally have a few more things associated with it, like who shot the bullet, and what its type is.

  3. Since all projectiles will be very similar, we can pre-allocate them, to avoid all the overhead of creating them dynamically. I can allocate an array of 1000 projectiles, and now they can be accessed with just an index, and they are all sequential in memory, so processing them will be quick.

  4. They have a fixed lifetime/range, so I can expire old bullets and recycle the memory into new bullets very quickly.

  5. Once they hit something, I can also expire them, so they have a finite lifetime.

  6. Since we know when they were created, if we need new ones and we don't have any free in our pre-allocated list, I can just grab the oldest ones and recycle them, and people won't notice if bullets expire slightly early.

  7. They are rendered as sprites (usually) or as low poly models, and take up very little space on screen, so they are fast to render.

Taking all of those things into account, bullets tend to be relatively cheap. If our budget ever got consumed by bullets and rendering them, we'd generally just redesign it to limit the number of shots you can fire at a time (you'll see this in many old arcade games), use beam weapons that move instantly, or slow the fire rate down to make sure we stay within budget.

\$\endgroup\$
  • 12
    \$\begingroup\$ I must disagree with 5), which actually just makes the whole thing complicated in modern games. In earlier shooters this was acceptable, nowadays even COD allows players to shoot through wooden walls. 6) would be unacceptable for any competitive system, although it would be a rare problem. \$\endgroup\$ – SBoss Jul 1 '16 at 7:03
  • 17
    \$\begingroup\$ @SBoss then rephrase: "Once they hit something they cannot penetrate, I can also expire them, so they have a finite lifetime.". And for 6 you can get a worst case by capping the max fire rate per character and then keeping an array of length num_characters * max_bullets_per_character \$\endgroup\$ – ratchet freak Jul 1 '16 at 9:10
  • 14
    \$\begingroup\$ @SBoss I think #6 is more for eg. top-down space games, where a slow-moving bullet might travel a great distance off-screen before hitting something/disappearing. Obviously it's not a problem in CoD-type games, where bullets move fast and quickly reach the bounds of the world. \$\endgroup\$ – BlueRaja - Danny Pflughoeft Jul 1 '16 at 12:57
  • 1
    \$\begingroup\$ The vast majority of games do NOT model bullets (external ballistics) at all . Most games employ a technique called "hit-scanning". \$\endgroup\$ – Aron Jul 4 '16 at 3:24
44
\$\begingroup\$

Probably one of the most efficient ways to implement bullets is using what is known as hitscan. It is rather simple in its implementation - when you fire, you check to see what the gun is aiming at (possibly using a ray to find the closest entity/object/mesh), and then you 'hit' it, doing damage. If you want to make it seem more like an actual, fast moving invisible bullet was fired, you can fake it by adding a slight delay dependent on distance before doing damage.

This approach essentially assumes the fired projectile has infinite velocity, and is typically used for weapons types such as lasers and particle beams/cannons and maybe some forms of sniper rifles.

The next approach would be to model the fired bullet as a projectile, which is modelled as its own entity/object that is subject to collision, and possibly gravity and/or air resistance that alters its direction and velocity. It is more complex than the hitscan approach due to the extra physics equations, and more resource intensive due to there being an actual bullet object, but can provide more realistic bullets.

As for managing collisions between projectile based bullets and other objects in game, collision detection can be greatly simplified by sorting your objects into quad or octrees. Octrees are primarily used in 3d games, while quadtrees can be used in 2d or 3d games. The advantages of using one of these trees is that you can greatly reduce the number of possible collision checks. For example, if you have 20 objects active in the level, without using one of these trees, you'll have to check all 20 for a collision with the bullet. Dividing the 20 objects among the leaves (end nodes) of the tree, and you can reduce the number of checks down to however many entities are present in the same leaf as the bullet.

As for these approaches – hitscan and projectile, both can be used freely in 2d or 3d games. It depends more on what the weapon is, and how the creator has decided that the weapon shall function.

\$\endgroup\$
  • \$\begingroup\$ The information about the design pattern, Hitscan, and quad/octrees really helps. Also, thanks for the info! \$\endgroup\$ – Eric Jul 1 '16 at 3:37
  • 8
    \$\begingroup\$ If you do not sue a hitscan but simulate the projectiles they can warp through thin objects because they move so fast. In that case remember that eveyrthing in games is fake. Even though a bullet is only a few centimeters long you can do the collision detection as if the bullet is a meter long. This way you can still do nice bullet drop and time of flight simulations without having to worry too much about bullets warping through objects without hitting them :). \$\endgroup\$ – Roy T. Jul 1 '16 at 7:16
  • 2
    \$\begingroup\$ Are there games where the physics of bullets (as opposed to, say, cannon shells) respect things like gravity (bullet drop), air resistance? (That is, games besides specialty games where the focus is precision target shooting or something like that: FPS, etc.) I'm not a gamer, but I'm surprised that that level of fidelity is (even sometimes) needed. \$\endgroup\$ – davidbak Jul 1 '16 at 17:26
  • 3
    \$\begingroup\$ @davidbak: it depends strongly on the typical in-game encounter and the realism expected from the game genre. If you are mostly (only?) fighting close quarters combat, then indeed, that level of fidelity is not needed. But if the option for long range combat exists (e.g. snipers, or archers in a more RPG-like setting), gravity affecting missiles is nowadays kind of expected. If you aim your rocket launcher upwards, you'd still expect the rocket to land and explode somewhere, no? Still, the trajectories are not always calculated from real physics, just an approximation (for performance reasons) \$\endgroup\$ – hoffmale Jul 1 '16 at 17:47
  • 1
    \$\begingroup\$ @davidbak Battlefield since Bad Company 2 had bullet drop. Both for rifles, pistol, tank shells, rockets, everything. Battlefield 3 is free on Origin, you can check (IIRC). Battlefield 4 of course, also has this 'feature'. Other game where you can see this is "Sniper Elite". 2 or 3 are the newer titles. Physics play an important role in that game. \$\endgroup\$ – Apache Jul 3 '16 at 22:32
7
\$\begingroup\$

I'm by no means an expert, but to answer your question, yes, you would need many of those things you mention.

For your 2D example, you could have a position and velocity for a bullet. (You might also need a lifetime or maximum distance, depending on how you implement your bullets.) That would usually involve 2 (x,y) values. If they were floats, that's 16 bytes. If you have 100 bullets, that's only 1600bytes or about 1.5k. That's nothing on a machine today.

Next, you mention the sprites. You would only need a single sprite to represent each bullet. Its size would depend on the bit depth you're drawing at and how big it should appear on screen. Even uncompressed at, say, 256x256 in full float 32-bit per channel, that's 1MB for the sprite. (And that would be very big!) You would draw the same sprite at each bullet location, but it doesn't take additional memory for each copy of the sprite. It would be similar for an on-hit effect.

You mention firing every 0.01 seconds. That would be 100 bullets per second from your weapon. Even for a futuristic weapon that's quite a lot! According to this wikipedia article:

When the trigger is pulled, the rate at which rounds are fired is the cyclic rate. Typical cyclic rates of fire are 600–900 RPM for assault rifles, 1,000-1,100 RPM in some cases, 900-1,200 RPM for submachine guns and machine pistols, and 600-1,200 RPM for machine guns. M134 Miniguns mounted on attack helicopters and other combat vehicles can achieve rates of fire of over 100 rounds per second (6,000 RPM).

So that would the rate of an attack helicopter!

For a large world like you mention in Battlefield/Call of Duty/etc., they may calculate all those bullet positions, but not draw all of them if the action is far away. Or they may not simulate them until you get close. (I have to admit I'm guessing a little on this part as I haven't worked on anything that large.)

\$\endgroup\$
6
\$\begingroup\$

Wouldn't it be hard on the processor to process thousands of these objects being instantiated, then destroyed (when on-hit effect is triggered)? RAM space?

I think you're underestimating just how fast computers are. This was sometimes a problem on the systems of the 80s and 90s. It's partly why the original Space Invaders won't let you fire another bullet until the current one has hit. Some games suffered from "slowdown" if there were too many sprites on screen.

Nowadays, though? You have enough processing power for thousands of operations per pixel that are required to do texturing and lighting. There is no problem with thousands of moving objects; this lets you do destructible terrain (e.g. Red Faction) where every fragment does collision processing with other fragments and follows a ballistic curve.

You need to be a little careful algorithmically - you can't do the naive approach of checking every object against every other object when you have thousands of objects. Bullets don't generally check for collisions with other bullets.

A little side anecdote: the first version of networked Doom (the 90s original) sent one packet over the network for every bullet fired. When one or more players got the machine gun, this could easily overwhelm the network. The 90s was full of people illicitly playing Doom on university or work networks getting into trouble with their network administrators when the network became unusable.

\$\endgroup\$
  • \$\begingroup\$ I wonder how the chainsaw worked in this context \$\endgroup\$ – reas0n Jul 1 '16 at 18:52
  • 1
    \$\begingroup\$ IIRC, the real problem with first network doom is that it avoided the need to have each packet sent separately to every opposing player by using broadcast packets instead. That reduced the number of packets sent, but unfortunately posed a considerable CPU load on every machine on the network, including those that weren't playing the game. \$\endgroup\$ – supercat Jul 1 '16 at 20:26
1
\$\begingroup\$

I'm far from an expert but I've been working on a multiplayer 2D shooter game in my spare time.

My method

There are varying bullet classes between the client and server (even when playing offline, a server instance is started on a separate process and connected to by the 'main' game).

Every tick (60 per second) the client works out a bearing between the player's mouse pointer and the centre of the screen (where their character is) and it's part of the information sent to the server. If the player is also firing at that moment (assuming the weapon is loaded and ready), a server-side bullet instance is created, with simply some co-ordinates, and a base damage (which derives from the stats of the weapon that shot it). The bullet instance then uses some math functions to work out an X and Y velocity from the bearing we gathered from the client.

For every subsequent tick, the bullet moves itself by those coordinates, and reduces its base damage by a predefined amount. If this value goes below 1, or if it hits a solid object in the world, the bullet instance is deleted, and as testing point collisions is incredibly cheap in 2D, even rapid firing weapons have a negligible impact on performance.

As for the client, the bullet information isn't actually received over the network (it proved wasteful in testing), instead as part of the per-tick update each character has a 'fired' Boolean, which if true the client creates a local bullet object which works almost exactly like server ones, the only difference being it has a sprite.

This means that although the bullet you see isn't an entirely accurate representation of it on the server, any difference would be barely noticeable if at at all to a player, and the network benefits outweigh any inconsistencies.

Note on different methods

Some games, including my own, move the bullets each tick as if they're physical objects, whereas others just create a vector in the direction of shooting, or compute the bullet's entire path in the tick it is created, for example in Counter-Strike games. There are a few little client-side tricks to disguise it such as an animation of the bullet firing, but for all intents and purposes each bullet is just a laser.

With 3D models which may have complex hitboxes, it's standard to test collisions against a simple bounding box FIRST, and if that succeeds, go on to more 'detailed' collision detection.

\$\endgroup\$
0
\$\begingroup\$

It's called collision detection. 8-bit computers did this using player-missile graphics in hardware. Modern day game engines use physics engines and linear algebra. The current direction of a weapon is represented as a 3D vector. That provides an infinite line in the direction of fire. Every moving object has one or more bounding sphere as that's the simplest object to detect a collision with a line. If the two intersect, that's a hit, if not, there's no hit. But the scenery might be in the way, so that has to be checked for as well (using hierarchical bounding volumes). The nearest object that has an intersection is the one that has been hit.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.