- Home /
[RESOLVED] Raycast - Calculate Angular Difference from Object
Hi. I'm a spatial memory researcher using Unity to develop a VR experiment. In the experiment, participants will navigate a maze populated with objects at random locations. I will then open an identical environment with the objects 'removed' (actually invisible), and ask them to point with the mouse reticle to a specific object; in some cases the object may be occluded by a wall (see attached 'Raycast Help').
From what I gather, there is a way to assign objects to a layer so that the raycast will ignore walls. This is great, but...
My question: Is it possible to calculate the angular difference between the ray vector and the centroid of a target object?
It is unlikely participants will aim the raycast directly on the target object, however, I still must take a measurement of their attempt, and record the angular difference between their current location and a target object.
I have some experience in Unity, but my scripting skills are not robust. I appreciate any help the community can offer. Thank you!!
Unfortunately I don't have time to give a full answer at the moment, but I'll try to respond later. In short, yes, the angle you want is the inverse cosine of the dot product between the two vectors. You would create vector A from player to object, and normalize it. Create vector B from player to mouse point (convert Input.mousePosition to world space using the Camera.main.ScreenToWorldPoint() method, but replace the world point Y value with the Object's Y value (so the vector is in the same Y plane as the player to object vector)), then normalize that. Now take $$anonymous$$athf.Acos(Vector3.Dot(vecA, vecB)) to get the angle in radians;
I guess you can raycast once the player clicks the left button to declare the object's position. The length of the raycast can be taken out from the distance between the player and the hidden object (use Vector3.Distance). If no event triggers from the raycast (which is expected), then subtract the angle between the camera's position and the hidden object's from the camera's position and the raycast's end location. Use Vector3.Angle to get the angles.
@buttmatrix Please select an answer with the Tick under the thumbs if your questions are answered and concluded.
Answer by Vasco Manuel · Apr 10, 2015 at 12:26 PM
@buttmatrix I think you can do it like this:
public GameObject player;
public GameObject target;
public Vector3 tLoc;
public float angle;
public Vector3 playerLoc;
void Update(){
tLoc = target.transform.position - this.transform.position;
tLoc.y = 0;
playerLoc = player.transform.forward;
playerLoc.y = 0
angle = Vector3.Angle(playerLoc, tLoc );
Debug.Log(angle);
}
(using @BlackWingsCorp base code)
@Vasco $$anonymous$$anuel This is brilliant! Thank you so much for your help! Storing the playerLoc separately and then setting the y = 0 was a great solution.
@Vasco $$anonymous$$anuel @BlackWingsCorp @Blackup The code you all have provided has been really helpful. Currently, I have the function being called on left mouse click, and it prints to the console. This occurs once per frame, and so about 10 (approximately) identical values are printed.
Is there a way to adjust so that the value is printed just once per click, rather than once per frame?
You can always use something like this:
if(Input.GetButtonUp("Fire1")){
Debug.Log(value);
}
@BlackWingsCorp better use Input.GetButtonUp(0)
ins$$anonymous$$d of "Fire1"
, since he mentioned left click!
@Vasco $$anonymous$$anuel "Fire1" is mapped by default to the left mouse click. A more direct approach for inputs is to get the keys ins$$anonymous$$d of buttons:
Input.Get$$anonymous$$eyUp($$anonymous$$eyCode.$$anonymous$$ouse0)
It's much easier since you know exactly what key you're using, but using them would make your scripts longer in case you want to launch your game on multiple platforms.
Answer by BlackWingsCorp · Apr 09, 2015 at 03:46 PM
Hey, one very easy way to calculate the angle using raycast is to use Vector3.Angle(), it will return the angle between 2 objects whether constants or moving.
Here's a C# example:
public GameObject player;
public GameObject target;
public Vector3 tLoc;
public float angle;
void Update(){
tLoc = target.transform.position - this.transform.position
angle = Vector3.Angle(player.transform.forward, tLoc );
Debug.Log(angle);
}
Hope this helps
@BlackWingsCorp First, your script is wonderful. Thank you so much for sharing your expertise!
I noticed that tLoc is Vector3, so if the target object is off the ground (y>0), then the angle between the participant's forward position and the target object can never approach 0. Is there a way to correct this?
I apologize for the late response. I wanted to try and solve it myself before I asked another question. Thanks again for your help!
I think I need to calculate the angle based on 'x' and 'z' only, ignoring the y parameter...?
There are a few ways of ignoring the y difference. Short answer, you could : First store the player.transform.forward in temporary Vector3 Then set the y property to 0.0f for both your temp vector3, as well as tloc. Then use these new Vector3 values in the Angle function ins$$anonymous$$d.
@Blackup Thanks a lot for your input! I think I have some idea what you are describing. I also understand there is a constructor in Vector3 "public Vector3(float x, float y);" that might work.
Before I try that, would you $$anonymous$$d providing a short example script to illustrate your method?
Answer by Blackup · Apr 11, 2015 at 10:02 AM
Based on the initial sample above... To ignore the y position you can either use Vector2 functions... Or you can use Vector3 and set the y value to zero... First I'll show you the Vector3 way...
public GameObject player;
public GameObject target;
public Vector3 pLoc;
public Vector3 tLoc;
public float angle;
void Update(){
tLoc = target.transform.position - this.transform.position
pLoc = player.transform.forward;
tLoc.y = 0.0f;
pLoc.y = 0.0f;
angle = Vector3.Angle(pLoc, tLoc );
Debug.Log(angle);
}
And then this is Vector2 way... they are similar in function, but I think the Vector2 way adds unnecessary conversions.
public GameObject player;
public GameObject target;
public Vector3 vector;
public Vector2 pLoc;
public Vector2 tLoc;
public float angle;
void Update(){
vector = target.transform.position - this.transform.position;
tLoc = new Vector2(vector.x, vector.z); // We are replacing the Y value with Z
pLoc = new Vector2(player.transform.forward.x, player.transform.forward.z);
angle = Vector2.Angle(pLoc, tLoc );
Debug.Log(angle);
}
Please take care that I have "winged" these script changes and they haven't been tested.
@Blackup, I apologize for not getting back to you sooner. I will test your code ASAP. It should be noted that Vasco $$anonymous$$anuel's method was also successful, but given that I am still learning Unity scripting, I would like to explore your method as well. Thanks so much for your help btw. It means a lot to have your expertise :)