Getting started
So while working on my game spaceTradeSim I ran into
a problem. This problem was "how do I make it easy to add space stations
to the map". This is a problem I have been struging to come up with a
soloution for for some time now.
But I found one. That soloution was an Entity Componet System(ECS). I
tried to stay away from, not for any real reason other then I thought it
would require a large code rewrite and I thought it was hard. Turned out
I was wrong on both accounts.
It wasn't hard and I didn't need a large code rewrite. I thought it was
hard mainly because I had never created one or used one before. Bouns was
that I didn't need a large code rewrite. I think this was mainly because
I already had an entity class. All I had todo to it was four functions to
it and that was about it. So enough of this talk time to get in and
explain some things. I think I'll start with the ECS.
Building the ECS
This is somthing that I had been advoiding as stated above. But I started
looking into it while I was working on the space stations. I found this
helpful video on
YouTube. While I didn't end up implamenting the same system he used.
It inspired me to take another look at it.
At the start the idea was that I would have a virtual class called
EntityComponent that all other components would from and build off.
class EntityComponent { public: EntityComponent(); ~EntityComponent(); virtual void OnCreate(){} virtual void OnDraw(){} virtual void OnTick(){} virtual void OnUpdate(){} uint64 GetID() const { return m_ID; }; std::string GetStringID() const { return m_strID; }; void SetID(const std::string &id) { m_strID = id; m_ID = (uint64)djb2Hash((unsigned char*)m_strID.c_str()); } private: std::string m_strID; uint64 m_ID; };So its pretty simple. A component inherits this class and just fills out the OnXXX() functions. This was a good idea at the time. Then came the, "How do I tell the game what component an entity has easily?" question.
This was easily done using my Json parser. I have a jsonpp wrapper that is handy for these things. I added the data needed to the json file describing the species that inhabit the world and added the required code.
All was working fine, until I thought "But how do I let modders change these?" Not just modders but me as well. That I felt was the point where I decided that I should add Lua back in. The component system was in that state for about an hour maybe two hours. Then I started with the Lua.
Adding Lua
Around 2013 Lua had been added to the code base, but it was removed due
to me not being experienced enough to write a decent wrapper. Back then I
didn't really need it and it was more of an experiment then anything.
But as time went on and the simulation turned into a game and I added Lua
back onto the todo list. It was on the back burner but it was there. I
guessed that I would implament it when I needed gameplay.
The first thing that I did was work out how I'm going to save the current
lua state. I came up with the Lua instance.
enum LuaIncludeFlags { INC_LUA_MAP = 1, INC_LUA_MAP_MANG = 2, INC_LUA_ACTOR = 4, INC_LUA_ACTOR_MANG = 8, INC_LUA_UI = 16 }; class LuaInstance { public: LuaInstance(); ~LuaInstance(); void InitLuaInstance(const int &flags, const uint64 &id); bool LoadFile(const std::string &file); bool CallLuaState(); void CleanInstance(); bool ReloadInstance(); std::string GetLastError() const; luabridge::LuaRef GetLuaGlobal(const std::string &name); uint64 GetInstanceID() const { return m_id; } private: lua_State * p_lstate; bool m_firstLoad; uint64 m_id; std::vector<std::string> m_files; int m_flags; };So some of the more eagle eyed of you might have noticed the
luabridge::
namespace being used. Thats because I'm using
LuaBridge for my
lua c api wrapper. Personaly I think its great. Soloution to the largest
problem I had with Lua and that was the api calls. I'm sure with time I
could have worked something out but it wouldn't be anywhere near this good.
You might also notice the
bool m_firstLoad
. This is because
I discovered you can load more then one lua file into a state. I don't know
if this is not ok but it seems to work for now so yay for me. For this to
work you need to call luaL_loadfile(lua_State*, const char*)
on the first file loaded. Then for each file after that you call
luaL_dofile(lua_State*, const char*)
. Once you have finished
loading files you have to call lua_pcall(lua_State*, 0, 0, 0)
for you to be able to access any of the stuff you just loaded. This is
called in CallLuaState()
.
I think Lua will either throw an error when there are conflics or just over write what ever is already there. I tested this but I can't remember what it did. Either way something happens :D.
The
LuaIncludeFlags
enum allows me to control what classes and data the
lua_State
has access to. Not all states need access to the map, but
others do. I have an update to this in the works already. I'm going to allow
a callback in the Init function so its more matainable in the long run.
Loading Components
The last problem was how do I get all of this working together. That was an easy fix, use the json loader that I already have. For the engine to be able to find the conponents we first need to tell it where to find them.
{ "components": [ { "flags": [ "Actor" ] }, { "id": "component_basic_actor", "file": "lua/Components/ComponentActor.lua" }, { "id": "component_health", "file": "lua/Components/ComponentActor.lua" }, { "id": "component_move", "file": "lua/Components/ComponentActor.lua" }, { "remove_flags": [ "Actor" ] } ] }
Thats what this json is used for. It tells the engine which
LuaIncludeFlags
the components will need access too and also the name of the components. These flags can
be nested as well. I am still experimenting with this system but I think it has potential.
If im not sure what I need I can always just use the Everything
flag.
The End
I think thats the basics of the system. But there is a lot more going on it the background
when it comes to the LUA and the component system. But I think thats better left for another
day.
Back to writtings / Home