C#

Fools ignore complexity; pragmatists suffer it; experts avoid it; CREATIVES remove it.

Tuesday, November 10, 2009

Generic interface game flow



Question? is that possible to create an architecture, interface based, which allows you programming a game flow independent of his content?

answer: yes!

Note that Minigame_Pinball and Minigame_Pinball_Score are just interface implementations that don't know nothing about the game flow, they are using IGameObj to connect to the flow and returning a condition from FLOW_CONDITIONS, they register themselves and they are allocated only when I need them :) (it's easy to pre-allocate if you want to)

Now, you can copy the code, create your own game class, derive that from IGameCreateObj and implementing your own IGameObj and have fun!!!!



//class MyOwn : public IGameCreateObj<MyOwn>

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <typeinfo>
#include <time.h>

using namespace std;

enum FLOW_CONDITIONS { LOOP = 0, NEXT_FLOW, BACK, NEXT };

class IGameObj
{
public:
virtual void Init() = 0;
virtual FLOW_CONDITIONS Loop() = 0;
virtual void Uninit() = 0;
virtual IGameObj *CreateANew() = 0;

virtual ~IGameObj(){};
};

template<class X>
class IGameCreateObj : public IGameObj
{
public:
//make singleton
static X* GetInstance() { static X x; return &x; }
//able to get a copy
IGameObj *CreateANew() { return new X(); };

virtual ~IGameCreateObj(){};
};

class Minigame_Pinball : public IGameCreateObj<Minigame_Pinball>
{
private:
double timeOut;

public:
void Init() { timeOut = clock(); };
FLOW_CONDITIONS Loop()
{

timeOut += clock()-timeOut;

if (timeOut > 3000)
return NEXT;

cout << "Minigame_Pinball " << timeOut << endl;

return LOOP;
};

void Uninit() {};
};

class Minigame_Pinball_Score : public IGameCreateObj<Minigame_Pinball_Score>
{
public:
void Init() {};
FLOW_CONDITIONS Loop() { return LOOP;};
void Uninit() {};
};

struct FlowCondition
{
IGameObj *obj1;
FLOW_CONDITIONS fc;
IGameObj *obj2;
};

class IGameFlow
{
public:
virtual bool Loop() = 0;
};

class GameFlow : public IGameFlow
{
private:
vector<const FlowCondition* const> _list;

IGameObj *_currentObj;

IGameObj *AllocType( IGameObj *obj_info )
{
IGameObj *mg = NULL;
mg = obj_info->CreateANew();
return mg;
}

FlowCondition *SearchCondition( FLOW_CONDITIONS fc )
{
vector<const FlowCondition* const>::iterator it;
for( it = _list.begin(); it != _list.end(); ++it )
if ((*it)->fc == fc) return (FlowCondition *)*it;
return NULL;
};

public:
GameFlow() : _currentObj(NULL) {};

void RegisterCondition(IGameObj *obj1, const FLOW_CONDITIONS fc, IGameObj *obj2 )
{
FlowCondition *p = new FlowCondition();
p->fc = fc;
p->obj1 = obj1;
p->obj2 = obj2;
_list.push_back( p );

if (_currentObj == NULL)
{
_currentObj = AllocType(obj1);
_currentObj->Init();
}
return;
}

virtual ~GameFlow()
{
vector<const FlowCondition* const>::iterator it;
for( it = _list.begin(); it != _list.end(); ++it )
delete *it;

delete _currentObj;
}

bool Loop()
{
FLOW_CONDITIONS fc = _currentObj->Loop();
if (fc==NEXT_FLOW) return false;

FlowCondition *tmp = SearchCondition( fc );
if (tmp != NULL)
{
_currentObj->Uninit();
delete _currentObj;
_currentObj = AllocType(tmp->obj2);
_currentObj->Init();
}

return true;
}
};

class GameFlowManager
{
private:
vector<const IGameFlow* const> _list;

IGameFlow* _gFlowCurrent;

public:
GameFlowManager() : _gFlowCurrent(NULL) {};

void RegisterFlow(IGameFlow *gflow)
{
if (_gFlowCurrent==NULL) _gFlowCurrent = gflow;
_list.push_back( gflow );
return;
}

void Loop()
{
while(_gFlowCurrent->Loop())
{
}
}
};


int _tmain(int argc, _TCHAR* argv[])
{
GameFlow minigameFlow;
minigameFlow.RegisterCondition( Minigame_Pinball::GetInstance(), NEXT, Minigame_Pinball_Score::GetInstance());
minigameFlow.RegisterCondition( Minigame_Pinball_Score::GetInstance(), BACK, Minigame_Pinball::GetInstance());
minigameFlow.RegisterCondition( Minigame_Pinball_Score::GetInstance(), NEXT_FLOW, NULL );

GameFlowManager game;
game.RegisterFlow( (IGameFlow*)&minigameFlow );
game.Loop();

return 0;
}


Nowadays, I'm working as director of pre-production so "officially" I do not write code, anyway, I hope it can help someone :)