|
|
@@ -71,6 +71,21 @@ export class BallController extends Component {
|
|
|
private blockFireCooldown: Map<string, number> = new Map();
|
|
|
private FIRE_COOLDOWN = 0.05;
|
|
|
|
|
|
+ // 防围困机制配置
|
|
|
+ @property({
|
|
|
+ tooltip: '防围困检测时间窗口(秒)'
|
|
|
+ })
|
|
|
+ public antiTrapTimeWindow: number = 5.0;
|
|
|
+
|
|
|
+ @property({
|
|
|
+ tooltip: '防围困撞击次数阈值'
|
|
|
+ })
|
|
|
+ public antiTrapHitThreshold: number = 5;
|
|
|
+
|
|
|
+ // 防围困机制状态
|
|
|
+ private ballHitHistory: Map<string, number[]> = new Map(); // 记录每个球的撞击时间历史
|
|
|
+ private ballPhaseThrough: Map<string, number> = new Map(); // 记录每个球的穿透结束时间
|
|
|
+
|
|
|
// 带尾部特效的子弹容器预制体
|
|
|
@property({
|
|
|
type: Prefab,
|
|
|
@@ -627,6 +642,73 @@ export class BallController extends Component {
|
|
|
if (!ballNode || !otherNode) {
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
+ // 检查是否为方块碰撞
|
|
|
+ const nodeName = otherNode.name;
|
|
|
+ const nodePath = this.getNodePath(otherNode);
|
|
|
+ const hasWeaponChild = otherNode.getChildByName('Weapon') !== null;
|
|
|
+ const isBlock =
|
|
|
+ nodeName.includes('Block') ||
|
|
|
+ nodePath.includes('Block') ||
|
|
|
+ hasWeaponChild;
|
|
|
+
|
|
|
+ // 如果是方块碰撞,检查防围困机制
|
|
|
+ if (isBlock) {
|
|
|
+ const ballId = ballNode.uuid;
|
|
|
+ const currentTime = performance.now() / 1000; // 转换为秒
|
|
|
+
|
|
|
+ // 检查是否处于穿透状态
|
|
|
+ const phaseThroughEndTime = this.ballPhaseThrough.get(ballId);
|
|
|
+ if (phaseThroughEndTime && currentTime < phaseThroughEndTime) {
|
|
|
+ // 处于穿透状态,忽略碰撞但仍播放特效
|
|
|
+ const ballAni = BallAni.getInstance();
|
|
|
+ if (ballAni) {
|
|
|
+ let contactPos: Vec3 = null;
|
|
|
+ if (contact && (contact as any).getWorldManifold) {
|
|
|
+ const wm = (contact as any).getWorldManifold();
|
|
|
+ if (wm && wm.points && wm.points.length > 0) {
|
|
|
+ contactPos = new Vec3(wm.points[0].x, wm.points[0].y, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!contactPos) {
|
|
|
+ contactPos = otherNode.worldPosition.clone();
|
|
|
+ }
|
|
|
+ ballAni.playImpactEffect(contactPos);
|
|
|
+ }
|
|
|
+ // 禁用碰撞
|
|
|
+ if (contact) {
|
|
|
+ contact.disabled = true;
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 记录撞击历史
|
|
|
+ if (!this.ballHitHistory.has(ballId)) {
|
|
|
+ this.ballHitHistory.set(ballId, []);
|
|
|
+ }
|
|
|
+ const hitHistory = this.ballHitHistory.get(ballId);
|
|
|
+ hitHistory.push(currentTime);
|
|
|
+
|
|
|
+ // 清理过期的撞击记录
|
|
|
+ const timeThreshold = currentTime - this.antiTrapTimeWindow;
|
|
|
+ while (hitHistory.length > 0 && hitHistory[0] < timeThreshold) {
|
|
|
+ hitHistory.shift();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否达到防围困条件
|
|
|
+ if (hitHistory.length >= this.antiTrapHitThreshold) {
|
|
|
+ // 激活穿透状态,持续0.5秒
|
|
|
+ this.ballPhaseThrough.set(ballId, currentTime + 0.5);
|
|
|
+ // 清空撞击历史
|
|
|
+ hitHistory.length = 0;
|
|
|
+ // 禁用当前碰撞
|
|
|
+ if (contact) {
|
|
|
+ contact.disabled = true;
|
|
|
+ }
|
|
|
+ console.log(`Ball ${ballId} entered phase-through mode due to frequent collisions`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
// 计算碰撞世界坐标
|
|
|
let contactPos: Vec3 = null;
|
|
|
@@ -645,17 +727,6 @@ export class BallController extends Component {
|
|
|
if (ballAni) {
|
|
|
ballAni.playImpactEffect(contactPos);
|
|
|
}
|
|
|
-
|
|
|
- // 检查碰撞对象是否为方块,只有方块碰撞才发射子弹
|
|
|
- const nodeName = otherNode.name;
|
|
|
- const nodePath = this.getNodePath(otherNode);
|
|
|
-
|
|
|
- // 检查是否有Weapon子节点(判断是否为方块)
|
|
|
- const hasWeaponChild = otherNode.getChildByName('Weapon') !== null;
|
|
|
- const isBlock =
|
|
|
- nodeName.includes('Block') ||
|
|
|
- nodePath.includes('Block') ||
|
|
|
- hasWeaponChild;
|
|
|
|
|
|
if (isBlock) {
|
|
|
// 播放方块撞击动画
|
|
|
@@ -979,6 +1050,9 @@ export class BallController extends Component {
|
|
|
// 维持所有小球的恒定速度
|
|
|
this.maintainAllBallsSpeed();
|
|
|
|
|
|
+ // 清理过期的防围困状态
|
|
|
+ this.cleanupExpiredAntiTrapStates();
|
|
|
+
|
|
|
// 定期检查小球是否接近方块但没有触发碰撞(调试用)
|
|
|
if (this.activeBall && this.activeBall.isValid) {
|
|
|
this.debugCheckNearBlocks();
|
|
|
@@ -1248,18 +1322,50 @@ export class BallController extends Component {
|
|
|
// 清理冷却时间
|
|
|
this.blockFireCooldown.clear();
|
|
|
|
|
|
+ // 清理防围困状态数据
|
|
|
+ this.ballHitHistory.clear();
|
|
|
+ this.ballPhaseThrough.clear();
|
|
|
+
|
|
|
// 重置球速
|
|
|
this.updateBallSpeed();
|
|
|
|
|
|
console.log('[BallController] 球控制器重置完成');
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 清理过期的防围困状态
|
|
|
+ */
|
|
|
+ private cleanupExpiredAntiTrapStates() {
|
|
|
+ const currentTime = performance.now() / 1000;
|
|
|
+
|
|
|
+ // 清理过期的穿透状态
|
|
|
+ for (const [ballId, endTime] of this.ballPhaseThrough.entries()) {
|
|
|
+ if (currentTime >= endTime) {
|
|
|
+ this.ballPhaseThrough.delete(ballId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 清理单个小球的防围困状态数据
|
|
|
+ * @param ballId 小球的唯一标识符
|
|
|
+ */
|
|
|
+ public cleanupBallAntiTrapState(ballId: string) {
|
|
|
+ this.ballHitHistory.delete(ballId);
|
|
|
+ this.ballPhaseThrough.delete(ballId);
|
|
|
+ console.log(`Cleaned up anti-trap state for ball ${ballId}`);
|
|
|
+ }
|
|
|
+
|
|
|
onDestroy() {
|
|
|
// 清理事件监听
|
|
|
const eventBus = EventBus.getInstance();
|
|
|
eventBus.off(GameEvents.GAME_PAUSE, this.onGamePauseEvent, this);
|
|
|
eventBus.off(GameEvents.GAME_RESUME, this.onGameResumeEvent, this);
|
|
|
eventBus.off(GameEvents.RESET_BALL_CONTROLLER, this.onResetBallControllerEvent, this);
|
|
|
+
|
|
|
+ // 清理防围困状态数据
|
|
|
+ this.ballHitHistory.clear();
|
|
|
+ this.ballPhaseThrough.clear();
|
|
|
}
|
|
|
|
|
|
private updateBallSpeed() {
|