Reflective Factory (Maybe a Not So Beginner Topic)

Started by
13 comments, last by Shamino 18 years, 4 months ago
http://www.gamedev.net/reference/articles/article1415.asp That looks like THE BEST way to manage all your Scene_Objects, it looks ultimately complex and Object Oriented to the 100% MAX the language can go.... Is there anyone out there who can turn this article into a tutorial of some sort? The problem I have come across in my Game Engine is that I have to dynamically allocate objects into memory during runtime... For instance, if I click the build button on Worker A, to build, lets say a barracks, I have a Barracks object defined, but I need to create a new instance of that barracks object Right then and there on the fly. Thats where the Reflective Factory Design comes in... It is essentially a Class Factory.. (during runtime!) Is there anyone out there that can give a brief explanation of how the syntax of the whole thing works? I know the code has documentation, but it seems hard to follow. (Maybe I'm just a noob) Anyone willing to tackle this beast of a question?
----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------
Advertisement
pass the factory a name associated with concrete implementation of the class. Make sure each building has a "GetBuildingTypeId" function in there somewhere that's abstract and has to be implemented.

If you do something like that, you make it easier for yourself to plugin and use a scripting language down the line - as you just pass across either a string or number or whatever that IDs that class and the factory creates that instance for you.

Otherwise you could mess with the RTTI stuff in C++ itself. But I would err on the side of K.I.S.S.

http://www.freshsources.com/newcpp.html has a brief overview. [Google] probably is your friend.
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
I'm studying the whole class factory design on wikipedia, been working on it since this post was created.
----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------
So...

A class factory is essentially this: A class with a virtual constructor that takes a type argument.. In this case, an Object class with a virtual constructor..

So, for every individual and unique object we wanna make, we define them with a type variable, and then give them their uniqueness in the definition of the virtual constructor that Object would give it...

I'm assuming you can put function definitions in constructor definitions, otherwise how could you pass unique functions of each individual object into the factory?


No, this is more of a singleton Class factory design, a Class factory that builds all the class factories in your program, found this on wikipedia...

class Control {    public void display() {        // ...    }    // ...}class TextBox : Control {    // ...}class CheckBox : Control {    // ...}abstract class Field {    public void display() {        Control c = createControl();        c.display();    }    public abstract Control createControl();}class TextField : Field {    private string value;    public Control createControl() {        TextBox t = new TextBox();        t.setText(this.value);        return t;    }}class BooleanField : Field {    private boolean value;    public Control createControl() {        CheckBox c = new CheckBox();        c.setChecked(this.value);        return c;    }}


Trying to figure out that bit of code there, and how I would implement it into my scene management classes

[Edited by - Shamino on December 15, 2005 8:15:33 AM]
----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------
I'm not sure a class factory is the best choice here.

Factories are usually required when you want to make an object, but don't know [or care] what the resultant type is.

For example, if you had a tank, you might call a renderable_factory to "make me a tank's picture". The factory could then return whatever you configured as the picture of the tank. Using a factory makes it easier to change that image to an animation later on.

For the scenario you described, this is not the case. You want to make a barracks, and only really a barracks. You just want a callback mechanism to create it. Perhaps something like this?

template <typename T>struct Build_Requirements{    static int gold;    static int wood;    static int oil;}; template <typename T>struct Build{    //    // Callback functor for buttons to make buildings    //    void operator()(Player owner, int x, int y){        //        // Creates building type T at x,y under ownership of player        //        object  *building=0;        if(owner.gold >= Build_Requirements<T>::gold && owner.wood >= Build_Requirements<T>::wood && owner.oil>=Build_Requirements<T>::oil){            building=new T(owner,x,y);            //            // these next two lines are better off in the             //  T constructor            //            owner.buildings.push_back(building);            game.objects.push_back(building);         }else{            // Play feedback sound         }    }};// Elsewhereint Build_Requirements<barracks>::gold = 500;    int Build_Requirements<barracks>::wood = 250;int Build_Requirements<barracks>::oil = 0;// Set key to create it.barracks_button.onClick = new Build<barracks>();


It's still sort of a factory, but formal factories [from what I've seen] aim more for that abstraction in construction which is not the goal here.

[edit: Though making game objects types is dubious imo. A building is a type, a barracks is a parameter of a building, not inherited from it (IMO). Far too often you need to know what sort of building it is, and C++ doesn't allow for type knowledge to be easily garnered.]
I guess you're right, I realy don't need all the information regarding a barracks object...

I just need another picture of a barracks on my screen... right?

Look at this though, an object factory article on this website, aimed at handling objects (monsters in this case)

Download the source to see what I mean..

http://www.gamedev.net/reference/articles/article2097.asp
----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------
I'll try to implement what you put there telastyn, I just hope my rendering engine comes full circle soon... Been programming for a month now with nothing to look at.

This is the template that handles all objects that need to be built right, I can use this template to build anything I want, considering I have a class "barracks" with a pointer to a model in it, right? Well, the way you have it setup there, is that that is the template for all "building" objects, I can probably make several template classes that are specialized to meet the needs of what I'm adding to the world.

template <typename T>struct Build{    //    // Callback functor for buttons to make buildings    //    void operator()(Player owner, int x, int y){        //        // Creates building type T at x,y under ownership of player        //        object  *building= NULL;        if(owner.gold >= Build_Requirements<T>::gold && owner.wood >= Build_Requirements<T>::wood && owner.oil>=Build_Requirements<T>::oil){            building=new T(owner,x,y);            //            // these next two lines are better off in the             //  T constructor            //            owner.buildings.push_back(building);            game.objects.push_back(building);         }else{            // Play feedback sound         }    }};
----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------
Right.

The actual factory looks quite nice (and messy), but [imo] the example's inheritance is poor. Ogre and Goblin don't need to be their own types. Since you're more often than not going to need to differentiate between Ogres and Goblins [imo] they're actually better not being their own types in a language like C++.

Anyways, that's just design opinion...

Quote:
Trying to figure out that bit of code there, and how I would implement it into my scene management classes


So what's the difficulty with that code? What don't you understand about that pattern? What sort of scene management classes do you already have, and for what use are you trying to apply that factory with them?

[edit:

Quote:
This is the template that handles all objects that need to be built right, I can use this template to build anything I want, considering I have a class "barracks" with a pointer to a model in it, right?


Eh... How about you more fully explain what you want to do?

Maybe some code for what you've currently got, with a few comments about where you think the factory needs used?

/edit]
Alright, let me post a large bit of my current code. Theres alot of holes in it as of now, the whole project hasn't come full circle, and theres really nothing to show for all the hard work I've been doing.

This is Object.h
template <typename T>struct Build{    //    // Callback functor for buttons to make buildings    //    void operator()(Player::PLAYER_NUMBER, int x, int y, int z)	{        //        // Creates building type T at x,y under ownership of player        Object  *Building= NULL;		switch (Player::PLAYER_NUMBER)		{		case GAME_MASTER:				Building = new T(Player::PLAYER_NUMBER, x, y, z);			break;		case PLAYER_ONE:				Building = new T(Player::PLAYER_NUMBER, x, y, z);			break;		case PLAYER_TWO:	           Building = new T(Player::PLAYER_NUMBER, x, y, z);			break;		case PLAYER_THREE:	           Building = new T(Player::PLAYER_NUMBER, x, y, z);			break;		case PLAYER_FOUR:			break;	           Building = new T(Player::PLAYER_NUMBER, x, y, z);		case PLAYER_FIVE:	           Building = new T(Player::PLAYER_NUMBER, x, y, z);			break;		case PLAYER_SIX:	           Building = new T(Player::PLAYER_NUMBER, x, y, z);			break;		case PLAYER_SEVEN:	           Building = new T(Player::PLAYER_NUMBER, x, y, z);			break;		case PLAYER_EIGHT:	           Building = new T(Player::PLAYER_NUMBER, x, y, z);			break;		case WORLD:			           Building = new T(Player::PLAYER_NUMBER, x, y, z);			break;		}            //            // these next two lines are better off in the             //  T constructor            //         owner.buildings.push_back(building);        game.objects.push_back(building);    }};// Elsewhere// Set key to create it.//barracks_button.onClick = new Build<barracks>();#endif

This is Player.h
class Player{public:	bool AIControlled;		enum RACE_NUMBER	{		HUMANS		// Add races here	};	enum PLAYER_NUMBER	{		GAME_MASTER,		PLAYER_ONE,		PLAYER_TWO,		PLAYER_THREE,		PLAYER_FOUR,		PLAYER_FIVE,		PLAYER_SIX,		PLAYER_SEVEN,		PLAYER_EIGHT,		WORLD	};};

This is World.h
#include "Object.h"#ifndef WORLD_H#define WORLD_Hextern std::vector<Object*> Scene_Objects;class World{public:	World();	~World();	int  AddObjectToWorld(int type);	void AddObject(Object * Obj);	void RemoveObjectFromWorld();	void RemoveAllObjectsFromWorld();	private:		float WWidth;	float WLength;	float WHeight;};#endif

This is World.cpp
#include "World.h"std::vector<Object*> Scene_Objects;std::vector<Object*>::iterator Iter;void World::AddObject(Object * Obj){	Scene_Objects.push_back(Obj);};int World::AddObjectToWorld(int type){    Object * Obj = NULL;    switch (type)    {    case OBJ_CROSS:        Obj = new Cross();        break;    }	    if (Obj != NULL)    {		World::AddObject(Obj);    }	return 0;};void World::RemoveObjectFromWorld(){	for (Iter = Scene_Objects.begin(); Iter!=Scene_Objects.end();){    if ((*Iter)->MarkedForDeletion)    {        delete *Iter;        Iter = Scene_Objects.erase(Iter);    }    else    {        ++Iter;    }}};void World::RemoveAllObjectsFromWorld(){	for ( size_t i = 0; i < Scene_Objects.size( ); ++i )	{ 		delete Scene_Objects;	}	Scene_Objects.clear();};

This is Renderer.h
#include "Vector3.h"#include "World.h"struct     BasicTri {      Vector3 P1, P2, P3; 	      int     Texture;       BasicTri()     {          }       BasicTri(const BasicTri& t)      {           P1 = t.P1;           P2 = t.P2;           P3 = t.P3;           Texture = t.Texture;      }       BasicTri& operator=(const BasicTri& t)      {           if( &t != this ){                P1 = t.P1;                P2 = t.P2;                P3 = t.P3;                Texture = t.Texture;           }           return *this;      } };extern std::vector<BasicTri*> mBasicTri;class GLRenderer{public:	void	AddBasicTriToRenderer(BasicTri* t);	void	DrawSceneObjects();	void	RenderBasicTriangles();	void	RenderMS3DModels();};

This is Renderer.cpp
#include <windows.h>#include <gl\gl.h>#include <gl\glu.h>#include <gl\glaux.h>#include "NeHeGL.h"	#include "Renderer.h"	std::vector<BasicTri*> mBasicTri;void GLRenderer::AddBasicTriToRenderer(BasicTri* t){	mBasicTri.push_back(t);}void GLRenderer::RenderBasicTriangles(){	if (!mBasicTri.size())	{		return;	}	std::vector<BasicTri*>::iterator	ptr;	for (ptr = mBasicTri.begin(); ptr != mBasicTri.end(); ptr++)	{		BasicTri * temp = *ptr;		glBegin(GL_TRIANGLES);		glVertex3f(temp->P1.x,temp->P1.y,temp->P1.z);		glVertex3f(temp->P2.x,temp->P2.y,temp->P2.z);		glVertex3f(temp->P3.x,temp->P3.y,temp->P3.z);		glEnd();	}	}void GLRenderer::DrawSceneObjects(){}


Alrighty, Alot to take in here, and if you're willing to even bother to pay attention Telastyn I admire you.

Lets start from the top.

1. Object.h:

Currently a host to a couple different and possibly unrelated Classes and Functions.

One thing it does host is the Object master class, Object. All object objects can do is store a location in 3d space and be marked either true or false for deletion.

It holds an "OBJECTS" enum to give a number value to every type of object I wish to implement (something I plan on getting rid of, it isn't OPP and it is clunky!)

And finally, it contains the Build template you gave me (the thing that is supposed to replace that ENUM), I set it up so I have 10 main logic operators instead of a super huge chain of incoherant if/elses

2. Player.h

Currently hosts only a player class (that I may need to build a template for, seeing as how I'll possibly need 10 players in the same game instance at the same time). Required for the Build Template to work. (see include files for Object.h)

3. World.h/World.cpp

This is where my Management functions and classes go for my RenderVector management (the ultimate goal is to have the renderer deal with only one vector of objects that are sent to the scene)

Holds a Vector of Scene_Objects (the vector that will be sent to the renderer)

AddObjectToWorld(): Currently takes an int type that would create a switch inside the function for the object type (note back to where I was talking about clunky about that enum, this is the main area where I feel I need an object factory). This class works as a wrapper to the AddObject() function and supplies it with the necessary info on what to add to the renderer vector.

RemoveObjectFromWorld(): Iterates through the render vector to check for objects marked for deletion, and quite simply, erases them.

RemoveAllObjectsFromWorld(): Self Explanatory

4. Renderer.h/Renderer.cpp

This is where the magic happens, in the future sometime anyways.

DrawSceneObjects(): No use yet, Ultimately will end up reading a Scene_Objects vector given from the World class and its nifty vector management functions.


There it is, the grandmaster plan, probably more than a few holes in it.

[Edited by - Shamino on December 15, 2005 2:03:42 PM]
----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------
Well, it'd be decidedly rude to ask for code and then ignore the code once posted...

First off, why the big switch statement in Build? All of the cases do the same thing [except for #4 which breaks without building the object, which I'd guess is a typo]. Plus there's no variable name for the PLAYER_NUMBER, meaning it shouldn't compile. And the kind of example code I put at the bottom to add the created object to the game and player records will need commented, since you don't have that yet.

Second, why would you need a template to make 10 players?

struct Player{    // Note, these fields will likely be private/protected    //  but are made public for exmaple simplicity    string    name;    int       number;    // other stuff    Player(string in_name, int in_number):name(in_name), number(in_number){}};class Game{//// Once again, overly public//public:    vector<Player>    Players;};// for whatever reason we need to add a player,//  perhaps the game is just created and the number need added//  perhaps one connects via network...Game    current_game;Player  connecting_player("Shamino", 1);current_game.Players.push_back(connecting_player);


Once again, a terrible example, but something basic to show how to allow arbitrary number of players. No fancy factory needed, no templating, simple class instantiation.


To be a little blunt, it looks as though you've bit off tons more than you can chew. Start with little parts. Make something to represent the world, make test code to display that it works. Make a few test objects to populate the world, make sure they work. Make something to draw a cube, make sure it works. Put the cube in your world, make sure it works. Make differently colored/sized cubes depending on what test object is in your world....

Just increasing complexity. Not only does this give you the persistant feeling of success, it helps you do OOP better since you're focused only on one small part of the puzzle.

Hope this helps a little, and let me know if you've any more questions.

This topic is closed to new replies.

Advertisement