One script or Many for a weapon system?

I just started making a weapon system and I suddenly realized I had no clue where to begin. I know Inheritance stacking is very impractical, so composition is the way to go, but how do I go about it? I mean, should I write everything in one script and just select/deselect behavior from the editor, or should I write separate scripts?
To put it in perspective, imagine a pistol, a laser and a boomerang. All of these can be fired, but a boomerang can’t be reloaded for example, and pistol ammo doesn’t recharge, and a laser doesn’t have a firing rate.

So, do I write a separate script for every weapon, do I write one script and attach it to every weapon and exclude certain blocks of code, do I write a bunch of simple scripts for basic behavior and stack them up as components on a per weapon basis, or do I write one script with most of the functionality and inherit from it no more than once?

This is my first time writing a weapon system and I don’t wanna realize I’ve taken the wrong approach halfway through. Thing is I wanna have the option to add weapon customization in the future and I can just imagine the nightmare I’d face with inheritance.

I did not yet write a weapon system, at least not something advanced.
But Unity is based on components, I’d imagine the best way to go at it is to write many small (very small!) scripts, each in charge of a behavior (Rate of fire, reload, projectile, etc…) and then add them all to one object.
This way you can re-use the code, even in other projects.

While this makes use of Unity’s Mono system (I forgot how the component-based coding style is named), and it should get rid of any chances of code being duplicated, and fixing issues much easier (since each script is not big, or diverse in it’s operation), it does mean keeping track of many scripts, and making sure that each component/script can make use of the others.

I’ve tried doing this myself, but soon found myself focusing so much on optimizing the code and it’s synergy that I did not advance with my project at all!

You can also start with making a complete script of the weapon, and then cut pieces of it to other scripts if you don’t like it as one big script.

Note that I am pressured for time, otherwise I would def’ work on the multiple-small scripts method. I plan to do so when I can, I believe it would be worth it, though I don’t have much game-coding experience, but making mistakes is the best way to improve, ya know?

I just posted some code, screenshots, and an example project here:

Skill casting method and secondary effects

It might be useful. Weapons (or skills in the case of the example project) are composed of different behaviors (there’s a reason Unity calls them MonoBehaviors and not MonoScripts) such as behavior for targeting, behavior for reloading, behavior for firing, etc. This makes it very expandable in a minimum of code.

1 Like

The way I do it is one script for all weapons, then just add it to all the weapons I need it to be and change their variables values to fit the need of the weapon.

But alternatively you can do a mix of both, have a base abstract class where all your weapon inherit from. So you have the same base class you can easily reference, but also a completly separate script for each weapon to fit each individual behaviour.

Consider this, having too many different unique scripts for weapons can give you a headache unless you are sure you don’t need to add anything new in the game after release.

Consider also referencing all the weapons in some arrays for switching or anything else, if you have unique script per weapon you have to make a big block of code to manage each transition from one another, if instead you keep a modular approach you can manage everything with less code. It depends on how you want to sort things.

1 Like

I see, thank you for your replies. Seems to me that the best way to go would be component based. I was thinking of using composition like that but since you can’t NEW a MonoBehavior, I’m thinking of having some backing code for calculations or item management and then have the weapon manager NEW it, while having the functionality set up as separate small scripts.

I’ll fiddle around with it, but now at least I know what kind of structure to follow. Thanks again.

This is how I did my spell system: I have a base SpellScript that all spells inherit from. It contains variables such as damage, manaCost, and coolDown, along with some functions that all spells will use.
Then, each derived spell class has its own functions that define its own unique behaviour.

Maybe I’m misunderstanding, but you can use GameObject.AddComponent() to add a new instance of a MonoBehaviour to a GameObject at runtime.

1 Like

Speaking of runtime, and it’s kinda off-topic, but is it possible to change the physics from 3d to 2d and vice versa? I’m mostly interested in the Gravity Scale of the 2D settings, so that I can change that property at runtime if I have a perspective swapping as a gameplay mechanic.

I made a system with over a hundred weapons, and things like pointed, blunt, cutting. Weapons belong to different classes which require different skills to use effectively. Weapons swing at different speeds with different power and different attack directions.

Oh yeah, so answer to the question. It took like 10, 300 line scripts. And I am not done yet. So no, there is no “miracle script” for super easy weapon systems lol…

@Kensei - consider really leveraging composition. Try to keep your scripts under 20-30 lines and focused on one very specific behavior. For example, the Temporary.cs script in the example project I linked in my first post is only a couple lines long. It just invokes Destroy() after a time. That’s all it does. But you can add it to anything - rockets, flames, area effect heals, etc.

In the example project, I defined a rocket launcher by adding these components: AimCenter (aims in the center of the screen), ClickToCast (fires when the player mouse-clicks), and SpawnWithVelocity (shoots a projectile). You could imagine adding other components such as PlayAudioOnFire, PlayAnimationOnFire, etc. – all very short and generic, but in combination they can create sophisticated behavior.

Similarly, the rocket prefab has these components: Temporary (despawns after a time if it doesn’t hit anything), CollisionDamage (sends a TakeDamage message when it hits something), and SpawnCollisionReplacement (replaces itself with an explosion prefab when it hits something). Again, a combination of short, generic behaviors that create a specific result.

(Also, completely aside, the example project instantiates and destroys for simplicity, but in practice you’ll want to use object pools.)

Well with that many components per object (I’d imagine realistically I’d have something like 10-15) instantiating them (especially bullets) is probably uber inefficient. Thank’s for that example project TonyLi, really helped me out with a direction.

@sam286 I wasn’t asking for super miracles, all I needed to know is the most flexible code structure for the task.

@TonyLi I know about AddComponent(), but as far as I know it doesn’t work with constructors. That’s the only way I know how to use composition, have 3-4 constructors with different stuff in them (I learned about it online, all the examples were 1 script with 2-3 constructors of Employees and Managers n all that,even the vids over at pluralsight.com). So I don’t really see how AddComponent() can be used to initialize only a certain block of code, no biggie though, it seems Unity was designed with simplicity in mind, so having many small scripts should be the way to go instead of 1 fat script with many constructors.
Or have many scripts with interfaces and then have 1 constructor that takes interface types as parameters, and then when creating a new class it can just use that constructor.

Btw, AFAIK SendMessage isn’t very efficient, events my be more worthwhile, you probably know that but I’m just throwing it out there :stuck_out_tongue:

More simple, using Unity GameObjects system.

First add to scene all weapons(with scripts, sounds, etc… included), and attach weapons to player in correct position and rotation, parent weapons to player bones, etc…

After, in player GameObject attach this SmallWeaponSystem script:

using UnityEngine;
 
public class SmallWeaponSystem : MonoBehaviour {
 
    public GameObject[] weapons;
 
    [HideInInspector]
    public int currentWeapon = -1;
 
    int oldWeapon;
 
    void SelectWeapon(int index){
 
        for(int i=0;i<this.weapons.Length;i++){
 
            if(i==index) this.weapons[i].SetActive(true);
            else if(this.weapons[i].activeSelf) this.weapons[i].SetActive(false);
 
        }
 
    }
 
    void Start(){
 
        this.oldWeapon = this.currentWeapon;
        this.SelectWeapon(this.currentWeapon);
 
    }
 
    void Update(){
 
        //Verify switch weapon
        if(this.oldWeapon != this.currentWeapon){
 
            this.oldWeapon = this.currentWeapon;
            this.SelectWeapon(this.currentWeapon);
 
        }
 
    }
 
}

If you need LargeWeaponSystem, and need instantiate/destroy gameobjects, I would use Dependency Inyection class to instantiate/destroy/control a lot of number of weapons:

using UnityEngine;
 
public class LargeWeaponSystem : MonoBehaviour {
 
   public class Dependency{
    
      public virtual void Start(){}
      public virtual void Update(){}
      public virtual void FixedUpdate(){}
      public virtual void LateUpdate(){}
      public virtual void OnCollisionEnter(){}
      public virtual void Destroy(){}
      //etc...
    
   }
 
   [System.Serializable]
   public class FireBold : Dependency{
    
      //Variables
      public float damage;
      public GameObject prefab;
      public Transform parent;
      public Vector3 position;
      public Quaternion rotation;
      public KeyCode fireKey;
      // etc...
    
      public override void Start(){
       
         GameObject obj = (GameObject)Instantiate(this.prefab);
         obj.transform.parent = this.parent;
         obj.transform.localRotation = this.rotation;
         obj.transform.localPosition = this.position;
       
      }
    
      public override void Update(){
       
         //Update Stuff;
       
      }
    
      //etc...
    
   }
 
   [System.Serializable]
   public class StormGust : Dependency{
    
      //Create Dependency, as FireBold
      //Variables
      public float damage;
      public GameObject prefab;
      public Transform parent;
      public Vector3 position;
      public Quaternion rotation;
      public KeyCode fireKey;
      // etc...
    
      public override void Start(){
       
         GameObject obj = (GameObject)Instantiate(this.prefab);
         obj.transform.parent = this.parent;
         obj.transform.localRotation = this.rotation;
         obj.transform.localPosition = this.position;
       
      }
    
      public override void Update(){
       
         //Update Stuff;
       
      }
    
      //etc...
    
   }
 
   /*
   [System.Serializable]
   public class EarthSpike : Dependency{
 
      //Create Dependency, as Firebold
 
   }
   */
 
   [Header("FireBold Skills")]
   [Space(10f)]
   public FireBold fireBold;
   [Header("StormGust Skills")]
   [Space(10f)]
   public StormGust stormGust;
 
 
   Dependency[] weapon;
 
   [HideInInspector]
   public int currentWeapon = 0;
 
   int oldWeapon;
 
 
   void Start(){
    
      this.weapon = new Dependency[]{
       
         new Dependency(),//0.None
         this.fireBold,//1.Firebold
         this.stormGust//2.StormGurst
         //etc...
       
      };
    
      this.oldWeapon = currentWeapon;
    
      this.weapon[this.currentWeapon].Start();
    
   }
 
   void Update(){
    
      //Verify weapon switch
      if(this.oldWeapon != this.currentWeapon){
       
         //Destroy previous weapon
         this.weapon[this.oldWeapon].Destroy();
         //Create new weapon
         this.weapon[this.currentWeapon].Start();
       
         this.oldWeapon = this.currentWeapon;
       
      }
    
      this.weapon[currentWeapon].Update();
    
   }
 
   void FixedUpdate(){ this.weapon[currentWeapon].FixedUpdate(); }
   void LateUpdate(){ this.weapon[currentWeapon].LateUpdate(); }
   //etc...
 
}

If you just want to instantiate / destroy gameobjects and relinquish control to another script you just need to create dependencies to Start and Destroy function.

1 Like

All I can say is: profile. You might be surprised. In practice, the difference between one big script or 15 small ones is negligible. And SendMessage isn’t ideal if you’re sending messages every frame (e.g., 60 times/second), but it’s generally fine otherwise, and certainly in this case. Also, if you use object pools (and you should), you don’t need to instantiate every time you shoot a bullet.

1 Like

I say, as far as possible,split your codes to bunch of components.
For example suppose you need the reload function. You know that all of weapons need to reload
so you write an abstract class “weaponsystem” and an abstract method reload,then you write several classes(SMG,AK47,M16,G3) that implement the reload function.it is cool but suppose that G3 and AK47 have the same reload function but M16 and SMG have the same one. what to do. You need to copy and paste reload codes for G3 and AK47 (and M16 and SMG). It is not bad? surely it is bad and causes problems.
now suppose you write an interface IReloadable and implement it in Reload1 and Reload2 class.
So you can use Reload1 for SMG and M16 class and Reload2 for AK47 and G3 easily.
Component based procedures give you a power you can easily extend your codes and programs.
Use abstract class or common class for inheritence only when you are sure you need them.

1 Like