- Home /
Made a new system
Find edge/surface/ledge of mesh
Hello, I've been messing around with my character & i need to find out a way to detect if there's an edge/surface/ledge so the character can grab on to it.
Take the image below for example
I would like to detect the areas that are lined red & that are an edge/surface using C# so the character can grab on to it.
Any help would be appreciated, thanks! - Jabez
Is this for a render effect or do you need them for script logic?
Is this intended for character climbing?
Seems like it may be for base building?
ie need only certain objects to snap at certain locations
Thanks for the replies. Tis for character climbing, I need it to be dynamic.
If you can access the triangles, you can say that there's an edge between two triangles if the triangles are adjacent and the normal vectors of those triangles differ by a certain angle. The normal vector of a triangle can be obtained by taking the cross product of two of its sides as vectors, then normalizing.
If you need to detect if a triangle faces up, you can compare the normal of the triangle with Vector3.up
and get the angle between them.
I think using invisible colliders would be easier though.
@_Gkxd Didn't think of that when I tried! I remember all I did was I got the vertecies of the mesh the player hit, and then checked for vertecies with equal values on exactly two vectors, but that didn't work and I got bored so... This question has resparked my interest in this, I know what I will be doing tomorrow! Hope somebody has a complete answer, though! GL guys
Answer by Lahzar · Jul 11, 2015 at 07:33 PM
So I have been trying to do something with this all day long. And I managed to get the quad that the player hits, get the top edge of it and check if it is climable. I think it only works with perfect quads, and its not very good at all, but it works. So its far from perfect but its a good base for you to start building on!
using UnityEngine;
using System.Collections;
//using System.Collections.Generic;
//using System.Threading;
//TODO Clean up stuff. Fix bug and tweak stuff. Optimize stuff. Animation support. Add climbing support. Make usable for non-perfect quads. Find all edges within reach instead of just the closest.
//Made in a day by Imre Angelo aka Lahzar @ the unity forums - Enjoy!
public class RunFree3o : MonoBehaviour {
public float reach = 3; //TODO Check from players arms, legs etc
private Vector3[] quad;
private Vector3[] top;
void Start() {
quad = new Vector3[4];
}
void Update() {
if(top[0] != new Vector3())
Debug.DrawLine(top[0], top[1], Color.green, 1);
}
void OnControllerColliderHit(ControllerColliderHit col) {
RaycastHit hit;
Debug.DrawRay(col.point, col.moveDirection, Color.blue, 1);
if(Physics.Raycast(transform.position, col.moveDirection, out hit, 0.5f)) {
if(hit.normal.y == transform.position.y-(GetComponent<CharacterController>().height/2))
return;
MeshCollider meshCollider = hit.collider as MeshCollider;
if (meshCollider == null || meshCollider.sharedMesh == null)
return;
Mesh mesh = meshCollider.sharedMesh;
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
Transform hitTransform = hit.collider.transform;
StartCoroutine(CoroutineThing(mesh, hitTransform, vertices, triangles, hit.triangleIndex));
}
}
IEnumerator CoroutineThing(Mesh mesh, Transform hitTransform, Vector3[] vertices, int[] triangles, int triangleIndex) {
#region Get hit triangle
Vector3 p0 = vertices[triangles[triangleIndex * 3 + 0]];
Vector3 p1 = vertices[triangles[triangleIndex * 3 + 1]];
Vector3 p2 = vertices[triangles[triangleIndex * 3 + 2]];
p0 = hitTransform.TransformPoint(p0);
p1 = hitTransform.TransformPoint(p1);
p2 = hitTransform.TransformPoint(p2);
#endregion
Debug.DrawLine(p0, p1, Color.blue, 4);
Debug.DrawLine(p1, p2, Color.blue, 4);
Debug.DrawLine(p2, p0, Color.blue, 4);
#region Get last triangle
Vector3 p3 = new Vector3();
for(int i = 10; i > -10; i--) {
Vector3 p4 = new Vector3();
if(vertices[triangles[triangleIndex * 3 + i]] == null)
continue;
try {
p4 = hitTransform.TransformPoint(vertices[triangles[triangleIndex * 3 + i]]);
} catch (System.Exception ex) {
ex.Source = "ignore";
continue;
}
if(p4 != p0 && p4 != p1 && p4 != p2) {
int ex = 0, wai = 0, zed = 0;
if(Mathf.FloorToInt(p4.x) == Mathf.FloorToInt(p0.x)) { ex++; }
if(Mathf.FloorToInt(p4.y) == Mathf.FloorToInt(p0.y)) { wai++; }
if(Mathf.FloorToInt(p4.z) == Mathf.FloorToInt(p0.z)) { zed++; }
if(Mathf.FloorToInt(p4.x) == Mathf.FloorToInt(p1.x)) { ex++; }
if(Mathf.FloorToInt(p4.y) == Mathf.FloorToInt(p1.y)) { wai++; }
if(Mathf.FloorToInt(p4.z) == Mathf.FloorToInt(p1.z)) { zed++; }
if(Mathf.FloorToInt(p4.x) == Mathf.FloorToInt(p2.x)) { ex++; }
if(Mathf.FloorToInt(p4.y) == Mathf.FloorToInt(p2.y)) { wai++; }
if(Mathf.FloorToInt(p4.z) == Mathf.FloorToInt(p2.z)) { zed++; }
if(ex+wai+zed == 5) {
if(wai == 1) {
p3 = p4;
break;
}
}
}
}
#endregion
Debug.DrawLine(p0, p3, Color.red, 4);
Debug.DrawLine(p1, p3, Color.red, 4);
Debug.DrawLine(p2, p3, Color.red, 4);
if(p3 != new Vector3()) {
quad = new Vector3[4];
quad[3] = p3;
}
quad[0] = p0;
quad[1] = p1;
quad[2] = p2;
StartCoroutine(CheckMoves());
yield return null;
}
IEnumerator CheckMoves() {
top = new Vector3[2];
foreach(Vector3 v in quad) {
if(v == new Vector3())
continue;
if(Mathf.FloorToInt(v.y) == Mathf.FloorToInt(top[0].y) && v.y > top[1].y) {
top[1] = top[0];
top[0] = v;
} else if(v.y > top[0].y) {
top[1] = top[0];
top[0] = v;
}
}
if(Vector3.Distance(transform.position, (top[0]+top[1])/2) <= reach) { //This code block doesn't always work!
if(!Physics.CheckCapsule((top[0]+top[1])/2, (top[0]+top[1])/2+Vector3.up*2, 0.5f) && !Physics.Raycast((top[0]+top[1])/2+Vector3.up*2, Vector3.down, 1.95f)) {
if(Physics.Raycast((top[0]+top[1])/2+Vector3.up*2+transform.forward/2, Vector3.down, 2.1f)) {
transform.position = (top[0]+top[1])/2+Vector3.up*2;
}
}
}
yield return null;
}
}
First it gets the triangle that the players controllercollider hits. Then it Tries to figure out which vertex is the needed vertex to find the adjacent triangle and thus the whole quad. Now that its sure there is a level top edge, it finds the highest edge. Then it checks if the standard unity FPS controller can fit ontop of said edge! That last part is buggy but it works okay. Try it for yourself!
The big thing to do next is get all edges within reach, and not just the ones that are part of the quad hit by ControllerColliderHit. Also your mesh HAS to be read/write enabled I think!
This script actually works wonders C:, Thanks for sharing. I will edit it to suit my needs & send you copies of it :D. Thanks for your help.
Answer by pgomes · Jul 10, 2015 at 08:08 PM
Since the mesh is going to be dynamic, I suggest detecting the edge on the fly with casts. See the following picture:
You do a sphere cast (or horizontal raycast alternatively) above the character to detect if there is no vertical surface to climb to. You do a vertical raycast above and forward to detect if it is an actual edge. I used this technique successfully for a similar problem.
This approach does assume that you are generating/updating a collider mesh that matches the rendered mesh, or a proxy for that mesh (e.g. set of box colliders). If you have control over the colliders you might also want to separate the vertical and horizontal ones and tagged them differently for more specific checks.
Thanks for this information i actually saw a video that used a similar technique, I'll try it out a bit later when i get time & let you know how it goes :D.
Answer by PixelSpartan · Jul 10, 2015 at 06:19 PM
Put invisible trigger colliders
Thanks for the suggestion but my meshes are procedural so would not be possible. This would work otherwise.
So just procedurally-generate the colliders as you procedurally-generate the mesh...
The only thing is to generate the colliders i need to know where the edge/ledge/surface is & that's why i'm asking this question. Also, i'm not asking how to add colliders or anything to do with colliders, i'm asking how to find the edge/surface/ledge of a mesh.
And I'm suggesting that, if your mesh is generated procedurally, the edges can be deter$$anonymous$$ed entirely as a product of the procedure, no?
What is your starting data structure/description of the mesh?
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
How to remove white blinking edge on mesh? 1 Answer
Executing code on touching edges of a collider from the inside 0 Answers
Spawned Object Gradient Mesh Colors? 2 Answers