游戏引擎加入lua
2014-11-29 17:17
183 查看
游戏引擎加入lua
为了让游戏实现热更新,决定引入lua来开发游戏。游戏的主框架是c++,lua调用c++直接用tolua++就行了。lua的解析为了加快速度,我决定使用luajit,当然也可以实用luaC来做。
我们来测试下。
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
现在lua调用方面还有一些问题,如果父类有函数,子类只是继承,那么lua调用时会出错。还需要在子类的pkg中重写父类函数声明。这个以后再解决吧。
接下来,还要写游戏相关的编辑器了。
为了让游戏实现热更新,决定引入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中重写父类函数声明。这个以后再解决吧。
接下来,还要写游戏相关的编辑器了。
相关文章推荐
- Cocos2d-x Lua引擎制作的游戏代码加密
- 基于C++和Lua的移动游戏引擎Leadwerks 3发布
- Ubuntu14.04 lua游戏引擎Love安装
- Lua游戏引擎Love试用
- Lua2D游戏脚本引擎(渲染OpenGL) (源码放出)
- Ubuntu 16.04安装Lua游戏引擎Love
- lua 2d游戏引擎love2d安装和介绍
- Cocos2d-x Lua引擎制作的游戏代码加密
- iOS下集成Lua与socket,cjson第三方库(非游戏引擎下)
- 一个容易上手的2D游戏引擎love2d (lua)
- 一个简单的基于OpenGL的Lua的游戏引擎的实例
- cocos2dx-Lua引擎游戏脚本及图片资源解密与DUMP
- 基于C++和Lua的移动游戏引擎Leadwerks 3发布
- 游戏引擎演化史【转】
- Android开源游戏引擎——Alien3d
- libgdx游戏引擎开发笔记(九)SuperJumper游戏例子的讲解(篇三)---- 主游戏界面显示框架
- 我的游戏引擎设计——“SI引擎草案(一)原理和框架”
- Unity3D游戏开发之Lua与游戏的不解之缘(转载中)
- Torque游戏性能优化篇--在TORQUE游戏引擎里使用含LOD的DTS模型让游戏获得更高的FPS
- 游戏引擎开发