Does anyone know a script where you can detect the face of an cube clicked and perform an action on it?
Or can anyone write one that does that?
I’m guessing it’s something to do with Raycasting.
Thanks
Does anyone know a script where you can detect the face of an cube clicked and perform an action on it?
Or can anyone write one that does that?
I’m guessing it’s something to do with Raycasting.
Thanks
You can detect the side of the cube via the following algo
public enum MCFace
{
None,
Up,
Down,
East,
West,
North,
South
}
public MCFace GetHitFace(RaycastHit hit)
{
Vector3 incomingVec = hit.normal - Vector3.up;
if (incomingVec == new Vector3(0, -1, -1))
return MCFace.South;
if (incomingVec == new Vector3(0, -1, 1))
return MCFace.North;
if (incomingVec == new Vector3(0, 0, 0))
return MCFace.Up;
if (incomingVec == new Vector3(1, 1, 1))
return MCFace.Down;
if (incomingVec == new Vector3(-1, -1, 0))
return MCFace.West;
if (incomingVec == new Vector3(1, -1, 0))
return MCFace.East;
return MCFace.None;
}
Demo Video Bellow:
https://dl.dropboxusercontent.com/u/53593879/ShareX/2015/05/2015-05-26_20-20-16.mp4
Just some things you might need:
Meshes don’t have faces (from techincal view) they are made of triangles. So a cube-side is made of 2 triangles.
Using Raycasting: (I am also using a Mesh collider on a cube as opposed to the original box collider)… Hopefully this will save from the guessing game i had to go through. Remember though, this will only work for normal cubes of any size.
if (triIndex == 0 || triIndex == 1) {
Debug.Log ("Creating On: front - Of: " + myhit.collider.gameObject.name);
clone = Instantiate(toCreate, new Vector3 (xRndd, yRndd, (zRndd+1)), Quaternion.identity) as GameObject;
}
if (triIndex == 2 || triIndex == 3) {
Debug.Log ("Creating On: top - Of: " + myhit.collider.gameObject.name);
clone = Instantiate(toCreate, new Vector3 (xRndd, (yRndd+1), zRndd), Quaternion.identity) as GameObject;
}
if (triIndex == 4 || triIndex == 5) {
Debug.Log ("Creating On: back - Of: " + myhit.collider.gameObject.name);
clone = Instantiate(toCreate, new Vector3 (xRndd, yRndd, (zRndd-1)), Quaternion.identity) as GameObject;
}
if (triIndex == 6 || triIndex == 7) {
Debug.Log ("Creating On: bottom - Of: " + myhit.collider.gameObject.name);
clone = Instantiate(toCreate, new Vector3 (xRndd, (yRndd-1), zRndd), Quaternion.identity) as GameObject;
}
if (triIndex == 8 || triIndex == 9) {
Debug.Log ("Creating On: right - Of: " + myhit.collider.gameObject.name);
clone = Instantiate(toCreate, new Vector3 ((xRndd-1), yRndd, zRndd), Quaternion.identity) as GameObject;
}
if (triIndex == 10 || triIndex == 11) {
Debug.Log ("Creating On: left - Of: " + myhit.collider.gameObject.name);
clone = Instantiate(toCreate, new Vector3 ((xRndd+1), yRndd, zRndd), Quaternion.identity) as GameObject;
}
Here is another example of using the following game object format
gameobject
side0
side1
side_n
It combines meshes for shared materials. Built in face detection code with simple API
Initialise() Setup shared material for all sides
SetSharedMaterial() The material that will be used for the shared material
AddSelectables() Array of game objects e.g. side0, side1 which are selectable faces
RemoveSelectables() Array of game objects which are deselected faces
SelectableFaces() Returns the selectable faces
DetectFace() Takes a raycast and mesh collider from some input manager and returns the gameobject hit
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FaceSelection : MonoBehaviour
{
private List<MeshFilter> _combinedMeshFilters = new List<MeshFilter>();
private Material _sharedMaterial = null;
// Use this for initialization
void Start ()
{
Initialize();
}
/*
void Update()
{
if (Input.GetKeyDown(KeyCode.Alpha1))
AddSelectables(new GameObject[] { GameObject.Find("Barrel_6Sided_5Mat/Side") });
if (Input.GetKeyDown(KeyCode.Alpha2))
AddSelectables(new GameObject[] { GameObject.Find("Barrel_6Sided_5Mat/Top") });
if (Input.GetKeyDown(KeyCode.Alpha3))
AddSelectables(new GameObject[] { GameObject.Find("Barrel_6Sided_5Mat/BevelTop") });
if (Input.GetKeyDown(KeyCode.Alpha4))
RemoveSelectables(new GameObject[] { GameObject.Find("Barrel_6Sided_5Mat/Side") });
if (Input.GetKeyDown(KeyCode.Alpha5))
RemoveSelectables(new GameObject[] { GameObject.Find("Barrel_6Sided_5Mat/Top") });
if (Input.GetKeyDown(KeyCode.Alpha6))
RemoveSelectables(new GameObject[] { GameObject.Find("Barrel_6Sided_5Mat/BevelTop") });
/*
if (Input.GetKeyDown(KeyCode.Alpha2))
AddSelectables(new GameObject[] { GameObject.Find("Cube/Back") });
if (Input.GetKeyDown(KeyCode.Alpha3))
AddSelectables(new GameObject[] { GameObject.Find("Cube/Top") });
if (Input.GetKeyDown(KeyCode.Alpha4))
AddSelectables(new GameObject[] { GameObject.Find("Cube/Bottom") });
if (Input.GetKeyDown(KeyCode.Alpha5))
AddSelectables(new GameObject[] { GameObject.Find("Cube/Left") });
if (Input.GetKeyDown(KeyCode.Alpha6))
AddSelectables(new GameObject[] { GameObject.Find("Cube/Right") });
if (Input.GetKeyDown(KeyCode.Alpha7))
RemoveSelectables(new GameObject[] { GameObject.Find("Cube/Front") });
if (Input.GetKeyDown(KeyCode.Alpha8))
RemoveSelectables(new GameObject[] { GameObject.Find("Cube/Back") });
if (Input.GetKeyDown(KeyCode.Alpha9))
RemoveSelectables(new GameObject[] { GameObject.Find("Cube/Top") });
if (Input.GetKeyDown(KeyCode.Alpha0))
RemoveSelectables(new GameObject[] { GameObject.Find("Cube/Bottom") });
if (Input.GetKeyDown(KeyCode.Minus))
RemoveSelectables(new GameObject[] { GameObject.Find("Cube/Left") });
if (Input.GetKeyDown(KeyCode.Equals))
RemoveSelectables(new GameObject[] { GameObject.Find("Cube/Right") });
}
*/
/// <summary>
/// Initialize all faces to a shared material
/// </summary>
public void Initialize()
{
_combinedMeshFilters.Clear();
foreach (Transform t in gameObject.transform)
if (t.GetComponent<MeshRenderer>() != null)
_combinedMeshFilters.Add(t.gameObject.GetComponent<MeshFilter>());
if (_sharedMaterial == null)
{
_sharedMaterial = new Material(Shader.Find("Standard"));
_sharedMaterial.color = Color.red;
}
CombineMeshes(gameObject);
}
/// <summary>
/// Set the shared material
/// </summary>
public void SetSharedMaterial(Material material)
{
_sharedMaterial = material;
CombineMeshes(gameObject);
}
public GameObject DetectFace(RaycastHit hit, MeshCollider meshCollider)
{
Transform[] transforms = gameObject.GetComponentsInChildren<Transform>();
for (int i=0; i< transforms.Length; i++)
{
if (hit.collider.gameObject == transforms*.gameObject)*
{
Debug.Log("Object Hit = " + transforms*.gameObject.name + " Tri Index = " + hit.triangleIndex);*
return transforms*.gameObject;*
}
}
/*
Mesh mesh = meshCollider.sharedMesh;
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
Vector3 p0 = vertices[triangles[hit.triangleIndex * 3 + 0]];
Vector3 p1 = vertices[triangles[hit.triangleIndex * 3 + 1]];
Vector3 p2 = vertices[triangles[hit.triangleIndex * 3 + 2]];
Transform hitTransform = hit.collider.transform;
p0 = hitTransform.TransformPoint(p0);
p1 = hitTransform.TransformPoint(p1);
p2 = hitTransform.TransformPoint(p2);
Debug.DrawLine(p0, p1);
Debug.DrawLine(p1, p2);
Debug.DrawLine(p2, p0);*/
return null;
}
// Add selectable face objects
public void AddSelectables(GameObject[] obj)
{
bool combine = false;
for (int i = 0; i < obj.Length; i++)
{
MeshRenderer meshRenderer = obj*.GetComponent();*
if (meshRenderer != null)
{
if (!meshRenderer.enabled)
{
MeshFilter meshFilter = obj*.GetComponent();*
if (_combinedMeshFilters.Contains(meshFilter))
{
_combinedMeshFilters.Remove(meshFilter);
combine = true;
}
meshRenderer.enabled = true;
meshRenderer.material = new Material(Shader.Find(“Standard”));
MeshCollider meshCollider = obj*.GetComponent();*
if (meshCollider == null)
obj*.AddComponent();*
}
}
}
if (combine)
CombineMeshes(gameObject);
}
///
for (int i = 0; i < obj.Length; i++)
{
MeshRenderer meshRenderer = obj*.GetComponent();*
if (meshRenderer != null)
{
if (meshRenderer.enabled)
{
_combinedMeshFilters.Add(obj*.GetComponent());*
MeshCollider meshCollider = obj*.GetComponent();*
if (meshCollider != null)
GameObject.DestroyImmediate(meshCollider);
combine = true;
}
}
}
if (combine)
CombineMeshes(gameObject);
}
///
foreach (Transform t in gameObject.transform)
if (t.GetComponent() != null)
objs.Add(t.gameObject);
return objs.ToArray();
}
///
if (obj.GetComponent() != null)
GameObject.DestroyImmediate(obj.GetComponent());
return;
}
// Zero transformation is needed because of localToWorldMatrix transform
Vector3 position = obj.transform.position;
obj.transform.position = Vector3.zero;
CombineInstance[] combine = new CombineInstance[_combinedMeshFilters.Count];
int i = 0;
while (i < _combinedMeshFilters.Count)
{
combine.mesh = combinedMeshFilters*.sharedMesh;
combine.transform = _combinedMeshFilters.transform.localToWorldMatrix;
_combinedMeshFilters.gameObject.GetComponent().enabled = false;*
i++;
}
MeshFilter meshFilter = obj.transform.GetComponent();
if (meshFilter == null)
meshFilter = obj.AddComponent();
obj.transform.GetComponent().mesh = new Mesh();
obj.transform.GetComponent().mesh.CombineMeshes(combine, true, true);
obj.transform.gameObject.SetActive(true);_
// Add Mesh Renderer
MeshRenderer meshRenderer = obj.transform.GetComponent();
if (meshRenderer == null)
meshRenderer = obj.AddComponent();
// Add Mesh Collider
MeshCollider meshCollider = obj.transform.GetComponent();
if (meshRenderer != null)
GameObject.DestroyImmediate(meshCollider);
obj.AddComponent();
// Assign the Shared Material
meshRenderer.sharedMaterial = _sharedMaterial;
//Reset position
obj.transform.position = position;
}
}
I know this topic is a bit old, but here’s my answer on this one. First, I define an array containing the six main normal vectors for a cube’s facets.
static Vector3[] sides = {
Vector3.down,
Vector3.forward,
Vector3.left,
Vector3.right,
Vector3.back,
Vector3.up
};
You can then use this code to detect which side was intersected by a given RaycastHit:
public int FaceIntersectingHit(RaycastHit hit) {
var result = new DieFace();
// RaycastHits are in world space; convert to local space - note that we
// normalize the result too because scale can affect the transformed vector
Vector3 localNormal = transform.InverseTransformVector(hit.normal).normalized;
// There may be rounding errors in the result, so round to the nearest integer
Vector3 incoming = new Vector3(
Mathf.Round(localNormal.x),
Mathf.Round(localNormal.y),
Mathf.Round(localNormal.z)
);
// Compare against our list of faces to see which is hit
for (int i = 0; i < sides.Length; i++) {
var side = sides[i];
if (incoming == side) {
return i+1;
}
}
return 0;
}
Note that my method returns an integer based on the array but adds one to its value, because I’m using this to detect the facet of a six-sided die.