SRL Archive - Research & Development Lounge

projectile simulation


mixster

2009-10-07 02:17:32

R0b0t1 prompted me and I delivered:

const
init_force = 50; // Initial acceleration - ms^-2 (same as force in newtons assuming 1 kg projectile)
init_angle = 40; // Initial angle in degrees, with horizontal being 0 and vertical being 90
grav = 9.81; // Gravity, x ms^-2
wind = 5; // Wind, x ms^-2 - positive increases distance and vice versa
res = 5; // Resistance, x ms^-2
fps = 64.0;

var
m, r_for, x_for, y_for, cur_ang, x_pos, y_pos: Extended; // resultant force; x and y components of resultant force; current angle of projectile
i: Integer; // Timer
arc: TPointArray;
tb: TBox;

begin
r_for := init_force;
x_pos := 0;
y_pos := 0;
cur_ang := Radians(init_angle);
i := 0;
repeat
SetLength(arc, i + 1);
arc[i] := Point(Round(x_pos), Round(y_pos));

r_for := r_for - (res / fps);

x_for := (r_for * Cos(cur_ang)) - (wind / fps);
y_for := (r_for * Sin(cur_ang)) - (grav / fps);
cur_ang := ArcTan(y_for / x_for);
r_for := x_for / Cos(cur_ang);

x_pos := x_pos + (x_for / fps);
y_pos := y_pos + (y_for / fps);

i := i + 1;
until y_pos <= 0;

Writeln('Flight took ~' + IntToStr(Round(i / fps)) + 's');

SetLength(arc, i + 1);
arc[i] := Point(Round(x_pos), Round(y_pos));

if arc[i].y <> 0 then
begin
if arc[i].x = arc[i - 1].x then
arc[i].y := 0
else
begin
m := (arc[i].y - arc[i - 1].y) / (arc[i].x - arc[i - 1].x);
arc[i].x := arc[i - 1].x + Round(arc[i - 1].y / -m);
arc[i].y := 0;
end;
end;

tb := GetTPABounds(arc);

tb.y2 := tb.y2 + 5;
tb.x2 := tb.x2 + 2;

DisplayDebugImgWindow(tb.x2 + 2, tb.y2 + 5);
Wait(50);
GetDebugCanvas.Brush.Color := clWhite;
GetDebugCanvas.Rectangle(0, 0, tb.x2 + 2, tb.y2 + 5);
GetDebugCanvas.MoveTo(2, tb.y2);
GetDebugCanvas.Pen.Color := clBlack;

for i := 0 to High(arc) do
GetDebugCanvas.LineTo(arc[i].x, tb.y2 - arc[i].y);
end.

I have somewhat badly named variables where by I use force when it should be acceleration etc. but it seems to produce reliable and accurate results. The only known problem is that high resistance may lead to unnatural results as it relies on the projectile always moving forwards. Also, a near horizontal arc may cause a maths error if Scar rounds the gradient to 0, but I have been unable to reproduce myself.

Enjoy~

mastaraymond

2009-10-07 20:21:50

This be some reall stuff!! Make it output a formula -> f(x) = height of projectile, kay thnx <3

Dan Cardin

2009-10-08 00:01:49

you get the math error after putting force about 149. i also get it with moving around alot of the variables, but thats because i assume, i had my force at 149.

Killerdou

2009-10-08 00:50:01

You can easily solve that by giving it an x pos, speed and acc and an y pos, speed and acc, when you apply a force, you just split the vector in it's x and y components, (you won't get a devide by 0 error that way) and the best thing is, it's just a matter of adding a bit of extra code to make it 3d!

like this:



Type

tParticle = Record
PosX, PosY: Extended; //position
SpeedX, SpeedY, AccX, AccY : extended; //Speed and acceleration values
Rot,RotSpeed, RotAcc, IObject: extended; // Rotation
LastCheck : integer; // Last check for this particle
end;

var
Map : integer;

Procedure MoveParticle(var Particle : tParticle);
var
Time, T : extended; //Time since last check
begin
T := GetSystemTime;
Time := T - Particle.LastCheck;
Particle.LastCheck := GetSystemTime;
T := Time/1000;
Particle.PosX := Particle.PosX + Particle.SpeedX * T + Particle.AccX * T * T;
Particle.PosY := Particle.PosY + Particle.SpeedY * T + Particle.AccY * T * T;
Particle.SpeedX := Particle.SpeedX + Particle.AccX * T;
Particle.SpeedY := Particle.SpeedY + Particle.AccY * T;
If IsArrowDown(0) then Particle.AccY := - 100 else
If IsArrowDown(2) then Particle.AccY := 100 else
Particle.AccY := 0;

If IsArrowDown(1) then Particle.AccX := 100 else
If IsArrowDown(3) then Particle.AccX := -100 else
Particle.AccX := 0;
end;

Procedure DrawParticle(Particle: tParticle);
begin
Getdebugcanvas.Brush.Color := clWhite;
Getdebugcanvas.Pen.Color := clWhite;
Getdebugcanvas.Ellipse(Round(Particle.PosX + 5), Round(Particle.PosY + 10), Round(Particle.PosX - 5), Round(Particle.PosY - 5));
end;

var
Particles : Array of tParticle;

Procedure Setup;
begin
SetLength(Particles, 1);
Particles[0].PosX := 400;
Particles[0].PosY := 400;
Particles[0].AccX := -10;
Particles[0].Iobject := 1;
Particles[0].LastCheck := GetSystemTime;
end;

Procedure CalculateStuff;
var
I : integer;
begin
For I := 0 to High(Particles) do
MoveParticle(Particles[I]);
end;

Procedure DrawStuff;
var
I : integer;
begin
SafeDrawBitmap(Map, GetDebugCanvas, 0, 0);
For I := 0 to High(Particles) do
DrawParticle(Particles[I]);
GetDebugCanvas.Refresh;
end;

begin
Map := BitmapFromString(800,800, '');
DisplayDebugImgWindow(800, 800);
SafeDrawBitmap(Map, GetDebugCanvas, 0, 0);
Setup;
repeat
CalculateStuff;
DrawStuff;
Wait(1);
until(False);
end.

mixster

2009-10-08 00:55:17

I don't :(
I'm assuming you're running with latest Scar? 3.15b seems to handle 149 force fine… Specifying all the stuff you're using would be helpful, though I can suggest a fix, but it's a bit crude and would give slight inaccuracy.