Unity destination Vector based on rotation and the amount to move - c#

I'm having an issue where I can't figure out the algorithm to find out the destination point based on objects rotation and the amount to move. I have to move to the direction of my rotation a certain amount, but I don't know how to calculate the destination point I end up being at. Example:
Object location = (0, 0)
Object rotation = 45
Amount to move = 4
with these variables the destination point would be (2.5, 2.5)
Example 2:
Object location = (0, 0)
Object rotation = 0
Amount to move = 4
and with these it would be (0, 4)
The problem is, I don't know how to calculate the destination point when I know those variables. I need an algorithm that will calculate the destination point, can somebody help with this? :)
Regards, Tuukka.

If this is a strictly algorithmic question where you want to calculate the destination point (i.e. no game object to move around, but abstract data), you can do this:
Consider the two-dimensional plane in cartesian coordinates, (i.e. the standard x/y system). Let O be an object at point (0,0). From your "destination point" (2.5, 2.5) I can assume that you want the following thing:
So 45° is the angle and 4 (amount to move) is the length of the line segment you want to move along. Starting from (0,0), this end point can be calculated using sine and cosine by using the formula for the polar representation of a point:
But actually, that image is wrong, which we'll see in the following computation. If the movement is along the line with a slope angle of 45°, you'd land a little bit elsewhere.
Anyways, for this example, alpha would be 45° which is pi/4 in radians (you get this by dividing by 180 and multiplying with pi), and the radius r would be 4 (the amount we want to move), so we'd have calculated the destination point as:
If the point is located anywhere in the room (not at (0,0) but at (x_0, y_0)), then you can still add it as an offset:
So in code you'd write:
public static Vector2 ComputeDestination(Vector2 origin, float amountToMove, float angle)
{
//convert degrees to radians
var rad = angle * Mathf.Deg2Rad;
//calculate end point
var end_point = origin + amountToMove * new Vector2(Mathf.Cos(rad), Mathf.Sin(rad));
return end_point;
}

float homMuchToMove = 4f;
float angle = 45f;
float pointX = Mathf.Cos (ConvertToRadians (angle)) * homMuchToMove;
float pointY = Mathf.Sin (ConvertToRadians (angle)) * homMuchToMove;
public float ConvertToRadians(float angle)
{
return (Mathf.PI / 180f) * angle;
}
For these values you will get both points at 2.828427f

Related

Arc lines between 2 objects on a sphere Unity3D

I need a little help with maths for drawing lines between 2 points on a sphere. I have a 3d globe and some markers on it. I need to draw curved line from point 1 to point 2. I managed to draw lines from point to point with LineRenderer, but they are drawn with the wrong angle and I can't figure out, how to implement lines that go at the right angle. The code by far:
public static void DrawLine(Transform From, Transform To){
float count = 12f;
LineRenderer linerenderer;
GameObject line = new GameObject("Line");
linerenderer = line.AddComponent<LineRenderer>();
var points = new List<Vector3>();
Vector3 center = new Vector3(
(From.transform.position.x + To.transform.position.x) / 2f,
(From.transform.position.y + To.transform.position.y) ,
(From.transform.position.z + To.transform.position.z) / 2f
);
for (float ratio = 0; ratio <= 1; ratio += 1 / count)
{
var tangent1 = Vector3.Lerp(From.position, center, ratio);
var tangent2 = Vector3.Lerp(center, To.position, ratio);
var curve = Vector3.Lerp(tangent1, tangent2, ratio);
points.Add(curve);
}
linerenderer.positionCount = points.Count;
linerenderer.SetPositions(points.ToArray());
}
So what I have now is creepy lines rising above along y axis:
What should I take into account to let lines go along the sphere?
I suggest you to find the normal vector of your two points with a cross product (if your sphere is centered at the origin) and then normalize it to use it as a rotation axis for a rotation using quaternions. To make the interpolations, you can simply rotate the first point around this vector with an angle of k * a where k is a parameter from 0 to 1 and a is the angle between your first two vectors which you can find with the acos() of the dot product of your two normalized points
EDIT : I thought about a much easier solution (again, if the sphere is centered) : you can do a lerp between your two vectors and then normalize the result and multiply it by the radius of the sphere. However, the spacings between the resulting points wont be constant, especially if they are far from each other.
EDIT 2 : you can fix the problem of the second solution by using a function instead of a linear parameter for the lerp : f(t) = sin(t*a)/sin((PI+a*(1-2*t))/2)/dist(point1, point2) where a is the angle between the two points.

Find slope in degrees between two vector3

Hi,
I found a large number of references but without being able to adapt them to my needs.
As per attached figures I have my character in a given position. Below the character's feet is a new plane (). With the mouse wheel I move the character up along the Y axis and the plane moves with it. Then I drag the character to any position and I join the three vector3s with Gizmos lines. Now I need to know the slope in degrees between the starting point (the red point) and the new position of the character. I tried to use Vector3.Angle or Atan2 and many examples found around but all return different values when you rotate the character despite the slope is always the same. For example charAngle = Vector3.Angle (initialCharPos - character.transform.position, Vector3.left) returns the correct value only in that certain direction and I can get the 4 points left, right, forward, back. But for directions other than these? I was wondering if for each of the 360 points it is necessary to make checks based on the direction or if there is a faster way to get this value.
You can use Vector3.Angle, you just need to take it between the down direction & the direction from the new feet position to the start feet position, and subtract the result from 90:
Vector3 newFeetPosition;
Vector3 startFeetPosition;
// direction of "down", could be different in a zero g situation for instance
Vector3 downDirection = Vector3.down:
float slopeDegrees = 90f - Vector3.Angle(newFeetPosition - startFeetPosition, downDirection);
If you need the rise/run for other reasons, you can get them in the process of calculating the angle yourself using vector math:
Vector3 newFeetPosition;
Vector3 startFeetPosition;
// direction of "up", could be different in a zero g situation for instance
Vector3 upDirection = Vector3.up:
Vector3 feetDiff = newFeetPosition - startFeetPosition:
float riseMagnitude = Vector3.Dot(feetDiff, upDirection);
Vector3 riseVector = riseMagnitude * upDirection;
float runMagnitude = (feetDiff - riseVector).magnitude;
float slopeDegrees = Mathf.Rad2Deg * Mathf.Atan2(riseMagnitude, runMagnitude);

normalizing euler angles rotation vector

I need to display the rotation in Euler angles of an object's certain axis.
I am aware that retrieving the rotation of an object in Euler angles gives inconsistent results, some of which can be solved by simply using modulo 360 on the result. however one permutation that unity sometimes does when assigning a vector with the value of "transform.localRotation.eulerAngles" is instead of retrieving the Vector3 "V", it retrieves "(180, 180, 180) - V".
to my understanding, "(180, 180, 180) - V" does not result in the same real world rotation as V, unlike "(180, 180, 180) + V" which does leave the actual rotation unaffected.
what is the explanation for the phenomenon, and what is the best way of normalizing an Euler angles rotation vector assuming I know the desired and feasible value of one of its axes? (for example, to normalize it such that all of it's values are mod 360 and it's Z axis equals 0 assuming it does have a representation in which Z = 0)
I don't know about the first part of the question (it is different enough to be its own question imo) but I can answer your second one.
So, you have these inputs :
Quaternion desiredRotation;
float knownZ;
And you're trying to find Vector3 eulers where eulers.z is approximately knownZ and Quaternion.Euler(eulers) == desiredRotation.
Here's the procedure I would use:
First, determine the up direction rotated by desiredRotation and the up and right direction rotated by a roll of knownZ:
Vector3 upDirEnd = desiredRotation * Vector3.up;
Quaternion rollRotation = Quaternion.Euler(0,0,knownZ);
Vector3 upDirAfterRoll = rollRotation * Vector3.up;
Vector3 rightDirAfterRoll = rollRotation * Vector3.right;
We know the local up direction after desiredRotation is applied and that the only thing that can adjust the up direction after the roll knownZ is applied is the rotation done by the euler pitch component. So, if we can calculate the angle from upDirAfterRoll to upDirEnd as measured around the rightDirAfterRoll axis...
float determinedX = Vector3.SignedAngle(upDirAfterRoll, upDirEnd, rightDirAfterRoll);
// Normalizing determinedX
determinedX = (determinedX + 360f) % 360f;
...we can determine the x component of eulers!
Then, we do the same with the yaw component of eulers to make the new forward direction line up with the end forward direction:
Vector3 forwardDirEnd = desiredRotation * Vector3.forward;
Quaternion rollAndPitchRotation = Quaternion.Euler(determinedX, 0, knownZ);
Vector3 forwardDirAfterRollAndPitch = rollAndPitchRotation * Vector3.forward;
Vector3 upDirAfterRollAndPitch = upDirEnd; // unnecessary but here for clarity
float determinedY = Vector3.SignedAngle(forwardDirAfterRollAndPitch, forwardDirEnd, upDirAfterRollAndPitch );
// Normalizing determinedY
determinedY = (determinedY + 360f) % 360f;
Vector3 eulers = new Vector3(determinedX, determinedY, knownZ);
To ensure that the given quaternion can be made with the given component, you could check if the axes given to SignedAngle actually can rotate the input vector to the target vector, or you can just compare the calculated eulers and the given quaternion:
Quaternion fromEuler = Quaternion.Euler(eulerAngles);
if (fromEuler==desiredRotation)
{
// use eulerAngles here
}
else
{
// component and quaternion incompatible
}
Hopefully that helps.
I'm not quite sure I understand your question correctly, but the euler angles just represent the angles of 3 rotations applied around the 3 axis in a specific order, right? So why would you normalize it by adding 180 everywhere? You should bring each angle individually into the range 0-360 by modulo-ing them.
Your question seems to imply that you can obtain any orientation by only rotating around two axis instead of three... is that what you are trying to achieve?
Using quaternions could possibly help you, in fact an orientation can be defined with just 4 scalar values: an axis and an angle

How do I get the Euler Angle difference from one object position to anothers orientation

I'm looking to find the Euler angle difference (on one axis) between a targets position relative to the players orientation and position.
Hopefully the below image will help illustrate what I want.
What I have attempted so far is this
public GameObject player;
public GameObject target;
Vector3 targetDir = target.transform.position - player.transform.position;
float angle = Vector3.Angle(player.transform.forward, targetDir);
Debug.Log(angle);
This will output the angle between 0-180, no matter if its on the left and right.
What I want is the euler angle, so the angle should be represented between 0 to 360 degrees.
I have tried converting the angles to Quaternions but I'm getting inconsistent results. The number goes from very low to high with a small amount of movement in the console
var testRotation = Quaternion.FromToRotation(player.transform.forward,target.transform.position - player.transform.position);
Debug.Log(testRotation.eulerAngles.y);
I have tried doing this using vector3 and dot
Vector3 targetDir = player.transform.position - target.transform.position;
var rot = Mathf.Cos(Vector3.Dot(player.transform.forward, targetDir.normalized));
Debug.Log(rot);
but this will output a number between 0.5 and 0.999
Using Draco18s answer I ended up creating this method (which is far from perfect). It worked for me.
/// <summary>
/// Calculate angle difference on the Y axis between one object (target) and anothers (player) orientation and position as an angle between 0 & 360 degrees
/// </summary>
/// <param name="_target"></param>
/// <param name="_player"></param>
/// <returns></returns>
private float CalcAngle(GameObject _target, GameObject _player)
{
//Calculate the direction of the target removing any up or down rotation
Vector3 _newDirection = (new Vector3(_target.transform.position.x, 0, _target.transform.position.z) - new Vector3(_player.transform.position.x, 0, _player.transform.position.z));
// the vector that we want to measure an angle from
Vector3 referenceForward = _player.transform.forward;/* some vector that is not Vector3.up */
// Remove any up or down rotation
referenceForward = new Vector3(referenceForward.x, 0, referenceForward.z);
// the vector perpendicular to referenceForward (90 degrees clockwise)
// (used to determine if angle is positive or negative)
Vector3 referenceRight = _player.transform.right;
referenceRight = new Vector3(referenceRight.x, 0, referenceRight.z);
// Get the angle in degrees between 0 and 180
float angle = Vector3.Angle(_newDirection, referenceForward);
// Determine if the degree value should be negative. Here, a positive value
// from the dot product means that our vector is on the right of the reference vector
// whereas a negative value means we're on the left.
float sign = Mathf.Sign(Vector3.Dot(_newDirection, referenceRight));
// Create an angle between 0-360
float result = sign * angle;
if (result < 0)
{
result = 360 - Mathf.Abs(result);
}
//Give back the result
return result;
}
Thanks
I'm sure there's a dupe-target somewhere, but I can't find it.
I can, however, find some code that I got from...somewhere a number of years ago, complete with comments.
private float CalcAngle(Vector3 newDirection) {
// the vector that we want to measure an angle from
Vector3 referenceForward = transform.forward;/* some vector that is not Vector3.up */
// the vector perpendicular to referenceForward (90 degrees clockwise)
// (used to determine if angle is positive or negative)
Vector3 referenceRight = transform.right;
// Get the angle in degrees between 0 and 180
float angle = Vector3.Angle(newDirection, referenceForward);
// Determine if the degree value should be negative. Here, a positive value
// from the dot product means that our vector is on the right of the reference vector
// whereas a negative value means we're on the left.
float sign = Mathf.Sign(Vector3.Dot(newDirection, referenceRight));
return sign * angle;
}
This will give you a value from -180 to +180. Converting that to 0-360 will require a little math, but should be straight forward (hint: -179.99 -> +180.01).

Project 3D coordinates to a 2D plane

I have a plane defined by a normal vector and another normalalised direction vector that is moving along that plane, both in 3D space.
I'm trying to figure out how to project that normal direction 3D vector onto the plane such that it ends up being a 2D vector with x/y coordinates.
It sounds like you need to find the angle between the direction vector and the plane. The size of the projection is going to scale with the cosine of that angle. Since the normal vector of the plane is perpendicular, I think you can find the sine between the normal vector and your direction vector.
The angle between the two vectors is given by the dot product of the vectors over the magnitudes multiplied together. That gives us our theta. Take the sin of theta, and we have the scaling factor (I'll call it s)
Next, you need to define unit size vectors on the plane to project onto. It's probably easiest to do this by setting one of the unit vectors in the direction of the projection to move forward...
If you set the unit vector in the direction of the projection, then you know the length of the projection in that unit space by using the scaling factor and multiplying by the length of the vector.
After that, with the unit vector, multiply in the length and find your vector relative to your normally defined xyz axis.
I hope this helps.
Try something like this. I wrote a paper on this exact method a while ago and can provide you with a copy if you would like.
PointF Transform32(Point3 P)
{
float pX = (float)(((V.J * sxy) - V.I * cxy) * zoom);
float pY = (float)(((V.K * cz) - (V.I * sxy * sz) - (V.J * sz * cxy)));
return new PointF(Origin.X + pX, Origin.Y - pY);
}
cxy is the cosine of the x-y camera angle, measured in radians from the positive x-axis on the xy plane.
sxy is the sine of the x-y camera angle.
cz is the cosine of the z camera angle, measured in radians from the x-y plane (so the angle is zero if the camera rests on that plane).
sz is the sine of the z camera angle.
Alternatively:
Vector3 V = new Vector3(P.X, P.Y, P.Z);
Vector3 R = Operator.Project(V, View);
Vector3 Q = V - R;
Vector3 A = Operator.Cross(View, zA);
Vector3 B = Operator.Cross(A, View);
int pY = (int)(Operator.Dot(Q, B) / B.GetMagnitude());
int pX = (int)(Operator.Dot(Q, A) / A.GetMagnitude());
pY and pX should be your coordinates. Here, vector V is the position vector of the point in question, R is the projection of that vector onto your viewing vector, Q is the component of V orthogonal to the viewing Vector, A is an artificial X-axis formed by the cross-product of the viewing vector with the vector (0,0,1), and B is an artificial Y-axis formed by the cross product of A and (0,0,1).
It sounds like what you're looking for is something like a simple rendering engine, similar to this, which used the above formulae:
Hope this helps.

Categories

Resources