Double Jump turns into Triple Jump sometimes - unity3d

I'm using a counter to implement double jump, but it only works sometimes. The character is able to triple jump from time to time.
{
// Start is called before the first frame update
public Transform grounded;
public LayerMask playerMask;
public Rigidbody rb;
bool isGrounded;
float distToGround;
int counter;
void Start()
{
}
// Update is called once per frame
void Update()
{
if(Physics.OverlapSphere(grounded.position, 0.2f, playerMask).Length > 0)
{
isGrounded = true;
counter = 0;
}
else
{
isGrounded = false;
}
if(Input.GetButtonDown("Jump") && counter < 2)
{
counter++;
rb.AddForce(0, 1000, 0);
}
}
private void FixedUpdate()
{
if (!isGrounded)
{
rb.AddForce(0, -50, 0);
}
}
}
The Player Mask is set to everything except for the character itself.
Thank you for your time and help!

Problem solved. I think it was the problem of FixedUpdate() and Update(). I tried to move the ground check and jump code to FixedUpdate(), and Update() only checks for if the space button is pressed. So I initialized a bool variable jumped in Updated(), whenever space is pressed, jumped is set to true. And in FixedUpdate(), before applying force to the character, check if jumped is true. I am new to Unity and I'm not sure why this happens, I'm guessing it's because Update() and FixedUpdate() run on different speed, so they need to have an extra condition to align them.
Thanks for all the helps!

Related

Jump Animation Not Displaying in Unity 2D

I'm trying to "animate" (yes, I'm using the animate capabilities of Unity but I'm only changing the sprite from one still frame to another still frame) my character jumping in Unity. According to my code, everything should be running properly but for some reason, nothing happens in-game when I hit the Jump button (player is jumping but the sprite isn't changing). I'm following along with this tutorial and I've tried several others explaining how to use Unity's Animator and Animation windows but nothing seems to be working to change the sprite.
My code:
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 float movementY;
private Rigidbody2D myBody;
private SpriteRenderer sr;
private Animator anim;
private string WALK_ANIMATION = "Walk";
private bool isGrounded = true;
private string GROUND_TAG = "Ground";
private string JUMP_ANIMATION = "Jump";
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();
PlayerJump();
}
void FixedUpdate()
{
}
void PlayerMoveKeyboard()
{
movementX = Input.GetAxisRaw("Horizontal");
movementY = Input.GetAxisRaw("Vertical");
transform.position += new Vector3(movementX, 0f, 0f) * Time.deltaTime * moveForce;
}
void AnimatePlayer()
{
if (movementX > 0)
{
anim.SetBool(WALK_ANIMATION, true);
sr.flipX = false;
}
else if (movementX < 0)
{
anim.SetBool(WALK_ANIMATION, true);
sr.flipX = true;
}
else
{
anim.SetBool(WALK_ANIMATION, false);
}
}
void PlayerJump()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
isGrounded = false;
myBody.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
anim.SetBool(JUMP_ANIMATION, true);
Debug.Log("You should be jumping.");
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.CompareTag(GROUND_TAG))
{
isGrounded = true;
anim.SetBool(JUMP_ANIMATION, false);
}
}
}
My animator states currently:
I'm not sure if this is enough information or if I've included screenshots of everything so please feel free to ask for more input.
I have tried:
Changing the Transitions from (currently) Walking to Jumping to (previously) Idle to Jumping.
Removing the Walking animation entirely from the code
Removing the Transitions from Walking to Idle so that there were only transitions from Walking to Jumping
Checking "Has Exit Time" and unchecking "Has Exit Time"
Extending the length of the Jumping animation
Changing the speed of the jump from 1 to .5 and from 1 to 2
Using an if/else statement in the Animate Player function where the bool of JUMPING_ANIMATION only becomes true if the Y value of the player is higher than the base position when they're standing on the ground
(Trying to; not sure if I coded this correctly because I'm shaky on using triggers instead of bools) Use a trigger instead of a bool for initializing the jump animation
I'm absolutely positive I've just missed something in the tutorial or something else fairly simple and dumb but I cannot for the life of me figure out what it is I'm missing. I've searched the other jump animation issues here on SO, too, and none of them seem to be quite what I'm missing. As it stands, my code is telling me that the Jump parameter is becoming true properly based on the Console Log but nothing about the visual sprite changes for the character.
As I can see in your Animator, you need to set your "StateMachine Default State" to one of your used states (I suppose for your case, the Idle State).
You can do that with a right click on the Entry State.

Input.GetKeyDown is won't work properly Unity 2D

I'm trying to make my square jump in Unity 2D when I press Space button. I have the following code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
public float moveSpeed;
public float jumpSpeed;
float moveX;
Vector2 pos;
// Update is called once per frame
void Update()
{
MoveHorizontally();
Jump();
}
void MoveHorizontally(){
moveX = Input.GetAxis("Horizontal") * Time.deltaTime;
pos.x = moveX * moveSpeed;
transform.position = new Vector2(transform.position.x + pos.x,transform.position.y + pos.y);
}
void Jump(){
if (Input.GetKeyDown("space")){
GetComponent<Rigidbody2D>().AddForce(new Vector2(0,jumpSpeed * Time.deltaTime), ForceMode2D.Impulse);
Debug.Log("Jumped!");
}
}
}
When I press Space button, "Jumped!" message shows up, but my square not jumping. Any idea?
Thanks a lot.
I tried using Input.GetKey function. It works, but that function keeps making my square go upwards if i keep holding Space button.
U need to assing a value to jumpforce. and of course to the moveforce. if u dont wanna jump repeatly Just name a bool for checking if u are on ground. I use mine as" private bool = isGrounded".
and try the code below. It makes ur jump a condition as a function of touching ground.
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Ground"))
isGrounded = true;
}
and here is my code for jump.
public void PlayerJump() {
if (Input.GetButtonDown("Jump") && isGrounded) {
isGrounded = false;
myBody.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
}
}
whenever u jump it makes your bool variant false and whenever u touch ground it make ur bool true.
Make sure "Ground" must be same exactly the tag your ground has.
When you call AddForce() method you don't have to multiply with "Time.deltaTime".
What you can do is the following:
First cached your "Rigidbody2D" component from your Start() method because if you call it every frame it will affect your game performance.
private Rigidbody2D rigidbody2D;
void Start()
{
rigidbody2D = GetComponent<Rigidbody2D>();
}
Second, instead of Update() method you should call the FixedUpdate() method which is recommended when you deal with Physics
void FixedUpdate()
{
MoveHorizontally();
Jump();
}
Third, on your Jump() method as I said on top you should not multiply the jump force with Time.deltaTime, instead do the following:
public float jumpForce = 10f;
public void Jump()
{
if (Input.GetKeyDown(KeyCode.Space))
{
rigidbody2D.AddForce(Vector2.up*jumpForce);
}
}
You can adjust the jumpForce as you want.
Also Vector2.up is a shorthand for writing Vector2(0, 1)
You can read more about Vector2 here Vector2.up Unity's Documentation

Why is this Projectile Script Continuing to Hit Targets After I've Destroyed It?

I am new to Unity2D. I am trying to make castle defense game. When the spawners start to inheritance the enemies overlap (they should), but when the archer arrow collides whit the enemies it kills them all. I searched everywhere for the answer of this but nothing...
My questions is:
Is there a way to only hit one target at time?
Arrow script:
void Start()
{
target = GameObject.FindGameObjectWithTag("Enemy").GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
transform.position = Vector2.MoveTowards(transform.position, target.position, speedProjectile * Time.deltaTime);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("Enemy"))
{
Destroy(gameObject);
}
}
Enemy script:
void Start()
{
target = GameObject.FindGameObjectWithTag("target3").GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
}
private void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.CompareTag("arrow"))
{
EHealth -= HDamage;
}
if (EHealth <= 0)
{
Destroy(gameObject);
}
Your enemies are all taking damage because destroying a GameObject isn't something that happens immediately (for good reason). Because of this, in a single frame, any number of enemies can be hit by the same arrow.
If you'd like to rely on these collision methods, I'd suggest controlling the damage from the arrow instead of from the enemy, so you can be sure it only hits something once:
private bool dealtDamage = false;
private void OnTriggerEnter2D(Collider2D col) {
// Only deal damage once
if (dealtDamage) {
return;
}
// Does the thing I hit have this "EnemyScript" ?
var enemy = col.gameObject.GetComponentInChildren<EnemyScript>();
if (enemy == null) {
return;
}
// Make this "DealDamage()" method public on your EnemyScript
enemy.DealDamage();
dealtDamage = true;
Destroy(gameObject);
}
Then get rid of the Enemy Script's OnTriggerEnter2D because the arrow is handling it all. I don't know what the name of the script is on your enemies so I just called it EnemyScript. This is also calling a DealDamage() method that you'd have to make (which would probably look a lot like the one you current have listed in your "Enemy Script.")
its hard to say with the information given, based on what you have said it seems that all the entity's are just 1 entity(so when you kill 1 enemy you kill the only enemy which is all of them). you can have them run independently from each other.
Maybe you can use collision enter function to check bullet is hitting enemy's body.
You can use this code below...
private bool isEntered = false;
void OnCollisionEnter(Collision collision)
{
if(isEntered) return;
if(collision.gameObject.tag == "enemy") isEntered = true;
....
....
}
I hope it will work for you.
Finally figured it out. First of all the script is dealing damage so I should hit the target specific number of times, so I can't stop the collision of the arrow without turning it back on. So I made a method, that is being Invoked (InvokeRepeating()) after the arrow collides, that turns a bool back to false. Which method (function) of course should be in Update();
I am not sure if I am saying it right bc I am a beginner but I hope this helps somebody. :D
And here is the code:
Arrow Script:
private void DealDamage()
{
if (hit == true)
return;
eHealth.EHealth -= hDamage.HDamage;
hit = true;
}
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("enemy"))
{
DealDamage();
Destroy(gameObject); //Destroying arrow on collision
}
}
private void HitSetter()
{
hit = false;
}
void Update()
{
InvokeRepeating("HitSetter", 0f, 1.1f);
}
Enemy Script:
void Update()
{
if (EHealth <= 0)
{
anim.SetBool("EnemyDie", true);
Destroy(gameObject, 0.833f);
}
Also thanks to #Foggzie

is there a way to check where my player can only Jump once

I have a platformer game in unity 2D where my I'm trying to make my character have a slight hangtime delay falling down whenever players reached the edge of a platform they will have a small amount of time to jump. However if jump button is press twice quickly the character makes a double jump, character only needs to jump once if not falling on the edge of the platform.
Screenshot scene
Here is what I have done so far for my jumping mechanics:
float Hangtime;
float HangCounter = .3f;
void Update () {
JumpRequest();
}
void FixedUpdate() {
HangtimeImplents();
Jump();
isGrounded = Physics2D.OverlapCircle(feet.position, groundradius, groundlayers);
}
// For Jump --------------
void HangtimeImplents() {
if (isGrounded) // checks if grounded is true then hangtime is always .3f
{
Hangtime = HangCounter;
}
else
{
Hangtime -= Time.deltaTime;
}
}
public void JumpRequest() {
if (CrossPlatformInputManager.GetButtonDown("Jump") && Hangtime > 0f) // checks if Jump Button is pressed and Hangtime is greated than 0
{
isGrounded = false;
Jumprequest = true;
}
}
void Jump() // Jump Function if Jumprequest is true
{
if (Jumprequest)
{
mybody.AddForce(new Vector2(0, jumpforce) , ForceMode2D.Impulse);
}
Jumprequest = false;
}
There are many solutions, but the easiest ways is updating the code like this:
if (CrossPlatformInputManager.GetButtonDown("Jump") && Hangtime == HangCounter) // checks if Jump Button is pressed and Hangtime is greated than 0
{
isGrounded = false;
Jumprequest = true;
}
On your code, Time.deltaTime is less than the frame completion time.
So even isGrounded is false, Hangtime is still greater than 0.
Make an operator called jumpTimes
public int maximizeJumpTimes;
private int jumpTimes
When the player jump, Minus the jumpTimes; and if the jumpTimes == 0 make the player can't jump.
Hope this help :>

Unity Jumping Issue - Character won't jump if it walks to the platform

(This is a 2D project)
I have a jumping issue where if my character WALKS into an X platform, she won't jump, but when she JUMPS ONTO the X platform, she can perform a jump.
For the platforms I am currently using 2 Box Collider 2Ds (one with "is trigger" checked)
For the character I am currently using 2 Box Collider 2Ds (one with "is trigger" checked) and Rigidbody 2D.
Below is the code for jumping and grounded I am currently trying to use.
{
public float Speed;
public float Jump;
bool grounded = false;
void Start()
{
}
void Update()
{
if (Input.GetKeyDown(KeyCode.UpArrow))
{
if (grounded)
{
GetComponent<Rigidbody2D>().velocity = new Vector2(GetComponent<Rigidbody2D>().velocity.x, Jump);
}
}
}
void OnTriggerEnter2D()
{
grounded = true;
}
void OnTriggerExit2D()
{
grounded = false;
}
}
Issue arises on the same part of every platform. (Each square represents a single platform sprite and they have all the same exact characteristics, since I copy pasted each one of them). Please check the photo on this link: https://imgur.com/a/vTmHw
It happens because your squares have seperate colliders. Imagine this:
There are two blocks : A and B. You are standing on block A. Now you try to walk on block B. As soon as your Rigidbody2D collider touches block B, your character gets an event OnTriggerEnter2D(...). Now you claim, that you are grounded.
However, at this moment you are still colliding with block A. As soon as your Rigidbody2D no longer collides with block A, your character receives OnTriggerExit2D(...). Now you claim, that you are no longer grounded. But in fact, you are still colliding with block B.
Solution
Instead of having bool variable for checking if grounded, you could have byte type variable, called collisionsCounter:
Once you enter a trigger - increase the counter.
Once you exit a trigger - decrease the counter.
Do some checking to make sure you are actually above the collider!
Now, once you need to check if your character is grounded, you can just use
if (collisionsCounter > 0)
{
// I am grounded, allow me to jump
}
EDIT
Actually, after investingating question further, I've realized that you have totally unnecessary colliders (I'm talking about the trigger ones). Remove those. Now you have only one collider per object. But to get the calls for collision, you need to change:
OnTriggerEnter2D(...) to OnCollisionEnter2D(Collision2D)
OnTriggerExit2D(...) to OnCollisionExit2D(Collision2D)
Final code
[RequireComponent(typeof(Rigidbody2D))]
public sealed class Character : MonoBehaviour
{
// A constant with tag name to prevent typos in code
private const string TagName_Platform = "Platform";
public float Speed;
public float Jump;
private Rigidbody2D myRigidbody;
private byte platformCollisions;
// Check if the player can jump
private bool CanJump
{
get { return platformCollisions > 0; }
}
// Called once the script is started
private void Start()
{
myRigidbody = GetComponent<Rigidbody2D>();
platformCollisions = 0;
}
// Called every frame
private void Update()
{
// // // // // // // // // // // // // //
// Need to check for horizontal movement
// // // // // // // // // // // // // //
// Trying to jump
if (Input.GetKeyDown(KeyDode.UpArrow) && CanJump == true)
Jump();
}
// Called once Rigidbody2D starts colliding with something
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.collider.tag == TagName_Platform)
platformCollisions++;
}
// Called once Rigidbody2D finishes colliding with something
private void OnCollisionExit2D(Collision2D collision)
{
if(collision.collider.tag == TagName_Platform)
platformCollisions--;
}
// Makes Character jump
private void Jump()
{
Vector2 velocity = myRigidbody.velocity;
velocity.y = Jump;
myRigidbody.velocity = velocity;
}
}
Here can be minor typos as all the code was typed inside Notepad...
I think there are a couple of issues here.
Firstly, using Triggers to check this type of collision is probably not the best way forward. I would suggested not using triggers, and instead using OnCollisionEnter2D(). Triggers just detect if the collision space of two objects has overlapped each other, whereas normal collisions collide against each otehr as if they were two solid objects. Seen as though you are detecting to see if you have landed on the floor, you don't want to fall through the floor like Triggers behave.
Second, I would suggest using AddForce instead of GetComponent<Rigidbody2D>().velocity.
Your final script could look like something like this:
public class PlayerController : MonoBehaviour
{
public float jumpForce = 10.0f;
public bool isGrounded;
Rigidbody2D rb;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void OnCollisionEnter2D(Collision2D other)
{
// If we have collided with the platform
if (other.gameObject.tag == "YourPlatformTag")
{
// Then we must be on the ground
isGrounded = true;
}
}
void Update()
{
// If we press space and we are on the ground
if(Input.GetKeyDown(KeyCode.Space) && isGrounded)
{
// Add some force to our Rigidbody
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
// We have jumped, we are no longer on the ground
isGrounded = false;
}
}
}