Reputation: 43
I am working on 2D infinite runner. I have below code to take input from screen swipe to jump, slide and run fast. I am providing jumpHeight from editor and value is 500 with frame rate of 30. Code works fine generally but sometimes player jumps too high for up swipe. Similar code works as expected if input is from Keyboard. Why this is happening is beyond my understanding of unity. Any help is greatly appreciated.
using UnityEngine;
public class PlayerControl : MonoBehaviour
{
public float ForwardSpeed = 3.7f; //moves player in forward direction
public float speedOffset = 0.0f; //offset speed of player
public float JumpHeight = 250; //moves player in verticle direction
bool grounded = false; //checks if player is grounded or not
public Transform groundCheck;
float groundCheckRadius = 0.3f; //radius of groundcheck circle to check grounded bool
public LayerMask groundLayer;
Vector2 fingerStart;
Vector2 fingerEnd;
void Update()
{
foreach (Touch touch in Input.touches)
{
if (touch.phase == TouchPhase.Began)
{
fingerStart = touch.position;
fingerEnd = touch.position;
}
if (touch.phase == TouchPhase.Moved)
{
fingerEnd = touch.position;
if (Mathf.Abs(fingerEnd.y - fingerStart.y) > 50)//Vertical swipe
{
if (fingerEnd.y - fingerStart.y > 50)//up swipe
{
Jump();
}
else if (fingerEnd.y - fingerStart.y < -50)//Down swipe
{
//Slide();
}
fingerStart = touch.position;
}
}
if (touch.phase == TouchPhase.Stationary)
{
RunFast();
}
if (touch.phase == TouchPhase.Ended)
{
fingerEnd = touch.position;
if (Mathf.Abs(fingerEnd.y - fingerStart.y) > 50)//Vertical swipe
{
if (fingerEnd.y - fingerStart.y > 50)//up swipe
{
Jump();
}
else if (fingerEnd.y - fingerStart.y < -50)//Down swipe
{
//Slide();
}
}
}
}
if (Input.GetButton("Fire1"))
{
speedOffset = 2.5f;
}
else
{
speedOffset = 0.0f;
}
if (grounded && Input.GetKeyDown(KeyCode.UpArrow))
{
grounded = false;
GetComponent<Rigidbody2D>().AddForce(new Vector2(0, JumpHeight));
}
//check if circle overlaps with ground layer
grounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer);
//Debug.Log(grounded);
}
void FixedUpdate()
{
//set players forward velocityto forward speed variable
Vector2 PlayerForwardvelocity = GetComponent<Rigidbody2D>().velocity;
// Vector2 PlayerJumpHeight = GetComponent<Rigidbody2D>().AddForce()
PlayerForwardvelocity.x = ForwardSpeed + speedOffset;
GetComponent<Rigidbody2D>().velocity = PlayerForwardvelocity;
}
void Jump()
{
if (grounded)
{
GetComponent<Rigidbody2D>().AddForce(new Vector2(0, JumpHeight));
speedOffset = 0.0f;
}
}
void RunFast()
{
if (Input.GetButton("Fire1"))
{
speedOffset = 2.5f;
}
else
{
speedOffset = 0.0f;
}
}
}
Upvotes: 4
Views: 1170
Reputation: 125275
You have 2 problems with your code.
Your first problem lies in this line of code:
grounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer);
This line of code is failing. grounded
is always true
. Because of this, jump is called too many times while player is not grounded.
Replace this line of code with
Collider2D playerCollider = gameObject.GetComponent<Collider2D>();
grounded = Physics2D.OverlapCircle(playerCollider.transform.position, 1, groundLayer);
OR
Collider2D playerCollider = gameObject.GetComponent<Collider2D>();
grounded = playerCollider.IsTouchingLayers(groundLayer.value);
Another problem with your code is a false
report.Sometimes, collider overlapping returns true
even if it is false
. I tried moving that part of code to LateUpdate function but that didn't fix it.
You can fix it by implementing a timer. The timer resets to 0 and starts counting to 0.5 whenever player jumps. Don't jump when timer has not reached the value it is counting to. .5 to 1
is a perfect value for this. Increment the timer with Time.deltaTime
. Below is your whole code with timer and fixes.
public class PlayerControl : MonoBehaviour
{
public float ForwardSpeed = 3.7f; //moves player in forward direction
public float speedOffset = 0.0f; //offset speed of player
public float JumpHeight = 250; //moves player in verticle direction
bool grounded = false; //checks if player is grounded or not
public Transform groundCheck;
float groundCheckRadius = 0.3f; //radius of groundcheck circle to check grounded bool
public LayerMask groundLayer;
Vector2 fingerStart;
Vector2 fingerEnd;
public float resetTimer = 0.5f; //.5 second
float timerCounter = 0;
Collider2D playerCollider = null;
Rigidbody2D playerRigidBody;
void Start()
{
playerRigidBody = GetComponent<Rigidbody2D>();
playerCollider = gameObject.GetComponent<Collider2D>();
}
void Update()
{
foreach (Touch touch in Input.touches)
{
if (touch.phase == TouchPhase.Began)
{
fingerStart = touch.position;
fingerEnd = touch.position;
}
if (touch.phase == TouchPhase.Moved)
{
fingerEnd = touch.position;
if (Mathf.Abs(fingerEnd.y - fingerStart.y) > 50)//Vertical swipe
{
if (fingerEnd.y - fingerStart.y > 50)//up swipe
{
Jump();
}
else if (fingerEnd.y - fingerStart.y < -50)//Down swipe
{
//Slide();
}
fingerStart = touch.position;
}
}
if (touch.phase == TouchPhase.Stationary)
{
RunFast();
}
if (touch.phase == TouchPhase.Ended)
{
fingerEnd = touch.position;
if (Mathf.Abs(fingerEnd.y - fingerStart.y) > 50)//Vertical swipe
{
if (fingerEnd.y - fingerStart.y > 50)//up swipe
{
Jump();
}
else if (fingerEnd.y - fingerStart.y < -50)//Down swipe
{
//Slide();
}
}
}
}
if (Input.GetButton("Fire1"))
{
speedOffset = 2.5f;
}
else
{
speedOffset = 0.0f;
}
if (grounded && Input.GetKeyDown(KeyCode.UpArrow))
{
grounded = false;
playerRigidBody.AddForce(new Vector2(0, JumpHeight));
}
//check if circle overlaps with ground layer
grounded = Physics2D.OverlapCircle(playerCollider.transform.position, 1, groundLayer);
//OR Use grounded = playerCollider.IsTouchingLayers(groundLayer.value);
//Increment Timer if it is still less than resetTimer
if (timerCounter < resetTimer)
{
timerCounter += Time.deltaTime;
}
}
void FixedUpdate()
{
//set players forward velocityto forward speed variable
Vector2 PlayerForwardvelocity = playerRigidBody.velocity;
// Vector2 PlayerJumpHeight = playerRigidBody.AddForce()
PlayerForwardvelocity.x = ForwardSpeed + speedOffset;
playerRigidBody.velocity = PlayerForwardvelocity;
}
void Jump()
{
if (grounded)
{
//Exit if timer has not reached the required value to jump again
if (timerCounter < resetTimer)
{
Debug.Log("Failed To Jump because timer has not yet reached");
return; //Exit
}
timerCounter = 0; //Reset Timer
playerRigidBody.AddForce(new Vector2(0, JumpHeight));
speedOffset = 0.0f;
Debug.Log("Jumped");
}
else
{
Debug.Log("Not on the Ground");
}
}
void RunFast()
{
if (Input.GetButton("Fire1"))
{
speedOffset = 2.5f;
}
else
{
speedOffset = 0.0f;
}
}
}
Upvotes: 1