I'm trying to change the eulerAngle X of a camera rotation while keeping y and z at 0.
However, the following IEnumerator results in strange eulerAngles like 10, -74.653, 0.
I don't understand how the y value can change within the following function:
private IEnumerator LerpCameraNormalRot()
{
float duration = 0.5f;
for (float t = 0f; t < duration; t += Time.deltaTime)
{
float f = Mathf.Lerp(camera.transform.eulerAngles.x, 10, t / duration);
Vector3 vNew = new Vector3(f, 0, 0);
camera.transform.eulerAngles = vNew;
yield return 0;
}
}
Isn't this strange? I never change the Y and Z values!
I only want to change the X rotation (eulerAngle.x) from its current value to 10.
Thank you for the help!
You are setting the eulerAngles but that will result in different localEulerAngles which are indicated by the red box in your image. If the object has any ancestors that are rotated, they will probably not match!
To fix your immediate problem, you could use localEulerAngles instead of eulerAngles:
float f = Mathf.Lerp(camera.transform.localEulerAngles.x, 10, t / duration);
Vector3 vNew = new Vector3(f, 0, 0);
camera.transform.localEulerAngles= vNew;
The problem with this is that it does not account for the ability to go from 359 degrees around to 0 degrees. Better yet would be to use Quaternion.Euler and Quaternion.Slerp to use Quaternions rather than Euler angles:
private IEnumerator LerpCameraNormalRot()
{
float duration = 0.5f;
Quaternion startRotation = camera.transform.localRotation;
Quaternion goalRotation = Quaternion.Euler(10f, 0f, 0f);
for (float t = 0f; t < duration; t += Time.deltaTime)
{
camera.transform.localRotation = Quaternion.Slerp(startRotation, goalRotation, t/duration);
yield return 0;
}
camera.transform.localRotation = goalRotation;
}
Unity uses two representation of rotation, position, eulerAngles, etc
one is in worldspace
the other is in local space
The values not including local in their name are in world space.
If your object has than any parents that are rotated/scaled/translated you won't see the values you set in Unity since the Transform inspector displays the local coordinates.
If you want to set the local coordinates to the exact values instead use localPosition, localRotation or localEulerAngles instead.
For me it looks like you want to rotate the Camera around its local x axis by 10° in 0.5 secons.
So I think you could instead do it like
private IEnumerator LerpCameraNormalRot()
{
float duration = 0.5f;
float initialRotationX = camera.transform.localEulerAngles.x;
float targetRotationX = 10;
for (float t = 0f; t < duration; t += Time.deltaTime)
{
float currentX = Mathf.Lerp(initialRotationX, targetRotationX, t / duration);
camera.transform.localEulerAngles = new Vector3(currentX , 0, 0);
// Which would be the same as using
// camera.transform.localRotation = Quaternion.Euler(new Vector3(currentX, 0, 0));
yield return null;
}
// to be sure you have no overshooting you could set the target rotation fix here as well
camera.transform.localEulerAngles = new Vector3(targetRotation, 0 ,0);
// or
// camera.transform.localRotation = Quaternion.Euler(new Vector3(targetRotation, 0, 0));
}
Related
I hope somebody can help. I have a script that rotates a globe, over which a plane (which is stationary) flies from 1 country to another. This all works fine and is achieved with the following script
while (!DestinationReached)
{
Vector3 planetToCountry = (Destination.localPosition - Vector3.zero);//.normalized; //vector that points from planet to country
Vector3 planetToCamera = (camTr.position - tr.position).normalized; //vector that points from planet to camera/plane
Quaternion a = Quaternion.LookRotation(planetToCamera);
Quaternion b = Quaternion.LookRotation(planetToCountry);
b = Quaternion.Inverse(b);
newRotation = a * b;
tr.rotation = Quaternion.Slerp(tr.rotation, newRotation, 0.01f);
if (Approximately(tr.rotation, newRotation, 0.0001f)) //if here the plane has arrived
{
Debug.Log("Destination reached");
DestinationReached = true;
}
yield return new WaitForEndOfFrame();
}
It essentially calculates the angle between the plane (the camera is attached to the plane GO and views it from above) and the destination the globe needs to rotate to so that the plane looks as though it flies to the destination.
The issue I have is I need to make the flight time uniform regardless of the angle the globe must rotate, so lets say it must be 5 seconds, regardless if the plane flies from Paris to Ireland or Paris to Australia. Anybody have any ideas on how to do this.
I have to admit, I nicked this script for the web, as my Vector and Quaternion mathematics is hopeless :)
If you want to be flexible and e.g. add some easing at beginning and end but still finish within a fixed duration I would do it like this (I'll just assume here that your calculating the final rotation is working as intended)
// Adjust the duration via the Inspector
[SerializeField] private float duration = 5f;
private IEnumerator RotateRoutine()
{
// calculate these values only once!
// store the initial rotation
var startRotation = tr.rotation;
// Calculate and store your target ratoation
var planetToCountry = (Destination.localPosition - Vector3.zero);
var planetToCamera = (camTr.position - tr.position);
var a = Quaternion.LookRotation(planetToCamera);
var b = Quaternion.LookRotation(planetToCountry);
b = Quaternion.Inverse(b);
var targetRotation = a * b;
if(duration <= 0)
{
Debug.LogWarning("Rotating without duration!", this);
}
else
{
// track the time passed in this routine
var timePassed = 0f;
while (timePassed < duration)
{
// This will be a factor from 0 to 1
var factor = timePassed / duration;
// Optionally you can alter the curve of this factor
// and e.g. add some easing-in and - out
factor = Mathf.SmoothStep(0, 1, factor);
// rotate from startRotation to targetRotation via given factor
tr.rotation = Quaternion.Slerp(startRotation, targetRotation, factor);
// increase the timer by the time passed since last frame
timePassed += Time.deltaTime;
// Return null simply waits one frame
yield return null;
}
}
// Assign the targetRotation fix in order to eliminate
// an offset in the end due to time imprecision
tr.rotation = targetRotation;
Debug.Log("Destination reached");
}
So the problem here is the t used on your Quaternion.Slerp method, it's constant. This t is the "step" the slerp will do, so if it's contstant, it won't depend on time, it will depend on distance.
Try instead to do something like this, being timeToTransition the time you want that every rotation will match:
public IEnumerator RotatintCoroutine(float timeToTransition)
{
float step = 0f;
while (step < 1)
{
step += Time.deltaTime / timeToTransition;
//...other stuff
tr.rotation = Quaternion.Slerp(tr.rotation, newRotation, step);
//...more stuff if you want
yield return null;
}
}
Edit: addapted to your code should look like this
float timeToFly = 5f;
while (!DestinationReached)
{
step += Time.deltaTime / timeToTransition;
Vector3 planetToCountry = (Destination.localPosition - Vector3.zero);//.normalized; //vector that points from planet to country
Vector3 planetToCamera = (camTr.position - tr.position).normalized; //vector that points from planet to camera/plane
Quaternion a = Quaternion.LookRotation(planetToCamera);
Quaternion b = Quaternion.LookRotation(planetToCountry);
b = Quaternion.Inverse(b);
newRotation = a * b;
tr.rotation = Quaternion.Slerp(tr.rotation, newRotation, step);
if (step >= 1) //if here the plane has arrived
{
Debug.Log("Destination reached");
DestinationReached = true;
}
yield return null();
}
I'm trying to recreate a solar system and i'm using newton's law. The actual force works great but when i try to "predict" the path of a planet it just doesn't work: to predict it i use a lineRenderer whose points get placed on the position at the Tth instant ( T being time ). When i start the game they are close but as time passes the planet actually goes in "orbit" while the line shoots up.
I can't understand why this happens seen that i calculate the planet's position in the same way as the line's one. I also tried instantiating a ball but same result.
public void UpdateSpeed(Vector3 acceleration,float time)
{
velocity += acceleration * time;
}
public void UpdatePosition(float time)
{
transform.position +=velocity *time;
}
public void UpdateLine(float time)
{
position += velocity * time;
Debug.Log(position);
Instantiate(ball, position, Quaternion.identity);
line.positionCount++;
line.SetPosition(line.positionCount-1, position);
}
and here the function that computes acceleration
public Vector3 CalculateAcceleration(GameObject subj, float time)
{
Vector3 Acceleration = Vector3.zero;
foreach (var pl in planets)
{
if (pl != subj)
{
float sqrDistance= Mathf.Pow(Vector3.Distance(subj.transform.position, pl.transform.position), 2);
Vector3 direction = (pl.transform.position - subj.transform.position).normalized;
Acceleration += direction * (pl.GetComponent<Rigidbody>().mass * GravitationalConstant)/sqrDistance;
}
}
return Acceleration;
}
is the rigidbody attached to the planet making a difference for the position?
If you are having trouble with the orbit, calculate the OrbitFirst then call it to move your object with different time, just modify delta time below
LineRenderer lr;
//Build the orbit path/line
void CalculateOrbitEllipse()
{
Vector3[] points = new Vector3[segments + 1];
for (int i = 0; i < segments; i++)
{
Vector2 position2D = orbitPath.Evaluate((float)i / (float)segments);
points[i] = new Vector3(position2D.x, 0f, position2D.y);
}
points[segments] = points[0];
lr.positionCount = segments + 1;
lr.SetPositions(points);
}
//make the planet move,
//you can invoke this using your time variable, Time.deltaTime see below
IEnumerator AnimationOrbit()
{
if (orbitPeriod < 0.1f)
{
orbitPeriod = 0.1f;
}
float orbitSpeed = 0.1f / orbitPeriod;
while (orbitActive)
{
orbitProgress += Time.deltaTime * orbitSpeed;
orbitProgress %= 1f;
SetOrbitingObjectPosition();
lr = GetComponent<LineRenderer>();
CalculateOrbitEllipse();
yield return null;
}
}
void SetOrbitingObjectPosition()
{
Vector2 orbitPos = orbitPath.Evaluate(orbitProgress);
orbitingObject.localPosition = new Vector3(orbitPos.x, 0, orbitPos.y);
}
so as often happens i'm an idiot. I was calculating in the acceleration function distances with the actual bodies positions instead of the simulated position so i always had a false value that was increasing instead of varying
So I have the following function to generate 3 vector points let creates a triangle:
private Vector3[] GetFieldOfViewPoint() {
float Range = 3f;
float Angle = 45;
Vector3 offset = new Vector3(0, Range, 0);
Quaternion rotation1 = Quaternion.Euler(0, 0, -Angle / 2);
Quaternion rotation2 = Quaternion.Euler(0, 0, Angle / 2);
return new Vector3[3] {
transform.position,
(rotation1 * offset) + transform.position,
(rotation2 * offset) + transform.position
};
}
The issue I am having is that when I increase the angle, the rang decrease which does make some sense to me.
For example, 45 angle:
135 angle:
The issue I need help with is how can I modify the calculation in order to keep the range line (the furtherest straight green line) consistent regardless of the angle (which will be capped at 170 degrees)?
I can get the range to move with the angle but doing random calculations but it is not consistent and just does not work (at least with the random number I have tried). I also thought about hypotenuse formula however I only know 1 sides length (which is the range).
Any help with this would be awesome.
Here:
Script:
using UnityEngine;
public class TrignometryTest : MonoBehaviour
{
public float range;
[Range(5,170)]
public float angle;
Vector3 size = Vector3.one;
Vector3[] GetFOVPoints(float _range, float _angleInDegrees)
{
Vector3 rightPoint, leftPoint;
float halfAngle = _angleInDegrees/2;
rightPoint = new Vector3(_range * Mathf.Tan(halfAngle * Mathf.Deg2Rad), _range, 0);
leftPoint = new Vector3(-_range * Mathf.Tan(halfAngle * Mathf.Deg2Rad), _range, 0);
Vector3[] points = { transform.position, leftPoint, rightPoint};
return points;
}
void OnDrawGizmos()
{
var points = GetFOVPoints(range, angle);
Gizmos.DrawCube(points[0], size);
Gizmos.DrawCube(points[1], size);
Gizmos.DrawCube(points[2],size);
Gizmos.DrawLine(points[0], points[1]);
Gizmos.DrawLine(points[0], points[2]);
Gizmos.DrawLine(points[1], points[2]);
Gizmos.DrawLine(points[0],Vector3.up * range);
}
}
OutPut:
Hope this helps :)
I'm getting from a sensor 4 parameters every frame, O (origin), W (UP), V(FORWARD) and U(RIGHT), they are Vector3 elements, with this values i can know the position of my object is in space, but i can't find out the rotation to represent all movemets that the object does.
this is what i get so far(this run every frame with new values for entry vector):
Vector3 vetO = new Vector3(entry[0], entry[4], entry[8]); //o
Vector3 vetU = new Vector3(entry[1], entry[5], entry[9]); //u
Vector3 vetV = new Vector3(entry[2], entry[6], entry[10]); //v
Vector3 vetW = new Vector3(entry[3], entry[7], entry[11]); //w
//Here i'm doing this to analize the rotation of a plan
vetW.x = 0;
vetU.z = 0;
vetV.y = 0;
float angleX = Vector3.Angle(vetW, Vector3.up);
float angleY = Vector3.Angle(vetV, Vector3.forward);
float angleZ = Vector3.Angle(vetU, Vector3.right);
//This is because unity bug, when angle is up to 180º
angleX = vetW.z < 0 ? -angleX : angleX;
angleY = vetV.x < 0 ? -angleY : angleY;
angleZ = vetU.y < 0 ? -angleZ : angleZ;
Vector3 angles = new Vector3(angleX, angleY, angleZ);
transform.eulerAngles = angles;
transform.localPosition = vetO;
If I understand you correctly, you want to get the rotation of a certain gameobject, right?
To do this, you can use YourGameObject.transform.rotation.eulerAngles to give you a rotation in the form of Vector3. If you wanted to get just one angle you can use YourGameObject.transform.rotation.eulerAngles.x,YourGameObject.transform.rotation.eulerAngles.y or YourGameObject.transform.rotation.eulerAngles.z
[See my answer below! Thanks for the comments]
What is the best way to Decrease/Increase an angle without using "eulerAngles"?
I need turn my transform between 30 and 325, according to my "Y axis".
If it is less than zero we reduce the value of "Z axis" is greater if we increase the value of "Z axis".
I already tried:
if ( airPlanePlayerRigidbody2D.velocity.y < 0 ) {
var rotationVector = airPlanePlayerTransform.rotation;
rotationVector.z -= 2f;
Vector3 vec = new Vector3(rotationVector.x,rotationVector.y,rotationVector.z);
airPlanePlayerTransform.rotation = Quaternion.Euler(vec);
}
And:
public void Rotate(float angle)
{
airPlanePlayerTransform.Rotate(0, 0, angle); // this rotate forever
}
Thank you in advance.
UPDATE
In fact I already have a code that does what I need, but it is too long. I'm looking for some simpler. The result I want is this:
A more smoothed animation:Smooth animation down
And what I can do so far with the comments posted here, is this:Without animation
I think eulerAngeles is a good way to go with, and I successfully did the similar rotation effect in Unity by using eulerAngeles. You probably didn't find the correct way when you used it.
I used something like that:
Vector3 newRotationAngles = transform.rotation.eulerAngles;
if (Input.GetKey(KeyCode.LeftArrow)) {
newRotationAngles.z += 1;
} else if (Input.GetKey(KeyCode.RightArrow)) {
newRotationAngles.z -= 1;
}
transform.rotation = Quaternion.Euler (newRotationAngles);
Output: when you press the left/right arrow keys, the gameObject will rotate, but it is not always rotating. Hope this is helpful.
You want something like this:
public float maxAngle = 325f; // Max Angle for Z Axis
public float minAngle = 30f; // Min Angle for Z Axis
public float rotationDelta = 1f; // you can control your rotation speed using this
float tempAngle;
void Update()
{
// If AirPlane is rising, velocity in y is greater than 0
if(rigidbody2D.velocity.y > 0)
{
tempAngle = Mathf.LerpAngle(transform.eulerAngles.z, maxAngle, Time.time * rotationDelta);
}
// If AirPlane is falling, velocity in y is less than 0
else if(rigidbody2D.velocity.y < 0)
{
tempAngle = Mathf.LerpAngle(transform.eulerAngles.z, minAngle, Time.time * rotationDelta);
}
// If AirPlane is going straight in horizontal.
else
{
tempAngle = 0;
}
transform.eulerAngles = new Vector3(0, 0, tempAngle);
}
Researching the topic, I managed to get the result. Thanks for the replies.
if (airPlanePlayerRigidbody2D.velocity.y < 0) { //falling
var myNewQuaternion = Quaternion.Euler (new Vector3 (0, 0, -30));
var myQuaternion = Quaternion.Euler(new Vector3(transform.rotation.eulerAngles.x, transform.rotation.eulerAngles.y, transform.rotation.eulerAngles.z));
transform.rotation = Quaternion.Slerp (myQuaternion , myNewQuaternion , speed );
}
else if (airPlanePlayerRigidbody2D.velocity.y > 0) { // rising
var myNewQuaternion = Quaternion.Euler (new Vector3 (0, 0, 30));
var myQuaternion = Quaternion.Euler(new Vector3(transform.rotation.eulerAngles.x, transform.rotation.eulerAngles.y, transform.rotation.eulerAngles.z));
transform.rotation = Quaternion.Slerp (myQuaternion , myNewQuaternion , speed );
}