UE5 Prototyping: FPS Magazine System

Written by William G. C. Fox on May 4th, 2023 Views Report Post

A gameplay mechanic that I really enjoy in FPS is a realistic Magazine system with first person shooters. Normally the guns take a pool of bullets and each magazine resets from this pool, but in real life a magazine is an entity within it's self. Thus I created a baseline ammo management system called Ammo Management.

Ammo Management contains a structure called FMagazine which contains an id for the mag, a number of bullets and a max number of bullets. The latter two are floats to use a percentage system for UI which is yet to be implemented.

USTRUCT()
struct FMagazine
{
	GENERATED_BODY()

	UPROPERTY()
	int id;
	UPROPERTY()
	float numberOfBullets;
	UPROPERTY()
	float maxNumberOfBullets;

These FMagazine are then created in the constructor for the ammo management class and are stored in a TArray. The array is then poped to a variable called currentMag which is then acted apon.

for (int i=0; i < noOfMags; i++)
	{
		mags.Add(FMagazine(i + 1, 30));
	}

	currentMag = mags.Pop();

Ammo management is then referenced on the weapon it's, self in this case the FPS starter weapon. This is called when the weapon is attached to the gun in Unreal starter content script.

AmmoManager = NewObject<UAmmo_Management>(this);

When the gun is fired, first it checks if the Ammo Managament pointer has been created, if not it returns so there is not a nullptr error. When the current mag is out of ammo then the gun can not fire so it returns if no bullets in the magazine.

	//Weapon script
	if (AmmoManager == nullptr) 
	{
		return;
	}

	if (AmmoManager->GetCurrentAmmoCount() <= 0) 
	{
		UE_LOG(LogTemp, Error, TEXT("Out of ammo"));
		return;
	}
	
	//Ammo Management Script
	
	float UAmmo_Management::GetCurrentAmmoCount()
    {
	    return currentMag.GetNumberOfBullets();
    }
    
    //FMagazine Function just returns the number of bullets

After these checks the ammo is minused after the projectile is fired

//Weapon class
AmmoManager->MinusAmmo();

//Ammo Managment Class
void UAmmo_Management::MinusAmmo() 
{
	currentMag.FireBullet();
	currentMag.TooString();
}
//This calls function in FMagazine that simply -- the current ammo

Since the starter content does not have a reload function I created one. Setting up an action mapping to the key 'R'. The function calls Ammo Management reload which adds the current mag back to the array, then sorts the magazines in the array from most number of current bullets to end, and then pops the top one too the currentMag variable to use. To achieve the sorting I used the operator< function and to create a custom compare for the sort function on the FMagazine Structure. Just to be double safe I also define the operator for == in case of magazines with the same ammo.

//Weapon Class
void UTP_WeaponComponent::Reload()
{
	if (AmmoManager == nullptr) { return; }
	AmmoManager->MagsReload();
}

//Ammo Management Class
void UAmmo_Management::MagsReload() 
{
	mags.Add(currentMag);

	mags.Sort();
	TooString();
	currentMag = mags.Pop();
}
//Operator functions in FMagazine

	FORCEINLINE bool operator<(const FMagazine& mag1) const
	{
		if (this->numberOfBullets < mag1.numberOfBullets)
		{
			return true;
		}
		else
		{
			return false;
		}
	};

	
	FORCEINLINE bool operator==(const FMagazine& mag1) const
	{
		if (this->numberOfBullets == mag1.numberOfBullets)
		{
			return true;
		}
		else
		{
			return false;
		}
	};

As a result, all of the code works and creates a cool little system that I really enjoyed making. It is really fun having the skills to create the systems that I enjoyed from a young age (specifically Battlefield 2). I learned more on structures and how the function in Unreal since in the past I had used them for smaller specific data holders.

Comments (0)