下载 App

地图编辑器仿真 tick 重建抑制

07/27综合

UI 重建频率 = 事件分发的可靠性上限。 只要有一帧 SetRoot 销毁了正在处理 pointer 事件的控件,跨重建边界的 Down/Up 对就会被吞。高频仿真 tick 必须在 UI 边界按页面过滤。
horizontal linehorizontal line

关键代码(scripts/main.lua HandleUpdate)

四步走:先原地处理人员 → 聚合所有仿真 changed → 地图页强制压制 → 只放行显式/登录态变化。
lua
-- main.lua (HandleUpdate) -- 0) 各仿真服务独立更新,各自返回 changed 标志 local dailyChanged = CampusDaily.Update(timeStep) local playerChanged = PlayerComponentService.Update(timeStep) local planChanged = PersonalPlanService.Update(timeStep) local explorationChanged= RoomExplorationService.Update(timeStep) local hygieneChanged = CampusHygieneService.Update(timeStep) local personnelChanged = CampusPersonnel.Update(timeStep) local behaviorChanged = BehaviorObservationService.Update(timeStep) local observationChanged= ObservationTaskService.Update(timeStep) -- 1) 人员移动在地图页内原地刷新,走独立通道,不重建 if personnelChanged and currentPage_ == "mapEditor" then MapEditorPage.UpdateDynamicCharacters() personnelChanged = false end -- 2) 聚合所有仿真 tick 的 changed 标志(电平型,NPC 移动/思考时会"长期 true") local simulationChanged = dailyChanged or playerChanged or planChanged or explorationChanged or hygieneChanged or personnelChanged or behaviorChanged or observationChanged -- 3) 地图编辑器页强制压制——编辑页绝不因后台仿真每帧重建 if currentPage_ == "mapEditor" then simulationChanged = false end -- 4) 只允许三类显式信号触发整树重建 if simulationChanged or context_.rebuildUIPending -- RequestUIRebuild() 显式调用 or uid ~= lastLoggedUserId_ -- 登录用户切换 or isAdmin ~= lastLoggedAdminState_ -- 管理员状态变化 then context_.rebuildUIPending = false RebuildUI() -- 内部调用 UI.SetRoot(root, true) end
horizontal linehorizontal line

原地刷新关键代码(地图编辑模式.lua

人员位置每帧变,但不重建 UI,通过保留 widget 引用用 SetStyle 原地更新:
lua
-- 已放置人员的 dot widget 在进入编辑页时就建好,缓存到 dynamicCharDots_ function M.UpdateDynamicCharacters() for id, dot in pairs(dynamicCharDots_) do local char = MapData.data.characters[id] local sx, sy = WorldToScreenLocal(char.x, char.y) dot:SetStyle({ left = sx, top = sy }) -- 原地改 transform,无重建 end end
horizontal linehorizontal line

显式重建入口(被业务代码在结构变化时调用)

lua
function RequestUIRebuild() context_.rebuildUIPending = true end -- 调用场景:新增/删除房间、切换工具、打开/关闭面板(结构性变化,非每帧)
horizontal linehorizontal line

根因(为什么"放完房间要等一会才能再点左栏")

层级现象
仿真层CampusNavigation.Update:NPC 在校日常移动时每帧返回 changed=true
UI 层每帧 UI.SetRoot(root, true) → 整棵 widget 树(含左栏 VirtualList)以 ~60 fps 销毁重建
事件层pointerDown 落在旧控件 A → 下一帧 A 被销毁 → pointerUp 到达时 A 不存在 → OnClick 不触发
用户感知点"物品"按钮无响应;等待 1–2 秒后 NPC 恰好集体到达目的地,重建频率下降,点击恢复
horizontal linehorizontal line

新增仿真服务时的检查清单

  1. ☐ 该 changed 是否会在 NPC 持续移动/思考时"每帧 true"?→并入 simulationChanged 聚合,不要单独加进重建条件
  2. ☐ changed 是否驱动视觉?位置→原地 SetStyle文案/列表结构→走 RequestUIRebuild()
  3. ☐ 地图编辑器/菜单页等静态页一律压制 simulationChanged
  4. ☐ 如需"状态切换瞬间"刷新,让服务返回脉冲型 changed(切换瞬间 true,下一帧 false),脉冲可在所有页面放行
horizontal linehorizontal line

被否决方案(避免再踩)

  • 只压制 dailyChanged,放过 explorationChanged/behaviorChanged → 治标不治本
  • CampusNavigation.Update 改成只在状态变化瞬间返回 true → 破坏仿真语义(其他页面依赖每帧 changed 刷 HUD)
  • 给 VirtualList 加 key 复用 DOM 身份 → UI 库不支持跨 SetRoot 复用控件,事件监听器仍是新闭包
horizontal linehorizontal line
修复"地图编辑器刷新数据丢失"
问题诊断过程 用户报告:编辑器建好房间 → 保存 → 刷新页面 → 进编辑器 → 数据全没了。 关键线索:用户发现"校园日"模式刷新后能看到房间,但一进编辑器就没了。 这直接指向了编辑器页面自身的初始化逻辑,而非持久化链路。 根因 地图编辑模式.lua 的 EnsureBlankEditorLayout() 设计意图是"首次打开编辑器提供空白画布",用模块级布尔 editorLayoutClear
官方
4 赞
地图编辑器地面交互与视觉优化
一、问题链 用户截图反馈了三个递进的问题: 问题 1:点击地面类型(如食堂地砖),弹窗不关闭,挡住画布无法操作 ↓ 修复 v0.10.94 问题 2:地面贴图太大,一块地砖比小人还宽,比例失调 ↓ 修复 v0.10.96(改配置值) 问题 3:改了配置值后刷新仍然太大——旧存档的 tileSize=256 优先级高于新 config ↓ 修复 v0.10.97(反转渲染优先级) 二、关键代码
官方
4 赞
右键拖拽平移
一、问题本质 不是平移公式写错,而是 UrhoX UI 库指针事件派发的两个不对称陷阱叠加: 陷阱 现象 根因文件 OnPointerMove 不向父冒泡 右键按下 isPanning_=true,但 move 事件永远到不了 mapView urhox-libs/UI/Core/Widget.lua(OnPointerDown/Up 有 parent 冒泡,OnPointerMove 没有) H
官方
4 赞
拾取 / 丢弃物品
拾取的坑是「点击被遮挡」,丢弃的坑是「坐标反解会偏」。两者解法都是绕开复杂机制、用最直接的方式。 问题 根因 解法 走到物品旁点不动、捡不起来 物品命中层和主角标记同层、都无 zIndex,数组顺序靠后的主角标记盖在上面,点击命中的是主角而非物品 给可交互物品显式 zIndex = 20 拖拽丢东西飞到远处 用 ScreenToWorld 把鼠标像素反解成世界坐标当落点,依赖 zoom/offse
官方
4 赞
地面样式配置归一化
1. 唯一真相源 scripts/数据/地图/地面样式配置.lua local FloorStyleConfig = {} -- ★ 所有地面样式集中定义在这里 FloorStyleConfig.STYLES = { ["木地板"] = { texture = "image/地面/木地板_无缝.png", tileSize = 40 }, ["草地"] = { texture = "imag
官方
4 赞
图片加载三层优化设计
目标场景:地图编辑器"物品/墙体/地面/房间/人员"调色板弹窗的图片首帧显示 文档目的:沉淀"图片为什么慢、怎么治、发布后玩家体验如何、未来加资源时 preload_groups 怎么取舍"的完整结论,避免重复踩坑。 一句话结论 WASM 下图片"慢"不是单一问题,而是下载 / 解码 / 上传 GPU 三件事全堆在"玩家点弹窗那一刻"做导致的。优化的本质是把这三件事提前到启动期做(让玩家点的时候图
官方
1 赞
自动寻路赠送
核心功能 携带模式(点击物品跟鼠标)下: ≤8格:点击小人直接赠送(金色边框) >8格:点击远距离小人 → 自动寻路走向目标 → 到达8格内自动赠送 取消:玩家按方向键/WASD/摇杆手动移动 → 立即取消,恢复物品 关键经验(3条踩坑教训) 经验1:类型标识不是中文,是英文枚举值 地图编辑器放置的角色 type/job 字段是英文标识(teacher_3、doctor、cleaner_2、tea
官方
1 赞
以鼠标为锚点缩放
核心数学原理 缩放前后,鼠标指向的那个世界坐标点必须保持在同一屏幕位置。 已知:旧缩放 oldZoom、新缩放 newZoom、鼠标锚点屏幕坐标 (ax, ay)、当前偏移 (panX, panY) 步骤1:算出旧缩放下鼠标指向的世界坐标 wx = (ax - panX) / oldZoom wy = (ay - panY) / oldZoom 步骤2:新缩放后反解新偏移,让 wx/wy
官方
1 赞
槽位变更只走原子方法
经验 下标 = 视觉格子,空槽是合法状态。定长 9 槽,inventory[i]=nil 即第 i 格空着,中间允许空。任何"把物品往前挪/压缩/重排"的操作都会让"你看到的格子"和"数组真实下标"脱节 → 物品消失。渲染直映 inventory[i],数据层绝不重排。 稀疏表禁用 #。带 nil 洞的表 #inv 是未定义的(可能=0 直接读成空包,或=洞前边界)。所有遍历上界一律 Player
官方
4 赞
编辑器实体 zIndex 层级
问题 编辑器中墙段被房间面板覆盖不可见。原因:给房间设 zIndex=1 会创建 stacking context,其内部带背景色/TiledImage 的 floorLayer 作为 paint layer 在合成时盖过了同层级 zIndex=2 的墙段——差距太小不够可靠。 解决:底层不设 zIndex,上层用明显间距 错误做法(zIndex 间距太小 + 给底层也设 zIndex): --
官方
1 赞