Unity2D preventing character from multi-jumping - c#

My character can jump as much as you press ( W or space) I've read that you can prevent it with Raycast, but I don't understand how to do so. This is my character's code(this is a platformer game) :
private Rigidbody2D myRigidBody2D;
void Start () {
myRigidBody2D = GetComponent<Rigidbody2D>();
}
private void Update()
{
if (Input.GetButtonDown("Jump"))
{
myRigidBody2D.AddForce(new Vector2(0, jumpForce));
}
}

I've read that you can prevent it with Raycast
Yes, you can but you will likely run into problems which can still be fixed.
The best way to do this is to use a boolean variable to check if the character is touching the ground or not. You can set this variable to true or false in the OnCollisionEnter2D and OnCollisionExit2D functions.
Create a tag called "Ground". Change all your ground Gameobjects to this tag then the example below should prevent multiple jumping.
bool isGrounded = true;
private float jumpForce = 2f;
private Rigidbody2D myRigidBody2D;
void Start()
{
myRigidBody2D = GetComponent<Rigidbody2D>();
}
private void Update()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
myRigidBody2D.AddForce(new Vector2(0, jumpForce));
}
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Ground"))
{
isGrounded = true;
}
}
void OnCollisionExit2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Ground"))
{
isGrounded = false;
}
}

Related

Force on horizontal movement on unity 2D character controller is not working correctly

C# Character Controller
I'm new to using unity and I having difficulty with the force being applied to the sprite. The jump vector 2 variable is fine but I'm having issue with the horizontal movement. The code is not detecting if the key is held down,it will only add force to either side if you constantly tap the key, then it will move in a direction.
I'm not sure if I cant use the rigid body for horizontal movement or the vector is not written correctly. If you could please respond with the possible issues and or solution that would be helpful, thanks.
public float JumpForce;
public float HorizontalForce;
bool isGrounded = false;
Rigidbody2D RB;
void Start()
{
RB = GetComponent<Rigidbody2D>();
}
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
if(isGrounded == true)
{
RB.AddForce(Vector2.up * JumpForce);
isGrounded = false;
}
}
if (Input.GetKeyDown(KeyCode.A))
{
RB.AddForce(Vector2.left * HorizontalForce);
}
if (Input.GetKeyDown(KeyCode.D))
{
RB.AddForce(Vector2.right * HorizontalForce);
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("ground")) ;
{
if(isGrounded == false)
{
isGrounded = true;
}
}
}
}
You need GetButton() instead of GetButtonDown().
GetButtonDown is only true for a single frame, in which the button was pressed. GetButton returns true as long as you hold the button

When I press space, my character doesn't always jump in my Unity2D project

https://www.youtube.com/watch?v=gB1F9G0JXOo&t=11865s
I'm watching this tutorial and I did everything the same as the guy in the video did until the "Player Jumping" part, when his character can jump whenever he presses the space button, but for me it doesn't work the same way. Can somebody help or tell me what is the problem?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField]
private float moveForce = 10f;
[SerializeField]
private float jumpForce = 11f;
private float movementX;
private Rigidbody2D myBody;
private SpriteRenderer sr;
private Animator anim;
private string WALK_ANIMATION = "Walk";
private void Awake()
{
myBody = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
sr = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
PlayerMoveKeyboard();
AnimatePlayer();
}
private void FixedUpdate()
{
PlayerJump();
}
void PlayerMoveKeyboard()
{
movementX = Input.GetAxisRaw("Horizontal");
transform.position += new Vector3(movementX, 0f, 0f) * moveForce * Time.deltaTime;
}
void AnimatePlayer()
{
// we are going to the right side
if (movementX > 0)
{
anim.SetBool(WALK_ANIMATION, true);
sr.flipX = false;
}
// we are going to the left side
else if (movementX < 0)
{
anim.SetBool(WALK_ANIMATION, true);
sr.flipX = true;
}
else
{
anim.SetBool(WALK_ANIMATION, false);
}
}
void PlayerJump()
{
if (Input.GetButtonDown("Jump"))
{
myBody.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
}
}
} // class
Try moving your jump function from the FixedUpdate into the normal Update Function.
That should fix it.
Update gets called every frame and thus captures every input while FixedUpdate gets called in a fixed offset to update the physics logic of the engine. Now when you are looking for keyboard input, you want to always do that in Update, never in FixedUpdate because you'll miss input that was given to the player between the fixed update frames.
But since you want to do physics related changes only in FixedUpdate, you want to split the code between the two methods.
bool shouldJump = false;
void Update()
{
if (Input.GetButtonDown("Jump"))
shouldJump = true;
}
void FixedUpdate()
{
if (shouldJump)
{
myBody.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
shouldJump = false;
}
}
I have looked into the code in the video and he does the jump-logic in FixedUpdate. It seems weird to me and this is definitely bad practice. But one of the comments below addresses your issue as well and proposes a similar solution.

How can i get it so i dont have my character infinitely jump?

This is the code i have wrtten so far which makes the player controlled charcter be able to jump contanstantly i only want them to be able to jump when on the ground.
void Update()
{
this.transform.Translate(Input.GetAxis("Horizontal"), 0, 0);
xdirectionMovement = Input.GetAxis("Horizontal") * runspeed; //GetAxisRaw("Horizontal")
if (Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space)) //makes player jump
{
GetComponent<Rigidbody2D>().AddForce(jumpdistance, ForceMode2D.Impulse);
Ensure you only allow jumping when the character is grounded. One approach to check for that is to use a downwards raycast and see if the hit is below a certain threshold:
void Update()
{
print(IsGrounded());
}
bool IsGrounded()
{
const float distanceToGround = 1f;
return Physics.Raycast(
transform.position, -Vector3.up, distanceToGround) != null;
}
Another is to use a CharacterController component and check its bool:
CharacterController controller = null;
void Start()
{
controller = GetComponent<CharacterController>();
}
void Update()
{
print(controller.isGrounded);
}
Another is to listen for a collision event and set a bool:
bool collides = false;
void FixedUpdate()
{
print(collides);
collides = false;
}
void OnCollisionStay(Collision collision)
{
collides = true;
}
Note that sometimes, it's good usability practice to allow a bit of leeway so the user can jump even if they miss the ground by a few pixels, or miss the timing by a few milliseconds. Good luck!
Add a bool to check if player is grounded, add Vectors and Colliders to determine where player is, relative to the ground.
private BoxCollider2D box;
Vector3 maxValue= box.bounds.max;
Vector3 minValue=box.bounds.minValue;
Vector2 x = new Vector2(max.x, minValue.y -0.1f);
Vector2 y = new Vector2(minValue.x, minValue.y - 0.1f);
Collider2D ground = Physics.OverlapArea(x,y);
bool jump = false;
if (ground != null)
{
jump = true;
}
Finally you can add the bool to your Input, for example,
if (Input.GetKeyDown(KeyCode.Space) && jump == true)
{
//player jump
}

OnTriggerEnter2D() seems to not work unity

I am developing a platform game, where a ball should be able to move left and right and to jump. I could make the character jump or move successfully, but when i tried to check if it was on the ground, by creating an element as child of the character, with a trigger collider 2D, and wrote the code using a variable that was supposed to be true when the player was touching the ground, and false when it wasn't, it just did not activate.
Here is the code for the main movement script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Grounded : MonoBehaviour
{
GameObject Player;
// Start is called before the first frame update
void Start()
{
Player = gameObject.transform.parent.gameObject;
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (GetComponent<Collider2D>().tag == "Ground")
{
Player.GetComponent<Move2D>().isGrounded = true;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (GetComponent<Collider2D>().tag == "Ground")
{
Player.GetComponent<Move2D>().isGrounded = true;
}
}
}
And this is the Grounded script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move2D : MonoBehaviour
{
public float moveSpeed = 5f;
public bool isGrounded = false;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Jump();
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, 0f);
transform.position += movement * Time.deltaTime * moveSpeed;
}
void Jump()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector3(0f, 5f), ForceMode2D.Impulse);
}
}
}
Any help or information is really appreciated.
In
GetComponent<Collider2D>().tag == "Ground"
you are checking the tag of this GameObject's (child of Player) collider itself!
You probably rather wanted to check the tag of the thing you collided with.
Also avoid repeated GetComponent calls .. rather do it only once.
(Thanks #Jichael) You should also rather use CompareTag instead of using tag == "XY". It is more efficient and also actually checks if the given compare string exists as tag. If not an error is thrown while using == simply returns false which makes it hard to find evtl. typos.
// Would be better even to already reference this via Inspector
[SerializeField] private Move2D Player;
private void Awake()
{
if(!Player) Player = GetComponentInParent<Move2D>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Ground"))
{
Player.isGrounded = true;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.CompareTag("Ground"))
{
Player.isGrounded = true;
}
}
The same also in Move2D
[SerializeField] private Rigidbody2D rigidbody;
private void Awake()
{
if(!rigidbody) rigidbody = GetComppnent<Rigidbody2D>();
}
void Jump()
{
// This is very small but it is slightly cheaper to check the
// isGrounded value so this order is minimal faster if isGrounded is false
if (isGrounded && Input.GetButtonDown("Jump"))
{
rigidbody.AddForce(new Vector3(0f, 5f), ForceMode2D.Impulse);
}
}
Also note: Whenever a Rigidbody is involved you should not control the position via the Transform component but rather use Rigidbody2D.MovePosition to keep the Physics intact!
This has to be done however in FixedUpdate so your code would become something like
private Vector3 movement;
void Update()
{
Jump();
movement = new Vector3(Input.GetAxis("Horizontal"), 0f, 0f);
}
private void FixedUpdate ()
{
rigidbody.MovePosition(rigidbody.position += movement * Time.deltaTime * moveSpeed);
}
void Jump()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
rigidbody.AddForce(new Vector3(0f, 5f), ForceMode2D.Impulse);
}
}
Whether both objects require a Rigidbody or only the player depends on the ground objects (afaik):
ground objects are static &rightarrow; one (player) Rigidbody is enough
ground objects move &rightarrow; both need a rigidBody (the ground e.g. a kinematic)
Could you check both ball and ground has a rigidbody? which is required to trigger a trigger.
*Note: Trigger events are only sent if one of the Colliders also has a Rigidbody attached. *
Also could you change your code
private void OnTriggerEnter2D(Collider2D collision)
{
if (GetComponent<Collider2D>().tag == "Ground")
{
Player.GetComponent<Move2D>().isGrounded = true;
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "Ground")
{
Player.GetComponent<Move2D>().isGrounded = true;
}
}
Also I also recommend to use "layer" instead of using tag

I want my player to die when he touches multiple objects with different tags

How can I make my player die when he touches two different gameObjects with the different tags. The player should die when he touches "enemy" tag and "ground" tag. Also, I am not trying to use the same enemy tag for ground objects because I'm already using "enemy" tag for my other script.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move2d : MonoBehaviour
{
public float playerSpeed; //allows us to be able to change speed in Unity
public Vector2 jumpHeight;
public bool isDead = false;
private Rigidbody2D rb2d;
private Score gm;
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
gm = GameObject.FindGameObjectWithTag("gameMaster").GetComponent<Score>();
}
void Update()
{
if (isDead) { return; }
transform.Translate(playerSpeed * Time.deltaTime, 0f, 0f); //makes player run
if (Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space)) //makes player jump
{
GetComponent<Rigidbody2D>().AddForce(jumpHeight, ForceMode2D.Impulse);
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("ground")) // this will return true if the collision gameobject has ground tag on it.
{
isDead = true;
rb2d.velocity = Vector2.zero;
GameController.Instance.Die();
}
}
void OnTriggerEnter2D(Collider2D col)
{
if( col.CompareTag("coin"))
{
Destroy(col.gameObject);
gm.score += 1;
}
}
}
I think you may have asked the question wrong, and that you mean the player will die if he hits to ground or an enemy, rather than both at the same time. An easy fix is doing as Nikola suggested with a minor change:
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("ground") || collision.gameObject.CompareTag("enemy"))
{
isDead = true;
rb2d.velocity = Vector2.zero;
GameController.Instance.Die();
}
}
OnCollisionEnter will be individually called once for every object that starts touching your player during that frame.
You will need to create a way to track over multiple call to OnCollisionEnter the group of objects touching your player at that point. One way is to create a boolean for each type of tag you are checking:
private bool touchedGround = false;
private bool touchedEnemy = false;
private void LateUpdate() {
touchedGround = false;
touchedEnemy = false;
}
private void OnCollisionStay2D(Collision2D collision) {
if (collision.gameObject.CompareTag("ground")) {
touchedGround = true;
}
if (collision.gameObject.CompareTag("enemy")) {
touchedEnemy = true;
}
if (touchedGround && touchedEnemy) {
isDead = true;
rb2d.velocity = Vector2.zero;
GameController.Instance.Die();
}
}
At the end of each OnCollisionEnter you are verifying whether or not both tags have been encountered. In LateUpdate you reset the flags to false, so that the next frame you recheck.
As a side note, this solution is not very extensible. You may wish to use layers (LayerMask) and a bit of boolean logic allowing you to compare multiple categories in one operation. But that would be a different question.
Hello why not just do this
private void OnCollisionEnter2D(Collision2D collision)
{
switch (collision.gameobject.tag)
case "tag_here"
isDead = true;
rb2d.velocity = Vector2.zero;
GameController.Instance.Die();
break;
case "tag_here2"
isDead = true;
rb2d.velocity = Vector2.zero;
GameController.Instance.Die();
break;
case "tag_here3"
isDead = true;
rb2d.velocity = Vector2.zero;
GameController.Instance.Die();
break;
//Copy paste as long as you need more
}
switch statements are like shorter version of if statements.
You could use the following approach, it's called when Enemy || Gound Tag enters && exits.
private bool enemy, ground;
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Enemy"))
{
enemy = true;
}
if (collision.gameObject.CompareTag("Ground"))
{
ground = true;
}
if(enemy && ground)
{
Debug.Log("Enemy and Ground collision");
}
}
private void OnCollisionExit2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Enemy"))
{
enemy= false;
}
if (collision.gameObject.CompareTag("Ground"))
{
ground = false;
}
}

Categories

Resources