# Determining which edge of a box was clicked in Unity

How would I go about figuring out which edge of a box is clicked in Unity?

Quick image I created, colored in boxes represent edges on a face I would like to check for.

The current solution I have is half working. I create a raycast, check if it collided with a box, get the normal from the hit (to determine which side it collided with) & check if the point meets the criteria for X, Y, or Z (in the buffer zone, as outlined in the image). For example, if the raycast intersects the forward face I check that the point is inside one of the X buffers and not in one of the y buffers.

Currently only 2 sides on the forward / backward face work, as shown below

Finally, the related code.

void forwardMethod(RaycastHit hit){
if(intersectsX(hit.point, hit.transform) && !intersectsY(hit.point, hit.transform)){

}
else if(intersectsY(hit.point, hit.transform) && !intersectsX(hit.point, hit.transform)){

}
}

bool intersectsX(Vector3 hit, Transform transform){
float buffer = transform.localScale.x * edgeBufferPercent;
return hit.x <= buffer || hit.x >= transform.localScale.x - buffer;
}

bool intersectsY(Vector3 hit, Transform transform){
float buffer = transform.localScale.y * edgeBufferPercent;
return hit.y <= buffer || hit.y >= transform.localScale.y - buffer;
}

bool intersectsZ(Vector3 hit, Transform transform){
float buffer = transform.localScale.z * edgeBufferPercent;
return hit.z <= buffer || hit.z >= transform.localScale.z - buffer;
}


The buffer is equal to .15 if anyone is interested in that as well.

Any ideas? Am I approaching this wrong or is there a bug in my code I'm not seeing?

As Twyla mentioned, RaycastHit.point returns the world-space position. In order to get the local position of the hit point on the box, you have to transform the hit point with the inverse transformation of the box. You can do this by using hit.transform.InverseTransformPoint(hit.point), where hit is the RaycastHit object.

To determine what side the ray hit, you can transform hit.normal to the local normal using hit.transform.InverseTransformDirection(hit.normal). Once you have the local normal it's easy to determine what side is hit by checking which absolute value of the three components is largest, then check whether it's positive or negative. E.g. if the local normal is (-1, 0, 0), the left side of the box is hit, if it's (0, 1, 0) it's the top side.

Once you have the local position and know the largest component in the local normal, check if the other two components in the local position are in range of the edges. If x is the largest component, you check for y and z. If y is the largest component you check for x and z, etc.

Note: the way your code looks now, it seems like your box has it's center at localScale / 2. If this is not the case, and the box is centered around (0, 0, 0), you have to check if the components lie between -localScale.xyz / 2 + edgeWidth and localScale.xyz / 2 - edgeWidth.

Kind of a shot-in-the-dark here, but......

hit.point returns the world-space position of where RaycastHit impacted the collider. Compare that against the cube's position in world-space to produce a Vector3 relative to the cube's origin, then refactor for the cube's current orientation.

Whichever component of the Vector3 has the greatest magnitude indicates which side of the cube was hit.

You could do as above then normalize the Vector3. Check for absolute values above the threshold (probably around 0.85) for the two non-face axes to see if it's a valid trigger (to determine top/bottom or left/right), then +/- to single it out.

Just my own feeble attempt at the code...

using UnityEngine;
using System.Collections;

{
private float threshold = 0.8f;

void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hitInfo = new RaycastHit();
bool hit = Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hitInfo);
if (hit)
{
//
//  Did we click on the target cube?
//
if (hitInfo.transform.gameObject.tag == "ClickTarget")
{
Vector3 clicked = (hitInfo.point - this.transform.position).normalized;
//
//  Assuming we want the positive X face
//
//  Was the click beyond the threshold?
//
if(Mathf.Abs(clicked.y) > (clicked.x * threshold))
{
if(clicked.y > 0)
{
Debug.Log( "Clicked on top" );
} else {
Debug.Log( "Clicked on bottom" );
}
} else if(Mathf.Abs(clicked.z) > (clicked.x * threshold)) {
if(clicked.z > 0)
{
Debug.Log( "Clicked on right" );
} else {
Debug.Log( "Clicked on left" );
}
}
}
}
}
}
}


(I'm sure someone more proficient could give better details, but that's how I would approach the problem.)

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;
}

• You might want to upload videos on 'public' sites like youtube, because personal shared folder are more likely to be removed or cleaned up. Answers that can stand the test of time are the best :) – Alexandre Vaillancourt May 26 '15 at 18:39