- Home /
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.
Answer by Bunny83 · Apr 18, 2012 at 12:48 PM
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;
}
}
Looks like some serious work you've done there! Thank you! Will give this a go ASAP!
Wow @Bunny83! A very nice answer! $$anonymous$$akes my link seem inferior! :D
I just thought duplicating and editing meshes in an external application is not a very fast workflow. The solution is yours, i've just automated it ;)
btw: I don't remove the vertices since they could be shared by other tirangles. Finding isolated vertices is possible but needs some more fiddling ;)
Thanks ;) Just realized that i have a newer version on my pc. I will update the script. The old one doesn't have the maxangle variable.
Answer by Kleptomaniac · Apr 06, 2012 at 02:34 PM
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
Thanks man, that's kinda what I tried but I found that my capsule collider got stuck half way :(
@demize2010 do you use a CharacterController? I've tested it with rigidbodies and it works fine. The CharacterController is not a real physics object. I haven't tested it yet but i thing it should work as well.
Answer by jasperstocker · Apr 06, 2012 at 02:04 PM
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.
Thanks man, problem is that the platforms need to provide constant projectile and NPC collision detection, so switching them on and off isn't really an option :(
what about separating out the collisions for NPC/Bullets and the player.Have one box only for the player using layers in the physics settings and the other is always on and collides with everything but the player. A bit complex but it would work.
Answer by jimmay · Oct 12, 2012 at 04:59 PM
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;
}
}
Answer by sattri99 · Dec 30, 2013 at 09:22 AM
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
Your answer
![](https://koobas.hobune.stream/wayback/20220613070006im_/https://answers.unity.com/themes/thub/images/avi.jpg)