Quaternion.Lerp clock rotation - c#

I am trying to rotate the hand of the clock on click, but nothing is actually happening, none of the arrows is actually moving so I probably did something wrong... any help would be much appreciated, thank you
script below
public class Clock : MonoBehaviour {
public GameObject minuteHand;
public GameObject hourHand;
private Quaternion targetRotation;
float speed = 0.1f;
void Start()
{
targetRotation = transform.rotation;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
minuteHand.transform.rotation = Quaternion.Lerp(minuteHand.transform.rotation, targetRotation, Time.deltaTime * speed);
}
} }

Your code is only executed exactly in one single frame when the mouse button goes down.
Also that is not how Lerp works. Lerp expects a factor between 0 and 1 and interpolates both values. If you multiply your speed (0.1) and the Time.deltaTime which for 60FPS has a value of about 1/60f = 0.017f you get a resulting factor of about 0.0017f => You will always stay pretty close to the first value
especially since additionally your targetRotation always equals the current transform.rotation!
I assume what you wanted is: Every click move the minute 6° ahead (360°/60 minutes = 6°/minute).
Once a full circle is done move the hour 30° ahead (360°/12 hours = 30°/hour)
public class Clock : MonoBehaviour
{
public GameObject minuteHand;
public GameObject hourHand;
Quaternion originalRotationMinute;
Quaternion originalRotationHour;
int minutes;
int hours;
// Store original orientations
void Start()
{
originalRotationMinute = minuteHand.transform.rotation;
originalRotationHour = hourHand.transform.rotation;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
// First calculate the new time
minutes++;
if(minutes == 60)
{
minutes = 0;
hours++;
if(hours = 12)
{
hours = 0;
}
}
// Now update the rotations with the new time
// I'm assuming here you want to rotate around the Z axis
// if this is not the case change this according to your needs
var targetMinuteRotation = originalRotationMinute * Quaternion.Euler(0, 0, 6.0f * minutes);
var targetHourRotation = originalRotationHour * Quaternion.Euler(0, 0, 30.0f * hours);
minuteHand.transform.rotation = targetMinuteRotation;
hourHand.transform.rotation = targetHourRotation;
}
}
}
This would make the clock "jump" to the target rotations. If what you rather wanted is a smooth rotation (what I assume from the usage of Lerp) I would probably use a Coroutine for that:
public class Clock : MonoBehaviour
{
public GameObject minuteHand;
public GameObject hourHand;
float speed = 0.1f;
Quaternion originalRotationMinute;
Quaternion originalRotationHour;
int minutes;
int hours;
void Start()
{
originalRotationMinute = minuteHand.transform.rotation;
originalRotationHour = hourHand.transform.rotation;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
// cancel the current routines
StopAllCoroutines();
// calculate the new time
minutes++;
if(minutes == 60)
{
minutes = 0;
hours++;
if(hours = 12) hours = 0;
}
// I'm assuming here you want to rotate around the Z axis
// if this is not the case change this according to your needs
var targetMinuteRotation = originalRotationMinute * Quaternion.Euler(0, 0, 6.0f * minutes);
var targetHourRotation = originalRotationHour * Quaternion.Euler(0, 0, 30.0f * hours);
// This time instead of directly setting the values start coroutines
// to rotate the hands smoothly according to the given speed
StartCoroutine (RotateTo(minuteHand, targetMinuteRotation));
StartCoroutine (RotateTo(hourHand, targetHourRotation));
}
}
// rotate the given object to the given target rotation
// according to the given speed
private IEnumerator RotateTo(GameObject obj, Quaternion targetRotation)
{
var targetTransform = obj.transform;
var current = targetTransform.rotation;
// I your are not rotating on the Z axis change this accordingly to your needs
var difference = targetRotation.eulerAngels.z - current.eulerAngles.z;
if(Mathf.Approximately(0, difference)) yield break;
var duration = difference / speed;
var timePassed = 0f;
while(timePassed <= duration)
{
var factor = timePassed / duration;
targetTransform.rotation = Quaternion.Lerp(current, targetRotation, factor);
timePassed += Time.deltaTime;
yield return null;
}
// t be sure set the final rotation once in the end
targetTransform.rotation = targetRotation;
}
}
Typed on smartphone but I hope the idea gets clear

For me only this works. You can't modify speed to make it even slower, but it looks fine (may be it will be helpful to someone)
float speed = 0.1f //how fast rotate
private void FixedUpdate()
{
nextRot = hour * -30;
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(0, 0, nextRot), speed);
}

Related

What is wrong with the AnimationCurve ? The lerp duration never change

I I set the lerp to 1 or to 10 the transform is moving to the target too fast.
At the top :
public AnimationCurve curve = AnimationCurve.Linear(0.0f, 0.0f, 1.0f, 1.0f);
public float duration = 10.0f;
private float t;
At Start()
void Start()
{
t = 0.0f;
}
At Update()
void Update()
{
switch (state)
{
case TransitionState.MovingTowards:
var v = targetTransform.position - transform.position;
if (v.magnitude < 0.001f)
{
state = TransitionState.Transferring;
originTransform = targetTransform;
timer = 0;
return;
}
t += Time.deltaTime;
float s = t / duration;
transform.position = Vector3.Lerp(transform.position,
targetTransform.position, curve.Evaluate(s));
break;
}
}
The curve animation in the editor :
You seem to lerp from Transform.Position to your TargetPosition. That means that everytime you move you lerp from a new starting position. This will make your object move exponantially. Try lerping from a fixed start position to a fixed end position. (Unless this is what you actually want to happen)
If I read your code correctly your duration is the exact duration in seconds that your lerp should take. So I assume that's correct, just fix the "start" position of your lerp.

Moving from one place to another over time (or rotating) in Unity

I have a problem with moving from one place to another in Unity over time. I would like my character to move from current position to current + 1 on Y. Unfortunately, looks like it does not get the current position properly or something, since if I debug what I wrote, it says that the magnitude is always 1, so the point is moving with me. Shouldn't it just check the current position and add 1 to Y, move to that position and then check again? I have no idea what's wrong with this code, or if it's strictly connected with how Unity checks positions and things in real time?
public bool moving = false;
private Vector3 dir;
void FrontMovement()
{
Vector3 movement = new Vector3(-Mathf.Sin(transform.eulerAngles.z * Mathf.PI / 180), Mathf.Cos(transform.eulerAngles.z * Mathf.PI / 180), 0f); // always moving front, even after rotation
if (moving == false)
{
dir = movement - transform.position;
moving = true;
return;
}
transform.Translate(dir.normalized * Time.deltaTime);
if(dir.magnitude <= Time.deltaTime)
{
Debug.Log("Finished movement");
moving = false;
}
}
void FixedUpdate()
{
Debug.Log(dir.magnitude);
FrontMovement();
}
I would also like to know how to do rotations over time.
https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html
lerp also works for rotations
// Movement speed in units per second.
public float speed = 1.0F;
// Time when the movement started.
private float startTime;
// Total distance between the markers.
private float journeyLength;
void StartMoving() {
// Keep a note of the time the movement started.
startTime = Time.time;
Vector3 modifiedPosition = transform.position;
transform.position.y += 1.0f;
// Calculate the journey length.
journeyLength = Vector3.Distance(transform.position, modifiedPosition.position);
moving = true;
}
// Move to the target end position.
void Update()
{
if (moving) {
// Distance moved equals elapsed time times speed..
float distCovered = (Time.time - startTime) * speed;
// Fraction of journey completed equals current distance divided by total distance.
float fractionOfJourney = distCovered / journeyLength;
// Set our position as a fraction of the distance between the markers.
transform.position = Vector3.Lerp(startMarker.position, endMarker.position, fractionOfJourney);
if (fractionOfJourney >= 1.0f) {
moving = false;
}
}
}
You could use Coroutine + Vector3.Lerp to move with a specified amount of time:
public IEnumerator MoveToPosition(Transform transform, Vector3 position, float timeToMove)
{
var currentPos = transform.position;
var t = 0f;
while(t <= 1f)
{
t += Time.deltaTime / timeToMove;
transform.position = Vector3.Lerp(currentPos, position, t);
yield return null;
}
transform.position = position;
}
you call the coroutine in Start Method
StartCoroutine(MoveToPosition(transform, newposition, timeToMove))
You could use the same logic for Rotating, with Quaternion.Lerp or Slerp and Quaternion.LookRotation, of course you have lot of sample with rotation over time on WEB!! google is your friend...
public IEnumerator RotateToDirection(Transform transform, Vector3 position, float timeToRotate)
{
var startRotation = transform.rotation;
var direction = position - transform.position;
var finalRotation = Quaternion.LookRotation(direction);
var t = 0f;
while (t <= 1f)
{
t += Time.deltaTime / timeToRotate;
transform.rotation = Quaternion.Lerp(startRotation, finalRotation, t);
yield return null;
}
transform.rotation = finalRotation;
}

Why when rotating the object on z axis it's rotating also the x and y and not setting z to 0?

I want the object to rotate only on z. To keep x and y and changing z to 0.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Whilefun.FPEKit;
public class PlayAnimation : MonoBehaviour
{
public List<GameObject> cameras = new List<GameObject>();
public GameObject head;
private Animator anim;
private bool started = true;
private float animationLenth;
private bool rotateHead = false;
// Start is called before the first frame update
void Start()
{
anim = GetComponent<Animator>();
}
private void Update()
{
if (FPESaveLoadManager.gameStarted == true && started == true)
{
FPEInteractionManagerScript.Instance.BeginCutscene();
anim.enabled = true;
anim.Play("Stand Up", 0, 0);
animationLenth = anim.GetCurrentAnimatorStateInfo(0).length;
StartCoroutine(AnimationEnded());
started = false;
}
if(rotateHead == true)
{
head.transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(transform.rotation.x, transform.rotation.y, 0f), 1.0f * Time.deltaTime);
}
}
IEnumerator AnimationEnded()
{
yield return new WaitForSeconds(animationLenth);
anim.enabled = false;
FPEInteractionManagerScript.Instance.EndCutscene();
rotateHead = true;
}
}
I'm trying to rotate the head.
When the animation is end the head rotation is : X = -9.922001 Y = 6.804 Z = 4.167
When the rotation of the head is ending or maybe it's never ending since it's in the update ? The head rotation is : X = -4.529 Y = -9.392 Z = 0.988 Why x and y change and z never get to 0 ?
Another strange thing I saw now the position of the transform on X when the animation is ended is -5.089218e-12 what is this e-12 ?
An example of what I said on Musaps answer:
using UnityEngine;
public class ExampleLerp: MonoBehaviour
{
public float Start = 2.0f;
public float End = 0.0f;
public float TimeToTravel = 3.0f;
private float elapsedTime = 0.0f;
private float inverseTime;
// Start is called before the first frame update
void Start()
{
inverseTime = 1.0f/TimeToTravel;
}
private void Update()
{
float ratio = 0.0f;
// Increment our elapsedTime
elapsedTime += Time.deltaTime;
if(elapsedTime > TimeToTravel)
{
ratio = 1.0f;
}
else
{
ratio = elapsedTime * inverseTime // Or can be done as elapsedTime/TimeToTravel
}
Debug.log("value is: " + Math.Lerp(Start, End, ratio));
}
}
By using the current roation, and using the same target rotation, and not storing your progress over time, you will see a slowdown as it approaches the end but it will never actually reach the end mathematically.
First of all, e-12 is 10^-12 or in your case, it is -0.000000000005089218. This occurs maybe because of the float math.
z does not get to 0 because you are using a lerp function without altering the interpolation ratio on the way. Lerp stands for linear interpolation. The third parameter it gets (1.0f * Time.deltaTime in your code) is the interpolation ratio. If this ratio was 0.5, the value of z would jump to the middle value of the difference (which is (0 - 4.167) * 0.5). For example; Lerp(0, 1, 0.5f) makes it jump to 0.5 in the next frame and 0.75 in the other frame.
Let's consider your game runs at 60 frames per second. That makes Time.deltaTime = 1/60. When Lerp starts executing in your code, z is 4.167 in the first frame. You want it to reach zero. The ratio of the interpolation is 1.0f * Time.deltaTime which is 0.01666... or 1/60. That means z will jump to 1/60 of the difference.
In the second frame, z reaches to (4.167 + (0 - 4.167) / 60) = 4.09755
In the third frame, z reaches to (4.09755 + (0 - 4.09755 / 60) = 4.0292575
You can overcome this by testing the z value against a tolerance value.
if (rotateHead)
{
const float end = 0f;
var rotation = transform.rotation;
var z = Mathf.Lerp(rotation.z, end, 1.0f * Time.deltaTime);
const float tolerance = 1f;
if (Math.Abs(z - end) < tolerance) z = 0;
head.transform.rotation = Quaternion.Euler(rotation.x, rotation.y, z);
}

How to create a 'sprint' ability in a 2d game

I'm trying to create an ability of sprinting in 2d Unity (C#) that has an energy bar so it can't be used endlessly. What am I missing?
I've tried making sprint a function and call it when the X key was pressed. Tried to multiply the position of it but I got a blinking ability over a short distance.
\\ this is my movement script, other variables we're declared earlier in the code
void Update() {
Vector2 mi = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
mv = mi.normalized * speed;
}
private void FixedUpdate() {
rb.MovePosition(rb.position + mv * Time.fixedDeltaTime);
}
I expect the code to make the player go twice the normal speed when the X key is pressed but it can only be used when the energy hasn't ran out.
Don't see your implementation of "energy" but I would simply use a
public float energy;
// How much is energy reduced per second?
public float decreaseSpeed;
for increasing the speed if x is pressed do
private void Update()
{
Vector2 mi = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
mv = mi.normalized * speed;
if(Input.GetKey(KeyCode.X) && energy > 0)
{
// Reduce energy by decreaseSpeed per second
energy -= decreaseSpeed * Time.deltaTime;
// if needed avoid negative value
energy = Mathf.Max(0, energy);
// double the move distance
mv *= 2;
}
}
Btw ot is recommended to use Time.deltaTime instead of Time.fixedDeltaTime also in FixedUpdate (see Time.fixedDeltaTime)
In Update check if sprint axis (create a Sprint axis in the project input settings). Also, have a variable for how fast your energy bar drains while sprinting:
public float energyBarDrainRate = 1f;
private bool isSprinting = false;
void Update() {
isSprinting = Input.GetAxis("Sprint") > 0f;
Vector2 mi = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
mv = mi.normalized * speed;
}
Then in fixedupdate, check if your energy bar has enough to drain for that frame, and if it does then drain it and increase the effective movespeed that frame:
private void FixedUpdate() {
float energyToDrain = energyBarDrainRate * Time.deltaTime
bool drainSprint = energyAmount > energyToDrain ;
float effectiveMv = mv;
if (drainSprint && isSprinting) {
effectiveMv = mv * 2f;
energyAmount -= energyToDrain;
}
rb.MovePosition(rb.position + effectiveMv * Time.deltaTime);
}

How to apply an Easing algorithm in Unity?

I want to move the Camera with a smooth motion so I am writing this script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraScroll : MonoBehaviour {
public GameObject targetCamera;
private bool isScrolling = false;
private Vector3 initialPosition;
private Vector3 scrollTarget;
private float scrollDuration;
private float scrollTick;
private float timeStartedScrolling;
#region Easing
float EasingInOutSine(float time, float start, float change, float duration) {
return -change/2 * (Mathf.Cos(Mathf.PI*time/duration) - 1) + start;
}
float EasingOutSine(float time, float start, float change, float duration) {
dynamic td = time/duration;
return change * Mathf.Sin(td * (Mathf.PI/2)) + start;
}
float EasingInOutBack(float time, float start, float change, float duration) {
float td2 = time/(duration/2.0f);
float s = 1.70158f;
if ((td2) < 1.0f) {
return change/2.0f*(td2*td2*(((s*(1.525f))+1.0f)*td2 - (s*(1.525f)))) + start;
} else {
return change/2.0f*((td2-2.0f)*(td2-2.0f)*(((s*(1.525f))+1.0f)*(td2-2.0f) + (s*(1.525f))) + 2) + start;
}
}
#endregion
void Start() {
if (targetCamera == null) {
targetCamera = GameObject.Find("Main Camera");
}
// Tests
//Scroll(new Vector3(4.0f, 0.0f, -10f), 2f); // seems to be working?
//Scroll(new Vector3(4.0f, 0.0f, -10f), 0.3f); // easing gets cut off
Scroll(new Vector3(4.0f, 0.0f, -10f), 1f); // easing gets cut off
}
void Update() {
if (isScrolling) {
scrollTick += Time.deltaTime;
float s = scrollTick / scrollDuration;
float timeSinceStarted = Time.time - timeStartedScrolling;
float percentageComplete = timeSinceStarted / scrollDuration;
if (percentageComplete > 1.0f) {
isScrolling = false;
Debug.Log("Scrolling ended");
} else {
// time, start, change, duration
var easing = EasingInOutSine(Time.time, 0, Mathf.Clamp01(Time.time*scrollDuration), scrollDuration);
targetCamera.transform.position = Vector3.Lerp(initialPosition, scrollTarget, easing);
}
}
}
void Scroll(Vector3 targetPosition, float duration) {
scrollDuration = duration;
scrollTarget = targetPosition;
initialPosition = targetCamera.transform.position;
timeStartedScrolling = Time.time;
isScrolling = true;
}
}
The problem is that the motion of the camera is cutoff and the easing is not applied properly if the duration is set to be 1 second or lower
I'd like the script to be able to use different easing algorithms in the future and play with deltaTime properly, I am not sure if I am doing it the right way
I think you just need a constant change.
With an increasing change the slow down effect of the easing will be overpowered by the exponential movement caused by the increasing change. This effect is most apparent in low duration movements because it will never reach the value ceiling slower so it will keep speeding up until the end.
Since your using your easing algorithm to output a percentage to throw into a lerp, you probably want to use a change of 1 so that the easing interpolates between 0 and 1.

Categories

Resources