You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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计算,这样不受脚本执行顺序影响。

修改步骤:

  1. 在PlayerControls脚本中添加公开方法,返回当前移动速度:
public Vector2 GetCurrentMovementVelocity()
{
    return moveDirection * moveSpeed;
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 06:35:27