"isGrounded" 在 Unity 中初始跳转后变量不会更改为 false

"isGrounded" variable doesn't change to false after initial jump in Unity

概览

使用 Unity2D 2019.3.5,我正在使用 C# 制作平台游戏。我实施了光线投射来检测我的玩家何时接触地面并尝试仅让玩家只能跳跃一次。

问题

虽然我以为我的角色被编程为跳跃一次,但在第一次跳跃之后,Unity 引擎仍然对我的 "isGrounded" 变量显示一个复选标记,并且在击中之前第二次跳跃后才变为 false(未选中)地面。

我的代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player_Controller : MonoBehaviour
{

    public int playerSpeed = 10;
    public int playerJumpPower = 1250;
    private float moveX;
    public bool isGrounded;
    public float distanceToBottomOfPlayer = .7f;

    // Update is called once per frame
    void Update()
    {
        PlayerMove();
        PlayerRaycast();
    }

    void PlayerMove()
    {
        // CONTROLS
        moveX = Input.GetAxis("Horizontal");
        if (Input.GetButtonDown("Jump") && isGrounded == true)
        {
            Jump();
        }

        // ANIMATIONS

        // PLAYER DIRECTION
        if (moveX < 0.0f)
        {
            GetComponent<SpriteRenderer>().flipX = true;
        }
        else if (moveX > 0.0f)
        {
            GetComponent<SpriteRenderer>().flipX = false;
        }

        // PHYSICS
        gameObject.GetComponent<Rigidbody2D>().velocity = new Vector2(moveX * playerSpeed, 
gameObject.GetComponent<Rigidbody2D>().velocity.y);
    }

    void Jump()
    {
        GetComponent<Rigidbody2D>().AddForce(Vector2.up * playerJumpPower);
        isGrounded = false;
    }

    void PlayerRaycast()
    {
        // Ray Down
        RaycastHit2D rayDown = Physics2D.Raycast(transform.position, Vector2.down);

        if (rayDown.collider != null && rayDown.distance < distanceToBottomOfPlayer && 
rayDown.collider.tag == "ground")
        {
            isGrounded = true;
        }
    }
}

额外信息

我确实必须在“编辑”>“项目设置”>“物理 2D”>“在碰撞器中开始查询”中更改 Unity 设置。我必须关闭此设置(取消选中)才能让我的播放器使用我上面编写的代码进行跳跃。我知道还有其他方法可以让我的播放器跳跃,但是,这似乎是最有效的,同时保持了代码的可读性。

尝试过的解决方案

我认为问题是我有一个我不知道如何解决的光线投射问题。我查看了其他 Stack Overflow post,包括在写这篇 post 后推荐的那些,但其中 none 适用于我的问题。

最后的笔记

正如我之前所说,我知道还有其他方法可以使用不同的代码让我的播放器只跳一次,但是,我想坚持使用此代码以供我自己的学习目的和将来参考。

因为当你调用Jump().
时你不能确定isGroundedfalse 我认为问题出在这里。
调用 Jump() 时尽量不要设置 isGrounded 标志。设置 isGrounded 纯粹是 PlayerRaycast() 的工作。

    void Update()
    {
        // Raycast before moving
        PlayerRaycast();
        PlayerMove();
    }

     void PlayerRaycast()
    {
        // Ray Down
        RaycastHit2D rayDown = Physics2D.Raycast(transform.position, Vector2.down);

        if (rayDown.collider != null && rayDown.collider.tag == "ground")
        {
            if( rayDown.distance < distanceToBottomOfPlayer )
            {
                isGrounded = true;
            }
            else
            {
                isGrounded = false;
            }
        }
    }

    void Jump()
    {
        GetComponent<Rigidbody2D>().AddForce(Vector2.up * playerJumpPower);
        //isGrounded = false;
    }

第一个问题是你在同一帧添加力和检查地面。

Applied Force is calculated in FixedUpdate or by explicitly calling the Physics.Simulate method.

因此,在您按下 "Jump" 按钮后,该对象仍在地面上,直到下一帧到来。

要解决此问题,您只需交换 "move" 和 "raycast"

的顺序即可
void Update()
{
    PlayerRaycast();
    PlayerMove();
}

第二个问题,如果跳跃力不够大,下一帧物体还能贴近地面,在跳跃上升状态时应避免检查着陆。

void PlayerRaycast()
{
    if(GetComponent<Rigidbody2D>().velocity.y > 0)
        return;
    ...
}