fix(ignore): 放宽栅栏判定并加入静态形状诊断

静态障碍不再只认三角形:非三角形静态碰撞代理直接视为障碍,三角形按法线 |nz|<0.5 判定;新增 __hornetNoClip__.DebugFence(true) 打印静态形状样本(cat/type/三角/nz/是否跳过),用于定位未生效原因。
This commit is contained in:
TermiNexus committed 2026-10-21 00:18:51 +08:00
1 parent f3db51cf4b
commit 63c08ab165
3 files changed
+159 -27

No files matched your search

@@ -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,
+53 -9
View File
@@ -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,
+53 -9
View File
@@ -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,