Im trying to follow Imphenzia's youtube tutorial on Unity3d and i've basically followed his code to a tee (1:28:00 in the following video) and my character continues to jump in the air. Not sure what im missing here and its driving me crazy.
The video - https://www.youtube.com/watch?v=pwZpJzpE2lQ&t=5342s&ab_channel=Imphenzia
the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
private bool jumpKeyWasPressed;
private float horizontalInput;
private Rigidbody rigidBodyComponent;
private bool isGrounded;
// Start is called before the first frame update
void Start()
{
rigidBodyComponent = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
jumpKeyWasPressed = true;
}
horizontalInput = Input.GetAxis("Horizontal");
}
//Fixed update is called once every physics update
private void FixedUpdate()
{
if (isGrounded != true) //if (isGrounded == false) works too
{
return;
}
if (jumpKeyWasPressed)
{
rigidBodyComponent.AddForce(Vector3.up * 5, ForceMode.VelocityChange);
jumpKeyWasPressed = false;
}
rigidBodyComponent.velocity = new Vector3(horizontalInput, rigidBodyComponent.velocity.y, 0);
}
private void OnCollisionEnter(Collision collision)
{
// use ' collision. ' toget^^info using ^^^^^
isGrounded = true;
}
}
I was simply missing the line of code - private void OnCollisionExit. but he answers that later on, and removes the code that was written. my bad!
Related
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.
Hey Guys im Currently working on an 2D Endlessrunner and i need help, i want to only jump once until the character touches the ground again could anyone help me please i would be very thankfull
heres the code for the character :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dino : MonoBehaviour
{
bool isJumping;
Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
isJumping = false;
}
// Update is called once per frame
void Update()
{
if (Input.GetKey("space"))
{
rb.velocity = new Vector3(0, 20, 0);
isJumping = true;
}
}
}
Add the is jumping condition to your jump code like this:
// Update is called once per frame
void Update()
{
if (Input.GetKey("space") && !isJumping)
{
rb.velocity = new Vector3(0, 20, 0);
isJumping = true;
}
}
And handle the isJumping boolean in the collision logic of your character with the ground. You can keep a reference to the floor collider to check if whats dino is colliding with is the floor:
public collider2D floorCol; //attach in unity editor
OnCollisionEnter2D(Collision2D collision) {
//check you are colliding with the floor
if (collision.collider == floorCol)
isJumping = false;
}
assuming 2d for simplicity's sake.
I had my code working perfectly the character was moving side to side and jumping but I tried to get him to stop jumping twice and he just stopped moving entirely. Can you help me figure out what I did wrong? I have looked this question up but yet to find an answer that makes sense to me (I'm relatively new) so if some can work through this with me I would appreciate it greatly so I can get back to work learning.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
bool jumpKeyWasPressed;
float horizontalInput;
Rigidbody rigidBodyComponent;
bool isGrounded;
// Start is called before the first frame update
void Start()
{
rigidBodyComponent = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
//Space key input
if (Input.GetKeyDown(KeyCode.Space))
{
jumpKeyWasPressed = true;
}
horizontalInput = Input.GetAxis("Horizontal");
}
//main problem I think
void FixedUpdate ()
{
if (!isGrounded)
{
return;
}
if (jumpKeyWasPressed)
{
rigidBodyComponent.AddForce(Vector3.up * 5, ForceMode.VelocityChange);
jumpKeyWasPressed = false;
}
rigidBodyComponent.velocity = new Vector3(horizontalInput,rigidBodyComponent.velocity.y, 0);
}
void OnCollisionEnter(Collision collision)
{
isGrounded = true;
}
void OnCollisionExit(Collision collision)
{
isGrounded = false;
}
}
If isGrounded is false, you return right out of the function. The last line is also skipped this way. To fix this, you could simply check if isGrounded is true, and only execute the jumping code then (forget return).
if (isGrounded) {
if (jumpKeyWasPressed) {
// ...
}
}
The jump button is not working in my unity, as my player is not jumping. I followed a tutorial but it does not seem to work. Must be something wrong with my code but I cannot seem to work it out as there are no errors.
I am not sure but, I created an image in unity and placed it in canvas and named it "Fixed Joybutton", with the script Joybutton.cs, but when I run it and try to click it, it wont make my player jump. I thought the problem was the "joybutton = FindObjectOfType();" and changed it to "joybutton = GetComponent();" but still doesnt work.
This is the code for the jump button itself, Joybutton.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
{
[HideInInspector]
public bool Pressed;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void OnPointerDown(PointerEventData eventData)
{
Pressed = true;
}
public void OnPointerUp(PointerEventData eventData)
{
Pressed = false;
}
}
// and this is the code that is in my player, PlayerController.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerController : MonoBehaviour
{
protected Joystick joystick;
protected Joybutton joybutton;
protected bool jump;
public float speed;
public Text playerDisplay;
public Text scoreDisplay;
public bool isFlat = true;
private Rigidbody rb;
public static int count = 0;
private void Start()
{
joystick = FindObjectOfType<Joystick>();
joybutton = FindObjectOfType<Joybutton>();//this is to find the object, I changed it to "joybutton = GetComponent<Joybutton>();" but still doesnt work
rb = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
rb.velocity = new Vector3(joystick.Horizontal * 5f, rb.velocity.y, joystick.Vertical * 5f); // joystick to move my player, its working
if (!jump && joybutton.Pressed)// this is for the jump, that is not working
{
jump = true;
rb.velocity += Vector3.up * 10f;
}
if (jump && !joybutton.Pressed)
{
jump = false;
}
}
public void OnTriggerEnter(Collider other)
{
GameObject gameControl = GameObject.Find("Timer");//name of my gameobject
Loading loading = gameControl.GetComponent<Loading>();
if (other.gameObject.CompareTag("Pick Up"))
{
other.gameObject.SetActive(false);
count = count + 1;
Awake();
}
else if (other.gameObject.CompareTag("Time Boost"))
{
other.gameObject.SetActive(false);
loading.sec += 10;
if (loading.sec > 60)
{
int sec1 = loading.sec - 60;
loading.sec = sec1;
loading.minutes ++;
}
Awake();
}
}
private void Awake()
{
if (DBManager.username == null)
{
UnityEngine.SceneManagement.SceneManager.LoadScene(0);
}
playerDisplay.text = "Player: " + DBManager.username;//display username
scoreDisplay.text = "Score: " + count.ToString();//display score
}
}
//Player is moving but cannot jump. Please show me the error in my code and solution. Thank you so much.
There's a lot to unpack here.
UI Events like OnPointerDown occur on the frame cycle. FixedUpdate happens on the fixed cycle. There is a good chance that you have a very high framerate, resulting in 5-6 frames per fixed cycle. Your joybutton will only be set to "pressed" for one frame, so there is a very high chance that the joybutton triggers OnPointerUp before the jump can occur.
The quickest solution to this problem would be to not "reset" the joybutton's pressed state using OnPointerUp, and instead reset it when the jump is processed in your PlayerController. That being said, can you help me understand why these 2 scripts are separated?
Rather than set the .Pressed state to false in the JoyButton script, reset it after you consume it in your PlayerController:
if (!jump && joybutton.Pressed)// this is for the jump, that is not working
{
jump = true;
joybutton.Pressed = false; //Add this line of code
rb.velocity += Vector3.up * 10f;
}
How do yo do for avoiding the player to jump twice in the air?
private void FixedUpdate()
{
rb.velocity = new Vector3(
joystick.Horizontal * 5f, rb.velocity.y,
joystick.Vertical * 5f);
if (!jump && joybutton.Pressed)
{
jump = true;
rb.velocity += Vector3.up * 10f;
}
if (jump && !joybutton.Pressed)
{
jump = false;
}
}
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 → one (player) Rigidbody is enough
ground objects move → 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