您的位置:首页 > 编程语言 > Lua

游戏引擎加入lua

2014-11-29 17:17 148 查看
游戏引擎加入lua
为了让游戏实现热更新,决定引入lua来开发游戏。游戏的主框架是c++,lua调用c++直接用tolua++就行了。lua的解析为了加快速度,我决定使用luajit,当然也可以实用luaC来做。
#pragma once
#include "base/Singleton.h"
#include "string"
extern "C" {
#include "external/luajit/include/lua.h"
#include "external/luajit/include/lualib.h"
#include "external/luajit/include/lauxlib.h"
}
namespace cloud
{
class LuaEngine: public Singleton<LuaEngine>
{
public:
DECLARE_SINGLETON_CREATE_DESTROY
LuaEngine();
~LuaEngine();
void addSearchPath(const char* path);
int executeScriptFile(const char* filename);

int executeString(const char *codes);
int executeFunction(int numArgs);
lua_State *getLuaState();
void addLuaLoader(lua_CFunction func);
int luaLoadBuffer(lua_State *L, const char *chunk, int chunkSize, const char *chunkName);
private:

int _callFromLua;
lua_State *_state;
};
}


#include "LuaEngine.h"
#include "string"
#include "base/fileUtils/FileUtils.h"
#include "base/EngineLog.h"
#include "base/script/lua_interface.h"

namespace cloud
{
int gameEngine_lua_loader(lua_State *L)
{
static const std::string BYTECODE_FILE_EXT    = ".luac";
static const std::string NOT_BYTECODE_FILE_EXT = ".lua";

std::string filename(luaL_checkstring(L, 1));
size_t pos = filename.rfind(NOT_BYTECODE_FILE_EXT);
if (pos != std::string::npos)
{
filename = filename.substr(0, pos);
}

pos = filename.find_first_of(".");
while (pos != std::string::npos)
{
filename.replace(pos, 1, "/");
pos = filename.find_first_of(".");
}

// search file in package.path
unsigned char* chunk = nullptr;
size_t chunkSize = 0;
std::string chunkName;
FileUtils* utils = FileUtils::getInstance();

lua_getglobal(L, "package");
lua_getfield(L, -1, "path");
std::string searchpath(lua_tostring(L, -1));
lua_pop(L, 1);
size_t begin = 0;
size_t next = searchpath.find_first_of(";", 0);

do
{
if (next == std::string::npos)
next = searchpath.length();
std::string prefix = searchpath.substr(begin, next);
if (prefix[0] == '.' && prefix[1] == '/')
{
prefix = prefix.substr(2);
}

pos = prefix.find("?.lua");
chunkName = prefix.substr(0, pos) + filename + BYTECODE_FILE_EXT;
if (utils->isFileExist(chunkName.c_str()))
{
utils->readFile(chunkName.c_str(),&chunk,chunkSize);
break;
}
else
{
chunkName = prefix.substr(0, pos) + filename + NOT_BYTECODE_FILE_EXT;
if (utils->isFileExist(chunkName.c_str()))
{
utils->readFile(chunkName.c_str(),&chunk,chunkSize);
break;
}
}

begin = next + 1;
next = searchpath.find_first_of(";", begin);
} while (begin < (int)searchpath.length());

if (chunk)
{
LuaEngine::getInstance()->luaLoadBuffer(L, (char*)chunk, (int)chunkSize, chunkName.c_str());
delete []chunk;
}
else
{
LOG("can not get file data of %s", chunkName.c_str());
return 0;
}

return 1;
}

IMPLEMENT_SINGLETON_ALL(LuaEngine);
//std::string path = Application::getInstance()->getSearchDir() + filePath;
LuaEngine::LuaEngine():_callFromLua(0)
{
_state = lua_open();
luaL_openlibs(_state);

tolua_lua_interface_open(LuaEngine::getInstance()->getLuaState());

addLuaLoader(gameEngine_lua_loader);
}

LuaEngine::~LuaEngine()
{
if (nullptr != _state)
{
lua_close(_state);
}
}

void LuaEngine::addSearchPath(const char* path)
{
lua_getglobal(_state, "package");                                  /* L: package */
lua_getfield(_state, -1, "path");                /* get package.path, L: package path */
const char* cur_path =  lua_tostring(_state, -1);
lua_pushfstring(_state, "%s;%s/?.lua", cur_path, path);            /* L: package path newpath */
lua_setfield(_state, -3, "path");          /* package.path = newpath, L: package path */
lua_pop(_state, 2);                                                /* L: - */
}

int LuaEngine::executeScriptFile(const char* filename)
{
std::string code("require \"");
code.append(filename);
code.append("\"");
return executeString(code.c_str());
}

int LuaEngine::executeString(const char *codes)
{
luaL_loadstring(_state, codes);
return executeFunction(0);
}

int LuaEngine::executeFunction(int numArgs)
{
int functionIndex = -(numArgs + 1);
if (!lua_isfunction(_state, functionIndex))
{
//CCLOG("value at stack [%d] is not function", functionIndex);
lua_pop(_state, numArgs + 1); // remove function and arguments
return 0;
}

int traceback = 0;
lua_getglobal(_state, "__G__TRACKBACK__");                         /* L: ... func arg1 arg2 ... G */
if (!lua_isfunction(_state, -1))
{
lua_pop(_state, 1);                                            /* L: ... func arg1 arg2 ... */
}
else
{
lua_insert(_state, functionIndex - 1);                         /* L: ... G func arg1 arg2 ... */
traceback = functionIndex - 1;
}

int error = 0;
++_callFromLua;
error = lua_pcall(_state, numArgs, 1, traceback);                  /* L: ... [G] ret */
--_callFromLua;
if (error)
{
if (traceback == 0)
{
LOG("[LUA ERROR] %s", lua_tostring(_state, - 1));        /* L: ... error */
lua_pop(_state, 1); // remove error message from stack
}
else                                                            /* L: ... G error */
{
lua_pop(_state, 2); // remove __G__TRACKBACK__ and error message from stack
}
return 0;
}

// get return value
int ret = 0;
if (lua_isnumber(_state, -1))
{
ret = (int)lua_tointeger(_state, -1);
}
else if (lua_isboolean(_state, -1))
{
ret = (int)lua_toboolean(_state, -1);
}
// remove return value from stack
lua_pop(_state, 1);                                                /* L: ... [G] */

if (traceback)
{
lua_pop(_state, 1); // remove __G__TRACKBACK__ from stack      /* L: ... */
}

return ret;
}

lua_State *LuaEngine::getLuaState()
{
return _state;
}

void LuaEngine::addLuaLoader(lua_CFunction func)
{
if (!func) return;

// stack content after the invoking of the function
// get loader table
lua_getglobal(_state, "package");                                  /* L: package */
lua_getfield(_state, -1, "loaders");                               /* L: package, loaders */

// insert loader into index 2
lua_pushcfunction(_state, func);                                   /* L: package, loaders, func */
for (int i = (int)(lua_objlen(_state, -2) + 1); i > 2; --i)
{
lua_rawgeti(_state, -2, i - 1);                                /* L: package, loaders, func, function */
// we call lua_rawgeti, so the loader table now is at -3
lua_rawseti(_state, -3, i);                                    /* L: package, loaders, func */
}
lua_rawseti(_state, -2, 2);                                        /* L: package, loaders */

// set loaders into package
lua_setfield(_state, -2, "loaders");                               /* L: package */

lua_pop(_state, 1);
}

int LuaEngine::luaLoadBuffer(lua_State *L, const char *chunk, int chunkSize, const char *chunkName)
{
int r = 0;
r = luaL_loadbuffer(L, chunk, chunkSize, chunkName);
return r;
}
}

bool AppDelegate::applicationEnter()
{
//初始化全局数据

//场景切换

//only win32 can use this
#if (TARGET_PLATFORM == PLATFORM_WIN32)
//Engine::getInstance()->setFrameSize(Size(320,568));
Engine::getInstance()->setFrameSize(Size(568,320));
#else
Engine::getInstance()->setDesignSize(Size(320,480));
#endif

//Engine::getInstance()->setDisplayStats(false);
Engine::getInstance()->setAnimationInterval(1.f / 60.f);

//AudioEngine::getInstance()->playMusic("008.mp3");
//AudioEngine::getInstance()->playSoundOnce("correct.wav");

#if (TARGET_CODE == CODE_CPP)
LOG("cpp \r");
Scene* scene = new GameScene();
Engine::getInstance()->changeScene(scene);
#else
LOG("lua \r");
LuaEngine::getInstance()->executeScriptFile("script/main.lua");
#endif

return true;
}


我们来测试下。
--main.lua
-- cclog
local cclog = function(...)
print(string.format(...))
end

-- for CCLuaEngine traceback
function __G__TRACKBACK__(msg)
cclog("----------------------------------------")
cclog("LUA ERROR: " .. tostring(msg) .. "\n")
cclog(debug.traceback())
cclog("----------------------------------------")
return msg
end

local function main()
collectgarbage("collect")
-- avoid memory leak
collectgarbage("setpause", 100)
collectgarbage("setstepmul", 5000)

local scene = require("script/GameScene")
local gameScene = scene.new()

cloud.Engine:getInstance():changeScene(gameScene)

end

local status, msg = xpcall(main, __G__TRACKBACK__)
if not status then
error(msg)
end


--extern.lua
--Create an class.
function class(classname, super)
local superType = type(super)
local cls

if superType ~= "function" and superType ~= "table" then
superType = nil
super = nil
end

if superType == "function" or (super and super.__ctype == 1) then
-- inherited from native C++ Object
cls = {}

if superType == "table" then
-- copy fields from super
for k,v in pairs(super) do cls[k] = v end
cls.__create = super.__create
cls.super    = super
else
cls.__parentCtor = super
end

cls.ctor    = function() end
cls.__cname = classname
cls.__ctype = 1

function cls.new(...)
local instance = cls.__parentCtor(...)
-- copy fields from class to native object
for k,v in pairs(cls) do instance[k] = v end
instance.class = cls
instance:ctor(...)
return instance
end
end

return cls
end


--GameScene.lua
require "script/extern"

local GameScene = class("GameScene",function()
return cloud.Scene:new()
end)

function GameScene:ctor()
local spr = cloud.Sprite:new("house.jpg")
spr:setPosition(cloud.Vec2:new(480* 0.1,320 * 0.5))
self:addChild(spr)
end

return GameScene
有几个地方要注意下:
1、由于跨平台,lua接口的dofile没有用到,用luaLoadBuffer来代替。LuaEngine::executeScriptFile解析到要调用luafile时,会转到gameEngine_lua_loader中,然后调用readfile取出文件中数据。
2、luaC和luajit都是用来解析lua的,但luaC支持lua5.2,luaJit目前只支持lua5.1,但它比较快,所以我们决定用它。
3、lua可以事先编译下,以加快执行速度。luajit -b [脚本名] [编译后的脚本名]。这里如果用lua来生成二进制码,会报错,一定要用luajit,因为解析器是luajit的。
4、tolua++生成lua可以调用,用命令tolua++ -n lua_interface -o lua_interface.cpp lua_interface.pkg
#$include "Application.h"
#$include "Scene.h"
#$include "Sprite.h"

namespace cloud
{
class Engine:public Singleton<Engine>
{
void setDesignSize(const Size& size);
Size getDesignSize();
void setAnimationInterval(float interval);
void changeScene(Scene* scene);
void pushScene(Scene* scene);
Scene* getRunningScene();
Scene* popScene();
inline static Engine* getInstance();
};
typedef Node Layer;
class Scene:public SchedulerDelegate
{
Scene();
~Scene();
void addChild(Layer* layer);
void addChild(Layer*layer,int zOrder);
void visit(const Mat4& parentMat4);
virtual void onEnter();
virtual void onExit();
virtual bool dispatchMessage(unsigned int message,void* param);
virtual bool handerMessage(unsigned int message,void* param);
};
class Vec2
{
Vec2();
Vec2(float X,float Y);
};

typedef Vec2 Point;

class RenderNode:public Node
{
RenderNode();
}

class Sprite: public RenderNode
{
Sprite();
Sprite(const char* texturePath);

void initQuadWithTexture();

void setColor(const Color& color);
Color getColor();

void setOpacity(const float Opacity);
float getOpacity();
void updateColor();
void render(const Mat4& parentMat4);
void setPosition(Point position);
};

}


现在lua调用方面还有一些问题,如果父类有函数,子类只是继承,那么lua调用时会出错。还需要在子类的pkg中重写父类函数声明。这个以后再解决吧。

接下来,还要写游戏相关的编辑器了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: