Jump to content

  • Log In with Google      Sign In   
  • Create Account


Reflective Factory (Maybe a Not So Beginner Topic)


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
14 replies to this topic

#1 Shamino   Banned   -  Reputation: 100

Like
0Likes
Like

Posted 14 December 2005 - 05:56 PM

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?

Sponsor:

#2 paulecoyote   Members   -  Reputation: 1060

Like
0Likes
Like

Posted 15 December 2005 - 01:39 AM

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.

#3 Shamino   Banned   -  Reputation: 100

Like
0Likes
Like

Posted 15 December 2005 - 01:41 AM

I'm studying the whole class factory design on wikipedia, been working on it since this post was created.

#4 Shamino   Banned   -  Reputation: 100

Like
0Likes
Like

Posted 15 December 2005 - 02:15 AM

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]

#5 Telastyn   Crossbones+   -  Reputation: 3722

Like
0Likes
Like

Posted 15 December 2005 - 03:05 AM

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
}
}
};

// Elsewhere

int 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.]

#6 Shamino   Banned   -  Reputation: 100

Like
0Likes
Like

Posted 15 December 2005 - 04:25 AM

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

#7 Shamino   Banned   -  Reputation: 100

Like
0Likes
Like

Posted 15 December 2005 - 05:10 AM

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
}
}
};




#8 Telastyn   Crossbones+   -  Reputation: 3722

Like
0Likes
Like

Posted 15 December 2005 - 05:13 AM

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]

#9 Shamino   Banned   -  Reputation: 100

Like
0Likes
Like

Posted 15 December 2005 - 08:03 AM

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_H

extern 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[i];
}
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]

#10 Telastyn   Crossbones+   -  Reputation: 3722

Like
0Likes
Like

Posted 15 December 2005 - 11:15 AM

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.

#11 Shamino   Banned   -  Reputation: 100

Like
0Likes
Like

Posted 16 December 2005 - 03:39 AM

I would agree with you telastyn about writing too much code at once, I need to take it a few steps back..

#12 Shamino   Banned   -  Reputation: 100

Like
0Likes
Like

Posted 16 December 2005 - 03:47 AM

Here is what I have that I know works, but I need to find a better method of doing this.

Object.h

#include "MS3D.h"
#include "Player.h"

#ifndef OBJECT_H
#define OBJECT_H

class Object
{
public:

bool MarkedForDeletion;

struct Location
{
float locx;
float locy;
float locz;
};

};

class Cross : public Object
{
public:

MS3DModel *Model1;

};



enum OBJECTS
{
OBJ_CROSS
};


World.h

#include "Object.h"

#ifndef WORLD_H
#define WORLD_H

extern 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


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[i];
}
Scene_Objects.clear();
};



The ultimate goal is to create and manage a Scene_Objects vector that will be sent to the renderer for final compilation, it works, (i think), but it is clunky in the fact that if you were to add a new object, you have to change the OBJECTS enum in object.h, as well as add a switch statement to the AddObjectToWorld() function..

What can I possibly do to remedy this design problem?

#13 Lopez   Members   -  Reputation: 122

Like
0Likes
Like

Posted 16 December 2005 - 04:08 AM

Hi there,

I've had my management classes running flawlessly for a while now - i'm not too sure if it's the best way to do it, but it works fine for me.

Right, heer goes....

Every type of objects in my engine, ie. octree, scene object, character, particle emitter has it's own base class.

Above each base class I have a manager class.

The manager class uses a stack/queue of a size definable at execution.

when the engine starts, i have several script files that load all of the content, and the management classes automatically take care of the rest.

For scene objects, I only load one of each, so I define the following in the script
id path
MODEL 0 /.../.../model.3ds

and then define the properties of that object, such as it's physics properties, sound effects, etc.

then i create each instance, but only with the non generic data it requires. so..

INST 0
pos x, y, z
vel x, y, z
etc....

END_INST



So after parsing the script, the engine loads in one of each base object,

and then creates instances for each, only storing pointers to the generic data.

ATM i currently have a scene with over 150 instances of about 10 objects, with 10 characters all working fine like this, and at runtime if you want to add any scene objects, yuo just call the function _scenemanager->addObject( id, pos, vel ), and another object is thrown on the stack.

I know this is slightly different to what you are doing but I hope it helps.





#14 Shamino   Banned   -  Reputation: 100

Like
0Likes
Like

Posted 16 December 2005 - 04:29 AM

That does help alot actually. I was actually wondering whether or not I had to load another model into memory for each object I created, apparently I can just create a new pointer to it, sweet.

#15 Shamino   Banned   -  Reputation: 100

Like
0Likes
Like

Posted 16 December 2005 - 05:02 AM

So this is essentially what your scene_manager looks like, probably a more efficient AddScene_ObjectToWorld() function, but this is the gist of it..


#include "Scene_Manager.h"

std::vector<Object*> Scene_Objects;
std::vector<Object*>::iterator Iter;

void Scene_Manager::AddScene_Object(Object * Obj)
{
Scene_Objects.push_back(Obj);
};

int Scene_Manager::AddScene_ObjectToWorld(int type)
{
Object * Obj = NULL;

switch (type)
{
case OBJ_CROSS:
Obj = new Cross();
break;
case OBJ_SHIP:
// dostuff
break;
}


if (Obj != NULL)
{
Scene_Manager::AddScene_Object(Obj);
}
else
{
MessageBox( NULL, "No Class for the Case, Did you forget to add a class for your enum?", "Error", MB_OK | MB_ICONERROR );
return 0;
}

return 0;
};

void Scene_Manager::RemoveScene_ObjectFromWorld()
{
for (Iter = Scene_Objects.begin(); Iter!=Scene_Objects.end();)
{
if ((*Iter)->MarkedForDeletion)
{
delete *Iter;
Iter = Scene_Objects.erase(Iter);
}
else
{
++Iter;
}
}

};

void Scene_Manager::RemoveAllScene_ObjectsFromWorld()
{
for ( size_t i = 0; i < Scene_Objects.size( ); ++i )
{
delete Scene_Objects[i];
}
Scene_Objects.clear();
};





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS