Clamping an object axis in Vuforia AR camera - c#

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!

Related

How can I lock the rotation of a player in Unity?

I'm currently making a 2D game as a beginner and I made a spinning platform. But when it's rotating the player's rotation (z-axis) also changes because it's a child of the platform. I need this when I use moving platforms. Now I want to lock the z-axis of the rotation of the player. I already tried it in 3 different ways, but none of them seems to be working. Does anybody know how to do this?
These are the three ways I tried:
// 1
PlayerTrans.transform.Rotate(
PlayerTrans.transform.rotation.x,
PlayerTrans.transform.rotation.y,
0);
// 2
PlayerTrans.transform.Rotate(
PlayerTrans.transform.rotation.x,
PlayerTrans.transform.rotation.y,
0,
Space.Self);
// 3
PlayerTrans.transform.localRotation = Quaternion.Euler(new Vector3(
PlayerTrans.transform.localEulerAngles.x,
PlayerTrans.transform.localEulerAngles.y,
0f));
and this is, what my code looks like for staying on the moving platforms. I used raycasting for this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Raycasting : MonoBehaviour
{
// Start is called before the first frame update
Transform PlayerTrans;
public float RayCastRange = 3;
void Start()
{
PlayerTrans = transform.parent;
}
// Update is called once per frame
void Update()
{
RaycastHit2D PlattformCheck = Physics2D.Raycast(transform.position, -Vector2.up, RayCastRange);
if (PlattformCheck.collider != null)
{
if (PlattformCheck.collider.gameObject.tag == "Platform")
{
PlayerTrans.transform.SetParent(PlattformCheck.collider.gameObject.transform);
}
else
{
PlayerTrans.transform.SetParent(null);
}
}
else
{
PlayerTrans.transform.SetParent(null);
}
}
}
There are 2 ways that might help you:
Just freeze the rotation from the inspector:
you can use some LookAt function (there is one for 3D but you can look and find ones for 2D) and just look at the camera.
(if you cant find it let me know and I will add it)
You should raycast directly down and then apply velocities to both objects (un-child the player from the platforms). You could do something like this for the player:
public LayerMask mask; //set ‘mask’ to the mask of the
//platform in the Unity Editor.
public Vector3 velocity;
void Update()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, -Vector3.up, out hit, 0.1f, mask))
//0.1f is the distance to the platform to be able to be moved by the platform.
{
velocity = hit.collider.gameObject.GetComponent<Platform>().velocity;
}
float h = Input.GetAxis(“Horizontal”);
//this one is for CharacterController:
cc.Move(velocity);
//this one is for rigidbody:
rb.velocity = velocity;
velocity = 0;
}
It takes the velocity from the ‘Platform’ script and moves the player based on it. Now we should add the platform script. Call it ‘Platform’.
public Vector3 velocity;
public Vector3 a; //a and b are the two points you want the platform to go between.
public Vector3 b;
public float period = 2f; //the amount of seconds per cycle.
float cycles;
void Update()
{
cycles = Time.time / period;
float amplitude = Mathf.Sin(Mathf.PI * 2f * cycles);
Vector3 location = Vector3.Lerp(a, b, amplitude);
velocity = transform.position - location;
transform.position = velocity;
}
This script interpolates between the point a and b, then it finds the velocity and applies it. The player takes this velocity and it moves the player. Comment if you found an error.

How to make a ground object follow a flying object in unity?

I'm trying to make an object on the ground follow a flying object, for example a drone leading a human (for now i'm just using shapes - a cube and a capsule). My cube follows the capsule like i desire but i want the cube to follow the capsule on the ground only, rather than go up on the y-axis with the capsule. Right now, it follows the capsule everywhere, I want the capsule to lead while the cube follows along on the ground.
I have done some research on Google and Youtube but I have not seen any results. Please let me know how I can achieve this.
This is the code script attached to the cube(ground object)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class follow_target : MonoBehaviour
{
public Transform mTarget;
float mSpeed = 10.0f;
const float EPSILON = 0.1f;
Vector3 mLookDirection;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
mLookDirection = (mTarget.position - transform.position).normalized;
if((transform.position - mTarget.position).magnitude > EPSILON)
transform.Translate(mLookDirection * Time.deltaTime * mSpeed);
}
}
If the ground is planar, you can just set the y component to 0 (or whatever the ground y vector is).
If the ground changes in topology, you can do a raycast down from the capsule to get the hit point (vector3). You can use the hit point y component for the height. After that you will need to set the cubes rotation so that it is aligned to the ground. You could do that with a raycast as well, there are a number of examples of that online.
I hope that helps get you in the right direction.
Assuming a flat ground on Y = 0
either make sure your objects sticks to the ground so set
private const float EPSILONSQR = EPSILON * EPSILON;
void Update()
{
var difference = mTarget.position - transform.position;
mLookDirection = difference.normalized;
if(difference.sqrmagnitude > EPSILONSQR)
{
// In general be aware that Translate by default moves in the
// objects own local space coordinates so you probably would rather
// want to use Space.World
transform.Translate(mLookDirection * Time.deltaTime * mSpeed, Space.World);
var pos = transform.position;
// reset the Y back to the ground
pos.y = 0;
transform.position = pos;
}
}
or simply already map the direction down on the XZ plane (ignoring any difference in Y) like
private const float EPSILONSQR = EPSILON * EPSILON;
void Update()
{
var difference = mTarget.position - transform.position;
mLookDirection = difference.normalized;
// simply ignore the difference in Y
// up to you if you want to normalize the vector before or after doing that
mLookDirection.y = 0;
if(difference.sqrmagnitude > EPSILONSQR)
{
transform.Translate(mLookDirection * Time.deltaTime * mSpeed, Space.World);
}
}

Unity camera follows player script

I'm pretty new to Unity. I tried to create a script that the camera would follow the actor (with a little difference). Is there a way to improve the code? It works just fine. But I wonder if I did it the best way. I want to do it about as I wrote, so if you have any tips. Thank you
Maybe change Update to FixedUpdate ?
public GameObject player;
// Start is called before the first frame update
void Start()
{
player = GameObject.Find("Cube"); // The player
}
// Update is called once per frame
void Update()
{
transform.position = new Vector3(player.transform.position.x, player.transform.position.y + 5, player.transform.position.z - 10);
}
Making the camera following the player is quite straight forward.
Add this script to your main camera.
Drag the reference of the player object to the script and then you are done.
You can change the values in the Vector 3 depending on how far you want the camera to be from the player.
using UnityEngine;
public class Follow_player : MonoBehaviour {
public Transform player;
// Update is called once per frame
void Update () {
transform.position = player.transform.position + new Vector3(0, 1, -5);
}
}
Follows player in the back constantly when the player rotates, no parenting needed, with smoothing.
Piece of Knowledges:
Apparently, Quaternion * Vector3 is going to rotate the point of the Vector3 around the origin of the Vector3 by the angle of the Quaternion
The Lerp method in Vector3 and Quaternion stand for linear interpolation, where the first parameter gets closer to the second parameter by the amount of third parameter each frame.
using UnityEngine;
public class CameraFollow : MonoBehaviour
{
public Transform target;
public float smoothSpeed = 0.125f;
public Vector3 locationOffset;
public Vector3 rotationOffset;
void FixedUpdate()
{
Vector3 desiredPosition = target.position + target.rotation * locationOffset;
Vector3 smoothedPosition = Vector3.Lerp(transform.position, desiredPosition, smoothSpeed);
transform.position = smoothedPosition;
Quaternion desiredrotation = target.rotation * Quaternion.Euler(rotationOffset);
Quaternion smoothedrotation = Quaternion.Lerp(transform.rotation, desiredrotation, smoothSpeed);
transform.rotation = smoothedrotation;
}
}
This will always follow the player from the same direction, and if the player rotates it will still stay the same. This may be good for top-down or side-scrolling view, but the camera setup seems to be more fitting for 3rd person, in which case you'd want to rotate the camera when the player turns.
The easiest way to do this is actually not with code alone, simply make the camera a child of the player object, that way its position relative to the player will always stay the same!
If you do want to do it through code, you can change the code to be like this:
void Update()
{
Vector3 back = -player.transform.forward;
back.y = 0.5f; // this determines how high. Increase for higher view angle.
transform.position = player.transform.position - back * distance;
transform.forward = player.transform.position - transform.position;
}
You get the direction of the back of the player (opposite of transform's forward). Then you increase the height a little so the angle will be a bit from above like in your example. Last you set the camera's position to be the player's position and add the back direction multiplied by the distance. That will place the camera behind the player.
You also need to rotate the camera so it points at the player, and that's the last line - setting the camera's forward direction to point at the player.
Here is just another option. I always find it easier to have the variables which are populated in the inspector so you can adjust it and fine tune it as needed.
public GameObject player;
[SerializeField]
private float xAxis, yAxis, zAxis;
private void Update()
{
transform.position = new Vector3(player.transform.position.x + xAxis, player.transform.position.y + yAxis, player.transform.position.z + zAxis);
}

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

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.

Why does my directional light only rotate from 90º to -90º (Unity 5.5)

I have tried both of these C#scripts to rotate my directional light:
using System.Collections;
using UnityEngine;
public class LightRotator : MonoBehaviour
{
void Update ()
{
transform.rotation = Quaternion.Euler(transform.eulerAngles.x + 1.0f,
transform.eulerAngles.y,
transform.eulerAngles.z);
}
}
and
using System.Collections;
using UnityEngine;
public class LightRotator : MonoBehaviour
{
void Update ()
{
transform.localEulerAngles = new Vector3(transform.localEulerAngles.x + 1.0f,
transform.localEulerAngles.y,
transform.localEulerAngles.z);
}
}
They both seem to function exactly the same: If I change transform.eulerAngles.y to transform.eulerAngles.y + 0.5f, the light will rotate along the y-axis, and the same works for the z-axis. However, when I try to do this with the x-axis, it will rotate until it hits 90º, at which point it will continue to attempt rotation but it immediately and continuously shoved back to 90º. If I reverse the direction, it does the same thing at -90º. For example, the rotation might be: 88.5,89.0,89.5,90.0, 90.5, 89.93, 90.24, 89.4, etc.
What is causing this clamping and how do I fix it?
I think this is what you are looking for: http://answers.unity3d.com/questions/187073/rotation-locks-at-90-or-270-degrees.html
In order to fix your problem, you need to use an additional vector, change it inside Update every frame, and then pass it to the eulerAngles propriety of the transform.
Vector3 vect = Vector3.zero;
float rotationSpeed = 10f;
void Start () {
vect = transform.eulerAngles; //Set the vect rotation equal to the game object's one
}
void Update ()
{
vect.x += rotationSpeed * Time.deltaTime;
//Pass unique eulerAngles representation to the object without letting Unity change it
transform.eulerAngles = vect;
}
This happens btw because there're multiple euler angles representation of a single physical rotation in the 3D space, and when you work directly on the eulerAngles propriety of the transform, Unity makes some work behind the scenes, which can lead to a gimbal lock.
Use Quaternions. It's what Unity3d uses internally and doesn't have any of the side effects of euler angles.
using System.Collections;
using UnityEngine;
public class LightRotator : MonoBehaviour
{
public Vector3 RotationAxis = Vector3.right;
Quaternion _startRotation;
float _rotationIncrement = 0;
void Start()
{
_startRotation = transform.rotation;
}
void Update ()
{
Quaternion rotationMod =
Quaternion.AngleAxis(_rotationIncrement, RotationAxis);
_rotationIncrement += 1;
transform.rotation = _startRotation * rotationMod;
}
}
However, you probably want to use something like Quaternion.RotateTowards or Quaternion.Lerp along with Time.time and a rate. You will get much smoother results that way.
if you only want to rotate along the X-axis then set the other axis as 0.

Categories