Unity中为武器设置射速 解决协程实现仍每帧发射子弹问题
问题原因
你当前的实现出现逐帧发射的核心原因是:将协程启动逻辑放在逐帧执行的Update中,非常容易出现多协程并发的问题。
虽然你加了allowFire布尔开关,但协程从启动到执行到第一个yield返回前的逻辑虽然是当帧同步执行,但一旦出现脚本重复挂载、协程因物体/脚本失活意外中断、执行顺序异常等情况,就会出现开关状态不同步,多个协程实例同时运行各自生成子弹,最终表现为每帧都发射投射物。
修复方案
给你两种经过验证的稳定实现,按需选择即可:
方案1:时间戳计数(最推荐,逻辑简单无协程并发问题)
直接在Update中用时间戳判断发射间隔,不需要协程,出错概率最低:
public GameObject bulletPrefab; public float bulletSpeed; public float fireRate = 1f; // 记录下一次允许发射的时间点 private float _nextFireTimestamp; void Update() { // 按住上方向键 且 当前时间已达到允许发射的时间点 if (Input.GetKey(KeyCode.UpArrow) && Time.time >= _nextFireTimestamp) { ShootBullet("up"); // 更新下一次发射时间为当前时间+射速间隔 _nextFireTimestamp = Time.time + fireRate; } } void ShootBullet(string direction) { if (direction == "up") { // 原坐标写法可简化为 transform.position + Vector3.forward,效果完全一致 var bulletIns = Instantiate(bulletPrefab, transform.position + Vector3.forward, Quaternion.identity); // 发射瞬时力用Impulse模式更符合子弹物理效果 bulletIns.GetComponent<Rigidbody>().AddForce(Vector3.forward * bulletSpeed, ForceMode.Impulse); } }
方案2:单协程轮询(适合后续要扩展复杂射击逻辑的场景)
不要在Update里反复启动协程,游戏启动时只启动一次射击协程,在协程内部循环检测输入、控制间隔,从根源避免多协程并发:
public GameObject bulletPrefab; public float bulletSpeed; public float fireRate = 1f; void Start() { // 启动一次射击循环协程即可,不用反复启动 StartCoroutine(FireControlLoop()); } IEnumerator FireControlLoop() { while (true) { if (Input.GetKey(KeyCode.UpArrow)) { ShootBullet("up"); // 发射后等待射速间隔,再进入下一轮检测 yield return new WaitForSeconds(fireRate); } else { // 没按射击键就等下一帧再检测 yield return null; } } } void ShootBullet(string direction) { if (direction == "up") { var bulletIns = Instantiate(bulletPrefab, transform.position + Vector3.forward, Quaternion.identity); bulletIns.GetComponent<Rigidbody>().AddForce(Vector3.forward * bulletSpeed, ForceMode.Impulse); } }
额外优化提示
- 不建议用字符串传递方向参数,后续做多方向射击时很容易因为拼写错误出bug,直接传
Vector3类型的方向值更稳妥 - 记得给子弹预制体加碰撞体和自动销毁逻辑,避免场景里残留大量子弹实例造成性能浪费
- 如果需要做暂停/时间缩放相关的功能,
WaitForSeconds和Time.time都会受Time.timeScale影响,如果要做不受缩放影响的射击间隔,替换为WaitForSecondsRealtime和Time.realtimeSinceStartup即可
内容的提问来源于stack exchange,提问作者charlieyin
相关产品推荐
相关产品推荐

