Quaternion.Euler rotates wrong axis - c#

I have this problem where i have this: enter image description here
And then, i tried to rotate the y axis and that the "PartToRotate" follows the "enemy". enter image description here
But then, it rotates the axis z for -90 and i dont know why. enter image description here
Therefore i appreciate if you can help me with this. I leave all the code here:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Turret : MonoBehaviour
{
private Transform target;
public float range = 15f;
public Transform partToRotate;
public string enemyTag = "Enemy";
// Start is called before the first frame update
void Start()
{
InvokeRepeating("UpdateTarget", 0f, 0.5f);
}
void UpdateTarget()
{
GameObject[] enemies = GameObject.FindGameObjectsWithTag(enemyTag);
float shortestDistance = Mathf.Infinity;
GameObject nearestEnemy = null;
foreach ( GameObject enemy in enemies)
{
float distanceToEnemy = Vector2.Distance(transform.position, enemy.transform.position);
if(distanceToEnemy < shortestDistance)
{
shortestDistance = distanceToEnemy;
nearestEnemy = enemy;
}
}
if(nearestEnemy != null && shortestDistance <= range)
{
target = nearestEnemy.transform;
}
else
{
target = null;
}
}
// Update is called once per frame
void Update()
{
if (target == null)
{
return;
}
Vector3 dir = target.position - transform.position;
Quaternion lookRotation = Quaternion.LookRotation(dir);
Vector3 rotation = lookRotation.eulerAngles;
partToRotate.rotation = Quaternion.Euler(0f, rotation.y, 0f);
}
void OnDrawGizmosSelected ()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, range);
}
}
Here are the photos.enter image description here. thats how looks before, and the idea with the new code Vector3 direction = target.position - transform.position; float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg; partToRotate.rotation = Quaternion.Euler(0, 0, angle);
is that the "partToRotate" follows the enemy (target), and he do that, but it still get (plus the z, that is the axis i want) the x axis.enter image description here

Without seeing the values for target.position and transform.position can't ensure it but you may be experiencing a gimbal lock.
To avoid gimbal locking you must avoid euler angles and use only quaternions. In your code you create a quaternion, then you get the euler angles and then you create a new quaternion using only the Y axis of those euler angles. I assume that's to restrict the rotation to be on the XZ plane, you can do the same without using euler angles like this:
Vector3 dir = target.position - transform.position;
Quaternion lookRotation = Quaternion.LookRotation(new Vector3(dir.x, 0, dir.z));
partToRotate.rotation = lookRotation;

Related

Cylinder Snapping into place during Quaternion.RotateTowards

What I am trying to do is have a pillar in the center of a scene, and what i think is happening is the quaternion.RotateTowards is receiving a different starting/initial quaternion than the action which is causing it to snap/teleport into a different location which then starts to move to the next. I thought it may be because i'm misunderstanding how quaternions are handled in unity, so i've tried messing with normalizing it, but i can't seem to get any change on the teleport.
The goal is to attach this scrip to a simple 3D cylinder and have it wobble basically, where there will be a player on top of it trying to stay on it. However I can't seem to figure out why it is teleporting and was hoping for a second set of eyes.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlatformWobble : MonoBehaviour
{
public float timeDelay = 0;
public float rRange = 0.5f;
public float maxRotation = 20.0f;
public float rotRate = 0.05f;
private bool wobble = false;
private Vector3 randomRotation;
private Quaternion rotation;
private Quaternion destination;
private float x, y, z;
private bool inPlace;
private void Start()
{
randomRotation = new Vector3();
inPlace = true;
}
// Update is called once per frame
void FixedUpdate()
{
if (inPlace)
{
destination = RotateRandomly();
inPlace = false;
}
transform.localRotation = Quaternion.RotateTowards(transform.localRotation, destination, rotRate * Time.deltaTime);
if (transform.localRotation == destination)
{
inPlace = true;
}
}
//This will grab the rotation of the object and move it randomly, but won't exceed maxRotation;
Quaternion RotateRandomly()
{
randomRotation = new Vector3(Random.Range(-rRange, rRange), Random.Range(-rRange, rRange), Random.Range(-rRange, rRange));
rotation = transform.localRotation;
x = rotation.x + randomRotation.x;
if(x >= maxRotation && x <= 360 - maxRotation) { x = rotation.x; }
y = rotation.y + randomRotation.y;
if (y >= maxRotation && y <= 360 - maxRotation) { y = rotation.y; }
z = rotation.z + randomRotation.z;
if (z >= maxRotation && z <= 360-maxRotation) { z = rotation.z; }
return new Quaternion(x, y, z, transform.localRotation.w);
}
}
Your quaternion calculations are incorrect: quaternions do not hold angles, instead, they hold the representation of a rotation about an axis. I emphasized "representation" because it is a little complicated...
The x, y, z components of a quaternion hold the rotation axis unit vector scaled by the sine of the half angle of rotation. The w component holds the cosine of the half angle. That is...
// NOTE: rotationAngle is in radians rather than degrees
Quaternion MakeQuaternion (Vector3 rotationAxis, float rotationAngle)
{
float c = Mathf.Cos (rotationAngle / 2);
float s = Mathf.Sin (rotationAngle / 2);
Vector3 v = rotationAxis.normalized * s;
return new Quaternion (v.x, v.y, v.z, c);
}
Now, the tricky part for your problem is coming up with the rotation axis and angle for the desired effect.
One solution (if you wish to stick to Euler angles) is to compute quaternions for each Euler rotation and then combine them:
Quaternion EulerToQuat (float XAngle, float YAngle, float ZAngle)
{
Quaternion X = MakeQuaternion (Vector3.right, XAngle);
Quaternion Y = MakeQuaternion (Vector3.up, YAngle);
Quaternion Z = MakeQuaternion (Vector3.forward, ZAngle);
// combine the rotations such that the object is first rotated about the Z axis,
// then about the Y axis, then the X (ie, reverse order of multiplication).
// Reminder: quaternion multiplicate is not commutative: order matters, so if this
// is not the order you want, just siwtch things around
rotate X * Y * Z;
}
FixedUpdate doesn't get called every render frame, only on physics frames. You need a way to tell Unity to have each frame showing the rotation change, rather than only updating the rendering when the physics frame has just run.
That is what Rigidbody.MoveRotation is for. Cache a reference to the Rigidbody, calculate the new global rotation it should have, then call MoveRotation:
private Rigidbody rb;
private void Start()
{
randomRotation = new Vector3();
inPlace = true;
rb = GetComponent<Rigidbody>();
}
// ...
// Update is called once per frame
void FixedUpdate()
{
if (inPlace)
{
destination = RotateRandomly();
inPlace = false;
}
Quaternion newLocalRot = Quaternion.RotateTowards(transform.localRotation,
destination, rotRate * Time.deltaTime);
Quaternion newGlobalRot = transform.parent.rotation * newLocalRot;
rb.MoveRotation(newGlobalRot);
if (transform.localRotation == destination)
{
inPlace = true;
}
}

Rotate rigidbody with moverotation in the direction of the camera

I want to use moverotation to rotate the object in the direction of the Main Camera, like a common third person shooter, but I don't know how to set the quaternion values or otherwise
`using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class movimento : MonoBehaviour
{
[SerializeField] float walk = 1;
[SerializeField] float run = 2;
Vector3 movement = Vector3.zero;
private Rigidbody rig;
// Start is called before the first frame update
void Start()
{
rig = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftShift)) walk = (walk + run);
if (Input.GetKeyUp(KeyCode.LeftShift)) walk = (walk - run);
float Horizontal = Input.GetAxis("Horizontal");.
float Vertical = Input.GetAxis("Vertical");
movement = Camera.main.transform.forward * Vertical + Camera.main.transform.right * Horizontal;
float origMagnitude = movement.magnitude;
movement.y = 0f;
movement = movement.normalized * origMagnitude;
}
private void FixedUpdate ()
{
rig.MovePosition(rig.position + movement * walk * Time.fixedDeltaTime);
Quaternion rotation = Quaternion.Euler(???);
rig.MoveRotation(rig.rotation * rotation);
}
}`
i use a coroutine to do smooth rotation. I use Quaternion.LookRotation for the job.
so you indicate the position of object to look at and the duration of animation. Here you want to rotate face to the main camera
StartCoroutine(SmoothRotation(Camera.main.transform, 3f));
:
:
IEnumerator SmoothRotation(Transform target, float duration)
{
float currentDelta = 0;
var startrotation = transform.rotation;//use your rigisbody if you want here i use the gameobject
var LookPos = target.position - transform.position;
var finalrot = Quaternion.LookRotation(LookPos);
while (currentDelta <= 1f)
{
currentDelta += Time.deltaTime / duration;
transform.rotation = Quaternion.Lerp(startrotation, finalrot, currentDelta);//
yield return null;
}
transform.rotation = finalrot;
}
if you want to see (in scene when running) where your camera points just add this line of code in update():
Debug.DrawRay(Camera.main.transform.position, Camera.main.transform.TransformDirection(Vector3.forward) * 10f, Color.black);
if you want to point in same direction tha nthe Camera just change the line of finalrot in SmoothRotation Method:
var finalrot = Camera.main.transform.rotation;
you dont need to calculate the LookPos
for your problem of crazy rotation, i suggest you to reset rotation x and z
direction = hit.transform.position - transform.position;
Quaternion rotation = Quaternion.LookRotation(direction);
rotation.x = 0f;
rotation.z = 0f;
a tips to detect object what you want with the raycast inside spere : Physics.OverlapSphere: you could select what you want to cast when using the optional parameter layermask
private void DetectEnemy(Vector3 center, float radius)
{
var hitColliders = Physics.OverlapSphere(center, radius );
for (var i = 0; i < hitColliders.Length; i++)
{
print(hitColliders[i].name + "," + hitColliders[i].transform.position);
// collect information on the hits here
}
}
I created a raycast from the camera, and I would like to rotate the rigidbody to where the raycast is pointing but if i launch unity, it rotated wildly. What is the error?
Vector3 direction;
Vector3 rayDir = new Vector3(Screen.width/2,Screen.height/2);
void Update()
Ray ray = Camera.main.ScreenPointToRay(rayDir);
Debug.DrawRay(ray.origin, ray.direction * 10, Color.yellow);
RaycastHit hit = new RaycastHit ();
direction = hit.point - transform.position;
private void FixedUpdate ()
Quaternion rotation = Quaternion.LookRotation(direction);
rig.MoveRotation(rig.rotation * rotation);
Add a character controller component, this is what is for. See:
https://docs.unity3d.com/Manual/class-CharacterController.html

Adding a clamp to the rotation of a 2D object in Unity2D

I have been working for a long time trying to get a clamp implemented on my 2D object in Unity2D. I have struggled with this issue for weeks now. I have a rocketship that moves upwards on it's own, and I'm trying to make it where you can't go further than 40 degrees rotating. Some details to note is that the ship rotates(x-axis of course) and moves toward the mouse(x-axis, doesn't go further up more then the current velocity) as a way to avoid obstacles which are coming soon. Here's my code for the ship:
public class MiniGameRocketShipController : MonoBehaviour
{
public Vector2 velocity = new Vector2(0f, 1500f);
private Vector2 directionX;
private Vector2 directionY;
public Rigidbody2D rb;
public new Camera camera;
public float moveSpeed = 100f;
public float rotation;
private void Start()
{
rotation = rb.rotation;
}
private void Update()
{
rb.AddForce(velocity * Time.deltaTime);
rb.velocity = new Vector2(directionX.x * moveSpeed, directionY.y * moveSpeed);
FaceMouse();
}
void FaceMouse()
{
if (rotation > 180)
{
rotation = 360;
}
rotation = Mathf.Clamp(rotation, -40f, 40f);
rb.rotation = rotation;
Vector3 mousePosition = Input.mousePosition;
mousePosition = camera.ScreenToWorldPoint(mousePosition);
directionX = (mousePosition - transform.position).normalized;
directionY = transform.position.normalized;
transform.up = directionX;
}
}
You can use Vector2.SignedAngle and Mathf.Clamp to do this. Explanation in comments:
// world direction to clamp to as the origin
Vector2 clampOriginDirection = Vector2.up;
// what local direction should we compare to the clamp origin
Vector2 clampLocalDirection = Vector2.up;
// how far can the local direction stray from the origin
float maxAbsoluteDeltaDegrees = 40f;
void ClampDirection2D()
{
// world direction of the clamped local
// could be `... = transform.up;` if you are ok hardcoding comparing local up
Vector2 currentWorldDirection = transform.TransformDirection(clampLocalDirection);
// how far is it from the origin?
float curSignedAngle = Vector2.SignedAngle(clampOriginDirection, currentWorldDirection);
// clamp that angular distance
float targetSignedAngle = Mathf.Clamp(curSignedAngle, -maxAbsoluteDeltaDegrees,
maxAbsoluteDeltaDegrees);
// How far is that from the current signed angle?
float targetDelta = targetSignedAngle - curSignedAngle;
// apply that delta to the rigidbody's rotation:
rb.rotation += targetDelta;
}

When I hit play, my Sprite character rotates. Why?

I am making a top down game on Unity, so I am using the x and z axis as my plane. I have my character rotated x 90, y 0, z 0 so that it is flat on the plane. As soon as I hit play the character is rotated vertical?! I think it has something to do with my script to face the mouse position.
What it should look like:
When I hit play:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public static float moveSpeed = 10f;
private Rigidbody rb;
private Vector3 moveInput;
private Vector3 moveVelocity;
// Update is called once per frame
void Start()
{
rb = GetComponent<Rigidbody>();
mainCamera = FindObjectOfType<Camera>();
}
void Update()
{
// Setting up movement along x and z axis. (Top Down Shooter)
moveInput = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
moveVelocity = moveInput * moveSpeed;
//Make character look at mouse.
var dir = Input.mousePosition - Camera.main.WorldToScreenPoint(transform.position);
var angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(angle, Vector3.up);
}
void FixedUpdate()
{
// Allows character to move.
rb.velocity = moveVelocity;
}
}
Figured it out: I am answering my own question to help others.
Vector3 difference = Input.mousePosition - Camera.main.WorldToScreenPoint(transform.position);
float rotZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(90f, 0f, rotZ -90);
This does EXACTLY what I wanted!

Rotating avatar at certain x coordinate Unity

I have a problem with turning my avatar at a certain position on the map. The character moves back and forth, but he doesn't want to rotate at coordinates i entered. What am i doing wrong?
this is my code:
sing System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AvatarPingPong : MonoBehaviour {
// Use this for initialization
public float speed;
public float startXCord, endXCord;
float endTrunx, startTurnx;
public GameObject obj;
Vector3 EndPoint, StartPoint;
void Start () {
EndPoint = transform.position;
endXCord = EndPoint.x;
endTrunx = EndPoint.x - 2f;
StartPoint = transform.position;
StartPoint.x = startXCord;
startTurnx = StartPoint.x + 2f;
}
// Update is called once per frame
void Update () {
transform.position = new Vector3(PingPong (Time.time * speed, startXCord, endXCord ), transform.position.y, transform.position.z);
if (transform.position.x == startTurnx ) {
Debug.Log("start Check");
obj.transform.Rotate(0f, 180f, 0f);
}
if (transform.position.x == endTrunx ) {
obj.transform.Rotate(0f, 180f, 0f);
Debug.Log("einde check");
}
}
//function to change the default starting value of (0, 0, 0) to any value
float PingPong(float t, float minLength, float maxLength) {
return Mathf.PingPong(t, maxLength-minLength) + minLength;
}
}
I believe the problem is that you're trying to flip your avatar once he reaches a particular x-coordinate, but he may never reach that EXACT coordinate. if (transform.position.x == startTurnx) will only return true if the two values are EXACTLY the same, and your avatar isn't actually moving smoothly across the screen. He's actually jumping minute amounts every frame, so he may never land on exactly that point.
Instead, my recommendation would be to compare his new position against his old position to see which direction he's traveling in and flip him when he changes direction. Some code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AvatarPingPong : MonoBehaviour {
// Use this for initialization
public float speed;
public float startXCord, endXCord;
float endTrunx, startTurnx;
public GameObject obj;
Vector3 EndPoint, StartPoint;
//I'm going to assume you start it moving left. You may have to change this
bool goingLeft = false;
void Start () {
EndPoint = transform.position;
endXCord = EndPoint.x;
endTrunx = EndPoint.x - 2f;
StartPoint = transform.position;
StartPoint.x = startXCord;
startTurnx = StartPoint.x + 2f;
}
// Update is called once per frame
void Update () {
float prevX = transform.position.x;
float newX = PingPong (Time.time * speed, startXCord, endXCord );
transform.position = new Vector3(newX, transform.position.y, transform.position.z);
if (newX > prevX) {
//avatar is moving to the right, check to see if that's the direction it was going last Update
if (goingLeft) {
Debug.Log("Flipping Right");
obj.transform.Rotate(0f, 180f, 0f);
goingLeft = false;
}
}else if (newX < prevX){
//avatar is moving to the left, check to see it that's the direction it was going last Update
if (!goingLeft) {
Debug.Log("Flipping Left");
obj.transform.Rotate(0f, 180f, 0f);
goingLeft = true;
}
}
}
//function to change the default starting value of (0, 0, 0) to any value
float PingPong(float t, float minLength, float maxLength) {
return Mathf.PingPong(t, maxLength-minLength) + minLength;
}
}

Categories