Quote:
if you dont mind, could you please explain your method of using C++ for "scripting".
Well here's the guts of it. I have a Script class, which is an object that entities in my scene graph hold if they want to have a script attached to them. In a bare bones form, it might look something like this:
class Script{public: Script(char* fileName, char* funcName) :memory(0) { libHandle = LoadLibrary(fileName); script = (Message& (*)(Message &message))GetProcAddress(libHandle, funcName); } ~Script() { FreeLibrary(libHandle); delete(memory); } HINSTANCE libHandle; Message& (*script)(Message &message); void* memory;};
This is very bare bones. It doesn't protect its members, and it doesn't do error checking. Its enough to demonstrate the idea though.
This class puts a wrapper over a function in an external DLL. The function returns a referance to a Message object, and takes a Message object as a parameter. How you define your messages is up to you. You could do it the way Windows does it, where you have an enumerated member that tells you what kind of message it is, and have some general memory afterwards that you squeeze the data you need into. You could also make Message a base class, and create child clases that inherit from it that define your actual messages. However you do it is irrelivent really, as long as it works.
libHandle is a handle to the DLL. Each instance of the script class has its own handle, which is allocated when the object is created, and freed when the object is deleted. The DLL is only ever bound to the program once. Allocating another handle just gives you another means to access it. When there are no more handles referancing a DLL, the DLL is unbound from the program automatically.
script is a function pointer to the desired function inside the specified DLL. You call this like an ordinary function pointer, the only difference is we obtain the address of the function through a call to GetProcAddress().
memory is a pointer to the persistant memory for that script object. I'll touch on that again later.
With this class, you set up the script through a call like this:
"Script myScript("dll.dll", "FunctionName");"
That will bind the DLL to the program and get the pointer to the function. You can then just call the function through the function pointer like so:
"returnMessage = one.script(message);
In reality, you'd have a member function (which you'd probably inline) that puts a wrapper over the script function.
Anyway, that's how you use it in the main application. The DLL's themselves are like this:
EXPORT Message& ItWorked(Message &message){}
That could define a script in your DLL. EXPORT is a macro which hides some of the verbosiy of declaring that function, and is defined as follows:
"#define EXPORT extern "C" __declspec(dllexport)"
The actual body of the script is totally up to you. You can also make local functions in this DLL that are not exported, and call them from your scripts. Basically just do whatever it is you need to do to process and repond to the messages you recieve. There might be an "UPDATE" message for example, which upon recieving, your script should perform whatever general updates it needs to do that happen with time. This might mean updating an animation pose, or moving an object.
In order to allow your scripts to interact with your engine however, they need a set of functions they can call that give them access to all the things they might need to influence. They might need to create or modify other objects in your scene for example. To do that, you create an SDK of sorts that provides all the functions you'd need. This can just be a static or dynamic library, with a set of header files which define all the structures and functions you want the scripts to be able to call. I won't go into too much more detail on that, but suffice to say, you can create a set of high level functions that perform tedious or technical work for you. You can make these functions as low-level or as abstract as you want.
Finally, each instance of your script clas needs its own persistant memory. To do that, you define a struct for each script that needs persistant memory. If the ItWorked() script needs two values to be persistant for example, it might have a structure like the following:
struct ItWorked_Mem{ int val1; int val2;};
Because the Script class back in the engine has no idea about the size or contents of this structure, your script class has to be responsible for creating and deleting this block of memory. That can be accomplished quite easily. Pass your script Create and Delete messages, and upon reciecving them, it allocates and frees the memory as needed. The pointer to the memory is stored in the Script class, so it can be recalled when the script is next called through this->memory.
And that's about the guts of it.
Quote:
Voila! An easy to use end user scripting system that's fast and even has some neural net potential.
Pretty clear, or too out there?
Sounds like it could work. It'd take a bit of getting used to, but theoretically it should be possible to use.