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));