fix(ignore): 栅栏判定加入高度门槛,避免把地面一起忽略导致掉出地图

上一版 skippedFence 达 4.6 万,说明地面三角也被忽略。现规则:静态形状最高点必须高于本地坦克中心 1m 才可能是栅栏;三角形再要求 |法线z|<0.6。新增 byType 统计(cat/type → 忽略次数)便于确认命中对象。
This commit is contained in:
TermiNexus committed 2026-10-21 00:32:24 +08:00
1 parent bfc3821dc7
commit 580a456032
3 files changed
+126 -24

No files matched your search

@@ -6587,6 +6587,7 @@
ignoreFence: false,
skippedTank: 0,
skippedFence: 0,
byType: {}, // cat/type → 被忽略次数(诊断)
patched: false
};
@@ -6672,13 +6673,41 @@
return !IsTankShape(Shape);
}
// 「栅栏类」= 静态几何里除地面/缓坡以外的部分:
// · 三角形:法线接近水平(墙/栅栏/悬崖立面)→ 忽略;法线朝上(地面/缓坡)→ 保留
// · 非三角形静态碰撞代理(盒/柱等):地图里这类基本都不是可站立地面 → 忽略
var FPhysicsPosition = Fld('Physics.Position', 'bzr_1'); // 本地坦克世界位置
// 「栅栏类」= 静态几何中「明显高于车体中心」的那部分:
// · 三角形:|法线 z| < 0.6(立面)且最高点高于车体中心 1m → 墙/栅栏
// · 非三角形(盒/柱等代理):中心高于车体中心 1m → 障碍
// · 地面/缓坡(最高点不高于车体中心)→ 一律保留,避免掉出地图
function LocalTankZ() {
var Phys = GetLocalPhysics();
var P = Phys && Phys[FPhysicsPosition];
return (P && typeof P.h20_1 === 'number') ? P.h20_1 : null;
}
function ShapeMaxZ(Shape) {
if (IsTriangle(Shape)) {
var A = Shape[FV0], B = Shape[FV1], C = Shape[FV2];
if (!A || !B || !C) return null;
var Max = Math.max(A.h20_1, B.h20_1, C.h20_1);
return isFinite(Max) ? Max : null;
}
var Body = Shape[FShapeBody];
var State = Body && Body[FBodyState];
var P = State && State[FBodyPosVec];
return (P && typeof P.h20_1 === 'number') ? P.h20_1 : null;
}
function IsFenceLike(Shape) {
if (!IsStaticShape(Shape)) return false;
if (!IsTriangle(Shape)) return true;
return IsSteepTriangle(Shape);
var TankZ = LocalTankZ();
if (TankZ === null) return false;
var MaxZ = ShapeMaxZ(Shape);
if (MaxZ === null) return false;
if (MaxZ < TankZ + 100) return false; // 不高于车体中心 1m → 地面/缓坡/低矮物
if (!IsTriangle(Shape)) return true; // 高于车体中心的盒/柱代理 → 障碍
var Nz = TriangleNormalZ(Shape);
return Nz !== null && Math.abs(Nz) < 0.6; // 立面才算墙/栅栏
}
// ===== 诊断:识别「正在和什么物体重叠」=====
@@ -6799,9 +6828,13 @@
// ② 忽略栅栏(静态障碍;地面/缓坡保留)
if (Stat.ignoreFence && Local) {
if ((TankA && OwnerOf(ShapeA) === Local && IsFenceLike(ShapeB)) ||
(TankB && OwnerOf(ShapeB) === Local && IsFenceLike(ShapeA))) {
var FenceShape = null;
if (TankA && OwnerOf(ShapeA) === Local) FenceShape = ShapeB;
else if (TankB && OwnerOf(ShapeB) === Local) FenceShape = ShapeA;
if (FenceShape && IsFenceLike(FenceShape)) {
Stat.skippedFence++;
var Key = FenceShape[FShapeCategory] + '/' + FenceShape[FShapeType];
Stat.byType[Key] = (Stat.byType[Key] || 0) + 1;
return true;
}
// 诊断:本车与任意非坦克形状配对但没跳过 → 记录样本(含被挡住的栅栏)
@@ -6931,7 +6964,8 @@
GetStat: function () {
return {
running: Stat.running, mode: Stat.mode, ignoreFence: Stat.ignoreFence,
skippedTank: Stat.skippedTank, skippedFence: Stat.skippedFence, patched: Stat.patched
skippedTank: Stat.skippedTank, skippedFence: Stat.skippedFence,
byType: Stat.byType, patched: Stat.patched
};
}
};
+42 -8
View File
@@ -6587,6 +6587,7 @@
ignoreFence: false,
skippedTank: 0,
skippedFence: 0,
byType: {}, // cat/type → 被忽略次数(诊断)
patched: false
};
@@ -6672,13 +6673,41 @@
return !IsTankShape(Shape);
}
// 「栅栏类」= 静态几何里除地面/缓坡以外的部分:
// · 三角形:法线接近水平(墙/栅栏/悬崖立面)→ 忽略;法线朝上(地面/缓坡)→ 保留
// · 非三角形静态碰撞代理(盒/柱等):地图里这类基本都不是可站立地面 → 忽略
var FPhysicsPosition = Fld('Physics.Position', 'bzr_1'); // 本地坦克世界位置
// 「栅栏类」= 静态几何中「明显高于车体中心」的那部分:
// · 三角形:|法线 z| < 0.6(立面)且最高点高于车体中心 1m → 墙/栅栏
// · 非三角形(盒/柱等代理):中心高于车体中心 1m → 障碍
// · 地面/缓坡(最高点不高于车体中心)→ 一律保留,避免掉出地图
function LocalTankZ() {
var Phys = GetLocalPhysics();
var P = Phys && Phys[FPhysicsPosition];
return (P && typeof P.h20_1 === 'number') ? P.h20_1 : null;
}
function ShapeMaxZ(Shape) {
if (IsTriangle(Shape)) {
var A = Shape[FV0], B = Shape[FV1], C = Shape[FV2];
if (!A || !B || !C) return null;
var Max = Math.max(A.h20_1, B.h20_1, C.h20_1);
return isFinite(Max) ? Max : null;
}
var Body = Shape[FShapeBody];
var State = Body && Body[FBodyState];
var P = State && State[FBodyPosVec];
return (P && typeof P.h20_1 === 'number') ? P.h20_1 : null;
}
function IsFenceLike(Shape) {
if (!IsStaticShape(Shape)) return false;
if (!IsTriangle(Shape)) return true;
return IsSteepTriangle(Shape);
var TankZ = LocalTankZ();
if (TankZ === null) return false;
var MaxZ = ShapeMaxZ(Shape);
if (MaxZ === null) return false;
if (MaxZ < TankZ + 100) return false; // 不高于车体中心 1m → 地面/缓坡/低矮物
if (!IsTriangle(Shape)) return true; // 高于车体中心的盒/柱代理 → 障碍
var Nz = TriangleNormalZ(Shape);
return Nz !== null && Math.abs(Nz) < 0.6; // 立面才算墙/栅栏
}
// ===== 诊断:识别「正在和什么物体重叠」=====
@@ -6799,9 +6828,13 @@
// ② 忽略栅栏(静态障碍;地面/缓坡保留)
if (Stat.ignoreFence && Local) {
if ((TankA && OwnerOf(ShapeA) === Local && IsFenceLike(ShapeB)) ||
(TankB && OwnerOf(ShapeB) === Local && IsFenceLike(ShapeA))) {
var FenceShape = null;
if (TankA && OwnerOf(ShapeA) === Local) FenceShape = ShapeB;
else if (TankB && OwnerOf(ShapeB) === Local) FenceShape = ShapeA;
if (FenceShape && IsFenceLike(FenceShape)) {
Stat.skippedFence++;
var Key = FenceShape[FShapeCategory] + '/' + FenceShape[FShapeType];
Stat.byType[Key] = (Stat.byType[Key] || 0) + 1;
return true;
}
// 诊断:本车与任意非坦克形状配对但没跳过 → 记录样本(含被挡住的栅栏)
@@ -6931,7 +6964,8 @@
GetStat: function () {
return {
running: Stat.running, mode: Stat.mode, ignoreFence: Stat.ignoreFence,
skippedTank: Stat.skippedTank, skippedFence: Stat.skippedFence, patched: Stat.patched
skippedTank: Stat.skippedTank, skippedFence: Stat.skippedFence,
byType: Stat.byType, patched: Stat.patched
};
}
};
+42 -8
View File
@@ -63,6 +63,7 @@
ignoreFence: false,
skippedTank: 0,
skippedFence: 0,
byType: {}, // cat/type → 被忽略次数(诊断)
patched: false
};
@@ -148,13 +149,41 @@
return !IsTankShape(Shape);
}
// 「栅栏类」= 静态几何里除地面/缓坡以外的部分:
// · 三角形:法线接近水平(墙/栅栏/悬崖立面)→ 忽略;法线朝上(地面/缓坡)→ 保留
// · 非三角形静态碰撞代理(盒/柱等):地图里这类基本都不是可站立地面 → 忽略
var FPhysicsPosition = Fld('Physics.Position', 'bzr_1'); // 本地坦克世界位置
// 「栅栏类」= 静态几何中「明显高于车体中心」的那部分:
// · 三角形:|法线 z| < 0.6(立面)且最高点高于车体中心 1m → 墙/栅栏
// · 非三角形(盒/柱等代理):中心高于车体中心 1m → 障碍
// · 地面/缓坡(最高点不高于车体中心)→ 一律保留,避免掉出地图
function LocalTankZ() {
var Phys = GetLocalPhysics();
var P = Phys && Phys[FPhysicsPosition];
return (P && typeof P.h20_1 === 'number') ? P.h20_1 : null;
}
function ShapeMaxZ(Shape) {
if (IsTriangle(Shape)) {
var A = Shape[FV0], B = Shape[FV1], C = Shape[FV2];
if (!A || !B || !C) return null;
var Max = Math.max(A.h20_1, B.h20_1, C.h20_1);
return isFinite(Max) ? Max : null;
}
var Body = Shape[FShapeBody];
var State = Body && Body[FBodyState];
var P = State && State[FBodyPosVec];
return (P && typeof P.h20_1 === 'number') ? P.h20_1 : null;
}
function IsFenceLike(Shape) {
if (!IsStaticShape(Shape)) return false;
if (!IsTriangle(Shape)) return true;
return IsSteepTriangle(Shape);
var TankZ = LocalTankZ();
if (TankZ === null) return false;
var MaxZ = ShapeMaxZ(Shape);
if (MaxZ === null) return false;
if (MaxZ < TankZ + 100) return false; // 不高于车体中心 1m → 地面/缓坡/低矮物
if (!IsTriangle(Shape)) return true; // 高于车体中心的盒/柱代理 → 障碍
var Nz = TriangleNormalZ(Shape);
return Nz !== null && Math.abs(Nz) < 0.6; // 立面才算墙/栅栏
}
// ===== 诊断:识别「正在和什么物体重叠」=====
@@ -275,9 +304,13 @@
// ② 忽略栅栏(静态障碍;地面/缓坡保留)
if (Stat.ignoreFence && Local) {
if ((TankA && OwnerOf(ShapeA) === Local && IsFenceLike(ShapeB)) ||
(TankB && OwnerOf(ShapeB) === Local && IsFenceLike(ShapeA))) {
var FenceShape = null;
if (TankA && OwnerOf(ShapeA) === Local) FenceShape = ShapeB;
else if (TankB && OwnerOf(ShapeB) === Local) FenceShape = ShapeA;
if (FenceShape && IsFenceLike(FenceShape)) {
Stat.skippedFence++;
var Key = FenceShape[FShapeCategory] + '/' + FenceShape[FShapeType];
Stat.byType[Key] = (Stat.byType[Key] || 0) + 1;
return true;
}
// 诊断:本车与任意非坦克形状配对但没跳过 → 记录样本(含被挡住的栅栏)
@@ -407,7 +440,8 @@
GetStat: function () {
return {
running: Stat.running, mode: Stat.mode, ignoreFence: Stat.ignoreFence,
skippedTank: Stat.skippedTank, skippedFence: Stat.skippedFence, patched: Stat.patched
skippedTank: Stat.skippedTank, skippedFence: Stat.skippedFence,
byType: Stat.byType, patched: Stat.patched
};
}
};