Unity - AddForce() 同时使用 MovePosition() 进行移动

Unity - AddForce() while using MovePosition() for movement

我正在开发自上而下的游戏,但卡住了。 我试图让玩家在敌人击中他时被击退(他们的刚体碰撞)。

现在的问题是我正在使用 MovePosition 进行玩家移动,然后当我使用任何会改变玩家 RB 速度的东西时(设置 velocity/AddForce()),应用 force/velocity只是一瞬间。我认为这是因为我使用了以某种方式忽略或重置速度的 MovePosition。

有什么方法可以解决这个问题,而不必让玩家控制基于以最大速度添加力或计算击退持续多长时间?

很快,我希望击退顺利,因为现在我必须添加一个非常大的力才能有非常快(基本上是瞬间)的移动。

尝试重写它,让物理引擎为您处理一切。 您可以尝试使用 AddForce 移动对象,而在 "knockback" 状态下,您可以使用 AddForceForce.Impulse 作为参数。它应该按预期工作,并且会解决您肩膀的运动问题。

以防万一其他人偶然发现:从技术上讲,您可以通过将 MovePosition(pos) 替换为 transform.position = pos.

来完成 TE 最初想要的操作

这将保持 inertia\velocity 等,并且可能是您在非常特殊的情况下所需要的(在我的例子中,创建了一种自定义类型的非弹性铰链接头,其中强制位置变化非常小)。

此解决方案的主要缺点是,当位置转变为固体碰撞时,物理引擎可能会做出意想不到的事情。引擎仍将尝试解决这种情况并将对象推出碰撞,但它不知道它来自哪个方向(与 MovePosition 不同),可能会向错误的方向移动,从而让对象穿过地形。这就是为什么通常不建议在应用刚体时触摸 'transform'。

唯一对我有用的是设置一个 knockedOut 临时标志来决定何时停止调用 RigidBody.MovePosition() 暂时给 RigidBody.AddForce() 一些时间来采取行动。

然后使用 forceValueRigidBody.massRigidBody.linearDrag 来获得想要的效果。

这是我的 PlayerController 的实现:

using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    [SerializeField] float speed = 1f;
    [SerializeField] float impactEffect = 5f;
    [SerializeField] float knockOutTime = 0.05f;

    Vector2 direction = Vector2.zero;
    Rigidbody2D rbody;
    bool knockedOut;
    IEnumerator knockOutCoroutine;

    void Awake()
    {
        rbody = GetComponent<Rigidbody2D>();
    }

    void FixedUpdate()
    {
        if(!knockedOut) // Don't call Move() if I am knockedOut
            Move();
    }

    // Triggered by the InputSystem
    void OnMove(InputValue value)
    {
        // Set the direction value
        direction = value.Get<Vector2>();
    }

    // Controls the normal movement of the Player based on the actual `direction` value
    void Move()
    {
        Vector2 adjustedMovement = direction * speed * Time.fixedDeltaTime;
        Vector2 newPos = rbody.position + adjustedMovement;
        rbody.MovePosition(newPos);
    }

    // Invoke this method when Collision
    public void Impact(Vector2 impactPosition)
    {
        Vector2 impactDirection = ((Vector2)transform.position - impactPosition).normalized;
        rbody.velocity = Vector2.zero;
        rbody.AddForce(impactDirection * impactEffect, ForceMode2D.Impulse);
        KnockOut(knockOutTime);
    }

    void KnockOut(float seconds)
    {
        if(knockOutCoroutine != null)
            StopCoroutine(knockOutCoroutine);

        knockOutCoroutine = KnockOutCoroutine(seconds);
        StartCoroutine(knockOutCoroutine);
    }

    // KnockedOut stop movement effect
    IEnumerator KnockOutCoroutine(float seconds)
    {
        direction = Vector2.zero;
        knockedOut = true;
        yield return new WaitForSeconds(seconds);
        knockedOut = false;
    }
}