Why doesn't this simple physics freefall code work - c#

I have some code I created to simulate a freefall of an object in a vacuum. When timestep is set to 1000ms it works perfectly with no problems. when I set timestep lower than 1000ms it deviates wildly from actual values falling far faster than normal. It takes around 14 seconds to fall 1000 meters at 1000ms which is the correct value. At 100ms timestep it only takes 5 seconds. at 10ms it only takes 2.2 seconds.
Can someone please tell me what I am doing wrong? I thought I set the timestep calculation to be able to handle smaller steps.
Thank you
Body testbody = new Body();
testbody.pos = new Vector(0, 1000);
testbody.velocity = new Vector(0, 0);
Bodytrack(testbody);
static void Bodytrack(Body body)
{
watch.Start();
int timestep = 1000;
while (body.pos.Y > 0)
{
body.pos = body.pos + (body.velocity * (timestep / 1000.0));
if (body.pos.Y <= 0) { break; }
Thread.Sleep(timestep);
CalculateAcceleration(body);
Console.Clear();
Console.WriteLine(body.velocity.Y);
Console.WriteLine(body.pos.Y);
Console.WriteLine(watch.Elapsed.TotalSeconds);
}
watch.Stop();
}
public static void CalculateAcceleration(Body body)
{
body.acceleration = new Vector(0, -9.80665);
body.velocity = Vector.Add(body.acceleration, body.velocity);
}

This line is your problem:
body.acceleration = new Vector(0, -9.80665);
Every time through the loop, you set the acceleration to -9.8 (which is the acceleration for 1000ms) instead of the acceleration for the amount of time that has passed.
You need to take into account the amount of time that as passed. If 500ms has passed, then acceleration should only be -9.80665/2.
You need to change the method to this:
public static void CalculateAcceleration(Body body, int timestep)
{
body.acceleration = new Vector(0, -9.80665 * (timestep/1000));
body.velocity = Vector.Add(body.acceleration, body.velocity);
}

If I remember my math correctly (and manage to write it down using markdown)...
The idea is that the horizontal position is not affected by anything, gravity only affects the vertical position. So we can easily calculate where the object is horizontally (assuming no collisions).
X(t) = Vx * t.
This means that when you throw something horizontally at 10 metres per second, it will have traveled 100 metres after 10 seconds.
The formula för the horizontal positioning is a bit more advanced. It contains two parts, the first part is the movement without gravity, and the second part acts as a countering force by the gravity.
Y(t) = Vy * t - (g * t^2)/2 where g is gravity (usually 9.82).
Vy * t is the constant motion upwards if gravity wouldn't have pulled it down.
(g * t^2)/2 is the increasing pull of gravity.
A object that's dropped will have no initial force in any direction, thus { Vx = 0, Vy = 0 } and you can easily get the current position of it, at any time, using pos(t) = -(g * t^2)/2. (We also know that it will fall straight down so you do not have to calculate any horizontal positioning.) You already know the speed of it by speed(t) = g * t.
You can not trust time-slices as your code does. For one, Thread.Sleep isn't exact. It's better if you calculate time elapsed since last update, and use that in your calculations.

Related

How to move an object 500 pixels only at a different speed at random intervals?

I'm trying to write a piece of code that will allow an object to "dash" for 500 pixels at random. I've got the random time down, it's the specific distance that I've got an issue with.
This is for an assignment where I'm supposed to research how to do it, but I have had no luck in finding anything. I've tried creating an incremental variable, but that doesn't count quick enough, I'm sure it's related to this equation for the movement of the object: xPosition += xSpeed * xDirection;. I just can't figure it out. The code does show some other functions as well, like reducing the speed to the previous speed once the 500 pixels has been covered.
The other problem is that the grid I'm working on has positive and negative numbers, so I'm not sure, but will I also need to account for that as well?
if (xSpeed >= 10)
{
if (xPosition < 0)
{
distance = distance + 1;
}
if (xPosition > 0)
{
distance = distance + 1;
}
}
if (collisionNum == ranCollision - 1)
{
dashPosStart = xPosition;
}
// Method for stopping dash
if (distance >= 500)
{
while (xSpeed > startSpeed)
{
xSpeed--;
}
ySpeed = xSpeed / 2;
}
if (distance == 500)
{
dashPosFin = xPosition;
}
xPosition += xSpeed * xDirection; // Horizontal movement
yPosition += ySpeed * yDirection; // Vertical movement
The distance should be counting every pixel but it seems to be counting much slower than that, and slower than 60 times a second (the speed the program works). I've been trying to work this out for days now and just can't get my head round it. Any help/ advice would be great! Thanks.
You're always incrementing distance by one; thus, the dash will always take 500 frames to complete, rather than a distance of 500 pixels.
The shortest fix would be to increment distance by the dash speed each frame (make sure to handle negative values appropriately).

Control begin and end point object between 2 points

im trying to move a object in unity between 2 points, and at the moment it kinda works strange, i read the documentation and it says that the object begin point is (0,0,0) so my object goes under my other mesh that i have there, and the end point i can actually control, in my case it is 10, i want the object to move between 1.5 and 10(not 0 to 10)
i have this
void Update () {
transform.position = new Vector3(transform.position.x,Mathf.PingPong(Time.time,10.0f), transform.position.z);
}
when i try to put speed on the ball doing this:
void Update () {
transform.position = new Vector3(transform.position.x,Mathf.PingPong(Time.time,10.0f) * 10, transform.position.z);
}
the object does not colide and goes back at the end point it just stop looping and never came back how can i correct this 2 problems?
If your object has a collider, I suggest you move it via its Rigidbody rather than its Transform, to avoid potential collision issues. Try this:
public float MinY = 1.5f; // y position of start point
public float MaxY = 10f; // y position of end point
public float PingPongTime = 1f; // how much time to wait before reverse
public Rigidbody rb; // reference to the rigidbody
void Update()
{
//get a value between 0 and 1
float normalizedTime = Mathf.PingPong(Time.time, PingPongTime) / PingPongTime;
//then multiply it by the delta between start and end point, and add start point to the result
float yPosition = normalizedTime * (MaxY - MinY) + MinY;
//finally update position using rigidbody
rb.MovePosition(new Vector3(rb.position.x, yPosition, rb.position.z));
}
Here you have a better control on the distance to travel, and the speed.
Actually I didn't get exactly what are the problem you faced. But don't forget here and in your try, that you are directly modifying the position of the object, not adding forces or else.
Hope that helps you.
I think you simply misunderstood how the Mathf.PingPong method works :
first argument t is the value you want to "clamp" between 0 and the given length : this is were you want to put the Time.time as you did since this value will increase over time and therefore perpetually oscillate. If you want to increase/decrease the oscillation speed you have to multiply it.
second argument length is the max value of the "clamp" : if you want to increase/decrease the distance (in your case) you have either set it to 0 and multiply the whole Mathf.PingPong(...) by a value or directly give it the wanted value (both implementations will have a different effect.
Mathf.PingPong(Time.time * speed, 1.0f) * value : speed will affect the oscillation speed / value will affect the max value reached AND the speed / time to complete the oscillation (back and forth) will remain the same as value changes and decrease as speed increases
Mathf.PingPong(Time.time * speed, value) : speed will affect the oscillation speed / value will affect the max value reached BUT NOT the speed / time to complete the oscillation (back and forth) will increase as value increases and decrease as speed increases
About your other problems :
If you want to move your object between 1.5 and 10 you have to write something like this :
transform.position = new Vector3(transform.position.x, 1.5f + Mathf.PingPong(Time.time, 10.0f - 1.5f), transform.position.z);.
Also if you want to detect collision, avoid setting position manually as it will mess up with Physics and cause weird behaviors. Best way to move your object while keeping physic working is to do as #Heldap said using Rigidbody.MovePosition.

Source, Target, and Intermediate Way Points

Consider two points: (0,0,0) as source and (1000,0,0) as target
A cube game object wants to travel from source and target at a pre-defined/constant speed. Time taken: t1
Introduce 100 intermediate points between source and target, i.e. INTERMEDIATE_POINTS = 10
Example: (0,0,0), (10,0,0), (20,0,0), (30,0,0).... (980,0,0), (990,0,0), (1000,0,0). Same speed, time taken: t2.
Introduce 50 intermediate points, i.e. INTERMEDIATE_POINTS = 20 ; (0,0,0), (20,0,0), (40,0,0),..., (960,0,0), (980,0,0), (1000,0,0). Same speed, time taken: t3.
Result: t1 < t3 < t2, i.e. more intermediate points, more time taken to reach the target (although same path and same speed)
Question: If you compare, the game object moves in the same way (same path, same speed) in all the three cases (no intermediates, 100 intermediates, and 50 intermediates) that are mentioned above. But why is there a time difference to reach the target?
Code to test this scenario:
using UnityEngine;
using System.Collections.Generic;
public class TestSpeed : MonoBehaviour
{
private List<Vector3> listOfPoints = new List<Vector3>();
private int INTERMEDIATE_POINTS = 1;
private int counter = 1;
private float speed = 50.0f;
private float originalDistance = 0.0f;
private float distanceCovered = 0.0f;
private float overshoot = 0.0f;
private Vector3 modifiedTarget;
// for the car movement.
private Vector3 targetPosition; // after every loop, get the next position
private Vector3 currentPosition;
// Use this for initialization
void Start()
{
for (int i = 0; i <= 1000; i = i + INTERMEDIATE_POINTS)
{
listOfPoints.Add(new Vector3(i, 0, 0));
}
currentPosition = this.transform.position; // at the beginning, from (0,0,0)
targetPosition = listOfPoints[counter];
}
// Update is called once per frame
void Update()
{
originalDistance = Vector3.Distance(targetPosition, currentPosition);
distanceCovered = Vector3.Distance(transform.position, currentPosition);
if(Vector3.Distance(transform.position, new Vector3(0,0,0)) >= 995.0f)
{
System.TimeSpan t = System.TimeSpan.FromSeconds(Time.timeSinceLevelLoad);
string answer = string.Format("{0:D2}:{1:D2}:{2:D2}",
t.Hours,
t.Minutes,
t.Seconds);
}
if ((originalDistance - distanceCovered) <= 0.0f)
{
currentPosition = transform.position;
targetPosition = listOfPoints[counter];
counter++;
}
else
{
float step = speed * Time.deltaTime;
if((distanceCovered + step) >= originalDistance)
{
overshoot = distanceCovered + step - originalDistance;
counter++;
modifiedTarget = Vector3.Lerp(targetPosition, listOfPoints[counter], (overshoot / originalDistance));
}
else
{
modifiedTarget = targetPosition;
}
transform.position = Vector3.MoveTowards(transform.position, modifiedTarget, step);
}
}
}
How to use the code:
Just create a cube game object and assign the script to it. Near to string answer set a break-point to check the time duration with various number of intermediate points.
I'm pretty sure this logic here is the cause of the strange observation:
if ((originalDistance - distanceCovered) == 0.0f)
{
currentPosition = transform.position;
targetPosition = listOfPoints[counter];
counter++;
}
You check whether or not you've arrived at your destination waypoint by checking for an exact position match; however, the distance you travel per Update is anything but exact. That means that your object could very well overshoot the destination, then try to move back towards it, overshoot it again, then repeat.
I bet if you watch your cube in the scene view, you'll see it hover around a single waypoint for a bit until it manages to hit the exact distance it needed.
You're probably better off using an inequality here, for example:
if ((originalDistance - distanceCovered) <= 0.0f)
{ /* ... */ }
Your object has reached its waypoint if the distance it has traveled is greater than or equal to the distance it needed to travel. originalDistance - distanceCovered will be negative as soon as the object has reached or passed the waypoint.
EDIT:
X.....X.....X.....X.....X.....X
Here are some waypoints. Pretend we have an object traveling along the path of waypoints. It starts at the first one on the left and goes right. It'll be represented by an O.
O.....X.....X.....X.....X.....X
Now it moves along for a while. Due to the variability of Time.deltaTime, it might move one or two spots each tick. So let's say it winds up here after a few ticks:
X....OX.....X.....X.....X.....X
And during the next tick, it moves two:
X.....XO....X.....X.....X.....X
With your original check, the object will now travel backwards. It needed to travel a distance of seven spaces, but it traveled 8. So with your original check, originalDistance - distanceCovered != 0.0f. So it'll keep trying to hit that spot over and over again until it hits it on the dot.
Even if you introduce the idea of a "threshold", you're still going to have the same problem. There is no fixed distance traveled per tick, so that means that each waypoint will have some artificial "bounce" time unless that threshold is so large that the waypoints become meaningless.
If you use originalDistance - distanceCovered <= 0.0f, you will always move on to the next waypoint if it overshoots. Instead of trying to land the object in some small window, you're just making sure that the object has passed or met its waypoint.

Finding time to target with variable velocity [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
so i have torpedos in my game and they start out at 0 meters per second and accelerate realistically. after so many seconds they stop accelerating and travel at a constant rate forward.
I have a distance to the target and I basically am trying to calculate lead time for autoaiming.
So given
Distance to target;
Acceleration (per second);
burn time (number of seconds before acceleration stops);
I need to basically determine I believe the average meters per second the projectile is travelling.
The only way I can see to do it is something like this.
curdistance; //stores distance traveled per second
currentspeed; //stores speed at a given second
acceleration;
for(int timer = 1; curdistance < distanceToTarget;timer++)
{
currentspeed = currentspeed + acceleration;
curdistance = curdistance + ( currentspeed);
if(timer >= burnTime)
{
acceleration = 0;
}
}
Now this works but it has 2 problems.
The burn time has to be an int or else the smaller the fraction the greater the number of runs to keep accuracy.
If i want a 4.2 burn time for example in order to keep the accuracy i have to run it 42 times and calculate for every 10th of a second.
Also the average could be off by quite a bit depending on how much it overshoots a target depending again on how precise the timer is.
if my projectile is going at 30 meters per second and it needs to go 121 meters it'll add another full second of travel before it goes ok you've gone to/past the target which will mean it will actualy be aiming as it were at a point 29 meters further than it really should.
The only way to combat this with this algorithm is to check more often every 10th or 100th of a second.
I feel like though there might be a math equation I don't know that lets me solve this precisely.
Any Help?
During accelerated motion you can use d = a*t^2/2, or equivalently t = sqrt(2*d/a), at which time velocity v = a*t.
Then you can extrapolate to the target using that v.
As you describe it your movement happens in 2 parts. The first part is accelerated movement (with constant acceleration) and the second part is movement under constant velocity.
You can calculate the traveling distance (or time) for each one individually and then combine them for the desired result.
Keep in mind that you need to check for special cases where the target is closer than the burn distance. The code below does that with the check if (distanceToTarget < burnDistance)
// these will be the results
float timeToTarget;
float averageSpeed;
// assign values to these
float distanceToTarget;
float acceleration;
float burnTime;
float burnDistance = acceleration * burnTime * burnTime * 0.5;
if (distanceToTarget < burnDistance)
{
timeToTarget = Math.Sqrt(2 * distanceToTarget / acceleration);
}
else
{
float velocity = acceleration * burnTime;
timeToTarget = burnTime + (distanceToTarget - burnDistance) / velocity;
}
averageSpeed = distanceToTarget / timeToTarget;
If
d = initial distance to the target
b = burn time
a = acceleration
When the projectile stops accelerating, it will have
speed = a*b
distance (traveled) = dt = a*b^2/2
From that moment, it will need
time for impact = ti = (d-dt)/(a*b)
The total time will be
total time for impact = ti + b
This is one way:
Function VelocityGivenTime(burnTime, givenTime)
(
T = givenTime
If T > burnTime Then T = burnTime
return acceleration * T
)
Function DistanceGivenTime(burnTime, givenTime)
(
If burntime >= givenTime Then
T = givenTime
return 0.5 * acceleration * T^2
Else
T = burnTime
D = 0.5 * acceleration * T^2
D = D + VelocityGivenTime(T) * (givenTime - burnTime)
return D
End IF
)
However, if what you really wanted was the time to a target give its distance, you could do it like this:
Function TimeGivenDistance(burnTime, distance)
(
burnDistance = DistanceGivenTime(burnTime)
If distance > burnDistance Then
return burnTime + (distance - burnDistance) / VelocityGivenTime(burnTime)
Else
return SQRT(2 * distance / acceleration)
End If
)

Programming Floaty Motion

Anyone have a good idea for a method of achieving random floaty motion? Like, just general smooth drift in a constrained area.
This is what I tried:
var rangeX = 100;
var rangeY = 100;
var gravity = .001;
$('.floating').each(function() {
$(this).data('startX', $(this).position().left);
$(this).data('startY', $(this).position().top);
$(this).data('velocityX', 0);
$(this).data('velocityY', 0);
chooseDestination();
});
setInterval(function() {
$('.floating').each(function() {
var changeX = ($(this).data('destinationX') - $(this).position().left) * gravity;
var changeY = ($(this).data('destinationY') - $(this).position().top) * gravity;
var velocityX = $(this).data('velocityX') + changeX;
var velocityY = $(this).data('velocityY') + changeY;
$(this).data('velocityX', velocityX);
$(this).data('velocityY', velocityY);
$(this).css('left', $(this).position().left + velocityX);
$(this).css('top', $(this).position().top + velocityY);
});
}, 10);
setInterval(chooseDestination, 2000);
function chooseDestination() {
$('.floating').each(function() {
$(this).data('destinationX', $(this).data('startX') - rangeX/2 + Math.random() * rangeX);
$(this).data('destinationY', $(this).data('startY') - rangeY/2 + Math.random() * rangeY);
});
}
It really doesn't look very floaty though.
"Floaty" generally requires slow changes in velocity. What I would do, from a high-level perspective, is set up an acceleration/velocity/position model for movement, where your random generator affects acceleration only. You already have the other two layers. Establish limits for velocity and acceleration at any given point in time (I'd limit absolute distance, not just distance in X and Y), and don't update the rate of acceleration every frame. Lastly, deal in fractional values for velocity and acceleration, which will be turned into an integer position change only at the last minute when you're drawing the object's new position. What you'll end up with is an object that will slowly head off in one direction, then start drifting in a different direction, maybe stop and hover for a second, then start going again.

Categories

Resources