# Calculating orbits and using it in my game

This topic is 2972 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hello fellows!

I've recently been playing around with some simple gravity experiments. I belive i've managed to simulate "real" gravity quite well, but there are two things that i've yet managed to understand.

Number one, Orbits
My experiment is sort of simulating a very simple solar system, one sun, and two planets orbeting said sun. To maintain a stable orbit, i've only played around with "home-made-math" and guessing. This works to some extent, but seeing as it's not really accurate it won't work in the long run. How do i calculate the speed my orbeting objects need to have to stay in orbit? For this i only need the starting conditions, as they are just planets.

Number two, units
Stupid stupid units! How do they work? Seriously though, i'm not yet quite sure how i would use units inside my simulation. I'm using variables such as mass and force, and they are using their correct units. But how do i translate these into speed? I'm using a 2D vector to store my speed (X speed and Y speed). Perhaps if i store angle, and drop the speed. I can calculate the force beeing acted upon the object at said logic loop, and get the speed from that. I don't really know, feeling abit lost.

I'd hate to supply you with too little information, so here, have some source:

[source]

#ifndef MAIN_H
#define MAIN_H

/* === DEFINITIONS === */
#define PARTICLE_SIZE 5.0f
#define MAX_OBJECTS 3

/* === STRUCTS === */
struct vector {
float x, y;
};

struct objectStruct {
vector position, prev;
vector vel, prevVel;
float mass;
};

/* === FUNCTIONS === */

void run();
float niceTime();
void draw(float);
void start(int, int);
void viewport(int, int);
void logic(float, float);
objectStruct interpolate(const objectStruct*, float);
LRESULT CALLBACK messages(HWND, UINT, WPARAM, LPARAM);

/* === VARIABLES === */
HWND window;
HDC dContext;
HGLRC rContext;
bool stopGame;
bool mouse;
float lastTime;
float loopTime;
int fps;
int frames;
int loops;
int mouseX;
int mouseY;
objectStruct objects[MAX_OBJECTS];

#endif
[/source]

[source]

/*
* Objects!
*/

/* Includes */
#include <GL/gl.h>
#include <windows.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "main.h"

/*
* WinMain(...):
* Main entry point of application.
*/
int
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
/* Start the simulation */
start(1024, 768);

return (0);
}

/*
* start():
* Setup and prepare what we need for the simulation to run.
*/
void
start(int width, int height)
{
/* === WINDOW CREATION === */
GLuint pixelFormat;
DWORD exStyle, style;
RECT windowBounds;
HWND returnWindow;
WNDCLASS baseClass;

/* Initialize the base window class */
baseClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
baseClass.lpfnWndProc = messages;
baseClass.cbClsExtra = 0;
baseClass.cbWndExtra = 0;
baseClass.hInstance = GetModuleHandle(NULL);
baseClass.hbrBackground = NULL;
baseClass.lpszClassName = "particles";

/* Register the window class */
RegisterClass(&baseClass);

/* Setup the window bounds */
windowBounds.left = (long)0;
windowBounds.right = (long)width;
windowBounds.top = (long)0;
windowBounds.bottom = (long)height;

/* Set the window styles */
exStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
style = WS_OVERLAPPEDWINDOW;

/* Andjust the window bounsds */

/* Create the window */
returnWindow = CreateWindowEx(exStyle, "particles", "Particles!",
style | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0,
windowBounds.right - windowBounds.left, windowBounds.bottom - windowBounds.top,
NULL, NULL, GetModuleHandle(NULL), NULL);

/* Check if the window got created */
if (!returnWindow) {
MessageBox(NULL, "Failed to create the window", "Error", MB_OK);
exit(99);
}

/* Setup the pixelformatdescriptor, handle how we want things to be */
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), 1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16, 0, 0, PFD_MAIN_PLANE, 0, 0, 0, 0
};

/* Check for a device context */
if (!(dContext = GetDC(returnWindow))) {
MessageBox(NULL, "Failed to find a device context", "Error", MB_OK);
exit(99);
}

/* Choose the pixel format */
if (!(pixelFormat = ChoosePixelFormat(dContext, &pfd))) {
MessageBox(NULL, "Failed to choose the pixel format", "Error", MB_OK);
exit(99);
}

/* Set the pixel format */
if (!SetPixelFormat(dContext, pixelFormat, &pfd)) {
MessageBox(NULL, "Failed to set pixel format", "Error", MB_OK);
exit(99);
}

/* Retrive the rendering context */
if (!(rContext = wglCreateContext(dContext))) {
MessageBox(NULL, "Failed to create rendering context", "Error", MB_OK);
exit(99);
}

/* Make it the active context */
if (!wglMakeCurrent(dContext, rContext)) {
MessageBox(NULL, "Failed to set active context", "Error", MB_OK);
exit(99);
}

/* Show the window and the cursor */
ShowWindow(returnWindow, SW_SHOW);
ShowCursor(true);

/* Set the window in focus, and push it to the foreground */
SetForegroundWindow(returnWindow);
SetFocus(returnWindow);

/* Set the viewport */
viewport(width, height);

/* === PARTICLE SETUP === */
int index;

/* Seed */
srand(time(NULL));

/* Make the middle thingie */
objects[0].mass = 500;
objects[0].position.x = (1024 / 2);
objects[0].position.y = (768 / 2);
objects[0].stuck = 1;

objects[1].position.x = (1024 / 2) - 50;
objects[1].position.y = (768 / 2);
objects[1].mass = 1;
objects[1].vel.x = 0;
objects[1].vel.y = 2.5f;
objects[1].stuck = 0;

objects[2].position.x = (1024 / 2) - 200;
objects[2].position.y = (768 / 2);
objects[2].mass = 1;
objects[2].vel.x = 0;
objects[2].vel.y = 1.25f;
objects[2].stuck = 0;

/* Start running simulation */
lastTime = GetTickCount();
loopTime = lastTime;
stopGame = false;
run();
}

/*
* run():
* The simulation loop!
*/
void
run()
{
MSG msg;
int index;
objectStruct state;
float dt, t, currentTime, accumulator, deltaTime, newTime;

/* Initial values */
t = 0.0f;
dt = 0.01f;
currentTime = 0.0f;
accumulator = 0.0f;

/* Main game loop */
while (!stopGame) {
/* Always peek for messages, so we don't miss input */
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
/* Check if we need to end the loop */
if (msg.message == WM_QUIT) {
stopGame = true;
} else {
/* Translate and dispatch the message */
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

/* Clear the screen */
glClear(GL_COLOR_BUFFER_BIT);

/* Get the current time */
newTime = niceTime();
deltaTime = newTime - currentTime;
currentTime = newTime;

/* Stop our delta time from going insane */
if (deltaTime > 0.25f)
deltaTime = 0.25f;

/* Use given time to run logical steps */
accumulator += deltaTime;
while (accumulator >= dt) {
accumulator -= dt;
logic(t, dt);
t += dt;
}

/* Draw all of our particles */
glPointSize(PARTICLE_SIZE);
glBegin(GL_POINTS);
for (index = 0; index < MAX_OBJECTS; index++) {
continue;

/* Interpolate particle */
state = interpolate(&objects[index], accumulator / dt);

/* Draw the point */
glColor4f(0.5, 0.0f, 1.0f, 1.0f);
glVertex2f(state.position.x, state.position.y);
}
glEnd();

/* Swap buffers */
SwapBuffers(dContext);
}
}

/*
* logic():
* Update the position of our particles
*/
void
logic(float t, float dt)
{
int curIdx, idx;
float force, acceleration, angle, distance;

/* Loop through all particles */
for (curIdx = 0; curIdx < MAX_OBJECTS; curIdx++) {
/* Save my previous state */
objects[curIdx].prev = objects[curIdx].position;

continue;

/* Apply a force for each object */
for (idx = 0; idx < MAX_OBJECTS; idx++) {
/* Skip this loop if we're the same */
if (curIdx == idx || objects[idx].dead)
continue;

/* Calculate distance between objects */
distance = sqrt(
(objects[idx].position.x - objects[curIdx].position.x) * (objects[idx].position.x - objects[curIdx].position.x) +
(objects[idx].position.y - objects[curIdx].position.y) * (objects[idx].position.y - objects[curIdx].position.y)
);

/* Don't add any force if we're too close... */
if (distance <= PARTICLE_SIZE) {
if (objects[idx].stuck) {
break;
} else {
objects[curIdx].mass += objects[idx].mass;
continue;
}
}

/* Calculate the force this object will have upon us */
force = (1 * (objects[idx].mass * objects[curIdx].mass)) / (distance * distance);
acceleration = force / objects[curIdx].mass;
angle = atan2(objects[idx].position.y - objects[curIdx].position.y, objects[idx].position.x - objects[curIdx].position.x);

/* Now we can set our velocity */
objects[curIdx].vel.x += acceleration * cos(angle);
objects[curIdx].vel.y += acceleration * sin(angle);
}

/* And now we can move! */
objects[curIdx].position.x += objects[curIdx].vel.x;
objects[curIdx].position.y += objects[curIdx].vel.y;
}
}

/*
* draw(deltaTime):
* Draw the objects.
*/
void
draw(float dt)
{

}

/*
* viewport(width, height):
* Set our viewport.
*/
void
viewport(int width, int height)
{
/* Set the viewport */
glViewport(0, 0, width, height);

/* Modify the matrix so point (0, 0) is at the top left */
glMatrixMode(GL_PROJECTION);
glOrtho(0.0f, width, height, 0.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);

/* Enable blending and set out blending function */
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_BLEND);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
}

/*
* messages(...):
* Handle the windows messages.
*/
LRESULT CALLBACK
messages(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
/* Handle the message */
switch (message) {
/* Post the quit messsage if we pressed the X */
case WM_CLOSE:
PostQuitMessage(0);
return (0);
break;

/* Mouse click */
case WM_LBUTTONDOWN:
mouse = true;
mouseX = LOWORD(lParam);
mouseY = HIWORD(lParam);
break;

case WM_MOUSEMOVE:
mouseX = LOWORD(lParam);
mouseY = HIWORD(lParam);
break;

/* Mouse release */
case WM_LBUTTONUP:
mouse = false;
break;

/* Change the vievport if we resize the window */
case WM_SIZE:
/* Switch to this window */
viewport(LOWORD(lParam), HIWORD(lParam));
break;
}

return DefWindowProc(hWnd, message, wParam, lParam);
}

/*
* interpolate(current, alpha):
* Interpolate between previous and current positions.
*/
objectStruct interpolate(const objectStruct *curr, float alpha) {
objectStruct state;

/* Steal current properties */
state.mass = curr->mass;
state.stuck = curr->stuck;
state.prev = curr->prev;

/* Interpolate */
state.position.x = curr->position.x * alpha + curr->prev.x * (1 - alpha);
state.position.y = curr->position.y * alpha + curr->prev.y * (1 - alpha);
state.vel.x = curr->vel.x * alpha + curr->prevVel.x * (1 - alpha);
state.vel.y = curr->vel.y * alpha + curr->prevVel.y * (1 - alpha);

return state;
}

/*
* niceTime():
* A more performance correct time.
*/
float niceTime() {
static __int64 start = 0;
static __int64 frequency = 0;
__int64 counter = 0;

if (start == 0) {
QueryPerformanceCounter((LARGE_INTEGER *)&start);
QueryPerformanceFrequency((LARGE_INTEGER *)&frequency);
return (0.0f);
}

QueryPerformanceCounter((LARGE_INTEGER *)&counter);
return ((float)((counter - start) / double(frequency)));
}

[/source]

##### Share on other sites
Hi there Zomgbie,

Your gravity math seems right, although I would recommend you to use vector math to obtain the angle instead of Atan2. You need to normalize the distance vector, which is done by dividing each of its components with the distance scalar value. The result is a so called unit vector of magnitude 1, which means it only contains information about direction and not about length or distance. . Multiply your force scalar with the unit vector to get the correct force vector.

Also, the orbital velocity needed for a circular orbit is: v = sqrt(G * (m1 + m2) / r), where v is velocity perpendicular to the sun, m1 is planet mass, m2 is sun mass, and r is distance to sun center. It only took me a few moments to google the answer. I recommend wikipedia, which has a ton of great articles on celestial mechanics, which this topic is called.

Finally, here are some very simple code samples I made a while back. They show you how to set up a system of gravitationally interacting bodies, much like in the code sample you posted.

Cheers,
Mike

##### Share on other sites
Hey h4tt3n! (You're not Swedish by any chance?)

I rewrote my code to use vector math instead, as you suggested. I don't know why but it feels more clean... more pure. I liek.

Sadly, i did not understand how you started off your bodies in your second example, with the starting angle and such. But i got around that by spawning all bodies alinged on the X-Axis and then using the equation to calculate their orbital velocity at the given spawn point. I then applied that velocity either straight down or up, depending on which direction i wanted them to travel. Wich all resulted in perfect circular orbits.

Thanks alot for your help! The code you provided really set me off on the right track, that was awesome. As many thumbs up to you as possible!

##### Share on other sites
You're welcome Zomgbie. As for the initial velocity, it is provided by these lines:

.Vel.X = Sqr(Planet(0).Mass*G/Spawn_Distance)*Sin(-Spawn_Angle) .Vel.Y = Sqr(Planet(0).Mass*G/Spawn_Distance)*Cos(Spawn_Angle)

The Sin(-Spawn_Angle) and Cos(Spawn_Angle) part is simply the perpendicular unit vector, (-y, x), pointing at a right angle to the line between the sun and orbiting body.

If you're interested I could dig out some more advanced code samples with higher order integrators and different other nice functionalities.

Ps. close, I'm danish :-)

Cheers,
Mike

##### Share on other sites
I actually managed to pull that off before you posted! I wanted to take a body in motion and have it move into a perfect circular orbit around the target. For that i had to calculate the orbital velocity at my current position, set that as my target velocity, and adjust my real velocity towards it. Seeing as the body might want to enter orbit when it's not aligned with either axis, i had to crunch down on your calculation. Got it to work atleast! Sadly though, this makes for some ugly looking adjustments. Do you know any better way of taking a body in motion and putting it in orbit?

Oh, any example you could provide on this stuff would be completly amazing. I find this very intresting!

Damn. The Hatten part gave you away, but as usual i persumed you had to be Swedish ^^

• ### Game Developer Survey

We are looking for qualified game developers to participate in a 10-minute online survey. Qualified participants will be offered a \$15 incentive for your time and insights. Click here to start!

• 15
• 9
• 11
• 15
• 21