file-type

Lua脚本语言在AS3游戏开发中的应用

RAR文件

4星 · 超过85%的资源 | 下载需积分: 10 | 384KB | 更新于2025-09-07 | 81 浏览量 | 21 下载量 举报 收藏
download 立即下载
Lua 是一种轻量级、高效的脚本语言,广泛应用于游戏开发、嵌入式系统以及各种需要脚本扩展功能的场景中。而 AS3(ActionScript 3)则是 Adobe Flash 平台上的主要编程语言,用于开发 Flash 动画、网页游戏、富互联网应用程序(RIA)等。在实际开发中,有时会将 Lua 与 ActionScript 3 结合使用,尤其是在需要跨平台脚本逻辑复用、模块化设计或热更新等需求时。这种结合能够充分发挥 Lua 的灵活性和 AS3 在 Flash 平台上的表现能力。 ### Lua 与 AS3 的关系与结合方式 “Lua for AS3” 这一标题表明了一种技术方向:在 ActionScript 3 的开发环境中引入 Lua 脚本语言的支持。这种做法通常出现在游戏开发中,尤其是那些希望将 Lua 用于游戏逻辑控制、配置管理、任务系统、AI 行为树等场景的项目。由于 Lua 语言具有轻量、可嵌入、语法简洁、易于与其他语言交互等特点,因此它常被用作游戏的脚本层,而底层的渲染、输入处理、网络通信等功能则由 AS3 编写。 在 AS3 中使用 Lua 的常见方式包括: 1. **Lua 虚拟机嵌入**:通过将 Lua 虚拟机编译为 Flash 可执行的 SWF 文件(如压缩包中的 `library.swf` 文件),从而在 Flash 应用中加载并运行 Lua 代码。这种方式需要对 Lua 源码进行适当的修改和适配,以使其能够在 Flash 平台运行。 2. **Lua 到 AS3 的绑定与通信**:通过建立 Lua 与 AS3 之间的通信桥梁,实现两者之间的函数调用、变量传递和事件触发。例如,AS3 可以调用 Lua 函数来执行游戏中的某个行为逻辑,而 Lua 也可以通过特定接口调用 AS3 的方法来访问 Flash 平台的功能,如播放音效、显示动画等。 3. **脚本热更新**:利用 Lua 的动态加载特性,可以在不重新发布 Flash 应用的前提下,远程更新 Lua 脚本,实现游戏逻辑的热更新。这对于需要频繁迭代的游戏项目尤其重要。 4. **资源与配置管理**:Lua 常被用于编写游戏的配置文件、任务脚本、剧情脚本等。这些脚本可以在 AS3 中解析并执行,从而实现高度可配置化和模块化的设计。 ### 压缩包内容解析 压缩包中包含两个文件:`library.swf` 和 `catalog.xml`。 - **library.swf**:这是一个 Flash 可执行文件,很可能是嵌入 Lua 虚拟机的封装库。开发者可以通过加载该 SWF 文件,从而在 AS3 项目中获得 Lua 解释器的功能。该文件可能封装了 Lua 的核心 API、与 AS3 的交互接口、以及一些基础库函数。 - **catalog.xml**:该文件通常用于描述资源目录或模块信息。在此上下文中,可能是对 `library.swf` 中封装的 Lua 功能模块的索引或元数据描述。它可能包含模块名称、导出函数、资源路径等信息,用于帮助 AS3 项目正确调用 Lua 模块。 ### Lua 在游戏开发中的优势 在标题描述中提到:“as3用的脚本语言,做游戏经常用到的,对你可能有帮助”,这句话揭示了几个关键点: 1. **脚本语言在游戏开发中的重要性**:游戏开发中常常需要将核心逻辑与具体实现分离。使用脚本语言可以实现快速原型开发、逻辑热更新、跨平台复用等功能。Lua 在这方面表现尤为突出。 2. **AS3 与 Lua 的互补性**:AS3 作为 Flash 平台的标准语言,负责处理图形渲染、事件处理、用户交互等前端任务,而 Lua 更适合用于编写游戏逻辑、AI、状态机等部分。两者结合可以实现“逻辑与表现分离”的架构设计。 3. **灵活性与扩展性**:通过 Lua 的动态特性,开发者可以在不重新编译 AS3 代码的前提下,修改游戏行为。这对于调试、测试和后续维护非常有利。 4. **社区与生态支持**:Lua 在游戏行业有广泛的使用基础,拥有丰富的库和工具链支持。例如,LuaSocket(网络通信)、LuaFileSystem(文件操作)、Corona SDK(跨平台开发框架)等,都可以通过适当的方式在 AS3 项目中集成。 ### 典型应用场景 在实际开发中,“Lua for AS3”的应用场景主要包括: - **游戏逻辑脚本化**:将游戏中的角色控制、任务系统、对话系统、技能系统等用 Lua 编写,便于维护和扩展。 - **UI 逻辑与动画控制**:虽然 AS3 本身擅长 UI 和动画,但将复杂的 UI 行为用 Lua 实现可以提高可维护性。 - **配置文件与数据驱动开发**:使用 Lua 脚本来定义游戏配置、角色属性、关卡数据等,提升开发效率。 - **热更新与补丁系统**:通过远程加载 Lua 脚本,实现游戏逻辑的在线更新,避免重新打包发布。 - **AI 行为树与状态机**:Lua 适合用于构建复杂的行为逻辑,如敌人 AI、NPC 对话等。 ### 开发注意事项 尽管 Lua 与 AS3 的结合具有诸多优势,但在实际开发过程中也需要注意以下几点: - **性能开销**:Lua 是解释型语言,其执行效率低于编译型语言。对于性能敏感的代码(如物理引擎、渲染循环等),应尽量由 AS3 编写。 - **内存占用**:Flash 平台对内存有一定的限制,嵌入 Lua 虚拟机可能会增加 SWF 文件的体积和内存消耗。 - **调试复杂性**:Lua 与 AS3 的交互可能会引入新的调试难度,需要良好的日志系统和调试工具支持。 - **安全与防篡改**:Lua 脚本容易被反编译和修改,若用于敏感逻辑(如支付、关卡验证等),需进行加密或混淆处理。 ### 总结 “Lua for AS3”是一种将 Lua 脚本语言集成进 Flash 平台开发的技术方案,尤其适用于游戏开发中的逻辑脚本化、热更新、配置管理等场景。通过将 Lua 虚拟机(如压缩包中的 `library.swf`)嵌入 AS3 项目,并配合描述文件(如 `catalog.xml`)进行模块管理和接口调用,可以实现灵活、高效、可扩展的游戏开发架构。尽管存在一定的性能与调试挑战,但其带来的开发效率提升和架构灵活性使其成为 Flash 平台游戏开发中一个值得尝试的技术路径。

相关推荐

filetype

/* * Tencent is pleased to support the open source community by making xLua available. * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at * http://opensource.org/licenses/MIT * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #if USE_UNI_LUA using LuaAPI = UniLua.Lua; using RealStatePtr = UniLua.ILuaState; using LuaCSFunction = UniLua.CSharpFunctionDelegate; #else using LuaAPI = XLua.LuaDLL.Lua; using RealStatePtr = System.IntPtr; using LuaCSFunction = XLua.LuaDLL.lua_CSFunction; #endif namespace XLua { using System; using System.Collections.Generic; public class LuaEnv : IDisposable { internal RealStatePtr L; private LuaTable _G; internal ObjectTranslator translator; internal int errorFuncRef = -1; #if THREAD_SAFT || HOTFIX_ENABLE internal object luaEnvLock = new object(); #endif public LuaEnv() { #if THREAD_SAFT || HOTFIX_ENABLE lock(luaEnvLock) { #endif LuaIndexes.LUA_REGISTRYINDEX = LuaAPI.xlua_get_registry_index(); ; // Create State L = LuaAPI.luaL_newstate(); //Init Base Libs LuaAPI.luaopen_xlua(L); LuaAPI.luaopen_i64lib(L); LuaAPI.luaopen_perflib(L); translator = new ObjectTranslator(this, L); translator.createFunctionMetatable(L); translator.OpenLib(L); ObjectTranslatorPool.Instance.Add(L, translator); LuaAPI.lua_atpanic(L, StaticLuaCallbacks.Panic); LuaAPI.lua_pushstdcallcfunction(L, StaticLuaCallbacks.Print); LuaAPI.lua_setglobal(L, "print"); //template engine lib register TemplateEngine.LuaTemplate.OpenLib(L); AddSearcher(StaticLuaCallbacks.LoadBuiltinLib, 2); // just after the preload searcher AddSearcher(StaticLuaCallbacks.LoadFromCustomLoaders, 3); AddSearcher(StaticLuaCallbacks.LoadFromResource, 4); AddSearcher(StaticLuaCallbacks.LoadFromStreamingAssetsPath, -1); DoString(init_xlua, "Init"); init_xlua = null; AddBuildin("socket.core", StaticLuaCallbacks.LoadSocketCore); AddBuildin("socket", StaticLuaCallbacks.LoadSocketCore); AddBuildin("mime.core", StaticLuaCallbacks.LoadMimeCore); AddBuildin("rapidjson", StaticLuaCallbacks.LoadRapidJson); AddBuildin("lpeg", StaticLuaCallbacks.LoadLpeg); AddBuildin("sproto.core", StaticLuaCallbacks.LoadSprotoCore); //AddBuildin("pack", StaticLuaCallbacks.LoadPack); LuaAPI.lua_newtable(L); //metatable of indexs and newindexs functions LuaAPI.xlua_pushasciistring(L, "__index"); LuaAPI.lua_pushstdcallcfunction(L, StaticLuaCallbacks.MetaFuncIndex); LuaAPI.lua_rawset(L, -3); LuaAPI.xlua_pushasciistring(L, Utils.LuaIndexsFieldName); LuaAPI.lua_newtable(L); LuaAPI.lua_pushvalue(L, -3); LuaAPI.lua_setmetatable(L, -2); LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.xlua_pushasciistring(L, Utils.LuaNewIndexsFieldName); LuaAPI.lua_newtable(L); LuaAPI.lua_pushvalue(L, -3); LuaAPI.lua_setmetatable(L, -2); LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.xlua_pushasciistring(L, Utils.LuaClassIndexsFieldName); LuaAPI.lua_newtable(L); LuaAPI.lua_pushvalue(L, -3); LuaAPI.lua_setmetatable(L, -2); LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.xlua_pushasciistring(L, Utils.LuaClassNewIndexsFieldName); LuaAPI.lua_newtable(L); LuaAPI.lua_pushvalue(L, -3); LuaAPI.lua_setmetatable(L, -2); LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.lua_pop(L, 1); // pop metatable of indexs and newindexs functions LuaAPI.xlua_pushasciistring(L, "xlua_main_thread"); LuaAPI.lua_pushthread(L); LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); translator.Alias(typeof(Type), "System.MonoType"); LuaAPI.lua_getglobal(L, "_G"); translator.Get(L, -1, out _G); LuaAPI.lua_pop(L, 1); errorFuncRef = LuaAPI.get_error_func_ref(L); if (initers != null) { for (int i = 0; i < initers.Count; i++) { initers[i](this, translator); } } translator.CreateArrayMetatable(L); translator.CreateDelegateMetatable(L); #if THREAD_SAFT || HOTFIX_ENABLE } #endif } private static List<Action<LuaEnv, ObjectTranslator>> initers = null; public static void AddIniter(Action<LuaEnv, ObjectTranslator> initer) { if (initers == null) { initers = new List<Action<LuaEnv, ObjectTranslator>>(); } initers.Add(initer); } public LuaTable Global { get { return _G; } } public T LoadString<T>(string chunk, string chunkName = "chunk", LuaTable env = null) { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif if (typeof(T) != typeof(LuaFunction) && !typeof(T).IsSubclassOf(typeof(Delegate))) { throw new InvalidOperationException(typeof(T).Name + " is not a delegate type nor LuaFunction"); } int oldTop = LuaAPI.lua_gettop(L); if (LuaAPI.luaL_loadbuffer(L, chunk, chunkName) != 0) ThrowExceptionFromError(oldTop); if (env != null) { env.push(L); LuaAPI.lua_setfenv(L, -2); } T result = (T)translator.GetObject(L, -1, typeof(T)); LuaAPI.lua_settop(L, oldTop); return result; #if THREAD_SAFT || HOTFIX_ENABLE } #endif } public LuaFunction LoadString(string chunk, string chunkName = "chunk", LuaTable env = null) { return LoadString<LuaFunction>(chunk, chunkName, env); } public object[] DoString(string chunk, string chunkName = "chunk", LuaTable env = null) { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif int oldTop = LuaAPI.lua_gettop(L); int errFunc = LuaAPI.load_error_func(L, errorFuncRef); if (LuaAPI.luaL_loadbuffer(L, chunk, chunkName) == 0) { if (env != null) { env.push(L); LuaAPI.lua_setfenv(L, -2); } if (LuaAPI.lua_pcall(L, 0, -1, errFunc) == 0) { LuaAPI.lua_remove(L, errFunc); return translator.popValues(L, oldTop); } else ThrowExceptionFromError(oldTop); } else ThrowExceptionFromError(oldTop); return null; #if THREAD_SAFT || HOTFIX_ENABLE } #endif } private void AddSearcher(LuaCSFunction searcher, int index) { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif //insert the loader LuaAPI.xlua_getloaders(L); if (!LuaAPI.lua_istable(L, -1)) { throw new Exception("Can not set searcher!"); } uint len = LuaAPI.xlua_objlen(L, -1); index = index < 0 ? (int)(len + index + 2) : index; for (int e = (int)len + 1; e > index; e--) { LuaAPI.xlua_rawgeti(L, -1, e - 1); LuaAPI.xlua_rawseti(L, -2, e); } LuaAPI.lua_pushstdcallcfunction(L, searcher); LuaAPI.xlua_rawseti(L, -2, index); LuaAPI.lua_pop(L, 1); #if THREAD_SAFT || HOTFIX_ENABLE } #endif } public void Alias(Type type, string alias) { translator.Alias(type, alias); } int last_check_point = 0; int max_check_per_tick = 20; static bool ObjectValidCheck(object obj) { return (!(obj is UnityEngine.Object)) || ((obj as UnityEngine.Object) != null); } Func<object, bool> object_valid_checker = new Func<object, bool>(ObjectValidCheck); public void Tick() { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif lock (refQueue) { while (refQueue.Count > 0) { GCAction gca = refQueue.Dequeue(); translator.ReleaseLuaBase(L, gca.Reference, gca.IsDelegate); } } last_check_point = translator.objects.Check(last_check_point, max_check_per_tick, object_valid_checker, translator.reverseMap); #if THREAD_SAFT || HOTFIX_ENABLE } #endif } //兼容API public void GC() { Tick(); } public LuaTable NewTable() { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif int oldTop = LuaAPI.lua_gettop(L); LuaAPI.lua_newtable(L); LuaTable returnVal = (LuaTable)translator.GetObject(L, -1, typeof(LuaTable)); LuaAPI.lua_settop(L, oldTop); return returnVal; #if THREAD_SAFT || HOTFIX_ENABLE } #endif } private bool disposed = false; public void Dispose() { Dispose(true); System.GC.Collect(); System.GC.WaitForPendingFinalizers(); } public virtual void Dispose(bool dispose) { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif if (disposed) return; Tick(); LuaAPI.lua_close(L); ObjectTranslatorPool.Instance.Remove(L); if (translator != null) { translator = null; } L = IntPtr.Zero; disposed = true; #if THREAD_SAFT || HOTFIX_ENABLE } #endif } public void ThrowExceptionFromError(int oldTop) { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif object err = translator.GetObject(L, -1); LuaAPI.lua_settop(L, oldTop); // A pre-wrapped exception - just rethrow it (stack trace of InnerException will be preserved) Exception ex = err as Exception; if (ex != null) throw ex; // A non-wrapped Lua error (best interpreted as a string) - wrap it and throw it if (err == null) err = "Unknown Lua Error"; throw new LuaException(err.ToString()); #if THREAD_SAFT || HOTFIX_ENABLE } #endif } internal struct GCAction { public int Reference; public bool IsDelegate; } Queue<GCAction> refQueue = new Queue<GCAction>(); internal void equeueGCAction(GCAction action) { lock (refQueue) { refQueue.Enqueue(action); } } private string init_xlua = @" local metatable = {} local rawget = rawget local setmetatable = setmetatable local import_type = xlua.import_type local load_assembly = xlua.load_assembly function metatable:__index(key) local fqn = rawget(self,'.fqn') fqn = ((fqn and fqn .. '.') or '') .. key local obj = import_type(fqn) if obj == nil then -- It might be an assembly, so we load it too. obj = { ['.fqn'] = fqn } setmetatable(obj, metatable) elseif obj == true then return rawget(self, key) end -- Cache this lookup rawset(self, key, obj) return obj end -- A non-type has been called; e.g. foo = System.Foo() function metatable:__call(...) error('No such type: ' .. rawget(self,'.fqn'), 2) end CS = CS or {} setmetatable(CS, metatable) typeof = function(t) return t.UnderlyingSystemType end cast = xlua.cast if not setfenv or not getfenv then local function getfunction(level) local info = debug.getinfo(level + 1, 'f') return info and info.func end function setfenv(fn, env) if type(fn) == 'number' then fn = getfunction(fn + 1) end local i = 1 while true do local name = debug.getupvalue(fn, i) if name == '_ENV' then debug.upvaluejoin(fn, i, (function() return env end), 1) break elseif not name then break end i = i + 1 end return fn end function getfenv(fn) if type(fn) == 'number' then fn = getfunction(fn + 1) end local i = 1 while true do local name, val = debug.getupvalue(fn, i) if name == '_ENV' then return val elseif not name then break end i = i + 1 end end end xlua.hotfix = function(cs, field, func) local tbl = (type(field) == 'table') and field or {[field] = func} for k, v in pairs(tbl) do local cflag = '' if k == '.ctor' then cflag = '_c' k = 'ctor' end xlua.access(cs, cflag .. '__Hitfix0_'..k, v) -- at least one pcall(function() for i = 1, 99 do xlua.access(cs, '__Hitfix'..i..'_'..k, v) end end) end end "; public delegate byte[] CustomLoader(ref string filepath); internal List<CustomLoader> customLoaders = new List<CustomLoader>(); //loader : CustomLoader, filepath参数:(ref类型)输入是require的参数,如果需要支持调试,需要输出真实路径。 // 返回值:如果返回null,代表加载该源下无合适的文件,否则返回UTF8编码的byte[] public void AddLoader(CustomLoader loader) { customLoaders.Add(loader); } internal Dictionary<string, LuaCSFunction> buildin_initer = new Dictionary<string, LuaCSFunction>(); public void AddBuildin(string name, LuaCSFunction initer) { if (!initer.Method.IsStatic || !Attribute.IsDefined(initer.Method, typeof(MonoPInvokeCallbackAttribute))) { throw new Exception("initer must be static and has MonoPInvokeCallback Attribute!"); } buildin_initer.Add(name, initer); } //The garbage-collector pause controls how long the collector waits before starting a new cycle. //Larger values make the collector less aggressive. Values smaller than 100 mean the collector //will not wait to start a new cycle. A value of 200 means that the collector waits for the total //memory in use to double before starting a new cycle. public int GcPause { get { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif int val = LuaAPI.lua_gc(L, LuaGCOptions.LUA_GCSETPAUSE, 200); LuaAPI.lua_gc(L, LuaGCOptions.LUA_GCSETPAUSE, val); return val; #if THREAD_SAFT || HOTFIX_ENABLE } #endif } set { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif LuaAPI.lua_gc(L, LuaGCOptions.LUA_GCSETPAUSE, value); #if THREAD_SAFT || HOTFIX_ENABLE } #endif } } //The step multiplier controls the relative speed of the collector relative to memory allocation. //Larger values make the collector more aggressive but also increase the size of each incremental //step. Values smaller than 100 make the collector too slow and can result in the collector never //finishing a cycle. The default, 200, means that the collector runs at "twice" the speed of memory //allocation. public int GcStepmul { get { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif int val = LuaAPI.lua_gc(L, LuaGCOptions.LUA_GCSETSTEPMUL, 200); LuaAPI.lua_gc(L, LuaGCOptions.LUA_GCSETSTEPMUL, val); return val; #if THREAD_SAFT || HOTFIX_ENABLE } #endif } set { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif LuaAPI.lua_gc(L, LuaGCOptions.LUA_GCSETSTEPMUL, value); #if THREAD_SAFT || HOTFIX_ENABLE } #endif } } public void FullGc() { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif LuaAPI.lua_gc(L, LuaGCOptions.LUA_GCCOLLECT, 0); #if THREAD_SAFT || HOTFIX_ENABLE } #endif } public void StopGc() { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif LuaAPI.lua_gc(L, LuaGCOptions.LUA_GCSTOP, 0); #if THREAD_SAFT || HOTFIX_ENABLE } #endif } public void RestartGc() { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif LuaAPI.lua_gc(L, LuaGCOptions.LUA_GCRESTART, 0); #if THREAD_SAFT || HOTFIX_ENABLE } #endif } public bool GcStep(int data) { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif return LuaAPI.lua_gc(L, LuaGCOptions.LUA_GCSTEP, data) != 0; #if THREAD_SAFT || HOTFIX_ENABLE } #endif } public int Memroy { get { #if THREAD_SAFT || HOTFIX_ENABLE lock (luaEnvLock) { #endif return LuaAPI.lua_gc(L, LuaGCOptions.LUA_GCCOUNT, 0); #if THREAD_SAFT || HOTFIX_ENABLE } #endif } } } } LuaException: c# exception:System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary. at System.Collections.Generic.Dictionary`2[System.String,FishPathData].get_Item (System.String key) [0x000a2] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:150 at FishPathManager.getPath (System.String name) [0x00021] in D:\Lua_Dating546\Assets\Scripts\Util\FishPathManager.cs:117 at fish.setPath (System.String pathName, Single lifeTime, Boolean freeGroup, Single roat, Single speed) [0x00000] in D:\Lua_Dating546\Assets\Scripts\Util\fish.cs:744 at CSObjectWrap.fishWrap.setPath (IntPtr L) [0x000a4] in D:\Lua_Dating546\Assets\XLua\Gen\fishWrap.cs:403 stack traceback: [C]: in method 'setPath' game.fish.gameobject.Fish:192: in function 'game.fish.gameobject.Fish.setPath' game.fish.gameobject.Fish:131: in function 'game.fish.gameobject.Fish.initInfo' game.fish.view.FishGameRoot:881: in function 'game.fish.view.FishGameRoot.createOneFish' game.fish.view.FishGameRoot:776: in function 'game.fish.view.FishGameRoot.Update' base.system.UpdateBeat:22: in function 'base.system.UpdateBeat.Update' base.Main:13: in function 'update' XLua.LuaEnv.ThrowExceptionFromError (Int32 oldTop) (at Assets/XLua/Src/LuaEnv.cs:367) XLua.DelegateBridge.SystemVoid () (at Assets/XLua/Gen/DelegatesGensBridge.cs:34) LuaFramework.LuaManager.Update () (at Assets/Scripts/Manager/LuaManager.cs:111)

ztteml
  • 粉丝: 0
上传资源 快速赚钱