I want to allow the player to double-jump,but the player can keep jumping infinitely,can anyone help me? I use Unity 5
using System.Collections.Generic;
using UnityEngine;
public class PlayerControl : MonoBehaviour {
public float moveSpeed;
public float jumpHeight;
private bool grounded;
private Rigidbody2D rb;
private int jumpCount = 0;
private int maxJumps = 2;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown (KeyCode.Space) && jumpCount < maxJumps) {
rb = GetComponent<Rigidbody2D> ();
rb.velocity = new Vector2 (rb.velocity.x, jumpHeight);
jumpCount = jumpCount + 1;
}
if (grounded == true) {
jumpCount = 0;
}
if (Input.GetKey (KeyCode.D)) {
rb.velocity = new Vector2 (moveSpeed,rb.velocity.y);
}
if (Input.GetKey (KeyCode.A)) {
rb.velocity = new Vector2 (-moveSpeed,rb.velocity.y);
}
}
void OnCollisionEnter2D (Collision2D collider){
if (collider.gameObject.tag == "Ground") {
grounded = true;
}
}
}
The reason you can keep jumping infinitely is because you never set grounded to false so it will always reset the value of your jumpCount back to 0 here
if (grounded == true) {
jumpCount = 0;
}
So when you jump set grounded = false because you're no longer on the ground.
if (Input.GetKeyDown (KeyCode.Space) && jumpCount < maxJumps) {
rb = GetComponent<Rigidbody2D> ();
rb.velocity = new Vector2 (rb.velocity.x, jumpHeight);
jumpCount = jumpCount + 1;
grounded = false;
}
And when working with RigidBody and physics it's better to use FixedUpdate
FixedUpdate should be used instead of Update when dealing with Rigidbody.
Related
I've got my player sprite that has a blank "Front Check" object nested underneath it. The nested item is used to check if the front of the sprite is touching a wall. the problem I'm having is the Front Check object does not flip with the sprite when I move in the opposite direction. it was working fine earlier today and then out of the blue just quit. This is my first unity project and I'm just not quite sure what's missing. Any help is much appreciated. here is an image of the project and my movement script.
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
private Rigidbody2D rb;
private BoxCollider2D coll;
private SpriteRenderer sprite;
private Animator anim;
[SerializeField] private LayerMask jumpableGround;
public float checkRadius;
bool isTouchingFront;
public Transform frontCheck;
public float wallSlidingSpeed;
bool wallSliding;
bool wallJumping;
public float xWallForce;
public float yWallForce;
public float wallJumpTime;
private float dirX = 0f;
[SerializeField] private float moveSpeed = 7f;
[SerializeField] private float jumpForce = 14f;
[SerializeField] private float airControl;
private enum MovementState { idle, running, jumping, falling }
[SerializeField] private AudioSource JumpSoundEffect;
// Start is called before the first frame update
private void Start()
{
rb = GetComponent<Rigidbody2D>();
coll = GetComponent<BoxCollider2D>();
sprite = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
}
// Update is called once per frame
private void Update()
{
dirX = Input.GetAxisRaw("Horizontal");
if (dirX < 0 && IsGrounded())
{
rb.velocity = new Vector2(moveSpeed * -1, rb.velocity.y);
}
else if (dirX > 0 && IsGrounded())
{
rb.velocity = new Vector2(moveSpeed, rb.velocity.y);
}
else if (dirX == 0 && IsGrounded())
{
rb.velocity = new Vector2(0, rb.velocity.y);
}
if (Input.GetButtonDown("Jump") && IsGrounded())
{
JumpSoundEffect.Play();
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
}
UpdateAnimationState();
}
private void UpdateAnimationState()
{
MovementState state;
if (dirX > 0f && IsGrounded())
{
state = MovementState.running;
}
else if (dirX < 0f && IsGrounded())
{
state = MovementState.running;
}
else
{
state = MovementState.idle;
}
if (rb.velocity.y > .1f)
{
state = MovementState.jumping;
}
else if (rb.velocity.y < -.1f)
{
state = MovementState.falling;
}
//Wall Jump
isTouchingFront = Physics2D.OverlapCircle(frontCheck.position, checkRadius, jumpableGround);
if (isTouchingFront == true && IsGrounded() == false && dirX != 0)
{
wallSliding = true;
}
else
{
wallSliding = false;
}
if (wallSliding)
{
rb.velocity = new Vector2(rb.velocity.x, Mathf.Clamp(rb.velocity.y, -wallSlidingSpeed, float.MaxValue));
}
if (Input.GetButtonDown("Jump") && wallSliding == true)
{
wallJumping = true;
Invoke("SetWallJumpingToFalse", wallJumpTime);
}
if (wallJumping == true)
{
rb.velocity = new Vector2(xWallForce * -Input.GetAxisRaw("Horizontal"), yWallForce);
}
anim.SetInteger("state", (int)state);
}
void SetWallJumpingToFalse()
{
wallJumping = false;
}
public bool IsGrounded()
{
return Physics2D.BoxCast(coll.bounds.center, coll.bounds.size, 0f, Vector2.down, .1f, jumpableGround);
}
public void FixedUpdate()
{
if (dirX > 0f)
{
sprite.flipX = false;
}
else if (dirX < 0f)
{
sprite.flipX = true;
}
}
}
sprite.flipX only flips the rendering not the actual object scaling.
You rather want to use e.g.
var scale = transform.localScale;
scale.x = Mathf.Sign(dirX);
transform.localScale = scale;
Note that it needs to be UnityEngine.Mathf.Sign and not System.Math.Sign since the latter returns 0 for parameter 0 which would totally break your colliders etc. while Mathf.Sign returns only -1 or 1.
For whatever reason, my player movement stutters every second or every other second, and I'm sure it's to do with my player movement code being in the wrong update method.
Here's my player movement code
[SerializeField] private LayerMask groundLayerMask;
public float speed;
public float Jump;
public sword swordScript;
public GameObject swordSprite;
private float move;
private Rigidbody2D rb;
private BoxCollider2D boxCollider2d;
private bool facingRight;
public SpriteRenderer spr;
public Animator PlayerAnims;
public bool movementAllowed;
public float knockback;
void Awake()
{
boxCollider2d = GetComponent<BoxCollider2D>();
rb = GetComponent<Rigidbody2D>();
facingRight = true;
spr = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
boxCollider2d = GetComponent<BoxCollider2D>();
rb = GetComponent<Rigidbody2D>();
facingRight = true;
spr = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void FixedUpdate()
{
if(movementAllowed == true)
{
rb.velocity = new Vector2(move * speed, rb.velocity.y);
move = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(move * speed, rb.velocity.y);
if (isGrounded() && Input.GetButtonDown("Jump"))
{
rb.AddForce(new Vector2(rb.velocity.x, Jump));
}
}
}
void Update()
{
if (movementAllowed == true)
{
Flip(move);
if (move == 0)
{
PlayerAnims.SetBool("isRunning", false);
}
else
{
PlayerAnims.SetBool("isRunning", true);
}
}
}
private bool isGrounded()
{
float extraHeightText = .1f;
RaycastHit2D raycasthit2d = Physics2D.BoxCast(boxCollider2d.bounds.center, boxCollider2d.bounds.size, 0f, Vector2.down, extraHeightText, groundLayerMask);
Color rayColour;
if (raycasthit2d.collider != null)
{
rayColour = Color.green;
PlayerAnims.SetBool("isJumping", false);
}
else
{
rayColour = Color.red;
PlayerAnims.SetBool("isJumping", true);
}
Debug.DrawRay(boxCollider2d.bounds.center + new Vector3(boxCollider2d.bounds.extents.x, 0), Vector2.down * (boxCollider2d.bounds.extents.y + extraHeightText), rayColour);
Debug.DrawRay(boxCollider2d.bounds.center - new Vector3(boxCollider2d.bounds.extents.x, 0), Vector2.down * (boxCollider2d.bounds.extents.y + extraHeightText), rayColour);
Debug.DrawRay(boxCollider2d.bounds.center - new Vector3(boxCollider2d.bounds.extents.x, boxCollider2d.bounds.extents.y + extraHeightText), Vector2.right * (boxCollider2d.bounds.extents.x), rayColour);
return raycasthit2d.collider != null;
}
private void Flip(float move)
{
if (move > 0 && !facingRight || move < 0 && facingRight)
{
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
if (swordScript.isFollowing == true)
{
Vector3 swordScale = swordSprite.transform.localScale;
swordScale.x *= -1;
swordSprite.transform.localScale = swordScale;
}
}
}
I tried out your script and I think I barely see that stutter, but one thing seems to fix it.
You have this particular line twice in the code for no reason
rb.velocity = new Vector2(move * speed, rb.velocity.y);
Just remove the first one.
As a rule of thumb:
Calling rb.AddForce: This interacts with Physics - place it in FixedUpdate
Setting rb.velocity: This only sets a value. You are not triggering physics interactions with this - place it in Update
So, at first you should move your code which sets the velocity to the Update function. Also you are setting the velocity twice. Just remove the first line.
As an example here ist the Update and FixedUpdate function:
void FixedUpdate()
{
if(movementAllowed == true)
{
if (isGrounded() && Input.GetButtonDown("Jump"))
{
rb.AddForce(new Vector2(rb.velocity.x, Jump));
}
}
}
void Update()
{
if (movementAllowed == true)
{
float move = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(move * speed, rb.velocity.y);
Flip(move);
if (move == 0)
{
PlayerAnims.SetBool("isRunning", false);
}
else
{
PlayerAnims.SetBool("isRunning", true);
}
}
}
Since you are not using move anywhere else than Update you can just declare it as a local variable and remove the private float move; at the top of the script.
Last thing which should be mentioned:
You are getting component references both in Start or Awake. Generally you should get your references in Start and only get them in Awake when needed.
In my script, I have a patrol method and a chase method. My chase code using ray cast is not working properly. The only thing that works is my AI patrol code. When I hit a ray cast that I assign, it doesn't chase me. However, when I remove the patrol code in my script, my chase code works fine. What would be the problem? I want my AI to Patrol and Chase. If there is any collision and if the player is outside of the collision, the AI will start to patrol again
public class LineTrigger : MonoBehaviour {
private Transform targett;
public Transform sightStart, sightEnd;
public bool spotted = false;
public GameObject arrow;
public GameObject target;
public bool facingRight = false;
public float speed = 8;
[SerializeField]
float moveSpeed = 3f;
Rigidbody2D rb;
Vector3 localScale;
float dirX;
Vector3 directionToTarget;
void Start()
{
localScale = transform.localScale;
rb = GetComponent<Rigidbody2D> ();
dirX = -1f;
target = GameObject.Find ("Girl");
}
void Update()
{
if (transform.position.x < 1f)
dirX = 1f;
else if (transform.position.x > 20f)
dirX = -1f;
Raycasting ();
Behaviours ();
}
void Raycasting ()
{
Debug.DrawLine (sightStart.position, sightEnd.position);
spotted = Physics2D.Linecast (sightStart.position, sightEnd.position, 1 << LayerMask.NameToLayer ("Girl"));
}
void Behaviours()
{
if (spotted == true) {
arrow.SetActive (true);
directionToTarget = (target.transform.position - transform.position).normalized;
rb.velocity = new Vector2 (directionToTarget.x * moveSpeed, directionToTarget.y * moveSpeed);
}
else
{
arrow.SetActive (false);
}
}
void FixedUpdate()
{
rb.velocity = new Vector2 (dirX * moveSpeed, rb.velocity.y);
}
void LateUpdate()
{
CheckWhereToFace ();
}
void CheckWhereToFace()
{
if (dirX > 0)
facingRight = false;
else if (dirX < 0)
facingRight = true;
if (((facingRight) && (localScale.x < 0)) || ((!facingRight) && (localScale.x > 0)))
localScale.x *= -1;
transform.localScale = localScale;
}
}
I think you are missing a if(!spotted) in your FixedUpdate
This code will keep on jumping even though it is not on the ground how do you stop this (using Unity).
The Code:
using UnityEngine;
using System.Collections;
public class PlayerController : MonoBehaviour
{
//Movement
public float speed;
public float jump;
float moveVelocity;
//Grounded Vars
bool grounded = true;
void Update ()
{
//Jumping
if (Input.GetKeyDown (KeyCode.Space) || Input.GetKeyDown (KeyCode.UpArrow) || Input.GetKeyDown (KeyCode.Z) || Input.GetKeyDown (KeyCode.W))
{
if(grounded)
{
GetComponent<Rigidbody2D> ().velocity = new Vector2 (GetComponent<Rigidbody2D> ().velocity.x, jump);
}
}
moveVelocity = 0;
//Left Right Movement
if (Input.GetKey (KeyCode.LeftArrow) || Input.GetKey (KeyCode.A))
{
moveVelocity = -speed;
}
if (Input.GetKey (KeyCode.RightArrow) || Input.GetKey (KeyCode.D))
{
moveVelocity = speed;
}
GetComponent<Rigidbody2D> ().velocity = new Vector2 (moveVelocity, GetComponent<Rigidbody2D> ().velocity.y);
}
//Check if Grounded
void OnTriggerEnter2D()
{
grounded = true;
}
void OnTriggerExit2D()
{
grounded = false;
}
}
There are a couple of ways to achieve this but following your current implementation: why not set the isGrounded flag to false when you jump and just let the trigger handle resetting the flag when you land?
Some things you could check:
Are all your colliders 2D and set to be triggers?
Have you checked your layer collisions are actually happening?
public class PlayerController : MonoBehaviour
{
//Movement
public float speed;
public float jump;
float moveVelocity;
//Grounded Vars
bool isGrounded = true;
void Update ()
{
//Jumping
if (Input.GetKeyDown (KeyCode.Space) || Input.GetKeyDown (KeyCode.UpArrow) || Input.GetKeyDown (KeyCode.Z) || Input.GetKeyDown (KeyCode.W))
{
if(isGrounded)
{
GetComponent<Rigidbody2D> ().velocity = new Vector2 (GetComponent<Rigidbody2D> ().velocity.x, jump);
isGrounded = false;
}
}
moveVelocity = 0;
//Left Right Movement
if (Input.GetKey (KeyCode.LeftArrow) || Input.GetKey (KeyCode.A))
{
moveVelocity = -speed;
}
if (Input.GetKey (KeyCode.RightArrow) || Input.GetKey (KeyCode.D))
{
moveVelocity = speed;
}
GetComponent<Rigidbody2D> ().velocity = new Vector2 (moveVelocity, GetComponent<Rigidbody2D> ().velocity.y);
}
//Check if Grounded
void OnTriggerEnter2D()
{
isGrounded = true;
}
}
Im pretty sure you need to have "" for the GetKeyDown in () but im not sure im pretty new at this, my username says it.
I know it's been 6 years, but I found a solution that worked for me.
//Movement
public float speed;
public float jump;
float moveVelocity;
public Rigidbody2D rb;
bool isGrounded;
void Update()
{
//Grounded?
if(isGrounded == true){
//jumping
if (Input.GetKeyDown(KeyCode.Space) || Input.GetKeyDown(KeyCode.UpArrow) || Input.GetKeyDown(KeyCode.Z) || Input.GetKeyDown(KeyCode.W))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(GetComponent<Rigidbody2D>().velocity.x, jump);
}
}
moveVelocity = 0;
//Left Right Movement
if (Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A))
{
moveVelocity = -speed;
}
if (Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D))
{
moveVelocity = speed;
}
GetComponent<Rigidbody2D>().velocity = new Vector2(moveVelocity, GetComponent<Rigidbody2D>().velocity.y);
}
void OnCollisionEnter2D(Collision2D col)
{
Debug.Log("OnCollisionEnter2D");
isGrounded = true;
}
void OnCollisionExit2D(Collision2D col)
{
Debug.Log("OnCollisionExit2D");
isGrounded = false;
}
}
I have problem when making a 2d platformer game with Unity 5 about cannot perform double jump and just perform one jump for my playable character. Please check out what's wrong from my script below:
Player Movement.cs
using UnityEngine;
using System.Collections;
public class PlayerMovement : MonoBehaviour {
public float forwardSpeed = 1f;
public float jumpHeight;
bool doJump = false;
bool canJumpAgain;
bool isGrounded;
float deathCooldown;
Animator animator;
Rigidbody2D myRigidbody;
void Awake () {
myRigidbody = GetComponent<Rigidbody2D> ();
}
// Use this for initialization
void Start () {
animator = transform.GetComponentInChildren<Animator>();
if(animator == null) {
Debug.LogError("Didn't find animator!");
}
}
// Update is called once per frame
void Update () {
isGrounded = true;
//for movement with infinite run to right
myRigidbody.AddForce( Vector2.right * forwardSpeed );
transform.rotation = Quaternion.Euler(0, 0, 0);
}
void OnCollisionStay2D (Collision2D col) {
if (col.gameObject.tag == "Ground") {
if (Input.GetKeyDown (KeyCode.Space) || Input.GetMouseButtonDown (0) && isGrounded && !doJump) {
myRigidbody.velocity = new Vector2 (GetComponent<Rigidbody2D>().velocity.x, jumpHeight);
animator.SetTrigger("DoJump");
isGrounded = false;
canJumpAgain = true;
doJump = true;
}
//perform double jump but failed
if (Input.GetKeyDown (KeyCode.Space) || Input.GetMouseButtonDown (0) && doJump && !isGrounded) {
myRigidbody.velocity = Vector2.zero;
myRigidbody.velocity = new Vector2 (GetComponent<Rigidbody2D>().velocity.x, jumpHeight);
animator.SetTrigger("DoJump");
Debug.Log ("yes");
canJumpAgain = true;
doJump = false;
}
if (canJumpAgain) {
//myRigidbody.velocity = Vector2.zero;
myRigidbody.AddForce(Vector2.up * jumpHeight);
animator.SetTrigger("DoJump");
Debug.Log ("yes1");
}
}
}
void OnCollisionExit2D (Collision2D col) {
if (col.gameObject.tag == "Ground") {
isGrounded = false;
GetComponent<CircleCollider2D>().isTrigger = false;
}
transform.parent = null;
}
void OnTriggerEnter2D(Collider2D Other)
{
}
}
I'm pretty sure this will fix the problem you're facing. Do not set isGrounded to true every Update() call. On the OnTriggerEnter2D() you wan't to have something like this: if (other.gameObject.tag == "ground") isGrounded = true; On the OnTriggerExit2D() you need to have this: if (other.gameObject.tag == "ground") isGrounded = false; Set the floor tag in the Unity inspector to ground. Explained: Before each double jump you're checking if isGrounded is false, however this will be always true since you're making it true every frame.
Finally I can resolved with my way please check out my script below:
using UnityEngine;
using System.Collections;
public class PlayerMovement : MonoBehaviour {
public float forwardSpeed = 1f;
public float jumpHeight;
bool doJump;
bool canJumpAgain;
bool isGrounded;
float deathCooldown;
Animator animator;
Rigidbody2D myRigidbody;
private bool canJump = false;
private int availableJumps = 1;
float jumpForce = 0f; // Force added when the player jumps.
public float firstJumpForce = 0f;
public float secondJumpForce = 0f;
void Awake () {
myRigidbody = GetComponent<Rigidbody2D> ();
}
// Use this for initialization
void Start () {
animator = transform.GetComponentInChildren<Animator>();
if(animator == null) {
Debug.LogError("Didn't find animator!");
}
}
// Update is called once per frame
void Update () {
isGrounded = true;
//for movement with infinite run to right
myRigidbody.AddForce( Vector2.right * forwardSpeed );
transform.rotation = Quaternion.Euler(0, 0, 0);
if (Input.GetKeyDown (KeyCode.Space) || Input.GetMouseButtonDown (0) && isGrounded) {
doJump = true;
}
}
void FixedUpdate () {
if(doJump && canJump)
{
// Add a vertical force to the player.
myRigidbody.velocity = new Vector2 (GetComponent<Rigidbody2D>().velocity.x, jumpHeight);
animator.SetTrigger("DoJump");
// Can the player jump again??
availableJumps--;
if (availableJumps <= 0){
canJump = false;
} else {
//I modify the second jump force
jumpForce = secondJumpForce;
}
doJump = false;
}
}
void OnCollisionStay2D (Collision2D col) {
if (col.gameObject.tag == "Ground") {
canJump = true;
availableJumps = 1;
jumpForce = firstJumpForce;
}
}
void OnCollisionExit2D (Collision2D col) {
if (col.gameObject.tag == "Ground") {
isGrounded = false;
GetComponent<CircleCollider2D>().isTrigger = false;
}
transform.parent = null;
}
void OnTriggerEnter2D(Collider2D Other)
{
}
}
I cannot remove "isGrounded = true;" on void update section because cannot perform first jump although already tried from TuukkaX's solution