|
|
@@ -0,0 +1,406 @@
|
|
|
+import { _decorator, Component, Node, Vec3, find, BoxCollider2D, RigidBody2D, Collider2D, Contact2DType, IPhysics2DContact, ERigidBody2DType } from 'cc';
|
|
|
+import { sp } from 'cc';
|
|
|
+import EventBus, { GameEvents } from '../../Core/EventBus';
|
|
|
+import { GroundBurnAreaManager } from './GroundBurnAreaManager';
|
|
|
+import { PhysicsManager } from '../../Core/PhysicsManager';
|
|
|
+const { ccclass, property } = _decorator;
|
|
|
+/**
|
|
|
+ * 地面燃烧区域组件
|
|
|
+ * 用于在地面创建固定范围的燃烧区域,对进入区域的敌人造成持续伤害
|
|
|
+ */
|
|
|
+@ccclass('GroundBurnArea')
|
|
|
+export class GroundBurnArea extends Component {
|
|
|
+ private _duration: number = 0;
|
|
|
+ private _damage: number = 0;
|
|
|
+ private _tickInterval: number = 0;
|
|
|
+
|
|
|
+ private _isActive: boolean = false;
|
|
|
+ private _burnEffectNode: Node | null = null; // 燃烧特效节点
|
|
|
+ private _enemiesInArea: Set<Node> = new Set(); // 区域内的敌人
|
|
|
+ private _damageTimer: number = 0; // 伤害计时器
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 组件加载时的初始化
|
|
|
+ */
|
|
|
+ protected onLoad(): void {
|
|
|
+ // 立即禁用RigidBody2D组件,防止在物理世界未准备好时激活
|
|
|
+ const rigidBody = this.node.getComponent(RigidBody2D);
|
|
|
+ if (rigidBody) {
|
|
|
+ rigidBody.enabled = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 也禁用BoxCollider2D组件,确保完全避免物理系统访问
|
|
|
+ const collider = this.node.getComponent(BoxCollider2D);
|
|
|
+ if (collider) {
|
|
|
+ collider.enabled = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化地面燃烧区域
|
|
|
+ * @param position 燃烧区域位置
|
|
|
+ * @param duration 持续时间(秒)
|
|
|
+ * @param damage 每次伤害值
|
|
|
+ * @param tickInterval 伤害间隔(秒)
|
|
|
+ */
|
|
|
+ public initGroundBurnArea(position: Vec3, duration: number, damage: number, tickInterval: number): void {
|
|
|
+ this._duration = duration;
|
|
|
+ this._damage = damage;
|
|
|
+ this._tickInterval = tickInterval;
|
|
|
+ this._isActive = true;
|
|
|
+ this._damageTimer = 0;
|
|
|
+
|
|
|
+ // 设置节点位置
|
|
|
+ this.node.setWorldPosition(position);
|
|
|
+
|
|
|
+ // 获取预制体中的火焰特效节点
|
|
|
+ this.setupFlameEffect();
|
|
|
+
|
|
|
+ // 先禁用RigidBody2D组件,避免在物理世界未准备好时激活
|
|
|
+ const rigidBody = this.node.getComponent(RigidBody2D);
|
|
|
+ if (rigidBody) {
|
|
|
+ rigidBody.enabled = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置碰撞器
|
|
|
+ this.setupCollider();
|
|
|
+
|
|
|
+ console.log(`[GroundBurnArea] 初始化地面燃烧区域 - 位置: (${position.x.toFixed(1)}, ${position.y.toFixed(1)}), 持续时间: ${duration}秒, 伤害: ${damage}, 间隔: ${tickInterval}秒`);
|
|
|
+
|
|
|
+ // 启动燃烧区域
|
|
|
+ this.startBurnArea();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置碰撞器
|
|
|
+ */
|
|
|
+ private setupCollider(): void {
|
|
|
+ // 延迟设置碰撞器,确保物理系统已初始化
|
|
|
+ this.scheduleOnce(() => {
|
|
|
+ this.trySetupCollider();
|
|
|
+ }, 0.2); // 增加延迟时间到0.2秒
|
|
|
+ }
|
|
|
+
|
|
|
+ private trySetupCollider(): void {
|
|
|
+ // 检查物理系统是否已初始化
|
|
|
+ const physicsManager = PhysicsManager.getInstance();
|
|
|
+ if (!physicsManager) {
|
|
|
+ console.warn('[GroundBurnArea] 物理系统未初始化,延迟重试');
|
|
|
+ // 延迟重试
|
|
|
+ this.scheduleOnce(() => {
|
|
|
+ this.trySetupCollider();
|
|
|
+ }, 0.1);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const physicsSystem = physicsManager.getSystem();
|
|
|
+ if (!physicsSystem || !physicsSystem.enable) {
|
|
|
+ console.warn('[GroundBurnArea] 物理系统未启用,延迟重试');
|
|
|
+ // 延迟重试
|
|
|
+ this.scheduleOnce(() => {
|
|
|
+ this.trySetupCollider();
|
|
|
+ }, 0.1);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取碰撞器组件
|
|
|
+ const collider = this.node.getComponent(BoxCollider2D);
|
|
|
+ if (!collider) {
|
|
|
+ console.error('[GroundBurnArea] 未找到 BoxCollider2D 组件');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重新启用碰撞器组件
|
|
|
+ collider.enabled = true;
|
|
|
+
|
|
|
+ // 获取刚体组件并确保启用碰撞监听
|
|
|
+ const rigidBody = this.node.getComponent(RigidBody2D);
|
|
|
+ if (rigidBody) {
|
|
|
+ try {
|
|
|
+ rigidBody.type = ERigidBody2DType.Static;
|
|
|
+ rigidBody.enabledContactListener = true;
|
|
|
+ // 重新启用RigidBody2D组件
|
|
|
+ rigidBody.enabled = true;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('[GroundBurnArea] 设置刚体组件时出错:', error);
|
|
|
+ // 如果设置失败,再次延迟重试
|
|
|
+ this.scheduleOnce(() => {
|
|
|
+ this.trySetupCollider();
|
|
|
+ }, 0.1);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 监听碰撞事件(移除之前的监听器避免重复)
|
|
|
+ collider.off(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
|
|
|
+ collider.off(Contact2DType.END_CONTACT, this.onEndContact, this);
|
|
|
+ collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
|
|
|
+ collider.on(Contact2DType.END_CONTACT, this.onEndContact, this);
|
|
|
+
|
|
|
+ console.log(`[GroundBurnArea] 碰撞器设置完成,碰撞器启用: ${collider.enabled}, 刚体启用: ${rigidBody?.enabled}`);
|
|
|
+
|
|
|
+ // 验证碰撞器设置
|
|
|
+ if (!collider.enabled) {
|
|
|
+ console.error('[GroundBurnArea] 碰撞器未启用!');
|
|
|
+ }
|
|
|
+ if (rigidBody && !rigidBody.enabled) {
|
|
|
+ console.error('[GroundBurnArea] 刚体未启用!');
|
|
|
+ }
|
|
|
+ if (rigidBody && !rigidBody.enabledContactListener) {
|
|
|
+ console.error('[GroundBurnArea] 刚体碰撞监听未启用!');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 敌人进入燃烧区域
|
|
|
+ */
|
|
|
+ private onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null): void {
|
|
|
+ const enemyNode = otherCollider.node;
|
|
|
+
|
|
|
+ console.log(`[GroundBurnArea] 碰撞检测 - 节点名称: ${enemyNode.name}, 父节点: ${enemyNode.parent?.name || 'null'}`);
|
|
|
+
|
|
|
+ // 检查是否是敌人
|
|
|
+ if (this.isEnemyNode(enemyNode)) {
|
|
|
+ this._enemiesInArea.add(enemyNode);
|
|
|
+ console.log(`[GroundBurnArea] 敌人进入燃烧区域: ${enemyNode.name}, 区域内敌人数量: ${this._enemiesInArea.size}`);
|
|
|
+ } else {
|
|
|
+ console.log(`[GroundBurnArea] 非敌人节点进入燃烧区域: ${enemyNode.name}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 敌人离开燃烧区域
|
|
|
+ */
|
|
|
+ private onEndContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null): void {
|
|
|
+ const enemyNode = otherCollider.node;
|
|
|
+
|
|
|
+ // 检查是否是敌人
|
|
|
+ if (this.isEnemyNode(enemyNode)) {
|
|
|
+ this._enemiesInArea.delete(enemyNode);
|
|
|
+ console.log(`[GroundBurnArea] 敌人离开燃烧区域: ${enemyNode.name}, 区域内敌人数量: ${this._enemiesInArea.size}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查是否是敌人节点
|
|
|
+ */
|
|
|
+ private isEnemyNode(node: Node): boolean {
|
|
|
+ if (!node || !node.name) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查节点名称
|
|
|
+ const nameCheck = (
|
|
|
+ node.name.includes('Enemy') ||
|
|
|
+ node.name.includes('enemy') ||
|
|
|
+ node.name.includes('僵尸') ||
|
|
|
+ node.name.includes('Zombie') ||
|
|
|
+ node.name.includes('zombie')
|
|
|
+ );
|
|
|
+
|
|
|
+ // 检查父节点名称
|
|
|
+ const parentCheck = (
|
|
|
+ node.parent?.name === 'enemyContainer' ||
|
|
|
+ node.parent?.name === 'EnemyContainer' ||
|
|
|
+ node.parent?.name?.includes('enemy') ||
|
|
|
+ node.parent?.name?.includes('Enemy')
|
|
|
+ );
|
|
|
+
|
|
|
+ // 检查是否有EnemyInstance组件
|
|
|
+ const componentCheck = node.getComponent('EnemyInstance') !== null;
|
|
|
+
|
|
|
+ const isEnemy = nameCheck || parentCheck || componentCheck;
|
|
|
+
|
|
|
+ console.log(`[GroundBurnArea] 敌人检测 - 节点: ${node.name}, 父节点: ${node.parent?.name || 'null'}, 名称检查: ${nameCheck}, 父节点检查: ${parentCheck}, 组件检查: ${componentCheck}, 是敌人: ${isEnemy}`);
|
|
|
+
|
|
|
+ return isEnemy;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 启动燃烧区域
|
|
|
+ */
|
|
|
+ private startBurnArea(): void {
|
|
|
+ if (!this._isActive) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(`[GroundBurnArea] 启动地面燃烧区域 - 持续时间: ${this._duration}秒`);
|
|
|
+
|
|
|
+ // 启动更新循环
|
|
|
+ this.schedule(this.updateBurnArea, 0.1); // 每0.1秒更新一次
|
|
|
+
|
|
|
+ // 设置区域销毁定时器
|
|
|
+ this.scheduleOnce(() => {
|
|
|
+ this.destroyBurnArea();
|
|
|
+ }, this._duration);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新燃烧区域
|
|
|
+ */
|
|
|
+ private updateBurnArea(deltaTime: number): void {
|
|
|
+ if (!this._isActive) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新伤害计时器
|
|
|
+ this._damageTimer += deltaTime;
|
|
|
+
|
|
|
+ // 检查是否到了造成伤害的时间
|
|
|
+ if (this._damageTimer >= this._tickInterval) {
|
|
|
+ console.log(`[GroundBurnArea] 燃烧区域伤害检查 - 区域内敌人数量: ${this._enemiesInArea.size}, 伤害值: ${this._damage}`);
|
|
|
+
|
|
|
+ this.dealDamageToEnemiesInArea();
|
|
|
+ this._damageTimer = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清理无效的敌人节点
|
|
|
+ this.cleanupInvalidEnemies();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 对区域内的敌人造成伤害
|
|
|
+ */
|
|
|
+ private dealDamageToEnemiesInArea(): void {
|
|
|
+ if (this._enemiesInArea.size === 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(`[GroundBurnArea] 对区域内 ${this._enemiesInArea.size} 个敌人造成燃烧伤害: ${this._damage}`);
|
|
|
+
|
|
|
+ const eventBus = EventBus.getInstance();
|
|
|
+
|
|
|
+ // 对每个区域内的敌人造成伤害
|
|
|
+ for (const enemyNode of this._enemiesInArea) {
|
|
|
+ if (enemyNode && enemyNode.isValid) {
|
|
|
+ const damageData = {
|
|
|
+ enemyNode: enemyNode,
|
|
|
+ damage: this._damage,
|
|
|
+ isCritical: false,
|
|
|
+ source: 'GroundBurnArea'
|
|
|
+ };
|
|
|
+
|
|
|
+ eventBus.emit(GameEvents.APPLY_DAMAGE_TO_ENEMY, damageData);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 清理无效的敌人节点
|
|
|
+ */
|
|
|
+ private cleanupInvalidEnemies(): void {
|
|
|
+ const invalidEnemies: Node[] = [];
|
|
|
+
|
|
|
+ for (const enemyNode of this._enemiesInArea) {
|
|
|
+ if (!enemyNode || !enemyNode.isValid) {
|
|
|
+ invalidEnemies.push(enemyNode);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 移除无效的敌人
|
|
|
+ for (const invalidEnemy of invalidEnemies) {
|
|
|
+ this._enemiesInArea.delete(invalidEnemy);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置火焰特效
|
|
|
+ */
|
|
|
+ private setupFlameEffect(): void {
|
|
|
+ // 查找预制体中的 FlameEffect 子节点
|
|
|
+ const flameEffectNode = this.node.getChildByName('FlameEffect');
|
|
|
+ if (flameEffectNode) {
|
|
|
+ this._burnEffectNode = flameEffectNode;
|
|
|
+ // 启动火焰特效动画
|
|
|
+ const skeleton = flameEffectNode.getComponent(sp.Skeleton);
|
|
|
+ if (skeleton) {
|
|
|
+ skeleton.setAnimation(0, 'animation', true);
|
|
|
+ console.log('[GroundBurnArea] 火焰特效节点已找到并开始播放动画');
|
|
|
+ } else {
|
|
|
+ console.warn('[GroundBurnArea] FlameEffect 节点缺少 sp.Skeleton 组件');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ console.warn('[GroundBurnArea] 未找到 FlameEffect 子节点,请检查预制体配置');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置燃烧特效节点
|
|
|
+ */
|
|
|
+ public setBurnEffectNode(effectNode: Node): void {
|
|
|
+ this._burnEffectNode = effectNode;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 销毁燃烧区域
|
|
|
+ */
|
|
|
+ public destroyBurnArea(): void {
|
|
|
+ console.log(`[GroundBurnArea] 销毁地面燃烧区域`);
|
|
|
+
|
|
|
+ this._isActive = false;
|
|
|
+ this.unscheduleAllCallbacks();
|
|
|
+
|
|
|
+ // 清空敌人列表
|
|
|
+ this._enemiesInArea.clear();
|
|
|
+
|
|
|
+ // 销毁燃烧特效节点
|
|
|
+ if (this._burnEffectNode && this._burnEffectNode.isValid) {
|
|
|
+ console.log(`[GroundBurnArea] 销毁燃烧特效节点`);
|
|
|
+ this._burnEffectNode.destroy();
|
|
|
+ this._burnEffectNode = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 通知管理器移除此燃烧区域
|
|
|
+ const manager = GroundBurnAreaManager.getInstance();
|
|
|
+ if (manager) {
|
|
|
+ manager.removeBurnArea(this.node);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 销毁节点
|
|
|
+ if (this.node && this.node.isValid) {
|
|
|
+ this.node.destroy();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取燃烧区域是否激活
|
|
|
+ */
|
|
|
+ public isActive(): boolean {
|
|
|
+ return this._isActive;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取区域内敌人数量
|
|
|
+ */
|
|
|
+ public getEnemyCount(): number {
|
|
|
+ return this._enemiesInArea.size;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 组件销毁时清理资源
|
|
|
+ */
|
|
|
+ protected onDestroy(): void {
|
|
|
+ this._isActive = false;
|
|
|
+ this.unscheduleAllCallbacks();
|
|
|
+
|
|
|
+ // 清空敌人列表
|
|
|
+ this._enemiesInArea.clear();
|
|
|
+
|
|
|
+ // 清理燃烧特效节点
|
|
|
+ if (this._burnEffectNode && this._burnEffectNode.isValid) {
|
|
|
+ console.log(`[GroundBurnArea] 组件销毁时清理燃烧特效节点`);
|
|
|
+ this._burnEffectNode.destroy();
|
|
|
+ this._burnEffectNode = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 通知管理器移除此燃烧区域
|
|
|
+ const manager = GroundBurnAreaManager.getInstance();
|
|
|
+ if (manager) {
|
|
|
+ manager.removeBurnArea(this.node);
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(`[GroundBurnArea] 地面燃烧区域组件已销毁`);
|
|
|
+ }
|
|
|
+}
|