换装玩法
你可以在PC编辑器中新建地图,选择Lua模板图中的换装玩法,查看本示例的完整工程
游戏简介
玩家站在对应职业公告牌后方的区域内,会自动穿上对应职业的服饰。回到中心区域后,会自动卸下服饰。
技术要点
- Lua 实现ClassUtils.class函数
- Lua 理解面向对象编程,并定义DressUpArea类
- Lua 使用Lua文件配置装扮区域信息
- Lua 计算每个装饰区域的相关信息
- Lua 实现装扮区域的辅助函数
- Lua 实现创建装扮区域函数(createDressUpArea)
- Lua 根据角度创建角色展示模型
注:本示例可在编辑器模板图中下载。
技术要点分析
Lua 实现ClassUtils.class函数
- 此函数实现简单但功能强大的类系统
- 该系统特点:1.单继承 2.自动调用父类的构造函数 3.允许重写函数 4.提供类名和父类引用
- 为Lua提供面向对象编程结构,可以实现面向对象编程
lua
-- 定义ClassUtils.class函数,接受类名和可选父类作为参数
function ClassUtils.class(classname, super)
-- 父类检查,确保父类要么是nil,要么是table
local superType = type(super)
assert(super == nil or type(super) == "table", superType)
-- 创建新类,如果有父类,设置元表实现继承
local cls
if super then
cls = {}
setmetatable(cls, {__index = super})
cls.super = super
else
cls = {}
end
-- 属性设置,设置类名和元表
cls.__cname = classname
cls.__index = cls
-- 创建新实例,并设置其元表为类本身
function cls.new(...)
local instance = setmetatable({}, cls)
local child = cls
local classes = {cls}
-- 收集类及其所有父类
while child.super do
table.insert(classes, child.super)
child = child.super
end
-- 从最顶层开始,调用类中的构造函数
for i=#classes,1,-1 do
local ctor = rawget(classes[i], "ctor")
if ctor then
ctor(instance, ...)
end
end
-- 返回实例
return instance
end
-- 返回类
return cls
end
注:代码位置:Utils/ClassUtils.lua
Lua 理解面向对象编程,并定义DressUpArea类
理解类和对象的关系
类是对象的抽象,对象是类的实例
类定义了一类对象的属性和函数;对象是类的一个实例,可以通过类名创建对象
属性和方法是类的所有对象共有的
面向对象编程的核心思想:封装、继承、多态
封装:将对象的属性和行为封装到一个类中,以便于管理和使用(封装DressUpArea类)
继承:将父类中定义的属性和行为复制到子类中,以便于子类使用(本次示例不涉及)
多态:子类可以重写父类的同名函数,从而实现不同的行为(本次示例不涉及)
接下来我们将定义一个DressUpArea类,用于表示装扮区域。
lua
-- 导入ClassUtils.class函数
local class = require("Utils.ClassUtils").class
-- 定义DressUpArea类
---@class DressUpArea
---@field new fun(string, table, fun, fun, fun, Vector3, number): DressUpArea
local DressUpArea = class("DressUpArea")
-- 定义DressUpArea类构造函数
function DressUpArea.new(key, info, enterCallback, leaveCallback, pos, yaw)
-- 定义类的属性即该类会有哪些数据
self.id = dressUpId
self.info = info
self.exitTrigger = nil
self.enterCb = enterCallback
self.exitCb = exitCallback
self.areaObj = nil
self.showObj = nil
self.center = math.Vector3(0, 0, 0)
-- 初始化调用创建装扮区域函数
function DressUpArea:createArea(pos, yaw)
end
-- 定义DressUpArea类的函数即该类会有哪些行为(通俗讲该类可以做什么)
function DressUpArea:createArea(pos, yaw)
-- 以下代码省略,后面会详细介绍
end
注:以上代码简化,详细代码位置:main.lua
Lua 使用Lua文件配置装扮区域信息
- 配置装扮区域信息
- 使用Lua文件配置装扮区域信息
lua
return {
Doctor = {
name = "医生",
modelCreatureKey = Prefab.character["职业-医生1"],
},
Director = {
name = "指挥员",
modelCreatureKey = Prefab.character["职业-指挥员12"],
},
-- 以下省略
}
注:以上代码简化,详细代码位置:Data/DressUpData.lua
lua
-- 导入DressUpData.lua文件,即可使用DressUpData.lua文件中的数据
local DressUpData = require("Data.DressUpData")
注:代码位置:main.lua
使用lua文件配置装扮区域信息,可以将配置信息保存在一个lua文件中,方便管理和维护。
Lua 计算每个装饰区域的相关信息
lua
-- 计算圆形排列的参数
local numDressUps = #dressUpDatas -- 装饰区域数量
local angleDelta = math.pi * 2.0 / numDressUps -- 角度增量
local radius = 30 -- 装扮区域半径
local currAngle = 0.0 -- 当前角度
-- 遍历所有装饰区域信息
for _, data in ipairs(dressUpDatas) do
local key = data[1] -- 装饰区域ID
local info = data[2] -- 装饰区域信息
-- 计算装扮区域的位置
local dir = math.Vector3(math.cos(currAngle), 0, math.sin(currAngle)) -- 装扮区域方向
local pos = math.Vector3(0, -4, 0) + dir * radius -- 装扮区域位置
createDressUpArea(key, info, pos, math.pi - currAngle) -- 创建装扮区域(后面会详细介绍)
currAngle = currAngle + angleDelta -- 更新角度
end
注:代码位置:main.lua
Lua 实现装扮区域的辅助函数
lua
-- 创建装扮区域的辅助函数
local function createDressUpArea(key, info, pos, yaw)
-- 定义进入区域的回调函数
local function _enterCallback(role, character)
-- 如果角色为空,则返回
if character.get_role_id() == -1 then
return
end
-- 设置角色模型
character.set_model_by_creature_key(info.modelCreatureKey)
end
-- 创建装扮区域对象,并传入相关信息以及回调函数
DressUpArea.new(key, info, _enterCallback, nil, pos, yaw)
end
注:代码位置:main.lua
Lua 实现创建装扮区域函数(createDressUpArea)
lua
-- 创建装扮区域
-- pos 装扮区域位置
-- yaw 装扮区域朝向
function DressUpArea:createArea(pos, yaw)
local info = self.info
local text = info.name
-- 创建单位组
local area = GameAPI.create_unit_group(Consts.JOB_CHOOSE_PREFAB, pos, math.Quaternion(0, yaw, 0))
-- 将单位组赋值给类的属性
self.areaObj = area
-- 后面创建展示用角色会用到
local boardPos = pos
-- 遍历区域中的子对象
for _, child in ipairs(area.get_children()) do
local childName = child.get_name()
-- 设置装扮介绍
if string.find(childName, "名称", 1, true) == 1 then
child.set_billboard_text(text)
boardPos = child.get_position()
end
-- 设置装扮选择区域
if string.find(childName, "选择区域", 1, true) == 1 then
local areaId = LuaAPI.get_unit_id(child)
local areaCenter = child.get_position()
self.center = areaCenter
-- 注册进入装扮选择区域的触发器
LuaAPI.global_register_trigger_event(
{ EVENT.ANY_LIFEENTITY_TRIGGER_SPACE, Enums.TriggerSpaceEventType.ENTER, areaId },
function(_, _, data)
local character = data.event_unit
local role = character.get_role()
-- 检查是否为有效角色
if role == nil or character.get_camp_id() == -1 then
return
end
self.enterCb(role, character)
end
)
-- 注册退出装扮选择区域的触发器
self.exitTrigger = LuaAPI.global_register_trigger_event(
{ EVENT.ANY_LIFEENTITY_TRIGGER_SPACE, Enums.TriggerSpaceEventType.LEAVE, areaId },
function(_, _, data)
local character = data.event_unit
local role = character.get_role()
if self.exitCb then
self.exitCb(role, character)
end
end
)
end
end
-- 计算展示用角色的位置和方向(后面会详细介绍)
end
注:代码位置:main.lua
Lua 根据角度创建角色展示模型
lua
if info.modelCreatureKey then
-- 计算展示用角色的位置和方向
local dir = math.Vector3(0, 0, 1)
dir:set_pitch_yaw(0, yaw + math.pi / 2) -- 根据yaw计算展示用角色的朝向
-- 创建展示用角色
self.showObj = GameAPI.create_creature(
info.modelCreatureKey,
boardPos + dir * 1.0,
math.Quaternion(0, yaw + math.pi / 2, 0),
math.Vector3(1, 1, 1)
)
end
注:代码位置:main.lua
完整代码
lua
-- 导入必要的模块
local Consts = require("Data.Consts")
local DressUpData = require("Data.DressUpData")
local class = require("Utils.ClassUtils").class
-- 定义DressUpArea类
---@class DressUpArea
---@field new fun(string, table, fun, fun, fun, Vector3, number): DressUpArea
local DressUpArea = class("DressUpArea")
-- DressUpArea类的构造函数
function DressUpArea:ctor(dressUpId, info, enterCallback, exitCallback, pos, yaw)
self.id = dressUpId
self.info = info
self.exitTrigger = nil
self.enterCb = enterCallback
self.exitCb = exitCallback
self.areaObj = nil
self.showObj = nil
self.center = math.Vector3(0, 0, 0)
self:createArea(pos, yaw)
end
-- 创建装扮区域
function DressUpArea:createArea(pos, yaw)
local info = self.info
local text = info.name
-- 创建单位组
local area = GameAPI.create_unit_group(Consts.JOB_CHOOSE_PREFAB, pos, math.Quaternion(0, yaw, 0))
self.areaObj = area
local boardPos = pos
-- 遍历区域中的子对象
for _, child in ipairs(area.get_children()) do
local childName = child.get_name()
-- 设置装扮介绍
if string.find(childName, "名称", 1, true) == 1 then
child.set_billboard_text(text)
boardPos = child.get_position()
end
-- 设置装扮选择区域
if string.find(childName, "选择区域", 1, true) == 1 then
local areaId = LuaAPI.get_unit_id(child)
local areaCenter = child.get_position()
self.center = areaCenter
-- 注册进入装扮选择区域的触发器
LuaAPI.global_register_trigger_event(
{ EVENT.ANY_LIFEENTITY_TRIGGER_SPACE, Enums.TriggerSpaceEventType.ENTER, areaId },
function(_, _, data)
local character = data.event_unit
local role = character.get_role()
-- 检查是否为有效角色
if role == nil or character.get_camp_id() == -1 then
return
end
self.enterCb(role, character)
end
)
-- 注册退出装扮选择区域的触发器
self.exitTrigger = LuaAPI.global_register_trigger_event(
{ EVENT.ANY_LIFEENTITY_TRIGGER_SPACE, Enums.TriggerSpaceEventType.LEAVE, areaId },
function(_, _, data)
local character = data.event_unit
local role = character.get_role()
if self.exitCb then
self.exitCb(role, character)
end
end
)
end
end
if info.modelCreatureKey then
-- 计算展示用角色的位置和方向
local dir = math.Vector3(0, 0, 1)
dir:set_pitch_yaw(0, yaw + math.pi / 2)
-- 创建展示用角色
self.showObj = GameAPI.create_creature(
info.modelCreatureKey,
boardPos + dir * 1.0,
math.Quaternion(0, yaw + math.pi / 2, 0),
math.Vector3(1, 1, 1)
)
end
end
-- 创建装扮区域的辅助函数
local function createDressUpArea(key, info, pos, yaw)
-- 定义进入区域的回调函数
local function _enterCallback(role, character)
if character.get_role_id() == -1 then
return
end
character.set_model_by_creature_key(info.modelCreatureKey)
end
DressUpArea.new(key, info, _enterCallback, nil, pos, yaw)
end
-- 设置所有装扮区域
local function setupDressUpAreas()
local dressUpDatas = {}
for key, info in pairs(DressUpData) do
table.insert(dressUpDatas, { key, info })
end
-- 计算圆形排列的参数
local numDressUps = #dressUpDatas
local angleDelta = math.pi * 2.0 / numDressUps
local radius = 30
local currAngle = 0.0
-- 创建每个装扮区域
for _, data in ipairs(dressUpDatas) do
local key = data[1]
local info = data[2]
-- 计算装扮区域的位置
local dir = math.Vector3(math.cos(currAngle), 0, math.sin(currAngle))
local pos = math.Vector3(0, -4, 0) + dir * radius
createDressUpArea(key, info, pos, math.pi - currAngle)
currAngle = currAngle + angleDelta
end
DressUpArea.new("Default", { name = "取消装扮" }, function(role, character)
character.reset_model()
end, nil, math.Vector3(0, -4, 0), math.pi)
end
-- 注册游戏初始化事件
LuaAPI.global_register_trigger_event({ EVENT.GAME_INIT }, function()
setupDressUpAreas()
end)
注:代码位置:main.lua
编辑器配置
创建生物预设
- 打开预设编辑器面板,将蛋仔生物另存为预设
- 在新预设的属性窗口中设置蛋仔生物的装饰,并应用到所有对象
- 使用蛋仔开发助手同步至VSCode侧,实现动态创建NPC蛋仔生物
创建装扮选择区域预设
- 选中所有子组件,并合并组件组并创建装扮选择区域预设
- 在预设编辑器可查看装扮选择区域预设
- 使用蛋仔开发助手同步至VSCode侧,实现动态创建装扮选择区域