这次就不急着往下讲解游戏功能了,先来说下lua的功能,因为Unity热更新的问题,导致很多手游都会使用c#加lua来开发,因此有很多新手,或者用lua开发了一两年的程序员,还不是很了解lua,在使用中会出现很多问题。这里推荐一个比较好的文章,有英文基础的可以看下。
PS:为什么要说这个,因为最近在自己上线半年多的手游项目中发现一个bug,之前有看到过,但没时间仔细看,就扫了一眼。
先来看下我们项目中错误的代码:

dolocal base = { __index = __default_values, __newindex = function() error( "Attempt to modify read-only table" ) end }for k, v in pairs( achievement ) dosetmetatable( v, base )end
end

写的人想实现的功能其实是禁止其他人修改策划配置的表格,但这根本就是错的。假设策划配的表示A,这里的功能就是将A的元表设置为base,猜一下会不会触发__nexindex元方法,会在什么情况下触发。
       结论是会触发,但是是在A中没有该key的时候,比如A表中只有一个键值对,key为title,value位"XX活动"。调用A["title"] = 100, 则是不会触发__nexindex元方法,而调用A["time"] = 100,则会触发__nexindex元方法。实现了禁止其他人修改策划配置的表格的需求吗,没有,这代码就是废的,还浪费内存浪费时间去执行。写了个测试代码,可以自行测试。

测试代码:

local A =
{title = "XX活动"
}
local base =
{ __newindex = function() error( "Attempt to modify read-only table" ) end
}
setmetatable(A, base )
A["title"] = 100
Logger.Log("title: "..tostring(A["title"]))
A["time"] = 100

那真正的只读表怎么写呢,其实在之前的文章([实战]C++加Lua加SDL来重写龙神录弹幕游戏(2):Lua创建SDL窗口和[实战]C++加Lua加SDL来重写龙神录弹幕游戏(4):完善Game类)中,已经说过了__nexindex的特性,并且也在游戏中多次实现禁止重载函数的功能。
       为了方便记忆,我将读取Window.ini的功能修改了,添加了一个TB_Window.lua的配置表。
TB_Window.lua

local Window =
{["1"] = {title = "Ryuujinn",x = SDL_POSITION_TYPE.SDL_WINDOWPOS_CENTERED,y = SDL_POSITION_TYPE.SDL_WINDOWPOS_CENTERED,width = 640,height = 480,fullscreen = 0,},
}dofor k, v in pairs(Window) dolocal proxy = {}local mt = {__index = v,__newindex = function(t, k, v)error("attempt to update a read-only talbe", 2)end}Window[k] = setmetatable(proxy, mt)end
endreturn Window

相当于代理模式,就是新建一个表,并将策划的表设置新表的元表,我们读取数据不是从策划配的表上获取,而是从新表上获取,新表是个空表,因此会触发__index元方法,从元表(也就是策划配的表上)获取数据,在修改的时候就会触发__nexindex元方法,因此就完美的实现了禁止其他人修改策划配置的表格的需求。
       回到GameBase.lua,注释到读取Window.ini配置的代码,添加读取TB_Window配置的代码,来实现SDL窗口的初始化工作,之前忘记修改SDL窗口的位置,这次也一并修改了,使得SDL窗口居中显示。

--初始化
function GameBase:Init()self.m_bIsRunning = false--SDL初始化local flags = SDL_INIT_TYPE.SDL_INIT_VIDEOlocal bResult = Renderer.SDLInit(flags)if not bResult thenLogger.LogError("Renderer.SDLInit(%d) failed, %s", flags, Renderer.GetError())return falseend--1.读取Window.ini配置--local filePath = "Config/Window.ini"--local windowConfig = io.open(filePath, "r")--if not windowConfig then--  Logger.LogError("Error: Can't find %s", filePath)--  return false--end--self.m_title = windowConfig:read()--self.m_width = tonumber(windowConfig:read())--self.m_height = tonumber(windowConfig:read())--self.m_bFullscreen = tonumber(windowConfig:read()) ~= 0--windowConfig:close()--local pos_x = 100--local pos_y = 100--2.读取TB_Window配置local tb_window = require "Config.TB_Window"if not tb_window thenLogger.LogError("Error: Don't find the table Config.TB_Window")return falseendself.m_title = tb_window["1"].titlelocal pos_x = tb_window["1"].xlocal pos_y = tb_window["1"].yself.m_width = tb_window["1"].widthself.m_height = tb_window["1"].heightself.m_bFullscreen = tb_window["1"].fullscreen ~= 0--根据Window.ini配置或者TB_Window配置创建SDL窗口flags = 0if self.m_bFullscreen thenflags = flags | SDL_WINDOW_TYPE.SDL_WINDOW_FULLSCREENendself.m_pSDLWindow = Renderer.CreateWindow(self.m_title, pos_x, pos_y, self.m_width, self.m_height, flags)--创建SDLRendererflags = SDL_RENDERER_TYPE.SDL_RENDERER_ACCELERATED | SDL_RENDERER_TYPE.SDL_RENDERER_PRESENTVSYNCself.m_pSDLRenderer = Renderer.CreateRenderer(self.m_pSDLWindow, -1, flags)--SDL_Image初始化flags = SDL_IMAGE_INIT_TYPE.IMG_INIT_PNGbResult = Renderer.SDLImageInit(flags)if not bResult thenLogger.LogError("Renderer.SDLImageInit(%d) failed, %s", flags, Renderer.GetError())return falseendif self.OnInit thenbResult = self:OnInit()if not bResult thenreturn falseendendself.m_bIsRunning = truereturn true
end

这里还有对TB_Window测试的代码,可以自行测试是否真的完成只读表的功能。

        local tb_window = require "Config.TB_Window"Logger.Log("title: "..tostring(tb_window["1"].title))Logger.Log("x: "..tostring(tb_window["1"].x))Logger.Log("y: "..tostring(tb_window["1"].y))Logger.Log("width: "..tostring(tb_window["1"].width))Logger.Log("height: "..tostring(tb_window["1"].height))Logger.Log("fullscreen: "..tostring(tb_window["1"].fullscreen))tb_window["1"].title = niltb_window["1"].x = niltb_window["1"].y = niltb_window["1"].width = niltb_window["1"].height = niltb_window["1"].fullscreen = nil

因为现在手游开发,国内很多都是用Unity,而且为了热更新,因此很多都会用lua开发,在面试的时候也会经常碰到lua的问题,小厂问的比较简单,比如ipair和pair的区别啥的问题,大厂的话则是很细,你必须了解元表和元方法。曾在网易面试的时候被问到过元表和元方法,很轻松通过,但是被问到个问题,新手程序员在写代码的时候会经常将局部变量写成全局变量,问你怎么解决这个问题。刚被问到的时候有点懵,一时没想到,给的思考时间也不是很多。回去后想了下,不就是只读表的问题吗,然后仔细想了一下,还有一个方案,就是多次打印全局表的内容做对比。其实这2个解决方案都有缺陷,可以自己想下。
       也写了一个将全局表改成只读表的测试代码:

 Logger.Log("_ENV: "..tostring(_ENV))Logger.Log("__G: "..tostring(_G))v1 = 100Logger.Log("v1: "..tostring(v1))local proxy = {}local mt = {__index = _G,__newindex = function(t, k, v)error("attempt to update a global talbe", 2)end}_G = setmetatable(proxy, mt)--setfenv(1, _G)_ENV = _GLogger.Log("v1: "..tostring(v1))v2 = 100Logger.Log("v2: "..tostring(v2))

lua版本比较高,是5.3,因此没有用setfenv,会报错。查了下,lua5.2版本是用_ENV,测试可以。
这里有2篇文章不错,一篇代码较长,估计很多新手不太易于理解,另一篇则是写了实现只读和只写表的功能,可以详细阅读下。
lua只读表(1)
lua只读表(2)

github地址

[实战]C++加Lua加SDL来重写龙神录弹幕游戏(6):只读表相关推荐

  1. [实战]C++加Lua加SDL来重写龙神录弹幕游戏(1):准备

    自学程序加工作好几年了,是时候来练个手了(不算用引擎开发游戏).之前Google学习资料的时候,找到一个弹幕射击游戏的教程,稍微瞄了眼,还挺不错的,但用的是封装的DxLib库,这就不开心了,后面就没看 ...

  2. [实战]C++加Lua加SDL来重写龙神录弹幕游戏(5):添加背景

    敲代码很快,写博客很慢,如果写详细一点,就有点太长了,跟写策划文档没区别了,╮(╯_╰)╭.之前虽然在SDL窗口中显示出了一张人物图片,但也只是为了测试SDL_Image而已.现在就正式来完成这次的工 ...

  3. [实战]C++加Lua加SDL来重写龙神录弹幕游戏(2):Lua创建SDL窗口

    完成了准备工作之后,就可以开始撸代码了.因为项目也不是很大,就打算大部分都用lua来开发.上一篇已经写了一部分测试代码,但都是塞到一个Main.cpp之中,主要是为了测试配置是否成功.这次的工作就要把 ...

  4. [实战]C++加Lua加SDL来重写龙神录弹幕游戏(3):添加Logger类

    上一篇已经完成了Lua调用SDL创建窗口的功能了,めでたしめでたし.但在开始敲游戏代码之前,继续做一个准备工作,添加C++类logger.        在之前的测试中,控制台上全是白色的输出字,一眼 ...

  5. [实战]C++加Lua加SDL来重写龙神录弹幕游戏(4):完善Game类

    大部分前期工作都已经完成了,可以正式开始敲游戏代码了.之前显示的都是死循环的窗口,这次终于可以来解决这个问题了,先看看今天的结果.        这次C++中有部分修改的代码,但有了之前的基础,就不花 ...

  6. 【Python_PySide2学习笔记(一)】PySide2动态加载UI方式,重写关闭窗体事件

    ** PySide2设置关闭提示窗口:动态加载UI方式,重写关闭窗体事件 ** ** 前言 ** 转载:https://blog.csdn.net/qq_44703282/article/detail ...

  7. R语言时间序列(time series)分析实战:时序数据加载、绘制时间序列图

    R语言时间序列(time series)分析实战:时序数据加载.绘制时间序列图 目录

  8. matlab dwt 多层分解,利用matlab对图片进行多层小波分解 会的加QQ511607771 加过作图之后积分加倍送...

    利用matlab对图片进行多层小波分解 会的加QQ511607771 加过作图之后积分加倍送 mip版  关注:96  答案:2  悬赏:0 解决时间 2021-01-23 13:02 已解决 202 ...

  9. openlayers学习——3、openlayers加点加圆加图标图片

    openlayers加点加圆加图标图片 前言:基于Vue,学习openlayers,根据官网demo,记录常用功能写法.本人不是专业GIS开发,只是记录,方便后续查找. 参考资料: openlayer ...

最新文章

  1. c语言使能,什么是锁存使能输入
  2. 算法(1)斐波那契数列
  3. AutoFac Ioc依赖注入容器
  4. 关于使用两个GTP/GTX出现[DRC RTSTAT-1]error([route 35-54] critical warning)的问题详解
  5. Soalris10下NDOUtils安装
  6. Java Calendar add()方法与示例
  7. 【文末有福利】量子计算是对计算本质的发现
  8. 服务器电流源泉ups电源的三大形式
  9. linux-查找相关-find-locate-grep
  10. python怎么用turtle画圆_在Python中用turtle函数画同心圆
  11. python数据可视化字段_python数据爬取及数据可视化分析
  12. 理解Python的协程机制-Yield
  13. yii、yaf、ci等php框架性能对比
  14. ajax跨域原理以及jsonp使用
  15. 关于机器人方面的sci论文_化学教学论文发表【期刊论文】化学教学论文发表
  16. 工行u盾显示316_详解工行U盾及使用方法和注意事项
  17. 学成在线-处理常见视频格式avi,mp4,mov,rmvb,flv
  18. linux pthread_cleanup_push 线程实现,线程清理(pthread_cleanup_push函数和pthread_cleanup_pop函数)...
  19. java sam接口,Java中的SAM接口是什么?
  20. ThinkBook 14P 15P 笔记本结束休眠后触控板手势失效问题解决 亲测可行

热门文章

  1. 华为watch buds升级失败怎么办!
  2. java如何去除噪点_如何对付噪点?简单4步提升画质| 数码暗房
  3. STL-algorithm
  4. Zookeeper 笔记
  5. VIVO手机桌面ICON替换失败记录
  6. 智能手表爆发在即,差异化靠这五点
  7. 使用kubeadm升级K8S
  8. CSDN帐号被盗申诉无法获得回复
  9. autodesk Revit材质如何转换为标准材质Stardard
  10. java三目运算符多个条件判断