I'm making a game in a custom engine using C#. (Not Unity)
I've got a large grid and the x/y coordinates of two objects. The Player object and the Destination object. Along with the player's current rotation in degrees (0-360).
I've become too reliant on existing game engines and cannot work out how to find the rotation I need to put the player at to face the target.
playerRotation;//0 to 360 degrees.
playerX double = 47.43;
playerY double = 43.36;
targetX double = 52.15;
targetY double = 38.67;
My method at the moment is to try and get the distance between the objects by:
float distanceX = Math.Abs(playerX - destinationX);
float distanceY = Math.Abs(playerY - destinationY);
Which seems to work fine. Then I need to rotate the player to face the destination and have them move towards it until distanceX/Y are <= 0.
Edit: I've been messing with Atan2 to try and get the answer.
Vector2 playerCoords = new Vector2(playerX, playerY);
Vector2 targetCoords = new Vector2(targetX, targetY);
double theta = Math.Atan2((targetCoords.yValue - playerCoords.yValue), (targetCoords.xValue - playerCoords.xValue));
theta = theta * (180 / Math.PI);//Convert theta to degrees.
double sigma = playerRotation;//Direction in degrees the player is currently facing.
double omega = sigma - theta;
OutputLog("omega: " + omega);
My output log should be showing me the degrees my player needs to be facing to be facing the target. But it's giving me the wrong results.
Player: (4782, 4172) and
Target: (4749, 4157)
Angle should be about 286~.
But Theta = -155 and omega = 229.
Vector math can be very helpful, and it's not that complicated.
First vectors would be your player position and destination's:
Vector2 playerPos = new Vector2(playerX, playerY);
Vector2 destinationPos = new Vector2(destinationX, destinationY);
Now you can just subtract both vectors, to get a vector which points from one position to the other.
Vector2 delta = destination - playerPos; // Note, it might be the other way around: playerPos - destination
That delta vector has a length, and that is the distance between both points. There is usually a Length and a LengthSquared property available on the Vector class. Be aware however that calculating the length is quite CPU intensive because it uses a square root. If you want to compare that distance to a fixed distance like 200, just use the length squared and compare it to (200 * 200) which is way faster.
You can also use that delta, to let a bullet fly from one position to the other. You just need to normalize delta, there's a method for it, and you have it scaled down to length one. You can now use that delta, multiplied with a speed on each physics cycle to change the bullets position.
Now to get the angle, you can just use:
double angle = Math.Atan2 (delta.Y, delta.X); // Note that y and x are reversed here, and it should be like that.
Note that this angle is in radians, not degrees. A circle in radians starts at -PI and ends at PI. A full circle therefore is 2 * PI. To convert radians to degrees, you can see this question
Edit
I always assume that 12 o'clock is 0 degrees, 3 o'clock is 90, 6 o'clock is 180 and 9 o'clock is 270.
But actually in the cartesian coordinate system things are a bit different. I also made this false assumption in the code below.
But it turned out that I was wrong. See this picture
Now if you look at my sourcecode, all my variables are named incorrectly. If you look at their values however you can see that they match up with the picutre. Therefore the Atan2 correction is as it's supposed to be.
// Using Vector2 from System.Numerics;
internal static double RadianToDegree(double rad)
{
double thetaDegree = rad * (180.0 / Math.PI);
// Convert negative angles into positive ones
// https://stackoverflow.com/a/25725005/7671671
double thetaDegree2 = (thetaDegree + 360) % 360;
return thetaDegree2;
}
internal void Run()
{
// Player: (4782, 4172) and Target: (4749, 4157)
Vector2 player = new Vector2(4782, 4172);
Vector2 target = new Vector2(4749, 4157);
Vector2 delta = target - player;
double theta = Math.Atan2(delta.Y, delta.X);
double thetaDegree = RadianToDegree(theta);
// Given cartesian coordinate system
// positive y is up, negative is down
// positive x is right, negative is left
// Falsely assuming up is 0
// Falsely assuming right is 90
// Falsely assuming down is 180
// Falsely assuming left is 270
Vector2 v0 = new Vector2(0, 1);
Vector2 v45 = new Vector2(0.5f, 0.5f);
Vector2 v90 = new Vector2(0.5f, 0);
Vector2 v180 = new Vector2(0, -1);
Vector2 v270 = new Vector2(-1, 0);
double theta0 = Math.Atan2(v0.Y, v0.X);
double theta45 = Math.Atan2(v45.Y, v45.X);
double theta90 = Math.Atan2(v90.Y, v90.X);
double theta180 = Math.Atan2(v180.Y, v180.X);
double theta270 = Math.Atan2(v270.Y, v270.X);
double result0 = RadianToDegree(theta0);
double result45 = RadianToDegree(theta45);
double resultv90 = RadianToDegree(theta90);
double resultv180 = RadianToDegree(theta180);
double resultv270 = RadianToDegree(theta270);
// result 0 --> 90
// result 45 --> 45
// result 90 --> 0
// result 180 --> 270
// result 270 --> 180
}
Related
I have this formula to rotate around a sphere
double naX = o.Node.Angle.X;
double naY = o.Node.Angle.Y;
double x = o.DrawingPosition.X - 4.0 * Math.Cos(naX) * Math.Sin(naY);
double y = o.DrawingPosition.Y - 4.0 * Math.Sin(naX) * Math.Sin(naY);
double z = o.DrawingPosition.Z - 4.0 * Math.Cos(naY);
Where 4.0 is the radius to follow and o.DrawingPosition is the center
I want it to rotate along the transform x axis (I have a quaternion and a unit vector normalized for calculated for the Z -1 normal) but if I add offsets the angles won't match
naX += _rotationTicks;
naY += _rotationTicks;
For example, it will follow a infinite shaped trajectory, how can I calculate the correct rotation so it behaves like a perfect circle?
Edit:
I found this answer on Rotating body from spherical coordinates
But the main core difference is that both the XY rotation angles and the origins are arbitrary values, there is a forward vector calculated with a quaternion to determine facing like this:
var quat = System.Numerics.Quaternion.CreateFromYawPitchRoll((float)o.Node.Angle.X, (float)o.Node.Angle.Y, 0);
var dirVec = new Vector3(0, -1, 0).ToNumerics();
var downwards = quat.Multiply(dirVec); // it can be backwards, left, right, etc, changing the unit vector from dirVec
Thanks in advance.
I am trying to make a space game, but I don't know how to rotate my spacecraft to a specific point in vector3 whith addtorque.
For example, to kill the velocity, my current script to calculate the trajectory is this
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class CalculateTrajectory : MonoBehaviour
{
Vector3 Direction;
// Start is called before the first frame update
void Start()
{
Rigidbody rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate()
{
Rigidbody rb = GetComponent<Rigidbody>();
Vector3 trajectory = rb.velocity; // Velocity of gameObject (Vector3)
Vector3 NextPosition = transform.position + trajectory;
Vector3 Direction = (NextPosition - transform.position) * 1000;
Debug.DrawRay(transform.position, Direction, Color.green, 0.2f);
}
}
(sorry for my bad English I'm a 14 year old student from Germany)
You need to find the axis of rotation to get from the current heading (eg, transform.forward to the desired heading (rb.velocity if I correctly understand your goal). This can be found using the cross product:
// Note: will be scaled by the sine of the angle between the two vectors
var axis = Vector3.Cross (transform.forward, rb.velocity.normalized);
This can then be scaled by the desired torque which is then applied to the rigid body via AddTorque to give a proportional feedback. That is, this acts much like a spring. However, this will get weaker for angles > 90 degrees (see below)
var torque = axis * torqueFactor;
You might want to factor in the rigid body's angular velocity to include some damping. This can be done by subtracting the rigid body's angular velocity vector (scaled by some factor).
torque -= rb.angularVelocity * dampingFactor;
Note that finding good values for torqueFactor and dampingFactor can take some tweaking, and depends on the rigid body's moment of inertia (combination of mass and shape).
The problem with angles > 90 degrees:
When the angle is between 90 degrees and 180, the torque will drop off again as the angle approaches 180 degrees. This can be handled by computing the sine of the half angle, which can be done directly from the vectors:
// returns the sin(half angle between a and b).
// Loses the sign (thus direction) of the angle.
float HalfSin(Vector3 a, Vector3 b)
{
a = a.normalized;
b = b.normalized;
// (a - b).magnitude gives 2*sin(angle/2)
// using + will give 2*cos(angle/2)
return (a - b).magnitude * 0.5f;
}
This can then be used to scale the normalized result of the cross product of the two angles to get both direction and magnitude of the half-angle, and will be 0 at 0 degrees and 1 at 180 degrees (or would be if not for issues with normalizing the 0-vectors resulting from the cross product of parallel vectors).
Using the above ideas, this is my replacement for unity's FromToRotation (because it falls apart for very small angles due the normalization problem):
Quaternion fromtorot(Vector3 a, Vector3 b)
{
float ma = a.magnitude;
float mb = b.magnitude;
Vector3 mb_a = mb * a;
Vector3 ma_b = ma * b;
float den = 2 * ma * mb;
float mba_mab = (mb_a + ma_b).magnitude;
float c = mba_mab / den; // cosine of half angle
// find the rotation axis scaled by the sine of the half angle (s)
// using |a x b| = sin(angle) |a| |b| = 2 c s |a| |b|
// where c and s are the cosine and sine of the half hangle
// and mba_mab is 2 c |a| |b|
// c is not 0 until 180 degrees (vectors are anti-parallel)
Vector3 v = Vector3.Cross (a, b) / mba_mab;
return new Quaternion(v.x, v.y, v.z, c);
}
It behaves very well near 0 degrees, but does fall apart near 180 degrees, but that is to be expected: the axis of rotation is not defined. An undefined rotation axis is not a problem at 0 degrees, but is a very big problem at 180 degrees (try turning a book by 180 degrees about the X, Y and Z axes, or anything in between: very different results).
Example Image here
I am trying to find a way to calculate points on my cylinders top circle surface. My situation looks like this, I have a vector which is defining my cylinders direction in 3d room. Then I already calculated me a perpendicular vector with
Vector3.Cross(vector1, vector2)
Now I use the diameter/2 to calculate the point which is lying on the edge of the circular top surface of my cylinder. Now I want to rotate my vector always 90 degrees in order to get 4 points on the edge of the surface. All the 4 vectors defining them should be perpendicular to the cylinders direction. Can you help me how I can rotate the first perpendicular to achieve this?
I already tried:
Matrix4x4.CreateFromAxisAngle(vectorcylinderdirection, radiant)
Then I calculated again cross product but it doesnt work like I want to.
Edit:
public static void calculatePontsOnCylinder()
{
//Calculate Orthogonal Vector to Direction
Vector3 tCylinderDirection = new Vector3(1, 0, 0);
Vector3 tOrthogonal = Vector3.Cross(tCylinderDirection, new Vector3(-tCylinderDirection.Z,tCylinderDirection.X,tCylinderDirection.Y));
Vector3 tNormOrthogonal = Vector3.Normalize(tOrthogonal);
//Calculate point on surface circle of cylinder
//10mm radius
int tRadius = 10;
Vector3 tPointFinder = tNormOrthogonal * tRadius;
//tPointFinder add the cylinder start point
//not yet implemented
//now i need to rotate the vector always 90 degrees to find the 3 other points on the circular top surface of the cylinder
//don't know how to do this
// I thought this should do it
Matrix4x4.CreateFromAxisAngle(tCylinderDirection, (float)DegreeToRadian(90));
}
private static double DegreeToRadian(double angle)
{
return Math.PI * angle / 180.0;
}
In the picture you can see a example, the vector1 is what I need, always rotated 90 degrees and vector2 would be my cylinder direction vector
I possibly have found the correct formula:
Vector3 tFinal = Vector3.Multiply((float)Math.Cos(DegreeToRadian(90)), tPointFinder) + Vector3.Multiply((float)Math.Sin(DegreeToRadian(90)), Vector3.Cross(tCylinderDirection, tPointFinder));
Vector3 tFinal180 = Vector3.Multiply((float)Math.Cos(DegreeToRadian(180)), tPointFinder) + Vector3.Multiply((float)Math.Sin(DegreeToRadian(180)), Vector3.Cross(tCylinderDirection, tPointFinder));
Vector3 tFinal270= Vector3.Multiply((float)Math.Cos(DegreeToRadian(270)), tPointFinder) + Vector3.Multiply((float)Math.Sin(DegreeToRadian(270)), Vector3.Cross(tCylinderDirection, tPointFinder));
Interesting is that if I try it with (1,1,0) as cylinder direction it gives me correct directions but the length is different for 90 degrees and 270.
Here is the code that should solve your problem assuming that the input requirements are satisfied.
float zCutPlaneLocation = 20; // should not get bigger than cylinder length
float cylinderRadius = 100;
Vector3 cylinderCenter = new Vector3(0, 0, 0); // or whatever you got as cylinder center point, given as Vector3 since Point type is not defined
// will return 360 points on cylinder edge, corresponding to this z section (cut plane),
// another z section will give another 360 points and so on
List<Vector3> cylinderRotatedPointsIn3D = new List<Vector3>();
for (int angleToRotate = 0; angleToRotate < 360; angleToRotate++)
{
cylinderRotatedPointsIn3D.Add(GetRotatedPoint(zCutPlaneLocation, angleToRotate, cylinderRadius, cylinderCenter));
}
....
private static Vector3 GetRotatedPoint(
float zLocation, double rotationAngleInRadian, float cylinderRadius, Vector3 cylinderCenter)
{
Vector2 cylinderCenterInSection = new Vector2(cylinderCenter.X, cylinderCenter.Y);
float xOfRotatedPoint = cylinderRadius * (float)Math.Cos(rotationAngleInRadian);
float yOfRotatedPoint = cylinderRadius * (float)Math.Sin(rotationAngleInRadian);
Vector2 rotatedVector = new Vector2(xOfRotatedPoint, yOfRotatedPoint);
Vector2 rotatedSectionPointOnCylinder = rotatedVector + cylinderCenterInSection;
Vector3 rotatedPointOnCylinderIn3D = new Vector3(
rotatedSectionPointOnCylinder.X,
rotatedSectionPointOnCylinder.Y,
zLocation + cylinderCenter.Z);
return rotatedPointOnCylinderIn3D;
}
I just created a console app for this. First part of code should be added in main method.
Working with those matrices seems is not that easy. Also I am not sure if your solution works ok for any kind of angle.
Here the idea is that the rotated points from cylinder are calculated in a section of the cylinder so in 2D than the result is moved in 3D by just adding the z where the Z section was made on cylinder. I suppose that world axis and cylinder axis are on the same directions. Also if your cylinder gets along (increases) on the X axis, instead of Z axis as in example just switch in code the Z with X.
I attached also a picture for more details. This should work if you have the cylinder center, radius, rotation angle and you know the length of the cylinder so that you create valid Z sections on cylinder. This could get tricky for clockwise/counter clock wise cases but lets see how it works for you.
If you want to handle this with matrices or whatever else I think that you will end up having this kind of result. So I think that you cannot have "all" the rotated points in just a list for the entire cylinder surface, they would depend on something like the rotated points of a Z section on the cylinder.
I have two 3d vectors containing position data for two objects within my game. One of the objects needs to turn its camera directly to the other based on the current positions (the setting of the pitch/yaw are absolute so realistically I can ignore those two variables). What I need help with is the math, honestly I just can't find anything about doing this properly.
//An example in pseudocode
//(x, y, z)
Vector3 mychar = new Vector3(100, 100, 100);
Vector3 explosion = new Vector3(555, 1000, 300);
//(pitch, yaw)
Vector2 myRotation = new Vector2(0, 0); //Can assume this is always the case because the setting of rotation is absolute rather than relative
Vector2 expRotation = new Vector2(35, 14);
//The math I cant seem to figure out would be in this function
Vector2 newRotationj = GetRotationBetween(myRotation, mychar, explosion);
Here's what I have so far but I'm getting problems with the Y rotations (its the "up" direction and the horizon is at 0 degrees) Randomly my character will look in the mirrored direction in y (IE if the object is -20 below horizon it does +20) however it's only sometimes and I dont see where my math is wrong.
float dx = posIt.x - posMe.x;
float dy = posIt.y - posMe.y;
float dz = posIt.z - posMe.z;
yaw = Convert.ToSingle(Math.Atan(dx / dz) * 180 / Math.PI);
pitch = Convert.ToSingle(Math.Atan(dy / dz) * 180 / Math.PI);
It's been 10 years since I did any math like this... I am programming a game in 2D and moving a player around. As I move the player around I am trying to calculate the point on a circle 200 pixels away from the player position given a positive OR negative angle(degree) between -360 to 360. The screen is 1280x720 with 0,0 being the center point of the screen. The player moves around this entire Cartesian coordinate system. The point I am trying trying to find can be off screen.
I tried the formulas on article Find the point with radius and angle but I don't believe I am understanding what "Angle" is because I am getting weird results when I pass Angle as -360 to 360 into a Cos(angle) or Sin(angle).
So for example I have...
1280x720 on a Cartesian plane
Center Point (the position of player):
let x = a number between minimum -640 to maximum 640
let y = a number between minimum -360 to maximum 360
Radius of Circle around the player: let r always = 200
Angle: let a = a number given between -360 to 360 (allow negative to point downward or positive to point upward so -10 and 350 would give same answer)
What is the formula to return X on the circle?
What is the formula to return Y on the circle?
The simple equations from your link give the X and Y coordinates of the point on the circle relative to the center of the circle.
X = r * cosine(angle)
Y = r * sine(angle)
This tells you how far the point is offset from the center of the circle. Since you have the coordinates of the center (Cx, Cy), simply add the calculated offset.
The coordinates of the point on the circle are:
X = Cx + (r * cosine(angle))
Y = Cy + (r * sine(angle))
You should post the code you are using. That would help identify the problem exactly.
However, since you mentioned measuring your angle in terms of -360 to 360, you are probably using the incorrect units for your math library. Most implementations of trigonometry functions use radians for their input. And if you use degrees instead...your answers will be weirdly wrong.
x_oncircle = x_origin + 200 * cos (degrees * pi / 180)
y_oncircle = y_origin + 200 * sin (degrees * pi / 180)
Note that you might also run into circumstance where the quadrant is not what you'd expect. This can fixed by carefully selecting where angle zero is, or by manually checking the quadrant you expect and applying your own signs to the result values.
I highly suggest using matrices for this type of manipulations. It is the most generic approach, see example below:
// The center point of rotation
var centerPoint = new Point(0, 0);
// Factory method creating the matrix
var matrix = new RotateTransform(angleInDegrees, centerPoint.X, centerPoint.Y).Value;
// The point to rotate
var point = new Point(100, 0);
// Applying the transform that results in a rotated point
Point rotated = Point.Multiply(point, matrix);
Side note, the convention is to measure the angle counter clockwise starting form (positive) X-axis
I am getting weird results when I pass Angle as -360 to 360 into a Cos(angle) or Sin(angle).
I think the reason your attempt did not work is that you were passing angles in degrees. The sin and cos trigonometric functions expect angles expressed in radians, so the numbers should be from 0 to 2*M_PI. For d degrees you pass M_PI*d/180.0. M_PI is a constant defined in math.h header.
I also needed this to form the movement of the hands of a clock in code. I tried several formulas but they didn't work, so this is what I came up with:
motion - clockwise
points - every 6 degrees (because 360 degrees divided by 60 minuites is 6 degrees)
hand length - 65 pixels
center - x=75,y=75
So the formula would be
x=Cx+(r*cos(d/(180/PI))
y=Cy+(r*sin(d/(180/PI))
where x and y are the points on the circumference of a circle, Cx and Cy are the x,y coordinates of the center, r is the radius, and d is the amount of degrees.
Here is the c# implementation. The method will return the circular points which takes radius, center and angle interval as parameter. Angle is passed as Radian.
public static List<PointF> getCircularPoints(double radius, PointF center, double angleInterval)
{
List<PointF> points = new List<PointF>();
for (double interval = angleInterval; interval < 2 * Math.PI; interval += angleInterval)
{
double X = center.X + (radius * Math.Cos(interval));
double Y = center.Y + (radius * Math.Sin(interval));
points.Add(new PointF((float)X, (float)Y));
}
return points;
}
and the calling example:
List<PointF> LEPoints = getCircularPoints(10.0f, new PointF(100.0f, 100.0f), Math.PI / 6.0f);
The answer should be exactly opposite.
X = Xc + rSin(angle)
Y = Yc + rCos(angle)
where Xc and Yc are circle's center coordinates and r is the radius.
Recommend:
public static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Vector3 angles)
{
return Quaternion.Euler(angles) * (point - pivot) + pivot;
}
You can use this:
Equation of circle
where
(x-k)2+(y-v)2=R2
where k and v is constant and R is radius