Mathf.Clamp having strange behaviour - c#

So i have a simple character controller but i want to clamp the camera so that i cant turn 180 degree vertically. As far as i know my code should have worked.
The values i want it to clamp by are min-70 and max70. But whenever i enter those as Min and Max nothing changes.
This is my current code
float x = Mathf.Clamp(_joystick.Vertical * (_moveSpeed * Time.deltaTime), camMin, camMax);
myCamera.transform.eulerAngles -= new Vector3(x, 0);
I feel like im missing something by now but i am unable to find what. So any help would be appreciated :D

You are only clamping the change in angle, not the actual angle. You should do something like:
var angles = myCamera.transform.eulerAngles;
var x = angles.X + _joystick.Vertical * (_moveSpeed * Time.deltaTime);
x = Mathf.Clamp(x, camMin, camMax);
myCamera.transform.eulerAngles = new Vector3(x, angles.Y, angles.Z);

Related

How to get the signed angle of a quaternion on an arbitrary axis

I'm trying to make an airplane controller, I am kind of aiming for something between arcade and realistic, so I want the plane to turn with a force proportional to the roll.
I haven't coded in any adjustments and I'm still prototyping the whole thing, but I encountered a problem with getting the signed rotation angle while using quaternions, I had a look at Determining if quarternion rotation is clockwise or counter clockwise here on SO but I am having trouble generalizing the solution to the (almost) arbitrary plane the rotation can be at.
What I made by now:
private void FixedUpdate()
{
float desiredYaw = _yaw * _rotationSpeed * Time.fixedDeltaTime;
float desiredPitch = -_pitch * _rotationSpeed * Time.fixedDeltaTime;
float rotationStepSize = _throttle * Time.fixedDeltaTime;
Quaternion toRotate = Quaternion.Euler(desiredPitch, 0, desiredYaw);
Quaternion straighRotation = Quaternion.LookRotation(_transform.forward, Vector3.up );
_rotation = _transform.rotation * toRotate;
float turningForce = Quaternion.Angle( _rotation, straighRotation );
_rigidbody.MoveRotation( _rotation );
_rigidbody.AddTorque( turningForce * _rotationForce * rotationStepSize * Vector3.up );
_rigidbody.AddRelativeForce( _speed * rotationStepSize * Vector3.forward );
}
EDIT: I realized I'm calculating the turning force using the roll rather then the yaw, that was intended just wrong wording, corrected now.
Since all you need is a factor that describes how downward the plane's right is, you can just use the y component of the plane's right for that. No need to bring in quaternions or even trigonometry. Explanation in comments:
private void FixedUpdate()
{
// ...
// Calculate how downward local right is in range [-1,1]
// The more downward, the more tilted right the plane is
// positive = tilted right
// negative = tilted left
float turnFactor = -_transform.right.y;
// Could do things to modify turnFactor to affect easing here.
// For instance, if turning rate should start slower then rapidly increase:
// turnFactor = Mathf.Sign(turnFactor) * turnFactor * turnFactor;
// Use factor and _rotationForce member to calculate torque, apply along
// global up.
// We expect to call this every fixed frame so we can just use the default
// ForceMode of ForceMode.Force which multiplies fixed delta time inside.
_rigidbody.AddTorque(_rotationForce * turnFactor * Vector3.up);
// ...
}

Atan2 is giving the false angle when object's y or z change in Unity

I wrote a algorithm. Its normally working.
Just i can not get right angle of x when y or z changes
float x = Mathf.Atan2(transform.forward.y, transform.forward.z) * Mathf.Rad2Deg
This code is giving the right angle when my object angles are (x,0,0).
But when the y or z change(x,35,46), this code is giving false angle.
By the way i want to get 0-360 angle.
If i get this angle, code will work(i tested it).
So i am trying to get the rotation of x axis 0-360.
But the atan2 is not giving the right value.
Maybe i can use Vector3.Angle but it doesn't work that i want.
I don't ask too many questions in stackoverflow so if you didn't understand please tell me which part didn't you get it?
If I understand you correct you want the objects rotation around the X axis (global or local).
You could probably simply use Transform.eulerAngles something like
var x = transform.eulerAngles.x;
if(x < 0) angle += 360;
Or if you want the local rotation (relative to the parent) Transform.localEulerAngles
var x = transform.localEulerAngles.x;
if(x < 0) angle += 360;
No, I wouldn't figured out
int sign = (transform.forward.y<0) ? 1 : -1;
float x = (Vector3.Angle(transform.position, transform.forward) - 38) * sign * 180 / 100;
This code is just working on 0,90,0 angle
I still can not reach the right angle when the rotation change
I found some code with the combination of Cross,Dot,Angle:
float Angle360(Vector3 v1, Vector3 v2, Vector3 n)
{
float angle = Vector3.Angle(v1,v2);
float sign = Mathf.Sign(Vector3.Dot(n, Vector3.Cross(v1, v2)));
float signed_angle = angle * sign;
return (signed_angle + 180) % 360;
}
This code is not working too
It will be made probably with Vector3.Angle or Dot vs.
How can i find right angle with Vector3.Angle,
The value 38 is changing
Vector3.Angle(transform.position, transform.forward)
This code is showing the angle but when the rotation change it gives false value.
how can i get the angle of x when objects look change.
So this code is giving right when the value is x,0,0.
Mathf.Atan2(transform.forward.y, transform.forward.z) * Mathf.Rad2Deg
I think i am not using Vector3.Angle Correctly
I need to get the x value when the y and z values are different

How does Spaceflight simulator predict flight-paths?

I am trying to make a game with Unity that uses rockets that are in orbit and can transition to different planets, but I need to predict the rocket path in real-time, depending on the current velocity/direction of the rocket allot like spaceflight simulator does.
I have managed to get realistic physics and orbits when adding gravitational forces to a rigidBody:
var sateliteCords = Satelite.transform.position;
var planetCords = gameObject.transform.position;
var distance = sateliteCords - planetCords;
distanceFromSatelite = Vector3.Distance(Satelite.transform.position, gameObject.transform.position);
F = (Gravity * Mass) / Mathf.Pow(distanceFromSatelite,2);
forces = (distance / Mathf.Sqrt(Mathf.Pow(distance.x, 2)+ Mathf.Pow(distance.y, 2)+ Mathf.Pow(distance.z, 2))) * -F;
Satelite.GetComponent<Rigidbody>().AddForce(forces * Time.deltaTime, ForceMode.Force);
but I don't know how to predict future points in time to give me the orbit shape.
I have tried "speeding up the simulation" with Time.timeScale = 50; but it isn't fast enough.
I read somewhere that I need a copy of the solar system running independently form the unity updates, and calculate the rocket trajectory from there but I don't know how to approach that.
I'm a bit of a noob when it comes to physics simulations so can one of you geniuses please help?
Here's what your code is actually doing:
var sateliteCords = Satelite.transform.position;
var planetCords = gameObject.transform.position;
var distance = sateliteCords - planetCords;
distanceFromSatelite = distance.magnitude;
F = (Gravity * Mass) / Mathf.Pow(distanceFromSatelite,2);
forces = distance / distanceFromSatellite * -F;
Satelite.GetComponent<Rigidbody>().AddForce(forces * Time.deltaTime, ForceMode.Force);
You can further reduce it by doing:
var distance = sateliteCords - planetCords;
distanceSqrMag = distance.sqrMagnitude;
F = (Gravity * Mass) / distanceSqrMag;
forces = distance.normalized * -F;
Satelite.GetComponent<Rigidbody>().AddForce(forces * Time.deltaTime, ForceMode.Force);
Now that the code is much simpler, I can find a crucial mistake. ForceMode.Force already multiplies the Force by Time.deltaTime. What you want here is to switch to ForceMode.Impulse, or remove the deltaTime.
F = (Gravity * Mass) / distanceSqrMag;
forces = distance.normalized * -F;
Satelite.GetComponent<Rigidbody>().AddForce(forces, ForceMode.Force);
Or better yet, assuming Mass is the mass of the satellite, and gravity is not a general constant, but just the local gravity of your planet. ForceMode.Force divides the force by the mass of the rigidbody it is applied on.
F = Gravity / distanceSqrMag;
forces = distance.normalized * -F;
Satelite.GetComponent<Rigidbody>().AddForce(forces, ForceMode.Acceleration);
Now that's all out the way, you can do a very simple approximation by doing something like
var currentPos = _rigidBody.position;
var prevPos = currentPos;
var currentVelocity = _rigidBody.velocity;
var planetCords = gameObject.transform.position;
for (int i = 0; i < stepCount; i++)
{
var distance = planetCords - currentPos;
var forceMag = Gravity / distance.sqrMagnitude;
forces = distance.normalized * forceMag;
currentVelocity += forces * Time.fixedDeltaTime;
currentPos += currentVelocity * Time.fixedDeltaTime;
//Replace by a TrailRenderer you reset each frame
Debug.DrawLine(prevPos, currentPos, Color.Red, Time.deltaTime);
prevPos = currentPos;
}
Note that this code is untested, I probably made a mistake here or there, but that's the gist of it.
Don't worry about getting any difference in trajectory, this is exactly what unity does to predict positions. No need for fancy calculations when you got all the computing power you need.
If you want more than one-body gravity simulation, it's simple. Either apply the gravity of all planets, or simply the gravity of the closest planet taking its mass into account. I recommend the former if you have less than a dozen planets.
You don't need conics, or advanced math, or any of that. You're not kerbal space program. Sometime brute force is the best solution.

How to constraint object rotation to certain degree?

I have steering wheel that is controlled by physical daydream controller(it works similar to wii controller). I use this code to do it:
void Update() {
transform.localRotation = GvrController.Orientation;
transform.localRotation = new Quaternion(0.0f, 0.0f, -transform.localRotation.y, transform.localRotation.w);
}
I need to mess with axis, beacause default position of the controller isn't good for a steering wheel.
But in 3-axis angle between maximum rotation to the left and to the right is 180 degrees. In this range everything is fine, but if I rotate a little bit more this values change to negative and everything is messed up. What can i do to allow the player to rotate only in this range(0 - 180 on z axis of 3-axis rotation)?
EDIT: The main problem is that the values of rotation after crossing 0 or 180 change to negative values, which are the same for both, but in different order. After crossing 0 it s form -1 to -180 and and for 180 its -180 to -1.
Firstly, we need a value that we can actually clamp. We'll get that from the eulerAngles.z field (as a typical onscreen wheel rotates about z - you might need to change that to some other field depending on the controller):
void Update() {
// Get the angle:
float angle = GvrController.Orientation.eulerAngles.z;
// The magic - clamp it:
if(angle < -180f){
angle = -180f;
}
else if(angle > 180f){
angle = 180f;
}
// Apply it as a new rotation:
transform.localRotation = Quaternion.Euler(0f,0f,angle);
}
Try this:
if (transform.eulerAngles.z > 180)
transform.eulerAngles = new Vector3(transform.eulerAngles.y, transform.eulerAngles.y, 180);
else if (transform.eulerAngles.z < 0)
transform.eulerAngles = new Vector3(transform.eulerAngles.y, transform.eulerAngles.y, 0);
If anyone wonders I found a solution, based on a script from Luke's answer. I realized that the values that change to negative are fine, only thing wrong with them is that they are negative. So this is the working script:
transform.localRotation = GvrController.Orientation;
float angle = -transform.localRotation.y;
if (angle < 0.0f) {
angle = Mathf.Abs(angle);
}
transform.localRotation = new Quaternion(0.0f, 0.0f, angle, transform.localRotation.w);
Try this:
If (transform.rotation > 180)
transforn.rotation = 180;

3D Math / 2D Rotation Calculation: Split/cut 3D model?

I'm trying to rotate a 3D object on its Z axis (up/down).
public void RotateY(float angle)
{
foreach (CoordinateVertices cv in this.GetAll<CoordinateVertices>())
{
for (int i = 0; i < cv.Coordinates.Length; i++)
{
Vector3 old = cv.Coordinates[i];
float theta = Math.Atan2(old.Y, old.X) + angle;
float rayon = Math.Sqrt(Math.Pow(old.X, 2) + Math.Pow(old.Y, 2));
cv.Coordinates[i] = new Vector3(Math.Cos(theta) * rayon, Math.Sin(theta) * rayon, old.Z);
}
}
}
The trigonometry is fairly simple, and it seems to work fine, but for some reason, my 3D object gets cut in half.
Does anybody have an idea of what's going on? I would have posted this on the maths StackExchange, but it might be a problem with my programming too, and the trigonometry is really simple.
Edit: The following is an alternative for the doing the same as the above. It took me a few minutes to realize the following solution is identical to the code initially posted.
It should look like this:
double Xnew = X * cos(theta) + Y * sin(theta);
double Ynew = Y * cos(theta) - X * sin(theta);
Or in your code:
public void RotateY(float angle)
{
foreach (CoordinateVertices cv in this.GetAll<CoordinateVertices>())
{
for (int i = 0; i < cv.Coordinates.Length; i++)
{
Vector3 old = cv.Coordinates[i];
float xnew = old.X * Math.Cos(angle) + old.Y * Math.Sin(angle);
float ynew = old.Y * Math.Cos(angle) - old.X * Math.Sin(angle);
cv.Coordinates[i] = new Vector3(xnew, ynew, old.Z);
}
}
}
The above code assumes you're rotating about the origin. If you're not rotating about the origin, you just need to translate to the origin, rotate, then translate back.
See here for more details: http://en.wikipedia.org/wiki/Transformation_matrix#Rotation
As has been noted, nothing wrong with your code. However, you may also be interested in using the Transform function (which can operate on your entire array of coordinates at once). Vector3.Transform (Vector3[], Matrix). You can get your rotation with a rotation matrix calculated for a given angle, theta, about any axis. I would expect this to be significantly faster for large numbers of points. (Less trig calculations, and possibly hardware acceleration)
Actually, the bug disappeared, out of nowhere. I went on to test some more values, and they worked. I went back to the same value as before, and it worked. This is ridiculous, it always happens to me.
What's the name for that? Bugs that disappear by themselves.

Categories

Resources