I'm trying to rotate a group of objects 90 degrees in unity, so I set all of the objects to the same parent, then rotated the parent, it rotated just fine but it's too fast that I can't see, how can I slow it down? I tried both of codes below and both are the same :\ this code appears in a function that is called by update when the user presses on a button
float totalRotation = 0;
while (Mathf.Abs(totalRotation) < 90){
totalRotation += Time.deltaTime;
Parent.transform.RotateAround(temp.transform.position, flag*Vector3.right, Time.deltaTime);
}
and this one
Parent.transform.RotateAround(temp.transform.position, flag*Vector3.right, 90f);
thanks in advance!
Use a factor for the angle, something like
Parent.transform.RotateAround(temp.transform.position, flag*Vector3.right, Time.deltaTime * 0.5f);
Based on the while I would guess that you actually don't do this in Update. Either way this will not work since update is one frame. You don't want a while to do anything time related like this. Make your while an if instead. That way the rotation will probably be too slow so make the factor bigger. Currently your rotation is instant.
Edit:
Something like this would work:
public bool isRotating = false;
public float speed = 20.0f;
public Vector3 targetRotation;
void Update()
{
if(isRotating)
{
if(Vector3.Distance(transform.eulerAngles, targetRotation) > 1.0f)
{
transform.Rotate(Vector3.up * Time.deltaTime * speed);
}
else
{
transform.eulerAngles = targetRotation;
isRotating = false;
}
}
}
This would be around y only.
This part of the answer doesn't work. Please see the update
Actually you did it in the wrong way. Movements in Unity should be done slowly through updates, not just once. Like ChristophKn, I suggest using Coroutine.
const float speed = 180;
IEnumerator Rotate () {
float rotated = 0;
while (rotated < 90) {
float rotation = speed*Time.fixedDeltaTime;
rotated += rotation;
Parent.transform.RotateAround(temp.transform.position, flag*Vector3.right, rotation);
//wait for the next fixed update to continue.
yield return new WaitForFixedUpdate ();
}
}
To start rotating, call StartCoroutine(Rotate);
EDIT
I have another idea to do this, not using script but animation:
First, add a rotating animation to your parent objects, which rotates the angle you want.
To rotate the group of objects, set them to that parent like you did in your question, then start the animation.
At the end of the animation (you can call your methods at that point using animation events), call SetParent(null,true) for all your objects in the group. This will remove the parent but keep the world position.
Finally, set your parent's rotation to the original value.
Hope this will help.
Related
I'm newbie here, but will try to describe as clear as I can. (it's not that important, just to clarify) So I want to rotate an object for 405deg in 1 second only once, when space is pressed. I've thought it should be done using coroutines, so I've wrote something like this:
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
StopAllCoroutines(); //to prevent overlapping
StartCoroutine(Rotate());
}
}
IEnumerator Rotate()
{
//where final finalRotation is a rotation i want to get in a result
Quaternion finalRotation = Quaternion.Euler(0, 0, 405) * transform.rotation;
/*
using do..while to make sure that object will rotate even in case it's in same rotation as finalRotation
*/
do
{
//should smoothly rotate an object for 405deg in 1 sec but it doesnt
transform.Rotate(Vector3.forward, 405f * Time.deltaTime / 1f);
} while (transform.rotation != finalRotation);
yield return 1;
}
The code above works fine and everything is ok except one, it rotates an object instantly and not smoothly. I mean, if I use that line of code in Update, everything go smoothly
transform.Rotate(Vector3.forward, 405f * Time.deltaTime / 1f);
So my questions:
Why does it rotate smoothly in Update, but instantly in the coroutine;
and how can that be fixed?
Thanks in advance, once again, sorry if I made some mistakes when making a topic.
It updates instantly in the coroutine because you only yield when the rotation is complete. The frame wont be rendered until every pending/running coroutine yields or completes, so here your rotation completes and then finally the frame is rendered, making it an instant rotation.
Instead, you should yield in the loop.
You should also be aware that Quaternion finalRotation = Quaternion.Euler(0, 0, 405) * transform.rotation; and transform.Rotate(Vector3.forward, 405f); can have different results depending on parent object rotations, because they rotate in different spaces, the first rotating in global and the second rotating in local. This may result in the loop as written actually never ending.
Another problem is that there is no guarantee that summing up Time.deltaTime values will ever add up to 1f. Suppose on the very first frame, lag occurs, causing Time.deltaTime to be huge, 2f. You can see how it might never end as a result. As an alternative, you should remember the start rotation, then on each frame calcuate the rotation from that frame, then apply it that frame.
Altogether:
IEnumerator Rotate()
{
Quaternion startRotation = transform.rotation;
float endZRot = 405f;
float duration = 1f;
float t = 0;
while (t < 1f)
{
time = Mathf.Min(1f, t + Time.deltaTime/duration);
Vector3 newEulerOffset = Vector3.forward * (endZRot * t);
// global z rotation
transform.rotation = Quaternion.Euler(newEulerOffset) * startRotation;
// local z rotation
// transform.rotation = startRotation * Quaternion.Euler(newEulerOffset);
yield return null;
}
}
yield return null will wait one frame inside a coroutine. Right now you have placed the yield return 1; outside the while loop, so it wont do anything. In this situation I whould lerp between the two rotations in a while loop that goes from 0->1
or (if you dont want to do any of this stuff and dont care for a bit of bloat) use leantween, https://assetstore.unity.com/packages/tools/animation/leantween-3595
In the place where you rotate it one increment you have to wait for some time. Try
yield return new WaitForSeconds(0.1);
Also while I was typing this someone else answered.
God morning! I want to rotate an object smooth 90 degrees in an if statement. I've looked and searched for the solution everywhere but i couldn't find any fitting solution for my needs. This is what i want it to look like:
if (Swipe.Left)
{
Object smooth rotate 90 degrees down //left
}
I hope someone knows how i could do this! I'm thankful for any help :)
EDIT : I've tried this before but it seems like it does not work with if statements:
Quaternion newRotation = Quaternion.AngleAxis(90, Vector3.down);
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, .03f);
Since your Swipe.Left returns true during only one frame you have to sustain the logic for a longer period. To do that let's enable a flag on swipe and disable the flag when the target rotation is reached.
if (Swipe.Left)
{
swiped = true;
newRotation = Quaternion.AngleAxis(90, Vector3.down);
slerpEase = .03f;
}
if (swiped)
{
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, slerpEase);
slerpEase *= 0.95f;
if (Mathf.Approximately(1f, Quaternion.Dot(transform.rotation, newRotation)))
swiped = false; // reached the target rotation
}
Also notice that I used a variable named slerpEase to smoothly slow down the rotation as the object rotates over time. You may want to change 0.95f to make it dependent on delta time.
Note that, if you rotate it around two axis, comparing angles will be a bit tricky due to Gimbal lock.
I would use a coroutine for this, with an AnimationCurve to determine the smoothness of the turn. This will let you fine tune the appearance you want just using the inspector, and it allows for nice code re-use if you want different degrees.
[SerializeField] private AnimationCurve swipeRotateAnimationCurve; // configure in inspector
private Coroutine swipeRotateCoroutine = null;
private float swipeRotationDuration = 2f; // duration of rotation in seconds
// ...
if (Swipe.Left)
{
// Cancel any currently running coroutine
if (swipeRotateCoroutine != null)
{
StopCoroutine(swipeRotateCoroutine);
swipeRotateCoroutine = null;
}
swipeRotateCoroutine = StartCoroutine(DoHorizontalSwipeRotate(-90f));
}
else if (Swipe.Right)
{
// Cancel any currently running coroutine
if (swipeRotateCoroutine != null)
{
StopCoroutine(swipeRotateCoroutine);
swipeRotateCoroutine = null;
}
swipeRotateCoroutine = StartCoroutine(DoHorizontalSwipeRotate(90f));
}
// ...
private IEnumerator DoHorizontalSwipeRotate(float degreesRight)
{
float t = 0;
Quaternion startRot = transform.rotation;
// update rotation until
while (t < 1f)
{
// let next frame occur
yield return null;
// update timer
t = Mathf.Min(1f, t + Time.deltaTime/swipeRotationDuration);
// Find how much rotation corresponds to time at t:
float degrees = degreesRight * swipeRotateAnimationCurve.Evaluate(t);
// Apply that amount of rotation to the starting rotation:
transform.rotation = startRot * Quaternion.Euler(0f, degrees, 0f);
}
// allow for next swipe
swipeRotateCoroutine = null;
}
So When it comes to directions in Unity it is not always what we think it should be I've already answered a question related to this here. So in your case to rotate an object clock-wise, you need to use Vector3.forward instead of Vector3.down. This covers defining an axis around witch your object is going to rotate.
Now let's talk about the direction like rotate to right/rotate to left or clockwise/counter Clockwise. When you want to rotate your object clockwise you use -ve(imaginative) angle and when you want to rotate your object counter clockwise you use +ve(positive) angle.
So your code should be as follows to rotate your object clockwise:
Quaternion newRotation = Quaternion.AngleAxis(90, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, 0.03f);
I have a camera that orbits a point on the screen. Clicking and dragging your mouse left and right will orbit around the focus point and releasing the mouse will leave the camera at that angle.
I'm trying to create a function that will orbit the camera 180 degrees, smoothly. So for example, if the camera is behind the "player" and this function gets called, the camera will orbit to the front of the player over the course of X seconds and stop in the exact opposite position of where it started.
Here's the code I'm currently using for camera rotation based on mouse click and drag:
Quaternion camTurnAngle =
Quaternion.AngleAxis(Input.GetAxis("Mouse X") * rotationSpeed, Vector3.up);
this.cameraOffset = camTurnAngle * this.cameraOffset;
Vector3 newPos = this.currentFocusPoint + this.cameraOffset;
transform.position = Vector3.Slerp(transform.position, newPos, smoothFactor);
transform.LookAt(this.currentFocusPoint);
I am trying to replace this mouse-drag based camera rotate with a UI button (aka, a single function call) that will fully orbit the camera 180 degrees over a short period of time, and then it will rotate back if pressed again. Here's an MS paint drawing just in case my explanation is confusing:
I'm not sure how do to something like this. It sounds similar to using Vector3.Slerp, but I cannot find a solution that works. I am so far from a solution that I don't really have any sensible code to post. I'll just say that I've tried two methods so far:
1) I've tried using transform.RotateAround, where angle I pass in is rotateSpeed * Time.deltaTime. My issue is I don't know how to check for an end condition, and without one my camera will rotate forever.
2) I've tried messing with Quaternion.Slerp, but the results are seeing are not what I expected.
How can I achieve a smooth 180 degree camera orbit, that takes a predetermined amount of time to complete?
This is a textbook use for a coroutine. Basically, start a coroutine that keeps track of how much time is left in the orbit, then either updates cameraOffset based on the time that's left or Time.deltaTime, whatever is shorter.
private IEnumerator RotateCam(float rotateDuration)
{
float timeLeft = rotateDuration;
// possibly - disable control of camera here
while (timeLeft > 0)
{
yield return null;
float elapsedRotationTime = Mathf.Min(timeLeft, Time.deltaTime);
float angleThisFrame = 180f * elapsedRotationTime / rotateDuration;
Quaternion camTurnAngle = Quaternion.AngleAxis(angleThisFrame, Vector3.up);
this.cameraOffset = camTurnAngle * this.cameraOffset;
transform.position = this.currentFocusPoint + this.cameraOffset;
transform.LookAt(this.currentFocusPoint);
timeLeft -= Time.deltaTime;
}
// possibly - re-enable control of camera here
}
Then, when you want to begin the rotation:
// private Coroutine rotateCoroutine
this.rotateCoroutine = StartCoroutine(RotateCam(2f));
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 am making a 2d game that can use either the keyboard or an Xbox One controller as movement.
So far, I can move the players and play a running animation, jump animation etc using the keyboard, and I can move using the Xbox controller.
However, I can't play animations with the Xbox controller.
I have this:
void Update()
{
movement.x = Input.GetAxis("LeftJoystickX") * speed * Time.deltaTime;
// Orange player movement
if (orange)
{
transform.position += movement;
if (Input.GetKey(KeyCode.D))
{
ChangeDirection("right");
ChangeState(STATE_RUN);
transform.position += Vector3.right * speed * Time.deltaTime;
if (!onGround)
ChangeState(STATE_JUMP);
}
}
}
Here, if you use the keyboard, the animations play. If you use the controller, the player moves but the animations don't play. I've tried and narrowed it down to the fact that this piece of code will not evaluate:
if (Input.GetAxis("LeftJoystickX"))
as Input.GetAxis() returns a float and not a bool (?) and therefore cannot be put in an if statement.
I guess what I am asking is, is it possible to detect a direction of a Joystick push from a controller, such as something like Input.GetAxis().joystickPushedRight?
Or better yet, is there a different way I could execute
if (Input.GetKey(KeyCode.D))
{
ChangeDirection("right");
ChangeState(STATE_RUN);
transform.position += Vector3.right * speed * Time.deltaTime;
if (!onGround)
ChangeState(STATE_JUMP);
}
But use the joystick instead of Input.GetKey?
Thanks!
Input.GetAxis returns a float between -1 and 1.
If you take that into account together with a threshold for values close to zero you can determine if you need to go up or down or need to stop.
Your implementation will look like this:
const float threshold = 0.05;
const string axis = "LeftJoystickX";
if (Input.GetAxis(axis) < -threshold)
{
//go left
}
else if (Input.GetAxis(axis) > threshold)
{
//go right
}
else
{
// don't move
}