Software architecture
-
- Competition winner
- Posts: 167
- Joined: Sun Jul 19, 2009 11:27 am
- Location: the Netherlands
- Contact:
Software architecture
Hello Guys,
As my applications are growing I have the need of managing my programs.
My today's problems are more associated with program complexity rather then coding itself (for now). This makes it hard to start-up.
I have more plans to extend my applications, therefore at this point it is still possible to apply any structure software.
I don't have any experience with software architecture, so I'm wondering if anyone has any advise of choosing.
Thanks
Escen
As my applications are growing I have the need of managing my programs.
My today's problems are more associated with program complexity rather then coding itself (for now). This makes it hard to start-up.
I have more plans to extend my applications, therefore at this point it is still possible to apply any structure software.
I don't have any experience with software architecture, so I'm wondering if anyone has any advise of choosing.
Thanks
Escen
I create a struct for everything, and then in main.cpp init everything and set some parametres, so it looks like this
I don't know if it is the best solution, but works so far. But should I try multiplayer some day, I will probably run to problems with my player/npc management.
Code: Select all
initShaders(video);
levelManager levelMgr(smgr);
weatherManager weatherMgr(smgr);
grassManager grassMgr(smgr);
treeManager treeMgr(smgr);
weaponManager weaponMgr(smgr);
UIManager UIMgr(device);
npcManager npcMgr(smgr);
levelMgr.loadLevel("./data/levels/example/level.irr");
levelMgr.setLevelFog(SColor(0,128,128,128), 200, 800);
weatherMgr.initWeather();
weaponMgr.loadWeapons("./data/config/weapons.xml");
player* player1= new player(smgr, timer);
player1->setPrimaryWeapon(weaponMgr.createWeaponFromIndex(0), camera);
player1->setSecondaryWeapon(weaponMgr.createWeaponFromIndex(1), camera);
UIMgr.setActivePlayer(player1);
UIMgr.init();
npcMgr.setTerrain((ITerrainSceneNode*)smgr->getSceneNodeFromType(ESNT_TERRAIN));
...
irrRenderer 1.0
Height2Normal v. 2.1 - convert height maps to normal maps
Step back! I have a void pointer, and I'm not afraid to use it!
Height2Normal v. 2.1 - convert height maps to normal maps
Step back! I have a void pointer, and I'm not afraid to use it!
Ok, here is one question: I have 2 classes. Now I need to use method from first class in the second class. What is the best way to do this? I though about passing pointer on first class to second class, but with multiple classes it could get a little messy.
irrRenderer 1.0
Height2Normal v. 2.1 - convert height maps to normal maps
Step back! I have a void pointer, and I'm not afraid to use it!
Height2Normal v. 2.1 - convert height maps to normal maps
Step back! I have a void pointer, and I'm not afraid to use it!
-
- Posts: 1186
- Joined: Fri Dec 29, 2006 12:04 am
You could do it like this:
You have a class Game with takes care of the IrrlichtDevice (init, rendering, dropping, etc.) and other stuff like networking, physics, etc.
Then you pass a pointer of this class to the other classes.
Now you can access all the shared classes (device, physics, etc.) from the game pointer. All without using globals!
You need to use forward declaration for this to work. i.e. don't include the Game.h in Player.h but say:
and only include Game.h in Player.cpp.
Another thing you can do is to derive Game from irr::IEventReceiver and have a bool OnEvent(), so you can handle the events in your game class and access the other classes without too much hassle.
You have a class Game with takes care of the IrrlichtDevice (init, rendering, dropping, etc.) and other stuff like networking, physics, etc.
Code: Select all
class Game
{
public:
bool init() { /* init device here */ }
irr::IrrlichtDevice* getIrrlichtDevice() const { return device; }
private:
irr::IrrlichtDevice* device;
Physics* physics;
};
Code: Select all
class Player
{
public:
Player(Game* game) :
game(game)
private:
Game* game;
};
You need to use forward declaration for this to work. i.e. don't include the Game.h in Player.h but say:
Code: Select all
class Game; //forward declaration
class Player
{
}
Another thing you can do is to derive Game from irr::IEventReceiver and have a bool OnEvent(), so you can handle the events in your game class and access the other classes without too much hassle.
"Whoops..."
I came to the conclusion that inter-class communication in my next project will be done using a single message queue (e.g. the "select level" dialog needs to tell the "play game" class which level and options were selected). I already have such a queue in my IrrOde wrapper (which is quite extensible btw), but I'm not sure about whether or not I should re-use this one or add another one to my state machine. It will be something like this:
I think I'll try this piece of code once I'm back home ... got no idea if it really works, but it should.
This way you can have all classes communicate with each other without the need to know each other. E.g. the "select level" dialog sends a message with the selected level and some other information to the queue, the "play game" class reads the message and prepares to load the level whereas the "show highscore" class just ignores it. I found that this is quite a flexible approach to such communication things. In my "Stunt Marble Racers" project there are a lot of classes that know a lot of other classes, and a lot of special communication methods (like "selectLevel"), and for the next project I want to get rid of that.
Edit: of course some stuff is missing in the example, it is e.g. necessary to have a "remove message receiver" method in the queue. I think I'll do some additions once I'm home.
Code: Select all
#include <irrlicht.h>
using namespace irr;
using namespace core;
//the message interface to be sent
class IMessage {
public:
virtual u32 getCode()=0;
};
//the receiver interface that handles messages
class IMessageReceiver {
public:
virtual void handleMessage(IMessage *aMessage)=0;
virtual void queueDestroyed()=0;
};
//the messaging queue
class CMessageQueue {
protected:
list<IMessageReceiver *> m_lReceivers;
public:
~CMessageQueue() {
list<IMessageReceiver *>::Iterator it;
for (it=m_lReceivers.begin(); it!=m_lReceivers.end(); it++) (*it)->queueDestroyed();
}
void addReceiver(IMessageReceiver *p) {
//first make sure one receiver is only added once
list<IMessageReceiver *>::Iterator it;
for (it=m_lReceivers.begin(); it!=m_lReceivers.end(); it++) if ((*it)==p) return;
printf("CMessageQueue::addReceiver: adding receiver\n");
m_lReceivers.push_back(p);
}
void removeReceiver(IMessageReceiver *p) {
list<IMessageReceiver *>::Iterator it;
for (it=m_lReceivers.begin(); it!=m_lReceivers.end(); it++) {
printf("CMessageQueue::removeReceiver: removing receiver\n");
m_lReceivers.erase(it);
return;
}
}
void postMessage(IMessage *pMsg) {
list<IMessageReceiver *>::Iterator it;
printf("distributing message\n");
for (it=m_lReceivers.begin(); it!=m_lReceivers.end(); it++)
(*it)->handleMessage(pMsg);
}
};
//an implementation of the message to send a text string
class CTextMessage : public IMessage {
protected:
stringc m_sText;
public:
CTextMessage(stringc sText) { m_sText=sText; }
virtual ~CTextMessage() { }
virtual u32 getCode() { return 23; }
stringc getText() { return m_sText; }
};
//The actual receiver
class CReceiver : public IMessageReceiver {
protected:
CMessageQueue *m_pQueue;
public:
CReceiver(CMessageQueue *pQueue) { pQueue->addReceiver(this); m_pQueue=pQueue; }
virtual ~CReceiver() { if (m_pQueue) m_pQueue->removeReceiver(this); else printf("queue destroyed!\n"); }
virtual void handleMessage(IMessage *aMessage) {
if (aMessage->getCode()==23) { //the code of the text message
CTextMessage *p=(CTextMessage *)aMessage;
printf("text received: \"%s\"\n",p->getText().c_str());
}
}
//make sure we don't try to remove the receiver from a deleted queue
virtual void queueDestroyed() { m_pQueue=NULL; }
};
int main(void) {
//create queue, receiver and a test text message
CMessageQueue *pQueue=new CMessageQueue();
CReceiver *pReceiver=new CReceiver(pQueue);
CTextMessage *pMsg=new CTextMessage("Hello World");
//post the message to all registered receivers
pQueue->postMessage(pMsg);
//cleanup
delete pMsg;
delete pReceiver;
delete pQueue;
return 0;
}
This way you can have all classes communicate with each other without the need to know each other. E.g. the "select level" dialog sends a message with the selected level and some other information to the queue, the "play game" class reads the message and prepares to load the level whereas the "show highscore" class just ignores it. I found that this is quite a flexible approach to such communication things. In my "Stunt Marble Racers" project there are a lot of classes that know a lot of other classes, and a lot of special communication methods (like "selectLevel"), and for the next project I want to get rid of that.
Edit: of course some stuff is missing in the example, it is e.g. necessary to have a "remove message receiver" method in the queue. I think I'll do some additions once I'm home.
Last edited by Brainsaw on Tue May 11, 2010 10:44 am, edited 1 time in total.
Dustbin::Games on the web: https://www.dustbin-online.de/
Dustbin::Games on facebook: https://www.facebook.com/dustbingames/
Dustbin::Games on twitter: https://twitter.com/dustbingames
Dustbin::Games on facebook: https://www.facebook.com/dustbingames/
Dustbin::Games on twitter: https://twitter.com/dustbingames
That is not actually a conclusion. It is called a "decision". Yea?Brainsaw wrote:I came to the conclusion that inter-class communication in my next project will be done using a single message queue
Dreaming.I think I'll try this piece of code once I'm back home ... got no idea if it really works, but it should.
This could get messy!This way you can have all classes communicate with each other without the need to know each other.
I can hear birds chirping
I live in the Eye of Insanity.
I live in the Eye of Insanity.
Yes, after messing around with the call-method communication method I made the decision that next time it's going to be a message queue.Ulf wrote:That is not actually a conclusion. It is called a "decision". Yea?Brainsaw wrote:I came to the conclusion that inter-class communication in my next project will be done using a single message queue
I'll see this afternoon or eveningUlf wrote:Dreaming.I think I'll try this piece of code once I'm back home ... got no idea if it really works, but it should.
[/quote]Ulf wrote:This could get messy!This way you can have all classes communicate with each other without the need to know each other.
Well ... it would be possible to have e.g. send one message directly to another class, but using a message distributor is imho a good idea. I use this architecture in my IrrODE wrapper quite a lot, and it is still quite clean - I just missed using it for most of the other communication when it came to my project.
Dustbin::Games on the web: https://www.dustbin-online.de/
Dustbin::Games on facebook: https://www.facebook.com/dustbingames/
Dustbin::Games on twitter: https://twitter.com/dustbingames
Dustbin::Games on facebook: https://www.facebook.com/dustbingames/
Dustbin::Games on twitter: https://twitter.com/dustbingames
Just tested my code. Did (almost) work. A missing semicolon, and some typos. Updated my original post
Dustbin::Games on the web: https://www.dustbin-online.de/
Dustbin::Games on facebook: https://www.facebook.com/dustbingames/
Dustbin::Games on twitter: https://twitter.com/dustbingames
Dustbin::Games on facebook: https://www.facebook.com/dustbingames/
Dustbin::Games on twitter: https://twitter.com/dustbingames
Well thanks everyone for ideas, but as much as I hate it, I will have to use globals(but only 3 of them! ). I just don't want to rewrite approx. 1/3 of my code.. I will use this approach in my next project
irrRenderer 1.0
Height2Normal v. 2.1 - convert height maps to normal maps
Step back! I have a void pointer, and I'm not afraid to use it!
Height2Normal v. 2.1 - convert height maps to normal maps
Step back! I have a void pointer, and I'm not afraid to use it!
Ok, I have a little problem with that. Here is what I do:randomMesh wrote:...
this is how the architecture looks like
#include "engine.cpp"
#include "renderer.cpp"
and in engine.cpp the class from renderer.cpp(class Crenderer) is needed to be used. so in engine.cpp, I do
Code: Select all
class Crenderer; //forward declaration
So what is the right way do do this?
irrRenderer 1.0
Height2Normal v. 2.1 - convert height maps to normal maps
Step back! I have a void pointer, and I'm not afraid to use it!
Height2Normal v. 2.1 - convert height maps to normal maps
Step back! I have a void pointer, and I'm not afraid to use it!
hm, I dont know, maybe I'm wrong there. I don't have .h's, just .cpp's.
Edit: oh, right, now I finally understand how to do forward declaration of class
Edit: oh, right, now I finally understand how to do forward declaration of class
irrRenderer 1.0
Height2Normal v. 2.1 - convert height maps to normal maps
Step back! I have a void pointer, and I'm not afraid to use it!
Height2Normal v. 2.1 - convert height maps to normal maps
Step back! I have a void pointer, and I'm not afraid to use it!
OK, there seems to be another problem. When I forward declare class MyClass, I can now use that class, but I still can't use methods from it. How do I make also methods available? In other words, class A needs to use methods from class B and class B methods from class A.
irrRenderer 1.0
Height2Normal v. 2.1 - convert height maps to normal maps
Step back! I have a void pointer, and I'm not afraid to use it!
Height2Normal v. 2.1 - convert height maps to normal maps
Step back! I have a void pointer, and I'm not afraid to use it!
-
- Posts: 73
- Joined: Sat Jun 27, 2009 6:52 am