Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

An example for platform-independent development

0.00/5 (No votes)
22 Mar 2005 1  
A platform independent game model.

Introduction

During the last month, I’ve read many discussions about writing platform-independent applications. Such discussions make many beginners in programming unsure which library to use or which language would be the right choice. I even read arguments about disappearing importance of C++ because of the rising need of platform-independent development. In this article, I want to show in a little C++ game-example, that you do not have to possess great knowledge in specific libraries to port your application. It is more important to develop a good abstraction of the platform depending tasks.

Background

The first step you should do is wrapping, wrapping, wrapping. Therefore you should first identify the functions of your code where you have to use platform dependent functionality. In my game-application, I was very surprised that there was not too much of such functionality at all. First let’s have a look at the main parts of the application:

  1. User-Interaction
  2. Drawing
  3. Game-Logic

The only point that really is platform-dependent is number 2. Number 1 and 3 can be done easily using C++ and the STL. Is that really everything? No, you also have to find a way to run the game loop in some kind of message loop, which lies within your application window. So the principle task was to create a wrapper that is able to do the drawing and which performs the main loop for the app. I decided to write an abstract class as an interface for all required functions. Let’s have a look at its definition:

class GdiWrapper
{
public:
    GdiWrapper(){};
    //making the game known to the Wraper for callback
    void SetGameNotifier( GameNotifier* pTheGame ){ m_pTheGame = pTheGame; }
    virtual ~GdiWrapper(){};//all deinitialisation
    //asking the user for Input
    virtual bool GetUserInput( char* pstrStringOne, char* pstrStringTwo ) = 0;
    //outputs a Mesage on the Screen
    virtual void MessageOutput( char* pstrMessage ) = 0;
    virtual void OutputGameInfo( char* pstrText ) = 0;//outputs an Info Text    
    virtual int DrawLine( int x1, int y1, int x2, int y2 ) = 0;//Draws a Line
    //Draws a Scale on the Playfield
    virtual int DrawScale( int x1, int y1, int x2, int y2 ) = 0;
    //Draws the Playstone Circles
    virtual void DrawCircle( int x, int y, int Radiant, 
           bool bFilled, unsigned long dwColor ) = 0;
    //Draws something at the End of a Playfield - Row
    virtual void DrawEndLineText( int x, int y, int iCoordinate ) = 0;
    //Shows the Visualisation Window and inits the Main - Loop
    virtual void RunMainLoop(void) = 0;
    //returns if the Apps Message-loop is still running
    virtual bool LoopRunning(void) = 0;
protected:
    GameNotifier* m_pTheGame;
};

These functions are everything you need to visualize the game. That way you don’t need to wrap whole GUI-functionality, and you only need some functions that handle some tasks you need in your application. For porting the application, all you have to do is to inherit from GdiWrapper and implement the required functionality. If you take a look at the DrawEndLineText(...) function and its use in different porting of the GdiWrappers, you will notice, that only the console implementation really performs functionality to draw the coordinates at the end of the line.

Using the code

All versions of the code are initialized in a very similar way:

#include "Game.h"


int main(int argc, char* argv[])
{
    GdiWrapperKonsole theGdi;//instance of the choosen wrapper
    Game theGame( PlaySize( 5,5 ), &theGdi );//initializing the game
    theGame.Run();//running the game
    return 0;
}

In the MFC - Version, the initialisation is made in the InitInstance() of the application. You see, that the only work to do for porting, is to write a new GdiWrapper and initialising the game-instance with it.

Conclusion

Writing platform independent code does not always have to be a question of the used libraries. The development of a portable application may take a little more time, but afterwards porting the application shall be quite easy. You don’t always need the complete functionality of a large toolkit like QT, so it is often sufficient to just use some functionality you really need in your application. It’s never wrong to test your code during development on different compilers (you will be surprised to see how often I used BOOL, DWORD and other MS-specific defines). These are some major aspects you need to be careful about:

  1. Try to avoid platform specific definitions (e.g. MS specific DWORD, BOOL...).
  2. Keep your application data separated from your visualization (MVC-pattern, DOC/View...).
  3. Work with different compilers and test on different systems.
  4. When you write a wrapper, always divide the wrapped tasks into simple and understandable functions.

However, my code surely is not optimal - I think the OpenGL-Implementation with the SDL shows that, but only because during developing the game classes, I never planned to write such an implementation. I'm sorry for possible problems compiling the QT, SDL and wxWindows example, but I only tested it on my Linux with KDevelop 3.0. The MFC and console implementation was done with the VS6.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here