[b]AssetIO Debug[/b]
#ifndef ELYSIAN_ASSET_H #define ELYSIAN_ASSET_H #include#include "stdio.h" #include "stdarg.h" #include "asset_platform.h" namespace elysian { class Asset { public: enum DBG_TYPE { DBG_NORMAL, DBG_ERROR_1, DBG_ERROR_2 }; Asset(); //virtual bool Load(std::ifstream &stream)=0; // virtual bool Save(std::ofstream &stream)=0; static void setDebug(void (*func)(Asset::DBG_TYPE, char *)); static void Print(DBG_TYPE type, char *string, ...); private: static void (*dbgFunc)(Asset::DBG_TYPE, char *); }; } #endif
In an attempt to make the joint AssetIO framework even more complete and versatile, I've added a pretty sweet debugging mechanism. Every Save/Load-able asset is now inheriting from an "Asset" class. This asset class contains a mechanism for printing debug output during the loading/saving process. Its default implementation is simply a printf call. However, the class contains an empty function pointer that you may use to register your own debug function. Here is how I am using it with the Engine:
Asset::setDebug(&Debug::AssetPrint);
Now all internal debug statements are redirected to my Debug::AssetPrint function, which dumps them to our debug.txt and (if enabled) displays the errors in the Adventure Log.
The AssetIO also provides for additional functionality by providing not only a debug string, but a flag for the type of output:
enum DBG_TYPE { DBG_NORMAL, DBG_ERROR_1, DBG_ERROR_2 };
A powerful example of using this flag is for Marcel's editor. Normally, you guys would not care to hear "loading tileset, loading objectset, etc..." So these are tagged as DBG_NORMAL. However, you WILL care if the output is "COULD NOT LOAD MAP BECAUSE XYZ." Such output will be tagged with DBG_ERROR_#, and Marcel's editor will be able to pop a cute little error window for you gentlemen.
[b]Engine and System[/b]
So now that the AssetIO framework is nearing completion, I've gotten back to reimplementing the basic engine framework using the new IO. The first two (and probably most important classes) are Engine and System. The engine is composed of different Systems (TerrainSystem, CollisionSystem, WarpSystem, EnemySystem, BattleSystem, etc). Basically each system is in charge of managing some aspect of the game. One important thing to keep in mind is that at the very center of our framework is the "Area." The TerrainSystem does not contain its own Terrain. The WarpSystem does not actually contain the Warps. Rather, each of these entities is contained within "Area." System then has a static pointer to the current Area, so that every subsystem may operate on its own specific entity contained within the Area. Here is the system class: (not very exciting currently)
#ifndef ELYSIAN_SYSTEM_H #define ELYSIAN_SYSTEM_H namespace elysian { struct Level; struct Area; class System { protected: static Level *curLevel; static Area *curArea; public: //virtual void DebugDump()=0; }; } #endif
[b]LevelSystem[/b]
The "LevelSystem" is a subsystem whose duty is to load/swap Levels/Areas. It does this by containing a vector of Areas/Levels that are in RAM and changing the System::curLevel and System::curArea static pointers. Since these pointers are static, when the LevelSystem changes them, this results in every subsystem now operating on a new Area/Level. Here is a rough sketch:
Its current design is actually pretty simple. It keeps a cache of x amount of levels and y amount of areas (x is currently always 1 on every platform, and y is 3 for the PC and only 1 for the DC/PSP). If the level you wish to switch to isn't cached, it calls AssetIO to load it. In the future we can worry about streaming, more sophisticated caching, etc. For now, this is more than good enough.
[b]ScriptSystem[/b]
The ScriptSystem is the engine subsystem that is in charge of scripting. I have not actually implemented this yet, but I have been thinking about it quite a bit. Its job is to load/update "script components." Something that is important to our engine is to support both C++ "scripts" and Lua scripts. My first design was:
But I went on to generalize and elaborate upon the design with:
The entire basis of a "script" is that it is a collection of functions that are called at a particular time by a "trigger." The Update() function is triggered every frame. The OnHit() function is triggered when one collidable collides with another. The OnTalk() is triggered when an entity talks to the current entity, etc.
A C++ script can simply inherit from the "Script" component and reimplement each of the virtual trigger functions.
A LuaComponent also inherits from the "Script" component and contains an actual .lua script. Its implementation of these virtual triggers simply calls the functions contained in the Lua script.
You can see this heirarchy here: