I have tried something like
float speed = (currentPosition - previousPosition).magnitude / Time.deltaTime
in Update().
But I get very bad results because currentPosition and previousPosition are too close. After subtraction there's too much rounding error. The speed values fluctuate too much.
And also I don't want to average across multiple frames because that produces delays and only improves the result a little.
Is there any better way to calculate the speed of an object?
You could use a coroutine that calculates the speed slower than every frame:
void OnEnabled() {
StartCoroutine(SpeedReckoner());
}
public float Speed;
public float UpdateDelay;
private IEnumerator SpeedReckoner() {
YieldInstruction timedWait = new WaitForSeconds(UpdateDelay);
Vector3 lastPosition = transform.position;
float lastTimestamp = Time.time;
while (enabled) {
yield return timedWait;
var deltaPosition = (transform.position - lastPosition).magnitude;
var deltaTime = Time.time - lastTimestamp;
if (Mathf.Approximately(deltaPosition, 0f)) // Clean up "near-zero" displacement
deltaPosition = 0f;
Speed = deltaPosition / deltaTime;
lastPosition = transform.position;
lastTimestamp = Time.time;
}
}
Related
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
I want to write an IEnumerator to move at the desire distance at a specified time. I have tried to write the code for this but this is running a different way.
float moveDistance=1f;
float moveSpeed=5f;
float elapsedDistance = 0f;
while (elapsedDistance <= moveDistance)
{
elapsedDistance += Time.deltaTime * moveSpeed;
Vector3 cubeLocalPosition = transform.localPosition;
cubeLocalPosition.y += Time.deltaTime * moveDistance;
transform.localPosition = cubeLocalPosition;
yield return null;
}
Through this code, Object can't able to travel 1 unit distance. How can I correct this code?
Your while loop condition uses elapsedDistance which is increasing with moveSpeed. That latter is 5 so it will be 1 in 1/5 of a second. Your object is likely only moving 0.2unit.
you should use Mathf.Lerp or MoveTowards
float distance = 1f;
float time = 0f;
float period = 1f; // how long in second to do the whole movement
yield return new WaitUntil(()=>
{
time += Time.deltaTime / period;
float movement = Mathf.Lerp(0f, distance, time);
Vector3 cubeLocalPosition = transform.localPosition;
cubeLocalPosition.y += movement;
transform.localPosition = cubeLocalPosition;
return time >= 1f;
});
Following your own rotation, you calculate the finalpoint to go
and after,
you could use Vector3.Lerp or Vector.Slerp to move in the specified time..So the moving speed adapt itself following the time desired
var endpoint = transform.position + transform.forward.normalized * distance;
StartCoroutine(MoveToPosition(transform, endpoint, 3f)
:
:
public IEnumerator MoveToPosition(Transform transform, Vector3 positionToGO, float timeToMove)
{
var currentPos = transform.position;
var t = 0f;
while (t < 1f)
{
t += Time.deltaTime / timeToMove;
transform.position = Vector3.Lerp(currentPos, positionToGO, t);
yield return null;
}
transform.position = positionToGO;
}
I want to have a coroutine to use the lerp function and so far I managed to do so partially. The characters will turn their position to face the target object. However, I want the characters to move their rotation starting from their own while what is currently happening is their rotation is set to some abstract value like X=0 and then it will start rotating them from there.
What I want to do is simply turn the character to face another transform smoothly. Here's the code:
public IEnumerator LookSlerpAt(Transform target, int seconds)
{
IsTurningHead = true;
Vector3 relativePos = target.position - this.transform.position;
Quaternion lookRotation = Quaternion.LookRotation(relativePos);
float elapsedTime = 0f;
float fraction = elapsedTime / seconds;
while (elapsedTime <= seconds)
{
this.transform.rotation = Quaternion.Lerp(target.rotation, lookRotation, fraction);
print($"rotation:{this.transform.rotation}");
elapsedTime += Time.deltaTime;
fraction = elapsedTime / seconds;
yield return Time.deltaTime;
}
IsTurningHead = false;
}
Thanks for your time
if you want to rotate smoothly to direction you could use that as coroutine: You specify the specified time to rotate and the velocity is automatically adjusted
public IEnumerator RotateToDirection(Transform transform, Vector3 positionToLook, float timeToRotate)
{
var startRotation = transform.rotation;
var direction = positionToLook - 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;
}
I'm trying to implement my own gravity for a character.
But for some unknown to me reason the character's landing speed is higher landing than when jumping, by quite a lot (~15 initial jump speed, ~24 final land speed). I'm ultimately trying to replicate behavior shown on the gif below.
https://imgur.com/a/q77w5kS
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class Movement : MonoBehaviour {
public float speed = 3;
public float jumpSpeed = 15;
public float gravity = -1f;
private float ySpeed = 0;
private float jumpTime = 0;
private bool direction = false; //true for going up, false for falling down - gravity
private bool previousDirection = false; //to keep track of changing direction
private CharacterController _characterController;
// Start is called before the first frame update
void Start() {
_characterController = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update() {
float dx = Input.GetAxis("Horizontal");
float dz = Input.GetAxis("Vertical");
float dy = Input.GetAxis("Jump");
// move around
Vector3 movement = new Vector3(dx, 0, dz);
// limit diagonal movement
movement = Vector3.ClampMagnitude(movement, 1);
// speed
movement *= speed;
// change of direction
if(ySpeed>0 && direction!=true)
direction = true;
else if(ySpeed<0 && direction!=false)
direction = false;
// if changed direction - peak of the jump
if(direction!=previousDirection){
Debug.Log("Jump Time = " + jumpTime);
jumpTime = Time.deltaTime;
previousDirection = direction;
}
// jump/on ground
if(_characterController.isGrounded) {
ySpeed = -0.5f;
jumpTime = 0;
if(dy > 0f){
ySpeed = jumpSpeed;
}
}
// gravity - when not on ground
else{
// dv/dt
ySpeed += gravity*jumpTime;
// add jump time
jumpTime += Time.deltaTime;
// Debug.Log(jumpTime);
Debug.Log(ySpeed);
}
movement.y = ySpeed;
// direction adjustment
movement = transform.TransformDirection(movement);
movement *= Time.deltaTime;
_characterController.Move(movement);
}
}
I think I figured out what was wrong with my idea.
ySpeed += gravity*jumpTime
So every frame I'm adding more and more acceleration downwards. This should just be: ySpeed += gravity *Time.deltaTime
(Acceleration due to gravity is then constant, not getting greater as time passes)
It is being integrated over many steps, so each Update() cycle is a slice of time that adds some velocity based on acceleration due to gravity, multiplied by the amount of time taken by that little slice.
In another words...
Gravity is a constant acceleration. I've made it a linear function of the time spent in the air. So, I start the jump with no gravity and end the jump with very high gravity.
As you can see in the topic - I have a camera problem. I use a script (you can see below) and I have something like this - http://rapidgamesstudio.com/games/diggermachines/
What I want to achieve is a smooth following camera to player.
I've tried everything. I have about 50-60 fps and still this bug occures.
This is my camera code:
void Update() {
if(!player)
return;
//if(!isDiggableCamera) {
Vector3 point = Camera.main.WorldToViewportPoint(player.transform.position);
Vector3 delta = player.transform.position - Camera.main.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, point.z)); //(new Vector3(0.5, 0.5, point.z));
Vector3 destination = transform.position + delta;
destination.z = transform.position.z;
transform.position = Vector3.SmoothDamp(transform.position, destination, ref velocity, dampTime);
//Vector3 destination = new Vector3(player.transform.position.x, player.transform.position.y, transform.position.z);
//transform.position = Vector3.SmoothDamp(transform.position, destination, ref velocity, dampTime);
//} else {
// startDigging(0, 0, 0);
//}
leftSite.position = new Vector3(leftSite.position.x, player.position.y, leftSite.position.z);
rightSite.position = new Vector3(rightSite.position.x,
player.position.y, rightSite.position.z);
}
I tried execute this code in Update(), FixedUpdate(), LateUpdate() even with all three - and still is the same problem.
Below code for updating player position:
//move player
float changableSpeedX = 5000.0f;
float changableSpeedY = 6000.0f;
Vector2 speed = new Vector2(x * Time.deltaTime * changableSpeedX,
y * Time.deltaTime * changableSpeedY);
//if(playerRigidbody.velocity.y + speed.y >= Game.game().activeMaxVelY)
// speed.y = Game.game().activeMaxVelY - playerRigidbody.velocity.y;
playerRigidbody.AddForce(speed);
//AddForce(speed);
//and checking max speed
protected void checkSpeed()
{
if(playerRigidbody.velocity.x > Game.game().activeMaxVelX)
playerRigidbody.velocity = new Vector2(Game.game().activeMaxVelX, playerRigidbody.velocity.y);
if(playerRigidbody.velocity.x < -Game.game().activeMaxVelX)
playerRigidbody.velocity = new Vector2(-Game.game().activeMaxVelX, playerRigidbody.velocity.y);
if(playerRigidbody.velocity.y > Game.game().activeMaxVelY)
playerRigidbody.velocity = new Vector2(playerRigidbody.velocity.x, Game.game().activeMaxVelY);
if(playerRigidbody.velocity.y < maxSpeedYGravity)
playerRigidbody.velocity = new Vector2(playerRigidbody.velocity.x, maxSpeedYGravity);
}
Could anyone help me?
If you need more code please let me know which part (because I don't want to add too much unnecessary code)
Might i suggest a lerp sir , in the update function use this
maincamera.transform.position = new vector3(maincamera.transform.position,player.transform.poistion,speed*Time.deltaTime);
Try This One !!!
private float xMax;
[SerializeField]
private float yMax;
[SerializeField]
private float xMin;
[SerializeField]
private float yMin;
private Transform target;
// Use this for initialization
void Start () {
target = GameObject.Find("Player").transform;
}
// Update is called once per frame
void LateUpdate () {
transform.position = new Vector3(Mathf.Clamp(target.position.x, xMin, xMax), Mathf.Clamp(target.position.y, yMin, yMax),transform.position.z);
}
}