网站联系qq代码,个人做的好的淘宝客网站,公司网站建设应包含哪几个板块,简述商务网站建设步骤只要有修改#xff0c;子表就不用元表的参数了#xff0c;用自己的参数#xff08;只不过和元表里的那个同名#xff09;
子表用__index“继承”了父表的值#xff0c;此时子表仍然是空表
一定是创建这样一个同名的变量在原本空空的子表里#xff0c;
传参要传具体的变…只要有修改子表就不用元表的参数了用自己的参数只不过和元表里的那个同名
子表用__index“继承”了父表的值此时子表仍然是空表
一定是创建这样一个同名的变量在原本空空的子表里
传参要传具体的变量 __index
这样会报错
local obj {}
x1
setmetatable(obj, { __index x})
print(obj.health) Lua 对于 __index 元方法有严格要求它必须是一个表或一个函数。
local obj {}
setmetatable(obj, { __index 42 }) -- 错误尝试访问时 Lua 会报错print(obj.health) -- 报错bad argument #2 to setmetatable (index must be table or function)在 Lua 中当 __index 被设置为 nil 时Lua 的行为是将其视为没有定义 __index 元方法。这不会引发错误而是简单地返回 nil
Lua 不强制要求 __index 必须被定义或赋值只在查找键失败时才会检查是否存在有效的 __index
local obj {}
setmetatable(obj, { __index nil })print(obj.health) -- 输出: nil当 Lua 查找一个表中不存在的键时
如果该表的元表中定义了 __index 如果 __index 是表Lua 会在这个表中继续查找键。如果 __index 是函数Lua 会调用该函数并将原表和键作为参数传递重点在先找到再传入参数而且这时候的参数的self什么的也是自己此时传入的参数如果该表的元表存在但 __index 为 nil 或未定义或者有但是里面没找到Lua 会直接返回 nil不会报错。 __index 的函数可以没有返回值
Lua 将该返回空的值也视为 nil
__index 的函数在被调用时会自动接受两个参数调用的表缺失的键
这两个参数是自动传入的并且在实现 __index 时是强制要求的不提供这两个参数会导致错误
local obj {}
setmetatable(obj, {__index function(table, key)print(Table is:, table)print(Key is:, key)return Default value -- 显式返回一个值end
})print(obj.health) -- 输出:
-- Table is: table: 0x...
-- Key is: health
-- Default valuesetmetatable
通过特殊的元方法来改变表的行为延伸实现默认的操作逻辑比如算术运算、比较、索引、函数调用等。Lua 会在对应的情况下自动调用它们。
以下是元表里常用的内容 索引相关
__index自定义键的访问行为
__index接受的是一个访问对象可以是表可以是函数但不可以是单个的变量
当访问表中不存在的键时Lua 会去元表里的 __index 方法找值表里有值就拿表有方法可以返回值就接收这个返回值
示例 1使用表提供默认值
local defaults { health 100, mana 50 }
local t {}
setmetatable(t, { __index defaults })print(t.health) -- 输出: 100默认值
print(t.attack) -- 输出: nil没有定义示例 2使用函数动态生成值
local t {}
setmetatable(t, {__index function(_, key)return 键 .. key .. 不存在end
})print(t.unknown) -- 输出: 键 unknown 不存在__newindex自定义键的赋值行为
当试图给表中不存在的键赋值时Lua 会调用 __newindex 方法。
可以拦截并自定义赋值逻辑。
示例限制某些键的赋值
local t {}
setmetatable(t, {__newindex function(_, key, value)print(你不能直接添加新键 .. key .. 但我记录下来了)end
})t.newKey 123 -- 输出: 你不能直接添加新键 newKey但我记录下来了
print(t.newKey) -- 输出: nil算术操作
元表可以通过定义算术相关的元方法改变表在算术操作中的行为。以下是常用的元方法
__add加法
定义两个表相加时的行为
local t1 { value 5 }
local t2 { value 10 }setmetatable(t1, {__add function(a, b)return { value a.value b.value }end
})local result t1 t2
print(result.value) -- 输出: 15__sub、__mul、__div、__mod、__pow
这些方法分别用于减法、乘法、除法、取模、幂运算。例如
local t1 { value 2 }
local t2 { value 3 }setmetatable(t1, {__mul function(a, b)return { value a.value * b.value }end
})local result t1 * t2
print(result.value) -- 输出: 6比较操作
元表还可以控制比较操作的行为
__eq等于
local t1 { id 1 }
local t2 { id 1 }setmetatable(t1, {__eq function(a, b)return a.id b.idend
})print(t1 t2) -- 输出: true__lt 和 __le小于和小于等于
local t1 { value 5 }
local t2 { value 10 }setmetatable(t1, {__lt function(a, b)return a.value b.valueend
})print(t1 t2) -- 输出: true表的行为
__tostring自定义表的字符串表示
用于定义表被转换为字符串时的行为例如 print
local t { name test }setmetatable(t, {__tostring function(table)return 表的名字是 .. table.nameend
})print(t) -- 输出: 表的名字是test__len自定义表的长度
控制 # 操作符的行为
local t { a 1, b 2 }setmetatable(t, {__len function()return 100end
})print(#t) -- 输出: 100__call使表可以被调用
让表像函数一样调用
local t {}setmetatable(t, {__call function(_, a, b)return a bend
})print(t(3, 5)) -- 输出: 8实际使用场景
元表的功能非常强大常见的使用场景包括
默认值表使用 __index 为表提供默认值。运算符重载让自定义类型支持算术或比较运算。面向对象编程通过 __index 和元表实现类与对象。只读表使用 __newindex 拦截赋值行为防止表被修改。代理表通过 __index 或 __call 动态生成数据。
修正语法思路 关于元表绑定父表的一个小细节
local base {name base,speak function(self)print(Name is .. self.name)end
}local derived {}
setmetatable(derived, { __index base })-- 调用方法
derived:speak() -- 输出: Name is nil解释
derived 没有 name 属性speak 是从元表 base 中继承的。self 绑定到调用者 derived所以 self.name 为 nil。
如果在 derived 中添加属性行为会改变
derived.name derived
derived:speak() -- 输出: Name is derived 当使用 : 调用方法时Lua 会自动将表作为第一个参数传入函数并绑定到变量 self
self 完全取决于调用者传入的第一个参数
同一个函数被别的表继承之后如果要执行这个函数self指的就是那个新的继承的表对象
这个self是动态的 这里看上去是在给Object这个表对象写新方法但这个表对象会被作为元表去被别的表继承
function这个东西写哪都一样只是一种简写对于{}调用自己的方法然后会有一个self指代自己这个自己不是真的要是自己而是使用这个方法时的自己self的功能重点不在是定义在谁的{}而在对表之间的“继承”关系的强调 现在再看这个继承的代码看着简单但是通用性很强里面的确有很多知识点
把Object声明在全局突出class的感觉
new和subClass方法也写成全局的 不用:语法糖实现Object万物之父
所有的方法都要传入参数这个参数不会
参数能不能传入只靠local对参数的规定
-- Object 万物之父类
local Object {}-- 创建新对象方法
function Object.new(self)local instance {}setmetatable(instance, { __index self }) -- 设置元表继承父类的方法return instance
end-- 添加一个通用的方法
function Object.say(self, message)print([ .. tostring(self) .. ] says: .. message)
end-- 返回 Object 类
return Object要实现一个万物之父的Object
首先创建这个大表{}因为是万物之父所以这个表里包含了new一个新的自己的能力
在这体现为function Object:new() ..... end
封装
然后是对这个自我实例化的函数的实现也就是给这个万物之父Object表{}对象写一个可以创建出新的独立的{}表的方法这个创建的新{}对象里面还要实现和Object{}对象里一模一样的功能
那问题就来到了这个新{}对象的创建以及这个新{}要怎么复制Object对象里的各种方法和数据
这里巧妙的运用了闭包即Object{}对象里的new方法里面存在的各种变量也是有生命周期的
这一点同c#
所以在这个Object{}对象里的new函数里
local obj{}
...
return obj
这个return的值在外部用变量接了就可以用了
二次强调这个new的方法是属于Object这个{}的
lua创建一个对象的流程先写好一个实实在在的{}然后想有新的“实例”就通过元表setmetatable把新的“实例”去引用上已经存在的{}里的各种...这个部分是__index在发挥作用
换句话说这个功能本是setmetatable设置元表的一个小分支
但意外的是这个小分支是实现面向对象的继承和创建新对象的关键 setmetatable()先写元表再写上面的self.__indexself Lua热更新
Lua 是 Unity 热更新的一种常见选择但并不是唯一的方案。
以下是详细的解释和 Lua 在 Unity 热更新中的应用场景
Lua 热更新的核心原理
热更新的目标是在不重新发布客户端版本的情况下更新游戏逻辑。Lua 作为脚本语言可以加载和运行外部脚本文件更新逻辑时只需替换脚本资源而无需重新打包整个应用。
在 Unity 中Lua 热更新通常通过以下方式实现
将核心逻辑如主角行为、UI 逻辑写在 Lua 脚本中。在 C# 程序中嵌入 Lua 虚拟机通常使用 LuaBridge、SLua 或 XLua 等工具。在运行时动态加载和执行 Lua 脚本文件达到热更新的效果。
为什么 Lua 是热门选择
游戏行业传统Lua 广泛应用于 Cocos2d、Corona、Roblox 等游戏引擎因此在游戏行业中积累了丰富的生态和成熟的实践经验。Unity 插件支持Unity 中有多个成熟的 Lua 框架如 XLua、SLua、ToLua可以快速上手降低实现热更新的技术门槛。适配热更新需求Lua 支持动态加载脚本结合 Unity 的 AssetBundle 等机制可以在运行时替换特定逻辑满足热更新需求。
热更新的其他方案非 Lua ILRuntimeC# 热更新 ILRuntime 是一个开源的 .NET 运行时框架可以让游戏逻辑以 C# 写成的 DLL 文件形式热更新。优点无需学习新的语言直接使用 C# 编写代码性能接近原生逻辑。缺点复杂度较高调试和配置成本稍高于 Lua。 HybridCLR Unity 原生不支持代码热更新但使用 HybridCLR开源的 AOTInterpreter 混合运行时可以实现类似 Lua 的热更新效果。优点完全基于 C#不需要引入额外语言性能优越。缺点配置复杂需要对 Unity 和 CLR 有深入理解。 AssetBundle 热更新 通过资源的动态加载更新 UI、关卡、角色等内容而不是直接更新逻辑。优点简单易用适合仅更新资源的场景。缺点无法更新游戏逻辑。 Python 或其他脚本语言 某些项目可能会选择 Python 或其他轻量脚本语言如 JavaScript作为热更新脚本语言但使用较少生态不如 Lua 完善。
如果更倾向于使用 C# 完成所有开发任务也可以探索 ILRuntime 或 HybridCLR 等更贴近 Unity 原生的热更新方案。
Lua的实现流程
eg活动与任务系统 实际场景 游戏中的限时活动、节日任务等需要随时调整或增加比如 在春节期间增加一个“新年集福活动”。 在万圣节期间添加“收集南瓜”的任务完成后奖励独特皮肤。 为何需要热更新 这些任务的规则比如“收集多少南瓜”、“奖励什么物品”可能需要动态调整。如果每次都通过重新发布客户端更新这会导致玩家需要频繁下载新版本影响体验。 热更新解决方案 开发者只需通过服务器发送新的 Lua 脚本定义任务逻辑客户端运行时加载这些脚本即可完成更新。
如果开发者对游戏代码进行了加密或封装玩家无法随意加载和替换脚本内容。否则恶意脚本可能造成数据泄露或作弊行为
游戏需要内置一个脚本虚拟机比如 Lua VM支持动态加载和执行外部脚本。如果没有这种机制热更新就无法通过脚本实现。 热更新的限制
游戏逻辑中哪些部分允许用 Lua 控制哪些是固定在 C# 或引擎层Unity 原生中通常是预先定义好的。
一些核心机制如图形渲染、网络通信往往不会交给 Lua 脚本而是保留在底层代码中。
游戏服务器通常会校验客户端发来的逻辑更新如活动配置、技能参数。如果玩家任意替换脚本但未通过服务器校验可能无法生效。
策划使用Lua
程序员需要预先用框架如 Unity 的 XLua、ToLua 或自研绑定系统把 Lua 与游戏的核心逻辑或数据结构连接起来让 Lua 脚本中的变量和程序代码的数据能够互相访问。以下是常见的绑定机制
通过 Lua 表映射游戏数据
程序员定义一个数据表如角色属性、任务数据并通过接口将其暴露给 Lua 脚本。例如
public class Player
{public int health 100;public int mana 50;
}策划如何知道哪些变量可以用
程序员一般要提供一份脚本文档或模板列出策划可以操作的变量、数据结构和接口。
文档需要详细说明 player.health 表示角色当前血量类型是整数enemy.attackPower 表示敌人的攻击力。...
程序员和策划需要约定数据和逻辑的名称
-- Lua 脚本
local tasks {{ id 1, name 收集苹果, required 10, reward 100 },{ id 2, name 击败敌人, required 5, reward 200 }
}public void LoadTaskData(LuaTable tasks)
{foreach (var task in tasks){Debug.Log($任务 {task.name}: 收集 {task.required} 件物品奖励 {task.reward} 金币);}
}也可以通过工具自动生成绑定
存在一些框架如 XLua、ToLua支持自动生成 Lua 脚本的 API 文档和绑定代码
程序员在 Unity 中使用 XLua会自动生成可以在 Lua 中调用的 C# 函数和变量清单。策划直接查阅清单即可知道哪些数据可以修改。 程序员的职责通过绑定框架如 XLua将游戏数据和 Lua 脚本连接起来并定义清晰的接口。策划的职责按照约定好的变量名和函数名在 Lua 脚本中编写逻辑修改数据即可。工具和约定的作用策划只需了解绑定的变量和接口配合文档和自动生成的工具可以轻松完成任务配置或逻辑调整。