I made a game where planets orbit clockwise (More of a simulation). I'm wondering how I would make it go counter clockwise.
Host = the object the planet is orbiting
entity = planet
double angle;
if (host.Y == entity.Y && host.X == entity.X) //small offset
angle = Math.Atan2(host.Y - entity.Y + (Random.NextDouble()*2 - 1),
host.X - entity.X + (Random.NextDouble()*2 - 1));
else
angle = Math.Atan2(host.Y - entity.Y, host.X - entity.X);
float angularSpd = host.GetSpeed(s.Speed)/s.Radius;
angle += angularSpd*(time.thisTickTimes/1000f);
double x = entity.X + Math.Cos(angle)*radius;
double y = entity.Y + Math.Sin(angle)*radius;
Vector2 vect = new Vector2((float) x, (float) y) - new Vector2(host.X, host.Y);
vect.Normalize();
vect *= host.GetSpeed(s.Speed)*(time.thisTickTimes/1000f);
host.MoveTo(host.X + vect.X, host.Y + vect.Y);
Try changing the angle += angularSpd... to angle -= angularSpd...
Related
I have a functioning script that creates an arc of waypoints for a cinemachine track between 2 gameobject(vector3's), I'd like to add a variable so that I can control the height of the arc as seen in this pic:
At the moment the method I have creates the arc based on a radius of the distance between the 2 points, I'd like to be able to control the height of the arc so that it calculates the points similar to the drawn line in the pic. And to control where in the arc that maximum height occurs in relationship to the distance between posA and posB.
Here's the code I have for creating the arc as it is at the moment, which calculates the arc with a radius of half the distance between the 2 points:
static void CalcWaypoints(CinemachineSmoothPath path, Vector3 posA, Vector3 posB)
{
int greenRingPointsNum = 8;
float metersPerWaypoint = 10;
//Here we calculate how many segments will fit between the two points
int segmentsToCreate = Mathf.RoundToInt(Vector3.Distance(posA, posB) / metersPerWaypoint);
path.m_Waypoints = new CinemachineSmoothPath.Waypoint[segmentsToCreate + greenRingPointsNum];
Debug.Log("Creating " + segmentsToCreate + " waypoints");
// get circle center and radius
var radius = Vector3.Distance(posA, posB) / 2f;
var centerPos = (posA + posB) / 2f;
// get a rotation that looks in the direction of the target gameobject
var centerDirection = Quaternion.LookRotation((posA - posB).normalized);
for (var i = 0; i < segmentsToCreate; i++)
{
var angle = Mathf.PI * (i) / (segmentsToCreate + 1f);
var y = Mathf.Sin(angle) * radius;
var z = Mathf.Cos(angle) * radius;
var pos = new Vector3(0, y, z);
// Rotate the pos vector according to the centerDirection
pos = centerDirection * pos;
path.m_Waypoints[i] = new CinemachineSmoothPath.Waypoint();
path.m_Waypoints[i].position = centerPos + pos;
}
//create a circle of points around the target gameobject at a give radius
float greenRadius = 20f;
int waypointNum = segmentsToCreate;
for (int i = 0; i < greenRingPointsNum; i++)
{
float angle = i * Mathf.PI * 2f / greenRingPointsNum;
Vector3 newPos = posB + new Vector3(Mathf.Cos(angle) * greenRadius, posB.y, Mathf.Sin(angle) * greenRadius);
path.m_Waypoints[waypointNum] = new CinemachineSmoothPath.Waypoint();
path.m_Waypoints[waypointNum].position = newPos;
waypointNum++;
}
}
Hope one of you great folks can help out :)
An Arc like the following pic, where I can control the height and it's distance between the 2 points:
The easiest way to do this would probably be by dynamically making Bézier curves and then following them.
First, we define what direction the apex should be in, and find the apex:
float apexHeightFromA = 5f; // apex is 5 world units above A
float apexDistanceFactor = 0.5f; // apex is halfway from A to B
Vector3 upDirection = Vector3.up; // direction apex is in relative to points
Vector3 aToB = posB - posA;
Vector3 flatAToB = Vector3.ProjectOnPlane(aToB, upDirection);
Vector3 posApex = posA
+ flatAToB * apexDistanceFactor
+ apexHeightFromA * upDirection;
Now, the Bézier curves can be defined. If we use two cubic Bézier curves, we will need two control points, one on either side of the apex. That is to say, one going toward point A and one going toward point B.
Vector3 controlPointApexA;
Vector3 controlPointApexB;
It's arbitrary how to define these. A good starting point might be going horizontally halfway to the end each control point is towards.
Vector3 apexToA = posA - posApex;
Vector3 apexToB = posB - posApex;
float controlPointDistanceFactor = 0.5f;
controlPointA = posApex
+ controlPointDistanceFactor * Vector3.ProjectOnPlane(apexToA, upDirection);
controlPointB = posApex
+ controlPointDistanceFactor * Vector3.ProjectOnPlane(apexToB, upDirection);
However we define the control points, we can proceed with the iteration along the curve.
First, we need to determine how many waypoints should come before vs after the apex. A reasonable assumption is for it to be reached according to its position between the points.
float apexTravelFactor = apexDistanceFactor;
Then, we can use the formula for a quadratic Bézier curve...
... from A to apex or from apex to B depending on where we are in the curve.:
// cache for efficiency
Vector3 controlAToA = posA - controlPointA;
Vector3 controlBToB = posB - controlPointB;
Vector3 controlAToApex = posApex - controlPointA;
Vector3 controlBToApex = posApex - controlPointB;
for (var i = 0; i < segmentsToCreate; i++)
{
float overallT = (float)i / segmentsToCreate;
Vector3 control, controlToOrigin, controlToDest;
float t;
// are we going from a to apex or apex to b?
if (overallT < apexTravelFactor)
{
// going from a to apex
control = controlPointA;
controlToOrigin = controlAToA;
controlToDest = controlAToApex;
t = overallT / apexTravelFactor;
}
else
{
// going from apex to b
control = controlPointB;
controlToOrigin = controlBToApex;
controlToDest = controlBToB;
t = (overallT - apexTravelFactor) / (1f - apexTravelFactor);
}
Vector3 currentPos = control
+ Mathf.Pow(1f - t, 2f) * controlToOrigin
+ Mathf.Pow(t, 2f) * controlToDest;
path.m_Waypoints[i] = new CinemachineSmoothPath.Waypoint();
path.m_Waypoints[i].position = currentPos;
}
Altogether, moving constants & such to the top:
static void CalcWaypoints(CinemachineSmoothPath path, Vector3 posA, Vector3 posB)
{
//////////////////////////////////////////////
// CONSTANTS & FACTORS
// good candidates for [Serializable] fields
// in a singleton and/or if this were not static
//////////////////////////////////////////////
int greenRingPointsNum = 8;
float metersPerWaypoint = 10;
float apexHeightFromA = 5f; // apex is 5 world units above A
float apexDistanceFactor = 0.5f; // apex is halfway from A to B
Vector3 upDirection = Vector3.up; // direction apex is in relative to points
// can fiddle with to change "thickness" of curve
float controlPointDistanceFactor = 0.5f;
// can fiddle with to change how many waypoints come before vs after the apex
float apexTravelFactor = apexDistanceFactor;
////////
// LOGIC
////////
Vector3 aToB = posB - posA;
//Here we calculate how many segments will fit between the two points
int segmentsToCreate = Mathf.RoundToInt(aToB.magnitude / metersPerWaypoint);
path.m_Waypoints = new CinemachineSmoothPath.Waypoint[segmentsToCreate
+ greenRingPointsNum];
Debug.Log("Creating " + segmentsToCreate + " waypoints");
Vector3 flatAToB = Vector3.ProjectOnPlane(aToB, upDirection);
Vector3 posApex = posA
+ flatAToB * apexDistanceFactor
+ apexHeightFromA * upDirection;
Vector3 apexToA = posA - posApex;
Vector3 apexToB = posB - posApex;
Vector3 controlPointA = posApex
+ controlPointDistanceFactor
* Vector3.ProjectOnPlane(apexToA, upDirection);
Vector3 controlPointB = posApex
+ controlPointDistanceFactor
* Vector3.ProjectOnPlane(apexToB, upDirection);
Vector3 controlAToA = posA - controlPointA;
Vector3 controlBToB = posB - controlPointB;
Vector3 controlAToApex = posApex - controlPointA;
Vector3 controlBToApex = posApex - controlPointB;
for (var i = 0; i < segmentsToCreate; i++)
{
float overallT = (float)i / segmentsToCreate;
// if you want to have waypoint at posB:
// float overallT = (float)i / (segmentsToCreate-1);
Vector3 control, controlToOrigin, controlToDest;
float t;
// are we going from a to apex or apex to b?
if (overallT < apexTravelFactor)
{
// going from a to apex
control = controlPointA;
controlToOrigin = controlAToA;
controlToDest = controlAToApex;
t = overallT / apexTravelFactor;
}
else
{
// going from apex to b
control = controlPointB;
controlToOrigin = controlBToApex;
controlToDest = controlBToB;
t = (overallT - apexTravelFactor) / (1f - apexTravelFactor);
}
Vector3 currentPos = control
+ Mathf.Pow(1f - t, 2f) * controlToOrigin
+ Mathf.Pow(t, 2f) * controlToDest;
path.m_Waypoints[i] = new CinemachineSmoothPath.Waypoint();
path.m_Waypoints[i].position = currentPos;
}
//create a circle of points around the target gameobject at a give radius
float greenRadius = 20f;
int waypointNum = segmentsToCreate;
for (int i = 0; i < greenRingPointsNum; i++)
{
float angle = i * Mathf.PI * 2f / greenRingPointsNum;
Vector3 newPos = posB + new Vector3(Mathf.Cos(angle) * greenRadius, posB.y,
Mathf.Sin(angle) * greenRadius);
path.m_Waypoints[waypointNum] = new CinemachineSmoothPath.Waypoint();
path.m_Waypoints[waypointNum].position = newPos;
waypointNum++;
}
}
For funzies, try setting a negative value for apexHeightFromA, and/or a value that would put the "apex" between the "heights" of posA and posB. You'll see that it should still look ok, although it certainly won't be an "apex" anymore ;)
If you want to preview the waypoints, see below:
public class TestScript : MonoBehaviour
{
[SerializeField]
float apexHeightFromA = 5f; // apex is 5 world units above A
[SerializeField] float apexDistanceFactor = 0.5f; // apex is halfway from A to B
[SerializeField] Vector3 upDirection = Vector3.up; // direction apex is in relative to points
[SerializeField] Vector3 posA;
[SerializeField] Vector3 posB;
private void OnDrawGizmos()
{
CalcWaypoints();
}
void CalcWaypoints()
{
//////////////////////////////////////////////
// CONSTANTS & FACTORS
// good candidates for [Serializable] fields
// in a singleton and/or if this were not static
//////////////////////////////////////////////
// animate apex distance factor
apexDistanceFactor = Mathf.PingPong(Time.time * 0.3f, .8f) + 0.1f;
float metersPerWaypoint = 10;
// can fiddle with to change "thickness" of curve
float controlPointDistanceFactor = 0.5f;
// can fiddle with to change how many waypoints come before vs after the apex
float apexTravelFactor = apexDistanceFactor;
////////
// LOGIC
////////
Vector3 aToB = posB - posA;
//Here we calculate how many segments will fit between the two points
int segmentsToCreate = Mathf.RoundToInt(aToB.magnitude / metersPerWaypoint);
Debug.Log("Creating " + segmentsToCreate + " waypoints");
Vector3 flatAToB = Vector3.ProjectOnPlane(aToB, upDirection);
Vector3 posApex = posA
+ flatAToB * apexDistanceFactor
+ apexHeightFromA * upDirection;
Vector3 apexToA = posA - posApex;
Vector3 apexToB = posB - posApex;
Vector3 controlPointA = posApex
+ controlPointDistanceFactor
* Vector3.ProjectOnPlane(apexToA, upDirection);
Vector3 controlPointB = posApex
+ controlPointDistanceFactor
* Vector3.ProjectOnPlane(apexToB, upDirection);
Vector3 controlAToA = posA - controlPointA;
Vector3 controlBToB = posB - controlPointB;
Vector3 controlAToApex = posApex - controlPointA;
Vector3 controlBToApex = posApex - controlPointB;
for (var i = 0; i < segmentsToCreate; i++)
{
float overallT = (float)i / (segmentsToCreate + 1);
// if you want to have waypoint at posB:
// float overallT = (float)i / (segmentsToCreate-1);
Vector3 control, controlToOrigin, controlToDest;
float t;
// are we going from a to apex or apex to b?
if (overallT < apexTravelFactor)
{
// going from a to apex
control = controlPointA;
controlToOrigin = controlAToA;
controlToDest = controlAToApex;
t = overallT / apexTravelFactor;
}
else
{
// going from apex to b
control = controlPointB;
controlToOrigin = controlBToApex;
controlToDest = controlBToB;
t = (overallT - apexTravelFactor) / (1f - apexTravelFactor);
}
Vector3 currentPos = control
+ Mathf.Pow(1f - t, 2f) * controlToOrigin
+ Mathf.Pow(t, 2f) * controlToDest;
Gizmos.DrawSphere(currentPos, 1f);
}
}
}
So, Dani in his slightly new video -> "Making a Game, But I Only Have 3 Days" (https://youtu.be/S7Dl6ATRK2M) made a enemy which has a bow and arrow (at 5:39). I tried to recreate that but had no luck... I also can't find the website that he used... Today I found this https://physics.stackexchange.com/questions/56265/how-to-get-the-angle-needed-for-a-projectile-to-pass-through-a-given-point-for-t. It worked very well but still had problems if the target was far away and also it wasn't as accurate. The code so far is
float CalculateAngle()
{
float gravity = Physics.gravity.magnitude;
float deltaX = targetPositionMod.x - currentPosition.x;
float deltaY = targetPositionMod.y - currentPosition.y;
float RHSFirstPart = (velocity * velocity) / (gravity * deltaX);
float RHSSecondPart = Mathf.Sqrt(
((velocity * velocity) * ((velocity * velocity) - (2 * gravity * deltaY))
/ (gravity * gravity * deltaX * deltaX))
- 1);
float tanθ = RHSFirstPart - RHSSecondPart;
float angle = Mathf.Atan2(tanθ, 1) * Mathf.Rad2Deg;
if (angle < 0) return angle;
return -angle;
}
The -angle is because the forward axis starts points up when the x-rotation is negative (Unity). Maybe the reason of this not working as intended is that I am not that good at this kind of Physics (Part of that is me being only 14). Maybe the problem is in the code, maybe it is the formula. Any help is appreciated.
Thanks...
Edit:
The Archer class is:
using UnityEngine;
using System;
public class Archer : MonoBehaviour
{
[SerializeField] float velocity = default;
[SerializeField] Transform target = default;
[SerializeField] GameObject arrowPrefab = default;
[SerializeField] float coolDown = default;
Vector3 targetPositionMod;
Vector3 currentPosition;
Vector3 targetPosition;
float countDown = 0f;
void Start()
{
countDown = coolDown;
UpdateVariables();
}
void Update()
{
UpdateVariables();
SetAngle();
ShootBullet();
}
void UpdateVariables()
{
currentPosition = transform.position;
targetPositionMod = Mod(target.position);
targetPosition = target.position;
targetPosition.x /= 10;
targetPosition.y /= 10;
targetPosition.z /= 10;
countDown -= Time.deltaTime;
}
void SetAngle()
{
Vector3 direction = targetPosition - currentPosition;
Quaternion lookRotation = Quaternion.LookRotation(direction);
Vector3 rotation = lookRotation.eulerAngles;
rotation.x = (float) CalculateAngle();
transform.rotation = Quaternion.Euler(rotation.x, rotation.y, 0f);
}
void ShootBullet()
{
if (!(countDown <= 0f)) return;
countDown = coolDown;
GameObject arrow = Instantiate(arrowPrefab, transform.position, transform.rotation);
Rigidbody Rigidbody = arrow.GetComponent<Rigidbody>();
Rigidbody.AddForce(transform.forward * velocity, ForceMode.Impulse);
}
double CalculateAngle()
{
double gravity = Physics.gravity.magnitude;
double deltaX = targetPositionMod.x - currentPosition.x;
double deltaY = targetPositionMod.y - currentPosition.y;
double RHSFirstPart = (velocity * velocity) / (gravity * deltaX);
double RHSSecondPart = Math.Sqrt(
(((velocity * velocity) * ((velocity * velocity) - (2 * gravity * deltaY))
/ (gravity * gravity * deltaX * deltaX))
- 1));
double tanθ = RHSFirstPart - RHSSecondPart;
double angle = Math.Atan2(tanθ, 1) * Mathf.Rad2Deg;
if (angle < 0) return angle;
return -angle;
}
Vector3 Mod(Vector3 Vec)
{
if (Vec.x < 0) Vec.x -= 2 * Vec.x;
if (Vec.y < 0) Vec.y -= 2 * Vec.y;
if (Vec.z < 0) Vec.z -= 2 * Vec.z;
Vec.x /= 10;
Vec.y /= 10;
Vec.z /= 10;
return Vec;
}
}
Ok, as I can see, your implementation of formula from StackExchange is right, but you have to remember two things:
In unity there is a 3D world, so horizontal distance is not just pos1.x - pos2.x, but
Mathf.Sqrt( deltaX * deltaX + deltaZ * deltaZ ), where deltaX = targetPositionMod.x - currentPosition.x and deltaZ = targetPositionMod.z - currentPosition.z
In computer implementation you have no 100% accuracy of math, so some problems can appear because of computational accuracy. And it can have affect on big distances. You can try to use double instead of float or find another implementation for arctangent function (I think, this can really help). But try this (second) advice only if first didn't help. It's harder to implement and it slows computations a bit.
Algorithm:
Step 1: Set up a function that calculates the appropriate solution of a quadratic equation
a*x^2 + b*x + c = 0
double quadratic_root(a,b,c){
D = b^2 - 4*a*c
return ( - b - Math.Sqrt(D) ) / (2 * a)
}
Step 2: Input
current.x
current.y
current.z
target.x
target.y
target.z
velocity
gravity
Step 3: Calculate coefficients of the quadratic polynomial:
dist = Math.Sqrt( (target.x - current.x)^2 + (target.y - current.y)^2 )
a = gravity * dist^2 / (2 * velocity^2)
b = -dist
c = target.z - current.z + a
Step 4:
theta = Math.Atan2( quadratic_root(a,b,c), 1 )
Calculation behind the algorithm. You are in three space. The current position has coordinates
x = current.x
y = current.y
z = current.z
and the target has coordinates
x = target.x
y = target.y
z = target.z
Assume the angle between the initial velocity and the horizontal plane is theta. The magnitude of the projection of the distance between the current position and the target onto the horizontal $x,y-$plane is
dist = sqrt( (target.x - current.x)^2 - (target.y - current.y)^2 )
You are given the velocity magnitude velocity. Then, the speed with which the shadow (i.e. the orthogonal projection) of the arrow moves along the horizontal line between the source and the target is the magnitude of the shadow (i.e. the orthogonal projection) of the actual velocity
velocity * cos(theta)
The vertical speed of the arrow is then
velocity * sin(theta)
So the motion along dist follows the equation
dist = time * velocity * cos(theta)
and hence
time = dist / (velocity * cos(theta))
In the vertical direction, the motions is described by the equation
z = current.z + time * velocity * sin(theta) - time^2 * gravity / 2
You are interested in the time for which the arrow hits the target, which has vertical coordinate target.z, so
target.z = current.z + time * velocity * sin(theta) - time^2 * gravity / 2
The equation can be written as:
0 = - (target.z - current.z) + time * velocity * sin(theta) - time^2 * gravity / 2
We already know that
time = dist / (velocity * cos(theta))
so
0 = - (target.z - current.z) + dist * velocity * sin(theta) / (velocity * cos(theta)) - dist^2 * gravity / ( 2 * (velocity * cos(theta))^2 )
which can be slightly simplified to
0 = - (target.z - current.z) + dist * sin(theta) / cos(theta) - gravity * dist^2 / ( 2 * (velocity * cos(theta))^2 )
Because 1/( cos(theta)^2 ) = 1 + ( tan(theta) )^2 we obtain the quadratic in tan(theta) equation
a * ( tan(theta) )^2 + b * tan(theta) + c = 0
where
a = gravity * dist^2 / (2 * velocity^2)
b = - dist
c = target.z - current.z + a
Suppose I have a car in position P0 and I want to move it to position P1, just like these 4 examples:
The linear distance between P0 and P1 is d and the perpendicular maximum height the movement reaches is d/3. I want to simulate this clockwise semi-circular movement from P0 to P1.
Suppose dir = P1 - P0 (length d) and perp is the vector (of length d/3) perpendicular to dir.
Suppose t = 0 is the beginning of the semi-circular movement and t = 1 is the end, how can I measure the angle and the position of the car at t = i?
We have to find angle of this arc and circle center.
At first find radius.
R^2 = (d/2)^2 + (R-d/3)^2 //pythagorean
R = 13/24 * d
Now angle
half_angle = arcsin(12/13) ~ 67.4 degrees
angle = 2 * half_angle ~ 135 degrees = 2.35 radians
Normalize perp vector
uperp = perp / len(perp)
Get circle center
M = (P0 + P1)/2 //chord middle
C = M + uperp * 5/24 * d
Starting angle
A0 = atan2(P0.Y-C.Y, P0.X-C.X)
And finally coordinates
Car.X = C.X + R * Cos(A0 + t * angle)
Car.Y = C.Y + R * Sin(A0 + t * angle)
In Unity this would look like:
Vector2 startPosition;
Vector2 endPosition;
Vector2 perp;
float t;
float d = (endPosition - startPosition).magnitude;
float radius = 13f/24f * d;
float angle = 2f * Mathf.Asin(12f/13f);
Vector2 uperp = perp.normalized;
Vector2 M = (startPosition+endPosition)*0.5f;
Vector2 C = M + uperp * 5f/24f * d;
float A0 = Mathf.Atan2(startPosition.y-C.y, startPosition.x-C.x);
float At = A0 + t * angle;
Vector2 newPos = C + radius * new Vector2(Mathf.Cos(At), Mathf.Sin(At));
What is the best way to decelerate at a speed of any given value (e.g. accelerationDropOff = 1.5f) before it reaches the end destination?
public bool MoveFromCurrentToPosition(float x, float y, float velocity, float acceleration, float deltaTime)
{
float startX = positionX, startY = positionY;
float endX = x, endY = y;
float deltaX = endX - startX;
float deltaY = endY - startY;
float speed = velocity;
float elapsed = 0.01f;
// On starting movement
float distance = (float)Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
float directionX = deltaX / distance;
float directionY = deltaY / distance;
isMoving = true;
// On update
if (isMoving == true)
{
positionX += directionX * speed * elapsed;
positionY += directionY * speed * elapsed;
if (currentAcceleration == 0)
{
currentAcceleration = acceleration;
}
else if (currentAcceleration >= maxAcceleration) // <- Don't accelerate anymore
{
speed *= currentAcceleration;
positionX += (directionX * speed) * deltaTime; positionY += (directionY * speed) * deltaTime;
bounds.X = (int)positionX; bounds.Y = (int)positionY;
}
else
{
currentAcceleration += acceleration;
speed *= currentAcceleration;
positionX += (directionX * speed) * deltaTime; positionY += (directionY * speed) * deltaTime;
bounds.X = (int)positionX; bounds.Y = (int)positionY;
}
float a = x, o = y;
double angle = Math.Atan2(o, a);
angle = angle * 180 / Math.PI;
movementDirection = (float)(180 - angle);
// Decelerate before reaching the end point
if (Math.Sqrt(Math.Pow(positionX - startX, 2) + Math.Pow(positionY - startY, 2)) >= distance)
{
positionX = endX;
positionY = endY;
isMoving = false;
return true;
}
}
return false;
}
I have been stuck on this problem for a hour or two and Math.exe is not responding. Can anyone point me in the correct direction please?
It seems as if you are mixing up speed (velocity) and acceleration. Speed is the change of position with respect to a given time frame. Acceleration is the change of speed with respect to a given time frame. For a constant acceleration, position and velocity change as follows:
v1 = v0 + a * t
x1 = x0 + v0 * t + 1/2 * a * t^2
v0, v1 and x0, x1 are the velocities and positions at the beginning and end of the time frame, respectively, a is the acceleration, t is the time frame length. This is the exact formula if you assume constant acceleration over the period of the time frame. Often, you find approximations like the following, which introduce some integration errors:
v1 = v0 + a * t
x1 = x0 + v1 * t
I would suggest to use the exact formulas.
As far as I understand your question, you want to find an acceleration, such that a body moving at initial velocity v0 stops after travelling d length units.
This gives you the following equations:
0 = v0 + a * t //target velocity of 0
d = 0 + v0 * t + 1/2 * a * t^2 //travel distance of d
The solution is:
a = -1/2 * v0^2 / d
The time needed for this motion is:
t = 2 * d / v0
So calculate the acceleration once at the beginning of the deccelerating movement and then update current position and velocity with the formulas above.
Some additional hints for your code:
If you want to square a variable x, use x * x instead of Math.pow(x, 2). It is easier to read and has a better performance.
If you already use XNA, then use its Vector2 structure. This makes a lot of things much easier. You can just add two vectors and don't need to care about each component separately. There are methods to get the length of a vector, and so on.
I am rotating a circle with a mouse but I want there to be a limit on how far the circle can be rotated. (Lets say 3 full times). When it reaches it's limit it can no longer be turned in that same direction, but the opposite direction it can be. I got it stopping after the max turns but now I'm trying to find the direction and every time my mouse passes the x-axis of the circle the direction changes because atan2 gives me the angle relative to the x-axis. So the new mouse position is in one quadrant and the last position is in another quadrant so subtracting these angles doesn't give me what I want. Did I explain this well? Any suggestions?
private void HelmPb_MouseMove(object sender, MouseEventArgs e)
{
if ((e.Button != MouseButtons.Left) || _dontTurn) return;
double angle = OffsetAngle();
Point temp = MousePosition;
float degrees = Convert.ToSingle(angle - _offsetAngle);
float diff = _lastHelmAngle - degrees;
float absDiff = Math.Abs(diff) % 360;
if (absDiff > 180) absDiff = 360 - absDiff;
double angle1 = Math.Atan2(_lastHelmPoint.Y, _lastHelmPoint.X);
if (angle1 < 0) angle1 += 2*Math.PI;
double angle2 = Math.Atan2(temp.Y, temp.X);
if (angle2 < 0) angle2 += 2*Math.PI;
double direction = angle1 - angle2;
direction = direction*(180/Math.PI);
_deltaHelmTurn += Convert.ToSingle(absDiff);
if (_deltaHelmTurn >= (_maxHelmTurn*360.0))
{
if (direction > 0 && _lastHelmDirection > 0)
{
_deltaHelmTurn = Convert.ToSingle(_maxHelmTurn*360.0);
degrees = Convert.ToSingle(_maxHelmTurn*360.0)%360;
}
}
_lastHelmDirection = direction;
_lastHelmPoint = MousePosition;
_lastHelmAngle = Convert.ToSingle(degrees);
_sameHelmRotation = Convert.ToSingle(degrees);
HelmPb.Image.Dispose();
WaterDepthPlot.Update();
HelmPb.Image = RotateImage(_originalHelmImage, -degrees);
HelmPb.Update();
}
private double OffsetAngle()
{
int helmXMid = HelmPb.PointToScreen(Point.Empty).X + (HelmPb.Width / 2);
int helmYMid = HelmPb.PointToScreen(Point.Empty).Y + (HelmPb.Height / 2);
double angle = AngleFromPoints(MousePosition, new Point(helmXMid, helmYMid));
return angle;
}
private double AngleFromPoints(Point pt1, Point pt2)
{
Point p = new Point(pt1.X - pt2.X, pt1.Y - pt2.Y);
double alpha;
if (p.Y == 0) alpha = p.X > 0 ? 0d : 180d;
else
{
double f = 1d * p.X / (Math.Sqrt(p.X * p.X + p.Y * p.Y));
alpha = Math.Acos(f) * 180d / Math.PI;
if (p.Y > 0) alpha = 360d - alpha;
}
return alpha;
}
The direction of rotation between two vectors is equivalent to the sign of their cross product.
//Returns 1 for CCW, -1 for CW, 0 for no change
private int Direction(Point from, Point to)
{
double cross = (from.x * to.y) - (from.y * to.x);
return Math.Sign(cross);
}