- Home /
Pixel Perfect Collision Possible?
Hello, I have been trying for a little while now to achieve precise collision for a 2D platforming game with an orthographic camera, but I just can't seem to get it to behave how I want. The only way I have been able to get perfect collision is by using a CharacterController, which I can't use because of the limitations of the capsule shape. Also, I don't like the reactions by adding forces, so I am trying to manually set the velocity, or manually translate the object.
The Problem:
When landing from a jump, the character sometimes dips below into the ground, before snapping back up to ground level. This happens randomly, maybe 60% of the time. This is visually not good.
What I've Tried:
I have tried setting the velocity manually and then using Raycasts to detect the ground, and if the ground is present, just set the Y velocity to zero. I have also tried just translating the object and using Raycasts to detect the ground. Here is a real quick example you can test to demonstrate I am talking about:
1) Add another cube , make it smaller to use as the player. Add a rigidbody, disable gravity, constrain Z position and X,Y,Z rotation
2) Add a cube to use as the ground and stretch it out. Change the material to anything so you can differentiate from the player
3) Make your camera orthographic
4) Add this script to the player cube:
using UnityEngine;
using System.Collections;
public class TestBox : MonoBehaviour {
// Use this for initialization
private Rigidbody thisRigidbody;
private Transform thisTransform;
public float gravity = 500.0f;
public float upSpd = 300.0f;
private Vector3 movement = Vector3.zero;
private float rayLength;
void Start () {
thisRigidbody = GetComponent<Rigidbody>();
rayLength = collider.bounds.extents.y + 0.1f;
thisTransform = GetComponent<Transform>();
}
// Update is called once per frame
void Update ()
{
if( Input.GetMouseButtonDown(0) )
{
movement.y = upSpd;
}
if( IsGrounded() && movement.y < 0 )
movement.y = 0;
else
movement.y -= gravity * Time.deltaTime;
}
bool IsGrounded()
{
RaycastHit hit;
if (Physics.Raycast(thisTransform.position, -Vector3.up, out hit, rayLength))
{
return true;
}
return false;
}
void FixedUpdate()
{
thisRigidbody.velocity = movement;
}
}
You can adjust the gravity and upSpd depending on the scale of your objects. If you run it and click the mouse you'll see if the gravity is high enough, the dipping will occur.
I have no idea why the results are so inconsistent. The problem appears to be in the Raycast itself, where it returns true at slightly different times in code execution. The "dipping" doesn't happen when the Y speed is slow enough, but under any normal speed, the temporary dip happens.
Does anyone have ANY idea as to why this is happening? Am I going about this the wrong way? Is there a way for me to ensure that the Raycast always returns true at the right time? I don't know how the CharacterController works, but whatever it does, it seems to not suffer from this problem.
Thanks in advance!
You should consider using the forces and the built-in gravity. The main advantage is that the physics engine run several cycles between two fixed update, so that it remains (almost) always consistent.
In your case, between two updates your character can have pass through the ground, thus the dips. Setting the position/rotation of a non-kinematic rigidbody is disregarded.
Thanks for your reply. Unfortunately, I've tried using forces and built-in gravity, but the movement just isn't the same or desirable as the other methods. Surely there must be another way?
Even using forces and built-in gravity, the same issue happens. Any other solutions?
1 alternative you can do is make it use mesh colliders, and generate a mesh for things like slopes, and boxes, making a physics engine to support slopes should be easy enough
Answer by jellybit · Dec 16, 2012 at 02:38 PM
I found the answer for my particular project. Go to Edit->Project Settings->Physics. In here, you can set the Min Penetration For Penalty Force. The larger the number is, the more it will sink into objects. The smaller the number is, the less it will allow penetration. I used this in my game, and it resolves the character beautifully, however it does dip below for a single frame. Not bad though.
The only issue is that the smaller this number is, the more jitter you can experience. I haven't had that problem yet with how I've set things up, and you can always adjust the bounce threshold to try to compensate. There's more info here. Another thing you could try if that fails is to increase the scale of everything. That way, you won't likely see the issue.
Answer by AriesT · Aug 04, 2013 at 08:40 AM
As of my experience, it is not possible to have Super Mario World-like precise collision detection in Unity using the Character Controller or a Ridigbody+BoxCollider combo. Unless, you write your collision detection by yourself.
Using a Character Controller causes the object to fall off edges if you try to jump at the very edge because the CC already thinks it is falling - due to the capsule collider. Especially if you have a rather fast moving player / object. Using the standard Unity update speed causes fast objects to fall through triggers without triggering them or even falling through walls.
I never achieved a REALLY precise Super Mario World-like collision detection with Unity. It is just not possible without coding it from scratch.