如何使用CharacterController跳转到一个点?

4
我有一个跳跃脚本,但它根据JumpFactor常量的值来跳跃。我想使用JumpToPositionX(float target)方法进行跳跃。我认为我需要根据JumpToPositionX方法中目标值的大小计算JumpFactor值。我在维基百科上找到了一篇文章,但我甚至不知道从哪里开始。我没有找到关于如何在CharacterController中实现这一点的示例。
public class ExampleClass : MonoBehaviour
{
    const float Speed = 6f;
    const float JumpFactor = 14f;
    const float Gravity = 20f;
    
    private Vector3 moveDirection = Vector3.zero;

    CharacterController controller;

    void Start()
    {
        controller = GetComponent<CharacterController>();
    }

    void Update()
    {
        if (controller.isGrounded)
        {
            moveDirection = new Vector3(1f, 0f, 0f); // Move X Direction
            moveDirection *= Speed;
            if (Input.GetButton("Jump"))
                moveDirection.y = JumpFactor;

        }
        moveDirection.y -= Gravity * Time.deltaTime;
        controller.Move(moveDirection * Time.deltaTime);
    }

    // Method for jump, calling from other script.
    void JumpToPositionX(float target)
    {
    }
}
1个回答

7

理论

我假设你想使用弹道物理学。

让我们看看可以使用哪些公式:

speedFormula

positionFormula

其中:

  • V : 速度
  • a : 加速度
  • P : 位置
  • t : 起始时间
  • 0 : 初始值

已知是 (0f,-9.81f) 或者在你的情况下是 (0f,重力)

也已知,是跳跃前的初始位置。

是目标结束位置。

解决方案

你需要找到结束时间 和初始速度 。所以这里是位置方程:

我们可以简化这个过程,只考虑x轴,因为没有加速度。

简化为:

让我们把x位置公式中的孤立出来:

现在你有了跳跃的总时间。你只需使用y位置公式找到初始y速度

将 Vyo 隔离出来:

现在你拥有计算跳跃所需的所有信息:初始和结束位置,总时间,初始速度和加速度。

实现

这里是你可以创建的 MonoBehaviour 的示例:

public class JumpBehaviour : MonoBehaviour
{

    public CharacterController CharacterController;

    public Transform TargetPosition;

    private bool jumpStarted = false;
    private float totalTime = 0f;
    private float t = 0f;
    private Vector2 V0;

    private Vector2 initialPosition;

    private bool jumpKeyDown;

    private const float xSpeed = 6f;
    private const float gravity = -9.81f;

    void Update()
    {
        jumpKeyDown = Input.GetKeyDown(KeyCode.Space);
    }

    void FixedUpdate()
    {
        if (jumpStarted)
        {
            UpdateJumpPosition(Time.fixedDeltaTime);
        }
        else if (jumpKeyDown )
        {
            StartJump();
        }
    }

    private void UpdateJumpPosition(float deltaTime)
    {
        t += deltaTime;

        if(t > totalTime)
        {
            //End of jump
            transform.position = TargetPosition.position;
            jumpStarted = false;
            return;
        }

        Vector2 newPosition = new Vector2(0f, gravity) * t * t + V0 * t + initialPosition;

        CharacterController.Move(newPosition);
    }

    private void StartJump()
    {
        jumpStarted = true;

        initialPosition = transform.position;

        // Absolute because xSpeed could be positive or negative. We dont want negative time
        float delta = TargetPosition.position.x - transform.position.x;
        totalTime = Mathf.Abs(delta / xSpeed);
        t = 0f;

        float yInitialSpeed = (TargetPosition.position.y - transform.position.y - gravity * totalTime * totalTime) / totalTime;

        // Using Sign to set the direction of horizontal movement
        V0 = new Vector2(xSpeed * Mathf.Sign(delta), yInitialSpeed);
    }
}

这是计算得出2D跳跃和移动的初始值的示例。

如评论中derHugo所说,不应直接使用transform.position与物理效果相结合。

对于3D,计算方法类似,但是需要根据目标方向和速度大小计算出x、z方向上的速度。

以下是结果:unity result

结论

Vector3 delta = TargetPosition - transform.position;
delta.y = 0f;
float direction = delta.normalized;
Vector3 initialSpeed = direction * movementSpeed;

1
是的。这只是一个简单的例子,如果没有物理参与的话。否则,他/她应该适应gameObject如何移动,但至少他/她有公式可以计算随时间变化的位置和初始速度。 - D.B
1
@derHugo,我编辑了以添加更多信息。你认为这样更好吗? - D.B
由于问题直接涉及到物理引擎和CharacterController,我认为它非常相关 ;) - derHugo
1
你说得对。我可能会添加一个使用CharacterController的示例。 - D.B
1
@D.B 注意,你仍然应该在 Update 中获取用户输入(GetKeyDown),因为不能保证在 FixedUpdate 调用中它仍然有效 ;) 另外,我仍然会通过 var xyz = GetComponent<Rigidbody>().position 获取位置,并通过 GetComponent<Rigidbody>().MovePosition(xyz); 设置位置,以避免破坏物理效果 ;) 当然,你可以像往常一样缓存 Rigidbody 引用。 - derHugo
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接