I am fairly new to Unity so please bear with me I have tried looking for the answer everywhere, but have had no luck.
Basically I am using onMouseDrag to move a sprite around the background for a classroom (1366x768) that has a table. However, I want to limit where the sprite can go so that it does not end up off screen or off of the table on my background.
My sprite has the 2d box collider and rigidbody components attached (gravity is set to zero and it is at a fixed angle). I thought that by placing four 2d box colliders around the area I want to keep the sprite in it would be enough to contain it but the sprite simply goes straight through them.
I also read up about using Mathf.Clamp to restrict the area but I do not really understand how to use it from the examples I have seen.
Below is my code for moving the sprite:
using UnityEngine;
using System.Collections;
public class MovementScript : MonoBehaviour {
float x;
float y;
void Update() {
x = Input.mousePosition.x;
y = Input.mousePosition.y;
}
public void OnMouseDrag() {
transform.position = Camera.main.ScreenToWorldPoint (new Vector3 (x, y, 1.0f));
}
}
Any help would be greatly appreciated!
Moving an object using its transform is not the same as moving it.
When you use the transform, the object doesn't "move", it teleports every Update by a small amount. Unfortunately, the Rigidbody can't detect this change in position as movement and thus does not react with any colliders. Regardless, that's probably the more complicated way to do this. Using Clamp is definitely easier.
Clamp is a pretty straightforward function. it takes three args: a value, a min, and a max. If the value is less than min or greater than max, it returns that boundary. Otherwise, it returns the value itself.
For instance,
Mathf.Clamp(5, 1, 3); //returns 3
Mathf.Clamp(2, 1, 3); //returns 2
Mathf.Clamp(-2, 1, 3); //returns 1
This is simply a convenience function for something like this:
if(val > max) {
return max;
} else if(val < min) {
return min;
} else {
return val;
}
So using Clamp, you can restrict the values of your x and y coordinates:
//to avoid confusion, I'm referring to your x and y variables
//as inputX and inputY. they represent the mouse position.
public void OnMouseDrag() {
Vector3 pos = Vector3.zero;
pos.x = Mathf.Clamp(inputX, minX, maxX);
pos.y = Mathf.Clamp(inputY, minY, maxY);
pos.z = 1.0;
transform.position = Camera.main.ScreenToWorldPoint (pos);
}
Mathf.Clamp will keep the X and Y coordinates of your Transform within the range (minX, maxX) and (minY, maxY) respectively. You can create these variables as inspector variables so you can change them on the fly.
Related
I am trying to achieve an helical movement with min/max limiation :
positive rotation > negative transform
negative rotation > positive transform
Both positive & negative rotation would be done with the MRTK hands interactions provided script, so this is not my problem.
I (think) that I am struggling with the logic.
tl;dr : I want to screw something using Unity, MRTK and virtual hands.
I ended up solving my problem, after days of scratching my head really, really hard.
To be honest, I probably wouldn't have succeeded on my own, because of a logic problem (this is very often the case, isn't it?).
Initial Setup
In my case, the gameObject to interact with, is a screw.
I wanted it to perform a positive translation when a positive rotation occurs, vice-versa.
On this gameObject are attached some components :
a collider (mesh, box, capsule)
a rigidbody
a PointerHandler.cs (MRTK)
a ObjectManipulator.cs (MRTK)
a NearInteractionGrabbable.cs (MRTK)
the TwistingRotation.cs found below
The rigidbody, PointerHandler.cs, ObjectManipulator.cs and the TwistingRotation.cs configurations can be found here :
The magic appear with the PointerHandler.cs that allow us to detect when the near interaction happen with the screw, thx to the OnPointerDragged event.
TwistingRotation.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Translation of the object while rotating it when grabbed using the MRTK.ObjectManipulator
/// Boundaries and axis restrictions ongoing
/// </summary>
public class TwistingRotation : MonoBehaviour
{
/*******CACHE REFERENCE*******/
private Transform _myTransform;
[SerializeField] private float translationFactor = 90f;
private Vector3 _minPosition;
private Vector3 _maxPosition;
private Vector3 _previousVector;
private Rigidbody _rb;
private void Start()
{
// Cache reference
_myTransform = gameObject.transform;
_rb = gameObject.GetComponent<Rigidbody>();
// Reference for the previous rotation vector
_previousVector = _myTransform.up;
// Default position is the maximum transform.position (unscrewed)
_maxPosition = _myTransform.position;
// Minimum position is default transform.position + 1unit in local space direction
_minPosition = _maxPosition + Vector3.forward;
}
/// <summary>
/// Move the object according to the rotation angle value
/// A positive rotation leads to a positive translation, and vice-versa
/// </summary>
public void TranslateRotation()
{
// Retrieve the angle on a defined local axis when the rotation occur
var currentVector = _myTransform.up;
// Compute the angle between the previous and the current vector on a defined local axis
// Difference between the previous rotation vector, and the actual, on a global axis
var angle = Vector3.SignedAngle(_previousVector, currentVector, Vector3.forward);
// Move object proportional to its rotation
var translation = Vector3.forward * (angle / translationFactor);
_myTransform.Translate(translation, Space.Self);
// Get the GO current position
var currentPosition = _myTransform.position;
// Clamp for each axis between _minPosition and _maxPosition (the default spawn position)
// Doing a Mathf.Min/Max inside the Mathf.Clamp to insure that the min and max values are correct
var x = Mathf.Clamp(currentPosition.x, Mathf.Min(_minPosition.x, _maxPosition.x), Mathf.Max(_minPosition.x, _maxPosition.x));
var y = Mathf.Clamp(currentPosition.y, Mathf.Min(_minPosition.y, _maxPosition.y), Mathf.Max(_minPosition.y, _maxPosition.y));
var z = Mathf.Clamp(currentPosition.z, Mathf.Min(_minPosition.z, _maxPosition.z), Mathf.Max(_minPosition.z, _maxPosition.z));
// Compute the new position while taking the boundaries into consideration
var newPosition = new Vector3(x, y, z);
_myTransform.position = newPosition;
// Save position for the next frame
_previousVector = currentVector;
}
}
What is happening ?
Quite simple : the screw is translating over 1 (unity) unit while rotating.
The value of the rotation depend on the value of the translationFactor variable.
In my case, the value is 360 so a complete rotation over a 1 (unity) unit translation.
Result
It is far from being perfect, probably very much "meh" but hey, it is working (not as intended tho, but still) and it allowed me to move forward and I made my presentation.
Im trying to move an object in a 3d world using a controller, but think I am missing something cus it just clips away as soon as I give any input:
private void Update()
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
Vector3 movement = new Vector3(h, 0, v).normalized * Time.deltaTime * speed;
if(h != 0 || v != 0)
_rigidBody.MovePosition(movement);
}
Works fine using _rigidbody.velocity, but as I understand it that should be avoided for these types of things.
Rigidbody.MovePosition sets the position of the rigidbody with interpolation. It looks like you want to offset the position by movement, so you should probably set the velocity. If you do still want to use MovePosition, you should do _rigidBody.MovePosition(transform.position + movement);.
"using a controller"
Do you mean Character Controller Component? You have 2 options and they are both well explained in Unity Documentation.
Second is by Rigidbody Component.
I would like to simulate a vortex like force to a "bunch" of objects in my scene.
How can I do in Unity ?
Thanks
If you are using the physics system, there's two parts to this. Applying the vortex force, and getting the nice swirling effect. To apply the vortex force you can just loop over the rigidbodies and apply the force. To make the swirl look like a proper vortex swirl, you need to start the objects off with a tangential velocity that you can figure out using the vector cross product.
public float VortexStrength = 1000f;
public float SwirlStrength = 5f;
void Start () {
foreach(GameObject g in RigidBodies){
//to get them nice and swirly, use the perpendicular to the direction to the vortex
Vector3 direction = Vortex.transform.position - g.transform.position;
var tangent = Vector3.Cross(direction, Vector3.up).normalized * SwirlStrength;
g.GetComponent<Rigidbody>().velocity = tangent;
}
}
void Update(){
//apply the vortex force
foreach(GameObject g in RigidBodies){
//force them toward the center
Vector3 direction = Vortex.transform.position - g.transform.position;
g.GetComponent<Rigidbody>().AddForce(direction.normalized * Time.deltaTime * VortexStrength);
}
}
Circular motion:
float angle =0;
float speed=(2*Mathf.PI)/5 //2*PI in degress is 360, so you get 5 seconds to complete a circle
float radius=5;
void Update()
{
angle += speed*Time.deltaTime; //if you want to switch direction, use -= instead of +=
x = Mathf.Cos(angle)*radius;
y = Mathf.Sin(angle)*radius;
}
where the center of your circle is the center of your vortex.
Of course:
If you want multiple objects with diferent distance from vortex's
center you have to play with your radius variable (i would add a
Random.Range(minDistance, maxDistance))
If you want diferent speeds you can randomize/change the speed.
Randomize/change your x/y if you don't want a perfect circle
Hope i was clear enought.
I a new here and i try to start working with Unity Engine.
Could somebody explain me, how works Quaternion.Slerp? Because I want to rotate some object in different angles 90, 180 and 270. My code you can see below. Unfortunately when I add 180 degrees, object make crazy things and than put rotation to (0, 180, 180) for this game object. I would like to get (180,0,0)
public float speed = 0.1F;
private float rotation_x;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
rotation_x = transform.rotation.eulerAngles.x;
rotation_x += 180;
}
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(rotation_x, transform.eulerAngles.y, transform.eulerAngles.z), Time.time * speed);
}
Most examples out there including Unity examples from their official website are using Lerp in the wrong way. They didn't even bother to describe how it works in the API documentation. They just starch it in the Update() function and call it a day.
Mathf.Lerp, Vector3.Lerp, and Quaternion.Slerp work by changing from one position/rotation to another with the t value(last parameter) being passed in.That t value is also know as time.
The min of the t value is 0f and the max is 1f.
I will explain this with Mathf.Lerp to make it easier to understand. The Lerp functions are all the-same for both Mathf.Lerp, Vector and Quaternion.
Remember that Lerp takes two values and returns values between them. If we have a value of 1 and 10 and we do Lerp on them:
float x = Mathf.Lerp(1f, 10f, 0f); will return 1.
float x = Mathf.Lerp(1f, 10f, 0.5f); will return 5.5
float x = Mathf.Lerp(1f, 10f, 1f); will return 10
As you can see, the t(0) returns the min of the number passed in, t(1) returns the max value passed in and t(0.5) will return mid point between the min and the max value. You are doing it wrong when you pass any t value that is < 0 or > 1. That code in you Update() function is doing just that. Time.time will increase every second and will be > 1 in a second, so you have problems with that.
It recommended to use Lerp in another function/Coroutine instead of the Updated function.
Note:
Using Lerp has a bad side of it when it comes to rotation. Lerp does not know how to rotate Object with the shortest path. So bear that in mind. For example, you have an Object with 0,0,90 position. Lets say you want to move the rotation from that to 0,0,120 Lerp can sometimes rotate left instead of right to reach that new position which means it take longer to reach that distance.
Let's say we want to make the rotation (0,0,90) from whatever the current rotation is. The code below will change the rotation to 0,0,90 in 3 seconds.
ROTATION OVER TIME:
void Start()
{
Quaternion rotation2 = Quaternion.Euler(new Vector3(0, 0, 90));
StartCoroutine(rotateObject(objectToRotate, rotation2, 3f));
}
bool rotating = false;
public GameObject objectToRotate;
IEnumerator rotateObject(GameObject gameObjectToMove, Quaternion newRot, float duration)
{
if (rotating)
{
yield break;
}
rotating = true;
Quaternion currentRot = gameObjectToMove.transform.rotation;
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
gameObjectToMove.transform.rotation = Quaternion.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
rotating = false;
}
INCREMENTAL ANGULAR ROTATION OVER TIME:
And to just rotate the Object to 90 in z axis, the code below is a great example of that. Please understand there is a difference between moving Object to new rotational point and just rotating it.
void Start()
{
StartCoroutine(rotateObject(objectToRotate, new Vector3(0, 0, 90), 3f));
}
bool rotating = false;
public GameObject objectToRotate;
IEnumerator rotateObject(GameObject gameObjectToMove, Vector3 eulerAngles, float duration)
{
if (rotating)
{
yield break;
}
rotating = true;
Vector3 newRot = gameObjectToMove.transform.eulerAngles + eulerAngles;
Vector3 currentRot = gameObjectToMove.transform.eulerAngles;
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
gameObjectToMove.transform.eulerAngles = Vector3.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
rotating = false;
}
All my examples are based on frame-rate of the device. You can use real-time by replacing Time.deltaTime with Time.delta but more calculation is required.
Before anything, you can't add 180 on euler angles like that, and that's mainly what is causing your problem. You'd better use quaternion directly instead, or work on the transform itself.
You can think of a quaternion as an orientation in space. In contrary to what have been said, I do recommend learning how to use them if you can. However, I don't recommend using euler angles at all... as they're suject to different writing conventions, and will fail sometimes. You can look at 'gimbal lock' if you want details about that.
Simply a slerp or lerp (standing for spherical linear interpolation, or linear interpolation respectively) is a way to interpolate (go from one orientation to another, by increasing t from 0 to 1, in a coroutine or anywhere else) between orientation A and B. The difference between the two is that the slerp is giving you the shortest path from A to B.
In the end, when t = 1, lerp(A,B,t) and slerp(A,B,t) will give you B.
In your case, if you want to instantly rotate an object in space to a specific orientation, I suggest you use Quaternion.AngleAxis which is the most forward way to describe mathematically a quaternion.
If you want to add a rotation, say 90° to you actual orientation (without animation between the two), you can do something like this :
transform.rotation *= Quaternion.AngleAxis(axis_of_rotation, angle)
or use transform.rotate (depending on the parameters, it can be a right multiply, or left : local, or world transform).
Programmers' answer is detailling how to animate your transform. But I do suggest you to investigate quaternion themselves, as it will give you global understanding of space transforms.
I have a sphere gameobject with 5 cubes placed on different points on the surface of the sphere. When a key is pressed, i would like the sphere to spin for a few seconds and then slowly stops on the first cube point on the cube and always keeping the same direction in rotation. The issue i am facing now is that, Quaternion.Slerp always takes the shortest path to the next cube which means sometimes the rotation direction is changed. Any ideas?
Thanks
You can easily handle the rotation as a Vector3 of Euler angles.
This way you can Linearly Interpolate the angles to the correct value. And you can use coterminal angles so that you're always interpolating to a higher value ( so no backwards rotations would occur ). After every rotational step you might want to normalize the angles to the 0, 360 range with this approach though.
Example Code :
using UnityEngine;
using System.Collections;
public class Rotation : MonoBehaviour {
public Transform firstPosition;
public Transform secondPosition;
public float rotationDuration = 3;
void Start () {
transform.rotation = firstPosition.rotation;
StartCoroutine(Rotate());
}
IEnumerator Rotate() {
var deltaTime = 0.0f;
var distance = firstPosition.rotation.eulerAngles - secondPosition.rotation.eulerAngles;
while(deltaTime < rotationDuration) {
var rotation = transform.rotation.eulerAngles;
rotation = firstPosition.rotation.eulerAngles + deltaTime/rotationDuration*distance;
transform.rotation = Quaternion.Euler(rotation);
deltaTime += Time.deltaTime;
yield return null;
}
transform.rotation = secondPosition.rotation;
}
}