Jumping Collider and Ground Check

Hi guys,

I’m making a side scrolling game in 3D - the thing that the player will be doing the most is jumping, however I got a few problems with this.

First off my jump animation causes the character to pull up his legs, leaving his collider hangout beyond his feet (see the image below)

[27227-jump+collider+problem.jpg|27227]

Needless to say, this makes him collide with objects below his feet making jumping hacky and stupid.

As I use different states to control his animations I tried to change the position of the colliders center point, however that often causes the character to ignore the ground check and get stuck in the falling condition. You can see the code below:

void MovementMangemant(Vector3 targetDirection){
		if(targetDirection.x != 0f || targetDirection.z != 0f && IsGrounded() && !jumping){
			print ("Running");
			playerCollider.center = new Vector3(0, 4, 0.5f);
			playerSounds.state = PlayerSounds.States.Running;
			anim.SetBool(hash.fallingBool, false);
			anim.SetBool(hash.jumpingBool, false);
			Rotation(targetDirection.z, targetDirection.x);
			anim.SetFloat(hash.speedFloat, 5f, speedDampTime, Time.deltaTime);
			anim.speed = 2.5f;
		}else if(jumping){
			print ("jumping");
			//playerCollider.center = new Vector3(0, 5f, 0.5f);
			playerSounds.state = PlayerSounds.States.Jump;
			anim.SetBool(hash.jumpingBool, true);
			anim.speed = 1f;
		}else if(!IsGrounded() && !jumping &&!onElevator){
			print ("falling");
			playerCollider.center = new Vector3(0, 5f, 0.5f);
			anim.SetBool(hash.jumpingBool, false);
			anim.SetBool(hash.fallingBool, true);
			anim.speed = 1f;
		}else if(targetDirection.x == 0f || targetDirection.z == 0f && IsGrounded() && !jumping){
			print ("idle");
			playerCollider.center = new Vector3(0, 4, 0.5f);
			playerSounds.state = PlayerSounds.States.Idle;
			anim.SetFloat(hash.speedFloat, 0f);
			anim.SetBool(hash.jumpingBool, false);
			anim.SetBool(hash.fallingBool, false);
			anim.speed = 1f;
		}
		Vector3 newPosition = Vector3.Lerp(rigidbody.position, rigidbody.position+targetDirection, speed * Time.deltaTime); 
		rigidbody.MovePosition(newPosition);
	}

Again needless to say, that won’t work at all. I also tried using a mesh collider, but that won’t work at all.

Beyond that I have another (and perhaps related problem). It seems that my ground check is making some problems. Initially it seems to work, if I jump the animation change, and change correctly once I hit the ground - However, in the picture above I am able to jump again, as it apparently believes I am close enough to the ground to jump again. Strange thing however is that the animation does not change until I actually hit the ground.

Here is the code for my ground check:

private float distToGround;

distToGround = collider.bounds.extents.y;

bool IsGrounded(){
		if(onElevator){
			return true;
		}else{
			return Physics.Raycast(transform.position, -Vector3.up, distToGround + 0.1f);
		}
	}

And here is the entire script for the player locomotion:

using UnityEngine;
using System.Collections;

public class PlayerMotor : MonoBehaviour {


	protected Animator anim;

	private HashID hash;

	public float DirectionDampTime = 0.25f;
	public float turnSmoothing = 15f;
	public float speedDampTime;
	public float speed;
	public float jumpForce = 5.0f;
	public bool IJActive = false;
	public float IJDirection;
	public bool onElevator;

	private bool jumping = false;
	private float distToGround;
	private PlayerSounds playerSounds;
	private float v = 0;
	private CapsuleCollider playerCollider;

	void Awake(){
		anim = this.GetComponent<Animator>();
		playerSounds = this.GetComponent<PlayerSounds>();
		hash = GameObject.Find ("Game Controller").GetComponent<HashID>();
	}

	void Start(){
		onElevator = false;
		distToGround = collider.bounds.extents.y;
		playerCollider = GetComponent<CapsuleCollider>();
	}

	void FixedUpdate(){
		float h = Input.GetAxis("Horizontal");
		if(!IJActive){
			v = 0;
		}else if(IJActive){
			v = IJDirection;
		}
		if(Input.GetKey(KeyCode.Space) && IsGrounded() && !jumping){
			jumping = true;
			StartCoroutine(Jump(jumpForce));
		}
		MovementMangemant(new Vector3(v, 0, h));
	}

	void MovementMangemant(Vector3 targetDirection){
		if(targetDirection.x != 0f || targetDirection.z != 0f && IsGrounded() && !jumping){
			print ("Running");
			playerCollider.center = new Vector3(0, 4, 0.5f);
			playerSounds.state = PlayerSounds.States.Running;
			anim.SetBool(hash.fallingBool, false);
			anim.SetBool(hash.jumpingBool, false);
			Rotation(targetDirection.z, targetDirection.x);
			anim.SetFloat(hash.speedFloat, 5f, speedDampTime, Time.deltaTime);
			anim.speed = 2.5f;
		}else if(jumping){
			print ("jumping");
			//playerCollider.center = new Vector3(0, 5f, 0.5f);
			playerSounds.state = PlayerSounds.States.Jump;
			anim.SetBool(hash.jumpingBool, true);
			anim.speed = 1f;
		}else if(!IsGrounded() && !jumping &&!onElevator){
			print ("falling");
			playerCollider.center = new Vector3(0, 5f, 0.5f);
			anim.SetBool(hash.jumpingBool, false);
			anim.SetBool(hash.fallingBool, true);
			anim.speed = 1f;
		}else if(targetDirection.x == 0f || targetDirection.z == 0f && IsGrounded() && !jumping){
			print ("idle");
			playerCollider.center = new Vector3(0, 4, 0.5f);
			playerSounds.state = PlayerSounds.States.Idle;
			anim.SetFloat(hash.speedFloat, 0f);
			anim.SetBool(hash.jumpingBool, false);
			anim.SetBool(hash.fallingBool, false);
			anim.speed = 1f;
		}
		Vector3 newPosition = Vector3.Lerp(rigidbody.position, rigidbody.position+targetDirection, speed * Time.deltaTime); 
		rigidbody.MovePosition(newPosition);
	}

	void Rotation(float horizontal, float vertical){
		Vector3 targetDirection = new Vector3(vertical, 0f, horizontal);
		Quaternion targetRotation = Quaternion.LookRotation(targetDirection, Vector3.up);
		Quaternion newRotation = Quaternion.Lerp(rigidbody.rotation, targetRotation, turnSmoothing * Time.deltaTime);
		rigidbody.MoveRotation(newRotation);
	}

	IEnumerator Jump(float force){
		yield return new WaitForSeconds(0.001f);
		rigidbody.velocity = new Vector3(0, force, 0);
		jumping = false;
	}

	bool IsGrounded(){
		if(onElevator){
			return true;
		}else{
			return Physics.Raycast(transform.position, -Vector3.up, distToGround + 0.1f);
		}
	}

Hopefully someone can help me in these problems, because I am bit at a loss here.

Thank you for the reply -

Yes The character does actually jump, however, the jump animation causes the character to pull up his legs, however, his pivot remains the same so the collider does not move up as much as the legs are being pulled up.

I tried changing the size of the collider, however, that made some problems when he landed again as I needed to put the collider back in the correct position which made him get stuck in his falling state.

I’m not a programmer so I can’t really entirely understand what u wrote up there. But this tutorial helped me with my jumping code problems for my game.

https://unity3d.com/learn/tutorials/modules/beginner/live-training-archive/creating-a-basic-platformer-game

His method to achieve a nice looking jump animation allowed me to have the ground check state work for any type of floor. Be it ground or floating platforms.

hope it helps

I’ve had similar problem when I built my game character rig.

Problem is, most basic tutorials and examples and free packages out there do not actually have good working ground check, instead they use a simple raycast, like you do, and call it a day, good enough for an example.

I have modified one such example with a different ground check. I use capsule collider for my character, and his “isGrounded” check must pass one of the two conditions:

  • Either the center raycast down is hitting something solid, or
  • The combined normal of all contact points with anything solid is pointing significantly upward.

Here’s a pic to illustrate:
[66651-groundcheck1.jpg*|66651]

However this is the case you’re describing as being a problem, for example your character is standing at the edge of some object (hanging overedge actually):
[66652-groundcheck2.jpg*|66652]

As you can see, the raycast won’t be hitting anything, but we can still grab a contact point with another object, and see where is the collision normal pointing.

Here’s my (still uncomplete and needs-tweaking!) code, it’s not yet fully done but it should get you started on the right track:

bool IsGrounded() 
	{
        if (!isGroundedByCollision)
        {
            Vector3 cColliderBottomLocation = transform.position +
                                            ((cCollider.center - new Vector3(0, (cCollider.height) / 2 - 0.05f, 0)) * transform.localScale.y);

            float rayLength = 0.25f;

            Debug.DrawRay(cColliderBottomLocation, -Vector3.up * rayLength);
            return Physics.Raycast(cColliderBottomLocation, -Vector3.up, rayLength);
        }
        else
            return true;
	}


/// isGrounded by collision is gotten via Collision as:
public void OnCollisionStay(Collision other)
    {
        float highestDotProduct = float.MinValue;

        ContactPoint[] points = other.contacts;

        for (int i = 0; i < points.Length; i++)
        {
            float dotProduct = Vector3.Dot(points*.normal, Vector3.up);*

if (dotProduct > highestDotProduct)
{
highestDotProduct = dotProduct;
}
}

if (highestDotProduct > 0.7f)
{
EndJump();
isGroundedByCollision = true;
//print(“Correction via hit normal!”);
}
else
{
isGroundedByCollision = false;
}
}

public void OnCollisionExit(Collision other)
{
if (other.contacts.Length == 0)
isGroundedByCollision = false;
}
If you implement something like that, feel free to resize and re-do your character’s collider to your heart’s content. I am currently reducing the collider height by half when we’re not grounded, and expanding it back to original size when we have landed. Not instantly, as it produces jerky motion, each frame we’re constantly leaning towards desired collider size.
*
*