下载 App

精灵平滑移动、墙碰撞体积

07/29综合

一、

渲染层用连续坐标scripts/页面/校园地图.lua,三处统一):
lua
-- NPC 定位(第408-410行) local contLeft = (character.x or 0) * 5 - 18 - charLeft local contTop = (character.y or 0) * 5 - 36 - charTop -- 主角定位(第504-506行) local contLeft = px * 5 - 18 - charLeft local contTop = py * 5 - 36 - charTop -- 动态更新(每帧SetStyle,第1148-1150行) local anchorX = (ch.x or 0) * 5 - 18 - cl local anchorY = (ch.y or 0) * 5 - 36 - ct
碰撞层用CellOf格对齐scripts/小人/导航.lua):
lua
-- 角色"所在格":四舍五入到最近格,碰撞只检测这个格 local function CellOf(value) return math.floor((value or 0) + 0.5) end local function IsCellBlocked(cx, cy, blocked) local gx = CellOf(cx) local gy = CellOf(cy) if not InBounds(gx, gy) then return true end return blocked[CellKey(gx, gy)] == true end -- CanMove 用连续坐标,但碰撞检测最近格 function Navigation.CanMove(x, y, dx, dy, step, profession) local blocked = BuildBlockedCells(profession or {}) local nx = x + dx * step local ny = y + dy * step if IsCellBlocked(nx, ny, blocked) then return false, x, y -- 阻挡返回原位置 end return true, nx, ny end
主角操控scripts/系统/主角操控服务.lua):
lua
-- 连续坐标移动,碰撞阻挡后贴墙滑行 local step = PLAYER_SPEED * math.max(0, dt or 0) local canMove, nx, ny = Navigation.CanMove(spatial.x, spatial.y, dx, dy, step, ...) if not canMove then -- 贴墙滑行:斜向被挡时尝试单轴 local slideX = Navigation.CanMove(spatial.x, spatial.y, dx, 0, step, ...) local slideY = Navigation.CanMove(spatial.x, spatial.y, 0, dy, step, ...) if slideX then nx, ny = spatial.x + dx*step, spatial.y elseif slideY then nx, ny = spatial.x, spatial.y + dy*step else return false end end ActorSpatialService.SetPosition(playerId, nx, ny, direction)
horizontal linehorizontal line

二、围墙/房间墙碰撞体积分析 — 核心代码

房间墙段生成scripts/数据/地图/校园主地图.lua,第125-203行):
lua
function M.GetRoomWallSegments(room) if room.wallsEnabled ~= true then return {} end local thickness = 1 local rx, ry = room.x or 0, room.y or 0 local rw, rh = room.w or 0, room.h or 0 -- top墙:y=ry,x从rx到rx+rw-1,每段1格宽,h=thickness=1 for dx = 0, rw-1 do result[#result+1] = { side="top", x=rx+dx, y=ry, w=1, h=thickness, ... } -- bottom墙:y=ry+rh-1 result[#result+1] = { side="bottom", x=rx+dx, y=ry+rh-thickness, w=1, h=thickness, ... } end -- left墙:x=rx,y从ry到ry+rh-1,每段w=thickness=1 for dy = 0, rh-1 do result[#result+1] = { side="left", x=rx, y=ry+dy, w=thickness, h=1, ... } -- right墙:x=rx+rw-1 result[#result+1] = { side="right", x=rx+rw-thickness, y=ry+dy, w=thickness, h=1, ... } end return result end
自由墙段(第207-264行):
lua
local function GetFreeWallSegments(wall) local thick = 1 -- 自由墙也只有1格厚 -- 竖段:x=x0,y从y0到y1,每段w=thick=1 for dy = 0, y1-y0 do result[#result+1] = { side="free_v", x=x0, y=y0+dy, w=thick, h=1, ... } end end
BuildBlockedCells 标记阻挡格scripts/小人/导航.lua):
lua
local function BuildBlockedCells(profession) local blocked = {} for i = 1, #segments do if not passable then local width = math.max(1, Round(segment.w or 1)) -- =1 local height = math.max(1, Round(segment.h or 1)) -- =1 for dx = 0, width-1 do for dy = 0, height-1 do blocked[CellKey(Round(segment.x)+dx, Round(segment.y)+dy)] = true end end end end return blocked end
horizontal linehorizontal line

三、四面墙间距不一致的根因

阻挡格角色被挡在距墙格数精灵覆盖墙?
left墙 x=rxCellOf(x)=rxx≈rx+0.5(floor(0.5+0.5)=1=rx+1可走,x=0.4时CellOf=0=rx阻挡)0.5格是,左边缘-15.5px覆盖0-5px墙
right墙 x=rx+rw-1CellOf(x)=rx+rw-1x≈rx+rw-1.50.5格是,对称覆盖
top墙 y=ryCellOf(y)=ryy≈ry+0.5(房间内)/ y≈ry-0.5(房间外)0.5格是,上边缘覆盖
bottom墙 y=ry+rh-1CellOf(y)=ry+rh-1y≈ry+rh-1.50.5格
四面墙碰撞间距其实一致(都是0.5格)。图3看起来"漂浮更远"是因为角色在房间外(top墙上方草地),被墙阻挡在y≈ry-0.5位置,精灵向上延伸36px,看起来离墙远。图1/2在房间内,精灵脚贴地面,视觉上不同。
真正的问题:精灵36px宽/高相对5px格子太大,中心停在0.5格时精灵边缘必然覆盖相邻墙格像素。要解决视觉覆盖需要空气墙(把阻挡格内移1格)或缩小精灵尺寸。
地图编辑器仿真 tick 重建抑制
UI 重建频率 = 事件分发的可靠性上限。 只要有一帧 SetRoot 销毁了正在处理 pointer 事件的控件,跨重建边界的 Down/Up 对就会被吞。高频仿真 tick 必须在 UI 边界按页面过滤。 关键代码(scripts/main.lua HandleUpdate) 四步走:先原地处理人员 → 聚合所有仿真 changed → 地图页强制压制 → 只放行显式/登录态变化。 -- ma
官方
4 赞
地图编辑器地面交互与视觉优化
一、问题链 用户截图反馈了三个递进的问题: 问题 1:点击地面类型(如食堂地砖),弹窗不关闭,挡住画布无法操作 ↓ 修复 v0.10.94 问题 2:地面贴图太大,一块地砖比小人还宽,比例失调 ↓ 修复 v0.10.96(改配置值) 问题 3:改了配置值后刷新仍然太大——旧存档的 tileSize=256 优先级高于新 config ↓ 修复 v0.10.97(反转渲染优先级) 二、关键代码
官方
4 赞
修复"地图编辑器刷新数据丢失"
问题诊断过程 用户报告:编辑器建好房间 → 保存 → 刷新页面 → 进编辑器 → 数据全没了。 关键线索:用户发现"校园日"模式刷新后能看到房间,但一进编辑器就没了。 这直接指向了编辑器页面自身的初始化逻辑,而非持久化链路。 根因 地图编辑模式.lua 的 EnsureBlankEditorLayout() 设计意图是"首次打开编辑器提供空白画布",用模块级布尔 editorLayoutClear
官方
4 赞
以鼠标为锚点缩放
核心数学原理 缩放前后,鼠标指向的那个世界坐标点必须保持在同一屏幕位置。 已知:旧缩放 oldZoom、新缩放 newZoom、鼠标锚点屏幕坐标 (ax, ay)、当前偏移 (panX, panY) 步骤1:算出旧缩放下鼠标指向的世界坐标 wx = (ax - panX) / oldZoom wy = (ay - panY) / oldZoom 步骤2:新缩放后反解新偏移,让 wx/wy
官方
1 赞
右键拖拽平移
一、问题本质 不是平移公式写错,而是 UrhoX UI 库指针事件派发的两个不对称陷阱叠加: 陷阱 现象 根因文件 OnPointerMove 不向父冒泡 右键按下 isPanning_=true,但 move 事件永远到不了 mapView urhox-libs/UI/Core/Widget.lua(OnPointerDown/Up 有 parent 冒泡,OnPointerMove 没有) H
官方
4 赞
穿透层修复
经验一:CSS transform 穿透层修复模式 问题本质 当容器使用 CSS transform: translateX/Y/scale 平移/缩放子树时,即使父容器有 overflow = "hidden",引擎的命中检测可能不裁剪越界区域。如果 transform 后的子树有更高的 zIndex,它会在视觉范围外拦截其他兄弟节点的指针事件。 受影响的场景 地图编辑器(右键平移/滚轮缩放后)
官方
4 赞
图片加载三层优化设计
目标场景:地图编辑器"物品/墙体/地面/房间/人员"调色板弹窗的图片首帧显示 文档目的:沉淀"图片为什么慢、怎么治、发布后玩家体验如何、未来加资源时 preload_groups 怎么取舍"的完整结论,避免重复踩坑。 一句话结论 WASM 下图片"慢"不是单一问题,而是下载 / 解码 / 上传 GPU 三件事全堆在"玩家点弹窗那一刻"做导致的。优化的本质是把这三件事提前到启动期做(让玩家点的时候图
官方
1 赞
槽位变更只走原子方法
经验 下标 = 视觉格子,空槽是合法状态。定长 9 槽,inventory[i]=nil 即第 i 格空着,中间允许空。任何"把物品往前挪/压缩/重排"的操作都会让"你看到的格子"和"数组真实下标"脱节 → 物品消失。渲染直映 inventory[i],数据层绝不重排。 稀疏表禁用 #。带 nil 洞的表 #inv 是未定义的(可能=0 直接读成空包,或=洞前边界)。所有遍历上界一律 Player
官方
4 赞
自动寻路赠送
核心功能 携带模式(点击物品跟鼠标)下: ≤8格:点击小人直接赠送(金色边框) >8格:点击远距离小人 → 自动寻路走向目标 → 到达8格内自动赠送 取消:玩家按方向键/WASD/摇杆手动移动 → 立即取消,恢复物品 关键经验(3条踩坑教训) 经验1:类型标识不是中文,是英文枚举值 地图编辑器放置的角色 type/job 字段是英文标识(teacher_3、doctor、cleaner_2、tea
官方
1 赞
拾取 / 丢弃物品
拾取的坑是「点击被遮挡」,丢弃的坑是「坐标反解会偏」。两者解法都是绕开复杂机制、用最直接的方式。 问题 根因 解法 走到物品旁点不动、捡不起来 物品命中层和主角标记同层、都无 zIndex,数组顺序靠后的主角标记盖在上面,点击命中的是主角而非物品 给可交互物品显式 zIndex = 20 拖拽丢东西飞到远处 用 ScreenToWorld 把鼠标像素反解成世界坐标当落点,依赖 zoom/offse
官方
4 赞