I've got an empty game object filled with all my player components, within it is a model which animates itself when the controls are pressed which are instantiated by GetButtonDown. Currently everything in the player game object moves instantly to the next point (basically teleports there with no transition). Is there a way I can get it all to move over say 1 second rather than going there instantly? Here's what happens on GetButtonDown
if (Input.GetButtonDown ("VerticalFwd"))
{
amountToMove.y = 1f;
transform.Translate (amountToMove);
}
I've tried transform.Translate (amountToMove * Time.deltaTime * randomint);
and all sorts of ways to use Time
however that doesn't seem to work even though it seems the most logical way. I'm guessing because the GetButtonDown only "runs" when it is pressed and has no update function to "time" the move every frame? Any ideas?
amountToMove is saved as a Vector3 aswell.
The first formula is wrong. The correct formula would be:
this.transform.position += (Distance/MoveTime) * time.deltatime
Try .Lerp, it runs as a coroutine interpolating a value over some amount of time Vector3.Lerp(Vector3 start, Vector3 end, float time)
See documentation here
This should give you a rough idea of whats going on
Vector3 Distance = End - Start;
// this will return the difference
this.transform.position += Distance/time.deltatime * Movetime
// this divides the Distance equal to the time.deltatime.. (Use Movetime to make the movement take more or less then 1 second
IE: If the Distance is 1x.1y.1z, and time.deltatime is .1 and Movetime is 1.... you get a movement of .1xyz per tick, taking 1 second to reach the end point
FYI: transform.Translate works as a fancy .position +=, for this purpose you can use them interchangeably
EDIT
Here are a few solutions
Easy ::
Vector3 amountToMove = new Vector3(0,1,0); // Vector3.up is short hand for (0,1,0) if you want to use that instead
if (Input.GetButtonDown ("VerticalFwd"))
{
transform.position = Vector3.Lerp(transform.position ,transform.position + amountToMove, 1);
}
Hard and probably unnecessary ::
Write a Coroutine that does a kind of Linear interpolation for your specific application See documentation here
Related
My camera jitters on rotation, but not movement. This script is the only asset in a new project, and it's attached to the camera - it's supposed to be a free-flying camera. I added a few cubes into the environment so I could check it was moving correctly, and it stutters several times a second whenever it's rotated. Movement works just fine.
I've tried changing camera clipping planes, I've tried moving either movement or rotation or both into Update() instead, removing the default camera and adding my own, and since it's an otherwise empty project I'm showing over 500fps so don't have any performance issues - nothing has had any effect, for better or worse.
Any suggestions welcome! If someone has a minute to open up a default project and slap this script on the camera and make sure it's not something on my end, that would at least rule that out and I'd really appreciate it. Thanks in advance.
public class CameraController : MonoBehaviour
{
public Vector2 mouseSensitivity = Vector2.one;
public Vector3 moveSensitivity = Vector2.one;
public Transform camTransform;
Vector3 camEulers = Vector3.zero;
void Start()
{
camTransform = FindObjectOfType<Camera>().transform;
}
void LateUpdate()
{
KeyboardMovement();
LockedMouseRotation();
}
void KeyboardMovement()
{
camTransform.position += Input.GetAxis( "Vertical" ) * camTransform.forward * Time.deltaTime * moveSensitivity.y;
camTransform.position += Input.GetAxis( "Horizontal" ) * camTransform.right * Time.deltaTime * moveSensitivity.x;
}
void LockedMouseRotation()
{
camEulers.x -= Input.GetAxis( "Mouse Y" ) * mouseSensitivity.y * Time.deltaTime;
camEulers.y += Input.GetAxis( "Mouse X" ) * mouseSensitivity.x * Time.deltaTime;
camEulers.x = Mathf.Clamp( camEulers.x, -90.0f, 90.0f );
camTransform.rotation = Quaternion.Euler( camEulers );
}
}
Don't use Time.deltaTime here.
Your goal here is to convert measurements of linear distance (GetAxis("Mouse X") and GetAxis("Mouse Y")) into a measurement of angular distance (the angular distance you are using to adjust the camera's Euler angles). For that you only need a constant factor, which can be your mouseSensitivity components.
But GetAxis gets multiplied by Time.deltaTime in other places and works perfectly fine. Why do those work like that?
Your goal is not converting to distance from "throttle position" like one might from GetAxis("Horizontal") or GetAxis("Vertical") or GetAxis("customKeyButtonOrThrottleAxis") . That is a situation where you would want to multiply by a time (Time.deltaTime) to calculate a distance if you are using the position of the button or throttle as velocity. This is because distance = speed * time.
In some cases you might multiply by Time.deltaTime to calculate a velocity or speed if the button or throttle is to represent acceleration, because speed = acceleration * time.
Okay, so why doesn't Time.deltaTime work here?
When you use GetAxis("Mouse X") or GetAxis("Mouse Y"), you get a measurement of pixel difference in position since the last frame.
The typical way a mouse driven camera works in games is that moving the mouse the same distance in the same direction should move the camera the same way (assuming you don't move the camera all they way up or down of course).
So what that means is that multiplying by Time.deltaTime does here is often actually the opposite of what you might expect - moving the mouse the same distance moves the camera's rotation differently depending on how many frames that movement occurred over and how quickly those frames were rendered.
The faster the frames were rendered (that means, a smaller Time.deltaTime), the less camera rotation the same mouse distance becomes. This results in unpredictable jitter especially when the very act of moving the camera can drastically change the framerate.
So how do I fix it then?
So, the only thing you need to do here is to remove the multiplication by Time.deltaTime:
camEulers.x -= Input.GetAxis( "Mouse Y" ) * mouseSensitivity.y;
camEulers.y += Input.GetAxis( "Mouse X" ) * mouseSensitivity.x;
A similar situation where you might want to include Time.deltaTime is if you want to calculate the speed or velocity of the mouse movement.
But, in that case, you would want to divide by Time.deltaTime. This is because speed = distance / time.
Vector2 mouseVelocity = Vector2.Scale(mouseSensitivity,
new Vector2(Input.GetAxis("Mouse X"),
Input.GetAxis("Mouse Y"))
) / Time.deltaTime;
In most cases, you probably don't want to be concerned with the speed/velocity of the mouse movement. But, for example, those interested in a super monkey ball type game using a track ball input might find this side bit useful!
I've been struggling with the movement of my player since its movement was based on updating its world position with time frames. Using Update or FixedUpdate didn't change anything.
Since, I setted up another movement which involved a lot of changes in my code, this time based on speed (* Time.delaTime) and Update make my player moving faster when the mouse button is kept down, but FixedUpdate fix the problem and whatever if I just clic or keep the mouse down my player keep the same speed.
Code:
void FixedUpdate()
{
if(Input.GetMouseButton(0))
{
Vector3 target = GetMouseWorldPosition();
transform.position = Vector3.MoveTowards(transform.position, target , speed * Time.deltaTime);
Good luck and have fun :)
Issue 1
As you are already using Coroutine, you can use yield return new WaitForSeconds (1) instead of yield return null. you can adjust the number to set the wait duration between steps. Don't purposely slow down your CPU to achieve anything about timing. And don't mess up with Fixed Update time it's for physics simulation.
Issue 2
change it to yield return new WaitForSeconds (Input.GetMouseButton(0)?.5f:1);, so it waits shorter time if mouse button is held.
Issue 3
if(pathfinding==null || grid=null){
...(do random movement)
}else{
...(do pathfinding movement)
}
EDIT 1
if you don't want teleporting, you need to write transition yourself.
Vector3 oldPos = transform.position;
Vector3 newPos = nodeWaypoint;
float time = 0;
while(time<1){
yield return null;
time +=Time.deltaTime;
transform.position = Vector3.Lerp(oldPos,newPos,time);
}
Or just use DoTween
but TBH, your coding is quite problematic. it will keep doing pathfinding and calling UpdatePosition when the player holding click.
Hey stackoverflow Community,
First of all:
I'm still very new about programming with C# and Unity.
My question:
I'm working on an idea for a Movement of a Cube.
It is planned that the cube will move forward by pressing a key (W-Key). But it shouldn't just move forward. It should jump forward to the next point. So always plus 1 of its axis into which it should go. Accordingly, it is only intended to go forward, right, down, left. He won't be able to jump over behind. You should also see that the cube jumps in the respective direction, so it should not teleport itself. :D
Does anyone have an idea how I can realize this movement?
I am very much looking forward to your ideas.
(Sorry if my English is not so good, my English not the best. ^^)
best regards
xKarToSx
So, in order to understand movement, it's best to first understand Vectors in Unity. Since you want to be moving the cube in the forward direction, I'm going to assume this is a 3D game, in which case you want to use a Vector3.
A Vector3 has three components: X, Y, and Z. Each component is tied to an axis. In simple terms, X is tied to left and right, Y is tied to up and down, and Z is tied to forward and back. So, Vector3 position = new Vector3(0, 1, 2); will be a vector that is 1 unit above and 2 units in front of the starting position.
Assuming you've attached this script to the cube you want to move, you can track its position with transform.position. So, if you want to move the cube one unit forward, your code would look something like this:
if(Input.GetKeyDown(KeyCode.W)) // This code will activate once the user presses W.
{
transform.position += new Vector3(0, 0, 1);
}
That will move the cube one unit forward in the Z direction. However, you don't want it to teleport, you want to see it move, correct? In that case, you want to check out Unity's Vector3.Lerp function. Basically, you would use it to smoothly transition an object between two defined positions. You'll need to implement a timer and a for loop in order to make this work correctly.
So, to summarize, for moving one unit forward in the Z direction, your code would look something like this:
if(Input.GetKeyDown(KeyCode.Z))
{
float startTime = Time.time; //Time.time is the current in-game time when this line is called. You'll want to save this to a variable
float speed = 1.0f; //The speed if something you'll want to define. The higher the speed, the faster the cube will move.
Vector3 startPosition = transform.position; //Save the starting position to a different variable so you can reference it later
Vector3 endPosition = startPosition + Vector3.forward; //Vector3.Forward is equivalent to saying (0, 0, 1);
float length = Vector3.Distance(startPosition, endPosition); //You'll need to know the total distance that the cube will move.
while(transform.position != endPosition) //This loop while keep running until the cube reaches its endpoint
{
float distCovered = (Time.time - startTime) * speed; //subtracting the current time from your start time and multiplying by speed will tell us how far the cube's moved
float fraction = distCovered / length; //This will tell us how far along the cube is in relation to the start and end points.
transform.position = Vector3.Lerp(startPosition, endPosition, fraction); //This line will smoothly transition between the start and end points
}
}
I hope this helps you. This is my first time answering a question so sorry if I got some things wrong/it's not the most optimized. Good Luck!
I am working on the friction for a square object for after it is done being pushed by the player. The block's movements are controlled purely through script using transform.Translate(velocity) each frame and only a boxcollider2D. I am speculating that because i am controlling the block's movements each frame manually is the reason i can't seem to get the boxcolliders2D's physics material 2D friction to work.
Basically, I am trying to recreate the built in physics similar to rigidbodies by using transform.translate, or use the built in physics if possible. So far, i've landed on something like this
newVelocity.x -= ( 1 / (newVelocity.x * newVelocity.x));
In an attempt to reduce the velocity.x each frame until it hits 0 and shorts out. Obviously, this falls apart when the velocity.x becomes a decimal number. I'm stumped on what i should be doing to achieve a friction similar to rigid bodies where it slows down a little at first and then much faster towards the end as it stops. Any and all help appreciated, thanks in advance!
You could compare the signs of the velocity before and after calculating the change in velocity, and set the velocity to zero when the sign changes, like so:
newVelocity.x -= ( 1 / (newVelocity.x * newVelocity.x));
if(Mathf.Sign(previousVelocity.x) != Mathf.Sign(newVelocity.x)) {
newVelocity.x = 0f;
}
One thing, though, is that normal friction is not calculated using the above equation, it's calculated as follows:
newVelocity.x -= frictionCoefficient*Time.fixedDeltaTime;
And with this, you wouldn't need to compare the velocity to the previous velocity as you can just say if the velocity is low, it's zero. You could still compare the previous velocity sign stuff if you wanted to though, but this is probably more efficient
if(newVelocity.x < 0.01f) {
newVelocty.x = 0f;
}
In my game I have targets that spawn and move towards the player in a straight line. I use this code:
transform.LookAt(target.transform);
transform.position = Vector3.MoveTowards(transform.position, target.transform.position, speed);
Which makes the target home in on the player and move towards them.
I decided to try and make them move in a sin wave, so it looks less boring than going in a straight line, and I added a line of code, which now becomes:
transform.LookAt(target.transform);
transform.position = Vector3.MoveTowards(transform.position, target.transform.position, speed);
transform.position += transform.right * Mathf.Sin (Time.time * 3f) * 1f;
Now this works great, my enemies move left and right as they travel towards the player, except, they all follow the same path at the same time, resulting in a large group of targets moving left and right in a sort of choreographed way.
I believe it is because I use Time.time in the Mathf.Sin piece of code, which results in all the targets using the same value to generate a sine wave.
I've tried changing to Time.deltaTime and changing the Time.time variable to a Random.Range variable between 2 numbers, but they either don't work, stop my targets from moving, or make them look like they are vibrating. I've also tried using animations as well, but because of my MoveTowards code, this doesn't work either.
So, is there a way to make a GameObject move left and right in a sine wave and move forwards at the same time, but independently so they do not look choreographed? :-)
In order to offset the phase of the sine function (while keeping the frequency and amplitude) you have to add a constant to x in sin(x).
So you could try transform.position += transform.right * Mathf.Sin (Time.time * 3f + i) * 1f; where i is unique to the target moving towards the player.
See also: https://en.wikipedia.org/wiki/Sine_wave