Writing a C# script that allow player to construct with simple blocks. For this reason, I strike a raycast forward from playercam. When ray hit some object - I get the collison world coords with hit.point
If I Instantiate a constructing block on that coordinates - it will be created overlapping with other objects. I have to change coords.
How can I get the point that lay as shown in the picture above? This will allow me to calculate created blocks' coordinates
You can take the RaycastHit's point property and move out along the intersected face's normal to your block's extent (half its width; if it's a unit cube, this will be 0.5f); something like this:
if (Input.GetMouseButtonDown (0)) {
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast (ray, out hit)) {
Instantiate (prefab, hit.point + hit.normal * blockExtent, hit.transform.rotation);
}
}
This will instantiate your new block at the raycast's intersection point (you'd need to calculate the center of the intersected face if you wanted them to align precisely), and inherit the rotation of the intersected GameObject.
Note that this won't prevent a new block from spawning inside an existing block; it will just keep it from spawning inside the block that you raycasted against.
Related
I have my editor camera and I'd like to spawn objects where it is looking, as well as it is done by unity when primitives are Instantiated.
As I understand the title of your questions you want to raycast from the scene view camera against the global XZ axis which passes through the world origin and place an object at the hit position.
To find the "editor" or better said the SceneView camera you can use SceneView.camera.
var camera = SceneView.camera;
Then for creating a ray from it use either Camera.ScreenPointToRay e.g. if you want to take the mouse position into account
var ray = camera.ScreenPointToRay(Input.mousePosition);
or simply the cameras Transform.forward vector
var ray = new Ray(camera.transform.position, camera.transform.forward);
Or alternatively if you are calling this from within Editor.OnSceneGUI you could also use HandleUtility.GUIPointToWorldRay
var ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
Then for raycasting against the global XZ plane you can use a mathematical Plane
// Creates a XZ plane going through world origin
var plane = new Plane(Vector3.up, Vector3.zero);
Then you can raycast against it using Plane.Raycast
if(plane.Raycast(ray, out var distance)
{
var hitPoint = ray.GetPoint(distance);
// Spawn your object and set its position to hitPoint
}
I found the solution to my problem but in the end I don't need to do that.
To answer to your questions :
Please be more specific on what exactly you try to achieve .. e.g. spawn in front of > exactly which camera? And "in front" how far away?
I needed to instantiate my robot Prefab at the intersection of the XZ plane and the Raycast shot from the editor's view camera no matter how far it is.
And yeah I managed to make it work as Hugo saidin his previous answer.
So in my Unity exercise, I have to rotate a GameObject to face away from where I clicked my mouse. Also, I can only rotate around the Y-axis i.e. the GameObject is only allowed to rotate either purely to the right or purely to the left, and cannot tip towards the ground at any point. Also, I've got to do this without RayCasting (I've already done it WITH RayCasting, so as an exercise, I've got to do it without). Here's the code I've written after multiple attempts but it doesn't seem to be effective enough:
Vector3 clickLocation = Input.mousePosition;
Vector3 clickWorldLocation = Camera.main.ScreenToWorldPoint(new Vector3(clickLocation.x, clickLocation.y, transform.position.x)); //the transform.position.x is just to add some depth
transform.LookAt(new Vector3(clickWorldLocation.x, transform.position.y, clickWorldLocation.z), Vector3.up);
This code works fine if my GameObject remains in its starting position, but fails if I move it to another location and attempt the same action. Could someone help me out please?
First, call Camera.main as infrequently as you can, as it uses an expensive operation (FindGameObjectsWithTag) and doesn't cache the result.
private Camera mainCamera;
void Awake()
{
mainCamera = Camera.main;
}
To answer your question, don't use ScreenToWorldPoint in this situation, because a depth isn't most easily calculated. You don't have anything meaningful to use for the z component, and transform.position.x makes no sense here.
Instead, create a Plane that you want to click into (such as the plane parallel to the view plane and intersecting the object), use ScreenPointToRay to turn the mouse position into a Ray, then find where the ray and plane intersect. Plane.Raycast is much, much faster than a physics raycast. Then, find the direction from the mouse world position to the object, and use Quaternion.LookRotation to turn that direction into a rotation you can assign to the object's rotation:
Ray mouseRay = mainCamera.ScreenPointToRay(Input.mousePosition);
Plane mousePlane = new Plane(mainCamera.transform.forward, transform.position);
float rayDistance;
if (mousePlane.Raycast(mouseRay, out rayDistance))
{
Vector3 mouseWorldPos = mouseRay.GetPoint(rayDistance);
// make the z component of mouseWorldPos the same as transform.position
mouseWorldPos.z = transform.position.z;
Vector3 awayFromMouseDir = transform.position - mouseWorldPos;
transform.rotation = Quaternion.LookRotation(awayFromMouseDir, Vector3.up);
}
When the mouse is at the Door (red area) i want to do something. I am trying to cast a Ray but the Ray doesn't hit the Door and i cant find where exactly it is hitting. Also how can i Debug.DrawRay this ray?
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, Mathf.Infinity))
{
if (hit.collider.tag == "InteractiveDoor")
{
doorInteractGameObject.SetActive(true);
}
else
{
doorInteractGameObject.SetActive(false);
}
}
You might need to create a LayerMask and add it to the parameters for Physics.Raycast; also make sure that the door has a collider on it. You would be surprised how many times I just forgot to add a collider.
In terms of drawing the ray in case this is not working for some reason. Use Debug.DrawRay. I recommend putting it in FixedUpdate() or Update(). Color and duration you can set to whatever you want but I recommend having depthTest be false since you want the ray to be drawn regardless. Then call it like:
Debug.DrawRay(ray.origin, ray.direction, <color>, <duration>, false);
You can draw this ray as:
Debug.DrawRay(ray.origin, ray.direction);
or
Debug.DrawRay(Camera.main.transform.position, Camera.main.ScreenPointToRay(Input.mousePosition).direction);
Option 1 is more direct once you've defined your ray, but option 2 gives you more choice to play around if it turns out this Ray doesn't behave the way you expect it to.
camera.ScreenPointToRay expects a Vector3 but Input.mousePosition returns a Vector2. The Unity docs on Vector3 and Vector2 appear that you should be able to use Vector2 as Vector3 implicitely and it'll populate the z component as 0, but if the Debug.DrawRay shows that the ray is the issue, then you need to append a 0. Maybe something like:
Ray ray = Camera.main.ScreenPointToRay(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
The fix is to go to your camera and set the tag to 'MainCamera'. Or you can modify your code to have a camera variable and use that variable instead of Camera.main.
I'm trying to detect the ground ahead of a cube that doens't have boxcollider or rigid body. I can detect it ok but the distance never changes. I can use very big number or very small ones that it still detects the ground at the same distance.
The debug lines are working fine and nothing is detected before the ground. Even trying raycast without distance keeps the detection at the same distance.
I tried some other variations of the method Raycast with the same results.
Here is my script:
Ray ray = new Ray(transform.position, transform.forward);
RaycastHit hitInfo;
Debug.DrawRay(pos, transform.forward * detectDistance, Color.green);
if (Physics.Raycast(ray,out hitInfo, detectDistance)) {
Debug.Log(hitInfo.transform.name);
Debug.DrawRay(pos, transform.forward * detectDistance, Color.red);
}
If your main goal is to calculate the distance between two GameObjects (cube and ground), the use of Raycast is not a must. You can follow this other approach:
Vector3 dist = Vector.distance(cube.transform.position, ground.transform.position);
Debug.Log(string.Format("Distance between {0} and {1} is: {2}", cube, ground, dist));
However if you want to keep with the Raycast, without having all the information about how is your scene and how are the gameObjects located, I can only recommend you to try the following things:
1- Calculate the direction of the Ray like this:
Vector3 direction = ( cube.transform.position - ground.transform.position ).normalized;
Ray ray = new Ray( cube.transform.position, direction );
2- Calculate the distance from hit:
if (Physics.Raycast(downRay, out hit)) {
float distance = hit.distance;
}
3- And just in case calculate the distance from void FixedUpdate()
The problem was the other terrain chunks wasn't generating the collider mesh till you get really close to it.
I did find a way to generate it far away.
I have an object with circle collider 2d, and an object with box collider 2d. how do i detect when the circlecollider hits the top edge of the box collider. assuming none objects have any rotation. i want to do this in code C#
OnCollisionEnter triggers when something collides with the object containing this script, used like this:
void OnCollisionEnter(Collider collider)
{
foreach(CollisionPoint contact in collider.contacts)
{
//Do something
}
}
The above will give you a list of contact points for the collision, via which you should be able to determine where the collision occurred. An alternative method if you only need to detect collisions at a specified location, you could place an invisible child object within your cube, with a collider at the spot you want.
Edit:
Since you mentioned raycasts, there's 2 ways I can think of that these could be implemented. The first is to fire it upwards from the cube, but this has the issue of the ray firing from just 1 point, meaning some collisions might be missed (depending on the size of the cube & sphere).
The second method that pops to mind is to fire the ray parallel to the cube. This might sound a bit unorthodox, and I haven't tested it, but in theory it should work. Stick it in your cube:
void Update
{
Vector3 start = this.transform.position;
Vector3 end= this.transform.position;
//This attempts to place the start & end point just above the cube
//This of course assumes the cube isn't rolling around. If that's the case
//then these calculations get quite a bit more complicated
//Additionally the 0.01 might need adjusting if it's too high up off the cube
start.y += this.renderer.bounds.y/2 + 0.01f;
end.y += this.renderer.bounds.y/2 + 0.01f;
start.x -= this.renderer.bounds.x/2;
end.x += this.renderer.bounds.x/2;
Ray ray = new Ray(start, end);
RaycastHit hit;
if(Physics.Raycast(ray, out hit, start.x-end.x) && hit.name == "mySphere")
{
//Theoretically, the sphere hit the top of the box!
}
}