I want to apply transforms to my OpenGL project, using C#. To apply the position, I just did:
modelMatrix = Matrix4.CreateTranslation(position);
With "position" being a Vector3.
To the rotation, I did:
public void UpdateRotation()
{
gameObject.mesh.modelMatrix = Matrix4.CreateRotationX(rotation.x);
gameObject.mesh.modelMatrix = Matrix4.CreateRotationY(rotation.y);
gameObject.mesh.modelMatrix = Matrix4.CreateRotationZ(rotation.z);
}
With "rotation" also being a Vector3.
But, now, I need to apply the scale, but OpenTK's Matrix4 only have the "Matrix4.CreateScale()" method, that takes a float "scale" as input, and not a Vector3.
How can I fix this problem and scale using a Vector3, instead of float.
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 working on a game and in this game the trees are 2d , because of this i want them to rotate around the player (i used this script)
{
public Transform target;
void FixedUpdate()
{
transform.LookAt(target);
}
}
but when i get to close to the quad it goes above me so
How can i limit the quad's rotation on X?
Your problem is simply that you can change A to rotate around B.
Method 1: B does not move, and A hangs the script to implement transform's RotateAround(vector3 point, vector3 axis, float angle) function
For example A.RotateAround(B.position, Vector3.up, 30*Time.deltaTime); //A rotates around the y-axis of B.
Method 2: A does not move, A acts as a sub-object of B, and B hangs the script to achieve rotation, and then rotates with A, similar to simulating rotation around the central celestial body.
For example: B.Rotate (Vector3.up, 30*Time.deltaTime, Space.Self); //B rotates around its own up direction, thus rotating with A.
Common methods of rotation:
(1) Rotate around the coordinate axis
public void Rotate (Vector3 eulerAngles, Space relativeTo = Space.Self);
or
public void Rotate (float xAngle, float yAngle, float zAngleSpace, relativeTo = Space.Self);
Where relativeTo = Space.Self means to rotate around its own coordinate system, and ralativeTo = Space.World means to rotate around the world coordinate system.
(2) Rotate a certain vector
public void Rotate(Vector3 axis, float angle, Space relativeTo);
where axis is the direction of the rotation axis, angle is the rotation angle, relativeTo is the reference coordinate system, and the default is Space.self. The function of this method is to rotate the GameObject object by angle degrees around the axis in the relativeTo coordinate system.
(3) Rotate around the pivot point
public void RotateAround(Vector3 point, Vector3 axis, float angel);
The function is to rotate the GameObject by an angle degree around the axis of the point.
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;
}
I have a sphere as player and I want it to move relative to camera
and not world.
When I rotate camera along Y-axis by 90 or 270 degree and give input, it gives opposite output than expected.
Here is my script attached to sphere:
using System.Collections;
using UnityEngine;
public class mover : MonoBehaviour {
public Rigidbody sphere;
public GameObject cameraa;
void Start ()
{
sphere = GetComponent<Rigidbody> ();
}
void Update ()
{
// getting input in controlVector
Vector3 controlVector = new Vector3 (Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// transforming direction from world space to local space[to camera]
Vector3 localVectToCam = cameraa.transform.InverseTransformDirection (controlVector);
//applying inputs
sphere.AddForce (localVectToCam * 10);
}
}
when camera's direction is same as world and when camera is rotated along Y-axis by 180 degree, it works fine.
But on rotating camera along Y-axis by 90 and 270 degree is gives opposite output.
example:
camera rotation along Y-axis = 90 degree [ i.e (0,90,0) ]
input given = (0,0,1)
output got = (-1,0,0)
expected output = (1,0,0)
Can you help me to understand and correct it?
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 and Rigidbody.AddForce expects a world space vector.
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(controlVector);
sphere.AddForce(worldMove * 10);
Or alternatively you can also just multiply by the rotation like
var worldMove = transform.rotation * controlVector;
sphere.AddForce(worldMove * 10);
However, since you are using a Rigidbody there is also Rigidbody.AddForceRelative which basically works the same way and expects a local space vector which is then internally converted into a world space force.
sphere.AddRelativeForce (controlVector * 10);
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.