简体   繁体   中英

My character is doing an unexpected 3rd jump in Unity2D

Hello!

I am currently working on a 2D plattformer game in the Unity engine, where I have created a character that can preform a doubblejump.
It all worked pretty good in the beginning, but I have now realized that there is a game breaking bug that makes him able to preform a third jump (for some reason). And I can't for the sake of it figure out what the problem is.

PlayerController.cs

using UnityEngine;
using System.Collections;

public class PlayerController : MonoBehaviour {

    /*
    TODO: Find out why the character ocationally
    gets 3 jumps instead of 2.
    I think it's the "isGrounded" that returns
    a false posetive.
    */

[Header("Ground Recognition")]
public BoxCollider2D groundCollider;
public LayerMask groundAbles;

[Header("Audio")]
public GameObject jumpSound = null;

[Header("Visual")]
public GameObject jumpEffect = null;


float speed = 5f;
int maxJumps = 2;
int currentJumps = 0;

bool isGrounded = false;
float boundsLength = 0;

Vector3 movement;
// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update() {
    CheckGroundCollision();
    JumpLogic();

    movement = new Vector3(Input.GetAxis("Horizontal") * speed, 0, 0);
    movement *= Time.deltaTime;
    transform.position += movement;

    CheckGroundCollision();
}

void CheckGroundCollision()
{
    isGrounded = groundCollider.IsTouchingLayers(groundAbles);
}

void JumpLogic()
{

    if (isGrounded)
        currentJumps = 0;

    if (Input.GetButtonDown("Jump") && currentJumps < maxJumps)
    {
        GameObject newJumpSound = (GameObject)GameObject.Instantiate(jumpSound, (Vector2)transform.position, transform.rotation);
        GameObject newJumpEffect = (GameObject)GameObject.Instantiate(jumpEffect, (Vector2)transform.position - new Vector2(0, 0.25f), transform.rotation);
        GameObject.Destroy(newJumpEffect, 0.2f);
        GetComponent<Rigidbody2D>().velocity = new Vector2(GetComponent<Rigidbody2D>().velocity.x, 0);
        GetComponent<Rigidbody2D>().AddForce(new Vector2(0, 10), ForceMode2D.Impulse);
        currentJumps++;
        CheckGroundCollision();
    }
}
}  

All help is appreciated!

In void JumpLogic() , you're calling

currentJumps++;
CheckGroundCollision();

The very first time you press the jump-key, currentJumps becomes 1 (currentJumps++), but then isGrounded becomes true again (because the physics has not been applied yet in the current frame, and the character is still touching the ground layermask). So the next time JumpLogic() is called, currentJumps is reset to zero because of if (isGrounded) currentJumps = 0; before running the jump-key code.

I suggest removing the last call to CheckGroundCollision(); inside of void JumpLogic() . They are both called inside Update() anyway, in an order that looks like it would work.

I hope that helps!

UPDATE: I just noticed you're also calling CheckGroundCollision(); a second time inside Update() itself. That would be resetting the jump variable too.

Try this modification of your original code:

void Update() {
    CheckGroundCollision();
    JumpLogic();

    movement = new Vector3(Input.GetAxis("Horizontal") * speed, 0, 0);
    movement *= Time.deltaTime;
    transform.position += movement;

    //** NOTE- REMOVED THIS LINE
}

...

void JumpLogic()
{

    if (isGrounded)
        currentJumps = 0;

    if (Input.GetButtonDown("Jump") && currentJumps < maxJumps)
    {
        GameObject newJumpSound = (GameObject)GameObject.Instantiate(jumpSound, (Vector2)transform.position, transform.rotation);
        GameObject newJumpEffect = (GameObject)GameObject.Instantiate(jumpEffect, (Vector2)transform.position - new Vector2(0, 0.25f), transform.rotation);
        GameObject.Destroy(newJumpEffect, 0.2f);
        GetComponent<Rigidbody2D>().velocity = new Vector2(GetComponent<Rigidbody2D>().velocity.x, 0);
        GetComponent<Rigidbody2D>().AddForce(new Vector2(0, 10), ForceMode2D.Impulse);
        currentJumps++;

        //** NOTE- REMOVED THIS LINE
    }
}

I hope that helps!

I would suggest letting the collider dictate to the player controller the state of isGrounded rather than querying the collider. For example, continuing on andeart's suggestions, your code could look more like the following

void Update() {
    JumpLogic();

    movement = new Vector3(Input.GetAxis("Horizontal") * speed, 0, 0);
    movement *= Time.deltaTime;
    transform.position += movement;
}

//let the collider indicate to the player controller when
//a collision occurs, then determine if this collision is relevant
void OnCollisionEnter2D(Collision2D coll) {
    if(coll.IsTouchingLayers(groundAbles))    
      isGrounded = true;

    // or do some other check using layers or tags ....
}

void JumpLogic()
{
    if (isGrounded)
        currentJumps = 0;

    if (Input.GetButtonDown("Jump") && currentJumps < maxJumps)
    {
        GameObject newJumpSound = (GameObject)GameObject.Instantiate(jumpSound, (Vector2)transform.position, transform.rotation);
        GameObject newJumpEffect = (GameObject)GameObject.Instantiate(jumpEffect, (Vector2)transform.position - new Vector2(0, 0.25f), transform.rotation);
        GameObject.Destroy(newJumpEffect, 0.2f);
        GetComponent<Rigidbody2D>().velocity = new Vector2(GetComponent<Rigidbody2D>().velocity.x, 0);
        GetComponent<Rigidbody2D>().AddForce(new Vector2(0, 10), ForceMode2D.Impulse);
        currentJumps++;
    }
}

See Collider2D.IsTouchingLayers particularly the bottom paragraph which implies that the IsTouchingLayers method may not give the most acurate result with respect to the collider.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM