I try to make a Drag and Drop function in Unity3D.
How it should work:
I have an Object on a ground and i want to be able to just drag it around the ground.
I only want to change the x and the z position.
Sadly no Tutorial could help me out with this problem.
I tried raycasting and it worked to 'select' the Object but i could not change the position.
I managed to write a script to show me the mouse position in world Space but it isnt working really.
Can someone help me out with this and explain me how the code works?
What steps do i need to do so it works?
You can use raycast here and use the hit point as you position when dragging.
void MoveObject()
{
RaycastHit hit;
float movetarget;
//startPoint = hit.point;
ray = GetComponent<Camera> ().ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 100, Color.yellow);
if (Physics.Raycast(ray, out hit))
{
movetarget = hit.distance;
if (activated == false)
{
wall = Instantiate(prefab, hit.point, prefab.transform.rotation);
activated = true;
}
startPoint = hit.point;
}
wall.transform.position = hit.point;
}
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);
}
}
I'm making a top down space resource-gathering game, with 3D models.
I'm trying to cast a ray towards my mouse position, to detect collisions with the objects in my game,
for my little spaceship to know which direction to head to find resources.
The resources are in the form of asteroids the player has to break to gather.
In the code, I use the "center" vector to find the middle of the screen/player position, as the camera follows the player around. All movement happens along the X,Y axis. center.Z is set to 15 simply to counteract the -15Z position of the main camera in worldspace.
So far, the code "works" to the extent that it can detect a hit, but the ray doesn't travel in the direction of the mouse. I have to hold it way off for it to hit, and it's difficult to replicate, to the point where it almost seems random. Might the ray not be able to make sense of the mouse position?
In case I've worded myself poorly, this search ability is not meant to break the asteroid, simply locate it. The breaking ability is a separate script.
Code:
public class AbilitySearch : MonoBehaviour
{
Vector3 center;
Vector3 mousePos;
Vector3 direction;
private float range = 100f;
void Update()
{
center = Camera.main.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 15.0f));
mousePos = Input.mousePosition;
direction = mousePos - transform.position;
if (Input.GetMouseButton(1))
{
Search();
}
}
void Search()
{
RaycastHit hit;
if (Physics.Raycast(center, direction, out hit, range))
{
Debug.Log("You hit " + hit.transform.name);
}
}
}
Thanks in advance
When using Input.mousePosition the position is a pixel coordinate on your screen, so if your mouse was in the bottom left corner of your screen it would be 0,0. This causes the direction to be inaccurate. When what you need is to use the game space coordinates, which is done by using Camera.ScreenToWorldPoint(Input.mousePosition), as an example.
This solution allows you to click on a gameObject (provided it has a collider attached), move the current gameObject towards the clicked object, as well as gather an array of possible gameObjects (RaycastHit[s]) in the direction of the clicked one.
Vector3 destination;
RaycastHit[] possibleTargets;
bool isTravelling;
float searchDistance = 5f;
private void Update()
{
//If right mouse button has been pressed down
if (Input.GetMouseButtonDown(1))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit)) //If ray collides with something
{
Search(hit);
}
}
//If going to destination
if (isTravelling)
{
float step = 2f * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, destination, step);
//If approx arrived
if (Vector3.Distance(transform.position, destination) < 0.1f)
{
isTravelling = false; //Stop moving to destination
Debug.Log("You've arrived");
}
}
}
private void Search(RaycastHit _hit)
{
//Normalise directional vectors, so (if needed) they can be multipled as expected
Vector3 direction = (_hit.point - transform.position).Normalized();
RaycastHit secondHit;
//Grab objects in direction of clicked object (including the one clicked)
possibleTargets = Physics.RaycastAll(transform.position, direction, searchDistance);
Debug.Log($"There are {possibleTargets.Length} possible targets ahead");
Debug.Log($"You hit {_hit.transform.name}");
//Set destination, and set to move
destination = _hit.point;
isTravelling = true;
}
You used the ViewportToWorldPoint method, which expects normalized viewport coordinates in range 0 to 1, but you supplied what seems to me as world coordinates of your camera as its parameter.
You only need to cast a ray from camera to mouse pointer world position (see first line of code in method FindAsteroid) to check for collision with asteroid. The returned RaycastHit provides you with information about the collision - hit position, gameobject, collider - which you can use for other game logic, e.g. shooting a projectile from spaceship to asteroid hit point.
I edited your class and included a screenshot from my simple scene below, which shows the two different "rays":
The yellow ray goes from camera to asteroid hit point
The magenta ray goes from spaceship position to asteroid hit point.
I would also recommend filtering the raycast to affect only specific colliders -
see LayerMask (section Casting Rays Selectively)
public class AbilitySearch : MonoBehaviour
{
private float range = 100f;
private Camera mainCam;
private void Awake()
{
// TIP: Save reference to main camera - avoid internal FindGameObjectWithTag call
mainCam = Camera.main;
}
private void Update()
{
if (Input.GetMouseButton(1))
{
if (FindAsteroid(out var asteroidHit))
{
// Draw a line from spaceship to asteroid hit position
Debug.DrawLine(transform.position, asteroidHit.point, Color.magenta, Time.deltaTime);
Debug.Log($"You hit {asteroidHit.transform.name}");
}
}
}
private bool FindAsteroid(out RaycastHit hit)
{
// Create a ray going from camera position to mouse position in world space
var ray = mainCam.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, range))
{
// Draw a line from camera to world mouse position
Debug.DrawLine(ray.origin, hit.point, Color.yellow, Time.deltaTime);
// An asteroid was found
return true;
}
// An asteroid was NOT found
return false;
}
}
Sample spaceship scene
I'm trying to fix up some bugs in my project and I've come across a problem with a mechanic where I want an object to move to my mouse cursor on click and stay 3 units above where I clicked.
Here is my code:
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
var newPosition = hit.point;
currentDestination = newPosition + new Vector3(0, 3.0f, 0);//mainly this
notAtDestinationYet = true;
}
}
However by using raycasts, If I hit anything above my terrain like the top of a building or something, the object with this script attached will always move 3 units above where the raycast hits.
How do I make it stay at a constant Y-Axis value of 3 units (or whatever amount) no matter what? Say by using a public int.
Simply change the line where you set the currentDestination to use the x and z values from newPosition and a constant y value, instead of adding 3f to the y value of newPosition.
currentDestination = new Vector3(newPosition.x, 3.0f, newPosition.z);
My camera casts a ray to the centre of the screen and I have an object that looks at that direction. The problem is, I made it such that certain keys rotate the camera, so there's a new centre, and the ray from the camera moves too but the object doesn't look at the new direction
Here's my code:
void Update (){
int x = Screen.width / 2;
int y = Screen.height / 2;
//Get centre of screen from camera
Ray ray = Camera.main.ScreenPointToRay (new Vector3 (x, y));
Debug.DrawRay (ray.origin, ray.direction * 1000, new Color (1f, 0.922f, 0.016f, 1f));
//Set object direction
object.transform.LookAt (ray.direction);
}
I'd really appreciate some help with this, thank you!
I found a solution. Instead of looking at ray direction, I got a point from the ray using GetPoint and made the object look at that. It's working fine now.
Well the ray would be starting from the camera and going towards the middle of the screen. So if you wanted the object to look at the camera every Update, the direction would be wrong, but also what if the object wasn't in the center of the screen? If I'm misinterpreting your question, please comment and explain further. I think a better way to get the forward direction of the camera would be to use Camera.main.transform.forward, and you could put that in a Physics.Raycast as well. To have something always look at the camera you could try object.transform.LookAt(Camera.main.transform.position, -Vector3.Up);.
Edit: Also, if you mean the have the object look the same direction as the camera, you could always try object.transform.forward = Camera.main.transform.forward, or keep up with object.transform.rotation = Quaternion.LookRotation(Camera.main.transform.forward, Transform.up);
Alright new idea:
Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5F, 0.5F, 0));
RaycastHit hit;
if(Physics.Raycast(ray, out hit))
{
Vector3 point = hit.point;
object.transform.LookAt(point, Vector3.Up);
}
after turn the main UI framework to NGUI, we found that We couldn't hit object with collider anymore with following code which was working fine we not using NGUI:
private void checkRayCast()
{
if ((Input.GetMouseButtonDown(0)))
{
Debug.Log("shoot ray!!!");
Vector2 point = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit2;
if (Physics.Raycast(ray, out hit2, 100))
{
//this should hit a 3d collider
Debug.Log("we may hit something");
Debug.Log(hit2.collider.gameObject.tag);
}
RaycastHit2D[] hits = Physics2D.RaycastAll(point, Vector2.zero, 0f);
if (hits.Length != 0)
{
//this should all 2d collider
Debug.Log(" ohh, we hit something");
}
else
{
Debug.Log("you hit nothing john snow!!!");
}
//if (hit.transform.gameObject.GetComponent<Rigidbody2D>() != null)
//{
//}
}
}
And we found that we could not hit a 2d collider or 3d collider anymore
Here is the target object's inspector:
EDIT
After following #programmer 's advice and resize the collider to a very big one, hit was detected(thanks, #programmer)
But the collider was changed to so big that it not event make scene. And We should find how big this should be now.
before resizing the collider in the scene
note the green border which indicts the collider size made scene
here is the one that the collider works, but should be unreasonably large:
After digging around And I have figured this out:
The trick is we should use UICamera.currentCamera instead of Camera.main to shoot the ray when we using NGUI.
If we click here in the game view to shoot the ray:
And If we add a line like:
Debug.DrawLine(ray.origin, ray.origin + ray.direction * 100, Color.red, 2, true);
After
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
We should see the actual ray in the scene in a 3d mode:
Note the red line which represents the ray and It could not shoot at the desired position since Camera.main have a different scale.
But if we change the carmera to UICamera to shoot the ray, We could shoot the ray that was desired.
Ray ray = UICamera.currentCamera.ScreenPointToRay(Input.mousePosition);
Hope this could help guys meet with the same pit.