unity lerp scale doesn't work - unity3d

I am trying to make an object scale from zero to it's normal size when I instantiate it, so it will look like it popped to the screen.
So when the object start I get it's normal size then update it to zero, and then in update I am scaling it.
This is my code:
void Start()
{
normalScale = transform.localScale;
transform.localScale *= 0.1f;
}
void Update()
{
transform.localScale = Vector3.Lerp(transform.localScale * 0.1f, transform.localScale, 5f * Time.deltaTime);
// destroy item
if (transform.localScale == normalScale)
{
transform.localScale = transform.localScale * 0.1f;
}
}
Thank you.

With this you're always changing from it's current scale, which of course you changed last update
transform.localScale = Vector3.Lerp(transform.localScale * 0.1f, transform.localScale, 5f * Time.deltaTime);
What you need to do is create two Vector3 outside of the update function, one for the start size, one for the final size
Vector3 start = Vector3.zero;
Vector3 end = new Vector3(1,1,1);
You'll also need a timer:
float lerpTime = 0;
Altogether you get
transform.localScale = Vector3.Lerp(start, end, lerpTime);
lerpTime += Time.deltaTime // times whatever multiplier you want for the speed

There are a couple of issues with your code that are likely to be causing problems. The first is the start/end values you're passing in to the lerp:
Vector3.Lerp(transform.localScale * 0.1f, transform.localScale, 5f * Time.deltaTime);
On your second frame, .localScale is roughly (0.1, 0.1, 0.1). The max value of the lerp on your second frame is the value from your first frame. That means that your current code is endlessly shrinking the target - the opposite of what you wanted.
The other problem is the way you're handling the time. You're passing 5f * Time.deltaTime, which is (probably) always going to be less than 1. This means you will never reach the maximum value.
So, to fix these, you need two things: first, you need to make sure your min/max values are actually min/max values, not arbitrary values in between. Second, you need to make sure your third parameter progresses smoothly from 0 to 1 over a defined time.
Something like this:
public float ScaleTime = 5f; // the time it'll take to grow, settable in the inspector
public float ScaleTime = 5f; // the time it'll take to grow, settable in the inspector
Vector3 _targetScale;
Vector3 _startScale;
float _currentLerp = 0f;
void Start()
{
_targetScale = this.localScale;
_startScale = _targetScale * 0.1f;
}
void Update()
{
_currentLerp += Time.deltaTime * ScaleTime;
if (_currentLerp < 1)
{
transform.localScale = Vector3.Lerp(_startScale, _targetScale, _currentLerp);
}
else
{
transform.localScale = _targetScale; // make sure we definitely hit the target size
... do whatever else you need to do here...
}
}

As per https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html your 3rd parameter in
transform.localScale = Vector3.Lerp(transform.localScale * 0.1f, transform.localScale, 5f * Time.deltaTime);
is supposed to indicate the "fracJourney", in other words it should change from 0f to 1f to indicate the progress of your animation, but Time.deltaTime will give you the time since the last frame so that you probably see it jump around 0.005 (or whatever your frame rate is).
You need to add another variable to indicate the progress of your animation:
public float speed = 1.0F;
private float startTime;
void Start() {
startTime = Time.time;
normalScale = transform.localScale;
}
void Update()
{
float distCovered = (Time.time - startTime) * speed;
transform.localScale = Vector3.Lerp(Vector3.zero, normalScale, distCovered);
// destroy item
if (transform.localScale >= normalScale)
{
startTime = Time.time; // Reset the animation
}
}

Related

Rotating an object around the same pivot gives different results

Rotating an object around the same pivot gives different results
So I'm trying to make a door that opens when clicked on, and I have a door that works good as is, but for some reason when I try to make it work for another door using RotateAround, it moves along the Z axis when closing. The code is identical to the other door that works and works when opening this door, so I have no clue as to why it's having trouble closing for this one. The code I'm using to rotate them is as follows
IEnumerator CloseDoor()
{
float timer = 1f;
float speed = 30f;
Vector3 pivot = new Vector3(door.transform.position.x, door.transform.position.y,
door.transform.position.z + 1);
while (timer > 0)
{
door.transform.RotateAround(pivot, Vector3.down, speed * Time.deltaTime);
yield return new WaitForSeconds(0.001f);
timer -= Time.deltaTime;
}
}
IEnumerator OpenDoor()
{
float timer = 1f;
float speed = 30f;
Vector3 pivot = new Vector3(door.transform.position.x, door.transform.position.y,
door.transform.position.z + 1);
while (timer > 0)
{
door.transform.RotateAround(pivot, Vector3.up, speed * Time.deltaTime);
yield return new WaitForSeconds(0.001f);
timer -= Time.deltaTime;
}
}
I'll start with some overall advices, regarding your code.
Don't use Time.deltaTime with new WaitForSeconds(...). Time.deltaTime is an amout of time, that passed between Update() calls. But your logic is not inside Update(). You use own time inervals with new WaitForSecons(...), but if the specified amout of time is less then Time.deltaTime, then it will be executed every Time.deltaTime after all Update() calls. It works good for you only because your time interval 0.001f is low enough to be executed every Update(). When your argument in new WaitForSeconts(...) becomes more them Time.deltaTime, the rotation speed of the door becomes too low to door be opened completely. To made your code more clear and safe, return null with yield instruction. In this case, you can be shure, that coroutine will be executed every Update(). More info here.
Your code works with transform positioning, and probably you have physics in your game. All changes with physical object supposed to be done in the FixedUpdate(). In your case your cant return new WaitForFixedUpdate() in yield instruction and use Time.fixedDeltaTime with it.
So speaking about main question. In your code, you are doing pretty unclear and unsafe thing, like hardcoding pivot with global position offset here:
Vector3 pivot = new Vector3(door.transform.position.x, door.transform.position.y,
door.transform.position.z + 1);
Probably, not all doors will have same rotation, and for some of them offset, just with z coordinate will be wrong. Also it becomes wrong after door opening, because the position and rotation of the door changed when you rotating it around some point, that is not the center of the door. So you should base on local transform point, like this:
public class Door : MonoBehaviour
{
private bool _doorOpened = false;
private bool _doorOpening = false;
[SerializeField] // to see in the inspector.
private Vector3 _localDoorRotatePoint = new Vector3(0.5f, 0f, 0f);
// Update is called once per frame
void Update()
{
if (!_doorOpening && Input.GetKeyDown(KeyCode.E))
{
if (!_doorOpened)
{
StartCoroutine(OpenDoor());
}
else
{
StartCoroutine(CloseDoor());
}
}
}
IEnumerator CloseDoor()
{
_doorOpening = true;
var timer = 1f;
var speed = 30f;
// in my case _localDoorRotate point is (0.5f, 0f, 0). In your case it will be like (0, 0, 1f) or something like this.
// remember, that this point is in local transform coordinates, and in scales with this transform scale vector.
var pivot = transform.TransformPoint(_localDoorRotatePoint);
while (timer > 0)
{
transform.RotateAround(pivot, Vector3.down, speed * Time.fixedDeltaTime);
yield return new WaitForFixedUpdate();
timer -= Time.fixedDeltaTime;
}
_doorOpening = false;
_doorOpened = false;
}
IEnumerator OpenDoor()
{
_doorOpening = true;
var timer = 1f;
var speed = 30f;
// in my case _localDoorRotate point is (0.5f, 0f, 0f). In your case it will be like (0f, 0f, 1f) or something like this.
// remember, that this point is in local transform coordinates, and in scales with this transform scale vector.
var pivot = transform.TransformPoint(_localDoorRotatePoint);
while (timer > 0)
{
transform.RotateAround(pivot, Vector3.up, speed * Time.fixedDeltaTime);
yield return new WaitForFixedUpdate();
timer -= Time.fixedDeltaTime;
}
_doorOpening = false;
_doorOpened = true;
}
}
Helpfull links, that can help you with understanding this code:
Transform.TransformPoint
Transform.up

How to use Lerp Dash Movement in Unity

I'm doing a beginner project in Unity and want to use Vector3.Lerp to do animation for the dash movement. Here is my code and I'm struggling to see why it doesn't work.
void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
Vector3 start = transform.position;
Vector3 dash = new Vector3(start.x + m_DashDist, start.y);
transform.position = Vector3.Lerp(start, dash, Time.deltaTime);
}
}
How Lerp Works
The last argument you pass to the Lerp function is a float value (mostly between 0f and 1f).
If this value is 0f, Lerp will return the first argument (let's say _dashStart).
If this value is 1f, Lerp will return the second argument (let's say _dashEnd).
If this value is 0.5f, Lerp will return the "mid" between the first and the second argument.
As you can see, this function interpolates between these two arguments depending on the third argument.
This function has to be called every frame and the float value (the 3rd argument) needs to be incremented by Time.deltaTime every frame in your case.
But your if block is only executed once, when you press E. For instance, you can set a bool to true when the E key has been pressed. Here is the further code – hopefully with no mistakes:
public float dashTime = 0.1f;
private float _currentDashTime = 0f;
private bool _isDashing = false;
private Vector3 _dashStart, _dashEnd;
void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
if (_isDashing == false)
{
// dash starts
_isDashing = true;
_currentDashTime = 0;
_dashStart = transform.position;
_dashEnd = new Vector3(_dashStart.x + m_DashDist, _dashStart.y);
}
}
if (_isDashing)
{
// incrementing time
_currentDashTime += Time.deltaTime;
// a value between 0 and 1
float perc = Mathf.Clamp01(_currentDashTime / dashTime);
// updating position
transform.position = Vector3.Lerp(_dashStart, _dashEnd, perc);
if (_currentDashTime >= dashTime)
{
// dash finished
_isDashing = false;
transform.position = _dashEnd;
}
}
}
There are different ways of using Lerp. I mostly use this approach.

Unity3d lift movement down with keycode

this code does so when I press the A key or the B key, the elevator stops upwards on the first or second floor.
The question is
How do I modify the code that when the elevator is on the second floor, for example, and I press the A key again to make the elevator go down to the first floor?
Question two
If I replace
if (Input.GetKey (KeyCode.A)) {}
with this
if (Input.GetKeyUp (KeyCode.Keypad1)) {}
so the code does not work. Why?
Thank you for advice
Sorry for the bad English. Here is the code
public GameObject lift;
private bool keyHHit=false;
private bool keyHHitB=false;
void Update ()
{
if (Input.GetKey(KeyCode.B))
{
keyHHit=true;
}
if( keyHHit==true)
{
if(transform.localPosition.y >= 14.52)
{
transform.Translate(new Vector3(0, 0, 0) * 2 * Time.deltaTime, Space.Self);
}
else
{
transform.Translate(new Vector3(0, 2, 0) * 2 * Time.deltaTime, Space.Self);
}
}
if (Input.GetKey(KeyCode.A))
{
keyHHitB=true;
}
if( keyHHitB==true)
{
if(transform.localPosition.y >= 8.52)
{
transform.Translate(new Vector3(0, 0, 0) * 2 * Time.deltaTime, Space.Self);
}
else
{
transform.Translate(new Vector3(0, 2, 0) * 2 * Time.deltaTime, Space.Self);
//transform.Translate(Vector3.up * 0.05f);
}
}
}
So if I understand you correctly
you have two or 3 floors (doesn't matter, could be more later).
Once you press a button you want to move towards the target floor until you reach there. "Lock" input meanwhile
Once you reached a floor "unlock" Input again and allow to also go down if the current floor is above the target floor
I would use a Coroutine and Vector3.Lerp for this.
First lets have a class for floors so it is easier to add additional floors later
[Serializable]
public class FloorSetting
{
public KeyCode Key;
public Vector3 Position;
}
Then simply have an array of all floors target positions and according keys
// Configure these floors in the Inspector
// simply add the entries you need and adjust buttons and target positions
[SerializeField] private FloorSetting[] floors;
[SerializeField] private float moveUnitsPerSecond = 1f;
// This flag is for blocking any Input while moving
// thus preventing concurrent routines
private bool isMoving;
private void Update()
{
// if already moving do nothing
if(isMoving) return;
// check if any of the configured keys was pressed and start moving
// towards the according position
foreach(var floor in floors)
{
// You want to use GetKeyDown to only get the first press event
// you don't care about continues press since you will keep moving automatically until reaching the floor
if(Input.GetKeyDown(floor.Key))
{
StartCoroutine(MoveRoutine(floor.Position));
}
}
}
private IEnumerator MoveRoutine(Vector3 targetPosition)
{
// block concurrent routine
if(isMoving) yield break;
isMoving = true;
// Get the duration of movement
var startPosition = transform.position;
var duration = Vector3.Distance(startPosition, targetPosition) / moveUnitsPerSecond;
// Move smoothly towards the target position and add some easing
var timePassed = 0f;
while(timePassed <= duration)
{
// A interpolation factor between 0 and 1
var factor = timePassed / duration;
// optionally add ease-in and out
factor = Mathf.SmoothStep(0, 1, factor);
// Set the position to an interpolated position between start and target depending on the factor
transform.position = Vector3.Lerp(startPosition, targetPosition, factor);
// increase by the time passed since last frame
timePassed += Time.deltaTime;
// Tells Unity to "pause" the routine here, render this frame
// and continue from here in the next frame
yield return null;
}
// Just to be sure in he end set it to hard position
transform.position = targetPosition;
// optionally add some cooldown in seconds
yield return new WaitForSeconds(1f);
// Release the lock so next move can start now
isMoving = false;
}
If you don't need the easing you could as well also implement it lot easier using Vector3.MoveTowards like e.g.
while(!Mathf.Approximately(Vector3.Distance(transform.position, targetPosition), 0)
{
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveUnitsPerSecond);
yield return null;
}
transform.position = targetPosition;
Note: Typed on smartphone but I hope the idea gets clear

Why do I use Time.deltaTime twice in this code?

void FixedUpdate()
{
//for gravity
//
buildingVelocity += gravityScale * Physics2D.gravity * Time.deltaTime;
Vector2 deltaPosition = buildingVelocity * Time.deltaTime;
Vector2 moveAmount = Vector2.up * deltaPosition.y;
Moving (moveAmount);
//
//
}
//for gravity
//
void Moving(Vector2 moveAmount)
{
rb2d.position += moveAmount;
}
I'm creating gravity for my game, following along with a Unity tutorial. I can account for all the decisions they've made, except one. I just don't understand why do I need the deltaPosition Vector2? Is it because I need to multiply again by Time.deltaTime? And if so, why? My simple mind says well... if deltaPosition = buildingVelocity... I'll just make Vector2 moveAmount = Vector2.up * buildingVelocity.y; and not even have to create the deltaPosition variable. Can someone break this down for me? why do I multiply it twice by deltatime? Thanks!

How to prevent tilting over in a unity object

I've got a hover tank and I'm working on a controller for it. The goal is to have the float above the ground, but I don't want it to tilt over more the a degree or two. I want it to basically stay level all the time.
I'm using a Rigidbody on the tank to control it with .MovePosition and .MoveRotation. You can see the FixedUpdate function below. I've got a section below to keep thinks level, where I check if there tank is tipping past its maxTilt amount. If it is, the keep it at the max.
This makes the tank very jittery all the time. It looks like it's bouncing up and down quickly. I think its due to the hover forces, but I'm not sure.
How can I keep the tank level while still letting it hoover?
FixedUdpate
void FixedUpdate () {
if (!isServer) {
return;
}
CheckGrounded ();
Hoover ();
if (_moveForward) {
float moveAmount = moveSpeed * Time.deltaTime;
_rigidbody.MovePosition(_rigidbody.position + _rigidbody.transform.forward * moveAmount);
}
if (_moveBackward) {
float moveAmount = (-moveSpeed * 0.6f) * Time.deltaTime;
_rigidbody.MovePosition(_rigidbody.position + _rigidbody.transform.forward * moveAmount);
}
if (_turnLeft) {
Quaternion rotateAmount = Quaternion.Euler(new Vector3(0f, -angularSpeed, 0f) * Time.deltaTime);
_rigidbody.MoveRotation(_rigidbody.rotation * rotateAmount);
}
if (_turnRight) {
Quaternion rotateAmount = Quaternion.Euler(new Vector3(0f, angularSpeed, 0f) * Time.deltaTime);
_rigidbody.MoveRotation(_rigidbody.rotation * rotateAmount);
}
if (_jump && _isGrounded) {
_isJumping = true;
}
if (_isJumping && _jumpTimeLeft > 0) {
float moveAmount = jumpSpeed * Time.deltaTime;
_rigidbody.MovePosition(_rigidbody.position + _rigidbody.transform.up * moveAmount);
_jumpTimeLeft -= Time.deltaTime;
} else if (_isJumping) {
_isJumping = false;
_jumpTimeLeft = jumpTime;
}
// Keep things level
Vector3 rotation = _rigidbody.rotation.eulerAngles;
if (rotation.x > maxTilt) {
rotation.x = maxTilt;
} else if (rotation.x < -maxTilt) {
rotation.x = -maxTilt;
}
if (rotation.y > maxTilt) {
rotation.y = maxTilt;
} else if (rotation.y < -maxTilt) {
rotation.y = -maxTilt;
}
if (rotation.z > maxTilt) {
rotation.z = maxTilt;
} else if (rotation.z < -maxTilt) {
rotation.z = -maxTilt;
}
Quaternion q = new Quaternion ();
q.eulerAngles = rotation;
_rigidbody.rotation = q;
}
Hoover
void Hoover() {
foreach (Transform hoverPoint in hooverPoints) {
Ray ray = new Ray (hoverPoint.position, -hoverPoint.up);
RaycastHit hitInfo;
if (Physics.Raycast (ray, out hitInfo, hooverHeight)) {
float distance = Vector3.Distance (hoverPoint.position, hitInfo.point);
if (distance < hooverHeight) {
_rigidbody.AddForceAtPosition (hoverPoint.up * hooverForce * (1f - distance / hooverHeight), hoverPoint.position, ForceMode.Force);
}
}
}
}
I'm thinking that the reason you are seeing 'jitters' is because...
All physics calculations and updates occur immediately after FixedUpdate.
https://docs.unity3d.com/Manual/ExecutionOrder.html
Because you are adjusting the tilt in FixedUpdate(), which is then immediately followed by the Physics Engine running its calculations this will sometimes alter the tilt value giving a 'jitter'. The reason I say sometimes is because FixedUpdate() can run multiple times per frame (FPS dependant), which potentially means the following call order for a single frame:
FixedUpdate() // start frame
PhysicsEngine
FixedUpdate() // end frame
In the case above, there would be no jitter because you re-correct the tilt after the physics engine has done its thing - by complete fluke. However when you don't get a second FixedUpdate() call on a frame, you will have:
FixedUpdate() // start frame
PhysicsEngine // end frame
Which will result in your jitter.
So my suggestion is to break up your FixedUpdate() and offset any tilt corrections to LateUpdate(). Becuase LateUpdate() is always the last update call prior to the rendering of the frame.