- Home /
jumping and landing not very responsive
Hi everyone I decided not to add a rigidbody to my character, so I made the physics without it. I'm using the 3d unity engine to make a 2d game. I'm making an "auto-runner" game, a game where the character runs and you have to jump to avoid obstacles and kill enemies by jumping on their head.
I have two scripts attached to the player: one for the physics and one for the input.
Here's the input script:
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(PlayerPhysics))]
public class PlayerController : MonoBehaviour {
//Player Handling
public float getButtonUpForce = -14;
public float speed = 9;
public float secondGravity = -20;
public float gravity = 40;
public float acceleration = 25;
public float jumpHeight = 16.5;
//States
private bool jumping;
//System
private float currentSpeed;
private float targetSpeed;
private float animationSpeed;
private float animationVerticalSpeed;
private Vector2 amountToMove;
private Vector2 spawn;
//Components
private PlayerPhysics playerPhysics;
private Animator animator;
void Start () {
playerPhysics = GetComponent<PlayerPhysics> ();
animator = GetComponent<Animator> ();
spawn = transform.position;
}
void Update () {
//if the player falls, he dies and he goes to the start position
if (transform.position.y <= -6.6) {
Die ();
}
//RESET acceleration, in case you hit something
if (playerPhysics.movementStopped) {
targetSpeed = 0;
currentSpeed = 0;
}
//If player is touching the ground
if (playerPhysics.grounded) {
amountToMove.y = 0;
if (jumping) {
jumping = false;
animator.SetBool ("Jumping", false);
}
//Jump Input
if (Input.GetMouseButtonDown (0) || Input.GetButtonDown ("Jump")) {
amountToMove.y = jumpHeight;
jumping = true;
animator.SetBool ("Jumping", true);
} else {
}
}
// if he's not touching the ground...
if (!playerPhysics.grounded) {
//...and he's not jumping, that means he's falling and if he's falling then i want that he falls with greater gravity
if (jumping == false) {
amountToMove.y = secondGravity;
// i want that when he's falling he plays the falling animation.
// it plays the animation even on the ground, until i jump again and land and the animation goes back to normal. I have to fix that later.
//animator.SetBool ("Jumping", true);
}
}
if (Input.GetMouseButtonUp (0) || Input.GetButtonUp ("Jump")) {
amountToMove.y = getButtonUpForce;
jumping = true;
}
else {
}
animationSpeed = IncrementTowards (animationSpeed, Mathf.Abs (targetSpeed), acceleration);
animator.SetFloat ("Speed", animationSpeed);
//INPUT, must be AFTER the acceleration RESET!!! (see above)
targetSpeed = speed;
currentSpeed = IncrementTowards (currentSpeed, targetSpeed, acceleration);
//Set amount to move
amountToMove.x = currentSpeed;
amountToMove.y -= gravity * Time.deltaTime;
playerPhysics.Move(amountToMove * Time.deltaTime);
}
// Increase n towards target by speed
private float IncrementTowards(float n, float target, float a) {
if (n == target) {
return n;
}
else {
float dir = Mathf.Sign (target - n);
n += a * Time.deltaTime * dir;
return (dir == Mathf.Sign (target - n)) ? n : target;
}
}
void Die () {
transform.position = spawn;
}
}
and here's the physics script:
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(BoxCollider))]
public class PlayerPhysics : MonoBehaviour {
private BoxCollider collider;
private Vector3 s;
private Vector3 c;
private Vector3 originalSize;
private Vector3 originalCenter;
private float colliderScale;
public LayerMask collisionMask;
private int collisionDivisionsX = 5;
private int collisionDivisionsY = 10;
private float skin = .005f; //Tiny space so that the ray can detect the ground
[HideInInspector]
public bool grounded;
[HideInInspector]
public bool movementStopped;
Ray ray;
RaycastHit hit;
void Start() {
collider = GetComponent<BoxCollider> ();
colliderScale = transform.localScale.x;
originalSize = collider.size;
originalCenter = collider.center;
SetCollider (originalSize, originalCenter);
}
public void Move(Vector2 moveAmount) {
float deltaY = moveAmount.y;
float deltaX = moveAmount.x;
Vector2 p = transform.position;
//Check collision UP and DOWN
grounded = false;
for (int i = 0; i<collisionDivisionsX; i ++) { //These are the 3 "raycasts"
float dir = Mathf.Sign(deltaY);
float x = (p.x + c.x - s.x/2) + s.x/(collisionDivisionsX - 1) * i; //Left, center and rightmost point of the collider
float y = p.y + c.y + s.y/2 * dir; //Bottom of the collider
ray = new Ray(new Vector2(x,y), new Vector2(0,dir));
Debug.DrawRay(ray.origin, ray.direction);
if (Physics.Raycast(ray, out hit, Mathf.Abs(deltaY) + skin, collisionMask)) {
//Get Distance between player and ground
float dst = Vector3.Distance (ray.origin, hit.point);
//Stop player's downwards movement after coming within skin width of a collider
if (dst > skin) {
deltaY = dst * dir + skin;
}
else {
deltaY = 0;
}
grounded = true;
break;
}
}
//Check collision LEFT and RIGHT
movementStopped = false; //Avoids stickiness, check below for the "if" as "true"
for (int i = 0; i<collisionDivisionsY; i ++) { //These are the 3 "raycasts"
float dir = Mathf.Sign(deltaX);
float x = p.x + c.x + s.x/2 * dir; //Left, center and rightmost point of the collider
float y = p.y + c.y - s.y/2 + s.y/(collisionDivisionsY - 1) * i; //Bottom of the collider
ray = new Ray(new Vector2(x,y), new Vector2(dir,0));
Debug.DrawRay(ray.origin, ray.direction);
if (Physics.Raycast(ray, out hit, Mathf.Abs(deltaX) + skin, collisionMask)) {
//Get Distance between player and ground
float dst = Vector3.Distance (ray.origin, hit.point);
//Stop player's downwards movement after coming within skin width of a collider
if (dst > skin) {
deltaX = dst * dir - skin * dir;
}
else {
deltaX = 0;
}
movementStopped = true;
break;
}
}
//DIAGONAL FIX
if (!grounded && !movementStopped) {
Vector3 playerDir = new Vector3 (deltaX, deltaY);
Vector3 o = new Vector3 (p.x + c.x + s.x / 2 * Mathf.Sign (deltaX), p.y + c.y + s.y / 2 * Mathf.Sign (deltaY));
ray = new Ray (o, playerDir.normalized);
if (Physics.Raycast (ray, Mathf.Sqrt (deltaX * deltaX + deltaY * deltaY), collisionMask)) {
grounded = true;
deltaY = 0;
}
}
Vector2 finalTransform = new Vector2 (deltaX, deltaY);
transform.Translate (finalTransform, Space.World);
}
public void SetCollider (Vector3 size, Vector3 center) {
collider.size = size;
collider.center = center;
s = size * colliderScale;
c = center * colliderScale;
}
}
In the input script i wrote "//if he's not touching the ground and he's not jumping, that means he's falling and if he's falling then i want that he falls with greater gravity." because there are two "gravities": one is applied when you keep holding the jump button (because if you keep holding the jump button he will do a full jump and if you stop holding the jump button while in mid-air he will fall with a greater force on the ground) and the other when i simply fall without jumping, and that works, when he goes on "cubestairs" he perfectly goes down.
The problem here is that at the end this game will be challenging, and if i want difficult levels i want that the physics are perfect, otherwise it's frustrating. The problem is that when I jump and land and then jump immediately after he jumps, but if I jump and like 3 or four frames before landing i press the jump button he doesn't jump (of course, because he's not on the ground), but is there a way to write in code "3 or 4 frames before landing, if I press the jump button, then when you land jump immediately?
Thanks in advance, Leonardo.
ps. It would very much please me if you could also explain to me how to fix the following issue: when i rotate the boxcollider of the ground by 45° the player ignores the boxcollider and falls down. If anyone can figure out how to stand on it it would be great because i want slopes in my game. If you can't, no problem.
Hi I thought of making another ray and a "almostGrounded" bool. I tryed, but no results. If someone knows how to do this it would be great.
Answer by Cherno · Sep 07, 2014 at 02:38 AM
Take a look at this tutorial, it helped me a lot with running / jumping animations, and als owith jumping physics.
Thanks a lot for your answer! I'm reading it now.