I've been working on fps games for a while and i keep having to restart over and over for like 4-5 times now because I don't understand how to make a weapon system for my guns. I designed my own weapons in blender cause it was just fun and i imported them into my game but because of animations i don't understand how to play the animations that i made in unity without specifically calling them which i would have to do for every gun that i make (which i plan to have a lot if i could figure this out) which i've already tried but only manage to do for my ak-47 and pistol. Along with that I need help reusing animations because i don't want to make an animation for every gun then have to link them with each other in the animation tab.
Images(First screenshot shows all the animations i made for 2 guns, second screenshot shows the code i used to call said animations and the logic for when):
#Trouble with weapon system and animations
1 messages · Page 1 of 1 (latest)
An image of your code is not helpful
When asking a question about a problem with code, people who are volunteering to help need the text of the code. Unless you are asking about your IDE - and not the code itself - images of the code are not an acceptable substitute.
Source: https://idownvotedbecau.se/imageofcode
Please send your code as a codeblock. If you don't know how to send a codeblock, type []cb
If your weapons share common functionality such as shooting, reloading, holstering, aiming down sights, etc - then you can use Animation Overrides: https://docs.unity3d.com/Manual/AnimatorOverrideController.html - the idea is that instead of naming a state "Specific_Gun_Reload" you can just name it "Reload" and then have a "Specific Gun" override, that has a specific animation for reload, so you replace your animator with the override for each gun
I would also suggest trying to use ScriptableObjects as the data container for your prefabs of each weapon, this way you can adjust their info from the project and keep all their relevant references pre-setup in 1 place, your weapon script can just reference the type of ScriptableObject and you can use virtual functions or a interface to setup the common functionality like a public virtual Reload() then if you have a weapon that reloads different from others (for example, a shotgun or RPG may reload different than a rifle, but they all reload), you can public override Reload() for that specific gun, and simply call anim.Play("Reload") since the RPG override controller knows what animation its "Reload" state should use - you could even convert the states to hash and cache them so you dont have to worry about string literals breaking your code if you decide to rename a state: https://discussions.unity.com/t/using-hash/715264/3
I tried both of these in one of my game variations but i didnt rlly know how to use the override controller so ill see if i can get some guidance from some tutorials or smth and the scriptable object did work well but i had some problems with knowing which gun is currently equipped or not so i wasnt able to do some actions on specific guns like reloading
Another thing is on one of my variations i tried just having all my guns in the same place and disabling thr guns that arent equipped by the player but then it gave me an error saying couldnt find gameobject or smtb whenever i tried to reload
I would certainly look up some tutorials on how to use animation override controllers as they can be very helpful with "same but different" states, in a similar way virtual functions can be helpful for "same but different" logic - in my FPS I use a combination of all 3 of what you described, but I only use the approach of having all my guns disabled and toggling them after I initially spawn them, as players have "loadouts" with different scriptable objects representing the guns to keep in memory (or hierarchy in this case)
With virtual functions you shouldnt really need to know which gun is specifically equipped cause your gun script becomes the specific gun, for example:
public class SomeGun : Mono
{
[field: SerializeField] GunSO gun {get; set;}
[SerializeField] Animator anim;
int reloadHash;
void Awake()
{
reloadHash = Animator.StringToHash("Reload");
}
public void Init(GunSO source)
{
gun = source;
anim.controller = gun.overrides;
}
public virtual void Reload()
{
Debug.Log($"reloading gun: {gun.fancyName}");
DoReloadLogic(gun.reloadTime);
anim.Play(reloadHash);
}
}
[CreateAssetMenu()]
public class GunSO : ScriptableObject
{
public string fancyName;
public float reloadTime;
public AnimatorOverrideController overrides;
}
If you have a AK, pistol, sniper rifle, etc they all just use this, some manager can hold a list of GunSO and some UI can let you select from that list, create a prefab with SomeGun, call Init passing the GunSO or set it directly from the inspector, bind your reload input to call SomeGun.Reload, and your reloading whatever gun you have - and if you have a shotgun, RPG or something that reloads differently, then you can create a public class RPG : SomeGun and public override void Reload() {} to do whatever is different about that reload (maybe it loads 1 bullet per second over 5 seconds instead of a full magazine after 5 seconds, for example) - hopefully some of that helps better explain your first 2 variations
@empty isle, your codeblock was automatically pasted to https://paste.myst.rs/j5xhyzru
a powerful website for storing and sharing text and code snippets. completely free and open source.
So Im kinda stupid and cant understand this could u dumb it down for me pls
Ok so i figured out what u meant but when i tried using scriptable objects i didnt rlly know’t know how to access a specific one
Like for example if i needed to reload the ak i didnt know how to get the ak’s SO and change the value so i tried just saying that id the gunscript isnt null the SO.ammo = maxMqgSize but it didnt work
I need a script that only reloads the weapon equipped and everything else doesnt
The idea would be that some manager that initially assigns the AK, or shotgun or whatever knows about the specific weapon and passes the SO data to your weapon, so for example:
public class PlayerController : Mono
{
[SerializeField] SomeGun basePrefab;
SomeGun primary;
public void SetGun(GunSO gun)
{
primary = Instaniate(basePrefab, hand.transform, Quat.identity);
primary.Init(gun);
}
void OnInput()
{
if(input == "R") {primary.Reload();}
}
}
Not verbatim code but hopefully shows the idea, however you handle "input -> triggers action" it calls a base Reload function on the gun, the gun can be assigned by whatever knows about the SO, and the player doesnt need to know about the specific AK SO, it just needs to be sure in this case that primary != null before trying to call anything on it - so when you spawn maybe primary.gun is the AKSO, when you call primary.Reload it will reload based on the .gun SO data, being your AK - maybe you pick up a RPGSO later and replace primary.gun, reloading then reloading the RPG instead of the AK, and neither PlayerController or primary need to actually know what specific SO is assigned to .gun, whatever system sets .gun is the only thing that needs to know about the SO to assign it, so reloading doesnt need to have a specific "if AK reload this way, if M4 reload that way", having a inherited M4 : SomeGun handles that already and in that case, primary becomes M4 since the type of primary is SomeGun (in this very specific example though, a AK and M4 do the same thing with different stats so you technically wont need to inherit by changing the .gun SO)
@empty isle, your codeblock was automatically pasted to https://paste.myst.rs/8kaqc6nx
a powerful website for storing and sharing text and code snippets. completely free and open source.
I figured i tout thx
Or at least kinda
I still have a problem with showing the ammo of the weapon on the screen by accessing the scriptable object
For some reason it only uses the scriptable object for kne weapon no matter which one u have equipped
@empty isle u have any ideas
You could either create a local variable based on the scriptable object data when you initialize the weapon, and use that, for example:
public class SomeGun : Mono
{
[field: SerializeField] GunSO gun {get; set;}
int ammo;
public void Init(GunSO source)
{
gun = source;
...
ammo = gun.maxAmmo;
}
public virtual void Shoot()
{
if (ammo <= 0) { Debug.Log("out of ammo"); return;}
...
ammo--;
}
}
Or what I like to do is use a serialized class in my ScriptableObjects so I can clone the data with something like Newtonsoft JSON or just setup classes to clone the data for me, for example:
[CreateAssetMenu()]
public class GunSO : ScriptableObject
{
public GunData data;
}
[System.Serializable]
public class GunData
{
public string fancyName;
public int maxAmmo;
...
public GunData Clone()
{
var data = new GunData();
data.fancyName = fancyName;
data.maxAmmo = maxAmmo;
...
return data;
}
}
public class SomeGun : Mono
{
...
GunData data;
public void Init(GunSO source)
{
...
data = source.data.Clone();
//with JSON you can do something like:
//string json = JsonConvert.SerializeObject(source.data);
//data = JsonConvert.DeserializeObject<GunData>(json);
//could also save/load json string to/from file for something like persistant upgrades
}
}
If youd like more info on that approach: https://www.newtonsoft.com/json/help/html/serializingjson.htm (from Unity, it is a package youll need to install, you could also use the built-in JsonUtility though it has limitations with certain data types) - both should get you a similar result though
Np, glad you got it worked out 🙂
ok so
i kinda lied to u
i got another problem w it
using TMPro;
using UnityEngine;
public class Shooting : MonoBehaviour
{
public Weapon gunData;
public EquipSystem equipSystem;
public Camera fpsCam;
private void Start()
{
gunData.nextFire = 0f;
gunData.currentMagAmmo = gunData.maxMagAmmo;
gunData.currentMaxAmmo = gunData.maxMaxAmmo;
}
void Update()
{
if (Input.GetMouseButton(0) && EquipSystem.slotFull && Time.time >= gunData.nextFire && gunData.currentMagAmmo > 0)
{
gunData.nextFire = Time.time + 1f / gunData.fireRate;
Shoot();
}
else if (equipSystem.equipped && !gunData.multiFire && Input.GetMouseButtonDown(0) && gunData.currentMagAmmo > 0) Shoot();
}
void Shoot()
{
Debug.Log(gunData.name);
RaycastHit hit;
Physics.Raycast(fpsCam.transform.position, fpsCam.transform.forward, out hit, gunData.range);
gunData.currentMagAmmo -= 1;
}
}
in this code
gun data or Weapon is a reference to the scriptable object script named Weapon
every time i shoot a gun i shoot every gun even if i don't have it equipped and i don't know y
any ideas @empty isle ?
i tried to fix it and i know how to i just don't know how to integrate it
im accessing the entire script and changing the ammo there instead of accessing the inividual scriptable object and changing the individual weapons ammo
but idk how to change it without making a reference for each weapon
In the script you posted, your checking for input in Update, so if this script exists on 4 different objects in the scene, 4 Updates that are checking for input is running, so if you click and all other conditions are true, your Shoot function is going to be called on all 4
Because of this, I often make sure classes only handle logic they control, in this case a "weapon" would only handle things like shooting and reload, then id have whatever script should use the weapon to handle input, animations, sound, etc separately, that also makes it easier to setup AI to use weapons since the AI logic can call the shoot, reload, etc functions instead of input logic doing the same thing
For example, that might look something like this:
public class Player : Mono
{
public Weapon gun;
InputListener inputListener;
void Start()
{
inputListener = new(this);
}
void Update()
{
inputListener.OnUpdate();
}
public void OnInputAttack()
{
gun?.Shoot(); //the ? is a null check, its the same thing as if(gun != null) {gun.Shoot();}, if gun is null then Shoot wont be called and a error wont be thrown
}
}
public class InputListener
{
Player player;
public InputListener(Player owner) {player = owner;}
public void OnUpdate()
{
if (Input.GetButton(...)) {player.OnInputAttack();}
}
}
It doesnt have to be this in-depth, you could also do the logic inside Player in that example instead of having a InputListener, you could have the InputListener fire events instead of a Init (or in this example, a constructor) that references the player, then Player can just subscribe and respond to the input events - but the idea is that centralizing your input to a controller like a "player" script or "AI" script and out of your "weapon" script, means only 1 location checks for input that causes your shoot function to get called this way, hopefully that makes sense (I may have explained that poorly), if anything is confusing, I can try to clarify
@empty isle, your codeblock was automatically pasted to https://paste.myst.rs/1cfen671
a powerful website for storing and sharing text and code snippets. completely free and open source.
So i was working up to like 3 am yesterday and i got it fixed with something rlly simple thx anyways
I kept overlooking smth rlly dumb
Happens lol, good to hear you got it worked out!