I am moving an object in a parabolic arc this way:
public IEnumerator ParabolicMovement()
{
Vector3 startPos;
Vector3 targetPos;
float speed = 6;
float arcHeight = 3;
Vector3 nextPos = Vector3.zero;
while (transform.position != targetPos)
{
// Compute the next position, with arc added in
float x0 = startPos.x;
float x1 = targetPos.x;
float dist = x1 - x0;
float nextX = Mathf.MoveTowards(transform.position.x, x1, (speed) * Time.deltaTime);
float baseY = Mathf.Lerp(startPos.y, targetPos.y, (nextX - x0) / dist);
float arc = arcHeight * (nextX - x0) * (nextX - x1) / (-0.25f * dist * dist);
nextPos = new Vector3(nextX, baseY + arc, transform.position.z);
transform.position = nextPos;
yield return null;
}
}
This works, but I now want to take this and make it happen over a specific amount of time. I removed the loop from the original method and broke it up into two separate methods to accomplish this:
public IEnumerator BeginJumpOverTime()
{
float duration = 1f;
float startTime = Time.time;
float endTime = startTime + duration;
while (Time.time <= endTime)
{
float tNormalized = Mathf.Clamp((Time.time - startTime) / duration, 0f, 1f);
Vector2 newXAndY = CalculateXAndY(tNormalized);
transform.position = newXAndY;
yield return null;
}
}
public Vector2 CalculateXAndY(float t)
{
Vector3 startPos = GameEngine.Instance.battleManager.TurnHero.transform.position;
Vector3 targetPos = GameEngine.Instance.battleManager.TargetEnemy.transform.position;
float speed = 6;
float arcHeight = 3;
Vector3 nextPos = Vector3.zero;
// Compute the next position to make the parabola
float x0 = startPos.x;
float x1 = targetPos.x;
float dist = x1 - x0;
float nextX = Mathf.MoveTowards(transform.position.x, x1, (speed) * (Time.deltaTime) );
float baseY = Mathf.Lerp(startPos.y, targetPos.y, (nextX - x0) / dist);
float arc = arcHeight * (nextX - x0) * (nextX - x1) / (-0.25f * dist * dist);
nextPos = new Vector3(nextX, baseY + arc, transform.position.z);
return (nextPos);
}
I'm pretty certain this concept should work, I just can't seem to figure out where to factor tNormalized when it's passed into CalculateXandY(). Is anyone math savvy able to assist me with this? Thanks a ton!
Kind regards,
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);
}
}
}
I'm working on a script which rotates the camera diagonally (3D x & 3D y axis) around an player object. Inputs are following:
mouse z-axis for the y-axis rotation around the object
mouse x-axis for the diagonal "over-the-shoulder" rotation, thus modifying both y and x of camera rotation:
demo of my current camera script
It works, however, I can't get to clamp the y-axis rotation for the camera. X-axis works as wished. Watch the video above to see the problem at the end. It overrotates on the y axis so that the camera faces the player in the complete wrong direction. Maybe someone with a functioning brain can come up with a solution? Would be awesome as hell, thanks in advance!
Here's my script:
void Update() {
mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime;
mouseY = -(Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime);
rotationXAxis += mouseY;
rotationXAxis = ClampAngle(rotationXAxis, -30f, 30f);
float rotationYAxis = rotationXAxis;
rotationYAxis = ClampAngle(rotationYAxis, 0f, 30f);
Quaternion fromRotation = Quaternion.Euler(transform.rotation.eulerAngles.x, transform.rotation.eulerAngles.y, 0);
Quaternion toRotation = Quaternion.Euler(rotationXAxis, transform.rotation.eulerAngles.y - rotationYAxis * Time.deltaTime, 0);
Quaternion rotation = toRotation;
Vector3 negDistance = new Vector3(xPosOffset, yPosOffset, -distance);
Vector3 position = rotation * negDistance + player.position;
transform.rotation = rotation;
transform.position = position;
player.Rotate(Vector3.up * mouseX);
mouseY = Mathf.Lerp(mouseY, 0, Time.deltaTime);
}
float ClampAngle(float angle, float min, float max) {
if (angle < -360F)
angle += 360F;
if (angle > 360F)
angle -= 360F;
return Mathf.Clamp(angle, min, max);
}
I try to explain you in pseudo.
Sinus and cosinus are basicaly same but are offset by 90degrees.
What this function are producing is -1 .. 1 number what is -100% .. 100%
basicaly what you need is
var _x,_y,_z; //original position
var deg; // 0..360
x = _x * sin(deg);
y = _y * cos(deg);
z = _z;
This was rotate around world [0,0,0]
If you want to rotate around another axis
var a_x,a_y,a_z;
x = _x + a_x * sin(deg);
y = _y + a_y * cos(deg);
z = _z + a_z;
Biggest key is to preserve default location [_x,_y,_z]
Do not do this mistake
x = x * sin(deg);
Precisely it is a matrix
_x _y _z
x [cos -sin 0]
y [sin cos 0]
z [0 0 1]
x = _x * cos(deg) + _y * -sin(deg) + _z * 0;
y = _x * sin(deg) + _y * cos(deg) + _z * 0;
z = _x * 0 + _y * 0 + _z * 1;
Look at 3d matrix for your axis.
https://en.wikipedia.org/wiki/Rotation_matrix#In_three_dimensions
Try some thing like this
void Update(Player player) {
_position = player.position;
mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime;
mouseY = -(Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime);
_delta_x = mouseX - _position.x;
_delta_y = mouseY - _position.y;
_delta_z = 0;
_rot_x = ClampAngle(mouseX, mouseY, -30f, 30f);
_rot_y = ClampAngle(mouseX, mouseY, -30f, 30f);
_rot_z = 0;
Vector3 position = new Vector3(_delta_x, _delta_y, _delta_z);
position = position.rotate (rot_x, rot_y, rot_z);
position = position.add(_position.x,_position.y,_position.z);
player.position = position;
}
I'm currently making my Boss AI perform a jump attack against the player, the AI use both navmesh and character controller for movment (navmash only for pathfinding), but I'm having a hard time trying to move the AI to the designated position. here is my code:
CharacterController chara;
[SerializeField]
Transform playerTran;
[SerializeField]
float gravity = 3.8f;
[SerializeField]
float jumpForce = 50F;
Vector3 moveVector = Vector3.zero;
[SerializeField]
Transform jumpCheck;
[SerializeField]
Transform jumpPos;
// Use this for initialization
void Start ()
{
chara = GetComponent<CharacterController>();
playerTran = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
}
// Update is called once per frame
void Update ()
{
chara.Move(moveVector);
if (chara.isGrounded)
{
transform.LookAt(playerTran);
}
moveVector -= Vector3.up * gravity * Time.deltaTime;
if (Input.GetKeyUp(KeyCode.J))
{
jumpCheck.position = new Vector3(playerTran.position.x, 0, playerTran.position.z);
float angle = findAngle( playerTran.position.x - transform.position.x , playerTran.position.z - transform.position.z);
Vector3 _toJumpPos = MakeJumpCricle(playerTran.position , jumpCheck.localScale.x/2, angle);
jumpPos.position = new Vector3(_toJumpPos.x, 0, _toJumpPos.z);
moveVector = Vector3.up * jumpForce * Time.deltaTime;
}
}
float findAngle(float x, float y)
{
float value;
value = (float)((Mathf.Atan2(x, y) / Mathf.PI) * 180);
if (value < 0)
{
value += 360;
}
Debug.Log(value);
return value;
}
Vector3 MakeJumpCricle( Vector3 center, float radius, float angle)
{
Vector3 pos = Vector3.zero;
pos.x = center.x - radius * Mathf.Sin(angle * Mathf.Deg2Rad);
pos.y = 0;
pos.z = center.z - radius * Mathf.Cos(angle * Mathf.Deg2Rad);
return pos;
}
I want to move the AI to the jumpPos with both forward and up vectors but I'm not sure to do this.
visualization of the code
code visualization
I found a good solution, I used a Bezier Curves to generate a path
posting it here so other might find it helpful.
refrence link
http://www.theappguruz.com/blog/bezier-curve-in-games
public LineRenderer jumpLine;
private int numberOfPoint = 50;
[SerializeField]
List<Vector3> pointPositions = new List<Vector3>();
CharacterController chara;
[SerializeField]
Transform playerTran;
[SerializeField]
float gravity = 3.8f;
[SerializeField]
float jumpForce = 50F;
Vector3 moveVector = Vector3.zero;
[SerializeField]
Transform jumpCheck;
[SerializeField]
Transform jumpPos;
[SerializeField]
Transform jumpHightOne;
bool movejump;
Vector3 pZero;
Vector3 pOne;
float time;
// Use this for initialization
void Start ()
{
chara = GetComponent<CharacterController>();
playerTran = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
jumpLine.positionCount = numberOfPoint;
jumpLine.gameObject.SetActive(false);
}
// Update is called once per frame
void Update ()
{
chara.Move(moveVector);
if (chara.isGrounded)
{
transform.LookAt(playerTran);
}
moveVector -= Vector3.up * gravity * Time.deltaTime;
if (Input.GetKeyUp(KeyCode.J))
{
jumpCheck.position = new Vector3(playerTran.position.x, 0, playerTran.position.z);
float angle = findAngle( playerTran.position.x - transform.position.x , playerTran.position.z - transform.position.z);
Vector3 _toJumpPos = MakeJumpCricle(playerTran.position , jumpCheck.localScale.x/2, angle);
Vector3 middlePoint = GetTheMiddlePoints(transform.position, playerTran.position, 0.5f);
jumpHightOne.position = new Vector3(middlePoint.x, jumpHightOne.position.y, middlePoint.z);
jumpPos.position = new Vector3(_toJumpPos.x, 0, _toJumpPos.z);
//moveVector = Vector3.up * jumpForce * Time.deltaTime;
//movejump = true;
DrawLinerBezierCurves();
}
if (movejump)
{
//MoveTowardsTarget(jumpPos.position);
}
}
Vector3 GetTheMiddlePoints(Vector3 origin, Vector3 destination, float middlepointfactor)
{
return (destination - origin) * middlepointfactor + origin;
// (destination - origin) * 0.5f;
}
float findAngle(float x, float y)
{
float value;
value = (float)((Mathf.Atan2(x, y) / Mathf.PI) * 180);
if (value < 0)
{
value += 360;
}
//Debug.Log(value);
return value;
}
Vector3 MakeJumpCricle( Vector3 center, float radius, float angle)
{
Vector3 pos = Vector3.zero;
pos.x = center.x - radius * Mathf.Sin(angle * Mathf.Deg2Rad);
pos.y = 0;
pos.z = center.z - radius * Mathf.Cos(angle * Mathf.Deg2Rad);
return pos;
}
void MoveTowardsTarget(Vector3 targetPostios)
{
var offset = targetPostios - transform.position;
if (offset.magnitude > .1f)
{
offset = offset.normalized * 15;
chara.Move(offset * Time.deltaTime);
}
else
{
movejump = false;
}
}
// line Bezier Curves
Vector3 CalculateBezierCurvesPoints(float t , Vector3 p0, Vector3 p1 )
{
return p0 + t * (p1 - p0);
// P = P0 + t(P1 – P0) , 0 < t < 1
}
// line Bezier Curves
Vector3 CalculateBezierCurvesPoints(float t, Vector3 p0, Vector3 p1, Vector3 p2)
{
float u = 1 - t;
float uu = u * u;
float tt = t * t;
Vector3 p = uu * p0;
p = p + 2 * u * t * p1;
p = p + tt * p2;
return p;
//P = (1-t)^2 P0 + 2 (1-t) t P1 + t^2 P2 , 0 < t < 1
// uu u tt
// uu * p0 + 2 * u * t * p1 + tt * p2
}
void DrawLinerBezierCurves()
{
if (pointPositions.Count > 0)
{
pointPositions.Clear();
jumpLine.positionCount = 0;
jumpLine.positionCount = numberOfPoint;
}
for (int i = 1; i < numberOfPoint + 1; i++)
{
float t = i / (float) numberOfPoint;
if (!jumpLine.gameObject.activeInHierarchy)
{
jumpLine.gameObject.SetActive(true);
}
pointPositions.Add(CalculateBezierCurvesPoints(t, transform.position,jumpHightOne.position, jumpPos.position));
jumpLine.SetPosition(i - 1, pointPositions[i - 1]);
}
}
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.
How do I calculate the angle of a trajectory to hit the target, without knowing the velocity. I only know the max height, offset height and the distance to the target.
This is what I got so far (I don't know how to calculate offset height ):
float GetAngle(Vector3 startLocation, Vector3 endLocation, float maxHeight)
{
float distance = Mathf.Sqrt(Mathf.Pow(startLocation.x - endLocation.x,2) + Mathf.Pow(startLocation.z - endLocation.z,2));
float offsetHeight = endLocation.y - startLocation.y;
//how do I calculate offset height in this equation ?
return -Mathf.Atan (4 * maxHeight/ distance ) + Mathf.PI;
}
I use this to calculate the velocity (works fine I only need the correct angle):
float LaunchVelocity (Vector3 startLocation, Vector3 endLocation, float angle)
{
float range = Mathf.Sqrt(Mathf.Pow(startLocation.x - endLocation.x,2) + Mathf.Pow(startLocation.z - endLocation.z,2));
float offsetHeight = endLocation.y - startLocation.y;
float gravity = Physics.gravity.y;
float velocity = range * range * gravity;
velocity /= range * Mathf.Sin(2 * angle) + 2 * offsetHeight * Mathf.Pow(Mathf.Cos(angle),2);
return Mathf.Sqrt(velocity);
}
I got the solution:
float GetAngle(float height, Vector3 startLocation, Vector3 endLocation)
{
float range = Mathf.Sqrt(Mathf.Pow(startLocation.x - endLocation.x,2) + Mathf.Pow(startLocation.z - endLocation.z,2));
float offsetHeight = endLocation.y - startLocation.y;
float g = -Physics.gravity.y;
float verticalSpeed = Mathf.Sqrt(2 * gravity * height);
float travelTime = Mathf.Sqrt(2 * (height - offsetHeight) / g) + Mathf.Sqrt(2 * height / g);
float horizontalSpeed = range / TravelTime;
float velocity = Mathf.Sqrt(Mathf.Pow(verticalSpeed,2) + Mathf.Pow(horizontalSpeed, 2));
return -Mathf.Atan2(verticalSpeed / velocity, horizontalSpeed / velocity) + Mathf.PI;
}