游戏引擎加入lua

来源:互联网 发布:野生程序员博客 编辑:程序博客网 时间:2024/05/16 11:57
游戏引擎加入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_DESTROYLuaEngine();~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.pathunsigned 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 argumentsreturn 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 valueint 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 stacklua_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 tablelua_getglobal(_state, "package");                                  /* L: package */lua_getfield(_state, -1, "loaders");                               /* L: package, loaders */// insert loader into index 2lua_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 -3lua_rawseti(_state, -3, i);                                    /* L: package, loaders, func */}lua_rawseti(_state, -2, 2);                                        /* L: package, loaders */// set loaders into packagelua_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));#elseEngine::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);#elseLOG("lua \r");LuaEngine::getInstance()->executeScriptFile("script/main.lua");#endifreturn true;}




我们来测试下。
--main.lua-- ccloglocal cclog = function(...)    print(string.format(...))end-- for CCLuaEngine tracebackfunction __G__TRACKBACK__(msg)    cclog("----------------------------------------")    cclog("LUA ERROR: " .. tostring(msg) .. "\n")    cclog(debug.traceback())    cclog("----------------------------------------")    return msgendlocal 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)endlocal 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        endend    return clsend

--GameScene.luarequire "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)endreturn 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中重写父类函数声明。这个以后再解决吧。

接下来,还要写游戏相关的编辑器了。



0 0