Thursday 19 July 2012

Top-Down Shooters and Recoil

Any shooter fan who's played even a couple games knows how important recoil is for gun play. It's one of the primary attributes when balancing weapons, for example handicapping a weapon that deals high damage per shot with lots of recoil. Additionally, it can aid immersion, making the player feel even more like they are the soldier/person holding the weapon they're firing.

In this blog post, I'm going to be discussing the potential applications of recoil for a top-down shooter, how they might be implemented and what the advantages and disadvantages of each are.

Spread

Spread is the most common system used for simulating recoil in contemporary shooters. It revolves around the concept that the point on screen that the user is aiming at is the centre of a circle and that the bullets fired from the gun will land anywhere within that circle. Obviously the larger the circle, or "spread", for that weapon then the less likely the bullets will land exactly where the user is aiming.

Usually this circle tends to grow as the player continuously fires their weapon and shrink when they're not, encouraging users to use controlled bursts (as one would do when firing a real firearm) instead of prolonged spraying.


In 3D games, incorporating spread when firing would be accomplished by randomly selecting an angle between 0 and 2π radians and then adding a random offset less than the current amount of spread to the firing direction. For a 2D shooter, the spread is more of a line instead of a circle, but the concept is still the same. The difference is that you only pick from 2 directions, left and right.

Pros:
- Relatively simple to implement.
- Common mechanic that's easily understood.

Cons:
- No ability to "manage" recoil.
- Not very immersive.

For the purposes of simplicity, we'll assume that the player class keeps track of the total recoil, allowing it to persist when changing weapons or performing actions. This value will be incremented when the weapon is fired and decreases gradually over time using the player's Update() method. When we project the shots into our world, we simply adjust the firing angle by a random amount lower than the total recoil.
// Determine which direction to spread in.
float direction = random.NextDouble() > 0.5 ? 1 : -1;

// Determine amount of spread.
float spread = direction * (random.NextDouble() * recoil);

// Construct ray direction.
Vector2 aiming = Vector2.Transform(Vector2.UnitX,
    Matrix.CreateRotationZ(rotation + spread);

aiming.Normalize();
Now the direction is ready for raycasting into the world. It's been slightly offset from the player's rotation by an amount which is less than the total amount of spread in either direction. Obviously, you can combine multiple lines to shorten the implementation and save memory allocation.

View Kick

View kick is also relatively prevelant in modern 3D shooters, though it's usually used more as a slight immersive effect rather than the core mechanic. The idea behind it is that when the user fires their weapon, a slight offset or "kick" is applied to the player's view to mimic the gun recoiling in their hands. Larger calibre weapons would obviously add a greater offset than smaller ones.

Like spread, the view could gradually reset back to its previous position when not firing. Some players find it more difficult to engage multiple targets when the aim is resetting though, so some games simply leave the view where it ends up to avoid this issue.


Incorporating view kick in a 3D shooter is done by selecting a direction which is mostly up, using a similar approach to spread, and adjusting the player's view by an appropriate amount for the weapon held. Once again, for 2D shooters you would only select from two directions (left or right), though you would also add a small amount of noise to the offset to compensate for the lack of a second axis.

Pros:
- More immersive than other approaches.
- Allows user to manage recoil by adjusting aim.

Cons:
- Large amounts of kick can be jarring.

View kick is slightly simpler to implement than spread, as it just involves adjusting the player's rotation. However, the recoil value needs to be incremented both positively and negatively when firing to make it kick in both directions. You would then override the player's rotation property to include this value, such that every time it is updated it "kicks" the player's view.
// Determine which direction to kick in.
float direction = random.NextDouble() > 0.5 ? 1 : -1;

// Calculate random noise.
float noise = (random.NextDouble() > 0.5 ? 1 : -1)
    * (random.NextDouble() * MaxNoise);

// Determine amount of kick.
float kick = (direction * recoilRate) + noise;

// Add recoil.
recoil = MathHelper.Clamp(recoil + kick, -0.25f, 0.25f);
The above implementation assumes that there is a gradual reset and clamps the rotation to stop the player spinning, though you could omit both to permanently alter the player's aim and avoid the issues with resetting. Raycasting proceeds as normal since the offset is applied directly to the player's rotation value, not the aiming angle.

Crosshair Kick

Crosshair kick is a slightly different take on view kick which is more common in simulator-style shooters where head movement can be controlled seperately from weapon aim. It works by applying kick to the weapon and/or crosshair, rather than the entire character. Like view kick, more offset is added for larger calibre weapons.

Unlike view kick, it would be impossible to omit resetting the offset as the crosshair could potentially get stuck at a limit or otherwise scroll off screen. As such, the crosshair must gradually reset when not firing to avoid this problem, similar to how spread reduces when idle.


Crosshair kick is performed using an offset similar to that in view kick. The difference is that rather than immediately applying it to the player's view, the offset is applied to the aiming direction when firing. It might also be necessary to update both the crosshair and the player's arms/weapon to point in the direction of the offset so that the player has a rough idea of where they're aiming.

Pros:
- Allows user to manage recoil.
- Not as jarring as view kick.

Cons:
- Slightly more complicated to implement.

Crosshair kick is implemented in a similar manner to view kick. The difference is that rather than updating the player's rotation, this value is used when adjusting the aim. I prefer to adjust the vector, rather than the angle, as that allows us to more easily determine where the crosshair is on screen (so that we can draw it later).
// Calculate firing angle.
Vector2 aiming = Vector2.Transform(new Vector2(1, recoil),
    Matrix.CreateRotationZ(rotation));

aiming.Normalize();
This way the recoil value can be directly used when drawing the crosshair to place it exactly where the bullets would go. You simply scale the value by how far away the crosshair is from the player. (i.e. If the crosshair is 480 pixels away from the player than you use 480 * recoil.)

Friday 6 July 2012

Project "Lead"

What is Project "Lead"?

Lead is a small project I work on every so often to explore game development aspects and to keep my programming skills sharp. At the time of writing it is a very rudimentary top-down shooter platform which I hope to eventually evolve into a small game.

Which programming language do you use and why?

I use C# with the XNA tool kit when programming with Lead. XNA provides many tools and libraries which allow me to get into core gameplay development very quickly. C# is also one of the first programming languages I was taught and as such is the one I am most familiar with.


What features does Lead have currently?

Currently Lead has a lot of the fundamental basics of a shooter in place. The character is controllable and the camera follows them correctly. They also correctly collide with walls and obstacles without passing through. The player can also fire a weapon which can trace impacts and deal damage to targets.

Why did you choose to rotate the camera with the character?

Lead is an experimental platform for my ideas. Typically, top-down shooters have the camera positioned directly over the player with no rotation. The problem with this perspective is that it makes it difficult for enemies to sneak up and surprise the player. I'm a big fan of tactical shooters and survival/horror games, as such I wanted to experiment with a more focused camera perspective.

I considered using a kind of "torch" mechanic to only light up the area ahead of the player, but I realised that it might be equally effective to simply position the camera in front of the player. I also settled quickly on the idea of rotating the camera with the player to get around the issue where players using widescreens would see further depending on the camera position.

Demonstrating how the player can see further in some directions with a widescreen

What technical aspects have you explored during development?

During development I've had to look into simple collision detection, including circles, boxes, etc. I've also had to look into camera transformations, specifically a format which allows both triangle primitive transformations and for use in XNA's SpriteBatch class. More recently I've also looked into raycasting for dealing with weapon mechanics, including polygon and circle intersections.

What features are you planning on working on next?

I'm planning to get more in depth with weapon mechanics including recoil/spread, animations and variable firing modes (automatic, burst, etc.). After that I'll be looking into rudimentary AI to create some enemies, which will likely grow to involve pathfinding and enemies reacting to stimulus (such as gunfire). After that I'll likely start working on incorporating some proper art assets and a minimalistic HUD.