I'm trying to make a 2D top-down shooter in Unity. I want it so when the you hold down the left mouse button the player fires a series of bullets until you run out of ammo. The player's movement speed is slowed while firing and the players movement speed should be added to the bullet's movement speed. For some reason the players movement speed is only applied to the bullets AFTER the first bullet is fired. The first bullet always seems to keep the slightly faster 'sprint' movement speed.
Weapon script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weapon : MonoBehaviour
{
private GameObject bulletPrefab;
private Transform firePoint;
private PlayerControls player;
public float fireForce = 10f;
private bool cooldown = false;
private int bullets;
public int bulletDamage;
public int maxAmmo;
public float fireRate = 0.5f;
public float reloadRate = 2.5f;
public bool noAmmo = false;
public float walkSpeed = 2f;
private float timeSinceLastShot = 0f;
void Update()
{
// increase time since last shot
timeSinceLastShot = Time.deltaTime;
// if left-click is held down
if (Input.GetMouseButton(0))
{
// if enough time has passed since last shot
if (timeSinceLastShot >= fireRate && noAmmo == false)
{
player.moveSpeed = walkSpeed;
bullets -= 1;
cooldown = true;
if (bullets <= 0)
{
noAmmo = true;
player.moveSpeed = player.baseSpeed;
}
// instantiate a bullet
GameObject bullet = Instantiate(bulletPrefab, firePoint.transform.position, Quaternion.identity);
bullet.GetComponent<Bullet>().bulletDamage = bulletDamage;
// add player movement speed to bullet's speed
bullet.GetComponent<Rigidbody2D>().velocity = player.GetComponent<Rigidbody2D>().velocity;
bullet.GetComponent<Rigidbody2D>().AddForce(transform.up * fireForce, ForceMode2D.Impulse);
// reset time since last shot
timeSinceLastShot = 0f;
}
}
// if left-click is not held down
else
{
cooldown = false;
// restore player movement speed
player.moveSpeed = player.baseSpeed;
}
}
public void FillMag()
{
bullets = maxAmmo;
noAmmo = false;
}
}
PlayerControls:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControls : MonoBehaviour
{
public float moveSpeed = 5f;
public float baseSpeed = 5f;
public int health;
public Weapon weapon;
private Rigidbody2D rb;
Vector2 mousePosition;
Vector2 moveDirection;
public float walkSpeed = 2f;
void Update()
{
float moveX = Input.GetAxisRaw("Horizontal");
float moveY = Input.GetAxisRaw("Vertical");
moveDirection = new Vector2(moveX, moveY).normalized;
mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
rb.velocity = new Vector2(moveDirection.x * moveSpeed, moveDirection.y * moveSpeed);
Vector2 aimDirection = mousePosition - rb.position;
float aimAngle = Mathf.Atan2(aimDirection.y, aimDirection.x) * Mathf.Rad2Deg - 90f;
rb.rotation = aimAngle;
}
}
The player is moving from left to right. Player firing
According to Unity's lifecycle, inputs are only calculated just before the Update method is called, but physics are applied during the FixedUpdate method. Is this what is causing my problems? I've tried moving some calculations into FixedUpdate and LateUpdate but nothing seems to make any difference.
Any help is appreciated. I've been banging my head against this for a few days now. I'm an amature, so feel free to explain like I'm 5.
CodePudding user response:
So there are 2 problems with your code:
- if you are not holding down left mouse, but multiple click instead:
- the else statement in the Update() will immediately set it player back to base speed, which is not really matter if you intent to holding your mouse.
- There are 2 concurrent things happen
- first, you set player moveSpeed in Weapon Update()
- at the same time, you calculate player velocity in Player Update() But it TAKES TIME for player to update velocity before you get it right to your weapon.
Therefore, I recommend you to use IEnumerator to delay the fire action.
public class Weapon : MonoBehaviour
{
void FixedUpdate()
{
// increase time since last shot
timeSinceLastShot = Time.deltaTime;
// if left-click is held down
if (Input.GetMouseButton(0))
{
// if enough time has passed since last shot
if (timeSinceLastShot >= fireRate && noAmmo == false)
{
//player.moveSpeed = walkSpeed;
StopAllCoroutines();
StartCoroutine(SetSpeed());
bullets -= 1;
cooldown = true;
if (bullets <= 0)
{
noAmmo = true;
player.moveSpeed = player.baseSpeed;
}
// instantiate a bullet
StartCoroutine(FireBulelt());
// reset time since last shot
timeSinceLastShot = 0f;
}
}
// if left-click is not held down
}
IEnumerator SetSpeed()
{
player.moveSpeed = walkSpeed;
yield return new WaitForSeconds(0.5f);
cooldown = false;
// restore player movement speed
player.moveSpeed = player.baseSpeed;
yield return null;
}
IEnumerator FireBulelt()
{
yield return new WaitForSeconds(0.05f);
GameObject bullet = Instantiate(bulletPrefab, player.transform.position, Quaternion.identity);
print(" player.GetComponent<Rigidbody2D>().velocit " player.GetComponent<Rigidbody2D>().velocity);
// add player movement speed to bullet's speed
Rigidbody2D bulletRB = bullet.GetComponent<Rigidbody2D>();
print(" bulletRB.velocit " bulletRB.velocity);
bulletRB.velocity = player.GetComponent<Rigidbody2D>().velocity;
print(" after bulletRB.velocit " bulletRB.velocity);
bulletRB.AddForce(transform.up * fireForce, ForceMode2D.Impulse);
}
}
And you should add print() like I did to keep track of code behavior when you're debugging.