I'm having an issue where if the player mashes the jump button fast enough, they get an extra jump.
I'm pretty sure it has to do with my implementation of coyote time since it's letting the player jump even when slightly off the ground, but I'm not sure how I could change it to fix this issue without removing the mechanic entirely.
Here's the code I'm using for jumping.
//Check if player is grounded
IsGrounded = Physics2D.OverlapCircle(GroundCheck.position, 0.1f, GroundLayer);
//Coyote Jump
if (IsGrounded == true)
{
HangCount = HangTime;
}
else if(IsGrounded == false)
{
HangCount -= Time.deltaTime;
}
//Jump Buffer
if (Input.GetButtonDown("Jump"))
{
JumpBufferCount = JumpBufferLength;
}
else
{
JumpBufferCount -= Time.deltaTime;
}
//Jump
if (JumpBufferCount >= 0 && HangCount > 0)
{
RB.velocity = new Vector2(RB.velocity.x, JumpHeight);
FindObjectOfType<AudioManager>().Play("Jump");
HangCount = 0;
JumpBufferCount = 0;
}
}
}
Found a solution. I just added a check to see if my Y velocity was equal to or less than 0 so the player couldn't jump while already ascending.
Related
I have added the jumping animation to my player. When my player jumps, the animation plays. However when the player lands, the jump animation becomes stuck. When I check the animator tab, the jump animation is just being played on a loop.
This is the Code for the Jumping stuff - Animation/actual movement:
void Update()
{
horizontal = Input.GetAxisRaw("Horizontal");
animator.SetFloat("Speed", Mathf.Abs(horizontal));
if (Input.GetButtonDown("Jump") && IsGrounded())
{
rb.velocity = new Vector2(rb.velocity.x, jumpingPower);
jump = true;
animator.SetBool("isJumping", true);
}
if (Input.GetButtonUp("Jump") && rb.velocity.y > 0f)
{
rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f);
}
if (Input.GetButtonUp("Jump") && IsGrounded())
{
animator.SetBool("isJumping", false);
animator.SetBool("isGrounded", true);
jump = false;
}
Flip();
}
This should check whether the player is grounded or not:
private bool IsGrounded()
{
return Physics2D.OverlapCircle(groundCheck.position, 0.2f, groundLayer);
}
This statement does not what you think it does
if (Input.GetButtonUp("Jump") && IsGrounded())
GetButtonUp returns true the first frame the button is released (see docs).
But it will return false again if the button has been released. This is not made that clear in the docs. Or to put it in other words: GetButtonUp returns true in that frame where the button has been released but in the frame before it was still pressed.
You could just detect the moment the player lands to switch animations:
if (jump && IsGrounded()) { // player just landed
animator.SetBool("isJumping", false);
animator.SetBool("isGrounded", true);
jump = false;
}
When I'm moving left or right rb.velocity.y changes to weird numbers. How can I get rid of this?
I want to change the gravity when player is jumping and because of this bug it changes even if player is moving left and right.
void FixedUpdate(){
isGrounded = Physics2D.BoxCast(coll.bounds.center,coll.bounds.size, 0, Vector2.down, .1f,WhatIsGround);
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
if(jumped){
rb.AddForce(Vector2.up * jump, ForceMode2D.Impulse);
jumped = false;
}
if(rb.velocity.y < 0){
rb.gravityScale = fallMultiplier;
}
else if(rb.velocity.y > 0 && !(Input.GetKeyDown(KeyCode.Space) || Input.GetKeyDown(KeyCode.UpArrow))){
rb.gravityScale = lowJumpMultiplier;
}
else{
rb.gravityScale = 1f;
}
}
Both floating point calculations and Unity physics aren't precise. When your Rigidbody is moving, the gravity is trying to push it into the ground collider, while collision detection is pushing it upwards. This results in tiny oscillations in velocity.
You should never use direct comparison when working with float values. In this case, you need a small threshold to reliably detect the Y velocity direction:
if(rb.velocity.y < -.1f)
{
rb.gravityScale = fallMultiplier;
}
else if (rb.velocity.y > .1f && !(Input.GetKeyDown(KeyCode.Space) || Input.GetKeyDown(KeyCode.UpArrow)))
{
rb.gravityScale = lowJumpMultiplier;
}
else
{
rb.gravityScale = 1f;
}
I'm trying to make my character be able to jump whenever he is grounded OR if he's airborne to only be able to jump when he has extra jumps and x amount of time has passed.
Currenty I have this:ยด
void Jump()
{
jumpTime -= Time.fixedDeltaTime;
if ((jumpRemember > 0) && ((groundRemember > 0) || ((jumps>0) && (jumpTime <= 0))))
{
jumpRemember = 0;
groundRemember = 0;
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
jumps--;
jumpTime = timeSinceLastJump;
}
}
(the jump remember and ground remember are checks to see if i pressed the jump button or was grounded in the last 0.1 seconds)
but when he is grounded and collides with a roof and get sent back to ground he cant jump after the time has passed anyways, even though I used the ''OR'' operator.
Try using Debug.Log(variable) to check the states of your variables.
"when he is grounded and collides with a roof and get sent back to ground" - What variables does this change?
Are any of these variables being changed unexpectedly when the character collides with the roof?
I would leave this in a comment but you need reputation to unlock that feature of this site.
An example how you could approach your issue.
const int MAX_JUMPS = 2;
const FLOAT JUMP_TIMER = 1;
const float JUMP_FORCE = 100;
int jumps = 2;
float nextJump = 0;
bool grounded = true;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
TryJump();
}
void TryJump()
{
if (grounded || (jumps > 0 && nextJump < Time.time))
{
Jump();
}
}
void Jump()
{
rb.velocity = new Vector2(rb.velocity.x, JUMP_FORCE);
jumps--;
nextJump = Time.time + JUMP_TIMER;
}
void Grounded()
{
grounded = true;
jumps = MAX_JUMPS;
}
void Airborne()
{
grounded = false;
}
void OnCollisionEnter(Collision col)
{
if (col.tag == "Floor")
Grounded();
}
void OnCollisionExit(Collision col)
{
if (col.tag == "Floor")
Airborne();
}
I think you have misplaced parenthesis. You want both of the first two true or both of the second two right?
if ((jumpRemember > 0) && ((groundRemember > 0) || ((jumps>0) && (jumpTime <= 0))))
should it be?
if ((jumpRemember > 0 && groundRemember > 0) || (jumps>0 && jumpTime <= 0))
That second parenthesis in front of groundRemember is causing it to be grouped with the two to the right of the or statement. You are requiring remember all the time and then either ground remember > 0 OR the other two to both be true. It doesn't sound like that is what you are intending. Is it?
I am trying to prevent the player to double jump, for that I am using Raycast to check if the player is in the layer Floor. My solution works sometimes, but sometimes it allows the player to double jump.
This is my code:
void Movement()
{
float movement = Input.GetAxis("Horizontal");
Vector3 playerVelocity = new Vector3(movement*speed, myRigidbody.velocity.y, speed);
myRigidbody.velocity = playerVelocity;
if (Input.GetKeyDown("space") && isGrounded)
{
Vector3 jumpVelocity = new Vector3(0f, jumpSpeed, 0f);
myRigidbody.velocity += jumpVelocity;
isGrounded = false;
Debug.Log("floor:"+isGrounded);
anim.SetInteger("AnimationPar", 3);
myAudioSource.Stop();
AudioSource.PlayClipAtPoint(jumpSound,transform.position, volumeSoundEffects);
}
else if (isGrounded && !myAudioSource.isPlaying && !levelFinished)
{
myAudioSource.Play();
}
if (!isGrounded)
{
IsGrounded();
}
}
void IsGrounded()
{
RaycastHit hit;
int mask = 1 << LayerMask.NameToLayer("Floor");
if (Physics.Raycast(transform.position, Vector3.down, out hit, 0.01f, mask))
{
//Debug.Log("Is touching floor");
isGrounded = true;
Debug.Log("floot:" + isGrounded);
anim.SetInteger("AnimationPar", 1);
if (!myAudioSource.isPlaying)
{
myAudioSource.Play();
}
}
}
The Movement method is called in Update. I think that maybe the IsGrounded method is called to fast and the player is still to close to the floor.
An easy way to prevent double jump is using a counter instead of raycast, it really makes everything easy. put a counter in this portion of the code:
if (Input.GetKeyDown("space") && isGrounded && counter <2)
{
Vector3 jumpVelocity = new Vector3(0f, jumpSpeed, 0f);
counter++;
Im unable to debug your code at this time, but another way you could do it is using OnCollisionEnter and OnCollisionExit
You can detect all things your players collides with, if one of them is the Floor then its likely hes grounded. But it would be possible to touch the floor on the side of the player while falling, to avoid this use the grounded method agian. You said it does work but sometimes fails. So try something like:
void OnCollisionEnter (Collision col)
{
if(col.gameobject.layer == LayerMask.NameToLayer("Floor") && IsGrounded())
{
isGrounded = true;
}
}
void OnCollisionExit (Collision col)
{
if(col.gameobject.layer == LayerMask.NameToLayer("Floor") && !IsGrounded())
{
isGrounded = false;
}
}
Thats untested code so im not 100% sure if it will work. The Exit may not work since IsGrounded may still be able to raycast to the ground. But if you handle your layers/collision in your levels right then you can remove the IsGrounded calls entirely.
This isnt the perfect solution by far but it might work.
I have a small script that causes an object to bounce back and forth. It is a bird for an upwards scrolling endless runner. So it represents its flight path. This script moves it from one end to the other and when it reaches the end it flips the 2D sprite and travels in the opposite direction. It works most of the time. But the problem is that sometimes the image flips twice so now it looks like its flying backwards until it does it again. Each time it does it it appears to be random.
public class Fly : MonoBehaviour {
private bool dirRight = false;
public float speed;
public bool facingRight = false;
void Start (){
speed = Random.Range (15.0f, 22.0f);
}
void Update () {
if(transform.position.x >= 25.0f) {
dirRight = false;
Flip();
}
if(transform.position.x <= -25.0f) {
dirRight = true;
Flip();
}
if (dirRight)
transform.Translate (Vector2.right * speed * Time.deltaTime);
else
transform.Translate (-Vector2.right * speed * Time.deltaTime);
}
void Flip()
{
// Switch the way the player is labelled as facing
facingRight = !facingRight;
// Multiply the player's x local scale by -1
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
I modified the if statements and use my bools as well as the position as such:
if(transform.position.x >= 25.0f && dirRight == true) {
dirRight = false;
Flip();
}
if(transform.position.x <= -25.0f && dirRight == false) {
dirRight = true;
Flip();
}
I am running it now and waiting to see if it works.
you are calling Flip() method based on position. and position is updated per frame. So it takes time to Lerp from >=25 to <25 so in every frame when position is >= 25 or <= -25 it calls to Flip(). So you need to add another check for calling Flip(). May be facingright == true will work.