Edit or not the Transform.rotation in unity3D - c#

In unity documentation, under Transform.rotation, there is written:
Do not attempt to edit/modify rotation.
But few lines after, in the example, the assignment is used transform.rotation = ...
And setting transform.rotation directly seems to be everyday practice.
But can it be recommended? What about other properties, like localRotation, is the setter of the rotation property implemented so that the localRotation is changed too?

I feel like the wording on this in the docs used to be more clear, but this is what it implies:
It is highly discouraged to edit the transform.rotation of an object directly. that is to say doing something like transform.rotation = new Quaternion(0, 100, 15, 1); unless you really know the ins and outs of working with Quaternions, which are a great deal harder than working with EulerAngles which is more user friendly and easier to understand.
What you should be using instead (and this is also reflected in the code samples of the docs) are the methods made available by Unity to alter the rotation. These methods will deal with the complexity that comes with changing Quaternion values for you.
If we take a closer look at the code samples Unity provides:
Quaternion target = Quaternion.Euler(tiltAroundX, 0, tiltAroundZ);
transform.rotation = Quaternion.Slerp(transform.rotation, target, Time.deltaTime * smooth);
The two key factors here being target = Quaternion.Euler() which takes in three euler angles (angles on a 360 degree scale) and transforms them to Quaternions for you. And the other being rotation = Quaternion.Slerp() which takes in the current rotation as a quaternion, and a target rotation as quaternion and interpolates between the two.
Notice that neither of these two functions alters the transform.rotation "directly" from your side, but both pass through Unity's internal logic for converting to proper quaternions.
the same goes for methods like transform.Rotate.
transform.localRotation is basically the same as transform.rotation. The only difference being that the former indicates the rotation relative to the parent, and the latter its rotation relative to the world.
If you want to edit the rotation of an object directly it is easiest to edit the EulerAngles of the object. An example being:
transform.localEulerAngles = new Vector3(10, 150, 0);
Which will rotate your object 10 degrees around the X axis and 150 degrees along the Y axis, relative to its parent. Using transform.eulerAngles would rotate it relative to the world.
Note that, despite this being the easiest way to go around it. Unity does encourage using the Quaternions class and its functions to apply rotations, as using Euler angles can cause weird effects to happen.
There is also this doc talking about rotation and orientation in Unity which also states (emphasis mine):
Quaternions can be used to represent the orientation or rotation of a GameObject. This representation internally consists of four numbers (referenced in Unity as x, y, z & w) however these numbers don’t represent angles or axes and you never normally need to access them directly. Unless you are particularly interested in delving into the mathematics of Quaternions, you only really need to know that a Quaternion represents a rotation in 3D space and you never normally need to know or modify the x, y & z properties.
The reason for Unity using quaternions is, among others, that quaternions do not suffer from gimbal lock, which eulerAngles does. I quote from the article:
Unity stores all GameObject rotations internally as Quaternions, because the benefits outweigh the limitations.

The components of transform.rotation are not angles and are not able to be handled as angles.
As robertbu mentions in this article:
Many rotations (and likely the one you were attempting) can be
accomplished through other (simpler) methods, including
'Transform.Roate()' and directly accessing 'Transform.eulerAngles'-..
Transform.Rotate() - Rotate() does a relative rotation, and by
default, a local rotation-..

What they mean by not editing/modifying rotation is that rotation is a built in class by unity that handles rotations for objects.
So feel free to set the rotation of an object
like so:
Transform.rotation = object.transform.rotation;
But modifying the built in rotation class isn't recommended.

Related

Use MoveRotation to Look At Another Object Unity3d

Basically I am looking for a simple way for using the rigidbody/physics engine to have my ship look at another object (the enemy). I figured getting the direction between my transform and the enemy transform, converting that to a rotation, and then using the built in MoveRotation might work but it is causing this weird effect where it just kind of tilts the ship. I posted the bit of code as well as images of before and after the attempt to rotate the ship (The sphere is the enemy).
private void FixedUpdate()
{
//There is a user on the ship's control panel.
if (user != null)
{
var direction = (enemyOfFocus.transform.position - ship.transform.position);
var rotation = Quaternion.Euler(direction);
ship.GetComponent<Rigidbody>().MoveRotation(rotation);
}
}
Before.
After.
Well the Quaternion.Euler is a rotation about the given angles, for convenience provided as a Vector3.
Your direction is rather a vector pointing from ship towards enemyOfFocus and has a magnitude so the x, y, z values also depend on the distance between your objects. These are no Euler angles!
What you rather want is Quaternion.LookRotation (the example in the docs is basically exactly your use case ;) )
var direction = enemyOfFocus.transform.position - ship.transform.position;
var rotation = Quaternion.LookRotation(direction);
ship.GetComponent<Rigidbody>().MoveRotation(rotation);

Spawning objects parallel to the terrain

Story
so I'm working on making a procedural terrain generator script to spawn rocks on the ground all the big rocks don't care what rotation they are but I have some small rocks that have to be parallel to the terrain or they look weird, part of them floating off the ground other part stuck in the ground the quaternions I don't seem to understand
Problem
i am getting the terrainData.GetInterpolatedNormal and putting it into a vector3 called normals then when i am spawning my rock i rotate it towards ground
Instantiate(SmallRock, new Vector3(point.x, Heights, point.y) transform.rotation = new Quaternion(normals.x,normals.y, normals.z, 90));
my problem lies in the Quaternion(normals.x,normals.y, normals.z, 90)
I don't know what to put where like should I only put the normals.x/z there or should I put the normals.y in there too and I don't even know what 90 at the end does, and yes I know that the interpolatednormals returns a 0 to 1 so I tried multiplying it to make it rotate more than 1 but it seems to just not rotate the right way if you can't tell by now I really have no clue how quaternions work but everywhere i search I can't find anything that helps and I didn't really feel like learning about how quaternions work so thanks for saving me time
Quaternions use compound(Imaginary) numbers to represent a sequence of rotations in 3d space.
When you instantiate a new Quaternion using it's constructor you are providing it with what you think the Quaternion's real and imaginary numbers should be.
Despite the seemingly familiar x, y, and z names you should not manually modify or provided them, they are not euler angles, cartesian coordinates, or traditional vector components.
What you're currently passing it is portions of a direction instead of the real and imaginary parts of a Quaternion.
A normal is an "outwards direction" from a given position. So to get a rotation we need some other direction to compare it to in order to get a rotation.
Compare your direction with the up direction and you'll get a rotation that you can use.
Quaternion rotation = Quaternion.FromToRotation(Vector3.up, normalDirection);

Unity C# Trig Functions in movement

I'm creating an fps game in unity and chose to not use the included First Person Controller script to reduce complexity. The camera object will constantly be set to the players position and can rotate. I have created a script containing the following code for movement:
float v = Input.GetAxis("Vertical");
float h = Input.GetAxis("Horizontal");
Vector2 rotation = new Vector2(
Mathf.Cos(cam.transform.eulerAngles.y * deg2rad), (Mathf.Sin(cam.transform.eulerAngles.y * deg2rad))
);
rb.velocity = (
new Vector3(
(-rotation.x * h * speed) + (rotation.y * v * speed),
rb.velocity.y,
(-rotation.y * h * speed) + (-rotation.x * v * speed)
)
);
When I test out the game, the movement is correct along the x-axis in both directions, but is unusual when the players y rotation becomes something other than being aligned with the x-axis (Like moving the player backwards will actually move them forwards and visa-versa).
I'm open to an alternative besides using trig functions for the movement, as I have already used transform.forward and transform.right, but they didn't work entirely.
First thing I'd say is that unless you're intending to learn trig and geometrical functions then you should not reinvent the wheel. As you've stated you want to create a FPS game so really you should leverage the scripts and prefabs that others have created to enable the creation of your game.
If you don't want to use the inbuilt FPS Controller script I'd recommend using a free Asset package named '3rdPerson+Fly'. It appears a bit complex at first however you'll learn about states and stackable behaviours/modes which is going to get you an outcome much faster than creating from scratch. You'll also get the flexibility and openness that comes with a non-inbuilt package. This way you can peek at the inner workings if desired or simply build on top of them. Don't fall for NIH (Not Invented Here) syndrome and stand on the shoulders of giants instead :)
Good luck!
The issue you're having is likely caused by the fact that sin and cos can't determine by themselves which "quadrant" they're in. For example 30 degree angle is the same as a 150 degree angle as far as Sin is concerned.
This video is a fast and good explanation of the issue:
https://www.youtube.com/watch?v=736Wkg8uxA8

XNA - 3D Rotation about local (changing) ship axes - What am I missing?

I'm developing a 3D spaceshooter in XNA as a school project (basically Asteroids in 3D with power-ups), and have been working to implement roll, pitch, and yaw with respect to the ship's local axes. (I should emphasize: the rotation is not with respect to the absolute/world x, y, and z axes.) Sadly, I've been struggling with this for the last few weeks. Google and my neolithic monkey brain have failed me; maybe you folks can help!
Here's my setup:
Via keyboard input, I have the following variables ready to go:
yawRadians, which stores the desired yaw away from the ship's initial
position
pitchRadians, which stores the desired pitch away from the
ship's initial position
rollRadians, which stores the desired roll
away from the ship's initial position
The ship also maintains its own Front, Back, Right, Left, Top and Bottom unit vectors, which are used both for the rotations and also for propulsion. (Different keys will propel the ship toward the Front, Back, etc. This part is working great.)
Ultimately, I generate the rotation matrix mShipRotation, representing all of the ship's rotations, which is passed to the ship's draw method.
The problem I have is with the rotations themselves. Different solutions I've tried have had differing results. Here's what I've gone with so far:
Method 1 – Yaw, Pitch, and Roll relative to the absolute/world x, y, and z axes
At first, I naively tried using the following in my ship's Update method:
qYawPitchRoll = Quaternion.CreateFromYawPitchRoll(yawRadians, pitchRadians, rollRadians);
vFront = Vector3.Transform(vOriginalFront, qYawPitchRoll);
vBack = -1 * vFront;
vRight = Vector3.Transform(vOriginalRight, qYawPitchRoll);
vLeft = -1 * vRight;
vTop = Vector3.Transform(vOriginalTop, qYawPitchRoll);
vBottom = -1 * vTop;
mShipRotation = Matrix.CreateFromQuaternion(qYawPitchRoll);
(vOriginalFront, vOriginalRight, and vOriginalTop just store the ship's initial orientation.)
The above actually works without any errors, except that the rotations are always with respect to the x, y, and z axes, and not with respect to the ship's Front/Back/Right/Left/Top/Bottom vectors. This results in the ship not always yawing and pitching as expected. (Specifically, yawing degenerates to rolling if you have pitched up so the ship is pointing to the top. This makes sense, as yawing in this solution is just rotating about the world up axis.)
I heard about the Quarternion.CreateFromAxisAngle method, which sounded perfect. I could just combine three Quaternion rotations, one around each of the ship's local axis. What could go wrong?
Method 2 – Quaternion.CreateFromAxisAngle
Here's the second code snippet I used in my ship's Update method:
qPitch = Quaternion.CreateFromAxisAngle(vRight, pitchRadians);
qYaw = Quaternion.CreateFromAxisAngle(vTop, yawRadians);
qRoll = Quaternion.CreateFromAxisAngle(vFront, rollRadians);
qPitchYawAndRoll = Quaternion.Concatenate(Quaternion.Concatenate(qPitch, qYaw), qRoll);
vFront = Vector3.Normalize(Vector3.Transform(vOriginalFront, qPitchYawAndRoll));
vBack = -1 * vFront;
vRight = Vector3.Normalize(Vector3.Transform(vOriginalRight, qPitchYawAndRoll));
vLeft = -1 * vRight;
vTop = Vector3.Normalize(Vector3.Transform(vOriginalTop, qPitchYawAndRoll));
vBottom = -1 * vTop;
mShipRotation = Matrix.CreateFromQuaternion(qPitchYawAndRoll);
The above works perfectly if I only do one rotation at a time (yaw, pitch, or roll), but if I combine more than one rotation simultaneously, the ship begins to wildly spin and point in many different directions, getting more and more warped until it disappears entirely.
I've tried variants of the above where I first apply the Pitch to all the vectors, then the Yaw, then the Roll, but no luck.
I also tried it using Matrices directly, despite concerns of Gimbal Lock:
Method 3: Matrices
mShipRotation = Matrix.Identity;
mShipRotation *= Matrix.CreateFromAxisAngle(vRight, pitchRadians);
mShipRotation *= Matrix.CreateFromAxisAngle(vFront, rollRadians);
mShipRotation *= Matrix.CreateFromAxisAngle(vTop, yawRadians);
vFront = Vector3.Normalize(Vector3.Transform(vOriginalFront, mShipRotation));
vBack = -1 * vFront;
vRight = Vector3.Normalize(Vector3.Transform(vOriginalRight, mShipRotation));
vLeft = -1 * vRight;
vTop = Vector3.Normalize(Vector3.Transform(vOriginalTop, mShipRotation));
vBottom = -1 * vTop;
No luck; I got the same behavior. One rotation at a time is okay, but rotating about multiple axes resulted in the same bizarre spinning behavior.
After some brilliant debugging (read as: blindly outputting variables to the console), I noticed that the Front/Right/Top vectors were slowly, over time, becoming less orthogonal to one another. I added Normalization to vectors basically every step of the way, and also tried computing new vectors based on cross products, to try to ensure that they always remained perpendicular to one another, but even then they were not perfectly orthogonal. I'm guessing this is due to floating point math not being perfectly precise.
Note that I regenerate the mShipRotation matrix every Update method, so it cannot be accumulating drift or inaccuracies directly. I think that applying multiple Quarternion rotations may be accumulating error (as I can do one rotation just fine), but my attempts to fix it have not worked.
In short:
I can pitch/roll/yaw relative to the world axes x, y, and z just
fine. It's just not what the player would expect to happen as the
rolling/pitching/yawing is not relative to the ship, but to the
world.
I can roll, pitch, or yaw around the ship's local axes (Front/Back/Top/Bottom/Left/Right) just fine, but only one at a time. Any combination of them will cause the ship to spiral and deform rapidly.
I've tried Quaternions and Matrices. I've tried suggestions I've found in various forums, but ultimately do not wind up with a working solution. Often people recommend using Quaternion.CreateFromYawPitchRoll, not really realizing that the intent is to have a ship rotate about its own (constantly changing) axes, and not the (fixed) world axes.
Any ideas? Given a situation where you are given the roll, pitch, and yaw about a ship's front, right, and top vectors, how would you go about creating the rotation matrix?
You seem to be applying your overall angles (yawRadians, pitchRadians, rollRadians) to your local axis in your methods 2 & 3. These values are married to the world axis and have no meaning in local space. The root of your problem is wanting to hang onto the 3 angles.
In local space, use an angular amount that is the amount you want to rotate between frames. If you only pitched up 0.002f radians since the last frame, that would be what you would use when you rotate around the vRight axis.
This will screw with your overall angle values (yawRadians, pitchRadians, & rollRadians) and render them useless but most folks who stick with 3d programming quickly drop the angle approach to storing the orientation anyway.
Simply rotate your matrix or quaternion little by little each frame around your local axis and store the orientation in that structure (the quat or matrix) instead of the 3 angles.
There is no worries about gimbal lock when you are rotating a matrix about local axis like this. You would have to have 90 degree rotations between frames to bring that into the picture.
If you want to avoid error accumulation use a quat to store the orientation and normalize it each frame. Then the matrix you send to the effect will be made each frame from the quat and will be ortho-normal. Even if you didn't use a quat and stored your orientation in a matrix it would take hours or days to accumulate enough error to be visually noticeable.
This blog might help: http://stevehazen.wordpress.com/2010/02/15/matrix-basics-how-to-step-away-from-storing-an-orientation-as-3-angles/
I think this might be what you're looking for:
http://forums.create.msdn.com/forums/t/33807.aspx
I'm pretty sure that CreateFromAxisAngle is the way to go.

C# XNA Simulate Gravity

I have a question regarding using primitive shapes and simulating a circle to be free falling. How would I go about this? Do I use the formula for gravity?
It depends how detailed you want to be. Newtonian gravity laws dictate an force which has inverse square relationship between two bodies and their distance.
F is the magnitude of the gravitational force between the two point masses,
G is the gravitational constant,
m1 is the mass of the first point mass,
m2 is the mass of the second point mass, and
r is the distance between the two point masses.
So if you are simulating large bodies (say a planetary system) you need to apply gravity between each pair of objects.
Obviously, once you know the force you can apply Newton's second law (force=mass*acceleration) to calculate the acceleration you should apply to each body.
On the other end of the simplicity scale, if you have a fixed reference frame with respect to a single very large body (the ground) and multiple small objects, all you really need to do is apply a constant acceleration towards the ground. In this case, (in a vacuum) gravity on earth applies a constant acceleration of 9.81m/s2 to all objects. CodeInChaos gives a good code example of this.
(Of course we now know that Newtonian gravity isn't quite right and it breaks down at very large and very small scales. But that's a bit beyond this answer)
Gravity is just constant acceleration downwards.
Pseudocode:
const Vector3D Gravity=(0, 0, -9.8 m/s^2);
Vector3D Acceleration=Gravity;//insert other forces here
Vector3D Position+=Speed*DeltaT+0.5*Acceleration*DeltaT*DeltaT.
Vector3D Speed+=Acceleration*DeltaT;
You can use a physics engine, like Farseer:
http://roy-t.nl/index.php/2010/09/10/xna-farseer-platformer-physics-tutorial/
Simple version: if your circle has direction and speed then gravity is just a additional direction and speed pointing at the direction you want your gravity to pull.
Even simpler: just move the location of the circle downwards.
After this just fine tune it to fit your purpose with acceleration and setting the speed.

Categories

Resources