Unity3d - Drag and Drop object on RTS camera - c#

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.

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!");
}
}
}
}```

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;
}
}

Follow Player By Camera in 2D games

I used This code for my MainCamera for following the player in my 2d game in Unity5 :
using UnityEngine;
using System.Collections;
public class CameraFollow : MonoBehaviour {
public float dampTime = 0.15f;
private Vector3 velocity = Vector3.zero;
public Transform target;
// Update is called once per frame
void Update ()
{
if (target)
{
Vector3 point = GetComponent<Camera>().WorldToViewportPoint(target.position);
Vector3 delta = target.position - GetComponent<Camera>().ViewportToWorldPoint(new Vector3(0.5f, 0.5f, point.z)); //(new Vector3(0.5, 0.5, point.z));
Vector3 destination = transform.position + delta;
transform.position = Vector3.SmoothDamp(transform.position, destination, ref velocity, dampTime);
}
}
}
It work fine But player is in middle of screen allways . i wan player be in down of screen and my sprite for show Earth of my game will stick below the camera . i mean better in following pictures:
What I Want :
The Result :
You can add a vertical offset to the calculation. Just adding it to destination should do that I think.
Vector3 destination = ...
destination.y += someOffset;
transform.position = Vector3.SmoothDamp(...);
Otherwise you could also add an empty gameobject to the player gameobject and use that as your target.
One thing that you might need to consider is the resolution.

Unity calculate 3D Mouse position

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.

Unity Rigidbody Click to move

I'm wanting to have a script that when I click in my scene, my player will rotate and have a force added to it and will travel until it has reached the clicked point in my scene.
Right now I have it working using Vectors and having my player lerp from one point to another. But I want to amend it so I can use physics and get a better sense of my player moving. Like have him speed up to start moving and slowing down as he reaches my target loction
My script now looks like this
public GameObject isActive;
public float speed;
public Ray ray;
public Rigidbody rigidBody;
public Vector3 targetPoint;
// Use this for initialization
void Start ()
{
targetPoint = transform.position;
}
// Update is called once per frame
void Update ()
{
}
void FixedUpdate()
{
if (Input.GetMouseButton(0))
{
targetPoint = Camera.main.ScreenToWorldPoint (Input.mousePosition);
ChangeRotationTarget ();
}
Quaternion targetRotation = Quaternion.LookRotation (targetPoint - transform.position);
transform.rotation = Quaternion.Slerp (transform.rotation, targetRotation, speed * Time.deltaTime);
rigidbody.position = Vector3.Lerp(transform.position, targetPoint, speed * Time.fixedDeltaTime);
}
void ChangeRotationTarget ()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Plane playerPlane = new Plane (Vector3.up, transform.position);
float hitdist = 0.0f;
if (playerPlane.Raycast (ray, out hitdist))
{
targetPoint = ray.GetPoint (hitdist);
}
}
However, when I run this, he just slides from one point to another. Regardless of the drag or mass I put in my rigidbody.
Can someone help me make my changes? Or point me in the right direction
I didn't had time to test this, but it should almost be what you are looking for. Just apply your Quaternion and Rotation codes to it.
void FixedUpdate() {
if (Input.GetMouseButton(0))
{
targetPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
if (rigidBody.position != targetPoint)
{
reachedTargetPoint = false;
if (reachedTargetPoint == false)
{
GetComponent<Rigidbody>().AddForce(speed);
}
}
//zoneBeforeReachingTP should be how much units before reaching targetPoint you want to start to decrease your rigidbody velocity
if (rigidBody.position == (targetPoint - zoneBeforeReachingTP))
{
//speedReductionRate should be how much of speed you want to take off your rigidBody at the FixedUpdate time rate
rigidBody.velocity = speedReductionRate;
}
if(rigidBody.position == targetPoint)
{
rigidBody.velocity = new Vector3(0, 0, 0);
reachedTargetPoint = true;
}
ChangeRotationTarget();
}
}
That's because you modify the rigidbody.position, and your logic overrides the physics. What you have to do instead is to ApplyForce every physics frame, probably with ForceMode.Force or ForceMode.Acceleration.

Categories