From 63c08ab1657e8709671f6c138ad0aba822c3d884 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 21 Oct 2026 00:18:51 +0800 Subject: [PATCH] =?UTF-8?q?fix(ignore):=20=E6=94=BE=E5=AE=BD=E6=A0=85?= =?UTF-8?q?=E6=A0=8F=E5=88=A4=E5=AE=9A=E5=B9=B6=E5=8A=A0=E5=85=A5=E9=9D=99?= =?UTF-8?q?=E6=80=81=E5=BD=A2=E7=8A=B6=E8=AF=8A=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 静态障碍不再只认三角形:非三角形静态碰撞代理直接视为障碍,三角形按法线 |nz|<0.5 判定;新增 __hornetNoClip__.DebugFence(true) 打印静态形状样本(cat/type/三角/nz/是否跳过),用于定位未生效原因。 --- .../play/static/js/unpacked/hornet_bundle.js | 62 ++++++++++++++++--- 3dtank_bundle.js | 62 ++++++++++++++++--- tools/hornet_noclip.js | 62 ++++++++++++++++--- 3 files changed, 159 insertions(+), 27 deletions(-) diff --git a/3dtank.com/play/static/js/unpacked/hornet_bundle.js b/3dtank.com/play/static/js/unpacked/hornet_bundle.js index 858d42f..1a37c86 100644 --- a/3dtank.com/play/static/js/unpacked/hornet_bundle.js +++ b/3dtank.com/play/static/js/unpacked/hornet_bundle.js @@ -6638,19 +6638,29 @@ } // 三角形是否「陡」:法线 z 分量接近 0(墙/栅栏/悬崖立面) - function IsSteepTriangle(Shape) { - if (!Shape || Shape[FShapeType] !== TRIANGLE_TYPE) return false; + function IsTriangle(Shape) { + return !!(Shape && Shape[FShapeType] === TRIANGLE_TYPE); + } + + // 返回法线 z 分量(-1..1);读不到返回 null + function TriangleNormalZ(Shape) { + if (!IsTriangle(Shape)) return null; var A = Shape[FV0], B = Shape[FV1], C = Shape[FV2]; - if (!A || !B || !C) return false; - if (typeof A.f20_1 !== 'number' || typeof B.f20_1 !== 'number' || typeof C.f20_1 !== 'number') return false; + if (!A || !B || !C) return null; + if (typeof A.f20_1 !== 'number' || typeof B.f20_1 !== 'number' || typeof C.f20_1 !== 'number') return null; var ABx = B.f20_1 - A.f20_1, ABy = B.g20_1 - A.g20_1, ABz = B.h20_1 - A.h20_1; var ACx = C.f20_1 - A.f20_1, ACy = C.g20_1 - A.g20_1, ACz = C.h20_1 - A.h20_1; var Nx = ABy * ACz - ABz * ACy; var Ny = ABz * ACx - ABx * ACz; var Nz = ABx * ACy - ABy * ACx; var Len = Math.sqrt(Nx * Nx + Ny * Ny + Nz * Nz); - if (!(Len > 1e-6)) return false; - return Math.abs(Nz / Len) < FENCE_NORMAL_Z; + if (!(Len > 1e-6)) return null; + return Nz / Len; + } + + function IsSteepTriangle(Shape) { + var Nz = TriangleNormalZ(Shape); + return Nz !== null && Math.abs(Nz) < FENCE_NORMAL_Z; } // 静态几何:category 含 bit4,且不是坦克部件 @@ -6660,6 +6670,30 @@ return !IsTankShape(Shape); } + // 「栅栏类」= 静态几何里除地面/缓坡以外的部分: + // · 非三角形静态体(盒/柱等碰撞代理)→ 视为障碍 + // · 三角形且法线接近水平(墙/栅栏/悬崖立面)→ 视为障碍 + function IsFenceLike(Shape) { + if (!IsStaticShape(Shape)) return false; + if (!IsTriangle(Shape)) return true; + return IsSteepTriangle(Shape); + } + + // ===== 诊断(控制台 __hornetFenceDebug__ = true 开启)===== + var FenceSampleKeys = {}; + function RecordFenceSample(Shape) { + if (window.__hornetFenceDebug__ !== true) return; + var Cat = Shape[FShapeCategory]; + var Type = Shape[FShapeType]; + var Tri = IsTriangle(Shape); + var Nz = TriangleNormalZ(Shape); + 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)); + } + // ===== 判定:这一对形状要不要跳过 ===== function ShouldSkip(ShapeA, ShapeB) { var Local = GetLocalPhysics(); @@ -6687,13 +6721,16 @@ } } - // ② 忽略栅栏(只跳过陡峭静态三角形,保留地面/缓坡) + // ② 忽略栅栏(静态障碍;地面/缓坡保留) if (Stat.ignoreFence && Local) { - if ((TankA && OwnerOf(ShapeA) === Local && IsStaticShape(ShapeB) && IsSteepTriangle(ShapeB)) || - (TankB && OwnerOf(ShapeB) === Local && IsStaticShape(ShapeA) && IsSteepTriangle(ShapeA))) { + if ((TankA && OwnerOf(ShapeA) === Local && IsFenceLike(ShapeB)) || + (TankB && OwnerOf(ShapeB) === Local && IsFenceLike(ShapeA))) { Stat.skippedFence++; return true; } + // 诊断:本车与静态形状配对但没跳过 → 记录样本,便于定位分类/法线 + if (TankA && OwnerOf(ShapeA) === Local && IsStaticShape(ShapeB)) RecordFenceSample(ShapeB); + if (TankB && OwnerOf(ShapeB) === Local && IsStaticShape(ShapeA)) RecordFenceSample(ShapeA); } return false; @@ -6808,6 +6845,13 @@ IsTankOwner: IsTankOwner, IsTankShape: IsTankShape, IsSteepTriangle: IsSteepTriangle, + IsFenceLike: IsFenceLike, + // 诊断:__hornetNoClip__.DebugFence(true) 后撞一次墙,控制台会打印静态形状样本 + DebugFence: function (On) { + window.__hornetFenceDebug__ = On !== false; + FenceSampleKeys = {}; + console.log('[忽略] 栅栏诊断 = ' + (window.__hornetFenceDebug__ ? '开(撞墙后看 [忽略][栅栏样本])' : '关')); + }, GetStat: function () { return { running: Stat.running, mode: Stat.mode, ignoreFence: Stat.ignoreFence, diff --git a/3dtank_bundle.js b/3dtank_bundle.js index 858d42f..1a37c86 100644 --- a/3dtank_bundle.js +++ b/3dtank_bundle.js @@ -6638,19 +6638,29 @@ } // 三角形是否「陡」:法线 z 分量接近 0(墙/栅栏/悬崖立面) - function IsSteepTriangle(Shape) { - if (!Shape || Shape[FShapeType] !== TRIANGLE_TYPE) return false; + function IsTriangle(Shape) { + return !!(Shape && Shape[FShapeType] === TRIANGLE_TYPE); + } + + // 返回法线 z 分量(-1..1);读不到返回 null + function TriangleNormalZ(Shape) { + if (!IsTriangle(Shape)) return null; var A = Shape[FV0], B = Shape[FV1], C = Shape[FV2]; - if (!A || !B || !C) return false; - if (typeof A.f20_1 !== 'number' || typeof B.f20_1 !== 'number' || typeof C.f20_1 !== 'number') return false; + if (!A || !B || !C) return null; + if (typeof A.f20_1 !== 'number' || typeof B.f20_1 !== 'number' || typeof C.f20_1 !== 'number') return null; var ABx = B.f20_1 - A.f20_1, ABy = B.g20_1 - A.g20_1, ABz = B.h20_1 - A.h20_1; var ACx = C.f20_1 - A.f20_1, ACy = C.g20_1 - A.g20_1, ACz = C.h20_1 - A.h20_1; var Nx = ABy * ACz - ABz * ACy; var Ny = ABz * ACx - ABx * ACz; var Nz = ABx * ACy - ABy * ACx; var Len = Math.sqrt(Nx * Nx + Ny * Ny + Nz * Nz); - if (!(Len > 1e-6)) return false; - return Math.abs(Nz / Len) < FENCE_NORMAL_Z; + if (!(Len > 1e-6)) return null; + return Nz / Len; + } + + function IsSteepTriangle(Shape) { + var Nz = TriangleNormalZ(Shape); + return Nz !== null && Math.abs(Nz) < FENCE_NORMAL_Z; } // 静态几何:category 含 bit4,且不是坦克部件 @@ -6660,6 +6670,30 @@ return !IsTankShape(Shape); } + // 「栅栏类」= 静态几何里除地面/缓坡以外的部分: + // · 非三角形静态体(盒/柱等碰撞代理)→ 视为障碍 + // · 三角形且法线接近水平(墙/栅栏/悬崖立面)→ 视为障碍 + function IsFenceLike(Shape) { + if (!IsStaticShape(Shape)) return false; + if (!IsTriangle(Shape)) return true; + return IsSteepTriangle(Shape); + } + + // ===== 诊断(控制台 __hornetFenceDebug__ = true 开启)===== + var FenceSampleKeys = {}; + function RecordFenceSample(Shape) { + if (window.__hornetFenceDebug__ !== true) return; + var Cat = Shape[FShapeCategory]; + var Type = Shape[FShapeType]; + var Tri = IsTriangle(Shape); + var Nz = TriangleNormalZ(Shape); + 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)); + } + // ===== 判定:这一对形状要不要跳过 ===== function ShouldSkip(ShapeA, ShapeB) { var Local = GetLocalPhysics(); @@ -6687,13 +6721,16 @@ } } - // ② 忽略栅栏(只跳过陡峭静态三角形,保留地面/缓坡) + // ② 忽略栅栏(静态障碍;地面/缓坡保留) if (Stat.ignoreFence && Local) { - if ((TankA && OwnerOf(ShapeA) === Local && IsStaticShape(ShapeB) && IsSteepTriangle(ShapeB)) || - (TankB && OwnerOf(ShapeB) === Local && IsStaticShape(ShapeA) && IsSteepTriangle(ShapeA))) { + if ((TankA && OwnerOf(ShapeA) === Local && IsFenceLike(ShapeB)) || + (TankB && OwnerOf(ShapeB) === Local && IsFenceLike(ShapeA))) { Stat.skippedFence++; return true; } + // 诊断:本车与静态形状配对但没跳过 → 记录样本,便于定位分类/法线 + if (TankA && OwnerOf(ShapeA) === Local && IsStaticShape(ShapeB)) RecordFenceSample(ShapeB); + if (TankB && OwnerOf(ShapeB) === Local && IsStaticShape(ShapeA)) RecordFenceSample(ShapeA); } return false; @@ -6808,6 +6845,13 @@ IsTankOwner: IsTankOwner, IsTankShape: IsTankShape, IsSteepTriangle: IsSteepTriangle, + IsFenceLike: IsFenceLike, + // 诊断:__hornetNoClip__.DebugFence(true) 后撞一次墙,控制台会打印静态形状样本 + DebugFence: function (On) { + window.__hornetFenceDebug__ = On !== false; + FenceSampleKeys = {}; + console.log('[忽略] 栅栏诊断 = ' + (window.__hornetFenceDebug__ ? '开(撞墙后看 [忽略][栅栏样本])' : '关')); + }, GetStat: function () { return { running: Stat.running, mode: Stat.mode, ignoreFence: Stat.ignoreFence, diff --git a/tools/hornet_noclip.js b/tools/hornet_noclip.js index bf7f746..8405047 100644 --- a/tools/hornet_noclip.js +++ b/tools/hornet_noclip.js @@ -116,19 +116,29 @@ } // 三角形是否「陡」:法线 z 分量接近 0(墙/栅栏/悬崖立面) - function IsSteepTriangle(Shape) { - if (!Shape || Shape[FShapeType] !== TRIANGLE_TYPE) return false; + function IsTriangle(Shape) { + return !!(Shape && Shape[FShapeType] === TRIANGLE_TYPE); + } + + // 返回法线 z 分量(-1..1);读不到返回 null + function TriangleNormalZ(Shape) { + if (!IsTriangle(Shape)) return null; var A = Shape[FV0], B = Shape[FV1], C = Shape[FV2]; - if (!A || !B || !C) return false; - if (typeof A.f20_1 !== 'number' || typeof B.f20_1 !== 'number' || typeof C.f20_1 !== 'number') return false; + if (!A || !B || !C) return null; + if (typeof A.f20_1 !== 'number' || typeof B.f20_1 !== 'number' || typeof C.f20_1 !== 'number') return null; var ABx = B.f20_1 - A.f20_1, ABy = B.g20_1 - A.g20_1, ABz = B.h20_1 - A.h20_1; var ACx = C.f20_1 - A.f20_1, ACy = C.g20_1 - A.g20_1, ACz = C.h20_1 - A.h20_1; var Nx = ABy * ACz - ABz * ACy; var Ny = ABz * ACx - ABx * ACz; var Nz = ABx * ACy - ABy * ACx; var Len = Math.sqrt(Nx * Nx + Ny * Ny + Nz * Nz); - if (!(Len > 1e-6)) return false; - return Math.abs(Nz / Len) < FENCE_NORMAL_Z; + if (!(Len > 1e-6)) return null; + return Nz / Len; + } + + function IsSteepTriangle(Shape) { + var Nz = TriangleNormalZ(Shape); + return Nz !== null && Math.abs(Nz) < FENCE_NORMAL_Z; } // 静态几何:category 含 bit4,且不是坦克部件 @@ -138,6 +148,30 @@ return !IsTankShape(Shape); } + // 「栅栏类」= 静态几何里除地面/缓坡以外的部分: + // · 非三角形静态体(盒/柱等碰撞代理)→ 视为障碍 + // · 三角形且法线接近水平(墙/栅栏/悬崖立面)→ 视为障碍 + function IsFenceLike(Shape) { + if (!IsStaticShape(Shape)) return false; + if (!IsTriangle(Shape)) return true; + return IsSteepTriangle(Shape); + } + + // ===== 诊断(控制台 __hornetFenceDebug__ = true 开启)===== + var FenceSampleKeys = {}; + function RecordFenceSample(Shape) { + if (window.__hornetFenceDebug__ !== true) return; + var Cat = Shape[FShapeCategory]; + var Type = Shape[FShapeType]; + var Tri = IsTriangle(Shape); + var Nz = TriangleNormalZ(Shape); + 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)); + } + // ===== 判定:这一对形状要不要跳过 ===== function ShouldSkip(ShapeA, ShapeB) { var Local = GetLocalPhysics(); @@ -165,13 +199,16 @@ } } - // ② 忽略栅栏(只跳过陡峭静态三角形,保留地面/缓坡) + // ② 忽略栅栏(静态障碍;地面/缓坡保留) if (Stat.ignoreFence && Local) { - if ((TankA && OwnerOf(ShapeA) === Local && IsStaticShape(ShapeB) && IsSteepTriangle(ShapeB)) || - (TankB && OwnerOf(ShapeB) === Local && IsStaticShape(ShapeA) && IsSteepTriangle(ShapeA))) { + if ((TankA && OwnerOf(ShapeA) === Local && IsFenceLike(ShapeB)) || + (TankB && OwnerOf(ShapeB) === Local && IsFenceLike(ShapeA))) { Stat.skippedFence++; return true; } + // 诊断:本车与静态形状配对但没跳过 → 记录样本,便于定位分类/法线 + if (TankA && OwnerOf(ShapeA) === Local && IsStaticShape(ShapeB)) RecordFenceSample(ShapeB); + if (TankB && OwnerOf(ShapeB) === Local && IsStaticShape(ShapeA)) RecordFenceSample(ShapeA); } return false; @@ -286,6 +323,13 @@ IsTankOwner: IsTankOwner, IsTankShape: IsTankShape, IsSteepTriangle: IsSteepTriangle, + IsFenceLike: IsFenceLike, + // 诊断:__hornetNoClip__.DebugFence(true) 后撞一次墙,控制台会打印静态形状样本 + DebugFence: function (On) { + window.__hornetFenceDebug__ = On !== false; + FenceSampleKeys = {}; + console.log('[忽略] 栅栏诊断 = ' + (window.__hornetFenceDebug__ ? '开(撞墙后看 [忽略][栅栏样本])' : '关')); + }, GetStat: function () { return { running: Stat.running, mode: Stat.mode, ignoreFence: Stat.ignoreFence,