Unity中首次发射子弹未叠加玩家射击减速速度问题求助
问题根源
首次射击时,Weapon脚本中设置player.moveSpeed = walkSpeed后,玩家Rigidbody2D的velocity还没来得及更新——因为PlayerControls脚本中更新velocity的逻辑在Update中执行,而Unity默认不保证脚本Update的执行顺序。如果PlayerControls的Update先于Weapon执行,那么射击时读取的rb.velocity还是基于baseSpeed的旧值,导致子弹速度未叠加减速后的玩家速度;后续射击时,PlayerControls已经用新的walkSpeed更新了velocity,所以子弹速度正常。
解决方案
方案1:直接计算玩家当前速度(最稳健)
不要通过读取玩家Rigidbody2D的velocity来获取移动速度,而是直接用玩家当前的moveSpeed和moveDirection计算,这样不受脚本执行顺序影响。
修改步骤:
- 在
PlayerControls脚本中添加公开方法,返回当前移动速度:
public Vector2 GetCurrentMovementVelocity() { return moveDirection * moveSpeed; }
- 在
Weapon脚本中,替换获取玩家速度的代码:
原来的:
bullet.GetComponent<Rigidbody2D>().velocity = player.GetComponent<Rigidbody2D>().velocity;
改为:
bullet.GetComponent<Rigidbody2D>().velocity = player.GetCurrentMovementVelocity();
方案2:调整脚本执行顺序
在Unity编辑器中强制让PlayerControls的Update先于Weapon执行:
- 打开
Edit > Project Settings > Script Execution Order - 点击
+号,添加PlayerControls和Weapon脚本 - 将
PlayerControls拖到Weapon上方,确保它先执行
这种方式依赖编辑器设置,改动最小但不如方案1稳健。
方案3:将物理操作移到FixedUpdate(规范物理逻辑)
把PlayerControls中更新velocity的代码移到FixedUpdate(物理相关操作推荐放在FixedUpdate),同时结合方案1使用,彻底避免生命周期问题:
修改PlayerControls脚本中的Update和FixedUpdate:
void Update() { float moveX = Input.GetAxisRaw("Horizontal"); float moveY = Input.GetAxisRaw("Vertical"); moveDirection = new Vector2(moveX, moveY).normalized; mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); // 瞄准逻辑留在Update Vector2 aimDirection = mousePosition - rb.position; float aimAngle = Mathf.Atan2(aimDirection.y, aimDirection.x) * Mathf.Rad2Deg - 90f; rb.rotation = aimAngle; } void FixedUpdate() { // 物理velocity更新放在FixedUpdate rb.velocity = moveDirection * moveSpeed; }
修改后的完整代码示例
PlayerControls.cs
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; void Start() { rb = GetComponent<Rigidbody2D>(); } void Update() { float moveX = Input.GetAxisRaw("Horizontal"); float moveY = Input.GetAxisRaw("Vertical"); moveDirection = new Vector2(moveX, moveY).normalized; mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); Vector2 aimDirection = mousePosition - rb.position; float aimAngle = Mathf.Atan2(aimDirection.y, aimDirection.x) * Mathf.Rad2Deg - 90f; rb.rotation = aimAngle; } void FixedUpdate() { rb.velocity = moveDirection * moveSpeed; } public Vector2 GetCurrentMovementVelocity() { return moveDirection * moveSpeed; } }
Weapon.cs
using UnityEngine; public class Weapon : MonoBehaviour { public GameObject bulletPrefab; public Transform firePoint; public PlayerControls player; public float fireForce = 10f; 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 Start() { bullets = maxAmmo; } void Update() { timeSinceLastShot += Time.deltaTime; if (Input.GetMouseButton(0)) { if (timeSinceLastShot >= fireRate && !noAmmo) { player.moveSpeed = walkSpeed; bullets--; if (bullets <= 0) { noAmmo = true; player.moveSpeed = player.baseSpeed; } GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.identity); bullet.GetComponent<Bullet>().bulletDamage = bulletDamage; bullet.GetComponent<Rigidbody2D>().velocity = player.GetCurrentMovementVelocity(); bullet.GetComponent<Rigidbody2D>().AddForce(transform.up * fireForce, ForceMode2D.Impulse); timeSinceLastShot = 0f; } } else { player.moveSpeed = player.baseSpeed; } } public void FillMag() { bullets = maxAmmo; noAmmo = false; } }
注:Weapon脚本中的bulletPrefab、firePoint、player需在Inspector面板赋值,避免空引用错误。
内容的提问来源于stack exchange,提问作者FishMissile
相关产品推荐
相关产品推荐

