Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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

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.

Example of collision check

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

Working edges

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?

share|improve this question

2 Answers 2

up vote 1 down vote accepted

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.

share|improve this answer

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.

Edit: Misread the question

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;

public class BoxMenu : MonoBehaviour 
{
    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.)

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.