Rotate object in Unity 2D - c#

Help to understand the management of objects. At the moment, there is a rotation of the object. I want the arrow to rotate, and the angle of rotation depends on the current X and Y coordinates. Unity2D.
Now it is left (does not rotate), but it needs to be right (it always rotates and looks in one direction), but I don’t know how to calculate the degrees for rotation.
`
public float angle = 0; // угол
public float radius = 0.5f; // радиус
public bool isCircle = false; // условие движения по кругу
public float speed = 5f;
// Update is called once per frame
void Update()
{
angle += Time.deltaTime; // меняется значение угла
var x = Mathf.Cos(angle * speed) * radius + parent.position.x;
var y = Mathf.Sin(angle * speed) * radius + parent.position.y;
transform.position = new Vector3(x, y,0);
//transform.Rotate(0, 0, a);
}
`
Help me, how to calculate angle?

You need Mathf.Atan2, it will return a radian, then you need to multiply a Mathf.Rad2Deg to get the Euler angle.

Related

How to use LookRotation to rotate an object based on hand positions?

I'm creating an interaction system for a VR game and I'm struggling with two hand interactions. I'm using the Quaternion.LookRotation() method to generate the rotation of the grabbed object based on the positions and rotations of the hands. The forward part is pretty simple:
Vector3 fwd = (primaryHand.position - secondaryHand.position).normalized;
The "up" part is what I have difficulty with. Initially I tried using the average up direction of both hands:
Vector3 avgUp = Quaternion.Slerp(primaryHand.rotation, secondaryHand.rotation, 0.5f) * Vector3.up;
There is an issue with this approach: the hand's up vector might get aligned with the fwd vector, which causes the object to flip over when it goes over it. Here is a simple illustration of the problem:
The light green arrows represent the up direction of the hands, while the dark green is the calculated direction used as an argument for the LookRotation() method.
The obvious solution seems to be to pick a different fixed vector instead of "up", one which won't be so easily aligned with the fwd vector. In the example it could be a vector aligned with the fingers. But keep in mind that there are no restrictions on initial hand rotation so no matter which vector you choose the hands can always happen to be rotated so that the vectors align. And even if you pick the an optimal vector dynamically (one that is perpendicular to fwd), it's still at best 90 degrees from aligning with fwd.
To solve this I tried restricting the direction to the values which don't cause problems but this caused another issues (I had difficulties with determining which values are ok and which should be discarded). I feel like I'm doing something wrong here, is there any better solution to this problem?
You can calculate the delta rotations of the hands and apply it to the "base up" of the object (the new up if we only take into account the change in position of hands...which will of course be orthogonal to the axis of the object). Then determine the change in angle that results in that up being rotated with each hand. Average those angles out, then apply those angles with the hand-hand axis using Quaternion.AngleAxis to the "base up" from earlier. Then you have your forward and up for Quaternion.LookRotation).
Below is an example of how you can use this, including VR hand noise simulation. To see the test, create a new scene in unity and attach this to the camera and it will build the scene on play start. There is a grip/release gui that will appear in Play view. You can adjust the hand rotation in Scene view
Vector3 leftHandPosCurrent;
Vector3 rightHandPosCurrent;
Vector3 worldAxisPrev;
Quaternion leftHandRotPrev;
Quaternion leftHandRotCurrent;
Quaternion rightHandRotPrev;
Quaternion rightHandRotCurrent;
bool isGripping;
bool firstFrameGripping;
Rigidbody grippedRB;
Transform leftHand;
Transform rightHand;
Quaternion targetRot;
/*
* On subsequent frames of gripping, calculate deltas in positions and
* rotations, average out the hand's effects, then apply them to the gripped
* object
*/
void HandleGrippedRot()
{
Vector3 worldAxisCurrent = rightHandPosCurrent - leftHandPosCurrent;
if (!firstFrameGripping)
{
Vector3 prevUp = targetRot * Vector3.up;
// we haven't moved the transform based on the hands yet, so
// find the new up would be ignoring hand rotations
Vector3 newUp = Quaternion.FromToRotation(worldAxisPrev,
worldAxisCurrent) * prevUp;
float leftHandAngle = GetDegRot(newUp, leftHandRotPrev,
leftHandRotCurrent, worldAxisCurrent);
float rightHandAngle = GetDegRot(newUp, rightHandRotPrev,
rightHandRotCurrent, worldAxisCurrent);
float avgAngle = (leftHandAngle + rightHandAngle) * 0.5f;
newUp = Quaternion.AngleAxis(avgAngle, worldAxisCurrent) * prevUp;
targetRot = Quaternion.LookRotation(worldAxisCurrent,
newUp);
}
else
{
firstFrameGripping = false;
}
leftHandRotPrev = leftHandRotCurrent;
rightHandRotPrev = rightHandRotCurrent;
worldAxisPrev = worldAxisCurrent;
}
/*
* Given the "up" of the object without taking hand rotations into account
* and the axis, determine how a hand's delta rotation affects that up
* around the axis and return the angle of that rotation
*/
float GetDegRot(Vector3 baseUp, Quaternion prevHandRot, Quaternion curHandRot,
Vector3 axis)
{
Vector3 adjUp = (curHandRot * Quaternion.Inverse(prevHandRot)) * baseUp;
adjUp = Vector3.ProjectOnPlane(adjUp, axis);
return Vector3.SignedAngle(baseUp, adjUp, axis);
}
void Update()
{
AddVRNoise(leftHand);
AddVRNoise(rightHand);
leftHandPosCurrent = leftHand.position;
rightHandPosCurrent = rightHand.position;
leftHandRotCurrent = leftHand.rotation;
rightHandRotCurrent = rightHand.rotation;
if (isGripping)
{
HandleGrippedRot();
}
}
void StartGrip()
{
if (isGripping) return;
isGripping = true;
firstFrameGripping = true;
// grippedTransform is set accordingly at some point
}
void EndGrip()
{
if (!isGripping) return;
isGripping = false;
}
/*
* example of using targetRot to move rb
*/
private void FixedUpdate()
{
if (!isGripping) return;
Quaternion delta = targetRot
* Quaternion.Inverse(grippedRB.transform.rotation);
delta.ToAngleAxis(out float angle, out Vector3 axis);
// convert to shortest angle form
if (angle > 180f)
{
axis = -axis; angle = 360f - angle;
}
grippedRB.angularVelocity = angle * 0.25f * axis;
}
/*
* just for testing purposes
*/
void Start()
{
leftHand = CreateHand(true);
leftHand.position = Vector3.left;
rightHand = CreateHand(false);
rightHand.position = Vector3.right;
CreateArrow();
}
/*
* just for testing purposes
*/
void AddVRNoise(Transform hand)
{
Quaternion noise = Random.rotation;
noise.ToAngleAxis(out float angle, out Vector3 axis);
angle = 100f * Time.deltaTime;
noise = Quaternion.AngleAxis(angle, axis);
Quaternion noisyRot = hand.rotation * noise;
hand.rotation = noisyRot;
}
/*
* just for testing purposes
*/
void OnGUI()
{
if (GUI.Button(new Rect(0, 0, 100, 50), "Grip"))
{
StartGrip();
}
if (GUI.Button(new Rect(100, 0, 100, 50), "Release"))
{
EndGrip();
}
}
/*
* just for testing purposes
*/
Transform CreateHand(bool isLeft)
{
string handName = isLeft ? "Left" : "Right";
GameObject hand = new GameObject($"{handName}hand");
GameObject palm = GameObject.CreatePrimitive(PrimitiveType.Cube);
palm.transform.localScale = new Vector3(0.5f, 0.2f, 1f);
palm.transform.SetParent(hand.transform);
GameObject thumb = GameObject.CreatePrimitive(PrimitiveType.Cube);
thumb.transform.localScale = new Vector3(0.2f, 0.1f, 0.1f);
thumb.transform.SetParent(hand.transform);
thumb.transform.localPosition = new Vector3(isLeft ? 0.32f : -0.32f,
0f, -.31f);
return hand.transform;
}
/*
* just for testing purposes
*/
void CreateArrow()
{
GameObject arrow = new GameObject();
GameObject body = GameObject.CreatePrimitive(PrimitiveType.Cube);
body.transform.localScale = new Vector3(1f, 1f, 5f);
body.transform.SetParent(arrow.transform);
GameObject head = GameObject.CreatePrimitive(PrimitiveType.Cube);
head.transform.SetParent(arrow.transform);
head.transform.localEulerAngles = Vector3.up * 45f;
head.transform.localPosition = new Vector3(0f, 0f, 2.5f);
grippedRB = arrow.AddComponent<Rigidbody>();
grippedRB.useGravity = false;
arrow.transform.position = 2f * Vector3.up;
}

Movement without Riggibody

How to do without Rigidbody, rotate the object along the Y axis in both directions, and move the object forward in the right direction? How can you add acceleration and inertia to an object?
You can have a look at the Unity Documentation Here, or look at code provided.
float horizontalSpeed = 2.0f;
float verticalSpeed = 2.0f;
float speed = 10.0f;
void Movement()
{
// Get the horizontal and vertical axis.
// By default they are mapped to the arrow keys.
// The value is in the range -1 to 1
float x = Input.GetAxis("Vertical");
float z = Input.GetAxis("Horizontal");
// Make it move 10 meters per second instead of 10 meters per frame...
var moveVector = new Vector3(x, 0 , 0);
// Move translation along the object's z-axis
transform.Translate(moveVector * Time.deltaTime);
}
void Rotation()
{
// Get the mouse delta. This is not in the range -1...1
float h = horizontalSpeed * Input.GetAxis("Mouse X");
float v = verticalSpeed * Input.GetAxis("Mouse Y");
transform.Rotate(v, h, 0);
}

Cylinder Snapping into place during Quaternion.RotateTowards

What I am trying to do is have a pillar in the center of a scene, and what i think is happening is the quaternion.RotateTowards is receiving a different starting/initial quaternion than the action which is causing it to snap/teleport into a different location which then starts to move to the next. I thought it may be because i'm misunderstanding how quaternions are handled in unity, so i've tried messing with normalizing it, but i can't seem to get any change on the teleport.
The goal is to attach this scrip to a simple 3D cylinder and have it wobble basically, where there will be a player on top of it trying to stay on it. However I can't seem to figure out why it is teleporting and was hoping for a second set of eyes.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlatformWobble : MonoBehaviour
{
public float timeDelay = 0;
public float rRange = 0.5f;
public float maxRotation = 20.0f;
public float rotRate = 0.05f;
private bool wobble = false;
private Vector3 randomRotation;
private Quaternion rotation;
private Quaternion destination;
private float x, y, z;
private bool inPlace;
private void Start()
{
randomRotation = new Vector3();
inPlace = true;
}
// Update is called once per frame
void FixedUpdate()
{
if (inPlace)
{
destination = RotateRandomly();
inPlace = false;
}
transform.localRotation = Quaternion.RotateTowards(transform.localRotation, destination, rotRate * Time.deltaTime);
if (transform.localRotation == destination)
{
inPlace = true;
}
}
//This will grab the rotation of the object and move it randomly, but won't exceed maxRotation;
Quaternion RotateRandomly()
{
randomRotation = new Vector3(Random.Range(-rRange, rRange), Random.Range(-rRange, rRange), Random.Range(-rRange, rRange));
rotation = transform.localRotation;
x = rotation.x + randomRotation.x;
if(x >= maxRotation && x <= 360 - maxRotation) { x = rotation.x; }
y = rotation.y + randomRotation.y;
if (y >= maxRotation && y <= 360 - maxRotation) { y = rotation.y; }
z = rotation.z + randomRotation.z;
if (z >= maxRotation && z <= 360-maxRotation) { z = rotation.z; }
return new Quaternion(x, y, z, transform.localRotation.w);
}
}
Your quaternion calculations are incorrect: quaternions do not hold angles, instead, they hold the representation of a rotation about an axis. I emphasized "representation" because it is a little complicated...
The x, y, z components of a quaternion hold the rotation axis unit vector scaled by the sine of the half angle of rotation. The w component holds the cosine of the half angle. That is...
// NOTE: rotationAngle is in radians rather than degrees
Quaternion MakeQuaternion (Vector3 rotationAxis, float rotationAngle)
{
float c = Mathf.Cos (rotationAngle / 2);
float s = Mathf.Sin (rotationAngle / 2);
Vector3 v = rotationAxis.normalized * s;
return new Quaternion (v.x, v.y, v.z, c);
}
Now, the tricky part for your problem is coming up with the rotation axis and angle for the desired effect.
One solution (if you wish to stick to Euler angles) is to compute quaternions for each Euler rotation and then combine them:
Quaternion EulerToQuat (float XAngle, float YAngle, float ZAngle)
{
Quaternion X = MakeQuaternion (Vector3.right, XAngle);
Quaternion Y = MakeQuaternion (Vector3.up, YAngle);
Quaternion Z = MakeQuaternion (Vector3.forward, ZAngle);
// combine the rotations such that the object is first rotated about the Z axis,
// then about the Y axis, then the X (ie, reverse order of multiplication).
// Reminder: quaternion multiplicate is not commutative: order matters, so if this
// is not the order you want, just siwtch things around
rotate X * Y * Z;
}
FixedUpdate doesn't get called every render frame, only on physics frames. You need a way to tell Unity to have each frame showing the rotation change, rather than only updating the rendering when the physics frame has just run.
That is what Rigidbody.MoveRotation is for. Cache a reference to the Rigidbody, calculate the new global rotation it should have, then call MoveRotation:
private Rigidbody rb;
private void Start()
{
randomRotation = new Vector3();
inPlace = true;
rb = GetComponent<Rigidbody>();
}
// ...
// Update is called once per frame
void FixedUpdate()
{
if (inPlace)
{
destination = RotateRandomly();
inPlace = false;
}
Quaternion newLocalRot = Quaternion.RotateTowards(transform.localRotation,
destination, rotRate * Time.deltaTime);
Quaternion newGlobalRot = transform.parent.rotation * newLocalRot;
rb.MoveRotation(newGlobalRot);
if (transform.localRotation == destination)
{
inPlace = true;
}
}

How to clamp z rotation of a gameobject using touch input in unity without creating a jitter on y rotation

I am able to clamp the z rotation of my game object using the code below, but it always changes the y rotation on my first touch. I only want to rotate the z-axis.
private float zRotation;
public void RotateMove()
{
zRotation -= Input.GetTouch(0).deltaPosition.y * rotateSpeed * invert * Time.deltaTime;
// this limits the rotatioon
zRotation = Mathf.Clamp(zRotation, -80, 80);
//this rotates the game obj
lamp2Rotate.transform.eulerAngles = new Vector3(0.0f, 0.0f, zRotation);
}
sounds like 0 is not what you want for Y
You could instead keep the other two rotations:
var eulerAngles = lamp2Rotate.transform.eulerAngles;
eulerAngles.z = zRotation;
lamp2Rotate.transform.eulerAngles = eulerAngles;

Unity2D Smooth Rotation using Slerp

I'm making a patrol script for a game object. I want the object to rotate smoothly and slowly to face it's patrol target.
Unfortunately, the object snaps to it's new location.
I've asked on unity forums and can't get an answer.
How can I get the rotation to be smooth and slow?
Here's my code.
public Transform[] patrol;
public int Currentpoint;
public float moveSpeed;
public Vector3 john = new Vector3(0,1,0);
public Vector3 targetLocation;
void Start()
{
}
void Update()
{
if (transform.position == patrol [Currentpoint].position) {
Currentpoint++;
if (Currentpoint >= patrol.Length) {
Currentpoint = 0;
}
targetLocation = patrol [Currentpoint].position;
Vector3 targetDir = targetLocation - transform.position;
float angle = Mathf.Atan2 (targetDir.y, targetDir.x) * Mathf.Rad2Deg;
transform.localRotation = Quaternion.SlerpUnclamped (transform.localRotation, Quaternion.AngleAxis (angle, Vector3.forward), Time.deltaTime * 3);
Debug.Log (Currentpoint);
}
transform.position = Vector3.MoveTowards (transform.position, patrol [Currentpoint].position, moveSpeed * Time.deltaTime);
}
public static Quaternion Slerp(Quaternion a, Quaternion b, float t); The parameter t is clamped to the range [0, 1]
your times 3 is throwing it out so it is not between 0-1 ... I believe
If you like some mathematics then I have a solution for you.
You want to rotate object around another, like Earth and Sun. May be some other solutions may available but I would do it through LookAt and parametric equation of circle.
x = r * cos(theta) + displacementX
y = r * sin(theta) + displacementY
where r is radius, distance in your case
displacementX and displacementY are the distance from origin. If both (displacementX and displacementY) is 0 then it will rotate around origin (0,0). In other words displacementX and displacementY is the origin of rotation.
In Object(Earth) script, do it as follow
public Transform _sun;
float _theta = 0;
void Start ()
{
StartCoroutine ("ChangeAngle");
}
void Update ()
{
transform.LookAt (_sun);
float newX = (5 * Mathf.Cos (_theta)) + _sun.position.x;
float newY = (5 * Mathf.Sin (_theta)) + _sun.position.y;
transform.position = new Vector3 (newX, newY, _sun.position.z);
}
IEnumerator ChangeAngle ()
{
while (true) {
yield return new WaitForSeconds (0.01f);
_theta += 0.1f;
if (_theta >= 360)
_theta = 0;
}
}
You can further play with it
I found an answer,
It turns out that placing the rotational instructions within the if statement was the problem. I converted the rotation to a function, then placed the function above the general patrol movement in the same loop segment.
I don't know why it worked.
Thanks to everyone for their help.

Categories