Unity drag and drop in world positions - unity3d

I'm currently working on a basic card game in Unity and I'm having a fair bit of trouble working out how to perform a drag on my cards. My current layout is as follows:
I'm currently trying to use the IDragHandler interface to receive callbacks whenever a drag event is detected over my card object.
My end goal is to be able to slide the cards to the left/right based on the x axis of a touch/mouse slide.
I've currently tried using the eventdata.delta value passed into OnDrag() IDragHandler to move my card but this value, from what I can tell, is in pixels and when converted to world units using Camera.main.ScreenToWorldPoint() results in a value of 0,0,0. Likewise trying to keep track of where the drag started and subtracting the current position result in a value of 0,0,0.
I'm currently at a bit of a loss as to how I can drag a game object using world units so I'd greatly appreciate it if someone can provide me some guidance.
Thanks
EDIT:
^This is why you shouldn't StackOverflow late at night.
So to add some extra context:
Cards are just game objects with a mesh renderer attached and a canvas embedded to add the text (probably not the best way of adding text but it works)
Cards are all under a "hand" object which consists of a box collider2d to receive events and a script that implements IBeginDragHandler, IEndDragHandler and IDragHandler
So far I've tried two different approaches to calculating drag distances:
public void OnDrag(PointerEventData eventData)
{
Vector2 current = Camera.main.ScreenToWorldPoint(eventData.position);
Debug.Log("Current world position: "+current);
Vector3 delta = lastDragPoint - current;
// Store current value for next call to OnDrag
lastDragPoint = eventData.position;
// Now use the values in delta to move the cards
}
Using this approach I always get a value of 0,0,0 after my call to ScreenToWorldPoint so I cant move the cards
The second approach I've tried is:
public void OnDrag(PointerEventData eventData)
{
Vector3 screenCenter = new Vector3(Screen.width * 0.5f, Screen.height * 0.5f, 1f);
Vector3 screenTouch = screenCenter + new Vector3(eventData.delta.x, eventData.delta.y, 0f);
Vector3 worldCenterPosition = Camera.main.ScreenToWorldPoint(screenCenter);
Vector3 worldTouchPosition = Camera.main.ScreenToWorldPoint(screenTouch);
Vector3 delta = worldTouchPosition - worldCenterPosition;
// Now use the values in delta to move the cards
}
Using this approach I get a much better result (the cards actually move) but they don't correctly follow the mouse. If I drag a card I will move in the direction of the mouse however the distance moved is significantly less than the distance the mouse moved (i.e. by the time the mouse has reached the edge of the screen the card has only moved one cards width)

So after a bit of playing around and inspecting the various values I was receiving from the calls to ScreenToWorldPoint I finally tracked down the issue.
In the code for my second attempted approach I found that I was using an incorrect value for the Z argument in ScreenToWorldPoint. After doing a bit of research I found that his should be the Z distance between the camera and the (i guess) touch surface.
Vector3 screenCenter = new Vector3(Screen.width * 0.5f, Screen.height * 0.5f, -Camera.main.transform.position.z);
Vector3 screenTouch = screenCenter + new Vector3(eventData.delta.x, eventData.delta.y, 0);
Thanks to everyone who took the time to read through my question.

You could use OnMouseDown -method to detect when player starts to drag the card and update its positions according to mouse position. When the drag ends the OnMouseUp -method is called.
https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnMouseDown.html
https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnMouseUp.html

Related

How can I rotate an Object around the camera (angle)?

What I want to do is spawn an object in front of the camera and the side I am looking at. When I change the rotation of the camera (looking at a different side), the object is still spawned at the same position. How can I change this (that the Object also changes his angle)?
public void Create(Object myPrefab)
{
Vector3 instantGO = Camera.main.transform.position + new Vector3(0, 0, 7);
Instantiate(myPrefab, instantGO, Quaternion.identity);
}
Use transform.forward - this gives a position relative to the transform e.g. Camera.main.transform.position + (Camera.main.transform.forward * 5) for 5m in front of the cam.
The forward and left properties return a vector of magnitude one, pointing in the direction the transform is facing, or to it's left. You can use -forward and -left to align backwards or to the right. Add this to the position of the object and multiply it to give a position a number of units away. In recent versions of Unity you also have transform... up, down, right etc.
Use this Instantiate variant:
Instantiate(Object original, Vector3 position, Quaternion rotation, Transform parent);
And pass the Camera transform as the parent parameter. This will make the new object a child of the camera and thus always moves/rotates with the camera.

Unintended player movement from transform.InverseTransformDirection

this is my first time posting on here. I'm working on a game using the new Unity multiplayer networking solution.
In summary, the issue is that the player is not moving as intended.
I am trying to take player input as follows:
Vector3 worldSpaceDir = new Vector3(Input.GetAxisRaw("Vertical"), 0, Input.GetAxisRaw("Horizontal"));
then convert it to the object space coordinates of the player character:
_inputDirection = transform.InverseTransformDirection(worldSpaceDir);
The issue I'm having is with a rotation of 0 or 180 the player moves as expected with the WASD inputs, however, at 90 or 270 all the inputs are flipped(A = right, D = left, W = backward, S = forward).
I found a question that is exactly my question but no one responded with an answer. The question is quite old now so I wanted to ask it again for more visibility.
Here's a link to the original question.
Firstly, you are taking the worldSpaceDir wrong, it should be as follow
Vector3 worldSpaceDir = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
here we take horizontal input as X and vertical input as Z, because in Unity Forward is pointed as Z and not Y.
Secondly, we do not need to use InverseTransformDirection() we just need TransformDirection() something like following
Vector3 inputDirection = transform.TransformDirection(worldSpaceDir);
here we are telling unity to convert the worldSpaceDir that is relative to transform (local direction) into a world space direction, so we might actually give a proper name to worldSpaceDir.
The following would work for you.
private void Update() {
Move();
}
private void Move() {
Vector3 directionToMove = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
Vector3 inputDirection = transform.TransformDirection(directionToMove);
transform.position += inputDirection * Time.deltaTime;
}
I think you want to go the other way round actually!
Transform.InverseTransformDirection converts a vector from world space into local space.
What you get as input however is a local vector on the XZ plane. You want to apply this direction according to your player objects orientation, if e.g. pressing right (your input is 1,0,0) the object shall move towards its transform.right vector.
So you rather want to convert in the opposite direction into world space to move the object in the Unity world space.
You should rather be using Transform.TransformDirection!
var worldMove = transform.TransformDirection(input);
Or alternatively you can also just multiply by the rotation like
var worldMove = transform.rotation * input;
Note that if you are also using a Rigidbody like the question you linked there is also Rigidbody.AddRelativeForce which basically works the same way and expects a local space vector which is then internally converted into a world space force.

How to make a model appear in front of AR Camera after the session starts using ARFoundation?

I was looking to update the ARcamera position.I am doing ImageTracking project.It detects an image and a corresponding prefab is shown in front of the camera.It starts playing an animation.After the animation I want the prefab to come really close towards the camera.When I give the code prefab.position=ARcamera.position; after animation code,I think the prefab goes to the initial position where the ARCamera was when the app had started that is (0,0,0).
How to make the prefab come really close towards the front camera.
speed = 10f;
float step = speed * Time.deltaTime;
Showprefabs.transform.GetChild(0).position = Vector3.MoveTowards(Showprefabs.transform.GetChild(0).position,
new Vector3(Arcam.transform.position.x, Arcam.transform.position.y + 0.2f,
Arcam.transform.position.z + 6.3f), step);
//The values 0.2f and 6.3f I added using the Editor to make the prefab come near the camera(But in world position it is different.)
First of all I hope by "prefab" you mean already Instantiated GameObject. It makes no sense to move a prefab ;)
You tried to calculate the target position but did it with World-Space coordinates.
You probably want to do something like
var targetObject = Showprefabs.transform.GetChild(0);
var currentPosition = targetObject.position;
var targetPosition = Arcam.transform.position
// Place it 60cm in front of the camera
+ Arcam.transform.forward * 0.6f
// additionally move it "up" 20cm perpendicular to the view direction
+ Arcam.transform.up * 0.2f;
targetObject.position = Vector3.MoveTowards(currentPosition, targetPosition, step * Time.deltaTime);
If you want that movement a bit smoother so it moves slower if already close and faster if further away you might be interested in rather using Vector3.Lerp here
public float smoothFactor = 0.5f;
targetObject.position = Vector3.Lerp(currentPosition, targetPosition, smoothFactor);
Where a smoothFactor of 0.5 reads: Every frame set the object to a position in the center of the currentPosition and targetPosition. A value closer to 0 results in slower movement, closer to 1 in faster reaching the targetPosition.
Note that actually this approach will never really fully arrive at the targetPosition but only come very very close but usually this doesn't matter in AR where the Camera constantly moves a bit anyway.

How can I get the position of the tracked picture when detected in Unity with AR foundation?

I am using unity AR foundation image tracking. When a tracked picture appears on the screen, how can I get its position or its relative distance to the camera?
You can get the distance from the camera to the picture by giving the camera position and the game object transform position of the identified picture. I'm currently using this in one of my apps and its working nicely.
Vector3.Distance(pictureGameObject.transform.position, Camera.main.transform.position)
well you would need the position of that other object to check the Distance. This can be done by using Vector3.Distance.
First however you need the Transform of the other object. You can do this by making a Raycast, in this case I'm casting the raycast from the middle of the screen. Then I will assign the Transform of whatever object was hit to the hitTransform variable. After that I can use a Vector3.Distance to compare 2 positions and calculate the distance.
Transform hitTransform;
private float distance;
void Update()
{
Ray ray = cam.ViewportPointToRay(new Vector3(0.5F, 0.5F, 0));
RaycastHit hit
if (Physics.Raycast(ray, out hit))
{
hitTransform = hit.transform;
}
distance = Vector3.Distance(hitTransform.position, transform.position);
}
So in short, if you look at an object that is in the middle of the screen the distance between that object and the camera will be in the distance variable, you can also use a raycast when you click or press a button but for the sake of simplicity I used the middle of the screen for this.

check if centerPlane is not near edge in unity

I'm trying to make whack a mole game using project tango.
When user start the game, the program will create holes at random point for the moles to come out. Right now, I already can make the hole and spawn the mole at random, though I have a problem with the created hole.
The hole sometimes spawn at an edge contour of table and stuff and make the hole partially floating in the air. Is there any way to check if the centerPlane is near an edge or not?
Here's the screenshot of the problem I meant. I want the "hole" not to spawn on area that doesn't fit with it's height and width. Currently, I'm using farandole release.
EDIT 1:
I'm trying to do as Hristo suggest. But it doesn't work, the FindClosestPoint always return -1, even when I use the center of the screen. Here's the script I used. And for some additional info, I'm using the unitySDK and unity 5.5.2f1
bool CheckTheCorner(Camera cam,Vector3 planeCenter){
Vector2 firstPointInScreen = WorldToScreenConverter(cam,new Vector3 (planeCenter.x,planeCenter.y,planeCenter.z-holeheight));
Vector2 secondPointInScreen = WorldToScreenConverter(cam,new Vector3 (planeCenter.x,planeCenter.y,planeCenter.z+holeheight));
Vector2 thirdPointInScreen = WorldToScreenConverter(cam,new Vector3 (planeCenter.x-holewidth,planeCenter.y,planeCenter.z));
Vector2 fourthPointInScreen = WorldToScreenConverter(cam,new Vector3 (planeCenter.x+holewidth,planeCenter.y,planeCenter.z));
DebugText.text = m_pointCloud.FindClosestPoint (cam, new Vector2(Screen.width / 2, Screen.height / 2), 1).ToString ();
Vector3 firstPoint = m_pointCloud.m_points[m_pointCloud.FindClosestPoint(cam, firstPointInScreen, 1)];
Vector3 secondPoint = m_pointCloud.m_points[m_pointCloud.FindClosestPoint(cam, secondPointInScreen, 1)];
Vector3 thirdPoint = m_pointCloud.m_points[m_pointCloud.FindClosestPoint(cam, thirdPointInScreen, 1)];
Vector3 fourthPoint = m_pointCloud.m_points[m_pointCloud.FindClosestPoint(cam, fourthPointInScreen, 1)];
return false;
}
Vector2 WorldToScreenConverter(Camera cam,Vector3 worldPos){
Vector3 screenPos = cam.WorldToScreenPoint (worldPos);
return new Vector2 (screenPos.x,screenPos.z);
}
Ah yes, don't mind the return false one for the moment, I just put it there to avoid error since I'm still figuring out the FindClosestPoint.
What you can do is take the 4 corners on your plane and decide if they are all laying on a surface in the real world, if not you can make the plane elsewhere.
That can happen with the use of FindClosestPoint() method in the TangoPointCloud.cs. What that method does is makes a Raycast from your camera trough a certain point on the screen landing in the real world environment. The method then returns the index of that point. The list to search with the indexes is called m_points
So lets split it in steps:
Make 4 Vectors using the `FindClosestPoint().
Check if all 4 vectors are on the same plane (simple math).
If step 2 is true -> Instantiate your GameObject on that plane.
To get one of the vectors your code should be something like this:
Vector3 firstPoint = m_pointCloud.m_points[m_pointCloud.FindClosestPoint(Camera.main, new Vector2(Screen.width / 2, Screen.height / 2), 1)];
In this example I'm using the center of the screen as my Vector2 parameter. However you don't want the center of the screen . Instead you want the position of one corner from your plane translated as a screen point.
Hope that solves your problem. Cheers.