本地新增活动时,配置表数据是如何被读取的 核心结论 配置表数据和服务端数据不是两份独立的东西,而是同一条数据链路 :
1 2 3 4 5 6 7 你在 Excel 里填配置表 ↓ 导表 生成 txt/lua 文件(本地) ↓ 服务端也读取同一份配置表(或服务端自己有对应配置) 服务端组装数据 → 通过 Ntf 推送给客户端 ↓ 客户端 Model 接收 → 存储 → View 读取
关键点 :你在本地配置表填的数据(activityName、activityUIDetail、tagId 等),服务端也会读取,然后服务端把这些配置表字段和玩家的动态数据打包在一起 ,通过协议推送给客户端。
完整链路:从你填配置表到 View 显示 第 1 步:你填配置表 1 2 3 4 5 6 7 8 H_活动中心配置.xlsx 新增一行: id: 30999 activityName: 春日兑换 activityUIDetail: UI_ActivityTemplate_ExchangeLinearView tagId: 1 activityShopType: 142 backgroundUrl: spring_bg.astc ...
第 2 步:导表 导表后生成两个文件:
1 2 server/data/ActivityMainConfigForLetsGo_common.txt ← 服务端读 Export/pbin/lua/ActivityMainConfigForLetsGo_common.lua ← 客户端本地读
第 3 步:服务端读取配置 + 组装动态数据 + 推送 服务端做了什么:
1 2 3 1. 服务端读取配置表 → 拿到 activityName, activityUIDetail, tagId, timeInfo... 等静态字段 2. 服务端查询玩家进度 → 拿到 任务完成状态, 积分, 奖励领取进度... 等动态数据 3. 合并成一条数据 → 通过 ActivityListAll_C2S_Msg 协议推给客户端
推送的数据长这样(一条活动数据):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 { id = 30999 , activityName = "春日兑换" , activityUIDetail = "UI_ActivityTemplate_ExchangeLinearView" , activityType = "ATLinearRedeem" , activityNameType = "ANTLinearRedeem" , tagId = 1 , activityShopType = {142 }, backgroundUrl = {"spring_bg.astc" , "spring_title.astc" }, timeInfo = { beginTime={...}, endTime={...} }, clientParams = {"每日任务" , "兑换商店" }, activityTaskGroup = {570011 , 570021 }, }
第 4 步:客户端 Model 接收并存储 源码对应 ActivityModel_TabData.lua 第 160-180 行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 local function HandleActivityData (activityModel, callback) ActivityModel_TabData.gameServerActivityList = ActivityModel_TabData.gameServerActivityInfo.activityLabel for _, singleActivity in pairs (gameServerActivityList) do ValidateActivityListData(singleActivity) end InitAllActivityData(activityModel) end
源码对应 ActivityModel_TabData.lua 第 367-410 行 — 存储到映射表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function ActivityModel_TabData.InitActivityMap (activityModel) ActivityModel_TabData.m_ActivityInfoMap = {} ActivityModel_TabData.m_ActivityInfoMapById = {} for i, v in ipairs (gameServerActivityList) do m_ActivityInfoMapById[v.id] = v for index, tagId in ipairs (v.tagIds or {v.tagId}) do local activityInfo = DeepCopyTable(v) table .insert (m_ActivityInfoMap[tagId], activityInfo) end end end
第 5 步:View 通过 Model 读取 源码对应 UI_Activity_BaseView.lua 第 22-36 行:
1 2 3 4 5 6 7 function UI_Activity_BaseView:InitData () self .ActivityInfo = _MOE.Models.ActivityModel:GetActivityInfo(self .Data.tagId, self .Data.id) end
源码对应 ActivityModel_DataQuery.lua 第 34-50 行 — 查询函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 function ActivityModel_DataQuery.GetActivityInfo (activityModel, tagId, activityId) local activityInfoMap = activityModel:GetActivityInfoMapByTagId() local activityList = activityInfoMap[tagId] if activityList then for i, v in ipairs (activityList) do if v.id == activityId then return v end end end end
第 6 步:View 根据 activityUIDetail 加载 UMG 源码对应 UI_Events_Main.lua 第 810-819 行 + 第 1081 行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 local activityUIDetail = v.activityUIDetailtabInfo.UIName = activityUIDetail self .SubView = self :AddWidget(tabInfo.UIName, self .bp.w_nameSlot_Activity, info)
配置表字段 → UMG 加载的完整对应关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 你在 Excel 配置表填: activityUIDetail = "UI_ActivityTemplate_ExchangeLinearView" │ ↓ 导表 → 服务端读取 → 推送给客户端 │ 客户端 Model 收到: v.activityUIDetail = "UI_ActivityTemplate_ExchangeLinearView" │ ↓ View 层取出 │ UI_Events_Main.lua: tabInfo.UIName = "UI_ActivityTemplate_ExchangeLinearView" │ ↓ AddWidget(UIName, ...) │ BaseViewClass.lua 第 1482-1496 行: 1. 先查 UIWndNameToLuaPath[UIName] → 找不到 2. 加载 UE4 蓝图资产 (用 UIName 作为资产名) 3. 读取蓝图的 View 属性 → 得到 Lua 路径 View = "Feature.System.Script.System.Activity.Public.Template.View.UI_ActivityTemplate_ExchangeLinearView" 4. requireLuaView(View) → 加载 Lua 文件 5. 创建实例 → Open(Data) │ ↓ UI_ActivityTemplate_ExchangeLinearView:OnOpen(Data) → 渲染界面
如果服务端没推送怎么办(本地 fallback) ActivityModel_DataQuery.lua 第 72-81 行有一个兜底函数:
1 2 3 4 5 6 7 8 9 10 function ActivityModel_DataQuery.GetActInfoOrCfgByActId (activityModel, activityId) local actInfo = GetInfoByActivityId(activityModel, activityId) if not actInfo then actInfo = _MOE.Config.ActivityMainConfig:GetDataByKey(activityId) end return actInfo end
⚠️ 但注意:基类 InitData() 用的是 GetActivityInfo()(走服务端数据),不是 GetActInfoOrCfgByActId()(带 fallback)。所以如果服务端没推送这个活动,ActivityInfo 就是 nil。
完整图:你的配置表数据怎么到 View 的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 ┌──────────────────────────────────────────────────────────────────┐ │ 配置表数据 → 服务端 → Model → View 的完整链路 │ ├──────────────────────────────────────────────────────────────────┤ │ │ │ 你在 Excel 填: │ │ id=30999, activityUIDetail="UI_ActivityTemplate_ExchangeLinearView",│ │ activityName="春日兑换", tagId=1, activityShopType=142, ... │ │ │ │ │ ↓ 导表 │ │ 生成 txt + lua 文件 │ │ │ │ │ ↓ 服务端也读这份配置表 (或服务端有自己的配置系统同步) │ │ 服务端组装: │ │ { id=30999, │ │ activityName="春日兑换", ← 来自配置表 │ │ activityUIDetail="UI_ActivityTemplate_...", ← 来自配置表 │ │ tagId=1, ← 来自配置表 │ │ activityShopType={142}, ← 来自配置表 │ │ backgroundUrl={"spring_bg.astc"},← 来自配置表 │ │ timeInfo={...}, ← 来自配置表 │ │ taskProgress={570011=已完成}, ← 服务端运行时计算 │ │ score=30, ← 服务端运行时计算 │ │ rewardStatus={reward1=已领}, ← 服务端运行时计算 │ │ } │ │ │ │ │ ↓ 通过 ActivityListAll_C2S_Msg 协议推送 │ │ 客户端 Model 接收: │ │ m_ActivityInfoMapById[30999] = { 整条数据 } │ │ │ │ │ ↓ View: GetActivityInfo(tagId, activityId) │ │ View 拿到 ActivityInfo: │ │ self.ActivityInfo = { 整条数据 } │ │ ├─ activityName → 写入 w_txt_title (标题文本) │ │ ├─ backgroundUrl → 加载到 w_img_Activity_BG (背景图) │ │ ├─ activityUIDetail → 已用于加载 UMG (在 AddWidget 时) │ │ ├─ activityShopType → 用于查询商城商品 (RefreshExchangeInfo) │ │ ├─ activityTaskGroup → 用于查询任务列表 (TaskView) │ │ ├─ taskProgress → 判断任务是否完成, 显示打勾 │ │ ├─ score → 显示积分数字 │ │ └─ rewardStatus → 判断领奖按钮是否可点 │ │ │ └──────────────────────────────────────────────────────────────────┘
一句话总结
你在配置表填的每个字段(activityUIDetail、activityName、tagId…),服务端都会读取并和玩家动态数据一起打包推送。客户端 Model 收到后统一存储,View 通过 GetActivityInfo 读取。其中 activityUIDetail 字段决定了加载哪个 UMG 蓝图和 Lua 脚本,其他字段用于界面渲染和业务逻辑。
本文从 LearnByCompany 原始文档 自动同步。