Home > front end >  Handling automatic weapons in Unity 2D
Handling automatic weapons in Unity 2D

Time:12-09

I am working on simple 2D rouge-like game with weapons, currently my weapon script only handles the shooting of weapon once a left mouse button is clicked. What I am looking for is how to make that shooting happen at some interval. So when a user clicks mouse0 the bullet would shoot but there will be a delay before they can shoot again, as well as if the mouse0 is held then the weapon would shoot at specified firing rate.

Code for my controller:

using UnityEngine;

/* 
 * This class will be attached to every gun to monitor it's actions on key presses.
 * WeaponScript will be called and will be attached to each weapon as well depending on it's type with
 * stats. WeaponScript will manage how a weapon behaves instead of this script, this is just a mediator
 * so I don't have to write this code in abstract class.
 */
public class WeaponPickUpController : MonoBehaviour
{
    //public variables
    public float pickUpRange; // range at which a gun will be avaialable to pick up, will probably be constant in the end
    public GameObject player = null; // stores player object for it's rb component
    public Weapon weaponScript = null;
    //protected variables

    //private variables
    private readonly KeyCode _dropKey = KeyCode.Q; // todo: replace to read from config some day
    private readonly KeyCode _pickUpKey = KeyCode.E; // todo: replace to read from config some day
    private readonly KeyCode _shootKey = KeyCode.Mouse0; // todo: replace to read from config some day
    [SerializeField] private bool _equiped = false; // todo: remove serialize field

    // Start is called before the first frame update
    void Start()
    {
        // todo: init variables safely here, also perhaps set variable on save file
    }

    // Update is called once per frame
    void Update()
    {
        var distanceFromPlayer = Vector2.Distance(player.transform.position, this.transform.position);
        //Debug.Log(distanceFromPlayer);
        // short circuit this if gun is equiped
        if (!_equiped && distanceFromPlayer < pickUpRange && Input.GetKeyDown(_pickUpKey)) // todo: a range check here
        {
            PickUpWeapon();
        }

        //short circuit this if it is not equiped
        if (_equiped && Input.GetKeyDown(_dropKey))
        {
            DropWeapon();
        }

        // short circuit if not equiped
        if (_equiped && Input.GetKeyDown(_shootKey))
        {
            weaponScript.Shoot();
        }
    }

    private void PickUpWeapon()
    {
        _equiped = true;
        weaponScript.GetEquiped();
    }

    private void DropWeapon()
    {
        _equiped = false;
        weaponScript.GetUnequiped();
    }
}

Code for Weapon:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Weapon : MonoBehaviour
{
    public float damage = 10f;
    public float projectileSpeed = 1f;
    public float damageBonus = 0f;

    [SerializeField] private Rigidbody2D _rb = null;
    [SerializeField] private GameObject _player = null;
    [SerializeField] private Transform _firePoint = null;
    [SerializeField] private GameObject _projectile = null;
    //Only called once
    private void Start()
    {
        // load form config
    }

    // todo: make abstract since this is a superclass
    public void Shoot()
    {
        GameObject projectile = Instantiate(_projectile, _firePoint.position, _firePoint.rotation); // instantiate projectile and make it a child of weapon
        Rigidbody2D projectile_rb = projectile.transform.GetComponent<Rigidbody2D>();

        //projectile_rb.AddForce(_firePoint.up * projectileSpeed, ForceMode2D.Impulse);
        projectile_rb.velocity = _firePoint.up * projectileSpeed;
    }

    public void GetEquiped()
    {
        Transform playerTransform = _player.transform;
        float playerLocalOffset = _player.GetComponent<BoxCollider2D>().size.x;

        _rb.isKinematic = true; // makes this move with the player
        transform.rotation = playerTransform.rotation;
        transform.SetParent(playerTransform);
        transform.localPosition = new Vector3(-playerLocalOffset, 0, playerTransform.position.z);
    }

    public void GetUnequiped()
    {
        _rb.isKinematic = false; // makes it dynamic agian
        transform.SetParent(null);
        transform.position = new Vector3(transform.position.x, transform.position.y, 2);
    }
}

CodePudding user response:

Well in your weapon you could have a cooldown like e.g.

public class Weapon : MonoBehaviour
{
    ...

    // How much time has to pass between two bullets fired?
    [SerializeField] private float bulletDelay = 0.1f;

    // A flag indicating whether this weapon can currently shoot
    private bool canShoot = true;

    public void Shoot()
    {
        // while the flag is false do noting, you can't shoot now
        if(!canShoot) return;

        // ... Your shoot stuff here

        // set the flag because you just shot
        canShoot = false;
        // Invoke the CooldownFinished method after bulletDelay has passed
        Invoke(nameof(CooldownFinished,bulletDelay));
    }

    private void CooldownFinished()
    {
        canShoot = true;
    }
}

If you rather want to provide a "rate" then simply invert

[SerializeField] private float bulletsPerSecond = 10f;

and then

Invoke(nameof(CooldownFinished, 1f / bulletsPerSecond));
  • Related