I have a plane object. This plane is fixed relative to camera (plane is camera's child and always facing the camera -- Fixed Plane).
This plane has some RenderTexture on it. Now I want to know, what UV coordinate is currently under the mouse cursor.
The following script is sitting on this plane object:
//....
_collider = gameObject.GetComponent<Collider>(); // Plane's mesh collider
//....
void FixedUpdate()
{
RaycastHit hit;
var p = UnityEngine.Input.mousePosition;
if (_collider.Raycast(Camera.main.ScreenPointToRay(p), out hit, 100f))
{
var meshCollider = hit.collider as MeshCollider;
var rend = hit.collider.GetComponent<Renderer>();
if (rend == null || rend.sharedMaterial == null || rend.sharedMaterial.mainTexture == null || meshCollider == null)
return;
var pixelUV = hit.textureCoord;
pixelUV.x *= rend.material.mainTexture.width;
pixelUV.y *= rend.material.mainTexture.height;
Debug.Log("UV=[" + hit.textureCoord.x + ";" + hit.textureCoord.y + "]" + ", XY=[" + pixelUV.x + ";" + pixelUV.y + "]");
}
}
But coordinates I see in the log are very strange. First of all, when I change aspect ratio in a viewport, point that had XY[51,466] in 16:10 becomes XY[95,464] in 4:3 and so on. Secondly, offset is so huge that I am getting UV readings even if mouse pointer is nowhere near this plane.
I can't figure out how to raycast this right.
How to correctly get these UV readings regardless of a screen size?
UPD:
I ended up ditching mouse pointer entirely. Code above actually works well if you hide cursor and just look at the collision detection. Now I am showing a really small sphere at the ray hit point, and when you move your mouse, this sphere smoothly follows plane surface: now this is my "pointer". It works really well, and even better than "real" pointer: my 3d-cursor actually follows object geometry. And as this sphere represent actual raycast hit point, precision is great.
I really like this workaround, but question is still open.
Take a look at RaycastHit.textureCoord. (Link for Reference)
You can get a RaycastHit-object using somethin like this:
...
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100))
{
//use hit.textureCoord here
}
...
Source
Hope that helps.
Related
I'm using 2D colliers in my game, and I want to use a perspective camera also. I have a raycast working in orthographic that draws a line from the player to a clicked point. It, of course, breaks when I change the camera to perspective.
I've been trying to get it to work in 2D, but without success.
I'm trying to cast a ray from the player character in the direction of the mouse position on screen, and returning the first hit 2D collider.
This is what I have so far which doesn't work. I appreciate some help.
void RaycastToCollider()
{
Ray ray = new Ray();
ray.direction = Camera.main.ScreenToViewportPoint(Input.mousePosition) - transform.position;
ray.origin = transform.position;
RaycastHit2D hit = Physics2D.GetRayIntersection(ray, Mathf.Infinity);
if (hit.collider != null)
{
Debug.DrawLine(ray.origin, hit.point);
}
}
Why ScreenToViewportPoint? This only returns a pixel position (e.g. 356 x 847) into normalized viewport coordinates that move in range 0 to 1 (e.g. 0,07 * 0,26)
Since it is purely about the direction you want to get you could go the other way round via the screen.
Also not sure but I would simply use a Raycast instead of GetRayIntersection
void RaycastToCollider()
{
// already is in screen space anyway
var mouseScreenPos = Input.mousePosition;
var transformPos = transform.position;
var transformScreenPos = Camera.main.WorldToScreenPoint(transformPos);
var direction = (Vector2)mouseScreenPos - (Vector2)transformScreenPos;
var ray = new Ray(transformPos, direction);
var hit = Physics2D.Raycast(transformPos, direction, Mathf.Infinity);
if (hit.collider != null)
{
Debug.DrawLine(ray.origin, hit.point);
}
}
Hello everyone,
I'm trying to make a transparent Cube follow the cursor except when the cursor is on the top side of an opaque Cube.
To do this I use a plane(map), parallel to the top side of the cubes. I cast 2 Rays, one of which only hits the plane and the other only hits the opaque cube. Then I compare the two raycasthit points.
If the Opaque cube is not hit or is hit at a point under the plane, the transparent cube moves.
However, when using RaycastHit.point.y to compare the two, the raycasthit of the plane returns a wrong value with RaycastHit.point.y. (Which should always be 0)
When I Log it out in the console the value in the vector object and the y coordinate which is put out are different.
The other raycast does not have this problem.
if(Physics.Raycast(ray, out RaycastHit raycastHit2, float.MaxValue, mapOverlayLayer)){
Debug.Log("r2");
Debug.Log(raycastHit2.point);
Debug.Log(raycastHit2.point.y);
if(Physics.Raycast(ray, out RaycastHit raycastHit, float.MaxValue, gameObjectLayer)){
Debug.Log("r1");
Debug.Log(raycastHit.point);
Debug.Log(raycastHit.point.y);
GameObject hitObject = raycastHit.transform.gameObject;
if(hitObject.tag == "GenericMapObject"){
if(raycastHit2.point.y <= raycastHit.point.y){
Debug.Log("Mapoverlay is lower/equal");
}else{
Debug.Log("Mapoverlay is higher");
MoveCursorCube(raycastHit2.point);
}
}
}else{
Debug.Log("Only Mapoverlay");
MoveCursorCube(raycastHit2.point);
}
}
}
I could just compare one raycast to 0 statically, but I'm still interested why this might occur. Any Ideas?
I've the point of origin readily available where my mouse is on screen like so,
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Now imagine a cube. Anywhere you click on this cube, a line is drawn from the edge clicked on, through the object, and stops at the other end. Orientation, vertical or horizontal, is determined by which side is clicked on, one of the 4 sides, or top or bottom.
How does one determine the distance (from one edge of a mesh to the other), and orientation (vertical or horizontal)?
Thoughts?
Only idea I have so far is to use collision detection and using CollisionEnter as the start point and somehow draw a line that reaches the opposite end of the mesh and using CollisionExit to determine the destination (or exit) point. Then doing some calculation to determine the distance between the Enter and Exit methods.
The only way I can think of approaching this would be to cast a ray back in the other direction....
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
//offset the ray, keeping it along the XZ plane of the hit
Vector3 offsetDirection = -hit.normal;
offsetDirection.y = 0;
//offset a long way, minimum thickness of the object
ray.origin = hit.point + offsetDirection * 100;
//point the ray back at the first hit point
ray.direction = (hit.point - ray.origin).normalized;
//raycast all, because there might be other objects in the way
RaycastHit[] hits = Physics.RaycastAll(ray);
foreach (RaycastHit h in hits)
{
if (h.collider == hit.collider)
{
h.point; //this is the point you're interested in
}
}
}
This offsets the ray to a new location so that it retains the same XZ coordinates of the original hit, so the resulting endpoints form a line that is perpendicular with the world / scene Y axis. To do this we use the camera's Forward direction (as we want to get a point farther away from the view point). If we wanted to get a point for a line that is perpendicular to the hit surface (parallel to the surface normal) we could create an offset using the hit.normal instead.
You will probably want to put a layermask or maxdist parameter into the two raycast methods (so it checks fewer things and is faster), but that's on you.
Original code: which finds the two endpoints of a "single" ray cast through the object.
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
//offset the ray along its own direction by A LOT
//at a minimum, this would be the maximum thickness of any object we care about,
//PLUS the distance away from the camera that it is
ray.origin += ray.direction * 100;
//reverse the direction of the ray so it points towards the camera
ray.direction *= -1;
//raycast all, because there might be other objects in the way
RaycastHit[] hits = Physics.RaycastAll(ray);
foreach(RaycastHit h in hits)
{
if(h.collider == hit.collider)
{
h.point; //this is the point you're interested in
}
}
}
I have a reversed mesh sphere, with the normals pointing inside, and 6 planes making a cubemap outside this sphere, algo with the normals pointing inside.
I want to know the position of a raycast hit from camera in (0,0,0), also the center of those two figures, to the sphere.
I tryed to use convex mesh collider but the sphere has to much vertex and Unity doesn´t support it. My idea was to check the collision to the cube, and then make a collision to the sphere (with a normal sphere collider) with the direction reversed from the camera.
The second Raycast should come to the point of the first collision and collide with the sphere, but it reports that "print("I'm looking at nothing!");"
public void launchBttn()
{
GameObject.FindGameObjectWithTag("coordText").GetComponent<Text>().text = transform.forward.ToString();
Ray rayBox = GetComponent<Camera>().ViewportPointToRay(transform.forward);
RaycastHit hit;
if (Physics.Raycast(rayBox, out hit))
print("I'm looking at " + hit.transform.name);
else
print("I'm looking at nothing!");
GameObject.FindGameObjectWithTag("collText").GetComponent<Text>().text = hit.transform.position.ToString();
Vector3 fwd = -transform.TransformDirection(Vector3.forward);
RaycastHit hitSphere;
if (Physics.Raycast(hit.transform.position, fwd, out hitSphere))
{
print("I'm looking at " + hitSphere.transform.name);
}
else
{
print("I'm looking at nothing!");
}
GameObject.FindGameObjectWithTag("sphereCollText").GetComponent<Text>().text = hitSphere.transform.position.ToString();
}
I can not seem to get the raycast to hit the mesh collider. I need this to get the texture uv coordinates.
if (Input.GetMouseButtonUp(0))
{
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100f))
{
// We've hit this mesh b/c its the only one we have ...
var texture = meshRenderer.material.mainTexture as Texture2D;
var pixelUV = hit.textureCoord;
print("pixs uvs" + pixelUV.ToString());
pixelUV.x *= texture.width;
pixelUV.y *= texture.height;
var position = new Position(pixelUV.x, pixelUV.y);
print("Position: " + position.x + " " + position.y);
texture.FloodFillBorder(position.x, position.y, fillColor, borderColor);
texture.Apply();
}
}
My game object has this script with the update function checking for input, as well as mesh filter, mesh renderer, and mesh collider. The mesh is a simple quad made from unity's menu.
What am I doing wrong? I just do not understand why it is not hitting the mesh. The camera's z position is -10 and the mesh is at 0. Placing differently gives the same results.
You can check the Mesh component and see if 'IsTrigger' is checked but may i suggest something else?
Unity does whole this ray casting for you by default. MonoBehaviour class has functions such as OnMouseDown() or onMouseDrag() etc. where a GUIElement or in your case an object with Collider component gets these events. So if your game object has a collider component just implement one of these methods and you are ready to go.
This may you can keep your update function clean.
Check: http://docs.unity3d.com/ScriptReference/MonoBehaviour.html