自由学习记录(25)

news2024/11/27 10:14:18

只要有修改,子表就不用元表的参数了,用自己的参数(只不过和元表里的那个同名)

子表用__index“继承”了父表的值,此时子表仍然是空表

一定是创建这样一个同名的变量在原本空空的子表里,

传参要传具体的变量

__index

这样会报错

local obj = {}
x=1
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什么的也是自己此时传入的参数)
  • 如果该表的元表存在,但 __indexnil 或未定义或者有但是里面没找到,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 value

setmetatable()

通过特殊的元方法来改变表的行为(延伸)实现默认的操作逻辑,比如算术运算比较索引函数调用等。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.id
    end
})

print(t1 == t2) -- 输出: true
__lt__le:小于和小于等于
local t1 = { value = 5 }
local t2 = { value = 10 }

setmetatable(t1, {
    __lt = function(a, b)
        return a.value < b.value
    end
})

print(t1 < t2) -- 输出: true

表的行为

__tostring:自定义表的字符串表示

用于定义表被转换为字符串时的行为,例如 print

local t = { name = "test" }

setmetatable(t, {
    __tostring = function(table)
        return "表的名字是:" .. table.name
    end
})

print(t) -- 输出: 表的名字是:test

__len:自定义表的长度

控制 # 操作符的行为:

local t = { a = 1, b = 2 }

setmetatable(t, {
    __len = function()
        return 100
    end
})

print(#t) -- 输出: 100

__call:使表可以被调用

让表像函数一样调用:

local t = {}

setmetatable(t, {
    __call = function(_, a, b)
        return a + b
    end
})

print(t(3, 5)) -- 输出: 8

实际使用场景

元表的功能非常强大,常见的使用场景包括:

  1. 默认值表:使用 __index 为表提供默认值。
  2. 运算符重载:让自定义类型支持算术或比较运算。
  3. 面向对象编程:通过 __index 和元表实现类与对象。
  4. 只读表:使用 __newindex 拦截赋值行为,防止表被修改。
  5. 代理表:通过 __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.namenil

如果在 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.__index=self

Lua热更新

Lua 是 Unity 热更新的一种常见选择,但并不是唯一的方案。

以下是详细的解释和 Lua 在 Unity 热更新中的应用场景:

Lua 热更新的核心原理

热更新的目标是在不重新发布客户端版本的情况下更新游戏逻辑。Lua 作为脚本语言,可以加载和运行外部脚本文件,更新逻辑时只需替换脚本资源,而无需重新打包整个应用。

在 Unity 中,Lua 热更新通常通过以下方式实现:

  • 核心逻辑(如主角行为、UI 逻辑)写在 Lua 脚本中。
  • 在 C# 程序中嵌入 Lua 虚拟机(通常使用 LuaBridgeSLuaXLua 等工具)
  • 在运行时动态加载和执行 Lua 脚本文件,达到热更新的效果。

为什么 Lua 是热门选择

  • 游戏行业传统:Lua 广泛应用于 Cocos2d、Corona、Roblox 等游戏引擎,因此在游戏行业中积累了丰富的生态和成熟的实践经验
  • Unity 插件支持:Unity 中有多个成熟的 Lua 框架(如 XLuaSLuaToLua),可以快速上手,降低实现热更新的技术门槛。
  • 适配热更新需求:Lua 支持动态加载脚本,结合 Unity 的 AssetBundle 等机制,可以在运行时替换特定逻辑,满足热更新需求。

热更新的其他方案(非 Lua)

  1. ILRuntime(C# 热更新)

    • ILRuntime 是一个开源的 .NET 运行时框架,可以让游戏逻辑以 C# 写成的 DLL 文件形式热更新。
    • 优点:无需学习新的语言,直接使用 C# 编写代码;性能接近原生逻辑。
    • 缺点:复杂度较高,调试和配置成本稍高于 Lua。
  2. HybridCLR

    • Unity 原生不支持代码热更新,但使用 HybridCLR(开源的 AOT+Interpreter 混合运行时),可以实现类似 Lua 的热更新效果。
    • 优点:完全基于 C#,不需要引入额外语言;性能优越。
    • 缺点:配置复杂,需要对 Unity 和 CLR 有深入理解。
  3. AssetBundle 热更新

    • 通过资源的动态加载更新 UI、关卡、角色等内容,而不是直接更新逻辑。
    • 优点:简单易用,适合仅更新资源的场景。
    • 缺点:无法更新游戏逻辑。
  4. Python 或其他脚本语言

    • 某些项目可能会选择 Python 或其他轻量脚本语言(如 JavaScript)作为热更新脚本语言,但使用较少,生态不如 Lua 完善。

如果更倾向于使用 C# 完成所有开发任务,也可以探索 ILRuntimeHybridCLR 等更贴近 Unity 原生的热更新方案。

Lua的实现流程

eg:活动与任务系统

  • 实际场景
    游戏中的限时活动、节日任务等需要随时调整或增加,比如:

    • 在春节期间增加一个“新年集福活动”。

    • 在万圣节期间添加“收集南瓜”的任务,完成后奖励独特皮肤。

  • 为何需要热更新
    这些任务的规则(比如“收集多少南瓜”、“奖励什么物品”)可能需要动态调整。如果每次都通过重新发布客户端更新,这会导致玩家需要频繁下载新版本,影响体验。

  • 热更新解决方案
    开发者只需通过服务器发送新的 Lua 脚本,定义任务逻辑,客户端运行时加载这些脚本即可完成更新。

如果开发者对游戏代码进行了加密或封装,玩家无法随意加载和替换脚本内容。否则,恶意脚本可能造成数据泄露或作弊行为

游戏需要内置一个脚本虚拟机(比如 Lua VM),支持动态加载和执行外部脚本。如果没有这种机制,热更新就无法通过脚本实现。


热更新的限制

游戏逻辑中哪些部分允许用 Lua 控制,哪些是固定在 C# 或引擎层(Unity 原生)中,通常是预先定义好的

一些核心机制(如图形渲染、网络通信)往往不会交给 Lua 脚本,而是保留在底层代码中。

游戏服务器通常会校验客户端发来的逻辑更新(如活动配置、技能参数)。如果玩家任意替换脚本但未通过服务器校验,可能无法生效。

策划使用Lua

程序员需要预先用框架(如 Unity 的 XLuaToLua 或自研绑定系统)把 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 脚本中编写逻辑,修改数据即可。
  • 工具和约定的作用:策划只需了解绑定的变量和接口,配合文档和自动生成的工具,可以轻松完成任务配置或逻辑调整。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2248345.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Nginx】核心概念与安装配置解释

文章目录 1. 概述2. 核心概念2.1.Http服务器2.2.反向代理2.3. 负载均衡 3. 安装与配置3.1.安装3.2.配置文件解释3.2.1.全局配置块3.2.2.HTTP 配置块3.2.3.Server 块3.2.4.Location 块3.2.5.upstream3.2.6. mine.type文件 3.3.多虚拟主机配置 4. 总结 1. 概述 Nginx是我们常用的…

AIGC-----AIGC在虚拟现实中的应用前景

AIGC在虚拟现实中的应用前景 引言 随着人工智能生成内容&#xff08;AIGC&#xff09;的快速发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术的应用也迎来了新的契机。AIGC与VR的结合为创造沉浸式体验带来了全新的可能性&#xff0c;这种组合不仅极大地降低了VR内容的…

学习笔记035——MySQL索引

数据库索引 索引是为了提高数据的查询速度&#xff0c;相当于给数据进行编号&#xff0c;在查找数据的时候就可以通过编号快速找到对应的数据。 索引内部数据结构&#xff1a;B Tree 主键自带索引。 如&#xff1a; insert into user (id, name) values (1,f); insert int…

C语言数据结构-链表

C语言数据结构-链表 1.单链表1.1概念与结构1.2结点3.2 链表性质1.3链表的打印1.4实现单链表1.4.1 插入1.4.2删除1.4.3查找1.4.4在指定位置之前插入或删除1.4.5在指定位置之后插入或删除1.4.6删除指定位置1.4.7销毁链表 2.链表的分类3.双向链表3.1实现双向链表3.1.1尾插3.1.2头插…

计算机网络 网络安全基础——针对实习面试

目录 网络安全基础你了解被动攻击吗&#xff1f;你了解主动攻击吗&#xff1f;你了解病毒吗&#xff1f;说说基本的防护措施和安全策略&#xff1f; 网络安全基础 网络安全威胁是指任何可能对网络系统造成损害的行为或事件。这些威胁可以是被动的&#xff0c;也可以是主动的。…

上海乐鑫科技一级代理商飞睿科技,ESP32-C61高性价比WiFi6芯片高性能、大容量

在当今快速发展的物联网市场中&#xff0c;无线连接技术的不断进步对智能设备的性能和能效提出了更高要求。为了满足这一需求&#xff0c;乐鑫科技推出了ESP32-C61——一款高性价比的Wi-Fi 6芯片&#xff0c;旨在为用户设备提供更出色的物联网性能&#xff0c;并满足智能设备连…

初识java(2)

大家好&#xff0c;今天我们来讲讲java中的数据类型。 java跟我们的c语言的数据类型有一些差别&#xff0c;那么接下来我们就来看看。 一.字面常量&#xff0c;其中&#xff1a;199&#xff0c;3.14&#xff0c;‘a’&#xff0c;true都是常量将其称为字面常量。&#xff08;…

MMCM DRP动态配置方法(超详细讲解)

一、MMCM 源语介绍 1、调用源语 2、调用Clocking Wizard IP 调用Clocking Wizard IP核选择使用MMCM资源时&#xff0c;IP内部也是调用的MMCM源语。 Clocking Wizard IP中启用MMCM DRP接口方法&#xff1a; 在Clocking Wizard IP中设置分频倍频系数方法&#xff1a; IP核中生…

对于GC方面,在使用Elasticsearch时要注意什么?

大家好&#xff0c;我是锋哥。今天分享关于【对于GC方面&#xff0c;在使用Elasticsearch时要注意什么&#xff1f;】面试题。希望对大家有帮助&#xff1b; 对于GC方面&#xff0c;在使用Elasticsearch时要注意什么&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java…

Spring Boot 与 Spring Cloud Alibaba 版本兼容对照

版本选择要点 Spring Boot 3.x 与 Spring Cloud Alibaba 2022.0.x Spring Boot 3.x 基于 Jakarta EE&#xff0c;javax.* 更换为 jakarta.*。 需要使用 Spring Cloud 2022.0.x 和 Spring Cloud Alibaba 2022.0.x。 Alibaba 2022.0.x 对 Spring Boot 3.x 的支持在其发行说明中…

在Vue3项目中引入省市区联动插件

1. 打开HBuilder X 图1 2. 新建一个空项目 文件->新建->项目->uni-app 填写项目名称&#xff1a;vue3demo 选择项目存放目录&#xff1a;D:/HBuilderProjects 一定要注意vue的版本&#xff0c;当前选择的版本为vue3 图2 点击“创建”之后进入项目界面 图3 其中各文件…

STM32C011开发(3)----Flash操作

STM32C011开发----3.Flash操作 概述硬件准备视频教学样品申请源码下载参考程序生成STM32CUBEMX串口配置堆栈设置串口重定向FLASH数据初始化FLASH 读写演示 概述 STM32C011 系列微控制器内置 Flash 存储器&#xff0c;支持程序存储与数据保存&#xff0c;具备页面擦除、双字写入…

JVM详解:垃圾回收机制

java作为大型服务开发的主流语言&#xff0c;其运行会占用大量的内存空间&#xff0c;那么合理的使用有限的服务器资源至关重要。和大多数翻译性语言一样&#xff0c;java的运行环境jvm也内置垃圾回收机制&#xff0c;其通过一些合理的算法组合&#xff0c;定时来对堆中保存的不…

【拥抱AI】如何查看Milvus的使用情况?

查看Milvus的使用情况和性能指标可以帮助你了解数据库的健康状况、性能指标和资源使用情况。以下是一些常用的方法和工具&#xff0c;帮助你全面监控和查看Milvus的使用情况和性能指标。 1. 查看日志 Milvus的日志文件记录了运行时的各种信息&#xff0c;包括错误、警告和调…

基于Netty实现聊天室

前言 了解了Netty的基本功能和相关概念&#xff0c;使用基于Netty实现多人聊天的功能。 需求 1.服务端能够接收客户端的注册&#xff0c;并且接受用户的信息注册 2.服务端能够处理客户端发送的消息&#xff0c;并且根据消息类型进行私发或者广播发送消 3.服务端能够私发消…

利用 Jsoup 进行高效 Web 抓取与 HTML 处理

Jsoup 是一款 Java 的 HTML 解析器&#xff0c;可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API&#xff0c;可通过 DOM&#xff0c;CSS 以及类似于 JQuery 的操作方法来取出和操作数据。 官网&#xff1a;https://jsoup.org/ 中文文档&#xff1a;Jsou…

【c语言】文件操作详解 - 从打开到关闭

文章目录 1. 为什么使用文件&#xff1f;2. 什么是文件&#xff1f;3. 如何标识文件&#xff1f;4. 二进制文件和文本文件&#xff1f;5. 文件的打开和关闭5.1 流和标准流5.1.1 流5.1.2 标准流 5.2 文件指针5.3 文件的打开和关闭 6. 文件的读写顺序6.1 顺序读写函数6.2 对比一组…

004 逻辑变量与运算

当0和1表示逻辑状态时&#xff0c;两个二进制数码按照某种特定的因果关系进行的运算——就叫&#xff1a;逻辑运算 1.二值逻辑变量与基本逻辑运算 逻辑代数: 与普通代数不同,逻辑代数中的变量只有0和1两个可取值&#xff0c;它们分别用来表示完全两个对立的逻辑状态 逻辑运…

Deepnote、JupyterLab、Google Colab、Amazon SageMaker、VS Code对比

功能比较 平台语言支持扩展性数据连接可视化能力DeepnotePython、R、SQL中等&#xff0c;依赖云端支持主要云平台&#xff08;BigQuery、Snowflake等&#xff09;内置仪表盘与交互图表JupyterLab多种语言&#xff0c;插件支持广泛极高&#xff0c;完全可自定义使用库&#xff…

网络安全中的数据科学如何重新定义安全实践?

组织每天处理大量数据&#xff0c;这些数据由各个团队和部门管理。这使得全面了解潜在威胁变得非常困难&#xff0c;常常导致疏忽。以前&#xff0c;公司依靠 FUD 方法&#xff08;恐惧、不确定性和怀疑&#xff09;来识别潜在攻击。然而&#xff0c;将数据科学集成到网络安全中…