PDA

View Full Version : Physics of 2d Space



splattergnome
22-07-2003, 10:33 PM
Hello!

As a fan of 2d space games, the type where you fly around and visit planets or blast aliens from another galaxy, I am currently working on my own project. However, actual game control is a problem:

I am using a coordinate system for space (x,y), and I want the ship to be able to rotate with the left and right arrow key, thrust with the foward key, and anti-thrust (break) with the back key.

I was wondering, what formulars do I need to use in order to simulate this? And what options can I build inside to make control more realistic?

I would appreciate any help or suggestions.

splatty

WILL
23-07-2003, 07:27 AM
Well you can never have enough 2D space games. :) Heck, I've been writting one for a while now. First thing I'd recommend is 'boning up' on your physics. Understanding the basic fundimentals behind motion will save you a world of hurting later on(trust me!).

In general, movement in space is almost the same as movment here on earth, save the amount of pull of gravity. There is some gravitational pull in space(depending on the size and distance of objects around you), but it is so fractional it's practiacly zero-Gs. Basically if you can lob a cannon ball in a game like Scorched Earth or Worms, using code then you can make a ship float around in space... just add some kind of inertia...

Inertia is basically the momentum of an object. An object in space that is not disturbed will continue to move on inertia until something interupts it's movement. ie. An alien laser or the ships own rocket engines... Notice how when you see an astronaught/cosmonaught floating around in the ISS he doesn't really move until he/she pushes him/herself off a part of the equipment? Thats because of one of Newton's Laws of Motion.


Here is a site that has all kinds of Physics information and even a few equations: www.physicsclassroom.com (http://www.physicsclassroom.com/)

It, however is a bit detailed...


Hope this helps a bit. I'll see if I can find something a bit more staight-forward. If not I'll just cut and paste a few bits from my own code. Not as helpful for understanding in though.

Best of luck! 8)

splattergnome
25-07-2003, 05:38 PM
Thanks for the Link :-)

I am currently digged through the vector calculations on that page for some calculations, and started a very basic system for flight (for test purposes), which I will use before I use "real-universe" physics.

This is what I have worked out:


Player.x := Player.x + (cos(angle) * speed/5);
Player.y := Player.y + -(sin(angle) * speed/5);

But then again, I've thought about using a velocity.x and a velocity.y to go the "entire hog"... but I am not quite sure where to start. Well, I guess I have to keep on trying - eventually something will work out :-)

splatty

WILL
30-07-2003, 11:42 PM
I found my old cheat-sheet from when I started my current project about a year or so ago...

<u>Basic 2D Math Functions</u>

Find Velocity:
VelX := power * cos(Pi * AngleInRadians);
VelY := power * sin(Pi * AngleInRadians);

Find Distance Between Two Points:
Distance := sqrt(sqr(x2 - x1) + sqr(y2 - y1));

Find Rotation:
NewX := cos(AngleInRadians) * OldX - sin(AngleInRadians) * OldY;
NewY := sin(AngleInRadians) * OldX + cos(AngleInRadians) * OldY;

Find Angle From One Point To Another:
Angle := ArcCos((x2 - x1) / Distance);


You should be able to do quite a bit with these. You should however consider optimizing them a bit. Functions like sqrt and ArcCos cost quite a bit of cpu speed, but at least you can substitute something like z := sqr(x); with z := x * x;


I hope this is generally useful.

cairnswm
31-07-2003, 08:13 AM
Also for optimisation create an array with all the Cos/Sin etc of each angle. Then instead of calling a function to calculate the value just access it directly from the array.

patroclus02
17-08-2003, 11:45 AM
what about making a flying enemy??
It should follow a certain path in the sky (it can be aplied to space and also to earth). How could I do this? also this path should change in response to certain events such as palyer movement or scroll, etc..

thanks in advance!

WILL
17-08-2003, 02:06 PM
what about making a flying enemy??
It should follow a certain path in the sky (it can be aplied to space and also to earth). How could I do this? also this path should change in response to certain events such as palyer movement or scroll, etc..

You're entering a whole new ball-game now. Something like this isn't just a simple equation. Its more or less a algorithm or more, depenging on how complex this action gets. You can do this in many ways, such as; Have a set of "way-points" and either have it find it's way by steering from some AI or have little flight commands along a timeline... Depending on the type of game and type of AI you are using there are all sorts of different ways to do this. But whatever you do decide on you should establish your system of movement well before you move on to complexities. It'll save you alot of trouble later on. Ask any veteran here, they'll tell you the same. :)

Perhaps someone here could show you a system that they have used or came up with in some scenario or help you with one of your own, but they'd have to narrow down the type of perspective(side-view, birds-eye-view) or game physics envolved(2D/3D, but in this case 2D).

patroclus02
17-08-2003, 09:26 PM
well, my flying enemies are quite simple.
It is for a 2d platforms game style, and I'm talking about enemies that just fly around with no colisions with walls or anything. I just want it to fly in a way you don't see any straigth movement (I mean, not in lines, but in circular motion when its direction changes).

I did a very simple test telling the enemy to go go in direction of main character, and only change direction if it passed the main character by more than 100 pixels, for example, following straight lines in X and Y, but with acceleration in both axes. this way it looks like cirular motion when it changes direction!! Too easy, but too good also, :)

WILL
19-08-2003, 01:10 AM
Hmm... I think you are getting into AI here. That is a much different topic from the actual game physics. Getting to make the ships move in a "smarter" way rests soley on the game AI though you still want to let the physics do the work of moving the ship. AI has a limited effect on the game physics through some control variables such as speed, angle, etc.

Example 1: Steering the ship.

:arrow: You would change the angle of the ship by a set turn_rate for that ship either left or right.

Example 2: Increasing/Decreasing speed of the ship.

:arrow: Here you would change the speed of the ship either buy percentage, set amount or by incremental amounts that allow you to gradually change it.


It is usually best to keep these to things seperate in your code. Have a method or function for each if possible. This way, once you have created your physics routeens you can just leave them alone and you won't even have to worry about them. However it looks like you want to know more about AI. Perhaps if you post some of your questions on our AI forums someone can help you with those. I can list serveral methods for you to try and there are a few that others can suggest aswell.

joyrider
17-12-2003, 01:16 PM
Thanks for the ]Player.x &#58;= Player.x + &#40;cos&#40;angle&#41; * speed/5&#41;;
Player.y &#58;= Player.y + -&#40;sin&#40;angle&#41; * speed/5&#41;;[/code]


can somone please post some example code which uses this ? for example just plot a pixel so that when u turn left it turn 1 degree . I've been trying it for so long but i just suck at maths and never managed to get it working right.

thanks

Alimonster
17-12-2003, 01:46 PM
Thanks for the ]Player.x &#58;= Player.x + &#40;cos&#40;angle&#41; * speed/5&#41;;
Player.y &#58;= Player.y + -&#40;sin&#40;angle&#41; * speed/5&#41;;[/code]


can somone please post some example code which uses this ? for example just plot a pixel so that when u turn left it turn 1 degree . I've been trying it for so long but i just suck at maths and never managed to get it working right.

thanks
Check out the source code for my rotation (http://www.alistairkeys.co.uk/rotation.shtml) tutorial. One of the examples has a moveable tank -- that probably does what you need here. Note that you have to install the CDNDXTimer.pas component first (it's just a timer component) before opening the project.

If you want a more specific example then let me know and I'll whip one up later tonight.

joyrider
17-12-2003, 04:59 PM
thanks nice info there.

but i still got troubles, i'm using one single pixel, so i got no center to calculate it's the pixels possition itself

thing is i'm trying to get something like a turtle graphic system similar to ure vector tank example but then for just one pixel

Paulius
17-12-2003, 05:48 PM
:scratch: If you want to rotate anything you have to rotate it around something. Maybe you could explain how exactly you want it to look.

joyrider
17-12-2003, 06:14 PM
exactly the same like the tank example mentioned above but just for one pixel not vector graphics. so that he pixel moves around and i can turn it smooth. with the tank example u need to calculate the center and rotate it against that, but for just one pixel i have no clue what to do.

it's similar to a turtle graphics system. I thought that if i could create that, it wouldn't be hard to create a small top view race game without any scrolling

Paulius
17-12-2003, 09:15 PM
Just yse the pixel's position as the center, i minimized Alimonster's example to only use one pixel.
unit tank_class;

interface

uses
Graphics;

type
// The following represents a point of the tank. It would have x,y and z in
// the case of a 3D vector, but we don't need that for this example program
TVector2D = record
case Boolean of
False: (pts: array[0..1] of Real);
True : (x,y: Real);
end;


TTank = class
private
FDirection: TVector2D;
FShowDirection: Boolean;
protected
FPosition: TVector2D;
property Direction: TVector2D read FDirection write FDirection;
public
constructor Create;
destructor Destroy; override;
procedure Draw(Where: TCanvas);
procedure Move(const Speed: Real);
procedure Rotate(const rotation: Real);

property ShowDirection: Boolean read FShowDirection write FShowDirection default True;
end;

implementation

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

//
// MakeVector
//
// A little helper function to set a vector to the wanted values
//
procedure MakeVector(var Vec: TVector2D; _x,_y: Real);
begin
with Vec do
begin
x := _x;
y := _y;
end;
end;

//
// VectorLength
//
// Calculates the length of a 2D vector (for 3D, adjust to include the z value)
//
function VectorLength(var Vec: TVector2D): Real;
begin
with Vec do
Result := Sqrt(x*x + y*y);
end;

//
// Normalize
//
// Normalises the vector so that it's of unit length
//
procedure Normalize(var Vec: TVector2D);
var
RecipLength: Real;
begin
RecipLength := 1 / VectorLength(Vec);
with Vec do
begin
x := x * RecipLength;
y := y * RecipLength;
end;
end;


//
// Create
//
// The constructor for the class. Does constructor-y things
//
constructor TTank.Create;
var
i: Cardinal;
begin
inherited;
FPosition.x:=160;
FPosition.y:=120;

MakeVector(FDirection, 0, -1); // facing up -- y is positive down the screen
end;

//
// Destroy
//
// A destructor. No cleaning up to do yet
//
destructor TTank.Destroy;
begin
inherited;
end;

//
// Draw
//
// Displays the tank to a given canvas using LineTo
//
procedure TTank.Draw(Where: TCanvas);
var
i: Cardinal;
begin
with Where do
begin
// Move to the first point
Where.Pixels[Round(FPosition.x),
Round(FPosition.y)] := ClBlack;

// draw the direction too
if FShowDirection then
begin
Pen.Color := clRed;
MoveTo(Round(FPosition.X), Round(FPosition.Y));
LineTo(Round((FDirection.x * 140) + FPosition.X),
Round((FDirection.y * 140) + FPosition.Y));

Pen.Color := clBlack;
end;
end;
end;


//
// Move
//
// Moves the tank forward at a given speed in the direction it's facing
//
procedure TTank.Move(const Speed: Real);
var
i: Cardinal;
begin
FPosition.x := FPosition.x + (FDirection.x * Speed);
FPosition.y := FPosition.y + (FDirection.y * Speed);
end;

//
// Rotate
//
// Rotates the tank and its direction vector by the given angle (in degrees).
// This is the interesting part in terms of the tutorial :).
//
procedure TTank.Rotate(const rotation: Real);
const
DEG_TO_RAD: Real = PI/180;
var
i: Cardinal;
Angle: Real;
OldX, OldY: Real;
begin
Angle := Rotation * DEG_TO_RAD; // convert from degrees to radians

// Update the direction too. This doesn't need to be translated as we're only
// interested in which way it's pointing, not where it is
OldX := FDirection.x;
OldY := FDirection.y;
FDirection.x := OldX * cos(Angle) - OldY * sin(Angle);
FDirection.y := OldX * sin(Angle) + OldY * cos(Angle);
Normalize(FDirection);
end;

end.

joyrider
18-12-2003, 05:03 PM
ah tnx man,
i finally get the code & a bit of the maths but i do have one more question. I don't really get what the normalize procedure does or why it's need, (don't even know the meaning of normalize), i didn't use it with my test code but it still works.

Paulius
18-12-2003, 07:31 PM
Normalization means changing a vector so its direction stays the same, but its length becomes equal to 1, witch makes a lot of vector math functions simpler. In this case it?_Ts not really needed.

Alimonster
18-12-2003, 07:49 PM
I'll expand on what Paulius said...

Consider the basic premise of a vector: it's a bunch of numbers (one value per dimension, so x and y for R2 or x, y and z for R3) that should represents a direction. So, for example, the vector (x: 2, y: 3) represents "2 units to the right and 3 units up/down (depending on coordinate system)". Of course, such instructions have to be followed by a "start from here" point, which may well be the origin (x:0;y:0), although it doesn't have to be. It's all right saying "move 2 units to the right", but it has to be 2 units to the right of something!

So, you can keep a vector handy for a direction -- in the case here, which way the tank or your pixel is heading. Say we had the vector (x: 1; y: 0), which would be "move to the right". If you take any 2d point and add this vector onto it (point.x := point.x + vector.x; same for y) then you'd be moving along in this direction (since that's what the vector represents, a direction).

However, suppose we want to move along a bit further -- say, 5 times the usual amount in the direction. No problem, we'd code something like "point.x := point.x + (5 * vector.x); same for y". Everything is fine and dandy here -- we've moved 5 times further in the direction.

But...

(x: 1; y: 0) represents "move directly to the right"
(x: 2; y: 0) represents "move directly to the right"
(x: (n>0); y: 0) represents "move directly to the right"

So you see, you have have any number of vectors pointing in the same direction. The difference between each is that they're multiples of a common base vector of unit length (e.g. (x:2; y:0) can be expressed as 2 * (x:1; y:0), with the difference in meaning being "move 2 times as far in whatever direction as the standard one does".

If we took the vector (x: 2; y: 0) and applied it with the "point.x := point.x + (5 * vector.x); same for y" as before, we'd go in the right direction, but too far (twice as far, to be exact)!

Normalisation is a process to ensure the vector is of unit length. Unit vectors are handy because they only give a direction, which can then be multiplied by any value to move that far. With other vectors, you usually can't express multiples of them correctly unless they are normalised. Think about this in our example again: we keep a track of where we're heading, and we say "move in this direction for {current speed} units". Hence, it's gotta be normalised.

Hopefully this has made it clear why you sometimes need normalised vectors. If not, let me know!