Unity 5 non kinematic convex mesh collider requirement breaks ability to check materials with casting, is there a workaround?

I will paste code for a script to test this below but basically raycasting or spherecasting in Unity 4 and checking the material of the triangle that was hit worked perfectly because we were checking the hit triangle on the collider against the triangles in the mesh filter. In Unity 5 it fails because both types of casting always hit the generated convex collider triangles which don’t match the mesh filter triangles. The end result is that if you have a non kinematic RB, you are forced to enable the Convex check box which disables the ability to check materials.

That ability is critical in my game. All platforms that move or rotate are controlled with forces as is the player (which keeps the player on platforms instead of them sliding out from under his feet) and the player responds to different types of materials within a mesh filter. Example, if the material is Crawlable, the character can climb it, if the material is Ice, the player slides, Orient causes the player to orient to the surface normal. Without the ability to check the material, my game is broken. For example, this gear, the player would orient to the entire surface but only be able to climb and stick to the green parts because they have a crawlable material.

Below is the code I am using:

  1. Attach a script with the code to an
    empty and place it over a surface
    with a mesh collider and a material
    that has the word Crawlable in the
    name.
  2. Play your scene.
  3. Pressing Z does a Raycast, X a
    Spherecast. Both will return True,
    check the debug output after
    pressing Z and X
  4. Stop playing
  5. Attach a non kinematic RB to the
    surface with the collider, you are
    required to now enable convex, so
    enable convex
  6. Play your scene
  7. Press Z or X, they will both reward you with errors because they
    are hitting the auto generated
    convex collider which does not match
    your mesh filter. . .

Is there a way to get the material name of the triangle the player is above on a convex mesh collider in Unity 5? or some better way to do this? I am all ears and pretty stuck without a solution.

using System;
using System.Collections.Generic;
using UnityEngine;

namespace Assets._Code.Test
{
    public class RaycastMatCheckTest : MonoBehaviour 
    {
        private RaycastHit _hitDownward;
        private Ray _rayDownward;
        private int[] _hitTriangle;
        private int[][] _subMeshTrisJagged;
        private int[] _mTriangles;
        private Material[] _materials;
        private int[][] _subMeshTrisJaggedLvl;
        private int[] _mTrianglesLvl;
        private Material[] _materialsLvl;
        private Mesh m;
        private Mesh mLvl;
        private Mesh _mPrevious;
        private MeshFilter mf;
        private MeshFilter mfLvl;
        private Dictionary<string, bool>[] _materialsLvlKeyVal;
        private Dictionary<string, bool>[] _materialsKeyVal;
        private String[] _materialKeys;

        void Start()
        {
            _hitTriangle = new int[3];
            _materialKeys = new[]
            {
                "Crawlable",
                "Orient",
                "Parent",
                "Ice"
            };

            //Setup Lvl arrays to keep for default so they don't get GCd
            mfLvl = GameObject.FindGameObjectWithTag("Ground").GetComponent<MeshFilter>();
            mLvl = mfLvl.sharedMesh;
            _mTrianglesLvl = mLvl.triangles;
            _subMeshTrisJaggedLvl = new int[mLvl.subMeshCount][];
            _materialsLvl = mfLvl.transform.gameObject.GetComponent<Renderer>().materials;

            for (int i = 0; i < _subMeshTrisJaggedLvl.Length; i++)
            {
                _subMeshTrisJaggedLvl *= mLvl.GetTriangles(i);*

}

_materialsLvlKeyVal = new Dictionary<string, bool>[_materialsLvl.Length];

for (int j = 0; j < _materialsLvlKeyVal.Length; j++)
{
_materialsLvlKeyVal[j] = new Dictionary<string, bool>();

for (int k = 0; k < _materialKeys.Length; k++)
{
_materialsLvlKeyVal[j].Add(_materialKeys[k], _materialsLvl[j].name.Contains(_materialKeys[k]));
}
}
}

void Update()
{
if (Input.GetKeyDown(KeyCode.Z))
{
Physics.Raycast(transform.position, Vector3.down, out _hitDownward);
Debug.Log("Material is Crawlable " + CheckMaterial(_hitDownward, “Crawlable”));
}
if (Input.GetKeyDown(KeyCode.X))
{
Physics.SphereCast(transform.position, 0.25f,Vector3.down, out _hitDownward);
Debug.Log("Material is Crawlable " + CheckMaterial(_hitDownward, “Crawlable”));
}
}

bool CheckMaterial(RaycastHit hit, string stringName)
{
if (hit.collider == null || hit.collider.GetType() != typeof(MeshCollider) ||
!hit.transform.gameObject.GetComponent())
{
return false;
}

m = GetMesh(hit.transform.gameObject);

if (m == mLvl)
{
_hitTriangle[0] = _mTrianglesLvl[hit.triangleIndex * 3];
_hitTriangle[1] = _mTrianglesLvl[hit.triangleIndex * 3 + 1];
_hitTriangle[2] = _mTrianglesLvl[hit.triangleIndex * 3 + 2];

for (int i = 0; i < _subMeshTrisJaggedLvl.Length; i++)
{
for (int j = 0; j < subMeshTrisJaggedLvl*.Length; j += 3)
_
{*

if (subMeshTrisJaggedLvl*[j] == hitTriangle[0] &&
_subMeshTrisJaggedLvl[j + 1] == hitTriangle[1] &&
_subMeshTrisJaggedLvl[j + 2] == hitTriangle[2])*
{

return materialsLvlKeyVal*[stringName];*
}

}
}
}
else if (m)
{
if (m != _mPrevious)
{_

_mTriangles = m.triangles;
_subMeshTrisJagged = new int[m.subMeshCount][];
_materials = hit.transform.gameObject.GetComponent().materials;

for (int i = 0; i < _subMeshTrisJagged.Length; i++)
{
subMeshTrisJagged = m.GetTriangles(i);
}_

_materialsKeyVal = new Dictionary<string, bool>[_materials.Length];

for (int j = 0; j < _materialsKeyVal.Length; j++)
{
_materialsKeyVal[j] = new Dictionary<string, bool>();

for (int k = 0; k < _materialKeys.Length; k++)
{
_materialsKeyVal[j].Add(_materialKeys[k], _materials[j].name.Contains(_materialKeys[k]));
}
}

_mPrevious = m;
}

_hitTriangle[0] = _mTriangles[hit.triangleIndex * 3];
_hitTriangle[1] = _mTriangles[hit.triangleIndex * 3 + 1];
_hitTriangle[2] = _mTriangles[hit.triangleIndex * 3 + 2];

for (int i = 0; i < _subMeshTrisJagged.Length; i++)
{
for (int j = 0; j < subMeshTrisJagged*.Length; j += 3)*
{
if (subMeshTrisJagged*[j] == hitTriangle[0] &&
_subMeshTrisJagged[j + 1] == hitTriangle[1] &&
_subMeshTrisJagged[j + 2] == hitTriangle[2])*
{

return materialsKeyVal*[stringName];*
}

}

}
}

return false;
}

Mesh GetMesh(GameObject go)
{
if (go)
{
mf = go.GetComponent();
if (mf)
{
m = mf.sharedMesh;
if (!m) { m = mf.mesh; }
if (m)
{
return m;
}
}
}
return null;
}
}
}

Its not a solution you’re going to like, but I’m pretty sure you’re going to have to subdivide the model into models that only have 1 material each. Then you can just get the material of the renderer on the object you hit.

Or instead of making it material based, make it volume based. Add a volume to the area that is climbable, and when the player is in that area, they are climbing.