Difference between revisions of "Extending your Lua Interface"

From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Jump to: navigation, search
(Created page with 'This article explains how to extend the lua interface by binding classes and functions from your game code using tolua++. In other words, this article will show you how to intera…')
 
Line 12: Line 12:
 
  namespace PAL_LIB
 
  namespace PAL_LIB
 
  {
 
  {
class Level;
+
    class Level;
class Gui;
+
    class Gui;
 
+
    //
class Game
+
    class Game
{
+
    {
public:
+
    public:
enum ContextTypeEnum
+
          enum ContextTypeEnum
{
+
          {
CT_MENU = 0x01000uL,
+
              CT_MENU     = 0x01000uL,
CT_LEVEL_01 = 0x02000uL,
+
              CT_LEVEL_01 = 0x02000uL,
CT_LEVEL_02 = 0x04000uL,
+
              CT_LEVEL_02 = 0x04000uL,
CT_LEVEL_03 = 0x08000uL,
+
              CT_LEVEL_03 = 0x08000uL,
};
+
          };
typedef ContextTypeEnum Context;
+
          typedef ContextTypeEnum Context;
 
+
          //
static Game & Instance(void);
+
          static Game & Instance(void);
static Game * InstancePtr(void);
+
          static Game * InstancePtr(void);
~Game(void);
+
          ~Game(void);
 
+
          //
void init (void);
+
          void   init         (void);
void run (void);
+
          void   run           (void);
void triggerExit (void);
+
          void   triggerExit   (void);
void pause (void);
+
          void   pause         (void);
void step (void);
+
          void   step         (void);
void reloadContext (void);
+
          void   reloadContext (void);
void changeContext (Level * context);
+
          void   changeContext (Level * context);
void changeContext (Context context);
+
          void   changeContext (Context context);
Level * getContext (void) const;
+
          Level * getContext   (void) const;
Gui  * getGui (void) const;
+
          Gui  * getGui       (void) const;
 
+
          //
private:
+
    private:
static Game * s_instance;
+
          static Game * s_instance;
Game(void);
+
          Game(void);
 
+
          //
void loopGame (void);
+
          void loopGame           (void);
bool verifyExitConditions(void);
+
          bool verifyExitConditions(void);
 
+
          //
bool   running_;
+
          bool     running_;
bool   restart_;
+
          bool     restart_;
Level * context_;
+
          Level * context_;
Level * nextContext_;
+
          Level * nextContext_;
Gui * gui_;
+
          Gui   * gui_;
}; // class Game
+
    }; // class Game
 
  } // ns PAL_LIB
 
  } // ns PAL_LIB
 
 
   
 
   
 
  //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
 
  //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Line 63: Line 62:
 
  namespace CEGUI  
 
  namespace CEGUI  
 
  {
 
  {
class WindowManager;
+
    class WindowManager;
class Window;
+
    class Window;
 
  };
 
  };
 
  namespace PAL_LIB
 
  namespace PAL_LIB
 
  {
 
  {
class Gui
+
    class Gui
{
+
    {
enum WindowEnum
+
          enum WindowEnum
{
+
          {
UI_MAIN = 0x01,
+
              UI_MAIN   = 0x01,
UI_LOADING = 0x02,
+
              UI_LOADING = 0x02,
UI_LEVEL = 0x04,
+
              UI_LEVEL   = 0x04,
UI_PAUSE = 0x08,
+
              UI_PAUSE   = 0x08,
UI_HUD = 0x10,
+
              UI_HUD     = 0x10,
};
+
          };
+
          //
public:
+
    public:
Gui(void);
+
          Gui(void);
~Gui(void);
+
          ~Gui(void);
 
+
          //
void init (void);
+
          void init         (void);
void update (float dt);
+
          void update       (float dt);
 
+
          //
void setLoading (uint progress, String const& title);
+
          void setLoading   (uint progress, String const& title);
 
+
          //
void showMain (void);
+
          void showMain     (void);
void showLoading (void);
+
          void showLoading   (void);
void showLevelEnd (void);
+
          void showLevelEnd (void);
void showPause (void);
+
          void showPause     (void);
void showHUD (void);
+
          void showHUD       (void);
 
+
          //
void hideMain (void);
+
          void hideMain     (void);
void hideLoading (void);
+
          void hideLoading   (void);
void hideLevelEnd (void);
+
          void hideLevelEnd (void);
void hidePause (void);
+
          void hidePause     (void);
void hideHUD (void);
+
          void hideHUD     (void);
 
+
          //
protected:
+
    protected:
void updateMain (float dt);
+
          void updateMain     (float dt);
void updateLoading (float dt);
+
          void updateLoading (float dt);
void updateLevelEnd (float dt);
+
          void updateLevelEnd (float dt);
void updatePause (float dt);
+
          void updatePause   (float dt);
void updateHUD (float dt);
+
          void updateHUD     (float dt);
 
+
          //
ushort   active_;
+
          ushort                 active_;
CEGUI::WindowManager * wmgr_;
+
          CEGUI::WindowManager * wmgr_;
CEGUI::Window * wroot_;
+
          CEGUI::Window       * wroot_;
}; // class Gui
+
    }; // class Gui
 
  } // ns PAL_LIB
 
  } // ns PAL_LIB
  
Line 121: Line 120:
 
  {
 
  {
 
  public:
 
  public:
enum ContextTypeEnum
+
    enum ContextTypeEnum
{
+
    {
CT_MENU,
+
          CT_MENU,
CT_LEVEL_01,
+
          CT_LEVEL_01,
CT_LEVEL_02,
+
          CT_LEVEL_02,
CT_LEVEL_03
+
          CT_LEVEL_03
};
+
    };
typedef ContextTypeEnum Context;
+
    typedef ContextTypeEnum Context;
 
+
    //
static Game & Instance(void);
+
    static Game & Instance(void);
 
+
    //
void triggerExit (void);
+
    void   triggerExit   (void);
void pause (void);
+
    void   pause         (void);
void step (void);
+
    void   step           (void);
void reloadContext (void);
+
    void   reloadContext (void);
void changeContext (Context context);
+
    void   changeContext (Context context);
Gui   * getGui (void) const;
+
    Gui * getGui         (void) const;
 
  }; // class Game
 
  }; // class Game
 
 
   
 
   
 
  //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
 
  //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Line 147: Line 145:
 
  {
 
  {
 
  public:
 
  public:
void showMain (void);
+
    void showMain     (void);
void showLoading (void);
+
    void showLoading (void);
void showLevelEnd (void);
+
    void showLevelEnd (void);
void showPause (void);
+
    void showPause   (void);
void showHUD (void);
+
    void showHUD     (void);
 
+
    //
void hideMain (void);
+
    void hideMain     (void);
void hideLoading (void);
+
    void hideLoading (void);
void hideLevelEnd (void);
+
    void hideLevelEnd (void);
void hidePause (void);
+
    void hidePause   (void);
void hideHUD (void);
+
    void hideHUD     (void);
 
  }; // class Gui
 
  }; // class Gui
  
Line 169: Line 167:
 
  $#include "Game.hpp"
 
  $#include "Game.hpp"
 
  $#include "Gui.hpp"
 
  $#include "Gui.hpp"
 
+
//
 
  namespace PAL_LIB
 
  namespace PAL_LIB
 
  {
 
  {
$pfile "LUAGame.pkg"
+
    $pfile "LUAGame.pkg"
$pfile "LUAGui.pkg"
+
    $pfile "LUAGui.pkg"
 
  }
 
  }
  
Line 181: Line 179:
  
 
  REM Create lua interface
 
  REM Create lua interface
tolua++ -H LuaInterface.hpp -o LUAInterface.cpp LuaInterface.pkg
+
    tolua++ -H LuaInterface.hpp -o LUAInterface.cpp LuaInterface.pkg
 
  pause
 
  pause
 
   
 
   
Line 197: Line 195:
 
  */
 
  */
 
  #include "lstate.h"
 
  #include "lstate.h"
 
+
//
 
  /* Exported function */
 
  /* Exported function */
 
  int tolua_LuaInterface_open (lua_State* tolua_S);
 
  int tolua_LuaInterface_open (lua_State* tolua_S);
Line 205: Line 203:
 
  void Gui::init(void)
 
  void Gui::init(void)
 
  {
 
  {
Direct3D9Renderer & renderer = Direct3D9Renderer::create(GetDirect3DDevice());
+
    Direct3D9Renderer & renderer = Direct3D9Renderer::create(GetDirect3DDevice());
 
+
    //
// Add tolua++ bindings to your script module
+
    // Add tolua++ bindings to your script module
script_  = &LuaScriptModule::create();
+
    script_  = &LuaScriptModule::create();
lua_State * luaState = script_->getLuaState();
+
    lua_State * luaState = script_->getLuaState();
tolua_LuaInterface_open(luaState);
+
    tolua_LuaInterface_open(luaState);
+
    //
// Create a ResourceProvider and initialize the required dirs
+
    // Create a ResourceProvider and initialize the required dirs
DefaultResourceProvider * rp = new DefaultResourceProvider;
+
    DefaultResourceProvider * rp = new DefaultResourceProvider;
rp->setResourceGroupDirectory("schemes",   "gui/schemes/" );
+
    rp->setResourceGroupDirectory("schemes",     "gui/schemes/"     );
rp->setResourceGroupDirectory("imagesets", "gui/imagesets/" );
+
    rp->setResourceGroupDirectory("imagesets",   "gui/imagesets/"   );
rp->setResourceGroupDirectory("fonts",     "gui/fonts/" );
+
    rp->setResourceGroupDirectory("fonts",       "gui/fonts/"       );
rp->setResourceGroupDirectory("layouts",   "gui/layouts/" );
+
    rp->setResourceGroupDirectory("layouts",     "gui/layouts/"     );
rp->setResourceGroupDirectory("looknfeels", "gui/looknfeel/" );
+
    rp->setResourceGroupDirectory("looknfeels", "gui/looknfeel/"   );
rp->setResourceGroupDirectory("lua_scripts","gui/lua_scripts/" );
+
    rp->setResourceGroupDirectory("lua_scripts", "gui/lua_scripts/" );
 
+
    //
// Create the CEGUI::System object (using the render, resource provider,
+
    // Create the CEGUI::System object (using the render, resource provider,
// and script module created above .. the two NULLs specify System to
+
    // and script module created above .. the two NULLs specify System to
// use the default xml parser and image codec, respectively)
+
    // use the default xml parser and image codec, respectively)
System * system = &System::create(renderer, rp, NULL, NULL, script_);
+
    System * system = &System::create(renderer, rp, NULL, NULL, script_);
 
+
    //
// set the default resource groups to be used
+
    // set the default resource groups to be used
Imageset::setDefaultResourceGroup   ("imagesets" );
+
    Imageset::setDefaultResourceGroup         ("imagesets"   );
Font::setDefaultResourceGroup   ("fonts" );
+
    Font::setDefaultResourceGroup             ("fonts"       );
Scheme::setDefaultResourceGroup   ("schemes" );
+
    Scheme::setDefaultResourceGroup           ("schemes"     );
WidgetLookManager::setDefaultResourceGroup("looknfeels" );
+
    WidgetLookManager::setDefaultResourceGroup ("looknfeels" );
WindowManager::setDefaultResourceGroup   ("layouts" );
+
    WindowManager::setDefaultResourceGroup     ("layouts"     );
ScriptModule::setDefaultResourceGroup   ("lua_scripts");
+
    ScriptModule::setDefaultResourceGroup     ("lua_scripts" );
 
+
    //
// Load lua scripts
+
    // Load lua scripts
system.executeScriptFile("gui_masteroids.lua", "lua_scripts");
+
    system.executeScriptFile("gui_masteroids.lua", "lua_scripts");
 
+
    //
wmgr_  = WindowManager::getSingletonPtr();
+
    wmgr_  = WindowManager::getSingletonPtr();
wroot_ = WindowManager::getSingleton().getWindow("Root");
+
    wroot_ = WindowManager::getSingleton().getWindow("Root");
wroot_->activate();
+
    wroot_->activate();
 
  }
 
  }
  
Line 252: Line 250:
 
  -----------------------------------------
 
  -----------------------------------------
 
  function btn_clicked_main_play(e)
 
  function btn_clicked_main_play(e)
PAL_LIB.Game:Instance():getGui():hideMain()
+
    PAL_LIB.Game:Instance():getGui():hideMain()
PAL_LIB.Game:Instance():changeContext(PAL_LIB.Game.CT_LEVEL_01)
+
    PAL_LIB.Game:Instance():changeContext(PAL_LIB.Game.CT_LEVEL_01)
 
  end
 
  end
 
+
--
 
  function btn_clicked_options(e)
 
  function btn_clicked_options(e)
root:getChild("Root/ErrorMsg"):show()
+
    root:getChild("Root/ErrorMsg"):show()
 
  end
 
  end
 
+
--
 
  function btn_clicked_quit(e)
 
  function btn_clicked_quit(e)
PAL_LIB.Game:Instance():triggerExit()
+
    PAL_LIB.Game:Instance():triggerExit()
 
  end
 
  end
 
+
--
 
  function btn_clicked_ok(e)
 
  function btn_clicked_ok(e)
local we = CEGUI.toWindowEventArgs(e)
+
    local we = CEGUI.toWindowEventArgs(e)
we.window:hide()
+
    we.window:hide()
 
  end
 
  end
 
+
--
 
  function btn_clicked_pause_resume(e)
 
  function btn_clicked_pause_resume(e)
PAL_LIB.Game:Instance():getGui():hidePause()
+
    PAL_LIB.Game:Instance():getGui():hidePause()
 
  end
 
  end
 
+
--
 
  function btn_clicked_pause_restart(e)
 
  function btn_clicked_pause_restart(e)
PAL_LIB.Game:Instance():reloadContext()
+
    PAL_LIB.Game:Instance():reloadContext()
 
  end
 
  end
 
+
--
 
  -----------------------------------------
 
  -----------------------------------------
 
  -- Script Entry Point
 
  -- Script Entry Point
Line 282: Line 280:
 
  local logger = CEGUI.Logger:getSingleton()
 
  local logger = CEGUI.Logger:getSingleton()
 
  logger:logEvent(">>> Init script says hello")
 
  logger:logEvent(">>> Init script says hello")
 
+
--
  local system = CEGUI.System:getSingleton()
+
  local system   = CEGUI.System:getSingleton()
 
  local fontman  = CEGUI.FontManager:getSingleton()
 
  local fontman  = CEGUI.FontManager:getSingleton()
 
  local schememan = CEGUI.SchemeManager:getSingleton()
 
  local schememan = CEGUI.SchemeManager:getSingleton()
  local winman = CEGUI.WindowManager:getSingleton()
+
  local winman   = CEGUI.WindowManager:getSingleton()
 
+
--
  schememan:create("TaharezLook.scheme", "schemes")
+
  schememan:create("TaharezLook.scheme", "schemes" )
  fontman:create ("FairChar-30.font",   "fonts" )
+
  fontman:create ("FairChar-30.font",   "fonts"   )
  fontman:create ("DejaVuSans-10.font", "fonts" )
+
  fontman:create ("DejaVuSans-10.font", "fonts"   )
 
+
--
 
  system:setDefaultMouseCursor("TaharezLook", "MouseArrow")
 
  system:setDefaultMouseCursor("TaharezLook", "MouseArrow")
  system:setDefaultTooltip ("TaharezLook/Tooltip" )
+
  system:setDefaultTooltip("TaharezLook/Tooltip")
   
+
  --
 
  local root = winman:loadWindowLayout("Masteroids.layout")
 
  local root = winman:loadWindowLayout("Masteroids.layout")
 
  system:setGUISheet(root)
 
  system:setGUISheet(root)
 
+
--
 
  logger:logEvent("<<< Init script says goodbye")
 
  logger:logEvent("<<< Init script says goodbye")
  
Line 323: Line 321:
 
                 <Property Name="Text" Value="RESUME" />
 
                 <Property Name="Text" Value="RESUME" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.05,0},{0.95,0},{0.2,0}}" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.05,0},{0.95,0},{0.2,0}}" />
<Event Name="Clicked" Function="btn_clicked_pause_resume" />
+
                <Event Name="Clicked" Function="btn_clicked_pause_resume" />
 
             </Window>
 
             </Window>
 
             <Window Type="TaharezLook/Button" Name="Root/Pause/btn_Restart" >
 
             <Window Type="TaharezLook/Button" Name="Root/Pause/btn_Restart" >
Line 329: Line 327:
 
                 <Property Name="Text" Value="RESTART" />
 
                 <Property Name="Text" Value="RESTART" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.225,0},{0.95,0},{0.375,0}}" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.225,0},{0.95,0},{0.375,0}}" />
<Event Name="Clicked" Function="btn_clicked_pause_restart" />
+
                <Event Name="Clicked" Function="btn_clicked_pause_restart" />
 
             </Window>
 
             </Window>
 
             <Window Type="TaharezLook/Button" Name="Root/Pause/btn_Options" >
 
             <Window Type="TaharezLook/Button" Name="Root/Pause/btn_Options" >
Line 335: Line 333:
 
                 <Property Name="Text" Value="OPTIONS" />
 
                 <Property Name="Text" Value="OPTIONS" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.4,0},{0.95,0},{0.55,0}}" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.4,0},{0.95,0},{0.55,0}}" />
<Event Name="Clicked" Function="btn_clicked_options" />
+
                <Event Name="Clicked" Function="btn_clicked_options" />
 
             </Window>
 
             </Window>
 
             <Window Type="TaharezLook/Button" Name="Root/Pause/btn_Main" >
 
             <Window Type="TaharezLook/Button" Name="Root/Pause/btn_Main" >
Line 341: Line 339:
 
                 <Property Name="Text" Value="MAIN MENU" />
 
                 <Property Name="Text" Value="MAIN MENU" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.625,0},{0.95,0},{0.775,0}}" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.625,0},{0.95,0},{0.775,0}}" />
<Event Name="Clicked" Function="btn_clicked_main" />
+
                <Event Name="Clicked" Function="btn_clicked_main" />
 
             </Window>
 
             </Window>
 
             <Window Type="TaharezLook/Button" Name="Root/Pause/btn_Quit" >
 
             <Window Type="TaharezLook/Button" Name="Root/Pause/btn_Quit" >
Line 347: Line 345:
 
                 <Property Name="Text" Value="QUIT" />
 
                 <Property Name="Text" Value="QUIT" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.8,0},{0.95,0},{0.95,0}}" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.8,0},{0.95,0},{0.95,0}}" />
<Event Name="Clicked" Function="btn_clicked_quit" />
+
                <Event Name="Clicked" Function="btn_clicked_quit" />
 
             </Window>
 
             </Window>
 
         </Window>
 
         </Window>
Line 366: Line 364:
 
                 <Property Name="Text" Value="PLAY" />
 
                 <Property Name="Text" Value="PLAY" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.025,0},{0.2,0},{0.325,0},{0.8,0}}" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.025,0},{0.2,0},{0.325,0},{0.8,0}}" />
<Event Name="Clicked" Function="btn_clicked_main_play" />
+
                <Event Name="Clicked" Function="btn_clicked_main_play" />
 
             </Window>
 
             </Window>
 
             <Window Type="TaharezLook/Button" Name="Root/Main/btn_Options" >
 
             <Window Type="TaharezLook/Button" Name="Root/Main/btn_Options" >
Line 372: Line 370:
 
                 <Property Name="Text" Value="OPTIONS" />
 
                 <Property Name="Text" Value="OPTIONS" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.35,0},{0.2,0},{0.65,0},{0.8,0}}" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.35,0},{0.2,0},{0.65,0},{0.8,0}}" />
<Event Name="Clicked" Function="btn_clicked_options" />
+
                <Event Name="Clicked" Function="btn_clicked_options" />
 
             </Window>
 
             </Window>
 
             <Window Type="TaharezLook/Button" Name="Root/Main/btn_Quit" >
 
             <Window Type="TaharezLook/Button" Name="Root/Main/btn_Quit" >
Line 378: Line 376:
 
                 <Property Name="Text" Value="QUIT" />
 
                 <Property Name="Text" Value="QUIT" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.675,0},{0.2,0},{0.975,0},{0.8,0}}" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0.675,0},{0.2,0},{0.975,0},{0.8,0}}" />
<Event Name="Clicked" Function="btn_clicked_quit" />
+
                <Event Name="Clicked" Function="btn_clicked_quit" />
 
             </Window>
 
             </Window>
 
         </Window>
 
         </Window>
Line 394: Line 392:
 
             <Property Name="HorizontalAlignment" Value="Centre" />
 
             <Property Name="HorizontalAlignment" Value="Centre" />
 
             <Window Type="TaharezLook/StaticText" Name="Root/ErrorMsg/txt_Message" >
 
             <Window Type="TaharezLook/StaticText" Name="Root/ErrorMsg/txt_Message" >
                 <Property Name="Text" >
+
                 <Property Name="Text" >Sorry, the options feature is not implemented ... yet.</Property>
    Sorry, the options feature is not implemented ... yet.
+
</Property>
+
 
                 <Property Name="UnifiedAreaRect" Value="{{0,0},{0.1,0},{0.85,0},{0.591019,0}}" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0,0},{0.1,0},{0.85,0},{0.591019,0}}" />
 
                 <Property Name="HorizontalAlignment" Value="Centre" />
 
                 <Property Name="HorizontalAlignment" Value="Centre" />
Line 404: Line 400:
 
                 <Property Name="UnifiedAreaRect" Value="{{0,0},{0.7,0},{0.85,0},{0.88941,0}}" />
 
                 <Property Name="UnifiedAreaRect" Value="{{0,0},{0.7,0},{0.85,0},{0.88941,0}}" />
 
                 <Property Name="HorizontalAlignment" Value="Centre" />
 
                 <Property Name="HorizontalAlignment" Value="Centre" />
<Event Name="Clicked" Function="btn_clicked_ok" />
+
                <Event Name="Clicked" Function="btn_clicked_ok" />
 
             </Window>
 
             </Window>
 
         </Window>
 
         </Window>
Line 416: Line 412:
  
 
== Closing Thoughts ==
 
== Closing Thoughts ==
I hope that this tutorial helps. I know I was wishing for something like this when I implemented this in my game. The content is available, but it's scattered about several wiki pages, sites, and versions of CEGUI. Many thanks to CrazyEddie and the dev team; this is a great and powerful feature. Please let me know (or simply edit this wiki page) if I've made any mistakes or if you have ideas to improve this tutorial.
+
I hope that this tutorial helps. I know I was wishing for something like this when I implemented this in my game. The content is available, but it's scattered about several wiki pages, sites, and versions of CEGUI. Many thanks to CrazyEddie and the dev team; this is a wonderful, powerful feature. If I've made any mistakes or if you have anything to add to this tutorial, please, feel free to edit then in.

Revision as of 03:16, 16 December 2010

This article explains how to extend the lua interface by binding classes and functions from your game code using tolua++. In other words, this article will show you how to interact with your game objects within a lua script. A majority of the content explained here already exists within this wiki (namely: Creating a scriptable interface using CEGUI), but this article details the process in one convenient article.

The examples come from the code I am currently using in my game (simplified a bit for instructional purposes). I am using CEGUI version 0.7.5.


Step 1: Make the package file(s)

Package files are little more than copies of your header files, slightly modified for tolua++. Foremost, note that you only copy the public members. We are going to add two classes to our lua interface. First, I will show the actual c++ header files:

//¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Game			 Game.hpp			      Header
//_________________________________________________________________________
namespace PAL_LIB
{
    class Level;
    class Gui;
    //
    class Game
    {
    public:
         enum ContextTypeEnum
         {
              CT_MENU     = 0x01000uL,
              CT_LEVEL_01 = 0x02000uL,
              CT_LEVEL_02 = 0x04000uL,
              CT_LEVEL_03 = 0x08000uL,
         };
         typedef ContextTypeEnum Context;
         //
         static Game & Instance(void);
         static Game * InstancePtr(void);
         ~Game(void);
         //
         void    init          (void);
         void    run           (void);
         void    triggerExit   (void);
         void    pause         (void);
         void    step          (void);
         void    reloadContext (void);
         void	  changeContext (Level * context);
         void	  changeContext (Context context);
         Level * getContext    (void) const;
         Gui   * getGui        (void) const;
         //
    private:
         static Game * s_instance;
         Game(void);
         //
         void loopGame            (void);
         bool verifyExitConditions(void);
         //
         bool     running_;
         bool     restart_;
         Level  * context_;
         Level  * nextContext_;
         Gui    * gui_;
    }; // class Game
} // ns PAL_LIB

//¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// GUI				 Gui.hpp			      Header
//_________________________________________________________________________
namespace CEGUI 
{
    class WindowManager;
    class Window;
};
namespace PAL_LIB
{
    class Gui
    {
         enum WindowEnum
         {
              UI_MAIN    = 0x01,
              UI_LOADING = 0x02,
              UI_LEVEL   = 0x04,
              UI_PAUSE   = 0x08,
              UI_HUD     = 0x10,
         };
         //		
    public:
         Gui(void);
         ~Gui(void);
         //
         void init          (void);
         void update        (float dt);
         //
         void setLoading    (uint progress, String const& title);		
         //
         void showMain      (void);
         void showLoading   (void);
         void showLevelEnd  (void);
         void showPause     (void);
         void showHUD       (void);
         //
         void hideMain      (void);
         void hideLoading   (void);
         void hideLevelEnd  (void);
         void hidePause     (void);
         void hideHUD	     (void);
         //
    protected:
         void updateMain     (float dt);
         void updateLoading  (float dt);
         void updateLevelEnd (float dt);
         void updatePause    (float dt);
         void updateHUD      (float dt);
         //
         ushort                 active_;
         CEGUI::WindowManager * wmgr_;
         CEGUI::Window        * wroot_;
    }; // class Gui
} // ns PAL_LIB		

Notice that I have these class defined within my namespace (PAL_LIB). As you will see, our package files will specify this namespace in a slightly different manner. Also note that you do not have to have to define your classes (though it is generally a good idea to partition a large project, such as game, into one or more namespaces). When we make our package files, we simply remove all the private and protected bits. Like so:

//¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Game			Game.pkg			     Package
//_________________________________________________________________________
class Game
{
public:
    enum ContextTypeEnum
    {
         CT_MENU,
         CT_LEVEL_01,
         CT_LEVEL_02,
         CT_LEVEL_03
    };
    typedef ContextTypeEnum Context;
    //
    static Game & Instance(void);
    //
    void   triggerExit    (void);
    void   pause          (void);
    void   step           (void);
    void   reloadContext  (void);
    void   changeContext  (Context context);
    Gui  * getGui         (void) const;
}; // class Game

//¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// GUI				 Gui.pkg			     Package
//_________________________________________________________________________
class Gui
{
public:
    void showMain     (void);
    void showLoading  (void);
    void showLevelEnd (void);
    void showPause    (void);
    void showHUD      (void);
    //
    void hideMain     (void);
    void hideLoading  (void);
    void hideLevelEnd (void);
    void hidePause    (void);
    void hideHUD      (void);
}; // class Gui

You may have noticed that I left out a little more than just the non-public stuff from the game package: The enum values are not needed. The functions, Instance(void), getContext(void), changeContext(Level *), and a few others were left out because I don't want--or need--them in the lua interface. Similarly, the constructors and destructors are not copied into the package file. Note, however, if you wanted to instantiate an object from a lua script, then you would need a constructor (or some function that calls a constructor--like Game::Instance(void)).

Before we can use these packages with tolua++, we need one more package file. This package will simply include all the package files you want to add to your lua interface. In this case, it will include Game.pkg and Gui.pkg. Also, we will use this package file to define our namespace. One other thing to note here is the order in which the package files are listed. If I referenced Game.pkg after Gui.pkg, tolua++ would fail.

//¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Lua Interface	     Lua Interface.pkg			     Package
//_________________________________________________________________________
$#include "Game.hpp"
$#include "Gui.hpp"
//
namespace PAL_LIB
{
    $pfile "LUAGame.pkg"
    $pfile "LUAGui.pkg"
}


Step 2: Compile the package file(s)

For this step, you will need two things. The first, is tolua++.exe. You should find this in your CEGUI SDK bin folder (after you have compiled the SDK, that is). It may be named slightly differently depending on which build configuration you used to build CEGUI. Mine was named "tolua++ceguiStatic.exe" (yeah, yeah ... shame on me for statically linking), but I renamed it for convenience sake. The second thing you don't /really/ need, but it sure as heck makes life easier. It is a batch file (or shell script for you linux folks out there). You can make it by creating a text file, renaming it to "toluaMyLua.bat" (or [something similar].bat), and adding the following lines via your favorite text editor:

REM Create lua interface
    tolua++ -H LuaInterface.hpp -o LUAInterface.cpp LuaInterface.pkg
pause

Save it, run it, and ... hopefully--if you have done everything correctly--it will generate two files: LuaInterface.hpp and LUAInterface.cpp


Step 3: Include the single generated header in your application & apply it to your script module

Including the header in your application is easy (you just #include it), but first you will have to make a few adjustments to your project build configuration. You will need the Lua SDK (source code) for this part. If you don't already have it, you can download it here. After you download and extract the lua source, you will need to add the lua source folder to your "additional include directories". While you're there, also add "[CEGUI SDK Folder]cegui/include/ScriptingModules/LuaScriptModule/support/tolua++". This second include directory is merely for convenience (because tolua++.exe doesn't specify that awkwardly long path when it includes "tolua++.h" in its generated files).

Next, copy the two generated files into your project path and add them to your project. Open "LUAInterface.hpp" and add an include for "lstate.h". The final header should look this:

/*
** Lua binding: LuaInterface
** Generated automatically by tolua++-1.0.92 on 12/15/10 11:10:54.
*/
#include "lstate.h"
//
/* Exported function */
int tolua_LuaInterface_open (lua_State* tolua_S);

Finally, open whichever source file you are using to initialize CEGUI. I initialize CEGUI in the init function of my Gui class: "Gui.cpp". Include "LUAInterface.h". Now, you may have to adjust the way you initialize CEGUI. I had to switch from the bootstrap method to the manual method. My initialization function looks like this (notice we're calling the function from LUAInterface, "tolua_LuaInterface_open(luaState);" after creating the LuaScriptModule):

void Gui::init(void)
{
    Direct3D9Renderer & renderer = Direct3D9Renderer::create(GetDirect3DDevice());
    //
    // Add tolua++ bindings to your script module
    script_   = &LuaScriptModule::create();
    lua_State * luaState = script_->getLuaState();
    tolua_LuaInterface_open(luaState);
    //
    // Create a ResourceProvider and initialize the required dirs
    DefaultResourceProvider * rp = new DefaultResourceProvider;
    rp->setResourceGroupDirectory("schemes",     "gui/schemes/"     );
    rp->setResourceGroupDirectory("imagesets",   "gui/imagesets/"   );
    rp->setResourceGroupDirectory("fonts",       "gui/fonts/"       );
    rp->setResourceGroupDirectory("layouts",     "gui/layouts/"     );
    rp->setResourceGroupDirectory("looknfeels",  "gui/looknfeel/"   );
    rp->setResourceGroupDirectory("lua_scripts", "gui/lua_scripts/" );
    //
    // Create the CEGUI::System object (using the render, resource provider,
    // and script module created above .. the two NULLs specify System to
    // use the default xml parser and image codec, respectively)
    System * system = &System::create(renderer, rp, NULL, NULL, script_);
    //
    // set the default resource groups to be used
    Imageset::setDefaultResourceGroup          ("imagesets"   );
    Font::setDefaultResourceGroup              ("fonts"       );
    Scheme::setDefaultResourceGroup            ("schemes"     );
    WidgetLookManager::setDefaultResourceGroup ("looknfeels"  );
    WindowManager::setDefaultResourceGroup     ("layouts"     );
    ScriptModule::setDefaultResourceGroup      ("lua_scripts" );
    //
    // Load lua scripts
    system.executeScriptFile("gui_masteroids.lua", "lua_scripts");
    //
    wmgr_  = WindowManager::getSingletonPtr();
    wroot_ = WindowManager::getSingleton().getWindow("Root");
    wroot_->activate();		
}

And that's it as far as your game code is concerned. You may want to compile your project now to ensure you got everything correct. The rest is done via your layouts and scripts ... which is the next step.


Step 4: Creating your lua script(s) & adding events to your layout(s)

Now you need some Lua script functions to exploit your new lua interface functions. The following lua script is a simplified version of what I am using in my game:

-----------------------------------------
-- SCRIPT: gui_masteroids.lua
-----------------------------------------
function btn_clicked_main_play(e)
    PAL_LIB.Game:Instance():getGui():hideMain()
    PAL_LIB.Game:Instance():changeContext(PAL_LIB.Game.CT_LEVEL_01)
end
--
function btn_clicked_options(e)
    root:getChild("Root/ErrorMsg"):show()
end
--
function btn_clicked_quit(e)
    PAL_LIB.Game:Instance():triggerExit()
end
--
function btn_clicked_ok(e)
    local we = CEGUI.toWindowEventArgs(e)
    we.window:hide()
end
--
function btn_clicked_pause_resume(e)
    PAL_LIB.Game:Instance():getGui():hidePause()
end
--
function btn_clicked_pause_restart(e)
    PAL_LIB.Game:Instance():reloadContext()
end
--
-----------------------------------------
-- Script Entry Point
-----------------------------------------
local logger = CEGUI.Logger:getSingleton()
logger:logEvent(">>> Init script says hello")
--
local system    = CEGUI.System:getSingleton()
local fontman   = CEGUI.FontManager:getSingleton()
local schememan = CEGUI.SchemeManager:getSingleton()
local winman    = CEGUI.WindowManager:getSingleton()
--
schememan:create("TaharezLook.scheme", "schemes" )
fontman:create	("FairChar-30.font",    "fonts"   )
fontman:create	("DejaVuSans-10.font",  "fonts"   )
--
system:setDefaultMouseCursor("TaharezLook", "MouseArrow")
system:setDefaultTooltip("TaharezLook/Tooltip")
--
local root = winman:loadWindowLayout("Masteroids.layout")
system:setGUISheet(root)
--
logger:logEvent("<<< Init script says goodbye")

This script is called near the end of my initialization function (see above, Step 3). It loads a scheme, two fonts, and my layout. It also contains some function which will be used as event callbacks for my UI elements. As you may notice, several function make calls to varies functions of my Game object. The first function, "btn_clicked_main_play" tells the Gui object to hide Main menu window (which could be done without calling the Gui object, except that I have some other code in that function which I didn't want add to the lua interface); and then, it tells the Game object to change contexts (which effectively causes my game to switch states and load the first level). These functions are called via the event system. So, the next step is to specify (subscribe to) events in your layout file. Here's mine (again, simplified):

<?xml version="1.0" encoding="UTF-8"?>
<GUILayout >
   <Window Type="DefaultWindow" Name="Root" >
       <Property Name="UnifiedAreaRect" Value="{{0,0},{0,0},{1,0},{1,0}}" />
       <Window Type="TaharezLook/FrameWindow" Name="Root/Pause" >
           <Property Name="Text" Value="Pause Menu" />
           <Property Name="Visible" Value="False" />
           <Property Name="AlwaysOnTop" Value="True" />
           <Property Name="TitlebarFont" Value="DejaVuSans-10" />
           <Property Name="SizingEnabled" Value="False" />
           <Property Name="TitlebarEnabled" Value="True" />
           <Property Name="UnifiedAreaRect" Value="{{0,0},{0,0},{0.35,0},{0.5,0}}" />
           <Property Name="DragMovingEnabled" Value="False" />
           <Property Name="VerticalAlignment" Value="Centre" />
           <Property Name="CloseButtonEnabled" Value="False" />
           <Property Name="HorizontalAlignment" Value="Centre" />
           <Window Type="TaharezLook/Button" Name="Root/Pause/btn_Resume" >
               <Property Name="Font" Value="FairChar-30" />
               <Property Name="Text" Value="RESUME" />
               <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.05,0},{0.95,0},{0.2,0}}" />
               <Event Name="Clicked" Function="btn_clicked_pause_resume" />
           </Window>
           <Window Type="TaharezLook/Button" Name="Root/Pause/btn_Restart" >
               <Property Name="Font" Value="FairChar-30" />
               <Property Name="Text" Value="RESTART" />
               <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.225,0},{0.95,0},{0.375,0}}" />
               <Event Name="Clicked" Function="btn_clicked_pause_restart" />
           </Window>
           <Window Type="TaharezLook/Button" Name="Root/Pause/btn_Options" >
               <Property Name="Font" Value="FairChar-30" />
               <Property Name="Text" Value="OPTIONS" />
               <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.4,0},{0.95,0},{0.55,0}}" />
               <Event Name="Clicked" Function="btn_clicked_options" />
           </Window>
           <Window Type="TaharezLook/Button" Name="Root/Pause/btn_Main" >
               <Property Name="Font" Value="FairChar-30" />
               <Property Name="Text" Value="MAIN MENU" />
               <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.625,0},{0.95,0},{0.775,0}}" />
               <Event Name="Clicked" Function="btn_clicked_main" />
           </Window>
           <Window Type="TaharezLook/Button" Name="Root/Pause/btn_Quit" >
               <Property Name="Font" Value="FairChar-30" />
               <Property Name="Text" Value="QUIT" />
               <Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.8,0},{0.95,0},{0.95,0}}" />
               <Event Name="Clicked" Function="btn_clicked_quit" />
           </Window>
       </Window>
       <Window Type="TaharezLook/FrameWindow" Name="Root/Main" >
           <Property Name="Text" Value="Masteroid! XmachinaX v0.01.101130 - BETA" />
           <Property Name="Visible" Value="False" />
           <Property Name="AlwaysOnTop" Value="True" />
           <Property Name="TitlebarFont" Value="DejaVuSans-10" />
           <Property Name="SizingEnabled" Value="False" />
           <Property Name="TitlebarEnabled" Value="True" />
           <Property Name="UnifiedAreaRect" Value="{{0,0},{0,0},{0.85,0},{0.2,0}}" />
           <Property Name="DragMovingEnabled" Value="False" />
           <Property Name="VerticalAlignment" Value="Centre" />
           <Property Name="CloseButtonEnabled" Value="False" />
           <Property Name="HorizontalAlignment" Value="Centre" />
           <Window Type="TaharezLook/Button" Name="Root/Main/btn_Play" >
               <Property Name="Font" Value="FairChar-30" />
               <Property Name="Text" Value="PLAY" />
               <Property Name="UnifiedAreaRect" Value="{{0.025,0},{0.2,0},{0.325,0},{0.8,0}}" />
               <Event Name="Clicked" Function="btn_clicked_main_play" />
           </Window>
           <Window Type="TaharezLook/Button" Name="Root/Main/btn_Options" >
               <Property Name="Font" Value="FairChar-30" />
               <Property Name="Text" Value="OPTIONS" />
               <Property Name="UnifiedAreaRect" Value="{{0.35,0},{0.2,0},{0.65,0},{0.8,0}}" />
               <Event Name="Clicked" Function="btn_clicked_options" />
           </Window>
           <Window Type="TaharezLook/Button" Name="Root/Main/btn_Quit" >
               <Property Name="Font" Value="FairChar-30" />
               <Property Name="Text" Value="QUIT" />
               <Property Name="UnifiedAreaRect" Value="{{0.675,0},{0.2,0},{0.975,0},{0.8,0}}" />
               <Event Name="Clicked" Function="btn_clicked_quit" />
           </Window>
       </Window>
       <Window Type="TaharezLook/FrameWindow" Name="Root/ErrorMsg" >
           <Property Name="Text" Value="Error" />
           <Property Name="Visible" Value="False" />
           <Property Name="AlwaysOnTop" Value="True" />
           <Property Name="TitlebarFont" Value="DejaVuSans-10" />
           <Property Name="SizingEnabled" Value="False" />
           <Property Name="TitlebarEnabled" Value="True" />
           <Property Name="UnifiedAreaRect" Value="{{0,0},{0,0},{0.3,0},{0.2,0}}" />
           <Property Name="DragMovingEnabled" Value="False" />
           <Property Name="VerticalAlignment" Value="Centre" />
           <Property Name="CloseButtonEnabled" Value="False" />
           <Property Name="HorizontalAlignment" Value="Centre" />
           <Window Type="TaharezLook/StaticText" Name="Root/ErrorMsg/txt_Message" >
               <Property Name="Text" >Sorry, the options feature is not implemented ... yet.</Property>
               <Property Name="UnifiedAreaRect" Value="{{0,0},{0.1,0},{0.85,0},{0.591019,0}}" />
               <Property Name="HorizontalAlignment" Value="Centre" />
           </Window>
           <Window Type="TaharezLook/Button" Name="Root/ErrorMsg/btn_OK" >
               <Property Name="Text" Value="OK" />
               <Property Name="UnifiedAreaRect" Value="{{0,0},{0.7,0},{0.85,0},{0.88941,0}}" />
               <Property Name="HorizontalAlignment" Value="Centre" />
               <Event Name="Clicked" Function="btn_clicked_ok" />
           </Window>
       </Window>
   </Window>
</GUILayout>

The bits of interest here are the <Event ...> tags. Each button has one listed after the <Property ...> tags. The Event tag has two parameters Name and Function. Name is the name of the event. These names can difficult to track down (I imagine a complete list may exist somewhere, but I've yet to find it), but you can find most of them in the documentation API (under "Class->Class Members", click "e" and scroll down till you see things prefaced with Event). In this tutorial, I only used the "Clicked" event.

The second parameter is where you specify the lua function name. So, if you look at the last window definition ("Root/ErrorMsg") you see the OK button will call "btn_clicked_ok" when it is clicked ... and if you look back the lua script, you see this simply hides (ie., closes the ErrorMsg window). Notice that you can use the same function more than once. For example, "Root/Main" and "Root/Pause" each contain a button named "[...]/btn_Options" and they both specify the same lua function.


Closing Thoughts

I hope that this tutorial helps. I know I was wishing for something like this when I implemented this in my game. The content is available, but it's scattered about several wiki pages, sites, and versions of CEGUI. Many thanks to CrazyEddie and the dev team; this is a wonderful, powerful feature. If I've made any mistakes or if you have anything to add to this tutorial, please, feel free to edit then in.