How do I rotate a model without rotating it's local axis? - c#

I just need to rotate a model 90 degrees when it starts, without editing its local axis.
I have a missile flying 90 degrees sideways down a tunnel, and I need it to be facing down the tunnel while still retaining its movement directions. I've tried:
virtual public void setOrientationWorld(float x, float y, float z)
{
mOrientation *= Matrix.CreateRotationX(MathHelper.ToRadians(x));
mOrientation *= Matrix.CreateRotationY(MathHelper.ToRadians(y));
mOrientation *= Matrix.CreateRotationZ(MathHelper.ToRadians(z));
}
setOrientationWorld(0, -90, 0);
And I've tried:
virtual public void setOrientationLocal(float x, float y, float z)
{
mOrientation *= Matrix.CreateFromAxisAngle(mOrientation.Right, MathHelper.ToRadians(x));
mOrientation *= Matrix.CreateFromAxisAngle(mOrientation.Up, MathHelper.ToRadians(y));
mOrientation *= Matrix.CreateFromAxisAngle(mOrientation.Forward, MathHelper.ToRadians(z));
}
setOrientationLocal(0, -90, 0);
But they both ruin everything. When I do either, the missile rotates, and all the axis's change. Even just going setPosition(0,0,-1) makes it move along the worlds X axis instead of it's Z.
I have tried moving the missile both locally and globally, but to no avail, like this:
virtual public void moveLocal(float x, float y, float z)
{
mPosition += mOrientation.Right * x;
mPosition += mOrientation.Up * y;
mPosition += mOrientation.Forward * z;
}
And
virtual public void moveWorld(float x, float y, float z)
{
mPosition.X += x;
mPosition.Y += y;
mPosition.Z += z;
}
The only other relevant thing I can think of is the draw function, which may have something to do with it:
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.World = Matrix.Identity *
Matrix.CreateScale(mScale) *
transforms[mesh.ParentBone.Index] *
Matrix.CreateTranslation(mPosition) *
mOrientation;
effect.View = game1.getView();
effect.Projection = game1.getProjection();
}
Pls Halp!

The order you apply the translation and rotation is important. If you are applying the rotation globally then translating first will cause problems as you are moving the object away from it's centre of rotation. In that case you need to rotate and then translate.
When applying the rotations in the local space of the missile, the order you apply the rotations is important too. Applying them in the order "Roll", "Pitch", "Yaw" will produce a different result to applying them in the order "Pitch", "Yaw", "Roll" and so on for all six combinations. The order is dependent on what you are trying to achieve so you may have to create a method that can do all six and selects which one based on a parameter.

Related

Rotate object in Unity 2D

Help to understand the management of objects. At the moment, there is a rotation of the object. I want the arrow to rotate, and the angle of rotation depends on the current X and Y coordinates. Unity2D.
Now it is left (does not rotate), but it needs to be right (it always rotates and looks in one direction), but I don’t know how to calculate the degrees for rotation.
`
public float angle = 0; // угол
public float radius = 0.5f; // радиус
public bool isCircle = false; // условие движения по кругу
public float speed = 5f;
// Update is called once per frame
void Update()
{
angle += Time.deltaTime; // меняется значение угла
var x = Mathf.Cos(angle * speed) * radius + parent.position.x;
var y = Mathf.Sin(angle * speed) * radius + parent.position.y;
transform.position = new Vector3(x, y,0);
//transform.Rotate(0, 0, a);
}
`
Help me, how to calculate angle?
You need Mathf.Atan2, it will return a radian, then you need to multiply a Mathf.Rad2Deg to get the Euler angle.

How can I make a camera zoom script in c# for unity?

I want a c# script so when I hold right click it zooms in a little and when you stop holding right click it goes back to its original position.
You would first want to store the original camera z distance. (As I assume you want it to go forward in the z direction). Then you would want to have a float for the amount of tome zoom was held down. (Increase it when zooming, and decrease it when you dont click it).
Also have a target z, so it doesn't go too far
public float startZ;
public float targetZ;
public float zoomInSpeed;
public float zoomOutSpeed;
float heldDownTime;
float originalZ;
void Start()
{
startZ = transform.position.z;
}
void Update()
{
if (Input.GetKey(KeyCode.Mouse0))
{
heldDownTime += Time.deltaTime * zoomInSpeed;
}
else
{
heldDownTime -= Time.deltaTime * zoomOutSpeed;
heldDownTime = Mathf.Clamp(heldDownTime, 0, Mathf.Infinity);
}
float smoothed = System.Math.Tanh(heldDownTime);
Vector3 pos = transform.position;
transform.position = new Vector3(pos.x, pos.y, startZ + smoothed * (targetZ - startZ));
}
I basically get the time held down, then I smooth it out, so it doesn't zoom to far forward.
Let me know in the comments if you get any errors!

How to divide by Quaternion in Unity

Using this tutorial https://www.youtube.com/watch?v=waEsGu--9P8&list=PLzDRvYVwl53uhO8yhqxcyjDImRjO9W722 and Quaternions I've made a function, that finds the World Position of the grid tiles for Isometric Tilemap.
private Vector3 GetWorldPosition(int x, int y, int r, int u, int f) {
return Quaternion.AngleAxis(u, Vector3.up) * Quaternion.AngleAxis(r, Vector3.right)
* Quaternion.AngleAxis(f, Vector3.forward) * new Vector3(x, y) * cellSize;
}
It works perfectly well. It rotates a 2D grid to fit Unity isometric Tilemap. Now i need to do the opposite - get the tile, if i know the worldPosition. I supposed, if in the previous case I multiplied the Quaternions, now i need to divide the worldPosition.x and worldPosition.y by them. But this code
private void GetXY(Vector3 worldPosition, out int x, out int y, int r = 60, int u = 0, int f = 45) {
Quaternion rotation = Quaternion.AngleAxis(u, Vector3.up) * Quaternion.AngleAxis(r, Vector3.right)
* Quaternion.AngleAxis(f, Vector3.forward);
x = Mathf.FloorToInt(worldPosition.x / rotation / cellSize);
y = Mathf.FloorToInt(worldPosition.y / rotation / cellSize); }
does not even run, because of the mistake
"Operator '/' cannot be applied to operands of type 'Quaternion' and
'float'"
So why could I multiply Quaternion and float cellSize in the first case, but can not divide or multiply them in the second case?
And how to do the opposite operation?
Thank you for trying to help, but Inverse did not work out.
So I had 2D grid, that was rotated -45 degrees Z and 60 degrees X. It made the grid isometric. So because it was rotated X by 60 degr., in 2D vision this grid had more than 90 degrees between its own X and Y (the tiles were rhombus). And i found out the exact angles, which i can use for the coordinates - different for x and y. This function got the tiles right for me.
private Vector2Int GetXY(Vector3 worldPosition) {
Vector3 positionX = Quaternion.AngleAxis(-65, Vector3.forward) * worldPosition / cellSize;
Vector3 positionY = Quaternion.AngleAxis(-26, Vector3.forward) * worldPosition / cellSize;
return new Vector2Int(Mathf.FloorToInt(positionX.x * 1.55f), Mathf.FloorToInt(positionY.y * 1.55f));
}

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;
}
}

Converting FPS ground camera to FPS free flying camera

I have implemented a camera in my OpenGL(openTk) project
//Move(0f, 0.1f, 0f); } Forward
//Move(-0.1f, 0f, 0f); } Left
//Move(0f, -0.1f, 0f); } Back
//Move(0.1f, 0f, 0f); } Right
//Move(0f, 0f, 0.1f); } Up
//Move(0f, 0f, -0.1f); } Down
public static void Move(float x, float y, float z)
{
Vector3 offset = new Vector3();
Vector3 forward = new Vector3((float)Math.Sin((float)Orientation.X), 0, (float)Math.Cos((float)Orientation.X));
Vector3 right = new Vector3(-forward.Z,0,forward.X);
offset += x * right;
offset += y * forward;
offset.Y += z;
offset.NormalizeFast();
offset = Vector3.Multiply(offset, MoveSpeed);
Position += offset;
}
Where "Orientation" is the x,y of the direction the camera is facing. "Position" is the position of the Camera in the world, and "MoveSpeed" is float.
This camera works great. But it is ground based. By this I mean that only the x value of the camera orientation affects movement direction. The y value does not. I want to make a free flying camera so if you look up and press forward the camera will fly into the air.
I tried changing the forward declation to:
Vector3 forward = new Vector3((float)Math.Sin((float)Orientation.X), (float)Math.Sin((float)Orientation.Y), (float)Math.Cos((float)Orientation.X));
This partially works, the camera now can fly into the air. But its not right, the camera is moving the same forward amount no matter how far "up" you tilt it. The up is not replacing some of the forward, its being added onto it.
I hope this explanation makes sense.
Thanks
You can do something like this:
First get the forward vector from Orientation (you can use this for your Camera.LookAt as well)
public Vector3 getForward(Vector2 Orientation)
{
Vector3 forward = new Vector3(0, 1, 0);
//X axis rotation
forward.Z = (float)Math.Sin((float)Orientation.Y);
forward.Y = (float)Math.Cos((float)Orientation.Y);
//Z axis rotation
forward.X = forward.Y*(float)Math.Sin((float)Orientation.X);
forward.Y = forward.Y*(float)Math.Cos((float)Orientation.X);
return forward;
}
And then move your camera with:
public void Move(float horizontal, float strafe)
{
Vector3 forward=getForward(Orientation);
//forward vector projected on XoY plane and rotated 90 degrees
Vector3 leftXoY = new Vector3(-forward.y ,forward.x,0);
Vector3 moveDirection = Vector3.Multiply(forward,horizontal)+Vector3.Multiply(leftXoY,strafe);
moveDirection.Normalize();
Position += Vector3.Multiply(moveDirection,MoveSpeed);
}
So if you call move(1,-1); will move the camera forward (along the forward vector) and strafe right.
I have found the solution after fiddling with it for a long time. I do not know if this is an efficient way to do it or not but it does work.
public static void Move(float x, float y, float z)
{
Vector3 CameraTargetVector;
CameraTargetVector.X = (float)(Math.Sin((float)Orientation.X) * Math.Cos((float)Orientation.Y));
CameraTargetVector.Y = (float)Math.Sin((float)Orientation.Y);
CameraTargetVector.Z = (float)(Math.Cos((float)Orientation.X) * Math.Cos((float)Orientation.Y));
Vector3 offset = new Vector3();
offset += x * new Vector3(-CameraTargetVector.Z, 0, CameraTargetVector.X);
offset += y * CameraTargetVector;
offset.Y += z;
offset.NormalizeFast();
Position += Vector3.Multiply(offset, MoveSpeed);
}

Categories