Hello.
I’ve been working on a 3D Infinite Runner for the past week but came to an issue when it came to the physics of my character.
I’m having a few issues relating to the way my Rigidbody-controlled character jumps:
The jumps are unpredictable
Whenever my character jumps, it either jumps really low, to the height it’s supposed to, above the height it’s supposed to, or waaaaaaay above the height it’s supposed to and flies off the playing field and never comes back down because it is so high.
The physics act slightly different on different devices
The next issue comes from the jumps acting differently on different devices. A jump height of 100 gets me a way different jump height in the editor than in the Build. I made sure to use Time.deltaTime, which helped a little, but the difference is still there.
Here is my player code if it helps:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerMove : MonoBehaviour
{
private PlayerInput inputActions;
[SerializeField] bool tapped;
[SerializeField] Vector2 touchDelta;
public float speed;
public float jumpHight;
public float downForce;
public Rigidbody rb;
public bool isGrounded;
bool jump;
// Start is called before the first frame update
void Start()
{
inputActions = new PlayerInput();
inputActions.Enable();
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
//determine if we are touching the screen
if(inputActions.Move.InitialTap.phase == InputActionPhase.Performed)
{
tapped = true;
}
else
{
tapped = false;
}
if(tapped)
{
touchDelta = inputActions.Move.TouchDelta.ReadValue<Vector2>();
float clampedX = Mathf.Clamp(transform.position.x, -9, 9);
transform.position = new Vector3(clampedX, transform.position.y, transform.position.z);
//Move our player left or right if the absolute value of touchDelta.x value is greater than the absolute value of touchDelta.y
if(Mathf.Abs(touchDelta.x) > Mathf.Abs(touchDelta.y))
{
rb.AddForce(Vector3.right * touchDelta.x * speed * Time.deltaTime, ForceMode.Impulse);
}
if (touchDelta.y > touchDelta.x && touchDelta.y > 2.5 && isGrounded)
{
print("jump");
jump = true;
}
else
{
jump = false;
}
}
}
void FixedUpdate()
{
RaycastHit hit;
Physics.Raycast(transform.position, Vector3.down, out hit, 0.5f);
if(hit.collider != null)
{
isGrounded = true;
}
else
{
isGrounded = false;
}
rb.AddForce(Vector3.down * downForce * Time.deltaTime, ForceMode.Force);
if (jump)
{
rb.AddForce(Vector3.up * jumpHight * Time.deltaTime, ForceMode.Impulse);
}
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Obstacle"))
{
GameManager.GameOver();
}
}
}
And I’m also attaching a screenshot of the Ridgidbody settings I’m using:
first thing i noticed is you’re using addforce in update, which is a big no no. only adjust rigidbodies in fixedupdate.
you’re also using .deltaTime which is what fixedupdate is made specifically to not have to use.
fixedupdate is a set update rate so you’re changing things that don’t need to be. i don’t remember if it returns the deltatime of update or fixedupdate when in fixedupdate, but either way you don’t need it EVER in fixedupdate.
use deltaTime exclusively in update or coroutines mostly
Ok, I changed every ridgidbody-related thing to fixed updated and removed deltaTime, but it still doesn’t work.
There’s still a really weird height variation thing going on.
Here is the updated code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerMove : MonoBehaviour
{
private PlayerInput inputActions;
[SerializeField] bool tapped;
[SerializeField] Vector2 touchDelta;
public float speed;
public float jumpHight;
public float downForce;
public Rigidbody rb;
public bool isGrounded;
bool jump;
// Start is called before the first frame update
void Start()
{
inputActions = new PlayerInput();
inputActions.Enable();
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
//determine if we are touching the screen
if(inputActions.Move.InitialTap.phase == InputActionPhase.Performed)
{
tapped = true;
}
else
{
tapped = false;
}
if(tapped)
{
touchDelta = inputActions.Move.TouchDelta.ReadValue<Vector2>();
float clampedX = Mathf.Clamp(transform.position.x, -9, 9);
transform.position = new Vector3(clampedX, transform.position.y, transform.position.z);
if (touchDelta.y > touchDelta.x && touchDelta.y > 2.5 && isGrounded)
{
print("jump");
jump = true;
}
else
{
jump = false;
}
}
}
void FixedUpdate()
{
RaycastHit hit;
Physics.Raycast(transform.position, Vector3.down, out hit, 0.5f);
if(hit.collider != null)
{
isGrounded = true;
}
else
{
isGrounded = false;
}
//Move our player left or right if the absolute value of touchDelta.x value is greater than the absolute value of touchDelta.y
if (Mathf.Abs(touchDelta.x) > Mathf.Abs(touchDelta.y))
{
rb.AddForce(Vector3.right * touchDelta.x * speed, ForceMode.Impulse);
}
rb.AddForce(Vector3.down * downForce, ForceMode.Force);
if (jump)
{
rb.AddForce(Vector3.up * jumpHight, ForceMode.Impulse);
}
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Obstacle"))
{
GameManager.GameOver();
}
}
}
Ok, I added a Debug.log right before the rb.AddForce is called for the jump.
The number of times it is printed in the console varies from one to four times per jump.
Alright! Fixed the issue.
I needed to set isGrounded to false after jump was called just so the conditions for jumping couldnt be met in the few frames that my raycast detected ground.