Controling and binding the Game
|
|
Now that we have something to start with, it's time to actually control
the game through that main loop.
|
Before the simon.Create() function call previously added, we will add a new call
to the simon.SetMediaPath() function. This will specify the game's engine to look
in the specified directory to find its multimedia files (*.wav). Here is how the whole
chunk should looks like:
|
-- Show Simon game main form
simon.SetMediaPath("C:\\Prog\\Delphi\\MNet\\Bin\\Medias")
simon.Create()
-- Main processing loop
while simon.GetPowerStatus() == 1 do
if simon.GetPlayStatus() == 1 then
simon:Initialize()
-- Game processing loop
while GameState do
UserSequenceCount = 0
simon:AddSequence()
simon:DisplayMessage("Simon's Turn!", "clRed", 750)
simon:PlaySequence(MainSequence)
simon:DisplayMessage("Your Turn!", "clLime", 1000)
GameState = simon.GetUserSequence(SequenceCount, 2000)
if simon:GetScore() > 1000 then
simon:DisplayMessage("You Win!", "clAqua", -1)
GameState = false
end
end
end
-- Make sure the processor doesn't runs for no reason
Sleep(10)
end
simon.Destroy()
|
The function simon.GetPowerStatus() is used to determine if the "power" (in our
case virtual) is still on. If this function returns 1, this means the "power" is still
on. The simon.GetPlayerStatus() function returns 1 if the user pushed the "play" button.
Calling simon.DisplayMessage() will display the specified string using the specified
color (for color codes, look at the "Delphi/C++ Builder" column of
this website)
and the specified length in miliseconds. In our case, this will be used to display whose turn it is.
The function simon.GetUserSequence() will prompt the user to enter its sequence. The first
argument specifies the length of the sequence to retrieve and the second one specifies
the timeout value in miliseconds to use before considering the turn as wrong. In terms of
execution in the Lua script, this call should hang the code while waiting for user's response
but will eventually trigger automated calls to the simon:OnButtonClick() function everytime
the user pushes a button from the game. This function also return the state of the game
(false == game over). Now, the following should briefly explain how to configure the dll
in C++/Delphi to actually allow LuaEdit to bind to your code, in this case: simon.dll. (The full
code is available in the tutorial's sources but for a reason of time and complexity, it won't be explained in
details like the simon.lua script. Only the "initializer" part will be.)
As discussed in the part 1 of the tutorial, your dll MUST export the
specific function LuaDebug_Initializer(). This function will be called by LuaEdit right before starting
a debugging session. We will then use this "hook"/"callback" to register all of the game's engine
functions previously described. This will map the functions in Lua and prevent LuaEdit from yelding
errors because it can't find any reference to the function from the script. This way, runtime debugging
will be available through LuaEdit.
Delphi: (This function must be added in the "exports" instruction area of your code just like all the registred functions referenced in it)
|
// Register a c function in a specified table
procedure LuaRegisterCustom(L: PLua_State; ndx: Integer; funcname: String; func: lua_CFunction);
begin
lua_pushstring(L, funcname);
lua_pushcfunction(L, func);
lua_rawset(L, ndx - 2);
end;
// Register a c function using the globalsindex constant
procedure LuaRegister(L: PLua_State; funcname: String; func: lua_CFunction);
begin
LuaRegisterCustom(L, LUA_GLOBALSINDEX, funcname, func);
end;
// LuaEdit is calling this function everytime a script with a project
// specifying this dll as the initializer when debugging
function LuaDebug_Initializer(L: PLua_State): Integer;
begin
// Create new table on the lua stack
lua_newtable(L);
// Register delphi functions in that new table
LuaRegisterCustom(L, -1, 'SetMediaPath', SetMediaPath);
LuaRegisterCustom(L, -1, 'SetLight', SetLight);
LuaRegisterCustom(L, -1, 'Create', Create);
LuaRegisterCustom(L, -1, 'Destroy', Destroy);
LuaRegisterCustom(L, -1, 'GetUserSequence', GetUserSequence);
LuaRegisterCustom(L, -1, 'GetPowerStatus', GetPowerStatus);
LuaRegisterCustom(L, -1, 'GetPlayStatus', GetPlayStatus);
LuaRegisterCustom(L, -1, 'SetScore', SetScore);
LuaRegisterCustom(L, -1, 'GetScore', GetScore);
LuaRegisterCustom(L, -1, 'LockControls', LockControls);
LuaRegisterCustom(L, -1, 'UnlockControls', UnlockControls);
LuaRegisterCustom(L, -1, 'DisplayMessage', DisplayMessage);
// Register other miscalleneous functions
LuaRegister(L, 'Sleep', LuaSleep);
// Assing "simon" lua global variable to the new table
lua_setglobal(L, 'simon');
end;
|
C/C++: (This function must be added in the "EXPORTS" instruction area of your *.def file just like all the registred functions referenced in it)
|
// Register a c function in a specified table
void LuaRegisterCustom(lua_State *L, long ndx, const char* funcname, lua_CFunction func)
{
lua_pushstring(L, funcname);
lua_pushcfunction(L, func);
lua_rawset(L, ndx - 2);
}
// Register a c function using the globalsindex constant
void LuaRegister(lua_State *L, const char* funcname, lua_CFunction func)
{
LuaRegisterCustom(L, LUA_GLOBALSINDEX, funcname, func);
}
// LuaEdit is calling this function everytime a script with a project
// specifying this dll as the initializer when debugging
int LuaDebug_Initializer(lua_State *L)
{
// Create new table on the lua stack
lua_newtable(L);
// Register delphi functions in that new table
LuaRegisterCustom(L, -1, "SetMediaPath", SetMediaPath);
LuaRegisterCustom(L, -1, "SetLight", SetLight);
LuaRegisterCustom(L, -1, "Create", Create);
LuaRegisterCustom(L, -1, "Destroy", Destroy);
LuaRegisterCustom(L, -1, "GetUserSequence", GetUserSequence);
LuaRegisterCustom(L, -1, "GetPowerStatus", GetPowerStatus);
LuaRegisterCustom(L, -1, "GetPlayStatus", GetPlayStatus);
LuaRegisterCustom(L, -1, "SetScore", SetScore);
LuaRegisterCustom(L, -1, "GetScore", GetScore);
LuaRegisterCustom(L, -1, "LockControls", LockControls);
LuaRegisterCustom(L, -1, "UnlockControls", UnlockControls);
LuaRegisterCustom(L, -1, "DisplayMessage", DisplayMessage);
// Register other miscalleneous functions
LuaRegister(L, "Sleep", LuaSleep);
// Assing "simon" lua global variable to the new table
lua_setglobal(L, "simon");
}
|
The LuaRegisterCustom() and LuaRegister() functions will simplify the code's complexity
since we would have had to repeat those 3 lines of code for every function that
we needed to register in the Lua environment. Now, to complete this project/turotial,
all you need to do is inspired yourself from the supplied code and finish the dll. As mentioned
higher, this part will not be explained in detail.
Now you've seen enough to know how Lua works in interaction with LuaEdit to eventually
build your own project that way so that Lua code can finally be an easy thing to debug.
I hope this tutorial helped you in many ways. If you still have questions you can always
post a new thread in the "Tutorial" section of our forum or feel free to
contact me at the following address:
jf.goulet@luaedit.net. Thank you very much for using this tutorial and good luck in your future projects!
|
Hints:
- Try live debugging with the script you just finished using the compiled simon.dll
and lua.dll located in the bin folder. Copy/paste the project and Lua script you
created in that same folder, launch LuaEdit, set a breakpoint somewhere and press
play.
- Try adding some "Level" logic in the Lua code by modifying the game's variants
such as the sequence display speed, the player's timeout, the number of
items added to the main sequence per turn, etc...
- Challenge your firends with some "impossible to win" scripts! :P
- For more advanced users, try adding a multiplayer engine to play over LAN.
- Implement an *.exe application that will launch the dll the same way LuaEdit
does by calling at first the LuaDebug_Initializer() function and then loading
the Lua script using the luaL_loadbuffer() API. See Delphi code of the SimonExec
project.
|
|
www.luaedit.org
© Copyright 2004-2005 LuaEdit
Bind a Dll to LuaEdit (Tutorial)
|