Using Lua scripting from cocos2d-x

Datetime:2016-08-23 04:28:45          Topic: Lua  Cocos2d-X           Share

Add Lua to a new cocos2d-x c++ project.

NO preamble – let’s get into it.

cocos new usingLuaFromCPP -l cpp

Creates the new project.

Download Lua from http://www.lua.org (I’m using 5.3)

Download LuaBridge from https://github.com/vinniefalco/LuaBridge

Open the cocos2d-x project in Xcode

Copy the two downloaded folders to the Classes folder of your project.

Then right-click on the project and use the Add Files To… option to add the Lua and LuaBridge folders – making sure you use the options button to add the folders to both projects (mobile and desktop) if you want to use Lua in both) – and create Groups not folder references.

(I had loads of problems adding the folders elsewhere because first headers could’t be found, then I got linker errors. One day I will understand how Xcode works and get this right – but for now just add the folders as described!)

Build and Run just to be sure!

For this example I want to have a layer that will be used to represent my game world.

I want a script that will determine what’s in that world.

To keep things simple, the script will just determine which of two sprites to create, at what location. The actual creation of the sprites will be handled in the C++ code.

The ‘expanded’ idea will be that I can use scripts to replace what would often be done using pList files; so now I can add calculations and loops as well as simple data.

So let’s create a new class, WorldLayer

WorldLayer.hpp

#ifndef WorldLayer_hpp
#define WorldLayer_hpp

class WorldLayer : public cocos2d::Layer
{
public:
    CREATE_FUNC(WorldLayer);

    virtual bool init();

    void addASprite(int spriteType, float x, float y);
};

#endif /* WorldLayer_hpp */

WorldLayer.cpp

#include "WorldLayer.hpp"

USING_NS_CC;

bool WorldLayer::init()
{
    if ( !Layer::init() )
    {
        return false;
    }
    return true;
}

void WorldLayer::addASprite(int spriteType, float x, float y)
{
    Sprite* sprite;
    if (spriteType == 0)
        sprite = Sprite::create("CloseNormal.png");
    else
        sprite = Sprite::create("CloseNormal.png");

    sprite->setPosition(Vec2(x,y));

    this->addChild(sprite);
}

The method that is important is the ‘addASprite’ method which we will be calling from a script later on.

For now, let’s test it by changing HellowWorldScene.cpp’s init method as below…

bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }

    auto layer = WorldLayer::create();
    this->addChild(layer);

    layer->addASprite(1,100,100);

    return true;
}

Run this and you should see the single sprite created at (100,100)

A single sprite controlled by the c++ code – just for testing

So – time to add some scripting stuff!.

To avoid confusion, just remove the layer->addASprite(1,100,100); line so we will know any sprites displayed will be displayed from the script.

Go back to HelloWorldScene.cpp and add

#include "LuaBridge.h"
#include "lua.hpp"
using namespace luabridge;

Build again to make sure the header files can be found.

Add a new file to the project and call it “TestScript.lua” (I put mine in the Classes folder for simplicity)

Add this code to this file

function createSprites(yOffset, ySize)
    for x = 0, 600, 6 do
        y = math.sin(x)
        worldLayer:addASprite(1, x, y * ySize + yOffset)
    end --for
end --createSprites()

The script will just display 100 sprites in a Sine wave pattern.

The Y offset parameter lifts the sprites above the bottom of the screen and the Size parameter determines the height of the waves

So – now we have a script we need to tell Lua (via LuaBridge) about our WorldLayer class and its functions.

Change Helloworld::init() as follows

bool HelloWorld::init()
{
    if ( !Layer::init() )
    {
        return false;
    }

    auto worldLayer = WorldLayer::create();
    this->addChild(worldLayer);
//------
    // Prepare Lua - this is pretty standard startup for using Lua.
    // IN a real game you might want to have the LuaState as a class field or a global of some sort, so that it is accesible throughout your App
    lua_State* LuaState = luaL_newstate();
    luaL_openlibs(LuaState);

    // Load the script file into Lua
    auto file = FileUtils::getInstance( ) -> fullPathForFilename( "TestScript.lua" );
    luaL_loadfile(LuaState, file.c_str());

    // Tell Lua about our WorldLayer class and its addASprite method.
    // The strings "WorldLayer" and "addASprite" are what the class and method will be called within Lua - they don't have to match the actual names
    luabridge::getGlobalNamespace(LuaState)
        .beginClass<WorldLayer>("WorldLayer")               // Remember this is the Class name, not an instance name
        .addFunction("addASprite", &WorldLayer::addASprite)
        .endClass();

    luabridge::push(LuaState, worldLayer);                  // Push the instance pointer onto the LuaBridge stack
    lua_setglobal(LuaState, "worldLayer");                  // Tell Lua what to call this object

    /*
     Thanks to http://www.troubleshooters.com/codecorn/lua/lua_c_calls_lua.htmThanks
     PRIMING RUN. FORGET THIS AND YOU'RE TOAST
     */
    lua_pcall(LuaState, 0, 0, 0);

    //Call the Lua function    createSprites(yOffset, ySize)

    lua_getglobal(LuaState, "createSprites");   // the function.
    lua_pushnumber(LuaState, 200);              // parameter 1 (yOffset)
    lua_pushnumber(LuaState, 20);               // parameter 2 (ySize)

    /* call the function with 2 arguments, return no results */
    lua_call(LuaState, 2, 0);


    lua_close(LuaState);
//------
    return true;
}

Now run – and you should see the following:

SIne wave sprites – controller by Lua

Easy, eh?





About List