How to tilt an object depending on the direction of the player - c#

How do I correctly calculate the rotation angle so that the object dodges the player? It means that whichever side the player comes from, the object must turn away from the player depending on its direction.
I want the effect like in the video but without Joint, only rotation angle: https://www.youtube.com/watch?v=GJhiR3SOyXs
I need it for spherical map. And right now it looks like this.

calculate the direction Vector from player to the object.
calculate the axis of rotation by calculating the cross product of the direction Vector and the world up. Doing so creates a vector that is orthogonal to both, which is what we need.
calculate the angle by how much to rotate. This is done by clamping the distance (magnitude of direction) from 0 to the maximum effect distance and dividing it by the effect distance. Doing so creates a value from 0 to 1. However we need a value from 1 to 0, so there is no effect when the player is far away from the object and the maximum when close. To do so you simply subtract the initial value from 1. By multiplying the result with the max angle we calculate an angle in the range of 0 to maxAngle.
Finally we calculate the object rotation by multiplying the initial rotation with the rotation around the axis.
[SerializeField] Transform player;
[SerializeField] float effectMaxDistance=1;
[SerializeField] float maxAngle=50;
Quaternion initialRotation;
void Start(){
initialRotation = transfrom.rotation;
}
void Update(){
Vector3 dir = player.position - transform.position;
Vector3 axis = Vector3.Cross(dir, Vector3.up);
float angle = (1-(Mathf.Clamp(dir.magnitude, 0 effectMaxDistance) / effectMaxDistance)) * maxAngle;
transform.rotation = initialRotation * Quaternion.AngleAxis(angle, axis);
}
Note that cross products and Quaternion multiplications are not commutative and need to be done in this exact order!
Bonus answer:
If you're on a sphere you need to use the normal of the ground as the up direction. Since you've probably initially rotated the tree the right way up, you could do the following:
[SerializeField] Transform player;
[SerializeField] float effectMaxDistance=1;
[SerializeField] float maxAngle=50;
Quaternion initialRotation;
Vector3 initialUp;
void Start(){
initialRotation = transfrom.rotation;
initialUp = transfrom.up;
}
void Update(){
Vector3 dir = player.position - transform.position;
Vector3 axis = Vector3.Cross(dir, initialUp);
float angle = (1-(Mathf.Clamp(dir.magnitude, 0 effectMaxDistance) / effectMaxDistance)) * maxAngle;
transform.rotation = initialRotation * Quaternion.AngleAxis(angle, axis);
}

Related

how do I rotate a direction Vector3 upwards by an angle in unity

this is my direction vector
new Vector3(target.transform.position.x - projectile.position.x, 0, target.transform.position.z - projectile.position.z).normalized
I tried multiplying it by Quaternion.AngleAxis(45, Vector3.up) but that simply doesn't work
All other orientations like Vector3.left, right, etc. don't help either
The only thing I could observe is the way that the angle changes when I move the target or projectile
You were close. Use cross product to get the axis you need, use that in AngleAxis, then finally apply that rotation to your starting direction:
Vector3 RotateTowardsUp(Vector3 start, float angle)
{
// if you know start will always be normalized, can skip this step
start.Normalize();
Vector3 axis = Vector3.Cross(start, Vector3.up);
// handle case where start is colinear with up
if (axis == Vector3.zero) axis = Vector3.right;
return Quaternion.AngleAxis(angle, axis) * start;
}

Unity 3D - How to gllobaly rotate one object based on second

I have got a very large problem with rotation in Unity. What I want:
I have two 3D objects. Just one is for player manipulating, second object Transform.rotation and Transform.position is dependent on object number one with scale of 1/10. It means if I will move first object from (0,0,0) to (10,30,90) then obj.2 will move from (0,0,0) to (1,3,9). It's simple. But I have got LARGE problem with rotation.
I can't make rotation on normal transform because it's based on "local position".
Below I present my problem with simplest 2D object situation:
As you can see when I rotate red object +90 degrees the second object rotate +9 degrees and the axes become different in relation to the world. After more transformations in 3D world it make a large mess. For example after some transformations if I will want to rotate 3D object from me (like using accelerator on motocycle) on first make second object rotating from left to right (because it's based on object axis).
Of course using Transform.Rotate instead of Transform.localRotate (or Transform.EulerAngles instead of Transform.localEulerAngles) is not a solutions because it's means only if objects are childrens (it this this case are not).
WHAT I FOUND:
Using Transform.Rotate(Xdegree,Ydegree,Zdegree, Space.World) is solution for rotating second object !
What I need:
Xdegree, Ydegree and Zdegree from first (manipulated by player) object.
Transform.EulerAngles and Transform.Rotation DOESN'T work because it's returns "local objects" rotations.
So... I know that if 3D obj.2 rotation is (0;30;0) and i use obj2.Rotate(45,0,0) then the obj.2 rotation will be (~37.76;~39.23;~26.56) and it's okay. But I dont know how to convert the other way (from "local" rotation XYZ to degrees that I can use on Transform.Rotate() (of course I will divided this values (xyz) by 10 at the end because I have got 1/10 moving scale))
If you need one GameObject to have 1/10 of the rotation and position of another, you could use something like:
//the player-controlled cube
public Transform t1;
//the 1/10 cube
public Transform t2;
void Update(){
//set the position of t2 to 1/10 of the position of t1
t2.position = 0.1f * t1.position;
//get the axis and angle of t1's rotation
t1.rotation.ToAngleAxis(out float angle, out Vector3 axis);
//t2 should be rotated in the same direction (axis), but with 1/10th of the angle
t2.rotation = Quaternion.AngleAxis(angle * 0.1f, axis);
}
Edit: To allow resetting delta rotation and changing targets, you could do something like this. Note: this glitches when it wraps more than a full circle, I'm not an expert on Quaternions so you'd have to figure it out yourself.
//the player-controlled cube
public Transform t1;
//the 1/10 cube
public Transform t2;
private Vector3 t1originalPosition;
private Quaternion t1originalRotation;
private Vector3 t2originalPosition;
private Quaternion t2originalRotation;
void Start()
{
ResetTarget(t1);
}
void Update()
{
if (t1 != null)
{
//set the position of t2 to 1/10 of the position of t1
t2.position = t2originalPosition + 0.1f * (t1.position - t1originalPosition);
Quaternion t1Rotation = t1.rotation * Quaternion.Inverse(t1originalRotation);
//get the axis and angle of t1's rotation
t1Rotation.ToAngleAxis(out float angle, out Vector3 axis);
//t2 should be rotated in the same direction (axis), but with 1/10th of the angle
t2.rotation = Quaternion.AngleAxis(angle * 0.1f, axis) * t2originalRotation;
}
}
public void ResetTarget(Transform target = null)
{
t2originalPosition = t2.position;
t2originalRotation = t2.rotation;
t1 = target;
t1originalPosition = t1.position;
t1originalRotation = t1.rotation;
}
Use quaternions instead of the euler angles (xyz rotation angles). And simply give the global rotation value (quaternion) of one object to the other.
To add together quaternions, you just multiply them together.

Projectile shooting with custom force

I have made a projectile shooting system. I want to shoot an object from position A to B following an indicated path. Everything is working fine except for one thing. The velocity applied on the object is calculated based on the distance between A and B, the value of time is 1 to travel this distance. Meaning that the farther I hit, the quicker it goes. I want to have control of the force applied. Meaning that it should go with my set speed whether I hit near or far. Tried normalizing the velocity and multiplied it by my custom force value, but then it moves away from its trajectory.
(See this link below, no matter how close or far we hit the object. It goes with the same speed while following the trajectory indicated. I want to develop this functionality.
https://play.google.com/store/apps/details?id=com.ghakilo.trickytrack)
Vector3 calculateVelocity(Vector3 target, Vector3 origin, float time)
{
Vector3 distance = target - origin;
Vector3 distanceXZ = distance;
distanceXZ.y = 0f;
float Sy = distance.y;
float Sxz = distanceXZ.magnitude;
float Vxz = Sxz / time;
float Vy = 0f;
Vy = Sy / time + 0.5f * Mathf.Abs(Physics.gravity.y) * time;
Vector3 result = distanceXZ.normalized;
result = result * Vxz;
result.y = Vy;
return result;
}
Physics time!
Velocity is a vector whose magnitude is speed.
If you want to fix the speed at which your projectile starts, that only leaves the direction of the velocity in your hands, so you need to calculate the direction in which you want to yeet your projectile.
If you're like me, you shoot straight at the target, so you'd set the direction vector to go from the origin to the target. Which is simply target - origin, or what you calculate as distance. I'm going to call this direction because that's what it really is being used for.
Now to use this as the direction vector for your velocity, convert distance to a unit vector (not sure how you do this in the unity framework, but direction.Normalize()?)
Then multiply this by the speed to get your velocity vector!
Vector3 calculateVelocity(Vector3 target, Vector3 origin, float speed)
{
Vector3 direction = target - origin;
direction.Normalize();
Vector3 result = direction * speed;
return result;
}
First add rigidbody to your bullet and set the gravity scale to .5
and add this code to your bullet prefab
Vector3 calculateVelocity(Transform target, Transform origin, float time)
{
Vector3 direction = target.transform.position - origin.transform.position;
float distance = Vector3.Distance(origin.transform.position, target.transform.position);
result = direction.normalized * distance * speed * Time.deltaTime;
return result;
}

Unity - Apply two local rotations to object (trying to recreate the rotation of a controller joystick into a 3D mesh joystick)

I would like to recreate one on one the rotation of the real life controller joystick (i.e. 360 controller) into a 3D joystick mesh (that resembles the 360 controller one).
I thought about doing it by rotating the joystick in the X axis according to the magnitude of the input (mapping it to a min and max rotation in the X axis). And then figure the angle of the input and apply it to the Y axis of the 3D joystick.
This is the code I have, the joystick tilts properly in the X axis but the rotation in the Y axis doesn't work:
public void SetStickRotation(Vector2 stickInput)
{
float magnitude = stickInput.magnitude;
// This function converts the magnitude to a range between the min and max rotation I want to apply to the 3D stick in the X axis
float rotationX = Utils.ConvertRange(0.0f, 1.0f, m_StickRotationMinX, m_StickRotationMaxX, magnitude);
float angle = Mathf.Atan2(stickInput.x, stickInput.y);
// I try to apply both rotations to the 3D model
m_Stick.localEulerAngles = new Vector3(rotationX, angle, 0.0f);
}
I am not sure why is not working or even if I am doing it the right way (i.e. perhaps there is a more optimal way to achieve it).
Many thanks for your input.
I would recommend rotating it by an amount determined by the magnitude around a single axis determined by the direction. This will avoid the joystick spinning around, which would be especially noticeable in cases of asymmetric joysticks such as pilots joysticks:
Explanation in comments:
public void SetStickRotation(Vector2 stickInput)
{
/////////////////////////////////////////
// CONSTANTS (consider making a field) //
/////////////////////////////////////////
float maxRotation = 35f; // can rotate 35 degrees from neutral position (up)
///////////
// LOGIC //
///////////
// Convert input to x/z plane
Vector3 stickInput3 = new Vector3(stickInput.x, 0f, stickInput.y);
// determine axis of rotation to produce that direction
Vector3 axisOfRotation = Vector3.Cross(Vector3.up, stickInput3);
// determine angle of rotation
float angleOfRotation = maxRotation * Mathf.Min(1f, stickInput.magnitude);
// apply that rotation to the joystick as a local rotation
transform.localRotation = Quaternion.AngleAxis(angleOfRotation, axisOfRotation);
}
This will work for joysticks where:
the direction from its axle to its end is the local up direction,
it should have zero (identity) rotation on neutral input, and
stickInput with y=0 should rotate the knob around the stick's forward/back axis, and stickInput with x=0 should rotate the knob around the stick's left/right axis.
Figure out the problem, atan2 returns the angle in radiants, however the code assumes it is euler degrees, as soon as I did the conversion it worked well.
I put the code here if anyone is interested (not the change in the atan2 function):
public void SetStickRotation(Vector2 stickInput)
{
float magnitude = stickInput.magnitude;
// This function converts the magnitude to a range between the min and max rotation I want to apply to the 3D stick in the X axis
float rotationX = Utils.ConvertRange(0.0f, 1.0f, m_StickRotationMinX, m_StickRotationMaxX, magnitude);
float angle = Mathf.Atan2(direction.x, direction.y) * Mathf.Rad2Deg;
// Apply both rotations to the 3D model
m_Stick.localEulerAngles = new Vector3(rotationX, angle, 0.0f);
}

Stabilize hovercraft rigidbody upright using torque

I'm currently creating a game involving a hover-bike. when the bike collides with something , it's angles change naturally. I wish to create some sort of way for it to tend back to 0. Here's what I've tried:
if (hoverbike.rotation.x != 0 || hoverbike.rotation.z != 0)
{
hoverbike.AddTorque(x: Mathf.MoveTowardsAngle(hoverbike.rotation.x, 0, 0.01f), y: hoverbike.rotation.y, z: Mathf.MoveTowardsAngle(hoverbike.rotation.z, 0, 0.01f));
}
transform.Rotate(0.0f, -Input.GetAxis("Mouse X") * 0.5f, 0.0f);
It's hard to explain what it's doing because I don't understand what it's doing, it just seems to spin out.
Here's the rest of my code if interested: https://pastebin.com/kzMDQMVF, it's a mess but I'm still learning how to use Unity.
Oh and angle y shouldn't tend to 0 because that's the horizontal angle.
You can determine the quaternion that would rotate the hovercraft from its current up to world up using Quaternion.FromToRotation:
Rigidbody hoverRB; // hovercraft's rigidbody
Quaternion deltaQuat = Quaternion.FromToRotation(hoverRB.transform.up, Vector3.up);
Then use Quaternion.ToAngleAxis to convert that to an angle & axis:
Vector3 axis;
float angle
deltaQuat.ToAngleAxis(out angle, out axis);
Then, cancel out some of any existing rotational velocity so that you'll eventually reach the goal:
float dampenFactor = 0.8f; // this value requires tuning
hoverRB.AddTorque(-hoverRB.angularVelocity * dampenFactor, ForceMode.Acceleration);
And then apply some torque along the axis we found before, scaled by how much angle remains:
float adjustFactor = 0.5f; // this value requires tuning
hoverRB.AddTorque(axis.normalized * angle * adjustFactor, ForceMode.Acceleration);
Any conversion between radians Rigidbody uses and degrees of ToAngleAxis is redundant with the float constants, so don't worry too much about it.
Make sure this is all being done in FixedUpdate (or a function called/running in FixedUpdate time) due to how the torque's direction will likely need to change from one physics step to another. So, altogether:
Rigidbody hoverRB; // hovercraft's rigidbody
...
void FixedUpdate()
{
Quaternion deltaQuat = Quaternion.FromToRotation(hoverRB.transform.up, Vector3.up);
Vector3 axis;
float angle
deltaQuat.ToAngleAxis(out angle, out axis);
float dampenFactor = 0.8f; // this value requires tuning
hoverRB.AddTorque(-hoverRB.angularVelocity * dampenFactor, ForceMode.Acceleration);
float adjustFactor = 0.5f; // this value requires tuning
hoverRB.AddTorque(axis.normalized * angle * adjustFactor, ForceMode.Acceleration);
}

Categories

Resources