Associate Ammo with Weapon?

Hello.

I want a weapon system were Ammunitions are independent: that means multiple weapons can use the same kind of ammo.

Let’s say I have these 4 kind of Ammunitions:

Each one has its own ammount of munitions that, during the game, can be picked up from the ground.

Now let’s say I have two weapons:

  1. A pistol which uses SingleBullets
  2. A special pistol which uses **SingleBullets as PrimaryFire **and Electricity as SecondaryFire

As you can see, I have two weapons and both share the same kind of Ammo: the SingleBullets.
So if either the ‘pistol’ or the ‘special pistol’ shoot their PrimaryFire, ammos are taken from the SingleBullet munitions.


I started making this.

  • AAmmunition is an Actor-derived class which basically has two variables: **EAmmoType **(enumeration) and **CurrentAmmo **(int32, which stores the ammount of ammo during the game).

  • In the PlayerCharacter, I made an Inventory of TSubclassOf<AAmmunition>.

  • I create as many Blueprint-classes as the kind of AAmmunitions there are in my game (all derived from AAmmunition c++ class) 2.jpg

  • My Weapon class has its own attributes that describe which kind of Ammo to use for the Primary and the Secondary fire: TSubclassOf<AAmmunition>

The problem is right here.

How do I decrease the CurrentAmmo in the PlayerCharacter AmmoInventory from the Weapon class??? (when the player shoots the primary/secondary fire)

The PlayerCharacter has a DefaultAmmoInventory, which is an array of TSubclassOf<Ammunition> that I set in the blueprint.
When the game starts, those SubclassesOf<Ammunition> are spawned in another array called AmmoInventory

So you understand where the problem is?
The Character has an inventory of Ammunition* while in the Weapon class I have two SubclassesOf<Ammunition>, one for the Primary and one for the Secondary fire.

4.jpg

When I shoot from the Weapon->PrimaryFire() method, I want to decrease ammunitions from the Character::AmmoInventory that are the same type of Weapon::PrimaryAmmo

Also, is there a more efficient way to achieve this system?

As Unreal Newbie, I cannot be certain about the solution… but I might can provide you my own solution though. First, I think I gonna make TMap or TArray (Personally TMap, but in order to access it by Blueprint, u need to make UFUNCTIONs) of your character’s ammunition. TMap <EnumAmmoType, Ammunition class> or TArray<Ammuition class> (For now, I gonna use TMap). Second, in your weapon class, I think I gonna set two Variables by Enum -> EnumAmmoType AmmoPrimary, EnumAmmoType AmmoSecondary. In Blueprint of Weapon class, you can set ur ammo usage type as Enum. So, when you gonna use the function “Fire (or shoot)”, you check the current weapon (i think you made it as TSubclassOf<Weapon>) and its ammo usage. after getting its ammo usage type, access the ammunition inventory, decrease the specific type of ammo as you desired number. I think I would create UFUNCTION as “void ModifyTheAmmoQuantity(EnumAmmoType type, int changeInQuantity)” if your ammunition inventory is TMap…(use FindRef(type) to get the value) but I am not still familiar with TArray… sorry about it.

It is very newbie solution… that’s what I come to think of it… I hope you find it useful :stuck_out_tongue:

Alright but if I have a Secondary Fire for a specific weapon which is kinda particular (like the Sniper zoom-in), should I make ANOTHER class dedicated to the Sniper to override the virtual function SecondaryFire()?

Interesting idea. Your new class, so called “Sniper” can inherit from Weapon to have virtual function SecondaryFire(). Yet with my experimental approach, I think you can try to make SecondaryFire as object. In my thoughts, Let’s make the weapon class as AActor class and make Primary Fire / Secondary Fire from Actor-Component. Thus you can attach those components (Fire styles) to an weapon. If you ever want to implement Zoom in function from Sniper’s secondary fire, just make the “zoom in” as an object which can be inherited from Secondary Fire Object(or component). But… there is problem from that approach, which causes some problem (possibly newbie mistake) in my project. For myself, I have no idea how ticks in component can start as I want. If you ever need to use tick from component as your demand… I may need help about it too. But for experimental data structure, I think implementing those Fire Style as separate objects, might be interesting approach by further maintenance in my opinion. Well… I am newbie though… I hope you like it.

You might be better off separating ammo in your inventory versus the current ammo in your gun.

What I mean by this is that the weapon has its own ammo count, aka the magazine.
And then the inventory has its own ammo count per ammo type.

When you fire, you only decrease the ammo count in your weapon.
When you reload, you only decrease the ammo count in your inventory and refill the ammo count in your weapon.

This is what I do, and I’m sure you’d be able to extend it to secondary fire with different ammo type. With the secondary fire, you would just have to modify the reload to know what the current ammo type on the weapon is in order to decrease the correct ammo.

There’s a much faster way to do this without requiring TMap or TArray searches / comparisons every time you want to use ammo. In the case of rapid-fire weapons you’ll be pointlessly eating away chunks of performance doing those searches all the time.

Having pointers to each ammo class is irrelevant information, it’s must faster to use an ENUM and you don’t really sacrifice any flexibility doing this. If I was going to implement this as you’ve done it, I’d probably do it like this:

    • Create an ENUM for every different ammunition type. This makes it easy to add or remove them whenever you want:


UENUM(BlueprintType)
enum class EAmmoType : uint8
{
    AT_AmmoType1
    AT_AmmoType2
}


  1. In your player character class, store an ammo inventory as a TArray of int32 (or unit32 if you don’t need blueprint access), and use the ENUM as the index. You’ll probably want to have a #define somewhere that dictates the number of different ammo types you have.


UPROPERTY(BlueprintReadOnly, Category = "Ammo")
TArray<int32> AmmoInventory;


  1. Accessing / removing ammo then becomes as easy as this. This is faster than a TMap because you don’t need to do any comparisons, you’re just directly accessing the array members.


void AMyCharacter::InitializeAmmo()
{
	AmmoInventory.SetNumZeroed(NUM_AMMO_TYPES);
}

bool AMyCharacter::UseAmmo(const EAmmoType AmmoTypeToUse, const int32 NumUsed)
{
	// Ensure we don't use more ammo than possible
	const int32 ActualUse = FMath::Min(NumUsed, AmmoInventory(uint32)AmmoTypeToUse]);

	// Subtract the Ammo
	AmmoInventory(uint32)AmmoTypeToUse] -= ActualUse;

	// Return true if we've run out of ammo or something
	return AmmoInventory(uint32)AmmoTypeToUse] == 0 ? true : false;
}


That is a nice system, but how do I define the Maximum Ammount of Ammo for a specific AmmoType?

e.g:

Rockets (max: 50)
Electricity (max: 120)

That is a property strongly related to the AmmoType, independently from the Weapon which uses them.

Thanks.

I feel like this is not even UT4 specific more of a general programming question. I could think of the following solutions to add additional information to the proposed solution (tho my solution would involve a proper inventary system, handling “items”):

  • Add additional arrays containing the max/min whatever counts having the same indexes
  • Extend the array to have some kind of a class/struct etc, containing your information (current, max…)

Edit:

As for an example, for the first solution, add additional array like this:



TArray<int32> AmmoInventory;
TArray<int32> AmmoMax; // holds max for each ammo type

Yeah, could just do the above. Becomes a little less ideal then though, you could create a custom struct that has two int32’s and store an array of those instead (saves having two separate arrays).

To be honest I’m a little confused about the system, does the weapon determine how much ammo you can carry, or is the amount you carry determined by the player?

I’m a C++ programmer and I am fully aware this kind of algorithm in VERY simple… in C++
Unreal Engine treats C++ in a different way, it’s not as flexible as plain C++ is.

During the time, I had to change lots of my C++ programming algorithms and logic just to fit them with UE4.

What I’m about to achieve is very simple, listen.

I have a set of AmmoTypes, ok? Let’s say:

Rocket, Grenade, Bullet, Shell, Electricity…

Each AmmoType also has a Maximum Ammount of Ammunitions, like I said earlier:

Rocket (max: 50)
Grenade (max: 30)

So in my game, I can’t hold more than 50 rockets, or more than 30 grenades (depending on the GameMode… there could be a sort of Quake Instagib GameMode where the “Sniper” is infinite!)

Now, for every weapon I add to the game I’ll have to choose which AmmoType it should Use() for its Primary and Secondary fire.

As simple.

Chill dude, I’m helping. I don’t see that mentioned anywhere above…

It’s simple then - you create a static TArray of uint32 in the Gamemode, which determines maximum ammo type for each type. Access it the same way I do above in post #6. Fastest and most convenient way I can think of and you’re only storing the information you absolutely need.

I spent 5 minutes to find the part in my post that offended you. At the end I think you misunderstood the “As simple”.

I was absolutely calm, I was not blaming you for not understanding my concept :o

I’d never attack anyone who’s trying to help me. Rather, I really thank you guys =)

I will try tomorrow, I’ve been pretty busy today. I’ll keep you updated!

Sorry not at my computer so can’t post code but I’ll talk you through the way I went about it in my project .

I would just make a master weapon actor with all the necessary data inside, then for each specific type of weapon make a child copy of this master weapon actor and set the variables accordingly. Now you then need two enums and one bool variable. 1 enum weapon type, 2 enum ammo type, and the bool isPrimaryWeaponFire.

So let’s say I have my master weapon actor I then want to make the pistol from you’re first post I create a child copy of the master weapon actor and call it pistol . Inside I change all the relevant variables so name = pistol , for the weapon type enum set the type to pistol, for the ammo type do a check if isPrimaryWeaponFire is true then set the ammo type enum to be normal bullets, if the bool is false and we are in secondary fire mode set the enum to equal electricity bullets.

When you fire you first check the bool to find out if the weapon is firing its primary or secondary weapon fire mode. So let’s say I’m firing the pistol from you’re first example and it’s fire mode bool returns true for isPrimaryWeapon fire . This will mean that the if check we asked about earlier will return that the ammo enum must be normal bullets if the bool returns false the enum would return electric bullets.

No worries, just came across a bit blunt that’s all :wink:

But yeah in your case, I think that way all makes sense.

Ok I’ve set up everything.
The last thing I need now is to decide, for each AmmoType, its Maximum Ammount of munitions.

Since it depends from the GameMode being played, you suggested to set the inventory in the GameMode class.

I did it

But I can’t set the Maximum Ammount for each AmmoType one by one in the GameMode class defaults.

How can I do that? Am I forced to hardcode? like:

MaxAmmoInventory(uint8)Rocket] = 50
MaxAmmoInventory(uint8)Electricity] = 120


Also I would like to be able to decide if a particular Ammo is INFINITE.
With a class/struct I could’ve quickly used a bool.

Now, instead, I have to set it manually doing


AmmoInventory(uint8)**AmmoType**] = -1

and when I have to check whether it’s infinite or not:


 If(AmmoInventory(uint8)AmmoType] == -1) 

    @ this method you suggested is very performing/fast and easy, but all these good things seem to have a price at some point. It is and it's not flexible at the same time. For some things I have to use workarounds and it becomes a little tricky.

In the Gamemode blueprint you can press the + button for the array, and change the values there, and you can also edit them on the fly/in-game pretty easily.

Thing is, unless you know which entry corresponds to which ammo type, you could mess it up adding it all manually. My solution above is really to avoid having lots of unnecessary pointers/class references and duplication across your code, and prevents having to iterate TMaps and TArrays of unknown size everytime you fire the weapon, which could eventually become a bottleneck.

In regards to infinite ammo, I’d do that in the weapon - just skip the call to remove ammo if the weapon has bInfiniteAmmo = true or something, but to be honest this all depends on the design of the game and what you want to add later.

It’s very tedium and uncomfortable. If I ever decided to implement a new ammo/weapon it’ll be a mess. I should run all over my code changing Enums, define directives, Blueprints…

It would have been IDEAL to just make a struct (not even a class, although it’s the same thing) and make as many instances of that struct as the number of Ammo in the game.

In C++ I would have achieved this in a nutshell. UE4 sometimes is so tricky…

There really are a lot of possible ways to handle this, but in terms of infinite ammo weapons its often best to just have those weapons or fire types not decrement ammo at all.
For the instagib rifles in UT they are a separate subclass of their parent shockrifle with some changed fire mechanics, including not needing ammo. The when the instagib gamemode starts all players have their inventory cleared, and are given the instagib rifle. If you’re going to have a similar game mode I’d suggest doing the same.

Overall this sounds like an architecture problem.

Assuming you’re not doing a huge rewrite of things you’ll at least want to add in some kind of inventory management system on the player, part of which will be for ammo.
The whole ammo thing should be solvable with a messaging system.

The player entity can have a delegate for handling inventory adjustments.
The weapon subscribes the owner’s inventory adjustment delegate in its firing event and sends along an inventory changed event arg structure to tell the players inventory system to add or subtract some number of some subclass of inventory item. This way for things you know will be infinite, you just pass a value of 0 for how much to remove and the rest of the system operates as normal.

The player then handles the bookkeeping on their inventory. Infinite ammo could be set on the player as a flag or some other noticable way so when the inventory system goes to adjust the items the adjustment gets altered based on the current flag status.

Maximum inventory amounts should be able to be adjusted on the player, possibly with a similar messaging system, so the gamemode or some other class that can interact with the players can find all players and set their inventory amounts as well as capacities and defaults. You’ll need this sort of thing anyway if you want to implement a “backpack” or something that increases capacity as a pickup.

Also, I could swear some of this functionality is already in what ships with UE4 so you can have a template to work from and or build off of.

You said it! I really don’t know how to handle this system.

Each weapon has its own PrimaryFire and PrimaryFire-AmmoType. But it could also happen that one day I decide to make two weapons that fire not only the Primary, but also the SecondaryFire.
So this means I also need to CHECK if the weapon I’m calling SecondaryFire() method from, currently HAS a SecondaryFire-AmmoType associated.

This would have been achievable perfectly with **POINTERS **to Ammunition class. Working on Enums looks a bit tricky, even if very efficient (as suggested by @)

It would also have had more sense to have a TArray<Ammunition> instead of a TArray<int32>

Unreal Tournament has a SecondaryFire, that’s true; BUT the Ammo you spend from the SecondaryFire() are the same you spend for the Primary one, with the only difference in the way of shooting.

Also, what if each weapon has its own Primary or Secondary fire implementation? I should create a new inherited C++ class for each one? (taking advantage from polymorphism)

It’s a very simple C++ algorithm, the problem is the Engine limitations.

Also, let me add that there could be the case where the Weapon, in its SecondaryFire(), consumes both the Primary-AmmoType and the Secondary-AmmoType