I have problem with rotation; rotatetoToTarget; - c#

I want to 2dgameobject turns toward to target 2dgameobject
I have a Method - rotate to target.
Code:
Quaternion rawRoation = Quaternion.Slerp(transform.rotation,
Quaternion.LookRotation(Player.GetComponent<Transform>().position - transform.position),
10*Time.deltaTime);
transform.rotation = new Quaternion (0, 0, rawRoation.z, rawRoation.w);
Problem - WHen target's position.x < this.object.transform.position.x(1) - rotation breaks..
else(2) - all right.
1 -
enter image description here
2 - enter image description here

The problem is this line of code: transform.rotation = new Quaternion (0, 0, rawRoation.z, rawRoation.w);
I don't know what you want to achieve, but if you want to nullify the x and y rotation use:
transform.rotation = Quaternion.Euler(0.0f, 0.0f, rawRoation.eulerAngles.z);
Example of what your code does:
// Start is called before the first frame update
void Start()
{
Quaternion q1 = new Quaternion(0.3f, 0.7f, 0.4f, 0.5f);
Quaternion q2 = new Quaternion(0.0f, 0.0f, 0.4f, 0.5f);
Debug.Log(q1.eulerAngles);
Debug.Log(q2.eulerAngles);
}
And here the output:
q1: (344.8, 100.3, 59.1)
q2: (0.0, 0.0, 77.3)
Please have a look at the math of quaternions
Edit - 2D LookAt:
Your code does not work like you want because you are in 2D. If you want to look at a target with an x-position less than your object's in 3D you have to do a rotation around the y-axis by 180deg which in 2D is not a valid rotation. You cannot. You do this y-rotation to prevent that the object is upside down. but in 2D you cannot do such a rotation.
You have to choices:
If your camera looks from the side and your object can be upside down use your modified code and also set the x-scale to -1 if the targets x-position is less.
If your camera looks from top down use the following
code:
Code for upside down view:
// Update is called once per frame
void Update() {
Vector3 targetDir = target.position - transform.position;
float angle = Mathf.Atan2(targetDir.y, targetDir.x) * Mathf.Rad2Deg;
Quaternion q = Quaternion.AngleAxis(angle, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, q, Time.deltaTime * 10);
}

Related

Rotating raycast2D back and forth with object

I made a circle and attach a lazer box on top of it.
The lazer will fire a raycast to its upper y axis (straight up). I also add a line renderer to view it.
I want the raycast to rotate 90 degrees back and forth. Sort of like its scanning everything on top. My problem is that its not working properly. It does rotate back and forth but If I move the x position of the lazer object, the raycast will rotate in a weird angle.
Script for lazer object
public LineRenderer lineRenderer;
public LayerMask layerMask;
public float laserSpeed;
Vector3 pointA;
Vector3 pointB;
Vector3 castPosition;
RaycastHit2D rayCast;
float time;
void Start()
{
pointA = transform.eulerAngles + new Vector3(0f, 0f, 90f);
pointB = transform.eulerAngles + new Vector3(0f, 0f, -90f);
}
void Update()
{
time = Mathf.PingPong(Time.time * laserSpeed, 1);
transform.eulerAngles = Vector3.Lerp(pointA, pointB, time);
castPosition = new Vector3(transform.position.x, transform.position.y, transform.position.z);
rayCast = Physics2D.Raycast(castPosition, transform.TransformDirection(Vector2.up), 10f, layerMask);
lineRenderer.SetPosition(0, castPosition);
lineRenderer.SetPosition(1, transform.TransformDirection(Vector2.up) * 10f);
}
Using eulerAngles for continuous animations is quite "dangerous". Unity stores the rotations as Quaternion and there are multiple ways of how to represent these in euler space!
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.
so you should rather go for Quaternion and do e.g.
And then you are using transform.TransformDirection(Vector2.up) which is a direction and pass it to your line renderer as a position.
What you want there is rather the position combined from
transform.position + transform.up
So together it should probably rather be
public LineRenderer lineRenderer;
public LayerMask layerMask;
public float laserSpeed;
private Quaternion originalRotation;
private Quaternion minRotation;
private Quaternion maxRotation;
void Start()
{
originalRotation = transform.rotation;
minRotation = originalRotation * Quaternion.Euler(0, 0, -90);
maxRotation = originalRotation * Quaternion.Euler(0, 0, 90);
}
void Update()
{
// Note that Vector3 is a "struct" -> there is no need to manually use "new Vector3(transform.position.x, ...)"
var startPosition = transform.position;
lineRenderer.SetPosition(0, startPosition);
var factor = Mathf.PingPong(Time.time * laserSpeed, 1);
// instead of the eulers rather use Quaternion
transform.rotation = Quaternion.Lerp(minRotation, maxRotation, factor);
// "transform.up" basically equals using "transform.TransformDirection(Vector3.up)"
var rayCast = Physics2D.Raycast(startPosition, transform.up, 10f, layerMask);
if(rayCast.collider)
{
// when you hit something actually use this hit position as the end point for the line
lineRenderer.SetPosition(1, rayCast.point);
}
else
{
// otherwise from the start position go 10 units in the up direction of your rotated object
lineRenderer.SetPosition(1, startPosition + transform.up * 10f);
}
}

Rotation off by 90 degrees unity

I am trying to rotate my 2d object around my mouse. This is the code I have:
void Update()
{
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 direction = mousePosition - transform.position;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0, 0, angle);
// Debug.Log(angle);
}
My object, which is an arrow, is by default pointing down, so before I start the script I set its z rotation to 90, so it faces right. If I delete the transform.rotation line, the angle shown will be right, when my cursor is above it says 90, in the left it says 180 etc. So my question is: Why do I need to add 90 degrees to angle to make this actually work? Why doesn't this version work?
Mathf.Atan2(Also see Wikipedia - Atan2)
Return value is the angle between the x-axis [=Vector3.right] and a 2D vector starting at zero and terminating at (x,y).
It would work as expected if your arrow by default would point to the right but yours is
by default pointing down
you could simply add the offset rotation on top like
transform.rotation = Quaternion.Euler(0, 0, angle + 90);
Alternatively if you need this for multiple objects with different offsets either use a configurable field like
[SerializeField] private float angleOffset;
...
transform.rotation = Quaternion.Euler(0, 0, angle + angleOffset);
Or you could rotate it manually to face correctly to the right before starting the app and store that default offset rotation like
private Quaternion defaultRotation;
private void Awake ()
{
defaultRotation = transform.rotation;
}
and then do
transform.rotation = defaultRotation * Quaternion.Euler(0, 0, angle);

Quaternion.Slerp on X and Z axis without Y axis

I am trying to rotate the Player about X, Y, and Z axis. The Y axis should not move from last angle. Example, if I rotate 45 degree's to the left, the player should not rotate back to 0. The players X and Z axis rotate a maximum of 30 degrees, then when Input is no longer in use, settle to 0.
Through trial and error, I have finally gotten my Y angle to not Slerp back to 0. However, X and Z, still consider Y to be 0 degree's. The player is rotated (assume 45 degree's to the left), but movement along X and Z is as if Y is 0 degree's.
I've been reading articles and threads, and watching video's across multiple domains, including but not limited StackOverflow, Unity forums, Unity API, and YouTube video's.
Video of Current Game - notice the engine exhaust - X and Z never change to the new normal of the Camera view / Player Y direction.
void Update()
{
if(!controller.isGrounded)
{
//Three degree's
moveDirection = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Thrust"), Input.GetAxis("Vertical"));
moveDirection *= speed;
//rotate around Y-Axis
transform.Rotate(0, Input.GetAxis("Yaw") * rotationSpeed, 0);
float currentY = transform.eulerAngles.y; //save Y for later
//rotation around X and Z
float tiltAroundX = Input.GetAxis("Vertical") * tiltAngle;
float tiltAroundZ = -1 * (Input.GetAxis("Horizontal") * tiltAngle);
Quaternion targetRotation = Quaternion.Euler(tiltAroundX, currentY, tiltAroundZ);
Vector3 finalRotation = Quaternion.Slerp(transform.rotation, targetRotation, smooth).eulerAngles;
finalRotation.y = currentY; //reintroduce Y
transform.rotation = Quaternion.Euler(finalRotation);
controller.Move(moveDirection * Time.deltaTime);
}
After further research that lead me along different avenues, I discovered that there were two issues. Both issue's revolved around the fact that the Z-axis was never being normalized to the new Y-axis degree after rotation. #Ruzihm, solved the issue of Rotation. I solved the then visible issue of movement. Which became readily visible once rotation was working properly.
In essence, the Z-axis (transform.forward) must be recalculated after any change in the Y-axis rotation (Vector3.up). Once you have the new normal (transform.forward), the movement vector needed to flattened to the plane to keep the player from diving into the surface of the world. Thank you #Ruzihm for all your assistance.
Here is the new code:
//Three degree's
moveDirection = new Vector3(Input.GetAxis("Horizontal"),
Input.GetAxis("Thrust"),
Input.GetAxis("Vertical"));
//Normalize the movement direction and flatten the Plane
moveDirection = transform.TransformDirection(moveDirection);
moveDirection = Vector3.ProjectOnPlane(moveDirection, Vector3.up);
moveDirection *= speed;
// collect inputs
float yaw = Input.GetAxis("Yaw") * rotationSpeed;
float pitch = Input.GetAxis("Vertical") * tiltAngle;
float roll = -1 * (Input.GetAxis("Horizontal") * tiltAngle);
// Get current forward direction projected to plane normal to up (horizontal plane)
Vector3 forwardCurrent = transform.forward
- Vector3.Dot(transform.forward, Vector3.up) * Vector3.up;
// Debug to view forwardCurrent
Debug.DrawRay(transform.position, forwardCurrent * 2, Color.white);
// create rotation based on forward
Quaternion targetRotation = Quaternion.LookRotation(forwardCurrent);
// rotate based on yaw, then pitch, then roll.
// This order prevents changes to the projected forward direction
targetRotation = targetRotation * Quaternion.AngleAxis(yaw, Vector3.up);
// Debug to see forward after applying yaw
Debug.DrawRay(transform.position, targetRotation * Vector3.forward, Color.red);
targetRotation = targetRotation * Quaternion.AngleAxis(pitch, Vector3.right);
targetRotation = targetRotation * Quaternion.AngleAxis(roll, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, smooth);
controller.Move(moveDirection * Time.deltaTime);
There seem to be some incorrect assumptions about the order of rotations that apply when working with Euler angles. Roll is applied, then pitch, then finally yaw. This means that keeping the same yaw then setting the roll and pitch to zero (or even just changing roll) can completely change the flattened direction you're facing.
It may help to rotate by yaw, flatten the forward direction (aka project it to a completely horizontal plane) Then create a rotation based off that (using Quaternion.LookRotation) which you can then rotate by each axis manually.
if(!controller.isGrounded)
{
//Three degree's
moveDirection = new Vector3(Input.GetAxis("Horizontal"),
Input.GetAxis("Thrust"),
Input.GetAxis("Vertical"));
moveDirection *= speed;
// collect inputs
float yaw = Input.GetAxis("Yaw") * rotationSpeed;
float pitch = Input.GetAxis("Vertical") * tiltAngle;
float roll = -1 * (Input.GetAxis("Horizontal") * tiltAngle);
// Get current forward direction projected to plane normal to up (horizontal plane)
Vector3 forwardCurrent = transform.forward
- Vector3.Dot(transform.forward,Vector3.up) * Vector3.up;
// Debug to view forwardCurrent
Debug.DrawRay(transform.location, forwardCurrent, Color.white, 0f, false);
// create rotation based on forward
Quaternion targetRotation = Quaternion.LookRotation(forwardCurrent);
// rotate based on yaw, then pitch, then roll.
// This order prevents changes to the projected forward direction
targetRotation = targetRotation * Quaternion.AngleAxis(yaw, Vector3.up);
// Debug to see forward after applying yaw
Debug.DrawRay(transform.location, targetRotation * Vector3.forward, Color.red, 0f, false);
targetRotation = targetRotation * Quaternion.AngleAxis(pitch, Vector3.right);
targetRotation = targetRotation * Quaternion.AngleAxis(roll, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, smooth);
//debug new forward/up
Debug.DrawRay(transform.location, Transform.forward, Color.blue, 0f, false);
Debug.DrawRay(transform.location, Transform.up, Color.green, 0f, false);
controller.Move(moveDirection * Time.deltaTime);
}
This may be considered a partial answer because being able to determine a "flattened forward" direction and reorder the process of applying component rotations is useful to answering your question but may not be enough to get the full effect you want depending on the details.
As a sidenote, you may want to consider using Quaternion.RotateTowards instead of Quaternion.Slerp if you want to ensure that it will actually reach the target rotation instead of infinitely approach it.

Unity - Quaternion.Slerp in a semisphere

i have a orbit camera who's moving on a semishpere. I have a plane with some other objects over it. In the middle of the scene there is an empty object that i'm using as a pivot for my camera,all is working as intended. I say sempisphere because i do not want to go "under" the plane, infact i have a control to avoid it.
Now i want to look at an object and smmothly rotate in that direction. To do so i'm using this code:
void Update () {
// Smoothly rotates towards target
Vector3 targetPoint = myobj.transform.position;
Quaternion targetRotation = Quaternion.LookRotation(targetPoint - transform.position, Vector3.right);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 2f);
}
img link 1
img link 2
unless you really want to specify the upward direction as "Vector3.right", just remove the second parameter:
void Update()
{
// Smoothly rotates towards target
Vector3 targetPoint = myobj.transform.position;
Quaternion targetRotation = Quaternion.LookRotation(targetPoint - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 2f);
}

Unity Camera Quaternion.RotateTowards without rolling/banking

I have a camera facing the ground and I want to pan up to look at a target object in the distance.
Currently, I achieve this with the following:
Vector3 dir = targetPoint - transform.position;
Quaternion lookRotation = Quaternion.LookRotation(dir);
Quaternion newRotation = Quaternion.RotateTowards(transform.rotation, lookRotation, rotationDamping * Time.deltaTime);
transform.rotation = newRotation;
The camera performs the rotation and ends up pointing at the target object correctly, but as the camera pans up it tilts to one side making my game world set at an angle to the viewer, which is pretty disorienting:
How can I constrain the camera angle some way so that the horizon is always flat to the camera?
Thanks!
Update
Adding the line suggested by #Isaac below produces the correct rotation in relation to the horizon, but it snaps abruptly to z=0 at the start which is still not what I'm looking for.
transform.localEulerAngles = new Vector3 (transform.localEulerAngles.x, transform.localEulerAngles.y, 0);
There is an excellent Q/A on gamedev.stackexchange on this subject. You should try the pitch/yaw system suggested there.
Another suggestion is to correct for the roll of your camera during the rotation.
public float rollCorrectionSpeed;
public void Update()
{
float roll = Vector3.Dot(transform.right, Vector3.up);
transform.Rotate(0, 0, -roll * rollCorrectionSpeed);
Vector3 dir = targetPoint.position - transform.position;
Quaternion lookRotation = Quaternion.LookRotation(dir);
Quaternion newRotation = Quaternion.RotateTowards(transform.rotation, lookRotation, rotationDamping * Time.deltaTime);
transform.rotation = newRotation;
}
Edit:
There is an easier solution: Just keep the z rotation of the Quaternion you are rotating from to 0.
public void Update()
{
Vector3 angles = transform.rotation.eulerAngles;
Quaternion from = Quaternion.Euler(angles.x, angles.y, 0);
Vector3 dir = targetPoint.position - transform.position;
Quaternion to = Quaternion.LookRotation(dir);
transform.rotation = Quaternion.RotateTowards(from, to, rotationDamping * Time.deltaTime);
}
Upon experimentation, I found 2 possible solutions depending on what you want.
If you are just trying to follow the target I would recommend using LookAt, which automatically aligns to the world up. In your code that would be (in Update) transform.LookAt(dir);.
If you need/want the pan effect set the localEulerAngles after updating the rotation. This is what I did which worked:
//this is your code
Vector3 dir = targetPoint - transform.position;
Quaternion lookRotation = Quaternion.LookRotation(dir);
Quaternion newRotation = Quaternion.RotateTowards(transform.rotation, lookRotation, rotationDamping * Time.deltaTime);
transform.rotation = newRotation;
//this is what I added
transform.localEulerAngles = new Vector3 (transform.localEulerAngles.x, transform.localEulerAngles.y, 0);
The addition simply takes the way the camera is facing after updating the rotation using the quaternion and sets the z rotation to zero.
Let me know if you have any questions :)
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
new information/edits:
I believe I have found a solution, but it is ugly and I would appreciate feedback as to whether it stutters, etc.
This code is essentially the same as before, but now it checks to see the z angle and edits it more manually using a variable I called zDamping which affects the speed at which the camera rotates around the z access only.
I added outside of update:
public float zDamping; //public only for testing, it's convenient for finding an optimal value
private bool rotationCheck = false;
And then inside update():
//This is your code (unchanged)
Vector3 targetPoint = target.transform.position;
Vector3 dir = targetPoint - transform.position;
Quaternion lookRotation = Quaternion.LookRotation (dir);
Quaternion newRotation = Quaternion.RotateTowards (transform.rotation, lookRotation, rotationDamping * Time.deltaTime);
//This is what is new (remove my addition from before edits or it won't work)
if (transform.localEulerAngles.z >= 180f && transform.localEulerAngles.z <= 359f && !rotationCheck) {
transform.localEulerAngles = new Vector3 (transform.localEulerAngles.x, transform.localEulerAngles.y, transform.localEulerAngles.z + (rotationDamping * zDamping));
transform.rotation = newRotation;
}
else if (transform.localEulerAngles.z <= -180f && transform.localEulerAngles.z >= 1f && !rotationCheck) {
transform.localEulerAngles = new Vector3 (transform.localEulerAngles.x, transform.localEulerAngles.y, transform.localEulerAngles.z - (rotationDamping * zDamping));
transform.rotation = newRotation;
}
else {
transform.rotation = newRotation;
transform.localEulerAngles = new Vector3 (transform.localEulerAngles.x, transform.localEulerAngles.y, 0);
rotationCheck = true;
}
As I said, this solution is pretty ugly but it might work. You'll have to see what zDamping values work for your speeds to look natural (I recommend starting with .01). There will also be a small "jump" once you get close to the value, but the closer you make 359f to 360 and 1f to 0 the smaller that jump will be. The danger with making it too small is if you overshoot, but it should work even if it overshoots, but it will take a small amount of time.
Test it out and let me know what you think, sorry I couldn't find something more elegant right now. I also experimented with adding a separate Quaternion to exclusively rotate the z axis, but it did not work; feel free to experiment with that and if you want I can give more details about what I did.
Good luck and again, sorry for the sloppy solution.
Added a code in your line, hopefully it'll solve the problem.
Vector3 dir = targetPoint - transform.position;
Quaternion lookRotation = Quaternion.LookRotation(dir);
Quaternion newRotation = Quaternion.RotateTowards(transform.rotation, lookRotation, rotationDamping * Time.deltaTime);
newRotation.eulerAngles = new Vector3(newRotation.eulerAngles.x,newRotation.eulerAngles.y,transform.rotation.eulerAngles.z);
transform.rotation = newRotation;

Categories