Want more features? Check out the with advanced scriptableobject weapon system with combo chains, weapon switching, damage types, critical hits, and upgrade modifier slots.
Part of these game systems:
intermediate Health & Combat

Weapon System

ScriptableObject-driven weapon system with melee and ranged attacks, cooldowns, ammo, and damage calculation.

Unity 2022.3+ · 3.8 KB · WeaponSystem.cs

How to Use

1

Create weapon ScriptableObjects: Assets > Create > Weapons > Weapon Data

2

Configure melee (range, angle) or ranged (projectile, ammo)

3

Attach WeaponSystem to your player

4

Assign attack point Transform (muzzle or hand)

5

Set initial weapon or call EquipWeapon() at runtime

6

Left-click to attack (configurable)

7

Hook OnAmmoChanged to UI, OnAttack to animation/SFX

Source Code

WeaponSystem.cs
C#
using UnityEngine;
using UnityEngine.Events;

/// <summary>
/// ScriptableObject weapon definition. Create weapons via Assets menu.
/// </summary>
[CreateAssetMenu(menuName = "Weapons/Weapon Data")]
public class WeaponData : ScriptableObject
{
    [Header("Info")]
    public string weaponName = "Weapon";
    public Sprite icon;

    [Header("Type")]
    public bool isMelee = true;

    [Header("Damage")]
    public float damage = 10f;
    public float attackRate = 1f; // attacks per second

    [Header("Melee")]
    public float meleeRange = 2f;
    public float meleeAngle = 90f;
    public LayerMask meleeHitLayers = ~0;

    [Header("Ranged")]
    public GameObject projectilePrefab;
    public float projectileSpeed = 20f;
    public int maxAmmo = 30;
    public int ammoPerShot = 1;
}

/// <summary>
/// Weapon controller. Handles attacking, cooldowns, and ammo.
/// Attach to the player or weapon holder.
/// </summary>
public class WeaponSystem : MonoBehaviour
{
    [Header("Current Weapon")]
    [SerializeField] private WeaponData currentWeapon;

    [Header("References")]
    [SerializeField] private Transform attackPoint;

    [Header("Events")]
    public UnityEvent<WeaponData> OnWeaponEquipped;
    public UnityEvent OnAttack;
    public UnityEvent<int, int> OnAmmoChanged; // current, max
    public UnityEvent OnOutOfAmmo;

    private float cooldownTimer;
    private int currentAmmo;

    /// <summary>Currently equipped weapon data.</summary>
    public WeaponData CurrentWeapon => currentWeapon;

    /// <summary>Current ammo count (ranged only).</summary>
    public int CurrentAmmo => currentAmmo;

    private void Start()
    {
        if (currentWeapon != null)
            EquipWeapon(currentWeapon);
    }

    private void Update()
    {
        if (cooldownTimer > 0f)
            cooldownTimer -= Time.deltaTime;

        if (Input.GetMouseButton(0) && cooldownTimer <= 0f)
        {
            Attack();
        }
    }

    /// <summary>
    /// Equip a weapon by setting its data.
    /// </summary>
    public void EquipWeapon(WeaponData weapon)
    {
        currentWeapon = weapon;

        if (!weapon.isMelee)
        {
            currentAmmo = weapon.maxAmmo;
            OnAmmoChanged?.Invoke(currentAmmo, weapon.maxAmmo);
        }

        OnWeaponEquipped?.Invoke(weapon);
    }

    /// <summary>
    /// Perform an attack with the current weapon.
    /// </summary>
    public void Attack()
    {
        if (currentWeapon == null || cooldownTimer > 0f) return;

        if (currentWeapon.isMelee)
            MeleeAttack();
        else
            RangedAttack();

        cooldownTimer = 1f / currentWeapon.attackRate;
        OnAttack?.Invoke();
    }

    private void MeleeAttack()
    {
        Transform origin = attackPoint != null ? attackPoint : transform;
        Collider[] hits = Physics.OverlapSphere(origin.position, currentWeapon.meleeRange, currentWeapon.meleeHitLayers);

        foreach (var hit in hits)
        {
            if (hit.gameObject == gameObject) continue;

            // Angle check
            Vector3 dirToTarget = (hit.transform.position - origin.position).normalized;
            float angle = Vector3.Angle(origin.forward, dirToTarget);

            if (angle <= currentWeapon.meleeAngle * 0.5f)
            {
                HealthSystem health = hit.GetComponent<HealthSystem>();
                if (health != null)
                    health.TakeDamage(currentWeapon.damage);
            }
        }
    }

    private void RangedAttack()
    {
        if (currentAmmo < currentWeapon.ammoPerShot)
        {
            OnOutOfAmmo?.Invoke();
            return;
        }

        currentAmmo -= currentWeapon.ammoPerShot;
        OnAmmoChanged?.Invoke(currentAmmo, currentWeapon.maxAmmo);

        if (currentWeapon.projectilePrefab != null)
        {
            Transform origin = attackPoint != null ? attackPoint : transform;
            GameObject proj = Instantiate(
                currentWeapon.projectilePrefab,
                origin.position,
                origin.rotation
            );

            Projectile projectile = proj.GetComponent<Projectile>();
            if (projectile != null)
            {
                // Projectile handles its own speed from its own settings
            }
        }
    }

    /// <summary>Add ammo to the current weapon.</summary>
    public void AddAmmo(int amount)
    {
        if (currentWeapon == null || currentWeapon.isMelee) return;
        currentAmmo = Mathf.Min(currentAmmo + amount, currentWeapon.maxAmmo);
        OnAmmoChanged?.Invoke(currentAmmo, currentWeapon.maxAmmo);
    }

    /// <summary>Reload to max ammo.</summary>
    public void Reload()
    {
        if (currentWeapon == null || currentWeapon.isMelee) return;
        currentAmmo = currentWeapon.maxAmmo;
        OnAmmoChanged?.Invoke(currentAmmo, currentWeapon.maxAmmo);
    }

#if UNITY_EDITOR
    private void OnDrawGizmosSelected()
    {
        if (currentWeapon == null || !currentWeapon.isMelee) return;
        Transform origin = attackPoint != null ? attackPoint : transform;
        Gizmos.color = new Color(1f, 0.3f, 0.3f, 0.4f);
        Gizmos.DrawWireSphere(origin.position, currentWeapon.meleeRange);
    }
#endif
}
Ready for more? Weapon System Pro Advanced ScriptableObject weapon system with combo chains, weapon switching, damage types, critical hits, and upgrade modifier slots.