One Way Collider

Hey guys,

I was wondering if anyone had any incite on doing a 1 way collider.

The effect we’re trying to imitate is akin to old 2D platformers - eg you can jump up onto a platform from below but when coming from on high the platform gives you collision and stops you.

We are using physics for the character control (custom actor motors) and have tried a plane collider (where collision is given based on the direction of the normals) and this works but is a bit messy - characters have a habbit to “pop” up through it at a non even rate.

Any bright chaps have an idea or two?

Muchley appreciated.

Just in addition to Kleptomaniacs answer:

I’ve just written a small script that takes the Mesh of the GameObjects MeshFilter, create a duplicate and removes all faces which facenormals have a greater angle to the upvector than maxAngle. This mesh is assigned to the MeshCollider.

Setting maxAngle to 0 will remove all faces. Setting it to 45 it will just keep the faces that points upwards and aren’t steeper than 45°

I’ve tested it with the buildin cube mesh. I’ve just created a cube object, replaced the boxcollider with a meshcollider and attached my script. Turn of the meshrenderer at runtime and select the object to see the collision mesh.

//C#
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class PlatformCollision : MonoBehaviour
{
    public float maxAngle = 45.0f;
	void Start ()
	{
        float cos = Mathf.Cos(maxAngle);
        MeshFilter MF = GetComponent<MeshFilter>();
        MeshCollider MC = GetComponent<MeshCollider>();
        if (MF == null || MC == null || MF.sharedMesh == null)
        {
            Debug.LogError("PlatformCollision needs a MeshFilter and a MeshCollider");
            return;
        }
        Mesh M = new Mesh();
        Vector3[] verts = MF.sharedMesh.vertices;
        List<int> triangles = new List<int>(MF.sharedMesh.triangles);
        for (int i = triangles.Count-1; i >=0 ; i -= 3)
        {
            Vector3 P1 = transform.TransformPoint(verts[triangles[i-2]]);
            Vector3 P2 = transform.TransformPoint(verts[triangles[i-1]]);
            Vector3 P3 = transform.TransformPoint(verts[triangles[i  ]]);
            Vector3 faceNormal = Vector3.Cross(P3-P2,P1-P2).normalized;
            if (Vector3.Dot(faceNormal, Vector3.up) <= cos)
            {
                triangles.RemoveAt(i);
                triangles.RemoveAt(i-1);
                triangles.RemoveAt(i-2);
            }
        }
        M.vertices = verts;
        M.triangles = triangles.ToArray();
        MC.sharedMesh = M;
	}
}

May be a bit of a hacky solution but this may help … there has got to be a better way to do it though …

Hope that helps, Klep

Slight modifications to Bunny83’s great example. This allows collisions from top or bottom and only requires a mesh collider. I am using this for sprites, so I set the meshCollider mesh field to use a cube.

using UnityEngine;
using System.Collections.Generic;

[RequireComponent (typeof (MeshCollider))]
public class PlatformCollision : MonoBehaviour
{
	public bool topCollision = true;
    public float maxAngle = 45.0f;
	
    void Start ()
    {
        float cos = Mathf.Cos(maxAngle);
        MeshCollider MC = GetComponent<MeshCollider>();
        if (MC == null)
        {
            Debug.LogError("PlatformCollision needs a MeshCollider");
            return;
        }
        Mesh M = new Mesh();
        Vector3[] verts = MC.sharedMesh.vertices;
        List<int> triangles = new List<int>(MC.sharedMesh.triangles);
        for (int i = triangles.Count-1; i >=0 ; i -= 3)
        {
            Vector3 P1 = transform.TransformPoint(verts[triangles[i-2]]);
            Vector3 P2 = transform.TransformPoint(verts[triangles[i-1]]);
            Vector3 P3 = transform.TransformPoint(verts[triangles[i  ]]);
            Vector3 faceNormal = Vector3.Cross(P3-P2,P1-P2).normalized;
            if ( (topCollision && Vector3.Dot(faceNormal, Vector3.up) <= cos) ||
				 (!topCollision && Vector3.Dot(faceNormal, -Vector3.up) <= cos) )
            {
                triangles.RemoveAt(i);
                triangles.RemoveAt(i-1);
                triangles.RemoveAt(i-2);
            }
        }
        M.vertices = verts;
        M.triangles = triangles.ToArray();
        MC.sharedMesh = M;
    }
}

I’ve made a tutorial recently of how to make efficient one way collider for d game with source code (project folder) on my physicist3d.blogspot.com . It’s the best way of making effeicient one way collided. Link to my tutorial —
link text

I think your best option is to disable the platform when your character is below it and enable it only when you know he is able to stand on it.
You could use his Y coordinate to work out if he is high enough. A really easy solution if your character origin is at his feet - you can test the point is above the platform collider box.
Or use a capsule collider trigger to test if it’s above and not inside the platform.