Unity3D - How to move objects in a Sine wave independantly? - c#

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

Related

Camera Jitter on Rotation (not movement)

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!

Time.deltaTime and Axis Input

I have a simple question. Say I want to move my character by translating it with a Vector2 velocity varibale which is going to get it's values from Input.GetAxis. When I write code as this:
Vector2 input=new Vector2(Input.GexAxis("Horizontal"), Input.GetAxis("Vertical"));
velocity.x=input.x* Time.deltaTime * moveSpeed;
velocity.y+=gravityTime.deltaTime;
transform.Translate(velocityTime.deltaTime);
I get a weird error in which my character barely moves at all (it moves but very slowly and it stutters a lot). However, upon removing Time.deltaTime from velocity.x I get it to work normally. I know what Time.deltaTime is and what it is used for however I am not sure why I cannot use it with input.x but can use it with gravity?
btw moveSpeed and gravity are just float variables with publicly assigned values.
You can achieve movement with simpler (but a larger quantity of) code.
if (Input.GetKey(KeyCode.W))
{
gameObject.transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
}
Simply repeat this for each direction and it will work.

Accelerate object towards a certain direction

I am trying to make a game by myself and I encountered a difficulty.
I have this object and I need it to accelerate towards a vector 3 point.
I tried using the Vector3.MoveTowards command, but the object moves with a constant velocity and stops at the destination.
What I need to do is have the object accelerate from 0 velocity towards a vector 3 point and not stop at the point, but continue at the same direction after it went through the point.
Does anyone know how to do this?
Thank you!
Perform these steps in a method that is called in the Update or FixedUpdate method. FixedUpdate is recommended if you are using rigid bodies.
First, you need to find the direction from your position to the point, and define a velocity instance variable in your script if not using Rigid Bodies. If you are using a Rigidbody, use rigidbody.velocity instead. target is the Vector3 position that you want to accelerate towards.
// Use rigidbody.velocity instead of velocity if using a Rigidbody
private Vector3 velocity; // Only if you are NOT using a RigidBody
Vector3 direction = (target - transform.position).normalized;
Then you need to check if we have already passed the target or not. This check makes sure that the velocity remains the same
// If our velocity and the direction point in different directions
// we have already passed the target, return
if(Vector3.Dot(velocity, direction) < 0)
return;
Once we have done this we need to accelerate our Transform or Rigidbody.
// If you do NOT use rigidbodies
// Perform Euler integration
velocity += (accelMagnitude * direction) * Time.deltaTime;
transform.position += velocity * Time.deltaTime;
// If you DO use rigidbodies
// Simply add a force to the rigidbody
// We scale the acceleration by the mass to cancel it out
rigidbody.AddForce(rigidbody.mass * (accelMagnitude * direction));
I recommend that you use a Rigidbody since it makes much more sense when doing something like this.

Realistic turns for vehicle

I am trying to add realistic looking driving behavior to a simple vehicle. Currently I use the rigidbody MovePosition, and I only apply MoveRotation when I have detected forward/reverse axis from Input.
Instead I would like to apply direction/rotation even if the car is coasting, but do not want it turning when standing still, as if it were a tracked vehicle. I would also like the turn to appear to come from the front of the vehicle where the wheels (not actual objects in the game) would be located.
What I have so far is mostly borrowed from the tanks tutorial:
m_MovementInputValue = Input.GetAxis(m_MovementAxisName);
m_TurnInputValue = Input.GetAxis(m_TurnAxisName);
// Create a vector in the direction the tank is facing with a magnitude based on the input, speed and the time between frames.
Vector3 movement = transform.forward * m_MovementInputValue * m_Speed * Time.deltaTime;
// Apply this movement to the rigidbody's position.
m_Rigidbody.MovePosition(m_Rigidbody.position + movement);
if (m_MovementInputValue > 0)
{
float turn = m_TurnInputValue * m_TurnSpeed * Time.deltaTime;
// Make this into a rotation in the y axis.
Quaternion turnRotation = Quaternion.Euler(0f, turn, 0f);
// Apply this rotation to the rigidbody's rotation.
m_Rigidbody.MoveRotation(m_Rigidbody.rotation * turnRotation);
}
I think you can save yourself some time by looking at the Wheel Collider components of Unity API : http://docs.unity3d.com/Manual/class-WheelCollider.html , I saw a demo some times ago which used it to make a full racing game so it should do the trick to simulate realistic vehicles (Demo in question).
Other than that here is how you could do it :
For starters if you want your vehicle to turn only when it is moving forward or backward you can multiply your turn force by the velocity forward vector of your vehicle
float turn = m_TurnInputValue * m_TurnSpeed * Time.deltaTime * (m_Rigidbody.velocity.magnitude / MaxVehicleSpeed);
The idea of (m_Rigidbody.velocity.magnitude / MaxVehicleSpeed) is to have a value between 0 and 1 depending on the vehicle speed.
then for the turn to appear from the front of the vehicle you need to change your point of rotation, and I can't find a solution using MoveRotation. You can do that by using the AddForceAtPosition function of the Rigidbody but it'll become more complicated to compute it.
Here is the idea :
Vector3 force = transform.right * turn;
m_Rigidbody.AddForceAtPosition(force, MiddleOfWheelAxis);
With MiddleOfWheelAxis the point between your two front wheels.

Unity 3D Move object over specific time on GetButtonDown

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

Categories