Skip to content

3D称号玩法 ​

你可以在PC编辑器中新建地图,选择Lua模板图中的Lua模板-称号系统玩法,查看本示例的完整工程。

游戏简介 ​

玩家可以为蛋仔选择和佩戴不同的称号,称号会以3D形式显示在角色上方。系统支持称号选择和佩戴功能,并提供了完整的UI交互界面。

技术要点 ​

  • Lua 实现称号数据配置
  • Lua 自定义游戏事件
  • Lua 角色称号状态管理
  • Lua 称号存档数据处理
  • Lua 称号UI系统实现
  • Lua 3D显示层管理
  • Lua 事件系统集成

注:本示例可在编辑器模板图中下载。

编辑器配置 ​

搭建2DUI界面 ​

  • 搭建称号选择界面

搭建称号选择界面

  • 添加相应按钮的点击事件

称号标签1点击事件

注: 称号标签列表事件,佩戴按钮事件依次添加

搭建3DUI界面 ​

  • 点击界面,切换至场景界面,点击管理按钮后在自定义一栏中添加称号3D显示层

称号3D显示层

  • 点击设置,修改新场景界面配置

新场景界面配置

  • 关闭场景界面管理界面,为界面添加3DUI

关闭场景界面管理

  • 添加称号3D显示层UI节点

称号UI底板

称号UI文字

  • 修改称号3D显示层UI节点信息

修改UI节点信息

存档数据配置 ​

  • 点击规则按钮,在规则设置界面选择游戏进度一栏,添加自定义玩家存档数据

添加存档数据

编辑器数据同步到Lua编程中使用 ​

导出数据

编辑器侧:蛋仔开发助手已连接状态下且修改已经保存。

VSCode侧:蛋仔开发助手已连接状态下,需勾选UI节点,成就数据,预设编号并点击导出数据即可在Data目录下找到相应数据文件。

技术要点分析 ​

Lua 实现称号数据配置 ​

lua
local ArchivesData = require("Data.ArchivesData")

return {
	["潮流大师"] = {
		id = ArchivesData["潮流大师"].id,
		icon_id = 11144,
		name = "潮流大师",
	},
	["盲盒收藏家"] = {
		id = ArchivesData["盲盒收藏家"].id,
		icon_id = 11142,
		name = "盲盒收藏家",
	},
	["金牌蛋"] = {
		id = ArchivesData["金牌蛋"].id,
		icon_id = 11145,
		name = "金牌蛋",
	},
}

注:代码位置:Data/DesignationData.lua

Lua 自定义游戏事件 ​

lua
local GameEvents = {
	-- 来自预设
	UI_CLICK_DESIGNATION_LIST_1 = "称号标签1点击",
	UI_CLICK_DESIGNATION_LIST_2 = "称号标签2点击",
	UI_CLICK_DESIGNATION_LIST_3 = "称号标签3点击",
	UI_CLICK_DESIGNATION_WEAR = "佩戴称号",
}
return GameEvents

注:代码位置:GameEvents.lua

角色称号状态管理 ​

lua
-- 存储所有角色的称号状态信息
local allRoleStatus = {}

-- 定义系统中所有可用的称号
local DESIGNATION_KEY = {
    "潮流大师",
    "盲盒收藏家",
    "金牌蛋",
}

注:代码位置:main.lua

称号存档数据处理 ​

lua
--- 获取角色称号的存档值
--- @param role table 角色对象
--- @param key string 称号键值或存档键值
--- @return boolean 称号是否已获取或存档值
local function getArchiveValue(role, key)
	local result = role.get_archive_by_type(ArchivesData[key].vType, ArchivesData[key].id)
	return result
end

--- 设置角色称号的存档值
--- @param role table 角色对象
--- @param key string 称号键值或存档键值
--- @param value boolean 要设置的值
local function setArchiveValue(role, key, value)
	role.set_archive_by_type(ArchivesData[key].vType, ArchivesData[key].id, value)
end

--- 根据称号ID获取对应的称号键值
--- @param id number 称号ID
--- @return string|nil 找到则返回称号键值,否则返回nil
local function designationIdToKey(id)
	if not id then
		return nil
	end
	for _, key in ipairs(DESIGNATION_KEY) do
		if DesignationData[key].id == id then
			return key
		end
	end
	return nil
end

注:代码位置:main.lua

称号UI系统实现 ​

  • 创建3DUI界面
lua
-- 初始化所有角色的称号状态
for _, role in ipairs(GameAPI.get_all_valid_roles()) do
	-- 创建角色状态记录
	allRoleStatus[role.get_roleid()] = {
		selDesignationKey = DESIGNATION_KEY[1], -- 默认选中第一个称号
		wearDesignationKey = designationIdToKey(getArchiveValue(role, "佩戴称号key")), -- 获取已佩戴的称号
		designation3DLayer = role.get_ctrl_unit().create_scene_ui_bind_unit(PrefabData.layout["3D称号界面"], -- 创建3D显示层
		Enums.ModelSocket.socket_head,
		math.Vector3(0, 3, 0),
		-1.0,
		true,
		true
		),
	}
	-- 初始化UI显示
	initDesignationUI(role)
end

注:代码位置:main.lua

  • 初始化并更新称号UI
lua
--- 更新选中称号的UI显示
--- @param role table 角色对象
--- @param designationKey string 称号键值
local function updateSelDesignationUI(role, designationKey)
	if not role or not designationKey then
		return
	end

	-- 获取称号状态
	local owned = getArchiveValue(role, designationKey)
	local wearDesignationKey = designationIdToKey(getArchiveValue(role, "佩戴称号key"))

	-- 更新称号显示
	local designationInfo = DesignationData[designationKey]
	role.set_label_text(UINodes["选中称号标签文字"], designationInfo.name)
	role.set_image_texture_by_key_with_auto_resize(UINodes["选中称号标签"], designationInfo.icon_id, false)

	-- 更新佩戴按钮状态
	if owned == false then
		role.set_button_text(UINodes["佩戴按钮"], "未获取")
	elseif wearDesignationKey == designationKey then
		role.set_button_text(UINodes["佩戴按钮"], "已佩戴")
	else
		role.set_button_text(UINodes["佩戴按钮"], "佩戴")
	end
end

--- 初始化角色的称号UI界面
--- @param role table 角色对象
local function initDesignationUI(role)
	if not role then
		return
	end

	-- 初始化称号列表UI
	for i, key in ipairs(DESIGNATION_KEY) do
		local designationInfo = DesignationData[key]
		-- 设置称号图标
		role.set_image_texture_by_key_with_auto_resize(UINodes["称号标签" .. i], designationInfo.icon_id, false)
		-- 设置称号名称
		role.set_label_text(UINodes["称号标签文字" .. i], designationInfo.name)
		-- 根据获取状态设置显示样式
		local owned = getArchiveValue(role, key)
		if owned == false then
			role.set_label_color(UINodes["称号标签文字" .. i], 0x999999, 0)
		end
	end

	-- 初始化佩戴称号显示
	local roleStatus = allRoleStatus[role.get_roleid()]
	local wearDesignationKey = roleStatus.wearDesignationKey
	local designation3DLayer = roleStatus.designation3DLayer

	-- 处理未佩戴称号的情况
	if wearDesignationKey == nil then
		wearDesignationKey = DESIGNATION_KEY[1]
		setDesignationLayerState(designation3DLayer, false)
	else
		updateDesignationLayer(role, wearDesignationKey)
	end

	-- 更新选中称号UI
	updateSelDesignationUI(role, wearDesignationKey)
end

注:代码位置:main.lua

Lua 3D显示层管理 ​

lua
--- 设置称号3D显示层的可见性
--- @param layer number 3D显示层ID
--- @param visible boolean 是否可见
local function setDesignationLayerState(layer, visible)
	if not layer then
		return
	end
	for _, role in ipairs(GameAPI.get_all_valid_roles()) do
		GameAPI.set_scene_ui_visible(layer, role, visible)
	end
end

--- 更新称号的3D显示层
--- @param curRole table 当前角色对象
--- @param designationKey string 称号键值
local function updateDesignationLayer(curRole, designationKey)
	if not curRole or not designationKey then
		return
	end

	-- 获取显示层和称号信息
	local layer = allRoleStatus[curRole.get_roleid()].designation3DLayer
	local owned = getArchiveValue(curRole, designationKey)
	local designationInfo = DesignationData[designationKey]

	-- 获取UI节点
	local iconNode = GameAPI.get_eui_node_at_scene_ui(layer, UINodes["3D称号背景"])
	local nameNode = GameAPI.get_eui_node_at_scene_ui(layer, UINodes["3D称号文字"])

	-- 设置显示状态
	setDesignationLayerState(layer, owned)
	for _, role in ipairs(GameAPI.get_all_valid_roles()) do
		role.set_image_texture_by_key_with_auto_resize(iconNode, designationInfo.icon_id, false)
		role.set_label_text(nameNode, designationInfo.name)
	end
end

注:使用3D显示层可以让称号始终跟随角色位置。

注:代码位置:main.lua

Lua 事件系统集成 ​

lua
--- 处理称号佩戴按钮点击事件
--- @param role table 角色对象
--- @param designationKey string 称号键值
local function onClickDesignationWear(role, designationKey)
	if not role or not designationKey then
		return
	end

	local owned = getArchiveValue(role, designationKey)

	-- 处理称号佩戴逻辑
	if owned == false then
		role.show_tips("未获取")
	else
		role.show_tips("已佩戴")
		-- 保存佩戴状态
		setArchiveValue(role, "佩戴称号key", ArchivesData[designationKey].id)
		-- 更新UI显示
		updateSelDesignationUI(role, designationKey)
		updateDesignationLayer(role, designationKey)
	end
end


-- 注册称号佩戴按钮点击事件
LuaAPI.global_register_custom_event(GameEvents.UI_CLICK_DESIGNATION_WEAR, function(_, _, data)
	local roleStatus = allRoleStatus[data.role.get_roleid()]
	local designationKey = roleStatus.selDesignationKey
	roleStatus.wearDesignationKey = designationKey
	onClickDesignationWear(data.role, designationKey)
end)

-- 注册称号列表项点击事件
LuaAPI.global_register_custom_event(GameEvents.UI_CLICK_DESIGNATION_LIST_1, function(_, _, data)
	local designationKey = DESIGNATION_KEY[1]
	allRoleStatus[data.role.get_roleid()].selDesignationKey = designationKey
	updateSelDesignationUI(data.role, designationKey)
end)

注:代码位置:main.lua

完整代码 ​

lua
-- 导入必要的模块
local UINodes = require("Data.UINodes") -- UI节点配置数据
local ArchivesData = require("Data.ArchivesData") -- 存档相关数据
local DesignationData = require("Data.DesignationData") -- 称号配置数据
local GameEvents = require("GameEvents") -- 游戏事件定义
local PrefabData = require("Data.Prefab")

-- 存储所有角色的称号状态信息
-- key: roleId, value: {selDesignationKey, wearDesignationKey, designation3DLayer}
local allRoleStatus = {}

local allCreatureStatus = {}

local designation3DShowLayerDistance = 10

-- 定义系统中所有可用的称号
local DESIGNATION_KEY = {
	"潮流大师",
	"盲盒收藏家",
	"金牌蛋",
}

--- 获取角色称号的存档值
--- @param role table 角色对象
--- @param key string 称号键值或存档键值
--- @return boolean 称号是否已获取或存档值
local function getArchiveValue(role, key)
	local result = role.get_archive_by_type(ArchivesData[key].vType, ArchivesData[key].id)
	return result
end

--- 设置角色称号的存档值
--- @param role table 角色对象
--- @param key string 称号键值或存档键值
--- @param value boolean 要设置的值
local function setArchiveValue(role, key, value)
	role.set_archive_by_type(ArchivesData[key].vType, ArchivesData[key].id, value)
end

--- 根据称号ID获取对应的称号键值
--- @param id number 称号ID
--- @return string|nil 找到则返回称号键值,否则返回nil
local function designationIdToKey(id)
	if not id then
		return nil
	end
	for _, key in ipairs(DESIGNATION_KEY) do
		if DesignationData[key].id == id then
			return key
		end
	end
	return nil
end

--- 为所有角色设置所有称号为已获得状态
local function getAllDesignation()
	for _, role in ipairs(GameAPI.get_all_valid_roles()) do
		for _, key in ipairs(DESIGNATION_KEY) do
			setArchiveValue(role, key, true)
		end
	end
end

--- 设置称号3D显示层的可见性
--- @param layer number 3D显示层ID
--- @param visible boolean 是否可见
local function setDesignationLayerState(layer, visible)
	if not layer then
		return
	end
	for _, role in ipairs(GameAPI.get_all_valid_roles()) do
		GameAPI.set_scene_ui_visible(layer, role, visible)
	end
end

--- 更新选中称号的UI显示
--- @param role table 角色对象
--- @param designationKey string 称号键值
local function updateSelDesignationUI(role, designationKey)
	if not role or not designationKey then
		return
	end

	-- 获取称号状态
	local owned = getArchiveValue(role, designationKey)
	local wearDesignationKey = designationIdToKey(getArchiveValue(role, "佩戴称号key"))

	-- 更新称号显示
	local designationInfo = DesignationData[designationKey]
	role.set_label_text(UINodes["选中称号标签文字"], designationInfo.name)
	role.set_image_texture_by_key_with_auto_resize(UINodes["选中称号标签"], designationInfo.icon_id, false)

	-- 更新佩戴按钮状态
	if owned == false then
		role.set_button_text(UINodes["佩戴按钮"], "未获取")
	elseif wearDesignationKey == designationKey then
		role.set_button_text(UINodes["佩戴按钮"], "已佩戴")
	else
		role.set_button_text(UINodes["佩戴按钮"], "佩戴")
	end
end

--- 更新称号的3D显示层
--- @param curRole table 当前角色对象
--- @param designationKey string 称号键值
local function updateDesignationLayer(curRole, designationKey)
	if not curRole or not designationKey then
		return
	end

	-- 获取显示层和称号信息
	local layer = allRoleStatus[curRole.get_roleid()].designation3DLayer
	local owned = getArchiveValue(curRole, designationKey)
	local designationInfo = DesignationData[designationKey]

	-- 获取UI节点
	local iconNode = GameAPI.get_eui_node_at_scene_ui(layer, UINodes["3D称号背景"])
	local nameNode = GameAPI.get_eui_node_at_scene_ui(layer, UINodes["3D称号文字"])

	-- 设置显示状态
	setDesignationLayerState(layer, owned)
	for _, role in ipairs(GameAPI.get_all_valid_roles()) do
		role.set_image_texture_by_key_with_auto_resize(iconNode, designationInfo.icon_id, false)
		role.set_label_text(nameNode, designationInfo.name)
	end
end

--- 初始化角色的称号UI界面
--- @param role table 角色对象
local function initDesignationUI(role)
	if not role then
		return
	end

	-- 初始化称号列表UI
	for i, key in ipairs(DESIGNATION_KEY) do
		local designationInfo = DesignationData[key]
		-- 设置称号图标
		role.set_image_texture_by_key_with_auto_resize(UINodes["称号标签" .. i], designationInfo.icon_id, false)
		-- 设置称号名称
		role.set_label_text(UINodes["称号标签文字" .. i], designationInfo.name)
		-- 根据获取状态设置显示样式
		local owned = getArchiveValue(role, key)
		if owned == false then
			role.set_label_color(UINodes["称号标签文字" .. i], 0x999999, 0)
		end
	end

	-- 初始化佩戴称号显示
	local roleStatus = allRoleStatus[role.get_roleid()]
	local wearDesignationKey = roleStatus.wearDesignationKey
	local designation3DLayer = roleStatus.designation3DLayer

	-- 处理未佩戴称号的情况
	if wearDesignationKey == nil then
		wearDesignationKey = DESIGNATION_KEY[1]
		setDesignationLayerState(designation3DLayer, false)
	else
		updateDesignationLayer(role, wearDesignationKey)
	end

	-- 更新选中称号UI
	updateSelDesignationUI(role, wearDesignationKey)
end

--- 处理称号佩戴按钮点击事件
--- @param role table 角色对象
--- @param designationKey string 称号键值
local function onClickDesignationWear(role, designationKey)
	if not role or not designationKey then
		return
	end

	local owned = getArchiveValue(role, designationKey)

	-- 处理称号佩戴逻辑
	if owned == false then
		role.show_tips("未获取")
	else
		role.show_tips("已佩戴")
		-- 保存佩戴状态
		setArchiveValue(role, "佩戴称号key", ArchivesData[designationKey].id)
		-- 更新UI显示
		updateSelDesignationUI(role, designationKey)
		updateDesignationLayer(role, designationKey)
	end
end

--- 更新称号显示状态
local function update3DUILayerState()
	for _, role in ipairs(GameAPI.get_all_valid_roles()) do
		local character = role.get_ctrl_unit()
		local rolePos = character.get_position()
		-- 角色与生物的距离小于10时,显示称号UI
		for unit_id, status in pairs(allCreatureStatus) do
			local creaturePos = GameAPI.get_unit(unit_id).get_position()
			if (rolePos - creaturePos):length() < designation3DShowLayerDistance then
				GameAPI.set_scene_ui_visible(status.designation3DLayer, role, true)
			else
				GameAPI.set_scene_ui_visible(status.designation3DLayer, role, false)
			end
		end
		-- 角色与角色的距离小于10时,显示称号UI
		for _, otherRole in ipairs(GameAPI.get_all_valid_roles()) do
			if role ~= otherRole then
				local otherRolePos = otherRole.get_ctrl_unit().get_position()
				if (rolePos - otherRolePos):length() < designation3DShowLayerDistance then
					local wearDesignationKey = allRoleStatus[otherRole.get_roleid()].wearDesignationKey
					if wearDesignationKey ~= nil then
						GameAPI.set_scene_ui_visible(
							allRoleStatus[otherRole.get_roleid()].designation3DLayer,
							role,
							true
						)
					end
				else
					GameAPI.set_scene_ui_visible(allRoleStatus[otherRole.get_roleid()].designation3DLayer, role, false)
				end
			end
		end
	end
end

-- 注册游戏初始化事件
LuaAPI.global_register_trigger_event({ EVENT.GAME_INIT }, function()
	-- 解锁所有称号
	getAllDesignation()
	-- 为所有的生物添加默认称号UI
	local creatures = GameAPI.get_creatures_in_aabb(math.Vector3(0, 0, 0), 1000.0, 1000.0, 1000.0)
	for _, creature in ipairs(creatures) do
		local designation3DLayer = creature.create_scene_ui_bind_unit(
			PrefabData.layout["3D称号界面"], -- 创建3D显示层
			Enums.ModelSocket.socket_head,
			math.Vector3(0, 3, 0),
			-1.0,
			true,
			true
		)
		allCreatureStatus[creature.unit_id] = { designation3DLayer = designation3DLayer }
		local designationInfo = DesignationData["潮流大师"]
		-- 获取UI节点
		local iconNode = GameAPI.get_eui_node_at_scene_ui(designation3DLayer, UINodes["3D称号背景"])
		local nameNode = GameAPI.get_eui_node_at_scene_ui(designation3DLayer, UINodes["3D称号文字"])
		-- 设置显示状态
		for _, role in ipairs(GameAPI.get_all_valid_roles()) do
			role.set_image_texture_by_key_with_auto_resize(iconNode, designationInfo.icon_id, false)
			role.set_label_text(nameNode, designationInfo.name)
			GameAPI.set_scene_ui_visible(designation3DLayer, role, false)
		end
	end

	for _, role in ipairs(GameAPI.get_all_valid_roles()) do
		-- 创建角色状态记录
		allRoleStatus[role.get_roleid()] = {
			selDesignationKey = DESIGNATION_KEY[1], -- 默认选中第一个称号
			wearDesignationKey = designationIdToKey(getArchiveValue(role, "佩戴称号key")), -- 获取已佩戴的称号
			designation3DLayer = role.get_ctrl_unit().create_scene_ui_bind_unit(
				PrefabData.layout["3D称号界面"], -- 创建3D显示层
				Enums.ModelSocket.socket_head,
				math.Vector3(0, 3, 0),
				-1.0,
				true,
				true
			),
		}
		-- 初始化UI显示
		initDesignationUI(role)
	end

	-- 注册称号佩戴按钮点击事件
	LuaAPI.global_register_custom_event(GameEvents.UI_CLICK_DESIGNATION_WEAR, function(_, _, data)
		local roleStatus = allRoleStatus[data.role.get_roleid()]
		local designationKey = roleStatus.selDesignationKey
		roleStatus.wearDesignationKey = designationKey
		onClickDesignationWear(data.role, designationKey)
	end)

	-- 注册称号列表项点击事件
	LuaAPI.global_register_custom_event(GameEvents.UI_CLICK_DESIGNATION_LIST_1, function(_, _, data)
		local designationKey = DESIGNATION_KEY[1]
		allRoleStatus[data.role.get_roleid()].selDesignationKey = designationKey
		updateSelDesignationUI(data.role, designationKey)
	end)

	LuaAPI.global_register_custom_event(GameEvents.UI_CLICK_DESIGNATION_LIST_2, function(_, _, data)
		local designationKey = DESIGNATION_KEY[2]
		allRoleStatus[data.role.get_roleid()].selDesignationKey = designationKey
		updateSelDesignationUI(data.role, designationKey)
	end)

	LuaAPI.global_register_custom_event(GameEvents.UI_CLICK_DESIGNATION_LIST_3, function(_, _, data)
		local designationKey = DESIGNATION_KEY[3]
		allRoleStatus[data.role.get_roleid()].selDesignationKey = designationKey
		updateSelDesignationUI(data.role, designationKey)
	end)

	local function onPreTick()
		update3DUILayerState()
	end
	LuaAPI.set_tick_handler(onPreTick, nil)
end)

注:代码位置:main.lua