下载 App

围墙空气墙

07/281 浏览综合

问题根源

原系统用单点碰撞Round(nx), Round(ny) 只检测中心1个点),角色精灵 36px(7.2格) 宽,中心停在 x=0.5 时精灵左半完全覆盖 5px 围墙,视觉"穿墙"。
horizontal linehorizontal line

1. AABB 矩形碰撞检测 — scripts/小人/导航.lua

lua
-- 角色碰撞体尺寸(格):脚底占据的地面矩形 -- 半宽 0.5 格(2.5px),半高 0.3 格(1.5px) local ACTOR_HALF_W = 0.5 local ACTOR_HALF_H = 0.3 -- 边界:格 gx 占据连续空间 [gx, gx+1),上界用 < 不用 <= local function InBounds(x, y) return x >= 0 and y >= 0 and x < (MapData.data.width or 240) and y < (MapData.data.height or 160) end -- AABB 矩形碰撞:检测以(cx,cy)为中心、半宽halfW、半高halfH的矩形 -- 是否与任何墙格重叠或越界 local function IsRectBlocked(cx, cy, halfW, halfH, blocked) local minGX = math.floor(cx - halfW) local maxGX = math.floor(cx + halfW - 1e-6) local minGY = math.floor(cy - halfH) local maxGY = math.floor(cy + halfH - 1e-6) for gx = minGX, maxGX do for gy = minGY, maxGY do if not InBounds(gx, gy) or blocked[CellKey(gx, gy)] then return true end end end return false end -- 逐帧碰撞:用 AABB 替代单点 Round 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 IsRectBlocked(nx, ny, ACTOR_HALF_W, ACTOR_HALF_H, blocked) then return false, x, y -- 被阻挡,返回原位置 end return true, nx, ny end
horizontal linehorizontal line

2. 围墙空气墙(不可见碰撞缓冲)— scripts/数据/地图/校园主地图.lua

lua
local function GetFreeWallSegments(wall) local style = wall.style or "普通墙" -- 围墙/栅栏:碰撞占2格宽(视觉1格 + 内侧1格空气墙) -- 普通墙/破损墙:1格 local thick = (style == "围墙" or style == "栅栏") and 2 or 1 -- 竖段:w=thick 向校园内侧延伸 for dy = 0, (y1 - y0) do result[#result + 1] = { x = x0, y = y0 + dy, w = thick, h = 1, -- 围墙w=2,普通墙w=1 style = style, -- ...其他字段 } end -- 横段:h=thick 同理 end
BuildBlockedCells 遍历 segment.w/h 逐格标记 blocked[CellKey] = true,x=0(可见墙)和 x=1(空气墙)两列都阻挡通行。
horizontal linehorizontal line

3. 围墙逐格 Y-sorting 渲染 — scripts/页面/校园地图.lua

lua
-- 逐格拆分+borderRadius=0(防圆点),每格zIndex=CharZ(y) local function CreateFreeWallView(wall) local style = wall.style or "普通墙" local color = style == "围墙" and {110,88,60,255} or ... local visualPx = 5 -- 只渲染1格(5px)可见,空气墙不渲染 for gy = y0, y1 do segments[#segments + 1] = UI.Panel { position = "absolute", left = x0 * 5, top = gy * 5, width = visualPx, height = 5, -- 只画5px,不是thick*5 backgroundColor = color, borderWidth = 0, borderRadius = 0, -- 关键:防圆角变圆点 pointerEvents = "none", zIndex = CharZ(gy), -- 逐格Y排序 } end return segments end -- 调用处展开逐段数组 for i = 1, #(data.walls or {}) do local wallSegments = CreateFreeWallView(data.walls[i]) for j = 1, #wallSegments do campusChildren[#campusChildren + 1] = wallSegments[j] end end
horizontal linehorizontal line

4. 贴墙滑行(主角操控,不变)— scripts/系统/主角操控服务.lua

lua
local canMove, nx, ny = Navigation.CanMove(spatial.x, spatial.y, dx, dy, step, profile) if not canMove then -- 斜向撞墙时尝试单轴滑行 local slideX = math.abs(dx) > 0.01 and Navigation.CanMove(spatial.x, spatial.y, dx, 0, step, profile) local slideY = math.abs(dy) > 0.01 and Navigation.CanMove(spatial.x, spatial.y, 0, dy, step, profile) 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
horizontal linehorizontal line

关键参数对照表

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