Unity calculate 3D Mouse position - c#

I'm trying to make a C# script for a free throw ball game and need to get the mouse position in the world, and it should be in 3D to be able to throw in all 3 axes. I'm kinda new to scripting and the script I wrote works, but not right. I am not sure how to get the depth or the y axis working, because the screen is only 2d .
using UnityEngine;
using System.Collections;
public class ShootBall : MonoBehaviour {
private Rigidbody rb;
private RaycastHit hit;
private Vector3 com;
private Vector3 shootDirection;
void Start () {
rb = GetComponent<Rigidbody>();
}
void Update () {
}
void OnMouseDown (){
com = rb.worldCenterOfMass;
Debug.Log (com);
}
void OnMouseDrag (){
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.Log(ray);
Physics.Raycast(ray, out hit);
}
void OnMouseUp (){
shootDirection = com - hit.point;
rb.AddForce (shootDirection * 100);
}
}

The distance from the camera needs to be set on the z-value of the vector being passed into ScreenPointToRay. The reason why Input.mousePosition works as a parameter for ScreenPointToRay is because Vector2 can be implicitly cast to Vector3. So try doing something like this:
float distanceFromCamera = Vector3.Distance(ballGameObject.transform.position, Camera.main.transform.position);
Vector3 inputPosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, distanceFromCamera);
Ray ray = Camera.main.ScreenPointToRay(inputPosition);
ballGameObject is the gameObject reference to the ball that will be thrown.

Related

Not understanding why raycast code isnt working

Here is the Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RaycastControl : MonoBehaviour
{
LineRenderer line;
private Vector3 zeros;
public LayerMask EnemyLayer;
// Start is called before the first frame update
void Start()
{
line = GetComponent<LineRenderer>();
zeros = new Vector3(0f, 0f, 0f);
}
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Debug.Log("Detected the click");
Vector3 mouse = mouseToWorld(Input.mousePosition);
Ray ray = new Ray(zeros, mouse);
RaycastHit hitData;
if (Physics.Raycast(ray, 10000, EnemyLayer))
{
Vector3[] linePos = new Vector3[] { transform.position, mouse };
line.SetPositions(linePos);
Debug.Log("You've hit a Zomboid!");
}
}
}
public Vector3 mouseToWorld(Vector3 mousePos)
{
mousePos = Input.mousePosition;
mousePos.z = Camera.main.nearClipPlane;
Vector3 mouse = Camera.main.ScreenToWorldPoint(mousePos);
mouse.z = 0f;
return mouse;
}
}
Note: I'm using Unity2d
I'm trying to use 0,0,0 and the location of my mouse to cast a ray starting from 0,0,0 and running through the mouse location on to the specified max distance in if(physics.Raycast(ray,maxDistance,EnemyLayer)). this, however, does not work. When I click on or behind the "Zombie" object I've created, I dont get a raycast hit detected.
I've been sure to make sure that the layer mask set in this script is the same one set in the Zombie object. My Debug.Log("You've made it this far!"); line activates so I know the Script is in the scene and is being read, but the Physics.Raycast(ray,10000,EnemyLayer)) NEVER returns true, and we know this because Debug.Log("You've hit a Zomboid!") never shows up in console.
Note: The object this script is attached to, Center, sits at 0,0,0. its transform.position = 0,0,0
Your help is greatly appreciated.
Physics and Physics2D are separate in Unity and don't interact with each other.
Physics2D.Raycast() belongs to Physics2D and only responds to Physics2D (BoxCollider2D, CircleCollider2D, PolygonCollider2D, etc.)
Physics.Raycast() belongs to Physics and only responds to Physics (BoxCollider, SphereCollider, MeshCollider, etc.)
You are using Physics.Raycast() when you should be using Physics2D.Raycast().
This is how you would do it:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RaycastControl : MonoBehaviour
{
LineRenderer line;
//private Vector3 zeros;
public LayerMask EnemyLayer;
// Start is called before the first frame update
void Start()
{
line = GetComponent<LineRenderer>();
//zeros = new Vector3(0f, 0f, 0f); not necessary this is the same as Vector3.zero
}
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Debug.Log("Detected the click");
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
if (hit)
{
Vector3[] linePos = new Vector3[] { transform.position, hit.point};
line.SetPositions(linePos);
Debug.Log("You've hit a Zomboid!");
}
}
}
}```

Drawing a sprite on Raycast collision point

I am trying to emit a raycast from the player object and project a crosshair texture onto the world position the crosshair is aimed at. The crosshair should not overlap with the player and it should also only be emited in front of the Player gameObject.
I have tried this so far:
private float range = 100f;
public Texture crosshair;
private Rect crosshairPos;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Shoot();
}
Ray ray = new Ray(transform.position, transform.forward);
crosshairPos.x = ray.GetPoint(100f).x;
crosshairPos.y = ray.GetPoint(100f).y;
Graphics.DrawTexture(crosshairPos, crosshair);
Edit: After some testing, I am currently on the following snippet of code:
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Shoot();
}
Ray ray = new Ray(transform.position, transform.forward);
crosshairPos.x = ray.GetPoint(5f).x;
crosshairPos.y = ray.GetPoint(5f).y;
crosshairPos = Camera.main.WorldToScreenPoint(crosshairPos);
Vector2 crosshairPosSize = new Vector2(crosshair.width, crosshair.height);
Graphics.DrawTexture(new Rect((Vector2)crosshairPos, crosshairPosSize), crosshair);
}
I am however still unable to see a projected crosshair.
When you use a Ray from a GameObject you get a Vector in the world coordinate system. (The game world where your player is).
Graphics.DrawTexture() uses screen coordinates to draw the texture on screen.
Consider using Camera.Main.WorldToScreenPoint to change the world points that you get from Ray into points you can display on screen.
Here's an example of that
Vectors crosshairPos = new Vector3();
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Shoot();
}
Ray ray = new Ray(transform.position, transform.forward);
crosshairPos.x = ray.GetPoint(100f).x;
crosshairPos.y = ray.GetPoint(100f).y;
crosshairPos = Camera.Main.WorldToScreenPoint(crosshairPos);
Graphics.DrawTexture((Vector2)crosshairPos, crosshair);
}

How to make the tank look to the side of the mouse and shoot where the player clicks?

I have a tank in a 3rd person 3D game so my camera is looking at the tank from above and a bit from the side. I need the turret of my tank to rotate on the y-axis so that it always looks at the mouse and shoot where the player clicks. The problem is that my game is 3D and all I get is that it actually looks at the mouse (like upwards to the sky).
This is the code I have now:
private Vector3 target;
private Camera cam;
private void Start()
{
cam = Camera.main;
}
void Update()
{
Vector2 mousePos = new Vector2();
mousePos.x = Input.mousePosition.x;
mousePos.y = Input.mousePosition.y;
target = cam.WorldToScreenPoint(new Vector3(mousePos.x, mousePos.y, cam.nearClipPlane));
transform.LookAt(target);
}
Your problem lies here: target = cam.WorldToScreenPoint(new Vector3(mousePos.x, mousePos.y, cam.nearClipPlane));
What you actually need to do is generate a ray that comes out of the camera with ScreenPointToRay(), then do a Physics.Raycast() with that ray and get the RaycastHit from it. Then set the target equal to the RaycastHit.
For example:
Ray screenRay;
RaycastHit screenRayHit;
float maxRaycastDistance;
screenRay = cam.ScreenPointToRay(mousePos.x, mousePos.y);
if(Physics.Raycast(screenRay, out screenRayHit, maxRaycastDistance))
{
target = screenRayHit;
}
else
{
//just sets the turret to aim at the endpoint of the ray if you aim at nothing
target = screenRay.GetPoint(maxRaycastDistance);
}

Unity3d - Drag and Drop object on RTS camera

I have this code to drag and drop 3d objects on a world:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpyAI : MonoBehaviour {
Animator anim;
private Vector3 screenPoint;
private Vector3 offset;
// Use this for initialization
void Start () {
anim = gameObject.GetComponent<Animator>();
}
// Update is called once per frame
void Update () {
}
void OnMouseDown()
{
anim.SetBool("drag", true);
screenPoint = Camera.main.WorldToScreenPoint(transform.position);
offset = transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z));
}
void OnMouseDrag()
{
Vector3 curScreenPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z);
Vector3 curPosition = Camera.main.ScreenToWorldPoint(curScreenPoint) + offset;
transform.position = curPosition;
}
void OnMouseUp()
{
anim.SetBool("drag", false);
anim.SetBool("idle", true);
}
}
The problem is:
When im dragging the object, sometimes, depending on the mouse movement it goes undergound
How can I make the object to stay above the ground while dragging it?
The most obvious thing that should change is the z-value of curScreenPoint. Using the docs as reference, it should probably be:
Vector3 curScreenPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, Camera.main.nearClipPlane);
Now for maybe a bit more fine-tuned behavior, you might want to make the object raise up above the ground if the ground has slopes or something with complex colliders on it like a chair or table. To do this, you likely want to do a sphere cast down towards the ground from a point somewhat above what's calculated in curPosition. Use the hitInfo to see how far down it goes, and adjust the y position of curPosition accordingly.

Unity3D OnDrag move 3D object doesn't follow pointer

I'm new to the concept of transforming screen coordinates to world coordinates in a 3D world in general, even more so in Unity. I'm using the UnityEngine.EventSystems library, implementing the IDragHandler, as I want this to work the same no matter if a mouse or touch input is the source.
I want to drag around a 3D object in world space, locked to one z position. I found this excellent piece of code which I'm now using:
http://coffeebreakcodes.com/drag-object-with-mouse-unity3d/
That same code can also be found in the answer here: Drag 3d object using fingers in unity3d
It works exactly as I want it to, apart from one thing: The transformation works fine when the drag starts, but the object makes larger movements than the pointer does, getting worse the further from the object's original position it gets, as if the movement is multiplied with something that gets larger the further away from the original position I get.
This is my code, which is basically the same as in the link above, apart from using the PointerEventData's pointer coordinates instead of Input.mousePosition (a change that did not affect the behaviour):
public class DragInput : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
private Vector3 screenPoint;
private Vector3 offset;
public void OnBeginDrag(PointerEventData eventData)
{
screenPoint = Camera.main.WorldToScreenPoint(transform.position);
offset = transform.position - Camera.main.ScreenToWorldPoint(new Vector3(eventData.position.x, eventData.position.y, screenPoint.z));
}
public void OnDrag(PointerEventData eventData)
{
Vector3 cursorPoint = new Vector3(eventData.position.x, eventData.position.y, screenPoint.z);
Vector3 cursorPosition = Camera.main.ScreenToWorldPoint(cursorPoint) + offset;
transform.position = cursorPosition;
}
public void OnEndDrag(PointerEventData eventData)
{
}
}
EDIT: This video clip showcases the behaviour I'm describing: https://www.dropbox.com/s/qfx2p6kznizft1q/Unity%202017-01-13%2014-32-15-89.avi?dl=0
This is a perspective problem when dragging around 3D Object.
You need a make a Plane, covert the mouse position(screen pixels) to ray then check where they both intersect.
using UnityEngine;
using UnityEngine.EventSystems;
public class DragInput : MonoBehaviour, IPointerDownHandler, IDragHandler, IEndDragHandler
{
Camera mainCamera;
float zAxis = 0;
Vector3 clickOffset = Vector3.zero;
// Use this for initialization
void Start()
{
mainCamera = Camera.main;
zAxis = transform.position.z;
}
public void OnPointerDown(PointerEventData eventData)
{
clickOffset = transform.position - mainCamera.ScreenPointToWoldOnPlane(eventData.position, zAxis);
}
public void OnDrag(PointerEventData eventData)
{
transform.position = mainCamera.ScreenPointToWoldOnPlane(eventData.position, zAxis) + clickOffset;
}
public void OnEndDrag(PointerEventData eventData)
{
}
}
public static class extensionMethod
{
public static Vector3 ScreenPointToWoldOnPlane(this Camera cam, Vector3 screenPosition, float zPos)
{
float enterDist;
Plane plane = new Plane(Vector3.forward, new Vector3(0, 0, zPos));
Ray rayCast = cam.ScreenPointToRay(screenPosition);
plane.Raycast(rayCast, out enterDist);
return rayCast.GetPoint(enterDist);
}
}
The cause of the problem I posted for, was due to me having forgotten a PhysicsRaycaster on the MainCamera. I had a GraphicsRaycaster on a Canvas, but no PhysicsRaycaster present - I think that might have caused odd behaviours.
However, my code as it is has problems with the other desired behaviour I described, moving the object in a fixed distance to the camera. See #Programmer's answer to see how to fix that.
Their code is however not what solved the issue that was the purpose of this post, hence their answer not being the accepted answer.
Take a look at this snippet:
pos = Camera.main.ScreenToWorldPoint (Vector3 (Input.GetTouch(0).position.x,Input.GetTouch(0).position.y, 5));
transform.position = new Vector3(pos.x, pos.y, pos.z);
or else you can try this class, if this isn't working, I'm out of options :)
using UnityEngine;
using System.Collections;
public class DragObject : MonoBehaviour {
private float dist;
private Vector3 v3Offset;
private Plane plane;
private bool ObjectMouseDown = false;
public GameObject linkedObject;
void OnMouseDown() {
plane.SetNormalAndPosition(Camera.main.transform.forward, transform.position);
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
float dist;
plane.Raycast (ray, out dist);
v3Offset = transform.position - ray.GetPoint (dist);
ObjectMouseDown = true;
}
void OnMouseDrag() {
if (ObjectMouseDown == true){
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
float dist;
plane.Raycast (ray, out dist);
Vector3 v3Pos = ray.GetPoint (dist);
v3Pos.z = gameObject.transform.position.z;
v3Offset.z = 0;
transform.position = v3Pos + v3Offset;
if (linkedObject != null){
linkedObject.transform.position = v3Pos + v3Offset;
}
}
}
void OnMouseOut() {
ObjectMouseDown = false;
}
}

Categories