Recreating the weapon HUD, ammo system and weapon swap functionality
This article expains how the weapon HUD carousel and ammo system works
In Destiny 2 a player always has 3 weapons equipped. Theres also 3 different types of ammunition types:
Primary
ammo weapons do less damage and have infinite ammoSpecial
ammo weapons do more damage and have limited reservesHeavy
ammo weapons do considerably more damage have very limited reserves and only one of these weapons can be equipped at a time.To replenish Special
and Heavy
ammo, Destiny 2 uses a weapon ammo pickup system in which enemies sometimes drop ammo crates when killed, green ammo crates replenish Special
ammo and purple ammo crates replenish Heavy
ammo.
When it comes to the weapon HUD, the currently equipped weapon is always shown on top, while the unequipped weapons are shown minimized below. The order of the unequipped weapons will always try to respect the originally equipped order, and when swapping weapons it will reorder the unequipped weapons accordingly.
When picking up ammo, a small animation plays as well as showing the amount of newly picked ammo on the side of the UI, and the total reserves number is automatically updated. Finally, when a weapon runs out of ammo, the slot for that weapon turns red and the ammo number starts flashing red.
The player character has a GAS attribute set that holds info for the current equipped weapon. When a weapon is swapped, the data in the attribute set is saved onto a normal set of variables stored in the Player State (so that ammo counts persist even if a player is killed) and the newly equipped weapons saved attributes are loaded onto the GAS attribute set. This is handled by the WeaponEquipmentComponent in C++. The ammo type each weapon uses can be set up in the weapons WeaponInstance class which is a class that holds all the information about how a weapon functions, and is meant to be extended by blueprints for designers to tweak.
When firing a weapon, the firing cost Gameplay Effect removes 1 ammo from the player’s currently equipped weapon mag ammo attribute. When the magazine runs out of ammo, the GA_Reload
ability’s CanActivateAbility
function is overloaded to check wether the weapon has enough ammo to be reloaded. For Primary
ammo weapons, the reserves are set to -1 to indicate they have infinite ammo:
The ammo bricks are actors that inherit from custom C++ classes, DensPickupItem and DensAmmoBrick. When a player collides with the ammo brick collision sphere, the brick will check wether the colliding player’s corresponding ammo type is full or not. If that ammo type is full, the brick will not be picked up.
The weapon section in the HUD is comprised of a Weapon Carousel
Widget which holds three different individual WeaponInfo
Widgets
The weapon info widget gives the player information about the current weapon. It has the following dynamic elements that are initialized when a new weapon is equipped:
The following function is called when initializing a new weapon instance with the UI:
The weapon info widget also has widget animations that play when the weapon is equipped/unequipped and when ammo is picked up:
The weapon carousel widget holds three separate weapon info widgets. The currently equipped weapon info widget is set at the top, while the unequipped weapons are layed out minimized under it:
When the player swaps weapons, an event is called from C++ in the Weapon Carousel widget that moves the selected weapon to the topmost slot, playing the equipped animation(from the weapon info widget above) while simultaneously playing the weapon unequip animation on the previously equipped weapon. The event then runs a short algorithm that decides where each of the unequipped weapons should be placed in the carousel to try and respect the original equip order.
Once the position of each of the widgets is decided, an FInterp To node is used to smoothly move each of the weapon info widgets to their corresponding location. The following function is ran each tick to simulate a widget animation until the widget position is equal to the target position:
The algorithm for ordering the weapons is rather simple and not extremely efficient( O(N^2) ) but since it only iterates an array of 3 items it does its job. (It is designed to be able to support more than 3 weapon slots). The initial slots on the carousel are numbered from 0 to 2 (0, 1, 2).
CarouselNodeEmptyArray
) that holds boolean value on wether a slot in the carousel is empty and set the original slot of the weapon to be equipped to empty. (eg if equipping the weapon in slot 1, set the bool array slot 1 to empty)EquippedWeaponSlotArray
with the remaining weapon slots. (eg if equipping weapon in slot 1, leftover array should be [0, 2] )EquippedWeaponSlotArray
array with index j
:
CarouselNodeEmptyArray
array starting from the end with index k
:
CarouselNodeEmptyArray[k]
is empty) Or If (EquippedWeaponSlotArray[j]
>= k
):
EquippedWeaponSlotArray[j]
.CarouselNodeEmptyArray[j]
to empty and set CarouselNodeEmptyArray[k]
to not empty.Using this algorithm allows the weapons in the carousel to always be ordered. For example if a player has their Heavy
ammo weapon equipped and wants to change to their first unequipped weapon, the entire carousel will reorder the Heavy
ammo weapon to be at the bottom of the list: