Sign in to follow this  

Design problem: Abstract method needs access to "outer" data.

This topic is 4662 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello guys! I have a little design problem. Let me show you a little example. Here a simple font class that allows to load the font into memory with a definiton file:

abstract class Font
{
   public abstract void LoadFromDefFile( string fileNameAndPath );
}




Now I have a BitmapFont class which needs Textures to render itself. And that is the problem! - see code -

class BitMapFont : Font
{
   public void LoadFromDefFile( string fileNameAndPath )
   {
      // But now I need a texture manager. What is the best way to access one?
      // a) Singleton TextureManager ? Would make it easier... but no!
      // b) have a reference to the texture manager inside the BitMapFont class - hmm I need the manager only once here!
      // c) Haha! Just make a global texturemanager! No! Same as singleton! I don't even have a single global in my entire lib!
      // d) ...

      // How would you do it?

   }
}




Thank you, Riddle! PS.: Yes, this isn't the full font class-graph! It's: abstact Resource -> abstract Font -> abstract BitMapFont -> GLBitMapFont / D3D9BitMapFont I may drop the abstract BitMapFont. Im not sure what data I could share over the D3D9 and Gl BitMapFont. [Edited by - Riddle on March 10, 2005 2:00:01 AM]

Share this post


Link to post
Share on other sites
Well, yes!

Because the font definition files itself support more than one format and the code above was just an example.
I had this problem in more than one case.

Thanks,
Riddle.

Share this post


Link to post
Share on other sites
I see two more options that you may have overlooked:

1) Pass in a reference to the texture manager as a parameter to the method.

2) Rather than passing in the filename and path as a parameter maybe you could just pass some sort of texture handle used by your texture manager to identify resources. This could work similarly to how OpenGL gives you handles to resources.

Apart from choosing a solution that you feel is the most elegant, you also need to consider your requirements in terms of how you plan to extend or alter your application in the future.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
You could pass any info like texture managers as parameters to the constructor if you really insist on having texture managers separate.

I would just have a single texture manager for basic projects, and probably even for advanced ones :P

Share this post


Link to post
Share on other sites
Quote:
Original post by SmooJ
I see two more options that you may have overlooked:

1) Pass in a reference to the texture manager as a parameter to the method.

2) Rather than passing in the filename and path as a parameter maybe you could just pass some sort of texture handle used by your texture manager to identify resources. This could work similarly to how OpenGL gives you handles to resources.



1.) That will not work because it would break the abstract system. A TfT font doesn't need a texture!

2.) Same.



Quote:

Apart from choosing a solution that you feel is the most elegant, you also need to consider your requirements in terms of how you plan to extend or alter your application in the future.

I think my requirements are too high sometimes. But oh well.. better than too low!



Quote:

But since you're so deadset against them, perhaps some sort of registered callback or thread spawn to fetch the texture?

Yes, I don't like them :)!
Thread spawn? Umm.. could you explain that please. (:




Thank you for your help!
Riddle

[Edited by - Riddle on March 10, 2005 3:36:01 AM]

Share this post


Link to post
Share on other sites
Hi,
Here is my own design. I have:
- a system class: it stores all managers
- a texture manager class: name says it all.
- a task class: this was inspired from Game Programming Gems 1. This class stores references to AudiVideo Layers and Logic Layers. This class handles the game loop and delegates to the AVLayer the rendering process and to the LLayer the logic process (user Input, IA, ...).
- a Font manager: this font manager stores references to a generic font class. It also doubles as a font constructor. The font manager is initiated with a reference to the the texture manager by the system class.
- a generic Font class: base font class like you
- a bitmap Font class: derived one.

Here is what happens at game initiation:
- The system class creates all managers (singleton) according to the system specs (OpenGL/Windows, DirectX/Windows, else ...).
- The system class initiates the task manager with references to managers (font manager for example).
- The system class initiates the task manager with the game task.
- The game task creates the AVLayers and LLayers and initiates them. This creation is done through calling the add_layer method from the task manager. The task manager initiates the layers with references to needed managers (AVLayer gets a reference to the font manager).
- The AVLayer then loads in the font file through the font manager and is all set. The font manager manages to load in the texture through its own reference to the texture manager.
- At each update, the AVLayer selects the Font ID and calls the print method from the font manager.

Now, a little pseudo-code to make everything clearer about how the manager works:

class BaseFont:
public:
Font_Description
Texture_ID
Texture_FileName
virtual void Init(void) //this is overloaded to provide proper font initiation
virtual void Print(x,y,string) //this is overloaded to provide proper font printing
.
class FontManager
public:
void Init(TextureManager)
bool Select(FontID): search for the font ID. If exists, stores the reference into currentfont
void Print(x,y, string): call TextureManager->Select(currentFont->TextureID) then call currentFont->Print method
void Load(FontID, FontFileName): Loads a font into CurrentFont then add CurrentFont to the font list. Doubles as a font constructor
Calls TextureManager->Load(currentFont->TextureID, currentFont->TextureFileName)
private:
BaseFont* CurrentFont
std::list<BaseFont*> Fonts
.
class FontAudioVideoLayer
public:
void Init(FontManager*): setup the FntMgr reference then the user can call FntMgr->Load(155,"Myownfontfile.fnt")
void Update(elapsed_time): if(FntMgr->Select(155)) FntMgr->Print(10,100,"Hello World !")
void Close(void): FntMgr->Unload(155) //I remove the font loaded
private:
FontManager* FntMgr


Hope that helps.
Ghostly yours,
Red.

Share this post


Link to post
Share on other sites
Here's how I'd do it (using C++, sorry):

class BitmapFont : public Font
{
private:
TextureManager & m_TextureManager;
public:
BitmapFont( TextureManager & tm )
: m_TextureManager( tm )
{
}
void LoadFromDefFile( const std::string & fileNameAndPath )
{
m_TextureManager.load_new_texture( fileNameAndPath );
//...
}
};

//...

TextureManager my_manager; //create a new texture manager
BitmapFont my_font( my_manager ); //create a new bitmap font that will use my manager
my_font.LoadDefFromFile( "..." ); //load the new font



This borrows from the same method used by the C++ STL containers when invoking the manually specified allocators in their construction (for an example of this, see this thread where I create a rather ugly stack allocator which I wouldn't recommend using as it's not very optimized or anything - it's meant as a proof of concept :P). This situation is pretty much the same, only instead of allocating memory, you're allocating textures.

Share this post


Link to post
Share on other sites
Factory pattern perhaps?

The goal here is to seperate special case creation logic from the interface. You want to be dealing with behavior, not oddball prerequisites like "if I want a bitmap I need a texture manager". So, encapsulate that weird stuff into its own little factory and then spend your time dealing with what your true concern is: what a font does and how it will influence your system.

Share this post


Link to post
Share on other sites
Quote:
Original post by Riddle
Quote:
Original post by SmooJ
I see two more options that you may have overlooked:

1) Pass in a reference to the texture manager as a parameter to the method.

2) Rather than passing in the filename and path as a parameter maybe you could just pass some sort of texture handle used by your texture manager to identify resources. This could work similarly to how OpenGL gives you handles to resources.



1.) That will not work because it would break the abstract system. A TfT font doesn't need a texture!

2.) Same.



This approach could still work if you added another layer of abstraction. Rather than passing a texture manager or texture handle pass a resource manager or resource handle. A resource could then be a texture or anything else you might want to use to define the font.

Share this post


Link to post
Share on other sites
Thanks for your opinions and your great help!


@Red Ghost - Nice ideas, I once thought about creating one big System "Singleton".
But this also reduces reusability. So im not going this rout. Thank you! :)

@MaulingMonkey - I am doing exactly this now - I thing its a good way.

@The Reindeer Effect - I am creating a factory right now! ;)

@SmooJ - You are right, a font is a resource at the moment. The resource itself on the harddrive is the definitonfile.



My font manager will open the font-defintion file and checks the first entry, the type of the font, in the binary file.
It then will look at the registered font- factories, if one can create the font.



Any more opinions?

Again thank you,
Riddle.

Share this post


Link to post
Share on other sites
@Riddle:
The System class is not a singleton. It is the main class from which almost everything is spawned (OS interface, Screen interface and so on).As such it is reusable. Elements pertaining to the game are within the Task and Layer classes.
My WinMain function resemble this:

#include System.h
#include WindowsOS.h

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
//we declare our specific OS here and our system
WindowsOS* MyOS=WindowsOS::GetInstance();
System MySystem;
MyOS->SetWindowsID(hInstance);
MySystem.Init([PlatformType], (BaseOS*)MyOS);
MySystem.Run();
MySystem.Close();
delete MyOS;
}



And that is all. OS is externalized to avoid BaseOS interface contamination from differing platforms(hInstance is mandatory under windows, but for console programming there are no such specific parameters): this is why I wrote "almost everything is spawned" above. The windows OS class stores the message pump and everything linked to windows OS. Screen actions are handled by the Screen class.
The system class is all encompassing: running two system classes has no meaning.
Ghostly yours,
Red.

Share this post


Link to post
Share on other sites
Quote:
Original post by Red Ghost
@Riddle:
The System class is not a singleton. It is the main class from which almost everything is spawned (OS interface, Screen interface and so on).As such it is reusable. Elements pertaining to the game are within the Task and Layer classes.
My WinMain function resemble this:
*** Source Snippet Removed ***
And that is all. OS is externalized to avoid BaseOS interface contamination from differing platforms(hInstance is mandatory under windows, but for console programming there are no such specific parameters): this is why I wrote "almost everything is spawned" above. The windows OS class stores the message pump and everything linked to windows OS. Screen actions are handled by the Screen class.
The system class is all encompassing: running two system classes has no meaning.
Ghostly yours,
Red.


Well, I cant quite see the reasoning behind a few aspects of your code. I assume from your description that WindowsOS is really just a slightly overbearing name for your application class, that has platform specific code. And I dont know why it should be a singleton, but whatever, some people really like that stuff. And I am also confused by your System class. I think the big issue is that your naming ... sucks. I know it sounds silly for me to say that because there is no way in hell you are going to forget what WindowsOS or System does, but symptoms of bad naming at the top level like that mean that you are probably botching stuff up elsewhere, and as complexity grows, poor naming is going to hurt.

Names should be descriptive and specific. Not verbose necessarily, but they need to convey information about role, responsibility, and behavior.

Read this.

Share this post


Link to post
Share on other sites
@Reindeer:
I have read the link you posted and I must say that I do not see exactly what for you is a good naming system (I also followed the Tex link within to look into the code but this did not add clarification). I am open to every alternate point of vue as long as it does not turn into a theological debate ;). Be aware that the code is commented to explain the use of each class.
However I concede the bit I showed may not be explicit enough.

The point I wanted to make in the previous post was to show that code reuse of a general class managing all the interfaces to a platform and resources is possible.
The reasoning behind the system class is to provide a unique access point for setting and managing all interfaces upon which a game builds:
- OS interface: input controller initiation (keyboard, joystick, mouse), base application setup (under windows, window creation and message pump management).
- Screen interface: screen initiation, buffer management.
- Timer interface: timer manipulation (setting, resetting, elapsed time calculation)
- Messaging system interface
- Resource Managers interfaces: texture, font, graphic object (sprite, mesh), sound, music, agent, script, map ...
- Task Manager interface: functional interface to manage tasks. A task represents a state of the game (cf Game Programming Gems 1).

Whatever the type of game on a single platform, this system class is invariant. On multiple platforms, this system class behaves as a factory to create the correct instances of platform dependant interfaces (OS, Screen, Timer, Resource Managers for IO strategies). When developping a game, I just need to select the platform and develop tasks.

It is true the use of singleton classes does not add or remove anything to this design. It dates back from older versions of this design (a few years back) where all interfaces were created by each elements through calling the correct singleton GetInstance method: muddy and unmaintainable (no easy evaluation of impact of a change in a singleton code, duplication of platform dependant interface selection everywhere|( ).

Share this post


Link to post
Share on other sites

This topic is 4662 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this