im trying to move a object in unity between 2 points, and at the moment it kinda works strange, i read the documentation and it says that the object begin point is (0,0,0) so my object goes under my other mesh that i have there, and the end point i can actually control, in my case it is 10, i want the object to move between 1.5 and 10(not 0 to 10)
i have this
void Update () {
transform.position = new Vector3(transform.position.x,Mathf.PingPong(Time.time,10.0f), transform.position.z);
}
when i try to put speed on the ball doing this:
void Update () {
transform.position = new Vector3(transform.position.x,Mathf.PingPong(Time.time,10.0f) * 10, transform.position.z);
}
the object does not colide and goes back at the end point it just stop looping and never came back how can i correct this 2 problems?
If your object has a collider, I suggest you move it via its Rigidbody rather than its Transform, to avoid potential collision issues. Try this:
public float MinY = 1.5f; // y position of start point
public float MaxY = 10f; // y position of end point
public float PingPongTime = 1f; // how much time to wait before reverse
public Rigidbody rb; // reference to the rigidbody
void Update()
{
//get a value between 0 and 1
float normalizedTime = Mathf.PingPong(Time.time, PingPongTime) / PingPongTime;
//then multiply it by the delta between start and end point, and add start point to the result
float yPosition = normalizedTime * (MaxY - MinY) + MinY;
//finally update position using rigidbody
rb.MovePosition(new Vector3(rb.position.x, yPosition, rb.position.z));
}
Here you have a better control on the distance to travel, and the speed.
Actually I didn't get exactly what are the problem you faced. But don't forget here and in your try, that you are directly modifying the position of the object, not adding forces or else.
Hope that helps you.
I think you simply misunderstood how the Mathf.PingPong method works :
first argument t is the value you want to "clamp" between 0 and the given length : this is were you want to put the Time.time as you did since this value will increase over time and therefore perpetually oscillate. If you want to increase/decrease the oscillation speed you have to multiply it.
second argument length is the max value of the "clamp" : if you want to increase/decrease the distance (in your case) you have either set it to 0 and multiply the whole Mathf.PingPong(...) by a value or directly give it the wanted value (both implementations will have a different effect.
Mathf.PingPong(Time.time * speed, 1.0f) * value : speed will affect the oscillation speed / value will affect the max value reached AND the speed / time to complete the oscillation (back and forth) will remain the same as value changes and decrease as speed increases
Mathf.PingPong(Time.time * speed, value) : speed will affect the oscillation speed / value will affect the max value reached BUT NOT the speed / time to complete the oscillation (back and forth) will increase as value increases and decrease as speed increases
About your other problems :
If you want to move your object between 1.5 and 10 you have to write something like this :
transform.position = new Vector3(transform.position.x, 1.5f + Mathf.PingPong(Time.time, 10.0f - 1.5f), transform.position.z);.
Also if you want to detect collision, avoid setting position manually as it will mess up with Physics and cause weird behaviors. Best way to move your object while keeping physic working is to do as #Heldap said using Rigidbody.MovePosition.
Related
How can I keep the diagonal speed to be the same as the horizontal and vertical speed without clamping any value or using ".normaized". I tryed to normalize the values but I lost the joystick values between 1 and 0. Here is my code :
void ListenInput()
{
Vector3 rightDirection = camera.right;
Vector3 frontDirection = camera.GetForwardFromAngleY();
move = new Vector2(
Input.GetAxis("Horizontal"),
Input.GetAxis("Vertical")
);
MoveCharacter(rightDirection * move.x);
MoveCharacter(frontDirection * move.y);
}
void MoveCharacter(Vector3 velocity)
{
transform.position += velocity * Time.deltaTime * runningSpeed;
}
Here, you should clamp the magnitude of the input Vector2.
For example with Vector2.ClampMagnitude() from the Unity API.
That will keep the input non-binary and prevent the diagonal from getting larger than purely horizontal/vertical inputs.
void ListenInput()
{
Vector3 rightDirection = camera.right;
Vector3 frontDirection = camera.GetForwardFromAngleY();
move = new Vector2(
Input.GetAxis("Horizontal"),
Input.GetAxis("Vertical")
);
move = Vector2.ClampMagnitude(move, 1f);
MoveCharacter(rightDirection * move.x);
MoveCharacter(frontDirection * move.y);
}
void MoveCharacter(Vector3 velocity)
{
transform.position += velocity * Time.deltaTime * runningSpeed;
}
If you normalize a vector you will make sure it's length is 1. This is a great way to avoid quirks like "diagonal movement is faster than normal movement".
However, the fact that the length is always 1 also means that there is no "move slowly" or "move at full speed" distinction from the joystick. When you say "I lost the joystick values between 1 and 0" is due to this fact.
One way developers get around this is by using a mathematical formula to scale the speed.
You could:
Use the largest value (horizontal or vertical) to control the speed
Use the smallest value
Use a combination of the two
Another way to do this is to store how long ago the movement started, then scale the speed based on that. This method has its own challenges, but is very familiar to players.
Examples
For instance, if I have:
horizontalInput = 1
verticalInput = 0.5
This means my normalized vector looks like this:
I could:
Use the largest value
Move at full speed (1) on the direction of my vector.
Use the smallest value
Move at half speed (0.5) on the direction of my vector.
Use a Use a combination of the two values
For this instance, lets use the following formula: (x+y)/2.
Move at 3/4 speed (0.75) on the direction of my vector.
NOTE: This formula will not "feel" as nice if you have x=0 and y=1, this is just an example. You most likely want to use Min, Max, Avg and if-clauses to control how the speed works.
You can use different formulas and different techniques to make the movement in your game feel like what you want, but take the time to analyze WHY it feels like that.
So I am currently creating movement for a character and all is well except diagonal movement is doubled because it is obviously combining vertical and horizontal movement. Now I did try normalizing the Vector3, but that results in a delayed stop for the character. According to other forums I read, I think it's because it should only normalize when it is greater/less than -1/1 but I don't know how to setup this constraint. Please help!
forwardInput = Input.GetAxis("Horizontal");
horizontalInput = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(horizontalInput, 0, forwardInput).normalized * speed * Time.deltaTime;
transform.Translate(movement);
Problem with always normalizing is that you move with the same speed no matter how far the input is pressed.
This is no problem on a PC with keyboard input which is either 0, -1 or 1 but will not work as expected if e.g. using a controller since it will also round up the value in case you normalize if the original input vector's magnitude is smaller then 1.
So yes you should normalize only in case the magnitude of the input vector exceeds 1 e.g. when pressing in two directions at the same time in order to maintain a certain maximum speed. In order to not type redundant code I would rather separate the vector and assignment in different variables and use Vector3.Normalize instead:
forwardInput = Input.GetAxis("Horizontal");
horizontalInput = Input.GetAxis("Vertical");
var inputVector = new Vector3(horizontalInput, 0, forwardInput);
// note that a magnitude is always positive, there is no -1
// if magnitude > 1 then implicitely also sqrMagnitude > 1 and the other way round
// sqrMagnitude is faster to access then magnitude and for this check
// here provides the same result
if(inputVector.sqrMagnitude > 1) inputVector.Normalize();
var movement = inputVector * speed * Time.deltaTime;
transform.Translate(movement);
I've just recently been introduced to Time.deltaTime and lerping and have been kinda stuck for the past few days.
So when I plug in Time.deltaTime for the last float and then multiply by another float variable called speed, what exactly is happening? What is Time.deltaTime doing to speed each frame that gives the end result?
Thank you!!
public class CameraMovement : MonoBehaviour
{
public GameObject followTarget;
public float moveSpeed;
void Update()
{
if (followTarget != null)
{
transform.position = Vector3.Lerp(transform.position, followTarget.transform.position, moveSpeed * Time.deltaTime);
}
}
}
Vector3.Lerp expects a factor between 0 and 1 and interpolates the two positions so
0 would mean transform.position
1 would mean followTarget.transform.position
any other value between those the interpolation between them meaning something like
transform.position + (followTarget.transform.position - transform.position) * factor;
Time.deltaTime = time past since last frame rendered so most likely a value close to 0.017 (for 60FPS). You can calculate by yourself what value this gives you in average for moveSpeed * 0.017.
It looks like what you are actually looking for is rather Vector3.MoveTowards
void Update()
{
if (followTarget != null)
{
transform.position = Vector3.MoveTowards(transform.position, followTarget.transform.position, moveSpeed * Time.deltaTime);
}
}
Using Time.deltaTime * scalar in Lerp is perfectly fine, but it's not the "expected" way to use it.
LERP means "Linear intERPolation". With this mathematic tool, you are able to interpolate a value between two others using a linear function ( y = ax + b ) and a coefficient t between 0 and 1.
Supposing you are trying to get a position (C) between two others (A and B) (like your problem), you have the folowing drawing :
1°) If the initial and destination position does not change, having the parameter t vary between 0 and 1, the movement curve will be perfectly straight : the speed will be the same over time.
transform.position = Vector2.Lerp(InitialPosition, DestinationPosition, t ); // t from 0 to 1
2°) However, having t (almost) not vary over time (with Time.deltaTime), but chaning the position of A, the movement will decelerate over time, because you take a fixed percentage of the remaining distance each time. Since the distance decrease over time, you travel less distance over time too
transform.position = Vector2.Lerp(transform.position, DestinationPosition, 0.017 ); // 0.017, approx of Time.deltaTime at 60FPS
I'm a bit confused about some documentation for Unity pertaining to Euler angles. I just want to know if I'm not understanding a difference, or the sample does not follow the best practice. The documentation here
states:
Only use this variable to read and set the angles to absolute values. Don't increment them, as it will fail when the angle exceeds 360 degrees. Use Transform.Rotate instead.
Meanwhile, the code sample appears to be using increments that could exceed 360 degrees:
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour {
public float yRotation = 5.0F;
void Update() {
yRotation += Input.GetAxis("Horizontal");
transform.eulerAngles = new Vector3(10, yRotation, 0);
}
void Example() {
print(transform.eulerAngles.x);
print(transform.eulerAngles.y);
print(transform.eulerAngles.z);
}
}
Wouldn't incrementing a variable and then using that variable to set the value absolutely still run the risk of exceeding 360 degrees if the variable is over 360 degrees?
When you rotate your Object using euler angles then when it reaches 360 and try to exceed further, it becomes (minus)-360 and gradually increase from -359 to -1.
After executing following code your values will not exceed from 360 and will remain positive.
float rotateAngle = rotateObject.transform.localEulerAngles.y;
// Convert negative angle to positive angle
rotateAngle = (rotateAngle > 180) ? rotateAngle - 360 : rotateAngle;
rotateObject.transform.localEulerAngles = new Vector3(rotateObject.transform.localEulerAngles.x, rotateAngle, rotateObject.transform.localEulerAngles.z);
There are differences. When doing:
transform.eulerAngles.y += 1F;
You are invoking the += operator in Vector3.
However, when you set eulerAngles this way:
float newY = transform.eulerAngles.y + 1F;
transform.eulerAngles = new Vector3(.., newY, ..);
You are invoking a setter in Transform, and inside this setter, it probably includes the action of updating Transform.rotation.
The difference is that Unity can implement the updating logic in Transform class instead of the Vector3 class, which makes much more sense there.
We can verify this further below in the documentation:
Do not set one of the eulerAngles axis separately (eg. eulerAngles.x =
10; ) since this will lead to drift and undesired rotations. When
setting them to a new value set them all at once as shown above. Unity
will convert the angles to and from the rotation stored in
Transform.rotation.
I want to rotate an object with the left arrow key and I want to bound the rotation to 30 deg. My code is:
if (Input.GetKey(KeyCode.LeftArrow) && transform.localEulerAngles.z <= 30)
transform.Rotate(0,0,1);
Why rotation stops to 31 deg?
Obviously my problem is more complex than this, I have different bounds and I need precision. The reason of this example is to simply say that rotations are not precise if managed in this way.
I think the reason is that Unity3D internally uses quaternions and acting on degrees is just an approximation. I'm right? In this last case how can I cope to this?
For example, how can I use quaternions to bound of 30 degs a rotation on an axis?
By the way if the problem is not this, do you have other solutions?
I don't know how unity manage the rotation, but here your problem seem more simple.
In your if you use the '<=' comparison, so when your object is at 30 degree, you enter a last time in the if and rotate 1 more degree, use a '<' to stop at the right moment
Get the current rotation in the Start() function then use it to find an offset that will be used to perform the if statement. This should do it:
public GameObject gameObjectToRotate;
Vector3 defaultAngle;
float minRot = 30f;
float maxRot = 30f;
// Use this for initialization
void Start()
{
defaultAngle = gameObjectToRotate.transform.eulerAngles;
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.LeftArrow))
{
float offset = defaultAngle.z - gameObjectToRotate.transform.eulerAngles.z;
//Check if we are within min, max rot
if (offset < minRot && offset > -maxRot)
{
gameObjectToRotate.transform.Rotate(0, 0, 1);
Debug.Log("Rotating!");
}
}
}
The precision is 30.016. This is much more better than what you are getting now.