What I need is to calculate where my lookat 3d vector would be based upon the camera position, the Y rotation and Z rotation, I would assume that any number greater than 0 would be sufficient for the distance from the camera to look at.
Here is the static class I'm using to control the camera and calculate the view and projection matrices, etc.
Class members that involve the position and rotation:
public static Vector3 Position { get; set; } //Publicly available for use outside the class
public static Vector3 Rotation { get; set; }
private static Vector3 camPos = new Vector3(0.0f, 200.0f, 300.0f); //However we work with these for manipulation of values
private static Vector3 camTarget = new Vector3(0, 0, -1200.0f);
private static float rotY = 0, rotZ = 0; //I believe that these are the correct axis following the right hand catasian coordinate system
My camera's update function:
public static void Update()
{
//Controls here
//Update Position here
Position = camPos;
//Update camTarget based on Position, rotY and rotZ <- Need help with this bit
//After calculating camTarget update Rotation
Rotation = camTarget;
UpdateMatrices();
}
UpdateMatrices() handles everything else and works as it should but here it is just in case you would like to see it:
public static void UpdateMatrices()
{
Matrix rotationMatrix = Matrix.CreateRotationX(MathHelper.ToRadians(90));
Vector3 transformedReference = Vector3.Transform(Vector3.Forward, rotationMatrix);
View = Matrix.CreateLookAt(Position, Rotation, Vector3.Forward);
Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspectRatio, nearClip, farClip);
World = Matrix.Identity;
CamBoundingFrustrum = new BoundingFrustum(View * Projection);
}
Would anyone be kind enough to share the secret to this please?
Rather than using Vector3.Forward, pull the .Forward and .Up vectors from your rotation matrix:
public void UpdateMatrices()
{
/// assumes Rotation is a Vector3 containing rotation around each axis expressed in radians
Matrix rotationMatrix = Matrix.CreateFromYawPitchRoll(Rotation.Y, Rotation.X, Rotation.Z);
/// View = Matrix.CreateLookAt(Position, Rotation, Vector3.Forward);
View = Matrix.CreateLookAt(Position, Position + rotationMatrix.Forward, rotationMatrix.Up);
Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspectRatio, nearClip, farClip);
/// World matrix needs to match your translation and rotation used above
/// XNA expects matrices to be concatenated in SRT (scale, rotation, then translate) order:
World = rotationMatrix * Matrix.CreateTranslation(Position);
CamBoundingFrustrum = new BoundingFrustum(View * Projection);
}
Note that you have to add the camera's translation (via a vector you're storing, or from the camera's world matrix .Translation property) to the RotationMatrix.Forward to get a real target.
The matrix .Forward, .Backward,.Left,.Right,.Up and .Down properties are the same as the equivalent Vector3 properties transformed by the rotation matrix. so, RotationMatrix.Forward points in the direction you're looking, .Up is orthogonal to that, etc.
If you just use Vector3.Forward, you don't get a transformed vector, and weird things can happen.
Other notes:
Don't make everything static. Create an instance of your camera class during Game.Initialize() and add it as a property. static should be used with caution - it can cause all sorts of fun threading issues that will drive you insane.
I'd also recommend switching to a quaternion instead of yaw/roll/pitch (if you haven't already) since it avoids gimble lock, but that's a different topic.
Related
For a school assignment I am trying to make a tank point and shoot game, where I need align the tank to the plane normal using raycasting. But when I do this the Y axis rotaton gets locked and I can't rotate it any more.
The idea is that if a plane is angled, the tank is sticking to it and the transform.up is defined by the plane normal and the tank is able to go up and down the plane and also rotate on it.
This is the code.
public class PlayerController : MonoBehaviour
{
public GameObject player;
public float movementSpeed;
public float rotationSpeed;
RaycastHit hitinfo;
public float hoverHeight = 0.7f;
float offsetdistance;
//public Vector2 moveVal;
public Vector2 moveVal;
public Vector3 Dir;
public float moveSpeed;
//get OnMovie input and put it in a vector to be used later on
void OnMove(InputValue value)
{
moveVal = value.Get<Vector2>();
}
void Update()
{
//moves player
PlayerHover();
player.transform.Translate(new Vector3(0, 0, moveVal.y) * moveSpeed * Time.deltaTime);
player.transform.Rotate(0, moveVal.x * rotationSpeed * Time.deltaTime, 0, Space.Self);
}
void PlayerHover()
{
if (Physics.Raycast(transform.position, -Vector3.up, out hitinfo, 20f))
{
offsetdistance = hoverHeight - hitinfo.distance;
transform.up = hitinfo.normal;
transform.position = new Vector3(transform.position.x, transform.position.y + offsetdistance, transform.position.z);
}
}
You are probably using the wrong overload of transform.Rotate, you should probably be using:
public void Rotate(Vector3 axis, float angle, Space relativeTo = Space.Self);
Where the axis parameter should be the plane normal. Assuming you want to rotate around the plane normal
If you want to set the rotation directly you might need to use SetPositionAndRotation, you should be able to get the quaternion from LookDirection using the plane normal for the forward parameter, but you may need to play around with the axes or apply a 90-degree rotation to get the desired axis to point in the right direction.
In general if you want to rotate an object so that one axis matches up to some other axis you can:
Calculate the angle between the axes, i.e. compute the dot-product and apply the ACos function. Or use any built in methods.
Calculate an orthogonal vector by taking the cross-product between the two vectors
Use this axis and angle in the Rotate function above to produce the desired rotation. Or any other functions that produce a rotation that take an axis and angle.
Use the up-direction of your tank and the plane normal as input axes for the algorithm above if you want to rotate your tank to be orientated to the plane.
I'm new with Unity development and I'm having some issues rotating GameObjects.
I want to make and object rotate -90 degrees each time you call a function. At this moment I can make it rotate around its Y and Z axis and there's no trouble, however, if i change the parameters so it rotates around X it gets stuck after rotating from -90º (270º actually) to 180º.
Here's the code I'm using for testing:
public class RotateCube : MonoBehaviour
{
GameObject cube;
bool rotating;
private void Start()
{
cube = GameObject.Find("Cube");
rotating = false;
}
private void Update()
{
if (!rotating)
{
StartCoroutine(Rotate());
}
}
private IEnumerator Rotate()
{
rotating = true;
float finalAngle = SubstractNinety(cube.transform.eulerAngles.x);
while (cube.transform.rotation.eulerAngles.x != finalAngle)
{
cube.transform.rotation = Quaternion.RotateTowards(cube.transform.rotation, Quaternion.Euler(finalAngle, cube.transform.rotation.eulerAngles.y, cube.transform.rotation.eulerAngles.z), 100f * Time.deltaTime);
yield return null;
}
cube.transform.rotation = Quaternion.Euler(finalAngle, cube.transform.rotation.eulerAngles.y, cube.transform.rotation.eulerAngles.z);
yield return null;
rotating = false;
}
private float SubstractNinety(float angle)
{
if (angle < 90)
{
return 270f;
}
return angle - 90;
}
}
I'm updating all the coordinates in Quaternion.Euler in each iteration because I want the user to be able drag the object while it's rotating, but I wouldn't mind if the solution requires to define the Quaternion before the loop.
Why do you bother to go through the eulerAngles at all?
When using the .eulerAngles property to set a rotation, it is important to understand that although you are providing X, Y, and Z rotation values to describe your rotation, those values are not stored in the rotation. Instead, the X, Y & Z values are converted to the Quaternion's internal format.
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.
Rather directly use Quaternion! Simply add the desired rotation to the exsting one using the * operator
private IEnumerator Rotate()
{
rotating = true;
// This returns a new Quaternion rotation which is the original
// rotation and then rotated about -90° on the global X axis
var finalRotation = currentRotation * Quaternion.Euler(-90, 0, 0);
// simply directly use Quaternion comparison
while (cube.transform.rotation != finalRotation)
{
cube.transform.rotation = Quaternion.RotateTowards(cube.transform.rotation, finalRotation, 100f * Time.deltaTime);
yield return null;
}
cube.transform.rotation = finalRotation;
rotating = false;
}
Currently I am trying to write a script that will mirror the movements of one controller onto the other for users who only have 1 functioning arm. How do I mirror the position of the one controller onto the other that way the arms are in bilateral movement?
Mirroring the y axis and z axis were easy since they move together and the rotation was easy to mirror. I am unable to mirror the x axis movement. I want it so that if the one hand moves out the other does the same, and they both move in together. Any ideas how I may be able to do this? I have attached my current script. I also disabled the position tracking of the non mirrored controller with simple Boolean logic in the OVRCemeraRig script to prevent stutters in movement Need to use OVRCemeraRig since using final IK
I have tried taking a difference in the x positions in the working arm and then adding that value to the none working arm. That did not work.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VRMirror : MonoBehaviour
{
public bool mirrorLeft;
public bool mirrorRight;
public GameObject leftHand; //left hand anchor in OVRCameraRig
public GameObject rightHand; //right hand anchor from OVRCameraRig
void Start()
{
}
void FixedUpdate()
{
Transform left = leftHand.GetComponent<Transform>();
Transform right = rightHand.GetComponent<Transform>();
if (mirrorLeft)
{
Vector3 leftPos = left.position;
Quaternion leftRot = left.rotation;
leftRot.y = -leftRot.y;
right.position = leftPos;
right.rotation = leftRot;
}
else if (mirrorRight)
{
Vector3 rightPos = right.position;
Quaternion rightRot = right.rotation;
rightRot.y = -rightRot.y;
left.position = rightPos;
left.rotation = rightRot;
}
}
}
For the sake of robustness, let's assume your player's body's rotation might not necessarily always have its right pointing in the (1,0,0) world direction. Instead, we can get a reference to the player's Transform, playerTransform, (be sure to assign it using the inspector or in Start if you must) and make our calculations using that.
To calculate the bilateral symmetric position for a relative vector, you can calculate relativeVec - 2f * playerTransform.right * Vector3.Dot(relativeVec, playerTransform.right);. Explanation for why that works is in the comments.
For position, we can convert the absolute position of the source hand to be relative to the player's position, then find the relative position of the destination hand, then convert that back into an absolute position.
For rotation, determine up & forward for the source hand and reflect them to determine the up & forward for the destination hand. Use Quaternion.SetLookRotation to convert the vectors to the rotation for the destination hand.
We can use the same code for relative positions and for our direction vectors, so it actually doesn't take much code once you have the math. And also, since Transform is a class, we can make one method that does the reflection procedure, and then pass into it which transforms we want to be the source and destination:
public class VRMirror : MonoBehaviour
{
public bool mirrorLeft;
public bool mirrorRight;
public GameObject leftHand; //left hand anchor in OVRCameraRig
public GameObject rightHand; //right hand anchor from OVRCameraRig
public Transform playerTransform;
void Start()
{
}
void FixedUpdate()
{
Transform left = leftHand.GetComponent<Transform>();
Transform right = rightHand.GetComponent<Transform>();
if (mirrorLeft)
{
MirrorFromTo(left, right);
}
else if (mirrorRight)
{
MirrorFromTo(right, left);
}
}
void MirrorFromTo(Transform sourceTransform, Transform destTransform)
{
// Determine dest position
Vector3 playerToSourceHand = sourceTransform.position - playerTransform.position;
Vector3 playerToDestHand = ReflectRelativeVector(playerToSourceHand);
destTransform.position = playerTransform.position + playerToDestHand ;
// Determine dest rotation
Vector3 forwardVec = ReflectRelativeVector(sourceTransform.forward);
Vector3 upVec = ReflectRelativeVector(sourceTransform.up);
destTransform.rotation = Quaternion.LookRotation(forwardVec,upVec);
}
Vector3 ReflectRelativeVector(Vector3 relativeVec)
{
// relativeVec
// Take the relative vector....
// + Vector3.Dot(relativeVec, playerTransform.right)
// and for how far along the player's right direction it is
// away from the player (may be negative),
// * playerTransform.right
// move it that distance along the player's right...
// * -2f
// negative two times (i.e., along the left direction 2x)
return relativeVec
+ Vector3.Dot(relativeVec, playerTransform.right)
* playerTransform.right
* -2f;
}
}
I made some alteration to what #Ruzihm had. Thank you so much for the help. Everything works perfectly in the code I sampled below but I would recommend #Ruzihm answer since how he handles rotations. This code works if the player model is stationary and you are not turning your full body. If you need to turn use: playerTransform.right instead of Vector3.right in the ReflectRelativeVector function but using playerTransform.right will move the arm as the head moves.
public class VRMirror : MonoBehaviour
{
public bool mirrorLeft;
public bool mirrorRight;
public GameObject leftHand; //left hand anchor in OVRCameraRig
public GameObject rightHand; //right hand anchor from OVRCameraRig
public Transform playerTransform;
void Start()
{
}
void FixedUpdate()
{
Transform left = leftHand.GetComponent<Transform>();
Transform right = rightHand.GetComponent<Transform>();
if (mirrorLeft)
{
MirrorFromTo(left, right);
}
else if (mirrorRight)
{
MirrorFromTo(right, left);
}
}
void MirrorFromTo(Transform sourceTransform, Transform destTransform)
{
// Determine dest position
Vector3 playerToSourceHand = sourceTransform.position - playerTransform.position;
Vector3 playerToDestHand = ReflectRelativeVector(playerToSourceHand);
destTransform.position = playerTransform.position + playerToDestHand;
// Determine dest rotation
Quaternion destRot = sourceTransform.rotation;
destRot.y = -destRot.y;
destRot.z = -destRot.z;
destTransform.rotation = destRot;
}
Vector3 ReflectRelativeVector(Vector3 relativeVec)
{
// relativeVec
// Take the relative vector....
// + Vector3.Dot(relativeVec, playerTransform.right)
// and for how far along the player's right direction it is
// away from the player (may be negative),
// * playerTransform.right
// move it that distance along the player's right...
// * -2f
// negative two times (i.e., along the left direction 2x)
return relativeVec
+ Vector3.Dot(relativeVec, Vector3.right)
* Vector3.right
* -2f;
}
}
Here is a screen shot of the editor:
I'm trying to make a simple VR movement script using teleportation. I used three empty GameObjects to generate with them Quadratic BezzierCurve. p0 is a GameObject inside the player; it's moving with the player. My problem is that points (Gameobjects) aren't relative to player. When p0 is in position 0, 0, 0, the script works fine; but when I hooked it up to player and I move the player around scene, these points are not relative to player. I think I messed up with some basic stuff.
using UnityEngine;
public class settingPos : MonoBehaviour
{
public Transform tf;
public GameObject p0, p1, p2;
// 3 empty GameObjects to paste them into QuadraticBezzier Script which uses it for
// generate bezzier curve. p0 is gameObject inside player, it is moving with player
public float UpForce; //simply value for scaling Bezzier curve
Vector3 [] dir= new Vector3[2];
void Update ()
{
dir[0] = p0.transform.position;
Vector3 temp = transform.rotation * Vector3.forward;
temp *= UpForce;
dir[0] += temp;
dir[1] = new Vector3(dir[0].x * 2, 0, dir[0].z * 2); // This is end point of bezzier
// curve. I mentioned that
// distance between end-point
// and height of bezzier curve
// was this same as start-point
// and height.
p1.transform.position = dir[0];
p2.transform.position = dir[1];
}
}
Pretty short
It should look this same, but in other position.
This shows my issue
I have written this code for rotating and moving the camera.
Sadly, I am not very experianced with matrices and 3D programming, since I started only a few days ago:
plLookAt = new Vector3(plPos.X, plPos.Y, plPos.Z - 20);
if (kb.IsKeyDown(Keys.W))
{
plPos.Z++;
}
if (kb.IsKeyDown(Keys.A))
{
plPos.X++;
}
if (kb.IsKeyDown(Keys.S))
{
plPos.Z--;
}
if (kb.IsKeyDown(Keys.D))
{
plPos.X--;
}
view = Matrix.CreateLookAt(new Vector3(0, 0, 0), plLookAt, Vector3.UnitY);
view = view * Matrix.CreateRotationY(MathHelper.ToRadians(rotations.Y)) * Matrix.CreateRotationX(MathHelper.ToRadians(rotations.X));
view = view *Matrix.CreateTranslation(plPos);
if (PrMS.X < ms.X)
{
rotations.Y++;
}
else if (PrMS.X > ms.X)
{
rotations.Y--;
}
if (PrMS.Y < ms.Y)
{
rotations.X++;
}
else if (PrMS.Y > ms.Y)
{
rotations.X--;
}
plPos is the Player (camera) position
view is the view Matrix
rotations is where the rotation of the camera is saved (Vector3)
PrMS is the MouseState of the previous frame.
This code doesn't work and I think it is because of the order, which the multiplications are in, but I'm not sure. What is the best way of rotating the camera, so that it actually works and I can go in the direction the camera is facing.
Thank You in advance!
Your problem is not in the order of the matrix multiplication, it is that your rotations need to be around camera local axes and your are performing them around world axes.
I think that what you are expecting is that applying .CreateRotationX(rotations.X) and .CreateRotationY(rotationsY) will cause the camera to change pitch and yaw about it's own local axis. But these rotations always cause a rotation about the world axis. If your camera's local X axis just happens to be aligned with the world X axis and you performed a .CreateRotationX(), then it would work as expected. But in your case, you are rotating the camera about the Y axis first and this is throwing the camera's local X axis out of alignment with the world X axis so the next rotation (the X) is not going to go as expected. Even if you did the X first and the Y second, the X would throw the Y out of whack. Although the order of matrix multiplication does matter in general, in your particular case, it is the axis of rotation that is the problem.
It appears you are making a camera that is located at your player's position but can look left/right, up/down by mouse control. Here is a class that offers a different way to approach that criteria:
class Camera
{
public Matrix View { get; set; }
public Matrix Projection { get; set; }
Vector2 centerOfScreen;// the current camera's lookAt in screen space (since the mouse coords are also in screen space)
Vector3 lookAt;
float cameraRotationSpeed;
public Camera()
{
//initialize Projection matrix here
//initialize view matrix here
//initialize centerOfScreen here like this: new Vector2(screenWidth/2, screenHeihgt/2);
cameraRotationSpeed = 0.01f;//set to taste
}
public void Update(Vector3 playerPosition, MouseState currentMouse)
{
Matrix cameraWorld = Matrix.Invert(View);
Vector2 changeThisFrame = new Vector2(currentMouse.X, currentMouse.Y) - centerOfScreen;
Vector3 axis = (cameraWorld.Right * changeThisFrame.X) + (cameraWorld.Up * changeThisFrame.Y);//builds a rotation axis based on camera's local axis, not world axis
float angle = axis.Length() * cameraRotationSpeed;
axis.Normalize();
//rotate the lookAt around the camera's position
lookAt = Vector3.Transform(lookAt - playerPosition, Matrix.CreateFromAxisAngle(axis, angle)) + playerPosition;//this line does the typical "translate to origin, rotate, then translate back" routine
View = Matrix.CreateLookAt(playerPosition, lookAt, Vector3.Up);// your new view matrix that is rotated per the mouse input
}
}