Actually the object base class implements all of its methods to the best of it's ability.
I have encountered the same problem you have in my system at times. Let's say I have materials for objects. The material properties applied to an object determine how light interacts with the object. This is a resource but it is an engine resource not a game resource. So the first derivation of objects is just that. I have resource objects and then game objects. Keeping the two separate has been much easier than lumping them together.
Now I know this means you will have 2 derivations before you actually get to the specific objects, but it makes things a lot easier conceptually and architecturally. I hate having 2 derivations but I've not found a good way to get around it.
So in short my core engine setup:
Engine architecture
Resources
- CResource - Resource base class
- CMaterial - (D3DMATERIAL9)
- CShader - (ID3DXEffect)
- CLight - (D3DLIGHT9)
- CTexture - (IDirect3DTexture9)
- CD3DXMesh - (ID3DXMesh)
- CD3DXProgMesh - (ID3DXPMesh)
- CAnimFrame - animation frame class
Resource managers
- CResMgr - Resource manager base class
- CMatlMgr - Materials manager
- CShaderMgr - Shader manager
- CLightMgr - Light manager
- CTexMgr - Texture manager
- CD3DXMeshMgr - Model mesh manager
- CD3DXProgMeshMgr - Model progressive mesh manager
- CAnimSeq - animation sequence class - animation frame container class
Sound engine resources
- CDXSoundSegment - (IDirectMusicSegment8,IDirectMusicSegmentState8)
- CDXAudioPath - (IDirectMusicAudioPath8)
- CDXSoundEmitter - container class for sound segments - each emitter can have any number of sounds associated with it and each play on separate audio paths
Script support
- ScriptCmn.h - script support common header file
- CScriptVarMgr - Script variable container class
Utility classes
- CCamera - 3D camera support (cockpit/first person, chase, object, flyby, and free floating camera support)
- COrient3D - controls all 3D orientations for all objects
- CINIReader - reads ini files in form [Header] Key=Value
- CTimer - timer class
Engine objects (non-game specific)
- CSkyBox - skybox support
- CBillboard - billboarding support
- CSprite - sprite class (uses a CAnimSeq)
- CSkySphere - spherical skybox support (also textured sphere support)
Core engine classes
- CD3DApp - Direct3D application base class
- CD3DView - Direct3D view class (not quite implemented fully)
- CDXAudio - DirectMusic base class
- CDXInput - DirectInput base class
- CDXShowWnd - DirectShow base class
- CKeyboard - keyboard device class
- CMouse - mouse device class
- CGamePad - stick/pad device class
Debug
- CEngineException - exception handling class
- CMemCheck - memory checking class (not fully implemented)
This is a small list of the basic engine classes. An actual game coded with the engine would have many more object and game specific classes.
I'm not sure what you mean by a database but since all of my base classes can stream to disk, all of the derived classes inherit that support and/or can override the base implementation if needed.
Either you are going to create tons of template classes for managers or you are going to create tons of separate manager classes. Either way all objects cannot just be lumped into one huge container because they are so different from one another. My actual game code classes for my space project and for the Zelda project are also quite numerous but they derive from these. This is what I would consider my core engine code and I would like to keep it separate from game specific data and objects.
I was going to create a memory and file resource class but found that both of those were so simple they were not needed. Plus any memory class I come up with is probably not going to be faster than just using new and delete.
I also wanted to create an archive class but it proved to be a bit more difficult than I first thought.
The simple way to do object serialization is to take the MFC approach. If your base resource class has a Serialize() function or WriteToDisk() ReadFromDisk() then you can just make it a pure virtual. This means it will have to be implemented in derived classes and you cannot instantiate the base. Since the base object has no way of knowing how to write the object to disk I recommend this approach. So in your derived classes you simply code the virtual function specific to your object.
Code:
...
CShipMgr *pShipMgr=new CShipMgr();
pShipMgr->AddFromINI(m_spDevice,"INI\\Ships\\Ships.ini","Terran")
CArchiveFile *pFile=new CArchiveFile("TerranShips",WRITE_ONLY);
CArchiver *pArchive=new CArchiver(pFile,CArchiver::WRITE);
pShipMgr->Serialize(pArchive)
...
...
//CShipMgr derived from CResMgr which has pure virtual Serialize(CArchiver *pArchiver)
void CShipMgr::Serialize(CArchiver *pArchiver)
{
if (pArchiver->IsStoring())
{
//Write data to stream
}
else
{
//Read data from stream
}
}
As I said this is not quite complete yet since CArchiver has been proving to be a pain in the rump.