【unity c#】通过getkeydown及时获取键盘输入并反馈给unity物理系统

一般我们在控制玩家移动时,会选择使用getAxis来获取玩家在指定轴向上的输入。
对于一个需要读取玩家输入,并改变角色的控制类代码,我们会选择将其放入到update中使其逐帧执行。
但有时候,我们会需要使用到unity的物理系统来实现角色间的实体碰撞交互,此时物理系统相关的代码需要放入到fixdupdate中。

比如如下代码,一个简单的控制玩家移动的脚本。

public class PlayerBehavior : MonoBehaviour
{
    
    
    public float moveSpeed = 1.0f;
    public float rotateSpeed = 60f;
    public float jumpVelocity = 5.0f;

    private float vInput;
    private float hInput;

    private Rigidbody _rb;
    // Start is called before the first frame update
    void Start()
    {
    
    
        _rb = GetComponent<Rigidbody>();
    }

    // Update is called once per frame
    void Update()
    {
    
    
        vInput = Input.GetAxis("Vertical") * moveSpeed;
        hInput = Input.GetAxis("Horizontal" ) * rotateSpeed;
    }

    void FixedUpdate()
    {
    
    
        Vector3 rotation = Vector3.up * hInput;

        Quaternion angleRot = Quaternion.Euler(rotation * Time.fixedDeltaTime);

        _rb.MovePosition(this.transform.position + this.transform.forward * vInput * Time.fixedDeltaTime);
        _rb.MoveRotation(_rb.rotation * angleRot);
    }
}

在这里插入图片描述
此时,若我们想加入一个按space跳跃的功能,我们第一反应就会使用getkeydown(KeyCode.Space)去检查space是否有被按下。
由于我们的物理系统相关代码,只能写入到FixedUpdate中,如果我们仍通过对物体施加力的方式来实现跳跃的话,就需要要把执行逻辑写入到FixedUpdate中:给物体施加一个向上的瞬时力。

void FixedUpdate()
{
    
    
    if (Input.GetKeyDown(KeyCode.Space))
    {
    
    
        _rb.AddForce(Vector3.up * jumpVelocity, ForceMode.Impulse);
    }

    Vector3 rotation = Vector3.up * hInput;

    Quaternion angleRot = Quaternion.Euler(rotation * Time.fixedDeltaTime);

    _rb.MovePosition(this.transform.position + this.transform.forward * vInput * Time.fixedDeltaTime);
    _rb.MoveRotation(_rb.rotation * angleRot);
}

我们很快就会发现,由于FixedUpdate的执行和帧率的计算是相对独立的,所以会出现获取键盘输入不及时的情况,表现上,则为经常读取不到space输入导致角色跳不起来的情况。

下图这里,即便是我使用了洪荒之力疯狂欧拉连击空格,这个小胶囊顽固的跟杀手皇后的小车一样纹丝不动。最后这个胶囊也只是傲娇地一跳,对我进行狠狠地嘲讽。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

好小子,那既然放到FixedUpdate里面行不通,毕竟物理系统的帧计算独立于游戏帧。我们直接把Input.GetKeyDown(KeyCode.Space)扔到Update里面,这下总能读取到玩家输入了吧。

我们的逻辑非常简单:
新增一个private变量JInput ,用来控制AddForce里的起跳变量,在Update读取space的输入,有按下时矢量标签JInput 为起跳速度,没按下时JInput 为0。避免只按了一下,角色就原地升天的情况。
在这里插入图片描述
代码如下:
为了测试下能否读到空格输入,我们这里加一行打印输出。

void Update()
{
    
    
    vInput = Input.GetAxis("Vertical") * moveSpeed;
    hInput = Input.GetAxis("Horizontal" ) * rotateSpeed;
    if (Input.GetKeyDown(KeyCode.Space))
    {
    
    
        JInput = jumpVelocity;
        Debug.Log("jump jump jump!");
    }
    else
    {
    
    
        JInput = 0f;
    }
    
}

void FixedUpdate()
{
    
    
    _rb.AddForce(Vector3.up * JInput, ForceMode.Impulse);

    Vector3 rotation = Vector3.up * hInput;

    Quaternion angleRot = Quaternion.Euler(rotation * Time.fixedDeltaTime);

    _rb.MovePosition(this.transform.position + this.transform.forward * vInput * Time.fixedDeltaTime);
    _rb.MoveRotation(_rb.rotation * angleRot);
}

在这里插入图片描述
空格是读到了,但是你小子,倒是动一下啊!你对得起旁边奋力旋转的胶囊吗!

在这里插入图片描述
这里其实是因为Update和FixedUpdate之间的计算是相互独立的,就是说,游戏帧和物理系统的计算不具备真正时间上的先后关系

当我们在Update中把JInput 置为1之后,由于FixedUpdate往往未能及时跟上完成JInput 的处理,下一次Update又把JInput 置为0了,导致物理系统迟迟读不到空格按下的信息。

那我们在两个相对独立的线程中,要如何进行信息的正确共享呢?这就是加锁的基本思路。

即我们在Update中更新信息后,要确保FixedUpdate对其处理完后,才能进行重置,再让Update进行下一次的更新。

实现的代码如下:

void Update()
{
    
    
    vInput = Input.GetAxis("Vertical") * moveSpeed;
    hInput = Input.GetAxis("Horizontal" ) * rotateSpeed;
    if (Input.GetKeyDown(KeyCode.Space))
    {
    
    
        JInput = jumpVelocity;
        Debug.Log("jump jump jump!");
    }
    
}

void FixedUpdate()
{
    
    
    _rb.AddForce(Vector3.up * JInput, ForceMode.Impulse);
    JInput = 0f;

    Vector3 rotation = Vector3.up * hInput;

    Quaternion angleRot = Quaternion.Euler(rotation * Time.fixedDeltaTime);

    _rb.MovePosition(this.transform.position + this.transform.forward * vInput * Time.fixedDeltaTime);
    _rb.MoveRotation(_rb.rotation * angleRot);
}

在这里插入图片描述
可算是把你这妖猴给降服了。
在这里插入图片描述

可是我转念一想,凭什么这个GetAxis就能够正确地读到键盘输入给FixedUpdate呢?

我们查找unity doc的描述,发现其中有那么一句话:

GetAxis获取的输入是独立于游戏帧计算的,即其可以无需等到下一个游戏帧的到来,就可以获取输入反馈给物理系统。

在这里插入图片描述
而GetKeyDown则显然是帧相关的输入获取方式,可以看到连unity doc都推荐使用GetAxis来处理输入,麻烦GetKeyDown您尽快退群罢。

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/misaka12807/article/details/131905898