- Home /
Ray cast an area instead of a line
Hi everyone - I am reasonably new to Unity and C# but I'm finding my feet on an e-learning demo and trying to pack in as many self-objectives to learn on it to get to grips with both language and program.
The aim of the demo is to walk around and click on various items to carry out tasks. Currently I have the following code to raycast out to the mouse pointer when it is clicked and detect which object was clicked:
// test on every update if mouse has been left clicked on a gameObject
if (Input.GetMouseButtonDown(0)){ // if left button pressed...
Debug.Log("Main camera pixelWidth = "+Camera.main.pixelWidth);
Debug.Log("Main camera pixelHeight = "+Camera.main.pixelHeight);
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float distance = (this.InteractionDistance < 0) ? Mathf.Infinity : this.InteractionDistance;
if (Physics.Raycast(ray, out hit, distance)){
TransformClicked(hit.transform);
}
}
And this works. A little awkward because the user has to reposition the mouse to click, but it works.
So I decided after some play testing to change the mouseclick to a button press, using E, like on many FPS like Half Life.
if(Input.GetKeyDown("e")){ // use e click interact, more Half Life like
Debug.Log("Main camera pixelWidth = "+Camera.main.pixelWidth);
Debug.Log("Main camera pixelHeight = "+Camera.main.pixelHeight);
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(new Vector2(Screen.width/2, Screen.height/2)); //ray to centre of the screen
float distance = (this.InteractionDistance < 0) ? Mathf.Infinity : this.InteractionDistance;
if (Physics.Raycast(ray, out hit, distance)){
TransformClicked(hit.transform);
}
}
And again this works great - a ray is sent to the centre of of the screen but for me this is way to precise. What would be perfect would be if you could resize the size of the ray so instead of being a pixel or whatever it is, it was 50 x 50 or something.
Not really knowing if this was possible, I've searched online and set out to determine an area at the centre of the screen that is the "ray area" - if anything inside this area hits a gameobject then run the TransformClicked method.
So far I have worked out 4 Vector2 points on screen using
private void CreateClickPoint()
{
//work out center points for screen x and y
centerScreenX = Screen.width/2;
centerScreenY = Screen.height/2;
//work out a clickable area
xTopLeft = new Vector2(centerScreenX-30, centerScreenY-30); Debug.Log("xTopLeft = "+xTopLeft);
xTopRight = new Vector2(centerScreenX+30, centerScreenY-30); Debug.Log("xTopRight = "+xTopRight);
xBottomLeft = new Vector2(centerScreenX-30, centerScreenY+30); Debug.Log("xBottomLeft = "+xBottomLeft);
xBottomRight = new Vector2(centerScreenX+30, centerScreenY+30); Debug.Log("xBottomRight ="+xBottomRight);
}
And that's it. Is there anyway for me to use these 4 points and say When E is pressed, determine using rays if there is a game object within the area of these 4 points? Or have I missed something that is built in that is extremely obvious?
cheers for any help
Frank
Quick update - I made two changes; a bit more research (oops) and I found out about the SphereCast which does what I want in principle. http://answers.unity3d.com/questions/447305/c-more-accurate-or-larger-raycast.html and http://docs.unity3d.com/ScriptReference/Physics.SphereCast.html
I added a new class because my class $$anonymous$$ainGameLogic isn't on my gamecontroller -
public class RayCastSphere : $$anonymous$$onoBehaviour {
$$anonymous$$ainGameLogic $$anonymous$$GLConnector;
void Awake()
{
}
void Start()
{
$$anonymous$$GLConnector = new $$anonymous$$ainGameLogic();
}
void Update()
{
if(Input.Get$$anonymous$$eyDown("e")) {
RaycastHit sphereHit;
CharacterController charCtrl = GetComponent<CharacterController>();
Vector3 p1 = transform.position + charCtrl.center;
if(Physics.SphereCast(p1,2,transform.forward,out sphereHit, 10))
$$anonymous$$GLConnector.TransformClicked(sphereHit.transform);
}
}
}
But still no joy. Nothing happens when I press E, but no errors either. Any thoughts before I start to debug.log the lot?
cheers :)
Frank
Firstly, if the gameObject user wants to click, is big enough for the user to click, then ray should hit just fine, make sure that object's layer mask is not set to IgnoresRaycasts. If you really need to fire multiple rays you can use a for loop which alters the direction vector by, let's say, ±10° and use break to exit the loop if it does hit a collider. (You could use another for loop inside that if you wanna alter the direction along two axis)
Secondly, you are also using this.InteractionDistance in you code, would be nice if you could also provide the function.
I don't think there is a way to cast the ray over an area because then it might hit two objects.
Ray/Sphere/Capsule casts are tricky. An odd function (return by value AND ref,) you can't see them, lots of overloads, need to understand Vector math and local/global.
If everything else fails, just set up a simple scene with some big fat walls and a raycasting empty. $$anonymous$$ove and spin it by hand (while running) to see when/what if hits.
Answer by incorrect · Feb 12, 2015 at 05:41 AM
Like this?
using UnityEngine;
using System.Collections;
public class SphereCaster : MonoBehaviour {
public float castingRadius = 2f;
public float castingDistance = 10f;
void Update()
{
if(Input.GetKeyDown(KeyCode.E))
{
Debug.Log ("OMG! SO SPHERE! MUCH CASTING!");
RaycastHit sphereHit;
Ray ray = Camera.main.ScreenPointToRay(new Vector3(Camera.main.pixelWidth/2f, Camera.main.pixelHeight/2f, 0f));
if(Physics.SphereCast(ray, castingRadius, out sphereHit, castingDistance))
{
Debug.Log("WOW! SOMETHING HIT!");
MGLConnector.TransformClicked(sphereHit.transform);
}
}
}
}
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Change Transparency 1 Answer
How to Find Owner of a Trigger? 1 Answer