Search code examples
c++luacomponentsgame-engine

How to automatically initialize component parameters?


While doing a game engine that uses .lua files in order to read parameter values, I got stuck when I had to read these values and assign them to the parameters of each component in C++. I tried to investigate the way Unity does it, but I didn't find it (and I'm starting to doubt that Unity has to do it at all).

I want the parameters to be initialized automatically, without the user having to do the process of

myComponentParameter = readFromLuaFile("myParameterName")

for each one of the parameters.

My initial idea is to use the std::variant type, and storing an array of variants in order to read them automatically. My problems with this are:

  • First of all, I don't know how to know the type that std::variant is storing at the moment (tried with std::variant::type, but it didn't work for the template), in order to cast from the untyped .lua value to the C++ value. For reference, my component initialization looks like this:
bool init(luabridge::LuaRef parameterTable)
{
    myIntParameter = readVariable<int>(parameterTable, "myIntParameter");
    myStringParameter = readVariable<std::string>(parameterTable, "myStringParameter");
    return true;
}

(readVariable function is already written in this question, in case you're curious)

  • The second problem is that the user would have to write std::get(myIntParameter); whenever they want to access to the value stored by the variant, and that sounds like something worse than making the user read the parameter value.

  • The third problem is that I can't create an array of std::variant<any type>, which is what I would like to do in order to automatically initialize the parameters.

Is there any good solution for this kind of situation where I want the init function to not be necessary, and the user doesn't need to manually set up the parameter values?

Thanks in advance.


Solution

  • Let's expand my comment. In a nutshell, you need to get from

    • "I have some things entered by the user in some file"

    to:

    • "the client code can read the value without std::get"
      …which roughly translates to:
      "input validation was done, and values are ready for direct use."

    …which implies you do not store your variables in variants.


    In the end it is a design question. One module somewhere must have the knowledge of which variable names exist, and the type of each, and the valid values.

    • The input of that module will be unverified values.

    • The output of the module will probably be some regular c++ struct.

    • And the body of that module will likely have a bunch of those:

      config.foo = readVariable<int>("foo");
      config.bar = readVariable<std::string>("bar");
      // you also want to validate values there - all ints may not be valid values for foo,
      // maybe bar must follow some specific rules, etc
      

      assuming somewhere else it was defined as:

      struct Configuration {
          int fooVariable;
          std::string bar;
      };
      

    Where that module lives depends on your application. If all expected types are known, there is no reason to ever use a variant, just parse right away.

    You would read to variants if some things do not make sense until later. For instance if you want to read configuration values that will be used by plugins, so you cannot make sense of them yet.

    (actually even then simply re-parsing the file later, or just saving values as text for later parsing would work)