Search Unity

An impulse AddForce directed upwards (as a jump) triggers OnCollisionStay2D. (It shouldn't.)

Discussion in 'Scripting' started by Semetre, Feb 25, 2018.

  1. Semetre

    Semetre

    Joined:
    Feb 18, 2018
    Posts:
    5
    The entire code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class PlayerController : MonoBehaviour {
    6.     Rigidbody2D rb;
    7.     public float velMultiplier = 10f;
    8.     private bool onGround = false;
    9.  
    10.     // Use this for initialization
    11.     void Start () {
    12.         rb = GetComponent<Rigidbody2D>();
    13.     }
    14.    
    15.     void FixedUpdate() {
    16.         if (Mathf.Abs(Input.GetAxis("Horizontal")) >= 0.1f) {
    17.             Vector2 velVector = rb.velocity;
    18.             velVector.x = Input.GetAxis("Horizontal") * velMultiplier;
    19.             rb.velocity = velVector;
    20.         }
    21.  
    22.         // the (likely) relevant part:
    23.         if (Input.GetKeyDown(KeyCode.Space) && onGround) {
    24.             rb.AddForce(Vector2.up * 10f, ForceMode2D.Impulse);
    25.             onGround = false;
    26.             Debug.Log(onGround);
    27.         }
    28.  
    29.         if (Input.GetKeyDown(KeyCode.S)) {
    30.             rb.AddForce(Vector2.down * 15f, ForceMode2D.Impulse);
    31.         }
    32.     }
    33.      
    34.     void OnCollisionStay2D() {
    35.         onGround = true;
    36.         Debug.Log(onGround);
    37.     }
    38. }
    The problem:
    The player can jump twice.

    Brief explanation of the code:
    When Space is pressed, the following code makes the square that is the player character jump:
    Code (CSharp):
    1. rb.AddForce(Vector2.up * 10f, ForceMode2D.Impulse); // where rb is a reference to the GameObject's Rigidbody2D
    In order to prevent multiple jumps, I'm using onCollisionStay2D to check whether the square is touching a platform. If it isn't, then pressing Space shouldn't do anything.

    Likely root of the problem:
    I'm guessing the problem lies in the fact that AddForce doesn't instantly move the square upwards and the physics engine still counts the two colliders touching each other for an instant.

    I set onGround to be printed whenever onCollisionStay2D executes or Space is pressed, and here are the last few results:
    upload_2018-2-25_13-11-59.png


    Other notes:
    I'm very new to Unity, so pardon the crude code. I remember that Raycasting should be used in this case, whatever that is, but I thought I'd build a rudimentary 2D character controller myself to learn the basics first.

    Should I simply change AddForce to something else, or is there a way to fix this as is? I considered a time delay, but it seems very inelegant and fragile.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
  3. Semetre

    Semetre

    Joined:
    Feb 18, 2018
    Posts:
    5
    I'm not sure what that's supposed to tell me. Simply Debug.Logging Collision2D coll gives me a Collision2D object (duh), and Logging its various fields (collider, otherCollider, gameObject, relativeVelocity) only confirm that the relevant GameObjects are indeed the player and the platform serving as the ground its standing on.

    One interesting find may be that change in relativeVelocity is only Logged once:
    upload_2018-2-26_10-28-41.png
    However, I'm not sure what to take from that; does it mean that my hypothesis about the cause of the problem is incorrect?
     
  4. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    I personally prefer using a short raycast to determine if a character is grounded. I played with collision/trigger enter/exit/stay for a while, but ultimately found the raycast to be superior (at least for me).
    One example could be, you might jump sideways (or up), hit something, and then be "grounded" according to trigger/collision.

    One other note, you should move your input detection for jump to the Update() method, store the variable "jump" and execute it in FixedUpdate, at which point you change the variable to 'false' (ie: used). FixedUpdate could run more than once with the value of 1 key down.. also, a key down could be skipped.
    You may find that this change alone will solve your issue.
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    Print its .name field, which will tell you what GameObject it is on. There could be an invisible one you don't know about with a Collider2D on it. If you have multiple objects named "foo" then rename them foo1, foo2, etc. until you nail down precisely who is hitting you when you don't expect it.

    ALSO, check the actual size of the Collider2D: is it perhaps bigger than you expect?