0
\$\begingroup\$

I'm using touch inputs in my game to control the character. I would like to know how to disable my character from jumping again in the air.

if (FingerTouchy.phase == TouchPhase.Ended)
                {
       charcter.transform.Translate(Vector2.up * speed * Time.deltaTime);
       float jumpForce = 250;
                        charcter.GetComponent<Rigidbody2D>().AddForce(new Vector2(0, jumpForce));

                    }

Update

Im using Collision Detection

void OnCollisionEnter (Collision col)
    {
        if(col.gameObject.tag == "Ground")
        {
            grounded = true;

        }
    }

And checking if charcter is grounded

if(FingerTouchy.phase == TouchPhase.Ended)
            {

                    float jumpForce=250;
                    if (grounded)
                        continue;
                    charcter.transform.Translate(Vector2.up * speed * Time.deltaTime);
                    charcter.GetComponent<Rigidbody2D>().AddForce(new Vector2(0,jumpForce));
                        grounded=false;

                }

using collision/collision2d doesn't work too.

\$\endgroup\$

6 Answers 6

3
\$\begingroup\$

I've handling ground status with raycast.

I declare a global grounded variable and use it to check if my character can jump.

In the fixed update of my character controller, i call my groundedUpdater().

void GroundedUpdater ()
{
    grounded = false; //Set to false every frame by default
    RaycastHit2D[] hit;
    hit = Physics2D.RaycastAll (transform.position, Vector2.down, 0.6f);
    // you can increase RaycastLength and adjust direction for your case
    foreach (var hited  in hit) {
        if (hited.collider.gameObject == gameObject) //Ignore my character
            continue;
        // Don't forget to add tag to your ground
        if (hited.collider.gameObject.tag == "Ground") { //Change it to match ground tag
            grounded = true;
        }
    }
}

And where i handle inputs i do this

if (Input.GetKeyDown (KeyCode.UpArrow)) {
        if (grounded) { // can jump
            rig.AddForce (Vector3.up * jumpPower, ForceMode2D.Impulse);
            grounded = false; //Avoid direct double jump
        }
    }
\$\endgroup\$
5
  • 2
    \$\begingroup\$ While the code presented here is the one that I feel best suits the asker, there are some minor changes we can make for better efficiency. We can drop the if(hited.collider.gameobject = gameobject) statement entirely if we assign a layer mask to our ground objects that is used only for things we can stand on, and then raycast against only that layer. \$\endgroup\$ Commented Dec 21, 2015 at 16:33
  • \$\begingroup\$ @Draco18s did it work for you? \$\endgroup\$
    – user75648
    Commented Dec 21, 2015 at 17:18
  • \$\begingroup\$ @user75648 eh? I didn't pull any of these answers into Unity myself, I was just reading them. I happen to use the Standard Assets character controller which handles this on its own (also, I work in 3D, not 2D, so I do not know if a similar standard asset exists). \$\endgroup\$ Commented Dec 21, 2015 at 17:25
  • \$\begingroup\$ grounded bool is returning false \$\endgroup\$
    – user75648
    Commented Dec 21, 2015 at 17:36
  • \$\begingroup\$ @user75648 try to increase raycast length or change raycast vector angle in my case it's for 2D platform game. You can use ondrawgizmos to debug the raycast \$\endgroup\$
    – Sata
    Commented Dec 21, 2015 at 17:50
0
\$\begingroup\$

Checking the game object's velocity in y axis.

if (FingerTouchy.phase == TouchPhase.Ended && charcter.GetComponent<Rigidbody2D>().velocity.y==0)
                {
       charcter.transform.Translate(Vector2.up * speed * Time.deltaTime);
       float jumpForce = 250;
    charcter.GetComponent<Rigidbody2D>().AddForce(new Vector2(0, jumpForce));

                    }

This just works fine.

Let me know if this has any drawbacks.

\$\endgroup\$
1
  • \$\begingroup\$ This will not work 100% accurate, as if ground has some bumpiness, or even not, moving rigidbody having Y velocity exactly zero is difficult any how. Second thing, during jump and falling of character, at one point Y velocity would must pass through 0, which is a glitch. This speech is only for information sharing. Hope you understand, at least little bit. :) \$\endgroup\$ Commented Dec 19, 2015 at 14:48
0
\$\begingroup\$
if (FingerTouchy.phase == TouchPhase.Ended)
    {
        float jumpForce = 250;
        if (playerIsGrounded)
            charcter.transform.Translate(Vector2.up * speed * Time.deltaTime);
            charcter.GetComponent<Rigidbody2D>().AddForce(new Vector2(0, jumpForce));

    }

In the grounded check code :

void OnTriggerEnter(coll Collider)
{
    if (coll.tag == "Ground")
        playerIsGrounded = true;
}

void OnTriggerLeave(coll Collider)
{
    if(coll.tag == "Ground")
        playerIsGrounded = false;
}

Haven't used unity in a while but I think it's something like this?

\$\endgroup\$
8
  • \$\begingroup\$ @user75648 When the OnTriggerEnter(coll Collider) is called it will set the variable to true (since the player is on the ground) and the force (jump) is only called when this variable is set to true. And since its set to false when the player stops colliding with the ground, the player won't jump when he is in the air. \$\endgroup\$
    – BiiX
    Commented Dec 21, 2015 at 16:04
  • \$\begingroup\$ on checking playerIsGrounded=true , I'm still able to jump more than once and on playerIsGrounded=false , the AddForce line is ignored. \$\endgroup\$
    – user75648
    Commented Dec 21, 2015 at 16:55
  • \$\begingroup\$ @user75648 I edited the answer, maybe this will work? \$\endgroup\$
    – BiiX
    Commented Dec 21, 2015 at 17:01
  • \$\begingroup\$ playerIsGrounded is now returning false and I'm still able to jump more than once. \$\endgroup\$
    – user75648
    Commented Dec 21, 2015 at 17:06
  • 1
    \$\begingroup\$ This method won't work well in general. Imagine you walk from one platform onto another adjacent one. At first, your trigger touches the left platform, and playerIsGrounded is set to true, Then your trigger touches the right platform, and playerIsGrounded... is already true, so nothing changes. Lastly, your trigger exits the left platform, which sets playerIsGrounded to false - even though you're still touching the right platform! Instead you want a playerGroundContactsCount integer that you increment/decrement. Also, use CompareTag not == with tags. \$\endgroup\$
    – DMGregory
    Commented Aug 20, 2019 at 11:52
0
\$\begingroup\$

To do that, make a script name Ground. Attach Rigidbody2D to Ground and mark it as Kinematic. Assuming that your character has "Player" tag. Now time to use some delegate stuff.

A simple definition of delegate that delegate is use to notify other class on an event, even if that other class don't have any instance of first class. In other words, delegate does broadcast a message, then all other classes whichever wants to receive that broadcast, just set a listener in it.

For example, class A has an specific event. class B and class C wants to get notify when that specific even occurs in class A without having an instance of class A.

So similarly you can use delegate keeping a good structure of code.

using UnityEngine;
using System.Collections;

public class Ground : MonoBehaviour
{
    public delegate void OnCharacterLandedDelegate();
    public static event OnCharacterLandedDelegate OnCharacterLandedListener;
    // Use this for initialization
    void Start ()
    {

    }

    // Update is called once per frame
    void Update ()
    {

    }

    void OnCollisionEnter2D(Collision2D col)
    {
        if (col.gameObject.tag == "Player")
        {
            if (OnCharacterLandedListener != null)
                OnCharacterLandedListener();
        }
    }
}

Now in the script where you are handling touch. Add these lines of code.

bool _shouldJump = true;
void OnEnable()
{
    Ground.OnCharacterLandedListener += EnableJump;
}

void OnDisable()
{
    Ground.OnCharacterLandedListener -= EnableJump;
}

void EnableJump()
{
    _shouldJump = true;
}

Somewhere in your code,

if(FingerTouchy.phase == TouchPhase.Ended)
{
    //charcter.transform.Translate(Vector2.up * speed * Time.deltaTime);
    Jump();
}

void Jump()
{
    if (!_shouldJump)
        return;
    _shouldJump = false;
    float jumpForce=250;
    charcter.GetComponent<Rigidbody2D>().AddForce(new Vector2(0,jumpForce));
}
\$\endgroup\$
9
  • \$\begingroup\$ Made some edit, have a look \$\endgroup\$ Commented Dec 18, 2015 at 15:12
  • 2
    \$\begingroup\$ Using delegates seems like overkill to me in this situation, since you call it with a single if-check in Update. you could just call EnableJump directly, or even you could just do _shouldJump = true; \$\endgroup\$ Commented Dec 18, 2015 at 16:53
  • \$\begingroup\$ Update is my last resort \$\endgroup\$ Commented Dec 18, 2015 at 17:48
  • \$\begingroup\$ In a more complex code, this structure would definetely be the first choice of me; but for short scripts, I try to keep code as simple as possible to not to get confused while reading it later. But it is a matter of preferance, actually. \$\endgroup\$ Commented Dec 18, 2015 at 17:52
  • \$\begingroup\$ Actually even in this scenario how you can achieve 100% accuracy using Update? You will get glitch somehow. Second thing I avoid using update as much as possible \$\endgroup\$ Commented Dec 18, 2015 at 18:16
-1
\$\begingroup\$

The easiest way is to use the Boolean variable. Make it true if the character on the ground else make it false. If it is true then only add the force to the character.

Ex:

First check the player is on the ground.

This code snippet goes on ground GameObject.

void OnCollisionEnter2D(Collision2D col)
    {
        if (col.gameObject.tag == "Player")
        {
            if (OnCharacterLandedListener != null)
                OnCharacterLandedListener();
               new PlayerScript().canJump=true;// if player on the ground
        }
    }

Your input action

This code snippet goes to the Player gameobject.

   [HideInspector]   //Don't show  this field in the inspector.
    public bool canJump=false;  

        if(FingerTouchy.phase == TouchPhase.Ended)
        {
            //charcter.transform.Translate(Vector2.up * speed * Time.deltaTime);
            if(canJump)
                Jump();
        }

        void Jump()
        {
            if (!_shouldJump)
                return;
             _shouldJump = false;
             float jumpForce=250;
             charcter.GetComponent<Rigidbody2D>().AddForce(new Vector2(0,jumpForce));
             canJump=false; // if not on the ground
        }
\$\endgroup\$
3
  • \$\begingroup\$ Please understand the needs, code given by asker and then give your input. Your answer completely confuse the person who asked. He is a beginner. According to your answer, you are enabling in OnCollisionEnter which is attached to the ground, and with the delegate call and accessing that flag in another script that is controlling the character. MAN!!! Gimme a break. You are creating flag here and accessing it there and even didn't understand the implemented functionality. Well, this speech is just for information sharing, nothing personal., hope you understand. \$\endgroup\$ Commented Dec 19, 2015 at 14:57
  • \$\begingroup\$ @HamzaHasan Sorry for that mistake. I hope now it's alright \$\endgroup\$ Commented Dec 21, 2015 at 6:23
  • 1
    \$\begingroup\$ Again, you are creating new instance of PlayerScript having no relation with the one attached to Player GameObject :) \$\endgroup\$ Commented Dec 21, 2015 at 7:02
-2
\$\begingroup\$

Since I mis understood, I would use a part of @HamzaHasan 's answer, but I would not use AddForce.

I advice to not use Addforce to let a character jump/move because this is not ment for character movement, this is more or less used for explosion that give a "force" to your body, or space ships floating movement. I would advice using transform.Translate(Vector3.up * speed * Time.deltaTime, Space.World);. Where vector.3 defines the direction this could be;

  • Vector3.left
  • Vector3.right
  • Vector3.up
  • Vector3.down
\$\endgroup\$
4
  • 1
    \$\begingroup\$ The asker wants to prevent double jumping. That is, if the character has already jumped (or is falling) he doesn't want to let the player jump again. \$\endgroup\$ Commented Dec 18, 2015 at 14:57
  • \$\begingroup\$ Ah, I misread there, i read "How do I prevent my player from jumping in the air". OP could have written "how to prevent player from double jump" \$\endgroup\$
    – Dakpan
    Commented Dec 18, 2015 at 15:00
  • \$\begingroup\$ @JpHouten why would i use Vector3? \$\endgroup\$
    – user75648
    Commented Dec 18, 2015 at 15:05
  • \$\begingroup\$ @user75648 you can also use Vector2, it depends on your game, if you have a 2D game you should use Vector2. \$\endgroup\$
    – Dakpan
    Commented Dec 18, 2015 at 15:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .