Kid dream part VIII: Multi-platform game loop

This article will be more technical then the previous ones and as I promised it will be about creating multi-platform game loop. I will set up objectives which I want to achieve then I describe some available options and finally tell you about my solution.

Objective
The main goal is to create game that will run on Windows, Linux, Mac OS X as well. I would like to keep it simple and with minimum platform dependent code. The first step is to render simple screen covered by "space" texture and displayed text on the bottom of the screen.

Options
There are many ways how to create this game loop e.g create one loop for each platform. Well I was going this way for a while. It was easy for me to do on Windows platform but when I was trying to create the loop on Linux I used glut for that and this was the 'aha' moment for me. So I decided to take a look on multi platform libraries. 

Solution
I was considering GLFW, SDL, freeglut, etc. I've chosen SDL library because of its license, number of extensions available and interesting portability. So it is easy to create OpenGL window. The next advantage is SDL_image and SDL_ttf which allows to load textures from various format and draw texts very easily.

Difference
Well let's compare the initialisation and loop code with SDL library and without the library.

Code using Windows API:

[codesyntax lang="cpp"]

void WindowsGameLoop::start(){
	WNDCLASSEXA wClass;
	memset(&wClass,0,sizeof(wClass));
	wClass.lpszClassName = WINDOW_CLASS_NAME;
	wClass.cbSize        = sizeof(WNDCLASSEXA);
	wClass.lpfnWndProc = WndProc;
	wClass.hIcon = LoadIcon(NULL,IDI_WINLOGO);
	wClass.hCursor = LoadCursor(NULL,IDC_ARROW);
	wClass.hInstance = GetModuleHandle(NULL);
	RegisterClassExA(&wClass);
	_windowHandle = CreateWindowExA(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,WINDOW_CLASS_NAME,WINDOW_CAPTION, WS_OVERLAPPEDWINDOW |WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
		0, 0, 640, 480, NULL, NULL, GetModuleHandle(NULL), NULL);
	write2log("creating open gl window ");
	if( _windowHandle != NULL) {
		_gState = new MainMenu();
		GLuint PixelFormat;
		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,i
			0,
			0,
			0,
			0, 0, 0, 0,ignorovány
			16,
			0,
			0,
			PFD_MAIN_PLANE,vrstva
			0,
			0, 0, 0
		};
		

		_hdc = GetDC(_windowHandle);

		PixelFormat=ChoosePixelFormat(_hdc,&pfd);
		SetPixelFormat(_hdc,PixelFormat,&pfd);
		_hrc = wglCreateContext(_hdc);
		BOOL context = wglMakeCurrent(_hdc,_hrc);
		RECT screen;
		GetClientRect(_windowHandle, &screen);
		OnResize(screen.right,screen.bottom);
		
		ShowWindow(_windowHandle,SW_SHOW);
		SetForegroundWindow(_windowHandle);						
		SetFocus(_windowHandle);

		InitOpenGL();
		_isPaused = false;

		loop();
	}
}

/**
Game message loop
*/
void WindowsGameLoop::loop() {
	while(1) {
		MSG msg;
		if(_isActive && !_isPaused) {
			while(PeekMessage(&msg,_windowHandle,0,0,PM_REMOVE)) {
				TranslateMessage(&msg);
				DispatchMessage(&msg);
				if(msg.message == WM_CLOSE) break;
			}
			
			// game update
			if(_gState != NULL) {
				_gState->refresh();
				SwapBuffers( _hdc );
			}
			// if quit break the loop
			if(msg.message == WM_CLOSE)
				break;
		}else {
			if(GetMessage(&msg,_windowHandle,0,0) <= 0 ){
				break;	
			}// wait for message
			// if quit break the loop
			if(msg.message == WM_CLOSE) 
				break;
			// pass message to app
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
}

[/codesyntax]

SDL platform independent code:

[codesyntax lang="cpp" tab_width="1"]

bool GameLoop::init() {
	if(SDL_Init(SDL_INIT_EVERYTHING) >= 0) {
		_window = SDL_CreateWindow(WINDOW_CAPTION,SDL_WINDOWPOS_CENTERED,
			SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT,SDL_WINDOW_OPENGL);

		if(_window != 0 ) {
			_glContext = SDL_GL_CreateContext(_window);

			_renderer = SDL_CreateRenderer(_window,-1,0);

			SDL_SetRenderDrawColor(_renderer, 0, 0, 0, 255);
			SDL_RenderClear(_renderer);
			// show the window
			SDL_RenderPresent(_renderer);
			_quit = false;
			_active = true;
			_gState = new MainMenu();
			// open gl init...
			InitOpenGL();
			OnResize(WINDOW_WIDTH, WINDOW_HEIGHT);
			return true;
		}
	}
	return false;
}

/**
  Main game loop
*/
void GameLoop::start(){
	while(!_quit) {
		SDL_Event sdlEvent;
		if(_active){
			SDL_PollEvent(&sdlEvent);
			handleEvent(sdlEvent);
			_gState->refresh();
			SDL_GL_SwapWindow(_window);
		}else {
			SDL_WaitEvent(&sdlEvent);
			handleEvent(sdlEvent);
		}
	}
}

[/codesyntax]

As you can see the library reduced the code a little :-). The main advantage is that you don't have to know the platform specialties.

Result
Here you can see the main menu screen using multi platform game loop on Windows, Mac OS X and Ubuntu Linux.

Windows MacOS X

screen_ubuntu
 

Conclusion
I believe that I finally resign to reinvent the wheel *joke*. I have also several issues with right linking SDL library especially on Ubuntu 64bit. However it was worth it. The next thing to do is to handle keyboard input and show another game screen. Well I guess that this will be the easy part. The harder one will be terrain rendering which I will be main task to focus.

Leave a Reply

Your email address will not be published. Required fields are marked *

* Copy This Password *

* Type Or Paste Password Here *