From 2e4f97ac4c07bb51ff9960ad133f841d87c0691a Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 21 Oct 2026 00:25:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(ignore):=20=E6=A0=85=E6=A0=8F=E8=AF=8A?= =?UTF-8?q?=E6=96=AD=E8=BE=93=E5=87=BA=E9=87=8D=E5=8F=A0=E7=89=A9=E4=BD=93?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=EF=BC=88=E5=BD=A2=E7=8A=B6=20+=20=E9=82=BB?= =?UTF-8?q?=E8=BF=91=E5=9C=BA=E6=99=AF=E8=8A=82=E7=82=B9=E5=90=8D=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 保持原有几何判定(仅陡峭静态三角形);DebugFence(true) 后,本车与任意非坦克形状配对时按签名去重打印 cat/type/法线/位置/7m 内场景节点名,用于确认栅栏对应哪个物体;schema 补 Body.State/PositionVec。 --- .../play/static/js/unpacked/hornet_bundle.js | 96 ++++++++++++++++--- 3dtank_bundle.js | 96 ++++++++++++++++--- tools/hornet_noclip.js | 94 +++++++++++++++--- tools/hornet_schema.js | 2 + 4 files changed, 255 insertions(+), 33 deletions(-) diff --git a/3dtank.com/play/static/js/unpacked/hornet_bundle.js b/3dtank.com/play/static/js/unpacked/hornet_bundle.js index 1a37c86..a90fcf4 100644 --- a/3dtank.com/play/static/js/unpacked/hornet_bundle.js +++ b/3dtank.com/play/static/js/unpacked/hornet_bundle.js @@ -304,6 +304,8 @@ 'Shape.Vertex1': 'v9x_1', // 三角形顶点 1 'Shape.Vertex2': 'w9x_1', // 三角形顶点 2 'Body.Owner': 'x9i_1', // 刚体 → 拥有者(坦克物理组件 ejt) + 'Body.State': 'f9j_1', // 刚体当前物理态 + 'Body.PositionVec': 's9j_1', // 刚体位置向量(f9j_1.s9j_1) // ---- 碰撞世界(module_55088 的 ki)---- 'CollisionWorld.ShouldCollide': 'q9q()', // 唯一配对过滤:mask + 形状类型 → 生成接触 @@ -6670,16 +6672,82 @@ return !IsTankShape(Shape); } - // 「栅栏类」= 静态几何里除地面/缓坡以外的部分: - // · 非三角形静态体(盒/柱等碰撞代理)→ 视为障碍 - // · 三角形且法线接近水平(墙/栅栏/悬崖立面)→ 视为障碍 + // 「栅栏类」= 静态几何中法线接近水平(墙/栅栏/悬崖立面)的三角形 + // 保持原方案:只按几何判定,不做任何物体白名单 function IsFenceLike(Shape) { if (!IsStaticShape(Shape)) return false; - if (!IsTriangle(Shape)) return true; return IsSteepTriangle(Shape); } - // ===== 诊断(控制台 __hornetFenceDebug__ = true 开启)===== + // ===== 诊断:识别「正在和什么物体重叠」===== + // 碰撞形状本身没有名字(地图三角形是合并的三角汤),所以用两级信息: + // ① 形状自身:category / mask / type / 法线 + // ② 形状位置附近(默认 7m)场景树里的节点名(如 fence_s / wall_new_3w) + var FSceneRoot = Fld('Scene.RootNode', 'fyx_1'); + var FNodeName = Fld('Node.Name', 'g35_1'); + var FNodeMatrix = Fld('Node.Matrix', 'p35_1'); + var FNodeChildren = Fld('Node.Children', 'r35_1'); + var FBodyState = Fld('Body.State', 'f9j_1'); + var FBodyPosVec = Fld('Body.PositionVec', 's9j_1'); + + function ShapePoint(Shape) { + if (!Shape) return null; + if (IsTriangle(Shape)) { + var A = Shape[FV0], B = Shape[FV1], C = Shape[FV2]; + if (A && B && C) { + return { + x: (A.f20_1 + B.f20_1 + C.f20_1) / 3, + y: (A.g20_1 + B.g20_1 + C.g20_1) / 3, + z: (A.h20_1 + B.h20_1 + C.h20_1) / 3 + }; + } + return null; + } + // 非三角形:取所在刚体的世界位置 + var Body = Shape[FShapeBody]; + var State = Body && Body[FBodyState]; + var P = State && State[FBodyPosVec]; + if (P && typeof P.f20_1 === 'number') return { x: P.f20_1, y: P.g20_1, z: P.h20_1 }; + return null; + } + + function FindNearNodeNames(Point, Radius) { + if (!Point) return []; + var Gm = window.__gameMode__; + if (!Gm) return []; + var Scene = null; + try { Scene = Gm.lz4()[Fld('Camera.SceneRef', 'jz1_1')]; } catch (e) { return []; } + var Root = Scene && Scene[FSceneRoot]; + if (!Root) return []; + var Names = []; + var Seen = typeof Set !== 'undefined' ? new Set() : null; + var Visited = 0; + var R = Radius || 700; + var R2 = R * R; + (function Walk(Node, Depth) { + if (!Node || Depth > 12 || Visited > 4000) return; + if (Seen) { if (Seen.has(Node)) return; Seen.add(Node); } + Visited++; + var M = Node[FNodeMatrix]; + if (M && typeof M.length === 'number' && M.length >= 16 && typeof M[12] === 'number') { + var Dx = M[12] - Point.x, Dy = M[13] - Point.y, Dz = M[14] - Point.z; + if (Dx * Dx + Dy * Dy + Dz * Dz <= R2) { + var Nm = Node[FNodeName]; + if (Nm && Names.indexOf(Nm) < 0 && Names.length < 8) Names.push(Nm); + } + } + var Children = Node[FNodeChildren]; + if (Children && typeof Children.e1 === 'function') { + var N = Children.e1(); + for (var i = 0; i < N; i++) { + try { Walk(Children.g1(i), Depth + 1); } catch (e) {} + } + } + })(Root, 0); + return Names; + } + + // 每个「未跳过」的静态形状只打印一次(按签名去重) var FenceSampleKeys = {}; function RecordFenceSample(Shape) { if (window.__hornetFenceDebug__ !== true) return; @@ -6690,8 +6758,14 @@ var Key = Cat + '/' + Type + '/' + (Nz === null ? '-' : Nz.toFixed(2)); if (FenceSampleKeys[Key]) return; FenceSampleKeys[Key] = 1; - console.log('[忽略][栅栏样本] cat=' + Cat + ' type=' + Type + ' 三角=' + Tri + - ' nz=' + (Nz === null ? '-' : Nz.toFixed(2)) + ' 跳过=' + IsFenceLike(Shape)); + + var Pt = ShapePoint(Shape); + var Near = FindNearNodeNames(Pt, 700); + console.log('[忽略][重叠物体] cat=' + Cat + ' type=' + Type + ' 三角=' + Tri + + ' nz=' + (Nz === null ? '-' : Nz.toFixed(2)) + + ' 位置=' + (Pt ? ('(' + Pt.x.toFixed(0) + ',' + Pt.y.toFixed(0) + ',' + Pt.z.toFixed(0) + ')') : '-') + + ' 近邻节点=[' + Near.join(', ') + ']' + + ' 当前判定=' + (IsFenceLike(Shape) ? '忽略' : '保留')); } // ===== 判定:这一对形状要不要跳过 ===== @@ -6728,9 +6802,9 @@ Stat.skippedFence++; return true; } - // 诊断:本车与静态形状配对但没跳过 → 记录样本,便于定位分类/法线 - if (TankA && OwnerOf(ShapeA) === Local && IsStaticShape(ShapeB)) RecordFenceSample(ShapeB); - if (TankB && OwnerOf(ShapeB) === Local && IsStaticShape(ShapeA)) RecordFenceSample(ShapeA); + // 诊断:本车与任意非坦克形状配对但没跳过 → 记录样本(含被挡住的栅栏) + if (TankA && OwnerOf(ShapeA) === Local && !TankB) RecordFenceSample(ShapeB); + if (TankB && OwnerOf(ShapeB) === Local && !TankA) RecordFenceSample(ShapeA); } return false; @@ -6850,7 +6924,7 @@ DebugFence: function (On) { window.__hornetFenceDebug__ = On !== false; FenceSampleKeys = {}; - console.log('[忽略] 栅栏诊断 = ' + (window.__hornetFenceDebug__ ? '开(撞墙后看 [忽略][栅栏样本])' : '关')); + console.log('[忽略] 栅栏诊断 = ' + (window.__hornetFenceDebug__ ? '开(撞栅栏后看 [忽略][重叠物体])' : '关')); }, GetStat: function () { return { diff --git a/3dtank_bundle.js b/3dtank_bundle.js index 1a37c86..a90fcf4 100644 --- a/3dtank_bundle.js +++ b/3dtank_bundle.js @@ -304,6 +304,8 @@ 'Shape.Vertex1': 'v9x_1', // 三角形顶点 1 'Shape.Vertex2': 'w9x_1', // 三角形顶点 2 'Body.Owner': 'x9i_1', // 刚体 → 拥有者(坦克物理组件 ejt) + 'Body.State': 'f9j_1', // 刚体当前物理态 + 'Body.PositionVec': 's9j_1', // 刚体位置向量(f9j_1.s9j_1) // ---- 碰撞世界(module_55088 的 ki)---- 'CollisionWorld.ShouldCollide': 'q9q()', // 唯一配对过滤:mask + 形状类型 → 生成接触 @@ -6670,16 +6672,82 @@ return !IsTankShape(Shape); } - // 「栅栏类」= 静态几何里除地面/缓坡以外的部分: - // · 非三角形静态体(盒/柱等碰撞代理)→ 视为障碍 - // · 三角形且法线接近水平(墙/栅栏/悬崖立面)→ 视为障碍 + // 「栅栏类」= 静态几何中法线接近水平(墙/栅栏/悬崖立面)的三角形 + // 保持原方案:只按几何判定,不做任何物体白名单 function IsFenceLike(Shape) { if (!IsStaticShape(Shape)) return false; - if (!IsTriangle(Shape)) return true; return IsSteepTriangle(Shape); } - // ===== 诊断(控制台 __hornetFenceDebug__ = true 开启)===== + // ===== 诊断:识别「正在和什么物体重叠」===== + // 碰撞形状本身没有名字(地图三角形是合并的三角汤),所以用两级信息: + // ① 形状自身:category / mask / type / 法线 + // ② 形状位置附近(默认 7m)场景树里的节点名(如 fence_s / wall_new_3w) + var FSceneRoot = Fld('Scene.RootNode', 'fyx_1'); + var FNodeName = Fld('Node.Name', 'g35_1'); + var FNodeMatrix = Fld('Node.Matrix', 'p35_1'); + var FNodeChildren = Fld('Node.Children', 'r35_1'); + var FBodyState = Fld('Body.State', 'f9j_1'); + var FBodyPosVec = Fld('Body.PositionVec', 's9j_1'); + + function ShapePoint(Shape) { + if (!Shape) return null; + if (IsTriangle(Shape)) { + var A = Shape[FV0], B = Shape[FV1], C = Shape[FV2]; + if (A && B && C) { + return { + x: (A.f20_1 + B.f20_1 + C.f20_1) / 3, + y: (A.g20_1 + B.g20_1 + C.g20_1) / 3, + z: (A.h20_1 + B.h20_1 + C.h20_1) / 3 + }; + } + return null; + } + // 非三角形:取所在刚体的世界位置 + var Body = Shape[FShapeBody]; + var State = Body && Body[FBodyState]; + var P = State && State[FBodyPosVec]; + if (P && typeof P.f20_1 === 'number') return { x: P.f20_1, y: P.g20_1, z: P.h20_1 }; + return null; + } + + function FindNearNodeNames(Point, Radius) { + if (!Point) return []; + var Gm = window.__gameMode__; + if (!Gm) return []; + var Scene = null; + try { Scene = Gm.lz4()[Fld('Camera.SceneRef', 'jz1_1')]; } catch (e) { return []; } + var Root = Scene && Scene[FSceneRoot]; + if (!Root) return []; + var Names = []; + var Seen = typeof Set !== 'undefined' ? new Set() : null; + var Visited = 0; + var R = Radius || 700; + var R2 = R * R; + (function Walk(Node, Depth) { + if (!Node || Depth > 12 || Visited > 4000) return; + if (Seen) { if (Seen.has(Node)) return; Seen.add(Node); } + Visited++; + var M = Node[FNodeMatrix]; + if (M && typeof M.length === 'number' && M.length >= 16 && typeof M[12] === 'number') { + var Dx = M[12] - Point.x, Dy = M[13] - Point.y, Dz = M[14] - Point.z; + if (Dx * Dx + Dy * Dy + Dz * Dz <= R2) { + var Nm = Node[FNodeName]; + if (Nm && Names.indexOf(Nm) < 0 && Names.length < 8) Names.push(Nm); + } + } + var Children = Node[FNodeChildren]; + if (Children && typeof Children.e1 === 'function') { + var N = Children.e1(); + for (var i = 0; i < N; i++) { + try { Walk(Children.g1(i), Depth + 1); } catch (e) {} + } + } + })(Root, 0); + return Names; + } + + // 每个「未跳过」的静态形状只打印一次(按签名去重) var FenceSampleKeys = {}; function RecordFenceSample(Shape) { if (window.__hornetFenceDebug__ !== true) return; @@ -6690,8 +6758,14 @@ var Key = Cat + '/' + Type + '/' + (Nz === null ? '-' : Nz.toFixed(2)); if (FenceSampleKeys[Key]) return; FenceSampleKeys[Key] = 1; - console.log('[忽略][栅栏样本] cat=' + Cat + ' type=' + Type + ' 三角=' + Tri + - ' nz=' + (Nz === null ? '-' : Nz.toFixed(2)) + ' 跳过=' + IsFenceLike(Shape)); + + var Pt = ShapePoint(Shape); + var Near = FindNearNodeNames(Pt, 700); + console.log('[忽略][重叠物体] cat=' + Cat + ' type=' + Type + ' 三角=' + Tri + + ' nz=' + (Nz === null ? '-' : Nz.toFixed(2)) + + ' 位置=' + (Pt ? ('(' + Pt.x.toFixed(0) + ',' + Pt.y.toFixed(0) + ',' + Pt.z.toFixed(0) + ')') : '-') + + ' 近邻节点=[' + Near.join(', ') + ']' + + ' 当前判定=' + (IsFenceLike(Shape) ? '忽略' : '保留')); } // ===== 判定:这一对形状要不要跳过 ===== @@ -6728,9 +6802,9 @@ Stat.skippedFence++; return true; } - // 诊断:本车与静态形状配对但没跳过 → 记录样本,便于定位分类/法线 - if (TankA && OwnerOf(ShapeA) === Local && IsStaticShape(ShapeB)) RecordFenceSample(ShapeB); - if (TankB && OwnerOf(ShapeB) === Local && IsStaticShape(ShapeA)) RecordFenceSample(ShapeA); + // 诊断:本车与任意非坦克形状配对但没跳过 → 记录样本(含被挡住的栅栏) + if (TankA && OwnerOf(ShapeA) === Local && !TankB) RecordFenceSample(ShapeB); + if (TankB && OwnerOf(ShapeB) === Local && !TankA) RecordFenceSample(ShapeA); } return false; @@ -6850,7 +6924,7 @@ DebugFence: function (On) { window.__hornetFenceDebug__ = On !== false; FenceSampleKeys = {}; - console.log('[忽略] 栅栏诊断 = ' + (window.__hornetFenceDebug__ ? '开(撞墙后看 [忽略][栅栏样本])' : '关')); + console.log('[忽略] 栅栏诊断 = ' + (window.__hornetFenceDebug__ ? '开(撞栅栏后看 [忽略][重叠物体])' : '关')); }, GetStat: function () { return { diff --git a/tools/hornet_noclip.js b/tools/hornet_noclip.js index 8405047..38adb39 100644 --- a/tools/hornet_noclip.js +++ b/tools/hornet_noclip.js @@ -148,16 +148,82 @@ return !IsTankShape(Shape); } - // 「栅栏类」= 静态几何里除地面/缓坡以外的部分: - // · 非三角形静态体(盒/柱等碰撞代理)→ 视为障碍 - // · 三角形且法线接近水平(墙/栅栏/悬崖立面)→ 视为障碍 + // 「栅栏类」= 静态几何中法线接近水平(墙/栅栏/悬崖立面)的三角形 + // 保持原方案:只按几何判定,不做任何物体白名单 function IsFenceLike(Shape) { if (!IsStaticShape(Shape)) return false; - if (!IsTriangle(Shape)) return true; return IsSteepTriangle(Shape); } - // ===== 诊断(控制台 __hornetFenceDebug__ = true 开启)===== + // ===== 诊断:识别「正在和什么物体重叠」===== + // 碰撞形状本身没有名字(地图三角形是合并的三角汤),所以用两级信息: + // ① 形状自身:category / mask / type / 法线 + // ② 形状位置附近(默认 7m)场景树里的节点名(如 fence_s / wall_new_3w) + var FSceneRoot = Fld('Scene.RootNode', 'fyx_1'); + var FNodeName = Fld('Node.Name', 'g35_1'); + var FNodeMatrix = Fld('Node.Matrix', 'p35_1'); + var FNodeChildren = Fld('Node.Children', 'r35_1'); + var FBodyState = Fld('Body.State', 'f9j_1'); + var FBodyPosVec = Fld('Body.PositionVec', 's9j_1'); + + function ShapePoint(Shape) { + if (!Shape) return null; + if (IsTriangle(Shape)) { + var A = Shape[FV0], B = Shape[FV1], C = Shape[FV2]; + if (A && B && C) { + return { + x: (A.f20_1 + B.f20_1 + C.f20_1) / 3, + y: (A.g20_1 + B.g20_1 + C.g20_1) / 3, + z: (A.h20_1 + B.h20_1 + C.h20_1) / 3 + }; + } + return null; + } + // 非三角形:取所在刚体的世界位置 + var Body = Shape[FShapeBody]; + var State = Body && Body[FBodyState]; + var P = State && State[FBodyPosVec]; + if (P && typeof P.f20_1 === 'number') return { x: P.f20_1, y: P.g20_1, z: P.h20_1 }; + return null; + } + + function FindNearNodeNames(Point, Radius) { + if (!Point) return []; + var Gm = window.__gameMode__; + if (!Gm) return []; + var Scene = null; + try { Scene = Gm.lz4()[Fld('Camera.SceneRef', 'jz1_1')]; } catch (e) { return []; } + var Root = Scene && Scene[FSceneRoot]; + if (!Root) return []; + var Names = []; + var Seen = typeof Set !== 'undefined' ? new Set() : null; + var Visited = 0; + var R = Radius || 700; + var R2 = R * R; + (function Walk(Node, Depth) { + if (!Node || Depth > 12 || Visited > 4000) return; + if (Seen) { if (Seen.has(Node)) return; Seen.add(Node); } + Visited++; + var M = Node[FNodeMatrix]; + if (M && typeof M.length === 'number' && M.length >= 16 && typeof M[12] === 'number') { + var Dx = M[12] - Point.x, Dy = M[13] - Point.y, Dz = M[14] - Point.z; + if (Dx * Dx + Dy * Dy + Dz * Dz <= R2) { + var Nm = Node[FNodeName]; + if (Nm && Names.indexOf(Nm) < 0 && Names.length < 8) Names.push(Nm); + } + } + var Children = Node[FNodeChildren]; + if (Children && typeof Children.e1 === 'function') { + var N = Children.e1(); + for (var i = 0; i < N; i++) { + try { Walk(Children.g1(i), Depth + 1); } catch (e) {} + } + } + })(Root, 0); + return Names; + } + + // 每个「未跳过」的静态形状只打印一次(按签名去重) var FenceSampleKeys = {}; function RecordFenceSample(Shape) { if (window.__hornetFenceDebug__ !== true) return; @@ -168,8 +234,14 @@ var Key = Cat + '/' + Type + '/' + (Nz === null ? '-' : Nz.toFixed(2)); if (FenceSampleKeys[Key]) return; FenceSampleKeys[Key] = 1; - console.log('[忽略][栅栏样本] cat=' + Cat + ' type=' + Type + ' 三角=' + Tri + - ' nz=' + (Nz === null ? '-' : Nz.toFixed(2)) + ' 跳过=' + IsFenceLike(Shape)); + + var Pt = ShapePoint(Shape); + var Near = FindNearNodeNames(Pt, 700); + console.log('[忽略][重叠物体] cat=' + Cat + ' type=' + Type + ' 三角=' + Tri + + ' nz=' + (Nz === null ? '-' : Nz.toFixed(2)) + + ' 位置=' + (Pt ? ('(' + Pt.x.toFixed(0) + ',' + Pt.y.toFixed(0) + ',' + Pt.z.toFixed(0) + ')') : '-') + + ' 近邻节点=[' + Near.join(', ') + ']' + + ' 当前判定=' + (IsFenceLike(Shape) ? '忽略' : '保留')); } // ===== 判定:这一对形状要不要跳过 ===== @@ -206,9 +278,9 @@ Stat.skippedFence++; return true; } - // 诊断:本车与静态形状配对但没跳过 → 记录样本,便于定位分类/法线 - if (TankA && OwnerOf(ShapeA) === Local && IsStaticShape(ShapeB)) RecordFenceSample(ShapeB); - if (TankB && OwnerOf(ShapeB) === Local && IsStaticShape(ShapeA)) RecordFenceSample(ShapeA); + // 诊断:本车与任意非坦克形状配对但没跳过 → 记录样本(含被挡住的栅栏) + if (TankA && OwnerOf(ShapeA) === Local && !TankB) RecordFenceSample(ShapeB); + if (TankB && OwnerOf(ShapeB) === Local && !TankA) RecordFenceSample(ShapeA); } return false; @@ -328,7 +400,7 @@ DebugFence: function (On) { window.__hornetFenceDebug__ = On !== false; FenceSampleKeys = {}; - console.log('[忽略] 栅栏诊断 = ' + (window.__hornetFenceDebug__ ? '开(撞墙后看 [忽略][栅栏样本])' : '关')); + console.log('[忽略] 栅栏诊断 = ' + (window.__hornetFenceDebug__ ? '开(撞栅栏后看 [忽略][重叠物体])' : '关')); }, GetStat: function () { return { diff --git a/tools/hornet_schema.js b/tools/hornet_schema.js index 8e45b44..2c44f3d 100644 --- a/tools/hornet_schema.js +++ b/tools/hornet_schema.js @@ -80,6 +80,8 @@ 'Shape.Vertex1': 'v9x_1', // 三角形顶点 1 'Shape.Vertex2': 'w9x_1', // 三角形顶点 2 'Body.Owner': 'x9i_1', // 刚体 → 拥有者(坦克物理组件 ejt) + 'Body.State': 'f9j_1', // 刚体当前物理态 + 'Body.PositionVec': 's9j_1', // 刚体位置向量(f9j_1.s9j_1) // ---- 碰撞世界(module_55088 的 ki)---- 'CollisionWorld.ShouldCollide': 'q9q()', // 唯一配对过滤:mask + 形状类型 → 生成接触