How do I drag objects on the screen using `OnMouseDrag`? - c#

Tried doing 2D and 3D. I want the player to click and drag objects to stack them. I tried copying and pasting code from a video and it still didn't work.
UPDATE! Now it kinda works but it teleports the object far off the screen. I want the player to be able to smoothly click and drag the object.
The code as of now:
using UnityEngine;
using System.Collections;
public class DragObject : MonoBehaviour
{
private float deltax, deltay;
private void OnMouseDown()
{
deltax = 1f;//Camera.main.ScreenToWorldPoint(Input.mousePosition).x - transform.position.x;
deltay = 1f; //Camera.main.ScreenToWorldPoint(Input.mousePosition).y - transform.position.y;
}
private void OnMouseDrag()
{
Vector2 currentTouchPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
transform.position = new Vector2((Input.mousePosition).x - deltax, (Input.mousePosition.y) - deltay);
}
}

It's not clear at all what exact problem is for you, you obviously need to give more info and describe your issue better. But I'll try to help as I can.
To get mouse position you need to use:
Input.mousePosition
If you need mouse position in 2d coordinates you can use this:
Vector2 currentTouchPos = mainCamera.ScreenToWorldPoint(Input.mousePosition);

This method works regardless of your camera type or how the camera moves between frames
First, when the object is first clicked, get the plane at the object's position perpendicular to the direction of the camera:
Plane dragPlane = new Plane(Camera.main.transform.forward, transform.position);
Then, get the ray coming out of the camera from where the mouse is:
Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);
Find out where that ray intersects using Plane.Raycast:
float enter = 0.0f;
if (dragPlane.Raycast(camRay, out enter))
{
Vector3 fingerPosition = camRay.GetPoint(enter);
This gives you a position in world space, which is where you can consider the finger to currently be at. Just put the object there:
transform.position = fingerPosition;
Altogether it might look like this:
void OnMouseDrag()
{
Plane dragPlane = new Plane(Camera.main.transform.forward, transform.position);
Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);
float enter = 0.0f;
if (dragPlane.Raycast(camRay, out enter))
{
Vector3 fingerPosition = camRay.GetPoint(enter);
transform.position = fingerPosition;
}
}
No OnMouseDown necessary.
An alternative is to keep track of how fingerPosition changes over time, and Translate the transform based on that. Keeping as much math as possible in world units makes it easy:
Vector3 prevFingerPosition = Vector3.zero;
// Same code as before but we use it in different places so it goes in its own method.
// If the Raycast fails, return a decent guess.
private Vector3 GetMouseOnPositionInWorldSpace()
{
Plane dragPlane = new Plane(Camera.main.transform.forward, transform.position);
Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);
float enter = 0.0f;
if (dragPlane.Raycast(camRay, out enter))
{
return camRay.GetPoint(enter);
}
return prevFingerPosition;
}
void OnMouseDown()
{
prevFingerPosition = GetMouseOnPositionInWorldSpace();
}
void OnMouseDrag()
{
Vector3 fingerPosition = GetMouseOnPositionInWorldSpace();
transform.Translate(fingerPosition-prevFingerPosition);
prevFingerPosition = fingerPosition;
}
There is an issue behind using OnMouseDrag to handle dragging objects around is that if the cursor moves too quickly, it can move off the object in the span of a single frame, and it will "drop" the object.
If that's not desirable, you might want to set a bool to be true in OnMouseDown that remembers that the object was clicked and a move the logic out of OnMouseDrag and into Update where it can do its thing if that bool is true. And when the mouse is released, set that bool back to false.

Related

Unity3D - Using Raycasting to detect Game Objects. Top-down view

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

Clamping an object axis in Vuforia AR camera

screenshot hereI want to clamp Y-axis on a cube. I can do it in Unity camera. But, it does not react correctly when I am using it in Vuforia camera.
My problem was that the cube follows the camera. I would like the cube to stay in its position and ignore the AR camera position. I sense it has something to do with WorldtoViewpoint but I cannot figure it out. Can you teach me how to do this please? thankyou
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ClampMovementController : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Vector3 pos = transform.localPosition;
pos.y = Mathf.Clamp(transform.position.y, 0f, 0f);
transform.localPosition = pos;
}
}
This is my solution:
Actually its very simple. The INcorrect concept was my object attached to the AR camera, hence, object position is always moving related to camera position. Now. In order to make the object stays in its place. I need to get its localPosition. First. Store the localposition in Vector3 pos. And then do modification on Vector3 pos. At last, reassign the new value to the object localposition.
public class ClampMovementController : MonoBehaviour
{
public float currentPos;
public GameObject capsule;
void Update()
{
//store the value of object localPosition
Vector3 pos = capsule.transform.localPosition;
//modification on the value
pos.y = Mathf.Clamp(pos.y, currentPos, currentPos);
//rerassign the new value to the object localPosition
capsule.transform.localPosition = pos;
}
}
First of all your cube is moving with the camera because your image target is child of your ARCamera. Therefore, when you move the camera image target moves, then your cube moves as well. Make sure your ImageTarget has no parent.
I did not understand why you have to lock any movement in Y axis. I guess you are doing something wrong with lean touch when you move object. I have not used lean touch but i have achieved this with keyboard inputs. You can convert it to lean touch by modifying following script. Just add these line to your ImageTarget's DefaultTrackableEventHandler script:
//Variables for getting capsule and checking if ImageTarget is tracked
private bool isTracked = false;
private GameObject capsule;
Then create an Update method for getting input from user like this.
void Update()
{
if(isTracked)
{
if(Input.GetKey(KeyCode.W))
{
//using forward for moving object in z axis only.
//Also using local position since you need movement to be relative to image target
//Global forward can be very different depending on your World Center Mode
capsule.transform.localPosition += Vector3.forward * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.S))
{
capsule.transform.localPosition -= Vector3.forward * Time.deltaTime;
}
if (Input.GetKey(KeyCode.A))
{
//Using Vector3.left and right to be make sure movement is in X axis.
capsule.transform.localPosition += Vector3.left * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.D))
{
capsule.transform.localPosition += Vector3.right * Time.deltaTime;
}
}
}
As you can see there is no movement in Y axis because i used forward, left and right vectors to make sure movement in in only X and Y axis.
Last you have to make sure isTracked is updated. In order to do that you have to add isTracked = false; in OnTrackingLost method and isTracked = true; in OnTrackingFound method. Good luck!

dragging/dropping and triggers

I am trying to create a drag and drop functionality. I have a game object that lies on a cube (like a table). I want to allow the player to drag and drop this object to multiple "hot spots". At the same time, while this is a 3D game, they should not be allowed to drag the object off of the table, above it, bellow it, etc. Just drag across the top of the table. The way I have this setup is like this:
On the table top (a cube with a rigid body)I have two planes. This plane is just decorative to let the user see where to drop to. Each of these planes has a child plane (a much smaller area, centered in the parent). This child has a Box Collider (isTrigger= true) that extends high up into the air, like a pole sticking out of the ground)
I then have a cube that has a rigid body and a box collider (isTrigger= true). This cube is instantiated onto the "table" top, over one of the hot spots.
HotSpotCode.cs
public class HotSpotCodeScript : MonoBehaviour {
public void OnTriggerEnter(Collider other){
DraggableObject ds = other.GetComponent<DraggableObject>();
ds.startPos = this.gameObject.transform.position;
}
}
DraggableObject.cs
public class DraggableObject : MonoBehaviour {
float lerpTime = 1f;
float currentLerpTime;
float moveDistance = 10f;
Plane plane;
public Vector3 startPos, endPos;
public bool drag;
Vector3 position;
void Start(){
startPos = transform.position;
endPos = transform.position + transform.up * moveDistance;
}
void OnMouseDown(){
plane = new Plane(Vector2.up, transform.position);
drag = true; // start dragging
}
void OnMouseUp(){
lerp();
drag = false;
}
private void lerp() {
currentLerpTime += Time.deltaTime;
if (currentLerpTime > lerpTime)
{
currentLerpTime = lerpTime;
}
float perc = currentLerpTime / lerpTime;
transform.position = Vector3.Lerp(startPos, endPos, perc);
}
public void Update()
{
position = transform.localPosition;
if (drag)
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float distance;
if (plane.Raycast(ray, out distance))
{
transform.position = ray.GetPoint(distance);
}
}
}
public void OnTriggerEnter(Collider other){
endPos = other.transform.position;
}
}
This code mostly works somewhat. I am able to drag the object around and it won't leave the plan it it resides on, for short distances. But, the further away I drag the object, then release it, the more likely it is that the object will not snap back to its original start position and will get stuck somewhere along the way. I am not sure if this is an issue with the Lerp() function or maybe how the plane it is dragged across is created. Can anyone help me out? If you need more info, please let me know. I've been working on this for weeks and haven't gotten much further that this.
Another issue that pops up, as well, is that when the object that is being dragged is released, and comes into contact with the hot spot's collider, it stops the object at its exact point. So, if a corner of the cube comes into contact with the collider, the cube will not come to rest centered on the hot spot.

Unity: Rotate towards mouse in 3D topdown view

So I am trying to create a kind of turrent like a tank. The upper section needs to look at my mouse. I have tried
GetComponent().ScreenToWorldPoint(Argument);
many times togheter with
Input.mousePosistion();
But I couldn't figure this out really. I am programing this in C# if any one could help or provide a simple script that works in a 3D setting that would be great!
My code so far:
using UnityEngine;
using System.Collections;
public class Laser : MonoBehaviour {
public float Speed;
public AudioClip LaserSFX;
private Transform Player;
public Vector3 mousePos;
public Camera Cam;
// Use this for initialization
void Start () {
GameObject player = GameObject.FindWithTag("Player");
Player = player.transform;
if(!GetComponent<AudioSource>().isPlaying)
GetComponent<AudioSource> ().PlayOneShot (LaserSFX);
}
// Update is called once per frame
void Update () {
//Code to look at mouse or rotate
//Code to move towards it
float dist = Vector3.Distance(Player.position, transform.position);
if (dist >= 500)
{
Destroy(gameObject);
}
}
}
With kind regards,
Misterk99
You have to convert the mouse position to the view position, this means the ScreenToWorldPoint should be provided to you by the camera you are using at that time, and since you already have the camera as cam:
Vector3 pos = Input.mousePosition;
pos = Cam.ScreenToWorldPoint(pos);
with that now you have to face the position, and since you are using topdown 3D i supose the camera is facing the -y position. if so, don't forget to set the y you want:
pos.y = (your value here, depending to the position you are giving to your objects);
Vectgor3 dir = this.position - pos;
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(angle, Vector3.up);
I figured out a solution while trying to find one for myself. The answer #Spike gave led me on the right track. However, I used the mouse's viewpoint position and converted the objects position to the viewport position. Here is the code in Javascript (what I am using) which should be easy enough to convert to C#.
My camera is facing the -Y direction, FYI.
#pragma strict
var cam: Camera;
function Start () {
}
function Update () {
var mousePos: Vector3 = Input.mousePosition;
var playerPos: Vector3 = cam.WorldToScreenPoint(transform.position);
var dir: Vector3 = mousePos - playerPos;
var angle: float = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(-angle, Vector3.up);
}

Move Camera Over Terrain Using Touch Input in Unity 3D

I am new to Unity and I am trying to figure out how to move the camera over a map/terrain using touch input. The camera would be looking down at the terrain with a rotation of (90,0,0). The terrain is on layer 8. I have had no problem getting it moving with keyboard, now I am trying to move to touch and it is very different if you want to keep expected usage on iOS.
The best example I can think of on a built in iOS app is Maps where the user would touch the screen and that point on the map would stay under the finger as long as the finger stayed on the screen. So as the user moves their finger the map appears to be moving with the finger. I have not been able to find examples that show how to do it this way. I have seen may examples of moving the camera or character with the mouse but they don't seem to translate well to this style.
Also posted on Unity3D Answers:
http://answers.unity3d.com/questions/283159/move-camera-over-terrain-using-touch-input.html
Below should be what you need. Note that it's tricky to get a 1 to 1 correspondence between finger/cursor and the terrain when using a perspective camera. If you change your camera to orthographic, the script below should give you a perfect map between finger/cursor position and map movement. With perspective you'll notice a slight offset.
You could also do this with ray tracing but I've found that route to be sloppy and not as intuitive.
Camera settings for testing (values are pulled from the inspector so apply them there):
Position: 0,20,0
Orientation: 90,0,0
Projection: Perspective/Orthographic
using UnityEngine;
using System.Collections;
public class ViewDrag : MonoBehaviour {
Vector3 hit_position = Vector3.zero;
Vector3 current_position = Vector3.zero;
Vector3 camera_position = Vector3.zero;
float z = 0.0f;
// Use this for initialization
void Start () {
}
void Update(){
if(Input.GetMouseButtonDown(0)){
hit_position = Input.mousePosition;
camera_position = transform.position;
}
if(Input.GetMouseButton(0)){
current_position = Input.mousePosition;
LeftMouseDrag();
}
}
void LeftMouseDrag(){
// From the Unity3D docs: "The z position is in world units from the camera." In my case I'm using the y-axis as height
// with my camera facing back down the y-axis. You can ignore this when the camera is orthograhic.
current_position.z = hit_position.z = camera_position.y;
// Get direction of movement. (Note: Don't normalize, the magnitude of change is going to be Vector3.Distance(current_position-hit_position)
// anyways.
Vector3 direction = Camera.main.ScreenToWorldPoint(current_position) - Camera.main.ScreenToWorldPoint(hit_position);
// Invert direction to that terrain appears to move with the mouse.
direction = direction * -1;
Vector3 position = camera_position + direction;
transform.position = position;
}
}
I've come up with this script (I have appended it to the camera):
private Vector2 worldStartPoint;
void Update () {
// only work with one touch
if (Input.touchCount == 1) {
Touch currentTouch = Input.GetTouch(0);
if (currentTouch.phase == TouchPhase.Began) {
this.worldStartPoint = this.getWorldPoint(currentTouch.position);
}
if (currentTouch.phase == TouchPhase.Moved) {
Vector2 worldDelta = this.getWorldPoint(currentTouch.position) - this.worldStartPoint;
Camera.main.transform.Translate(
-worldDelta.x,
-worldDelta.y,
0
);
}
}
}
// convert screen point to world point
private Vector2 getWorldPoint (Vector2 screenPoint) {
RaycastHit hit;
Physics.Raycast(Camera.main.ScreenPointToRay(screenPoint), out hit);
return hit.point;
}
Pavel's answer helped me a lot, so wanted to share my solution with the community in case it helps others. My scenario is a 3D world with an orthographic camera. A top-down style RTS I am working on. I want pan and zoom to work like Google Maps, where the mouse always stays at the same spot on the map when you pan and zoom. This script achieves this for me, and hopefully is robust enough to work for others' needs. I haven't tested it a ton, but I commented the heck out of it for beginners to learn from.
using UnityEngine;
// I usually attach this to my main camera, but in theory you can attach it to any object in scene, since it uses Camera.main instead of "this".
public class CameraMovement : MonoBehaviour
{
private Vector3 MouseDownPosition;
void Update()
{
// If mouse wheel scrolled vertically, apply zoom...
// TODO: Add pinch to zoom support (touch input)
if (Input.mouseScrollDelta.y != 0)
{
// Save location of mouse prior to zoom
var preZoomPosition = getWorldPoint(Input.mousePosition);
// Apply zoom (might want to multiply Input.mouseScrollDelta.y by some speed factor if you want faster/slower zooming
Camera.main.orthographicSize = Mathf.Clamp(Camera.main.orthographicSize + Input.mouseScrollDelta.y, 5, 80);
// How much did mouse move when we zoomed?
var delta = getWorldPoint(Input.mousePosition) - preZoomPosition;
// Rotate camera to top-down (right angle = 90) before applying adjustment (otherwise we get "slide" in direction of camera angle).
// TODO: If we allow camera to rotate on other axis we probably need to adjust that also. At any rate, you want camera pointing "straight down" for this part to work.
var rot = Camera.main.transform.localEulerAngles;
Camera.main.transform.localEulerAngles = new Vector3(90, rot.y, rot.z);
// Move the camera by the amount mouse moved, so that mouse is back in same position now.
Camera.main.transform.Translate(delta.x, delta.z, 0);
// Restore camera rotation
Camera.main.transform.localEulerAngles = rot;
}
// When mouse is first pressed, just save location of mouse/finger.
if (Input.GetMouseButtonDown(0))
{
MouseDownPosition = getWorldPoint(Input.mousePosition);
}
// While mouse button/finger is down...
if (Input.GetMouseButton(0))
{
// Total distance finger/mouse has moved while button is down
var delta = getWorldPoint(Input.mousePosition) - MouseDownPosition;
// Adjust camera by distance moved, so mouse/finger stays at exact location (in world, since we are using getWorldPoint for everything).
Camera.main.transform.Translate(delta.x, delta.z, 0);
}
}
// This works by casting a ray. For this to work well, this ray should always hit your "ground". Setup ignore layers if you need to ignore other colliders.
// Only tested this with a simple box collider as ground (just one flat ground).
private Vector3 getWorldPoint(Vector2 screenPoint)
{
RaycastHit hit;
Physics.Raycast(Camera.main.ScreenPointToRay(screenPoint), out hit);
return hit.point;
}
}
Based on the answer from Pavel, I simplified the script and removed the unlovely "jump" when touch with more then one finger and release the second finger:
private bool moreThenOneTouch = false;
private Vector3 worldStartPoint;
void Update() {
Touch currentTouch;
// only work with one touch
if (Input.touchCount == 1 && !moreThenOneTouch) {
currentTouch = Input.GetTouch(0);
if (currentTouch.phase == TouchPhase.Began) {
this.worldStartPoint = Camera.main.ScreenToWorldPoint(currentTouch.position);
}
if (currentTouch.phase == TouchPhase.Moved) {
Vector3 worldDelta = Camera.main.ScreenToWorldPoint(currentTouch.position) - this.worldStartPoint;
Camera.main.transform.Translate(
-worldDelta.x,
-worldDelta.y,
0
);
}
}
if (Input.touchCount > 1) {
moreThenOneTouch = true;
} else {
moreThenOneTouch = false;
if(Input.touchCount == 1)
this.worldStartPoint = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position);
}
}
using UnityEngine;
// I usually attach this to my main camera, but in theory you can attach it to any object in scene, since it uses Camera.main instead of "this".
public class CameraMovement : MonoBehaviour
{
private Vector3 MouseDownPosition;
void Update()
{
// If mouse wheel scrolled vertically, apply zoom...
// TODO: Add pinch to zoom support (touch input)
if (Input.mouseScrollDelta.y != 0)
{
// Save location of mouse prior to zoom
var preZoomPosition = getWorldPoint(Input.mousePosition);
// Apply zoom (might want to multiply Input.mouseScrollDelta.y by some speed factor if you want faster/slower zooming
Camera.main.orthographicSize = Mathf.Clamp(Camera.main.orthographicSize + Input.mouseScrollDelta.y, 5, 80);
// How much did mouse move when we zoomed?
var delta = getWorldPoint(Input.mousePosition) - preZoomPosition;
// Rotate camera to top-down (right angle = 90) before applying adjustment (otherwise we get "slide" in direction of camera angle).
// TODO: If we allow camera to rotate on other axis we probably need to adjust that also. At any rate, you want camera pointing "straight down" for this part to work.
var rot = Camera.main.transform.localEulerAngles;
Camera.main.transform.localEulerAngles = new Vector3(90, rot.y, rot.z);
// Move the camera by the amount mouse moved, so that mouse is back in same position now.
Camera.main.transform.Translate(delta.x, delta.z, 0);
// Restore camera rotation
Camera.main.transform.localEulerAngles = rot;
}
// When mouse is first pressed, just save location of mouse/finger.
if (Input.GetMouseButtonDown(0))
{
MouseDownPosition = getWorldPoint(Input.mousePosition);
}
// While mouse button/finger is down...
if (Input.GetMouseButton(0))
{
// Total distance finger/mouse has moved while button is down
var delta = getWorldPoint(Input.mousePosition) - MouseDownPosition;
// Adjust camera by distance moved, so mouse/finger stays at exact location (in world, since we are using getWorldPoint for everything).
Camera.main.transform.Translate(delta.x, delta.z, 0);
}
}
// This works by casting a ray. For this to work well, this ray should always hit your "ground". Setup ignore layers if you need to ignore other colliders.
// Only tested this with a simple box collider as ground (just one flat ground).
private Vector3 getWorldPoint(Vector2 screenPoint)
{
RaycastHit hit;
Physics.Raycast(Camera.main.ScreenPointToRay(screenPoint), out hit);
return hit.point;
}
}

Categories