Unity Very Large 2D World - Floating point precision limits [duplicate] - c#

This question already has an answer here:
Unity3d floating point precision limitations
(1 answer)
Closed 3 years ago.
I am trying to make a game about flying a spaceship to different planets and moons. Something like Kerbal Space Program but in 2d. When I go more than 100000 units from the center of the world under the transform of the rocket there would appear a message/error(Due to floating point precision limitations, it is recommended to bring the world coordinates of the gameobject within a smaller range) and the script for calculating the distance to the nearest planet would stop working and display a 0. Is there a way to make 2d worlds larger than 100000 units?
The Script for the distance to the nearest planet;
{
GameObject Planet;
Vector2 Direction;
public float Height;
public int HeightINT;
public Text text;
public bool isKM;
void Start()
{
Planet = GameObject.FindGameObjectWithTag("PlanetCentre");
}
void Update()
{
Direction = (Planet.transform.position - transform.position);
RayCastHeight();
if(Height >= 1000)
{
text.text = (HeightINT/1000).ToString() + " KM";
}
if(Height < 1000)
{
text.text = HeightINT.ToString() + " M";
}
}
void RayCastHeight()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, Direction);
Height = (hit.distance * 5);
HeightINT = Mathf.RoundToInt(Height);
}
} ```

First what comes to my mind, I wuld maybe recommend you to store your distance as string, and then parsing it to int. But I'm not sure, how it will all work. Hope this helps a bit.

Related

Simulate Gravity & Orbit in Unity

Hey and thanks for your help in advanced.
I've watched a few youtube videos on how to add Solar System and orbiting Gravity in Unity and ended up using the this for help for the solar system gravity part.
https://www.youtube.com/watch?v=Ouu3D_VHx9o&t=114s&ab_channel=Brackeys
But right after i decided to trying to make my planet orbit the sun i used this Wikipage for the math equation
https://en.wikipedia.org/wiki/Orbital_speed
But for some reason either my planets flies away of the sun start flying towards the planet. I've been looking around for 2 days and can't seem to make it work and tried diffrent type of possiblies.
Here is my code
public class Planets : MonoBehaviour
{
const float G = 100F;
public Rigidbody rb;
public float CurrentV;
private void FixedUpdate()
{
Planets[] attractors = FindObjectsOfType<Planets>();
foreach (Planets AllPlanets in attractors)
{
if (AllPlanets != this)
{
Orbiting(AllPlanets, CurrentV);
Attract(AllPlanets);
}
}
}
void Attract(Planets objToAttract)
{
Rigidbody RbTpAttract = objToAttract.rb;
Vector3 direction = rb.position - RbTpAttract.position;
float distance = direction.magnitude;
float ForceMagnitude = G * (rb.mass * RbTpAttract.mass) / Mathf.Pow(distance, 2);
Vector3 Force = direction.normalized * ForceMagnitude;
RbTpAttract.AddForce(Force);
}
void Orbiting(Planets objToAttract, float CV)
{
Rigidbody RbTpAttract = objToAttract.rb;
Vector3 direction = rb.position - RbTpAttract.position;
float distance = direction.magnitude;
float ForceMagnitude = Mathf.Sqrt((G * rb.mass) / (2 / distance - 1 / RbTpAttract.mass));
Vector3 Force = direction.normalized * ForceMagnitude;
RbTpAttract.velocity += Force;
}
}
The problem is that the formula for orbital speed is used to derive the speed of an object in orbit, but you're using it as a form of constant thrust applied to each body towards each other. That's a bit like calculating the speed of a moving car, and then applying the same speed back to it as an impulse!
The only force experienced by objects in orbit is the one you get from Newton's law G * m * m / r*r. In order to actually orbit though, the planets will need an initial velocity - this can be calculated from the orbital speed formula. Calculate it at the given distance, and apply it on Start() in a direction perpendicular to the orbital plane and the direction to the sun (or whatever you want to orbit), you can get this from dir = Vector3.Cross(sunDir, Vector3.up).normalized
Note that gravitational systems are not numerically stable in physics engines relying on euler integration (such as PhysX). You need things like Runge-Kutta integration for that, or the planets will eventually lose their orbit if you leave the simulation running for long enough.

AABB vs Circle collision in custom physics engine

I have followed this tutorial: https://gamedevelopment.tutsplus.com/tutorials/how-to-create-a-custom-2d-physics-engine-the-basics-and-impulse-resolution--gamedev-6331 to create a 2d physics engine in c# (he works in almost all the time wrong and inconsistent pseudo c++) I've got Circle vs Circle collision and AABB vs AABB collision working fine. But when trying the AABB vs Circle collison (below) the two rigidbodies just stick together and slowly move glitchy in one direction.
I would be super thankful if someone could help me with this as I have spent days and still don't know what's causing the error.
If someone needs more information from my code, I'd be happy to provide it.
public static bool AABBvsCircle(ref Collision result) {
RigidBody AABB = result.a.Shape is AABB ? result.a : result.b;
RigidBody CIRCLE = result.b.Shape is Circle ? result.b : result.a;
Vector2 n = CIRCLE.Position - AABB.Position;
Vector2 closest = n;
float x_extent = ((AABB)AABB.Shape).HalfWidth;
float y_extent = ((AABB)AABB.Shape).HalfHeight;
closest.X = Clamp(-x_extent, x_extent, closest.X);
closest.Y = Clamp(-y_extent, y_extent, closest.Y);
bool inside = false;
if (n == closest) {
inside = true;
if (Abs(n.X) > Abs(n.Y)) {
// Clamp to closest extent
if (closest.X > 0)
closest.X = x_extent;
else
closest.X = -x_extent;
}
// y axis is shorter
else {
// Clamp to closest extent
if (closest.Y > 0)
closest.Y = y_extent;
else
closest.Y = -y_extent;
}
}
Vector2 normal = n - closest;
float d = normal.LengthSquared();
float r = ((Circle)CIRCLE.Shape).Radius;
// Early out of the radius is shorter than distance to closest point and
// Circle not inside the AABB
if (d > (r * r) && !inside)
return false;
// Avoided sqrt until we needed
d = (float)Sqrt(d);
if (inside) {
result.normal = -normal / d;
result.penetration = r - d;
}
else {
result.normal = normal / d;
result.penetration = r - d;
}
return true;
}
edit 1 collison resolution method in "Collision" struct
public void Resolve() {
Vector2 rv = b.Velocity - a.Velocity;
float velAlongNormal = Vector2.Dot(rv, normal);
if (velAlongNormal > 0)
return;
float e = Min(a.Restitution, b.Restitution);
float j = -(1 + e) * velAlongNormal;
j /= a.InvertedMass + b.InvertedMass;
Vector2 impulse = j * normal;
a.Velocity -= a.InvertedMass * impulse;
b.Velocity += b.InvertedMass * impulse;
const float percent = 0.2f; // usually 20% to 80%
const float slop = 0.01f; // usually 0.01 to 0.1
Vector2 correction = Max(penetration - slop, 0.0f) / (a.InvertedMass + b.InvertedMass) * percent * normal;
if (float.IsNaN(correction.X) || float.IsNaN(correction.Y))
correction = Vector2.Zero;
a.Position -= a.InvertedMass * correction;
b.Position += b.InvertedMass * correction;
}
Before doing any detailed examining of the code logic, I spotted this potential mistake:
result.normal = -normal / d;
Since d was set to normal.LengthSquared and not normal.Length as it should be, the applied position correction could either be (much) smaller or (much) bigger than intended. Given that your objects are "sticking together", it is likely to be the former, i.e. d > 1.
(The fix is of course simply result.normal = -normal / Math.Sqrt(d);)
Note that the above may not be the only source of error; let me know if there is still undesirable behavior.
Although your tag specifies C#; here are basic AABB to AABB & AABB to Circle collisions that are done in C++ as these are take from: LernOpenGL:InPractice:2DGame : Collision Detection
AABB - AABB Collsion
// AABB to AABB Collision
GLboolean CheckCollision(GameObject &one, GameObject &two) {
// Collision x-axis?
bool collisionX = one.Position.x + one.Size.x >= two.Position.x &&
two.Position.x + two.Size.x >= one.Position.x;
// Collision y-axis?
bool collisionY = one.Position.y + one.Size.y >= two.Position.y &&
two.Position.y + two.Size.y >= one.Position.y;
// Collision only if on both axes
return collisionX && collisionY;
}
AABB To Circle Collision Without Resolution
// AABB to Circle Collision without Resolution
GLboolean CheckCollision(BallObject &one, GameObject &two) {
// Get center point circle first
glm::vec2 center(one.Position + one.Radius);
// Calculate AABB info (center, half-extents)
glm::vec2 aabb_half_extents(two.Size.x / 2, two.Size.y / 2);
glm::vec2 aabb_center(
two.Position.x + aabb_half_extents.x,
two.Position.y + aabb_half_extents.y
);
// Get difference vector between both centers
glm::vec2 difference = center - aabb_center;
glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
// Add clamped value to AABB_center and we get the value of box closest to circle
glm::vec2 closest = aabb_center + clamped;
// Retrieve vector between center circle and closest point AABB and check if length <= radius
difference = closest - center;
return glm::length(difference) < one.Radius;
}
Then in the next section of his online tutorial he shows how to do Collision Resolution using the above method found here: LearnOpenGL : Collision Resolution
In this section he adds an enumeration, another function and an std::tuple<> to refine the above detection system while trying to keep the code easier & cleaner to manage and read.
enum Direction {
UP,
RIGHT,
DOWN,
LEFT
};
Direction VectorDirection(glm::vec2 target)
{
glm::vec2 compass[] = {
glm::vec2(0.0f, 1.0f), // up
glm::vec2(1.0f, 0.0f), // right
glm::vec2(0.0f, -1.0f), // down
glm::vec2(-1.0f, 0.0f) // left
};
GLfloat max = 0.0f;
GLuint best_match = -1;
for (GLuint i = 0; i < 4; i++)
{
GLfloat dot_product = glm::dot(glm::normalize(target), compass[i]);
if (dot_product > max)
{
max = dot_product;
best_match = i;
}
}
return (Direction)best_match;
}
typedef std::tuple<GLboolean, Direction, glm::vec2> Collision;
However there is a slight change to the original CheckCollsion() function for AABB to Circle by changing its declaration/definition to return a Collision instead of a GLboolean.
AABB - Circle Collision With Collision Resolution
// AABB - Circle Collision with Collision Resolution
Collision CheckCollision(BallObject &one, GameObject &two) {
// Get center point circle first
glm::vec2 center(one.Position + one.Radius);
// Calculate AABB info (center, half-extents)
glm::vec2 aabb_half_extents(two.Size.x / 2, two.Size.y / 2);
glm::vec2 aabb_center(two.Position.x + aabb_half_extents.x, two.Position.y + aabb_half_extents.y);
// Get difference vector between both centers
glm::vec2 difference = center - aabb_center;
glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
// Now that we know the the clamped values, add this to AABB_center and we get the value of box closest to circle
glm::vec2 closest = aabb_center + clamped;
// Now retrieve vector between center circle and closest point AABB and check if length < radius
difference = closest - center;
if (glm::length(difference) < one.Radius) // not <= since in that case a collision also occurs when object one exactly touches object two, which they are at the end of each collision resolution stage.
return std::make_tuple(GL_TRUE, VectorDirection(difference), difference);
else
return std::make_tuple(GL_FALSE, UP, glm::vec2(0, 0));
}
Where the above functions or methods are called within this function that does the actually logic if a collision is detected.
void Game::DoCollisions()
{
for (GameObject &box : this->Levels[this->Level].Bricks)
{
if (!box.Destroyed)
{
Collision collision = CheckCollision(*Ball, box);
if (std::get<0>(collision)) // If collision is true
{
// Destroy block if not solid
if (!box.IsSolid)
box.Destroyed = GL_TRUE;
// Collision resolution
Direction dir = std::get<1>(collision);
glm::vec2 diff_vector = std::get<2>(collision);
if (dir == LEFT || dir == RIGHT) // Horizontal collision
{
Ball->Velocity.x = -Ball->Velocity.x; // Reverse horizontal velocity
// Relocate
GLfloat penetration = Ball->Radius - std::abs(diff_vector.x);
if (dir == LEFT)
Ball->Position.x += penetration; // Move ball to right
else
Ball->Position.x -= penetration; // Move ball to left;
}
else // Vertical collision
{
Ball->Velocity.y = -Ball->Velocity.y; // Reverse vertical velocity
// Relocate
GLfloat penetration = Ball->Radius - std::abs(diff_vector.y);
if (dir == UP)
Ball->Position.y -= penetration; // Move ball bback up
else
Ball->Position.y += penetration; // Move ball back down
}
}
}
}
// Also check collisions for player pad (unless stuck)
Collision result = CheckCollision(*Ball, *Player);
if (!Ball->Stuck && std::get<0>(result))
{
// Check where it hit the board, and change velocity based on where it hit the board
GLfloat centerBoard = Player->Position.x + Player->Size.x / 2;
GLfloat distance = (Ball->Position.x + Ball->Radius) - centerBoard;
GLfloat percentage = distance / (Player->Size.x / 2);
// Then move accordingly
GLfloat strength = 2.0f;
glm::vec2 oldVelocity = Ball->Velocity;
Ball->Velocity.x = INITIAL_BALL_VELOCITY.x * percentage * strength;
//Ball->Velocity.y = -Ball->Velocity.y;
Ball->Velocity = glm::normalize(Ball->Velocity) * glm::length(oldVelocity); // Keep speed consistent over both axes (multiply by length of old velocity, so total strength is not changed)
// Fix sticky paddle
Ball->Velocity.y = -1 * abs(Ball->Velocity.y);
}
}
Now some of the code above is GameSpecific as in the Game class, Ball class, Player etc. where these are considered and inherited from a GameObject, but the algorithm itself should provide useful as this is exactly what you are looking for but from a different language. Now as to your actually problem it appears you are using more than basic motion as it appears you are using some form of kinetics that can be seen from your Resolve() method.
The overall Pseudo Algorithm for doing AABB to Circle Collision with Resolution would be as follows:
Do Collisions:
Check For Collision: Ball With Box
Get Center Point Of Circle First
Calculate AABB Info (Center & Half-Extents)
Get Difference Vector Between Both Centers
Clamp That Difference Between The [-Half-Extents, Half-Extents]
Add The Clamped Value To The AABB-Center To Give The Point Of Box Closest To The Circle
Retrieve & Return The Vector Between Center Circle & Closest Point AABB & Check If Length Is < Radius (In this case a Collision).
If True Return tuple(GL_TRUE, VectorDirection(difference), difference))
See Function Above For VectorDirection Implementation.
Else Return tuple(GL_FALSE, UP, glm::vec2(0,0))
Perform Collision Resolution (Test If Collision Is True)
Extract Direction & Difference Vector
Test Direction For Horizontal Collision
If True Reverse Horizontal Velocity
Get Penetration Amount (Ball Radius - abs(diff_vector.x))
Test If Direction Is Left Or Right (W,E)
If Left - Move Ball To Right (ball.position.x += penetration)
Else Right - Move Ball To Left (ball.position.x -= penetration)
Else Test Direction For Vertical Collision
If True Reverse Vertical Velocity
Get Penetration Amount (Ball Radius - abs(diff_vector.y))
Test If Direction Is Up Or Down (N,S)
If Up - Move Ball Up (ball.position.y -= penetration)
Else Down - Move Ball Down (ball.position.y += penetration)
Now the above algorithm shown assumes that the boxes are not rotated and that their top & bottom edges are parallel with the horizontal and that their sides are parallel with the left and right edges of the window-screen coordinates. Also in the bottom section with the vertical displacement this also assumes that the top left corner of the screen - the first pixel is (0,0), thus the opposite operation for vertical displacement. This also assumes 2D collisions and not 3D Ridged or Ragdoll type collisions. You can use this to compare against your own source - implementation, but as far as just looking at your code without running it through a debugger it is extremely hard for me to see or find out what is actually causing your bug. I hope this provides you with the help that you need.
The above code from the mentioned OpenGL tutorial website does work as I have tested it myself. This algorithm is of the simplest of collision detections and is by far no means a comprehensive system and it still has caveats or pitfalls not mentioned here, but does suffice for the application it was used in. If you need more information about Collision Detections there is a few chapters that can be read in Ian Millington's book Game Physics Engine Development Although his book is based on a generalized 3D Physics Engine and only briefly discuses Collision Detection as their are full Books dedicated to the growing popularity of such complex beasts.

Knocking back the Player in a Unity 2D TopDown Game

When something hits the Player, maybe an enemy or spikes etc., I want him to get knocked back in the oppisite direction of the enemy.
So what I already got:
public void ChangeHealth(float healthToAdd, Vector2 objectPosition) // Change the players health, objectPosition = enemies position or something else
{
if (healthToAdd < 0) // incoming damage
{
// ... other stuff
Knockback(objectPosition);
}
// ... other stuff
}
void Knockback(Vector2 objectPosition) // Knockback routine
{
Vector2 knockbackPosition = new Vector2( , ); // calculation is missing here! Calculate the new position by the knockback direction
rigid.MovePosition(Vector2.MoveTowards(playerPos, knockbackPosition, 2 * Time.deltaTime)); // the knock back
}
private void Update() // !! TEST !!
{
if (Input.GetKeyDown(KeyCode.E)) // TEST routine
{
ChangeHealth(-7, new Vector2(10,10)); // decrease players health by 7 and knock him back
}
}
And what is missing:
Vector2 knockbackPosition = new Vector2( , );
I am looking for a calculation like this picture is showing:
Vector2 knockbackPosition =
transform.position + (transform.position - objectPosition).normalized *a
To understand why is it equal to that you have to read through.
There are three points: E,P,K (Enemy, Player, Knockback)
And one scalar number: a (the greater this value is, the more knockback you'll have)
Now from your picture:
PK = EP*a
expand vectors into distances between two points:
(K-P) = (P-E)*a
calculate the location of K:
K = P + (P-E)*a
There is one problem though. (thanks to Rotem) with this formula as it is:
you'd expect a bigger knockback from a closer opponent.
We don't want the knockback to be dependent on the distance between P and E.
To remove the dependency on the former, normalize the vector before multiplying by a
So we add .normalized to use just the direction of (P-E) instead of its original vector

Source, Target, and Intermediate Way Points

Consider two points: (0,0,0) as source and (1000,0,0) as target
A cube game object wants to travel from source and target at a pre-defined/constant speed. Time taken: t1
Introduce 100 intermediate points between source and target, i.e. INTERMEDIATE_POINTS = 10
Example: (0,0,0), (10,0,0), (20,0,0), (30,0,0).... (980,0,0), (990,0,0), (1000,0,0). Same speed, time taken: t2.
Introduce 50 intermediate points, i.e. INTERMEDIATE_POINTS = 20 ; (0,0,0), (20,0,0), (40,0,0),..., (960,0,0), (980,0,0), (1000,0,0). Same speed, time taken: t3.
Result: t1 < t3 < t2, i.e. more intermediate points, more time taken to reach the target (although same path and same speed)
Question: If you compare, the game object moves in the same way (same path, same speed) in all the three cases (no intermediates, 100 intermediates, and 50 intermediates) that are mentioned above. But why is there a time difference to reach the target?
Code to test this scenario:
using UnityEngine;
using System.Collections.Generic;
public class TestSpeed : MonoBehaviour
{
private List<Vector3> listOfPoints = new List<Vector3>();
private int INTERMEDIATE_POINTS = 1;
private int counter = 1;
private float speed = 50.0f;
private float originalDistance = 0.0f;
private float distanceCovered = 0.0f;
private float overshoot = 0.0f;
private Vector3 modifiedTarget;
// for the car movement.
private Vector3 targetPosition; // after every loop, get the next position
private Vector3 currentPosition;
// Use this for initialization
void Start()
{
for (int i = 0; i <= 1000; i = i + INTERMEDIATE_POINTS)
{
listOfPoints.Add(new Vector3(i, 0, 0));
}
currentPosition = this.transform.position; // at the beginning, from (0,0,0)
targetPosition = listOfPoints[counter];
}
// Update is called once per frame
void Update()
{
originalDistance = Vector3.Distance(targetPosition, currentPosition);
distanceCovered = Vector3.Distance(transform.position, currentPosition);
if(Vector3.Distance(transform.position, new Vector3(0,0,0)) >= 995.0f)
{
System.TimeSpan t = System.TimeSpan.FromSeconds(Time.timeSinceLevelLoad);
string answer = string.Format("{0:D2}:{1:D2}:{2:D2}",
t.Hours,
t.Minutes,
t.Seconds);
}
if ((originalDistance - distanceCovered) <= 0.0f)
{
currentPosition = transform.position;
targetPosition = listOfPoints[counter];
counter++;
}
else
{
float step = speed * Time.deltaTime;
if((distanceCovered + step) >= originalDistance)
{
overshoot = distanceCovered + step - originalDistance;
counter++;
modifiedTarget = Vector3.Lerp(targetPosition, listOfPoints[counter], (overshoot / originalDistance));
}
else
{
modifiedTarget = targetPosition;
}
transform.position = Vector3.MoveTowards(transform.position, modifiedTarget, step);
}
}
}
How to use the code:
Just create a cube game object and assign the script to it. Near to string answer set a break-point to check the time duration with various number of intermediate points.
I'm pretty sure this logic here is the cause of the strange observation:
if ((originalDistance - distanceCovered) == 0.0f)
{
currentPosition = transform.position;
targetPosition = listOfPoints[counter];
counter++;
}
You check whether or not you've arrived at your destination waypoint by checking for an exact position match; however, the distance you travel per Update is anything but exact. That means that your object could very well overshoot the destination, then try to move back towards it, overshoot it again, then repeat.
I bet if you watch your cube in the scene view, you'll see it hover around a single waypoint for a bit until it manages to hit the exact distance it needed.
You're probably better off using an inequality here, for example:
if ((originalDistance - distanceCovered) <= 0.0f)
{ /* ... */ }
Your object has reached its waypoint if the distance it has traveled is greater than or equal to the distance it needed to travel. originalDistance - distanceCovered will be negative as soon as the object has reached or passed the waypoint.
EDIT:
X.....X.....X.....X.....X.....X
Here are some waypoints. Pretend we have an object traveling along the path of waypoints. It starts at the first one on the left and goes right. It'll be represented by an O.
O.....X.....X.....X.....X.....X
Now it moves along for a while. Due to the variability of Time.deltaTime, it might move one or two spots each tick. So let's say it winds up here after a few ticks:
X....OX.....X.....X.....X.....X
And during the next tick, it moves two:
X.....XO....X.....X.....X.....X
With your original check, the object will now travel backwards. It needed to travel a distance of seven spaces, but it traveled 8. So with your original check, originalDistance - distanceCovered != 0.0f. So it'll keep trying to hit that spot over and over again until it hits it on the dot.
Even if you introduce the idea of a "threshold", you're still going to have the same problem. There is no fixed distance traveled per tick, so that means that each waypoint will have some artificial "bounce" time unless that threshold is so large that the waypoints become meaningless.
If you use originalDistance - distanceCovered <= 0.0f, you will always move on to the next waypoint if it overshoots. Instead of trying to land the object in some small window, you're just making sure that the object has passed or met its waypoint.

Rotation of Object Unity rotating at wrong value

My problem is I want to be able to rotate a cylinder 9 times. 360/9 is 40 so I all I should need to do is rotate by 40 degrees 9 times. This doesn’t work however as when I rotate the cylinder for the first time instead of by 40 degrees it rotates by 39.99 degrees. This happens at other rotations as well.
I’m rotating by doing this.
if(Input.GetKeyUp("i"))
transform.GetChild(m_Section).Rotate(0,40.0f,0);
I have unity version 3.4 it is not pro and I’m coding in C#.
Any help appreciated as I have just started trying to learn unity.
Unity3D stores rotations in a pretty abstract mathematical representation called quaternion. Tranformation from and to Euler angles (what you see in Unity editor) involves a cascade of trigonometric functions and thus is prone to rounding errors especially for the simple float type.
To get around this problem in your case I recommend storing the initial Quaternion object before starting to rotate and set it at the end of the process. Some pseudo-code:
public class RotationController : MonoBehaviour {
Quaternion rotationAtStart;
int numOfRotations = 0;
void rotate () {
numOfRotations++;
if (numOfRotations == 1) {
rotationAtStart = transform.rotation;
} else if (numOfRotations < 9) {
transform.Rotate (new Vector3 (0,40.0f,0));
} else if (numOfRotations == 9) {
transform.rotation = rotationAtStart;
}
}
void Update () {
if (numOfRotations < 9) {
rotate ();
}
}
}
The special situation of 360° makes this approach stable. For less than 360° you have to live with small rounding errors. For this case I would recommend calculating the target quaternion Quaternion.RotateTowards and set it in the last step analog to the 360 case.
Another useful thing for you are Animations. You can define an animation as smooth or in discrete steps and just call GameObject.animation.Play("MyRotation") if "i" is pressed. Use an AnimationEvent at the end to get informed when the animation is terminated.
And at last Mathf contains a function Approximately that deals with the problem of floating point imprecision.
Have a look at the answer of Kungfooman in this post, he descripes the problem with the rotation over 90 degree or at 90 degree as well as 270 degree. He provides an Extension which will always calculate the correct pendant of the Quaternion for the value you want to set. Have a look here:
using UnityEngine;
using System.Collections;
public class ExtensionVector3 : MonoBehaviour {
public static float CalculateEulerSafeX(float x){
if( x > -90 && x <= 90 ){
return x;
}
if( x > 0 ){
x -= 180;
} else {
x += 180;
}
return x;
}
public static Vector3 EulerSafeX(Vector3 eulerAngles){
eulerAngles.x = CalculateEulerSafeX(eulerAngles.x);
return eulerAngles;
}
}
And I used it like this:
Quaternion newQuat = m_directionalLight.transform.rotation;
Vector3 nL = new Vector3(ExtensionVector3.CalculateEulerSafeX(xNewValueLight),
0,
0);
newQuat.eulerAngles = nL;
m_directionalLight.transform.rotation =
Quaternion.Lerp(m_directionalLight.transform.rotation,
newQuat,
Time.deltaTime);

Categories

Resources