rotate Object towards another in Unity3D - unity3d

I have already this function from this question. I changed the sign of the rotation:
void rotateBotConnector()
{
Vector3 diff = (player.transform.position - botConnector.transform.position).normalized;
float rot_z = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
botConnector.transform.localRotation = Quaternion.Euler(0f, 0f, -(rot_z - 90f));
}
But the problem is, that now my object follows the player on the XZ plane but when the rotation reaches a certain degree, left or right, the object stops to rotate towards my player.
For better understanding: http://imgur.com/vWaqc31

Why not just use:
var target : Transform;
transform.LookAt(Vector3(target.transform.position.x, target.transform.position.y, transform.position.z);
It seems a lot easier than using euler. This way you look at target's x & y but transform your z.
Also I'm no expert with euler but it seems like it is limited to a 90 degree turn and I think this may be the reason why:
Quaternion.Euler(0f, 0f, -(rot_z - 90f));

Unless you have absolute necessity to rotate via angles maybe you'd rather go with manipulating the forward vector of the object, it's easier to understand the logic this way:
void rotateBotConnector()
{
Vector3 targetForwad = botConnector.transform.position - player.transform.position;
targetForward.y= 0f;
targetForward.Normalize(); // <-- this is very expensive function try avoid using it whenever possible
botConnector.forward = Vector3.Lerp(botConnector.forward, targetForward, Time.deltaTime));
}
once again, if you are short on cpu cycles you may want to go for trying to calculate angles instead of vectors. But in most cases this is ok. (the code needs a spell checking)

Related

Unity3d, moving the player forward

Good evening. I am realizing (Unity3d) the moving forward (in the plane which is made by X- and Z-axis) using Rigidbody.velocity, the direction depends on MyPlayer.transform.localEulerAngles.y. Tell me, please:
Which of the realizations of the method for the button which moves (it will be called every frame if the button is pressed) is "cheaper":
a)
public void MoveForward()
{
PlayerRigidbody.velocity = new Vector3(speed * (float)(Math.Sin(((double)(transform.localEulerAngles.y / 180)) * Math.PI)), PlayerRigidbody.velocity.y, speed * (float)(Math.Cos(((double)(transform.localEulerAngles.y / 180)) * Math.PI)));
}
b)
public void MoveForward()
{
Quaternion rotation = Quaternion.AngleAxis((transform.localEulerAngles.y - 180), Vector3.up);
Vector3 temp = new Vector3(0, PlayerRigidbody.velocity.y, -speed);
PlayerRigidbody.velocity = rotation * temp;
}
Is there any dependency on Time.deltaTime?
You're setting the velocity, so if you mean speed when you use speed then there shouldn't be any need to include Time.deltaTime.
As far as performance goes, (1) you can implement it both ways and profile to find out which takes longer, and (2) the difference is almost certainly negligible and you should go with whatever is easier to read, because readability is key to maintainability.
Regarding point 2, IMO neither option is readable. Are you just trying to make the thing move in its local forward direction? Try using the TransformDirection method, like:
PlayerRigidbody.velocity = speed*transform.TransformDirection(Vector3.forward);
transform.TransformDirection says,
Description
Transforms direction from local space to world space.
so just pass that function the Vector3.forward and let Unity's built-in method handle the math.

Only rotate one axis on Unity gameobject

I have inherited some code that simply does this....
transform.rotation = m.rotation;
I know that my model will always be standing upright and therefore it only needs to rotate around on one axis. How can I only change the Y rotation.
I can't seem to get this to work because I get stuck right away when I try:
transform.rotation = Quaternion.Euler(m.rotation.x, m.rotation.y, m.rotation.z);
Which does not align it the same as:
transform.rotation = m.rotation;
I was hoping to then just change the m.rotation.y. Any reason this doesn't work?
The m value looks like this
public MarkerInfo(string name, bool isVisible, Vector3 position, Quaternion rotation, Vector3 scale){
this.name = name;
this.isVisible = isVisible;
this.position = position;
this.rotation = rotation;
this.scale = scale;
}
If m stands for a c# class and not a uniy one, changing its rotations will have no effect. You need to apply your rotation to a unity Transform, that normally is an instance of a component of a gameobject of the scene. That way you can see in the scene the changes you apply to the transform of that specific gameobject.
I think you need to know what unity gameobject does MarkerInfo manipulate or stands for, get its Transform with gameObject.transform and rotate that.
With transform.RotateAround or manipulating directly yout transform's Transform.eulerAngles you should be able to get your rotation.
Take into account that this line: transform.rotation = Quaternion.Euler(m.rotation.x, m.rotation.y, m.rotation.z); you are trying wont do what I think you think it does. The x,y,z of a quaternion are not the euler angles.
To apply that rotation to the transform you need to do transform.rotation = m.rotation, so the fact those line do not do same make perfect sense.
The best ways to do this are by using Quaternion.Euler, which converts a Vector3 to a Quaternion (Unity's confusing variable for storing rotation), or by using Quaternion.Set, which sets the values of a Quaternion similar to Vector3.Set. The first one has 2 declarations: 1 where you pass a vector3 variable, and another where you pass 3 ints/floats. The following 2 lines of code both do the same thing using both declarations.
transform.rotation += Quaternion.Euler(Vector3.up);
transform.rotation += Quaternion.Euler(0, 1, 0);
The second method works like this (I don't know what the fourth one does, but it's typically 1):
// transform.Set(x, y, z, 1);
transform.Set(0, 45, 0, 1);
transform.Set(transform.x + 1, transform.y, transform.z, 1);
Quaternions are confusing and annoying at first, but Unity comes with a lot of fancy rotation-focused functions that coultn't be possible without them. You can use Quaternion.Euler to convert a Vector3 to a Quaternion and then use those functions on them. They could come in handy later. You can find them here under "public/static methods".
I suppose you want player to have same forward vector as other object but keep the original up vector?
I would avoid Euler Angles as much as possible because they are "glitchy" and try like that:
var forward = m.forward;
forward.y = 0;
transform.rotation = Quaternion.LookRotation(forward, Vector3.up);
just resolve the case
m.forwad == Vector3.down || m.forward == Vector3.up
and it should be working

Unintended player movement from transform.InverseTransformDirection

this is my first time posting on here. I'm working on a game using the new Unity multiplayer networking solution.
In summary, the issue is that the player is not moving as intended.
I am trying to take player input as follows:
Vector3 worldSpaceDir = new Vector3(Input.GetAxisRaw("Vertical"), 0, Input.GetAxisRaw("Horizontal"));
then convert it to the object space coordinates of the player character:
_inputDirection = transform.InverseTransformDirection(worldSpaceDir);
The issue I'm having is with a rotation of 0 or 180 the player moves as expected with the WASD inputs, however, at 90 or 270 all the inputs are flipped(A = right, D = left, W = backward, S = forward).
I found a question that is exactly my question but no one responded with an answer. The question is quite old now so I wanted to ask it again for more visibility.
Here's a link to the original question.
Firstly, you are taking the worldSpaceDir wrong, it should be as follow
Vector3 worldSpaceDir = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
here we take horizontal input as X and vertical input as Z, because in Unity Forward is pointed as Z and not Y.
Secondly, we do not need to use InverseTransformDirection() we just need TransformDirection() something like following
Vector3 inputDirection = transform.TransformDirection(worldSpaceDir);
here we are telling unity to convert the worldSpaceDir that is relative to transform (local direction) into a world space direction, so we might actually give a proper name to worldSpaceDir.
The following would work for you.
private void Update() {
Move();
}
private void Move() {
Vector3 directionToMove = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
Vector3 inputDirection = transform.TransformDirection(directionToMove);
transform.position += inputDirection * Time.deltaTime;
}
I think you want to go the other way round actually!
Transform.InverseTransformDirection converts a vector from world space into local space.
What you get as input however is a local vector on the XZ plane. You want to apply this direction according to your player objects orientation, if e.g. pressing right (your input is 1,0,0) the object shall move towards its transform.right vector.
So you rather want to convert in the opposite direction into world space to move the object in the Unity world space.
You should rather be using Transform.TransformDirection!
var worldMove = transform.TransformDirection(input);
Or alternatively you can also just multiply by the rotation like
var worldMove = transform.rotation * input;
Note that if you are also using a Rigidbody like the question you linked there is also Rigidbody.AddRelativeForce which basically works the same way and expects a local space vector which is then internally converted into a world space force.

How do I make something happen at a specified rotation which I incrementally go towards and may over shoot?

So I have a script for a day/night cycle attached to the directional light in unity. It slowly rotates the light which creates an effective day/night cycle. There's an event that I want to call once every sunset, or more specifically when the x rotation of the light is at 200 degrees. The problem is my script rotates a little bit each frame, according to Time.deltatime which is obviously not perfectly consistent. Because of this, I might be at a rotation just below 199 and then at the next frame, I might be at a rotation just above 200 degrees, overshooting it so that it's never actually 200 degrees. I tried to get around this by checking if the x rotation is above 200 AND the x rotation - my rotate amount in that frame is below 200, then calling the event. That was the idea but it didn't work for some reason. It never calls the event. Here's my script.
using UnityEngine;
using UnityEngine.Events;
public class DayNightCycle : MonoBehaviour
{
public TerrainGenerator terrainGenerator;
public float dayLength = 3;
float rotationSpeed;
public UnityEvent night;
public float timeNightStarts = 200;
// Start is called before the first frame update
void Start()
{
rotationSpeed = 360 / (dayLength * 60);
}
// Update is called once per frame
void Update()
{
if (terrainGenerator.mapLoaded)
{
Vector3 rotateAmount = Vector3.right * rotationSpeed * Time.deltaTime;
transform.Rotate(rotateAmount, Space.World);
float xRotation = transform.eulerAngles.x;
if (xRotation >= timeNightStarts && xRotation - rotateAmount.x < timeNightStarts)
{
night.Invoke();
}
}
}
}
The problem you are facing is expected since Unity uses Quaternions under the hood and quaternion to euler conversions are not stable.
Quote from Unity docs:
When you read the .eulerAngles property, Unity converts the
Quaternion's internal representation of the rotation to Euler angles.
Because, there is more than one way to represent any given rotation
using Euler angles, the values you read back out may be quite
different from the values you assigned. This can cause confusion if
you are trying to gradually increment the values to produce animation.
To avoid these kinds of problems, the recommended way to work with
rotations is to avoid relying on consistent results when reading
.eulerAngles particularly when attempting to gradually increment a
rotation to produce animation. For better ways to achieve this, see
the Quaternion * operator.
If you want to avoid Quaternions, you can represent the eulerX angle as a float variable in your code. Increment its value, always set the transform.euler.x from it, but never read it back from the transform. If no other script or physics affects your transform (which should be the case for sun) you will be fine.

How to move an object by a certain angle over a period of time in a circle [duplicate]

I a new here and i try to start working with Unity Engine.
Could somebody explain me, how works Quaternion.Slerp? Because I want to rotate some object in different angles 90, 180 and 270. My code you can see below. Unfortunately when I add 180 degrees, object make crazy things and than put rotation to (0, 180, 180) for this game object. I would like to get (180,0,0)
public float speed = 0.1F;
private float rotation_x;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
rotation_x = transform.rotation.eulerAngles.x;
rotation_x += 180;
}
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(rotation_x, transform.eulerAngles.y, transform.eulerAngles.z), Time.time * speed);
}
Most examples out there including Unity examples from their official website are using Lerp in the wrong way. They didn't even bother to describe how it works in the API documentation. They just starch it in the Update() function and call it a day.
Mathf.Lerp, Vector3.Lerp, and Quaternion.Slerp work by changing from one position/rotation to another with the t value(last parameter) being passed in.That t value is also know as time.
The min of the t value is 0f and the max is 1f.
I will explain this with Mathf.Lerp to make it easier to understand. The Lerp functions are all the-same for both Mathf.Lerp, Vector and Quaternion.
Remember that Lerp takes two values and returns values between them. If we have a value of 1 and 10 and we do Lerp on them:
float x = Mathf.Lerp(1f, 10f, 0f); will return 1.
float x = Mathf.Lerp(1f, 10f, 0.5f); will return 5.5
float x = Mathf.Lerp(1f, 10f, 1f); will return 10
As you can see, the t(0) returns the min of the number passed in, t(1) returns the max value passed in and t(0.5) will return mid point between the min and the max value. You are doing it wrong when you pass any t value that is < 0 or > 1. That code in you Update() function is doing just that. Time.time will increase every second and will be > 1 in a second, so you have problems with that.
It recommended to use Lerp in another function/Coroutine instead of the Updated function.
Note:
Using Lerp has a bad side of it when it comes to rotation. Lerp does not know how to rotate Object with the shortest path. So bear that in mind. For example, you have an Object with 0,0,90 position. Lets say you want to move the rotation from that to 0,0,120 Lerp can sometimes rotate left instead of right to reach that new position which means it take longer to reach that distance.
Let's say we want to make the rotation (0,0,90) from whatever the current rotation is. The code below will change the rotation to 0,0,90 in 3 seconds.
ROTATION OVER TIME:
void Start()
{
Quaternion rotation2 = Quaternion.Euler(new Vector3(0, 0, 90));
StartCoroutine(rotateObject(objectToRotate, rotation2, 3f));
}
bool rotating = false;
public GameObject objectToRotate;
IEnumerator rotateObject(GameObject gameObjectToMove, Quaternion newRot, float duration)
{
if (rotating)
{
yield break;
}
rotating = true;
Quaternion currentRot = gameObjectToMove.transform.rotation;
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
gameObjectToMove.transform.rotation = Quaternion.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
rotating = false;
}
INCREMENTAL ANGULAR ROTATION OVER TIME:
And to just rotate the Object to 90 in z axis, the code below is a great example of that. Please understand there is a difference between moving Object to new rotational point and just rotating it.
void Start()
{
StartCoroutine(rotateObject(objectToRotate, new Vector3(0, 0, 90), 3f));
}
bool rotating = false;
public GameObject objectToRotate;
IEnumerator rotateObject(GameObject gameObjectToMove, Vector3 eulerAngles, float duration)
{
if (rotating)
{
yield break;
}
rotating = true;
Vector3 newRot = gameObjectToMove.transform.eulerAngles + eulerAngles;
Vector3 currentRot = gameObjectToMove.transform.eulerAngles;
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
gameObjectToMove.transform.eulerAngles = Vector3.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
rotating = false;
}
All my examples are based on frame-rate of the device. You can use real-time by replacing Time.deltaTime with Time.delta but more calculation is required.
Before anything, you can't add 180 on euler angles like that, and that's mainly what is causing your problem. You'd better use quaternion directly instead, or work on the transform itself.
You can think of a quaternion as an orientation in space. In contrary to what have been said, I do recommend learning how to use them if you can. However, I don't recommend using euler angles at all... as they're suject to different writing conventions, and will fail sometimes. You can look at 'gimbal lock' if you want details about that.
Simply a slerp or lerp (standing for spherical linear interpolation, or linear interpolation respectively) is a way to interpolate (go from one orientation to another, by increasing t from 0 to 1, in a coroutine or anywhere else) between orientation A and B. The difference between the two is that the slerp is giving you the shortest path from A to B.
In the end, when t = 1, lerp(A,B,t) and slerp(A,B,t) will give you B.
In your case, if you want to instantly rotate an object in space to a specific orientation, I suggest you use Quaternion.AngleAxis which is the most forward way to describe mathematically a quaternion.
If you want to add a rotation, say 90° to you actual orientation (without animation between the two), you can do something like this :
transform.rotation *= Quaternion.AngleAxis(axis_of_rotation, angle)
or use transform.rotate (depending on the parameters, it can be a right multiply, or left : local, or world transform).
Programmers' answer is detailling how to animate your transform. But I do suggest you to investigate quaternion themselves, as it will give you global understanding of space transforms.