How to use Graphic Raycaster with WorldSpace UI? - c#

I'm trying to figure out how Graphic.Raycaster works, but documentation doesn't help. I want to use it to cast raycast from some position at a certain angle and hit the UI. The other thing is that I don't know how to make it interact with the UI(drag, click etc.). I know that's broad subject, but I just can't find any good explanation of how to use it, so I would be grateful for any explanation.

From Unity docs:
The Graphic Raycaster is used to raycast against a Canvas. The
Raycaster looks at all Graphics on the canvas and determines if any of
them have been hit.
You can use EventSystem.RaycastAll to raycast against graphics(UI) elements.
Here is a short example for your case:
void Update() {
// Example: get controller's current orientation:
Quaternion ori = GvrController.Orientation;
// If you want a vector that points in the direction of the controller
// you can just multiply this quat by Vector3.forward:
Vector3 vector = ori * Vector3.forward;
// ...or you can just change the rotation of some entity on your scene
// (e.g. the player's arm) to match the controller's orientation
playerArmObject.transform.localRotation = ori;
// Example: check if touchpad was just touched
if (GvrController.TouchDown) {
// Do something.
// TouchDown is true for 1 frame after touchpad is touched.
PointerEventData pointerData = new PointerEventData(EventSystem.current);
pointerData.position = Input.mousePosition; // use the position from controller as start of raycast instead of mousePosition.
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointerData, results);
if (results.Count > 0) {
//WorldUI is my layer name
if (results[0].gameObject.layer == LayerMask.NameToLayer("WorldUI")){
string dbg = "Root Element: {0} \n GrandChild Element: {1}";
Debug.Log(string.Format(dbg, results[results.Count-1].gameObject.name,results[0].gameObject.name));
//Debug.Log("Root Element: "+results[results.Count-1].gameObject.name);
//Debug.Log("GrandChild Element: "+results[0].gameObject.name);
results.Clear();
}
}
}
The above script is not tested by myself. So there might be some errors.
Here are some other references to help you understand more:
Graphics Raycaster of Unity; How does it work?
Raycast against UI in world space
How to raycast against uGUI objects from an arbitrary screen/canvas position
How do you perform a Graphic Raycast?
GraphicRaycaster
Hope it helps.

Umair M's current suggestion doesn't handle the fact that the ray is originating in world space and traveling at an angle.
It doesn't look to me like you can do a GUI raycast in world space at an angle even if your canvas is in world space. This page suggests a technique of creating a non-rendering camera, moving it around in 3D space with the ray you want to cast, and then doing the GUI raycast relative to that camera. I haven't tried it yet, but it sounds promising.

Related

I'd like to cast a ray to an imaginary plane engendered by x axis and z axis

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.

transform.LookAt on 3d text is showing the text backwards

I am using Unity 2019.2.
Here is my code:
GameObject[] damageTexts = GameObject.FindGameObjectsWithTag("CombatText");
foreach (GameObject damageText in damageTexts)
{
damageText.transform.LookAt(Camera.main.transform);
}
It is making all 3D Texts which I want to look at the camera. However the text is backwards written ?
Why? How can I fix that?
LookAt
Rotates the transform so the forward vector points at /target/'s current position.
Unity UI components usually have their forward vector pointing away from the camera. So you need to always invert the direction if you want to use LookAt
damageText.transform.LookAt(damageText.position - Camera.main.transform.position);
This simply makes it look at a position in the opposite direction.
You can also use LookRotation
damageText.transform.rotation = Quaternion.LookRotation((damageText.position - Camera.main.transform.position).normalized);

Get Unity Blend Tree to update based on player rotation (mouse position)

I'm working on the animations for a character in a top down game. I got the animations to work when the player is moving forwards towards the top, however, whenever I rotate the player with the mouse (to aim) the forward animation is now left/right, etc, etc. I'm using Unity3d and C# with blend tree animations.
The code I'm using is straightforward:
animator.SetFloat("VelX", playerInput.Horizontal);
animator.SetFloat("VelY", playerInput.Vertical);
I can't figure out how to make the forward animation play whenever I'm moving TOWARDS the mouse position.
My Solution:
var heading = Input.mousePosition - this.transform.position;
var distance = heading.magnitude;
var dir = heading / distance;
animator.SetFloat("VelX", playerInput.Horizontal * Mathf.Cos(dir));
animator.SetFloat("VelY", playerInput.Vertical * Mathf.Sin(dir));
Any help would be appreciated.
You may have figured this out already but I just had the same issue and I just found out a way to do this. Hopefully it helps others.
Vector3 inputVector = (Vector3.forward * Input.GetAxis("Vertical")) + (Vector3.right * Input.GetAxis("Horizontal"));
Vector3 animationVector = _playerModel.transform.InverseTransformDirection(inputVector);
var VelocityX = animationVector.x;
var VelocityZ = animationVector.z;
this._playerAnimator.SetFloat("x", VelocityX);
this._playerAnimator.SetFloat("z", VelocityZ);
I my case I have my movement script on a parent empty object. This object rotates the child player model, which is the one getting animated. If you do this then you will need to reference the player model like I am doing. Otherwise you can you transform if you are using the parent object.
I am using a character controller on my parent object not a rigidbody, but it may work the same either way.
From my understanding the key part is 'InverseTransformDirection' which is taking my physical world horizontal and vertical vectors and then converting them based on the rotation of my player model. This then makes the movements be relative to the players rotation rather than the worlds rotation and I can move backwards or strafe correctly as I am moving my mouse for rotation and wasd for movement.

Camera.main.ScreenToWorldPoint(Input.mousePosition) always returns camera position, doesnt matter where i click

I am trying to create a script that converts mouseclick position into position in GridLayout. I trying to use Camera.main.ScreenToWorldPoint(), but its returning coordinates of camera, not of the point clicked, doesnt metter if i use static camera or camera fixed on player. I attached this script to CharacterRobotBoy prefab from standard unity assets.
using UnityEngine;
public class Position : MonoBehaviour
{
void Update()
{
if (Input.GetMouseButtonUp(0))
{
Vector3 pz = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Debug.Log("camera:"+pz);
pz.z = 0;
GridLayout gridLayout = transform.parent.GetComponentInParent<GridLayout>();
Vector3Int cellPosition = gridLayout.WorldToCell(pz);
Debug.Log("cell position:"+cellPosition);
}
}
}
Can my code be fixed for the task, or is there a different solution for the problem. Thanks for your help.
PS: I am new to unity.
Suppose you have a flat plane and a camera looking at it from above.
Think of the screen as the lens of the camera, and the cursor as a small ant walking over it. If I remember correctly, ScreenToWorldPoint returns the location of the ant in world-space, which is somewhere in the sky. By setting z := 0, you get a point directly below the camera, regardless of where the cursor is.
What you should do instead is cast a ray from the center of the camera-view through the ant, and collide it with the plane. The collision point is what you are looking for.
It can be done via the ScreenPointToRay method.
You should check out Brackeys RPG tutorial, he's done something similar.
Hope it helps :)

How do I rotate an object to a specific position around another object? (Essentially orbiting to specified position)

I have a planet and a moon. The moon is parented to a dummy object, which is located at the center of the planet (essentially 0,0,0). The moon is free to rotate around (to any position on) the planet. It should maintain a constant distance from the planet.
I have a specific point in mind that I want to rotate the moon to, though it needs to stay upright. That is, the moon should consistently point "up" relative to the planet's surface. Basically it's like a "moveTo" script only in my case the moon should "rotate" around the planet until it reaches the point I'm looking for.
Here's what I have so far, though I can't work out the correct logic to use:
Vector3 targetDir = moveToPos - moon.transform.position;
float angle = Vector3.Angle( targetDir, moon.transform.up );
dummy.transform.RotateAround (moveToPos, moon.transform.up, angle);
Am I thinking of this correctly? Once I get this working, I'd like to feed the moon different Vector3 positions and have the moon rotate to them on the surface of the planet. I have searched for something similar but can't find what I'm looking for.
The marker displayed in this screenshot should say "Rotate here", but this is essentially what my scene looks like:
You've already made things a lot easier by nesting your moon inside an empty transform. If it's properly set up*, this means you won't have to directly manipulate the moon's transform - you just need to rotate the container object until it faces the target position.
*By this, I mean the container object is at (0, 0, 0) relative to the planet, and the moon is only locally translated along the z-axis so it lines up with the container's transform.forward vector.
The problem is easier to approach if we break it down into smaller steps:
Determining the target direction the container needs to face. We can get this by just subtracting the container's position from the target position.
Calculating the required rotation for the container to face the target direction. This is a good place for Quaternion.LookRotation().
Rotating the container until its direction matches the target direction. Quaternion.Lerp() can be used to achieve this.
Here's how you might implement these steps:
Quaternion targetRotation;
Quaternion startRotation;
float progress = 1;
void SetTargetPosition(Vector3 position)
{
// Calculating the target direction
Vector3 targetDir = position - dummy.transform.position;
// Calculating the target rotation for the container, based on the target direction
targetRotation = Quaternion.LookRotation(targetDir);
startRotation = dummy.transform.rotation;
// Signal the rotation to start
progress = 0;
}
void Update()
{
if (progress < 1)
{
// If a rotation is occurring, increment the progress according to time
progress += Time.deltaTime;
// Then, use the progress to determine the container's current rotation
dummy.transform.rotation = Quaternion.Lerp(startRotation, targetRotation, progress);
}
}
Note: If the moon is rotating too quickly (it will currently complete the rotation in ~1 second), just add less to progress every frame, eg. Divide it by a factor.
As long as the container is centered in the planet, the moon should remain a constant distance away from the surface at all times, and by this method will always keep a consistent "up" direction relative to the planet's surface.
Hope this helps! Let me know if you have any questions.

Categories

Resources