|
|
@@ -2,15 +2,12 @@ import { _decorator, Component, Node, Vec2, Vec3, find, RigidBody2D, Collider2D,
|
|
|
const { ccclass, property } = _decorator;
|
|
|
|
|
|
/**
|
|
|
- * 子弹控制器 - 重构版本
|
|
|
+ * 子弹控制器 - 安全版本
|
|
|
*
|
|
|
- * 新的设计:
|
|
|
- * 1. 静态方法 fireBulletFromPosition: 用于从指定位置发射子弹,包含完整的子弹创建和初始化逻辑
|
|
|
- * 2. 实例方法 initializeTarget: 用于初始化子弹的目标(寻找最近敌人)和方向
|
|
|
- * 3. BallController调用静态方法来发射子弹,实现了逻辑分离
|
|
|
- *
|
|
|
- * 工作流程:
|
|
|
- * BallController.fireBullet() -> BulletController.fireBulletFromPosition() -> bulletInstance.initializeTarget()
|
|
|
+ * 设计原则:
|
|
|
+ * 1. 在子弹添加到场景之前设置所有物理属性
|
|
|
+ * 2. 延迟启用物理组件,避免m_world为null错误
|
|
|
+ * 3. 确保物理组件在正确的时机激活
|
|
|
*/
|
|
|
@ccclass('BulletController')
|
|
|
export class BulletController extends Component {
|
|
|
@@ -26,429 +23,154 @@ export class BulletController extends Component {
|
|
|
private targetEnemy: Node = null;
|
|
|
private rigidBody: RigidBody2D = null;
|
|
|
private lifeTimer: number = 0;
|
|
|
- private initialDirection: Vec2 = null; // 存储初始方向
|
|
|
private direction: Vec3 = null;
|
|
|
+ private firePosition: Vec3 = null;
|
|
|
+ private isInitialized: boolean = false;
|
|
|
+ private needsPhysicsSetup: boolean = false;
|
|
|
|
|
|
/**
|
|
|
- * 静态方法:从指定位置发射子弹攻击最近的敌人
|
|
|
- * @param bulletPrefab 子弹预制体
|
|
|
- * @param firePosition 发射位置(世界坐标)
|
|
|
- * @param weaponNode 武器节点(可选,用于调试)
|
|
|
- * @returns 创建的子弹节点
|
|
|
+ * 设置子弹的发射位置
|
|
|
+ * @param position 发射位置(世界坐标)
|
|
|
*/
|
|
|
- static fireBulletFromPosition(bulletPrefab: Prefab, firePosition: Vec3, weaponNode?: Node): Node | null {
|
|
|
- console.log('🔫 === 静态子弹发射系统启动 ===');
|
|
|
- console.log('发射参数:', {
|
|
|
- 发射位置: firePosition,
|
|
|
- 武器节点: weaponNode ? weaponNode.name : '无',
|
|
|
- 子弹预制体: bulletPrefab ? bulletPrefab.name : '无'
|
|
|
- });
|
|
|
-
|
|
|
- if (!bulletPrefab) {
|
|
|
- console.error('❌ 子弹预制体未提供');
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- // 创建子弹实例
|
|
|
- console.log('🏗️ 创建子弹实例...');
|
|
|
- const bullet = instantiate(bulletPrefab);
|
|
|
- if (!bullet) {
|
|
|
- console.error('❌ 子弹实例创建失败');
|
|
|
- return null;
|
|
|
- }
|
|
|
- console.log('✅ 子弹实例创建成功:', bullet.name);
|
|
|
-
|
|
|
- // 将子弹添加到GameArea中
|
|
|
- const gameArea = find('Canvas/GameLevelUI/GameArea');
|
|
|
- if (gameArea) {
|
|
|
- gameArea.addChild(bullet);
|
|
|
- console.log('✅ 子弹已添加到GameArea');
|
|
|
- } else {
|
|
|
- console.error('❌ 找不到GameArea节点');
|
|
|
- // 尝试添加到Canvas
|
|
|
- const canvas = find('Canvas');
|
|
|
- if (canvas) {
|
|
|
- canvas.addChild(bullet);
|
|
|
- console.log('✅ 子弹已添加到Canvas(备用方案)');
|
|
|
- } else {
|
|
|
- console.error('❌ 找不到Canvas节点,子弹创建失败');
|
|
|
- bullet.destroy();
|
|
|
- return null;
|
|
|
- }
|
|
|
- }
|
|
|
+ public setFirePosition(position: Vec3) {
|
|
|
+ this.firePosition = position.clone();
|
|
|
+ this.needsPhysicsSetup = true;
|
|
|
+ console.log('🎯 子弹发射位置已设置:', this.firePosition);
|
|
|
+ }
|
|
|
+
|
|
|
+ start() {
|
|
|
+ console.log('🚀 子弹start()开始...');
|
|
|
|
|
|
- // 设置子弹初始位置
|
|
|
- try {
|
|
|
- if (gameArea) {
|
|
|
- // 将世界坐标转换为GameArea的本地坐标
|
|
|
- const gameAreaTransform = gameArea.getComponent(UITransform);
|
|
|
- if (gameAreaTransform) {
|
|
|
- const localPos = gameAreaTransform.convertToNodeSpaceAR(firePosition);
|
|
|
- bullet.position = localPos;
|
|
|
- console.log('✅ 子弹位置已设置:', {
|
|
|
- 世界坐标: firePosition,
|
|
|
- 本地坐标: localPos
|
|
|
- });
|
|
|
- } else {
|
|
|
- console.error('❌ GameArea缺少UITransform组件');
|
|
|
- bullet.position = new Vec3(0, 0, 0);
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 如果添加到Canvas,直接使用世界坐标
|
|
|
- bullet.worldPosition = firePosition;
|
|
|
- console.log('✅ 子弹世界位置已设置:', firePosition);
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('❌ 设置子弹位置失败:', error);
|
|
|
- bullet.position = new Vec3(0, 0, 0);
|
|
|
- }
|
|
|
+ // 设置生命周期
|
|
|
+ this.lifeTimer = this.lifetime;
|
|
|
|
|
|
- // 获取子弹控制器组件并初始化
|
|
|
- const bulletController = bullet.getComponent(BulletController);
|
|
|
- if (bulletController) {
|
|
|
- console.log('✅ 获取到子弹控制器组件');
|
|
|
- // 设置子弹属性(如果需要的话)
|
|
|
- // bulletController.damage = 10; // 可以根据武器类型调整
|
|
|
-
|
|
|
- // 确保节点完全加入场景树后再初始化
|
|
|
- console.log('⏰ 延迟初始化子弹目标,确保节点完全加载...');
|
|
|
- bulletController.scheduleOnce(() => {
|
|
|
- console.log('⏰ 开始延迟初始化子弹目标');
|
|
|
- try {
|
|
|
- // 再次检查节点状态
|
|
|
- if (bullet.isValid && bullet.active) {
|
|
|
- console.log('✅ 子弹节点状态正常,开始初始化目标');
|
|
|
- bulletController.initializeTarget();
|
|
|
- } else {
|
|
|
- console.error('❌ 子弹节点状态异常:', {
|
|
|
- 有效: bullet.isValid,
|
|
|
- 激活: bullet.active
|
|
|
- });
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('❌ 延迟初始化子弹目标失败:', error);
|
|
|
- }
|
|
|
+ // 如果需要设置物理属性,延迟处理
|
|
|
+ if (this.needsPhysicsSetup) {
|
|
|
+ // 使用更短的延迟,确保物理世界准备好
|
|
|
+ this.scheduleOnce(() => {
|
|
|
+ this.setupPhysics();
|
|
|
}, 0.05);
|
|
|
- } else {
|
|
|
- console.error('❌ 子弹预制体缺少BulletController组件');
|
|
|
- console.log('子弹组件列表:', bullet.components.map(c => c.constructor.name));
|
|
|
}
|
|
|
|
|
|
- console.log('🎯 子弹发射完成,将自动寻找并攻击最近的敌人');
|
|
|
- console.log('🔫 === 静态子弹发射系统结束 ===');
|
|
|
-
|
|
|
- return bullet;
|
|
|
+ console.log('✅ 子弹start()完成');
|
|
|
}
|
|
|
-
|
|
|
- start() {
|
|
|
- console.log('🚀 子弹开始初始化...');
|
|
|
-
|
|
|
- // 调试碰撞组信息
|
|
|
- this.debugCollisionGroups();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置物理属性
|
|
|
+ */
|
|
|
+ private setupPhysics() {
|
|
|
+ console.log('🔧 开始设置子弹物理属性...');
|
|
|
|
|
|
- // 获取刚体组件 - 直接从根节点获取,不是从Icon子节点
|
|
|
+ // 获取物理组件
|
|
|
this.rigidBody = this.node.getComponent(RigidBody2D);
|
|
|
+ const collider = this.node.getComponent(Collider2D);
|
|
|
+
|
|
|
if (!this.rigidBody) {
|
|
|
- console.error('❌ 子弹找不到RigidBody2D组件');
|
|
|
+ console.error('❌ 子弹预制体缺少RigidBody2D组件');
|
|
|
return;
|
|
|
}
|
|
|
- console.log('✅ 子弹刚体组件获取成功');
|
|
|
|
|
|
- // 获取碰撞体组件
|
|
|
- const collider = this.node.getComponent(Collider2D);
|
|
|
if (!collider) {
|
|
|
- console.error('❌ 子弹找不到Collider2D组件');
|
|
|
+ console.error('❌ 子弹预制体缺少Collider2D组件');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // 🔧 修复碰撞组设置 - 确保子弹在正确的碰撞组中
|
|
|
- const correctBulletGroup = 3; // BULLET组
|
|
|
- if (collider.group !== correctBulletGroup) {
|
|
|
- console.log(`🔧 修复子弹碰撞组: ${collider.group} -> ${correctBulletGroup}`);
|
|
|
- collider.group = correctBulletGroup;
|
|
|
- }
|
|
|
- if (this.rigidBody.group !== correctBulletGroup) {
|
|
|
- console.log(`🔧 修复子弹刚体组: ${this.rigidBody.group} -> ${correctBulletGroup}`);
|
|
|
- this.rigidBody.group = correctBulletGroup;
|
|
|
- }
|
|
|
-
|
|
|
- // 重置刚体的预设速度
|
|
|
- this.rigidBody.linearVelocity = new Vec2(0, 0);
|
|
|
- console.log('🔧 已重置刚体预设速度');
|
|
|
+ console.log('✅ 物理组件获取成功');
|
|
|
|
|
|
- // 设置刚体参数
|
|
|
+ // 确保物理组件设置正确
|
|
|
+ this.rigidBody.enabledContactListener = true;
|
|
|
this.rigidBody.gravityScale = 0; // 不受重力影响
|
|
|
this.rigidBody.linearDamping = 0; // 无阻尼
|
|
|
this.rigidBody.angularDamping = 0; // 无角阻尼
|
|
|
- this.rigidBody.allowSleep = false; // 不允许休眠
|
|
|
|
|
|
- // 设置子弹生命周期
|
|
|
- this.lifeTimer = this.lifetime;
|
|
|
+ // 绑定碰撞事件
|
|
|
+ collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
|
|
|
+ console.log('✅ 碰撞事件已绑定');
|
|
|
|
|
|
- // 注册碰撞事件 - 直接从根节点获取碰撞体组件
|
|
|
- if (collider) {
|
|
|
- collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
|
|
|
- console.log('✅ 子弹碰撞事件已注册');
|
|
|
- } else {
|
|
|
- console.error('❌ 子弹找不到Collider2D组件');
|
|
|
+ // 设置子弹位置
|
|
|
+ if (this.firePosition) {
|
|
|
+ this.setPositionInGameArea();
|
|
|
}
|
|
|
|
|
|
- console.log('🚀 子弹初始化完成');
|
|
|
+ // 初始化方向和速度
|
|
|
+ this.initializeDirection();
|
|
|
+ this.isInitialized = true;
|
|
|
+
|
|
|
+ console.log('✅ 子弹物理属性设置完成');
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 公开方法:初始化子弹目标(由静态发射方法调用)
|
|
|
+ * 在GameArea中设置子弹位置
|
|
|
*/
|
|
|
- public initializeTarget() {
|
|
|
- console.log('🎯 开始初始化子弹目标...');
|
|
|
- console.log('🔍 当前节点状态:', {
|
|
|
- 节点名称: this.node.name,
|
|
|
- 节点有效: this.node.isValid,
|
|
|
- 节点激活: this.node.active,
|
|
|
- 父节点: this.node.parent ? this.node.parent.name : '无'
|
|
|
- });
|
|
|
-
|
|
|
- // 延迟一帧后再设置方向和速度,确保节点完全初始化
|
|
|
- console.log('⏰ 设置延迟调用 initializeBulletDirection...');
|
|
|
- this.scheduleOnce(() => {
|
|
|
- console.log('⏰ 延迟调用触发,开始执行 initializeBulletDirection');
|
|
|
- try {
|
|
|
- this.initializeBulletDirection();
|
|
|
- } catch (error) {
|
|
|
- console.error('❌ initializeBulletDirection 执行出错:', error);
|
|
|
- }
|
|
|
- }, 0.02);
|
|
|
-
|
|
|
- // 添加备用方案:如果延迟调用失败,直接调用
|
|
|
- this.scheduleOnce(() => {
|
|
|
- console.log('🔍 检查延迟调用是否成功...');
|
|
|
- if (!this.direction) {
|
|
|
- console.warn('⚠️ 延迟调用可能失败,直接调用 initializeBulletDirection');
|
|
|
- try {
|
|
|
- this.initializeBulletDirection();
|
|
|
- } catch (error) {
|
|
|
- console.error('❌ 备用调用也失败:', error);
|
|
|
- }
|
|
|
- } else {
|
|
|
- console.log('✅ 延迟调用成功,子弹方向已设置');
|
|
|
+ private setPositionInGameArea() {
|
|
|
+ const gameArea = find('Canvas/GameLevelUI/GameArea');
|
|
|
+ if (gameArea) {
|
|
|
+ const gameAreaTransform = gameArea.getComponent(UITransform);
|
|
|
+ if (gameAreaTransform) {
|
|
|
+ const localPos = gameAreaTransform.convertToNodeSpaceAR(this.firePosition);
|
|
|
+ this.node.position = localPos;
|
|
|
+ console.log('✅ 子弹位置已设置:', localPos);
|
|
|
}
|
|
|
- }, 0.1);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // 初始化子弹方向
|
|
|
- private initializeBulletDirection() {
|
|
|
- console.log('🎯 === initializeBulletDirection 方法开始执行 ===');
|
|
|
+ /**
|
|
|
+ * 初始化子弹方向
|
|
|
+ */
|
|
|
+ private initializeDirection() {
|
|
|
console.log('🎯 开始初始化子弹方向...');
|
|
|
- console.log('🔍 子弹节点状态检查:', {
|
|
|
- 节点名称: this.node.name,
|
|
|
- 节点有效: this.node.isValid,
|
|
|
- 节点激活: this.node.active,
|
|
|
- 刚体组件: !!this.rigidBody,
|
|
|
- 当前位置: this.node.position,
|
|
|
- 世界位置: this.node.worldPosition
|
|
|
- });
|
|
|
|
|
|
// 寻找最近的敌人
|
|
|
this.findNearestEnemy();
|
|
|
|
|
|
- // 设置初始方向
|
|
|
+ // 设置方向
|
|
|
if (this.targetEnemy) {
|
|
|
- console.log('🎯 设置子弹朝向目标敌人');
|
|
|
this.setDirectionToTarget();
|
|
|
} else {
|
|
|
- // 没有敌人目标时,设置随机方向
|
|
|
- console.log('🎲 没有敌人目标,设置随机发射方向');
|
|
|
+ // 随机方向
|
|
|
const randomAngle = Math.random() * Math.PI * 2;
|
|
|
this.direction = new Vec3(Math.cos(randomAngle), Math.sin(randomAngle), 0);
|
|
|
- console.log('随机方向:', this.direction);
|
|
|
+ console.log('🎲 使用随机方向');
|
|
|
}
|
|
|
|
|
|
- // 设置初始速度
|
|
|
- this.updateVelocity();
|
|
|
-
|
|
|
- // 再次确认速度设置
|
|
|
- this.scheduleOnce(() => {
|
|
|
- if (this.rigidBody) {
|
|
|
- const currentVelocity = this.rigidBody.linearVelocity;
|
|
|
- console.log('🔍 最终确认子弹速度:', currentVelocity);
|
|
|
- const speed = Math.sqrt(currentVelocity.x * currentVelocity.x + currentVelocity.y * currentVelocity.y);
|
|
|
- console.log('🔍 速度大小:', speed);
|
|
|
-
|
|
|
- if (speed < 50) {
|
|
|
- console.warn('⚠️ 子弹速度过低,重新设置...');
|
|
|
- this.updateVelocity();
|
|
|
- }
|
|
|
- }
|
|
|
- }, 0.1);
|
|
|
+ // 应用速度
|
|
|
+ this.applyVelocity();
|
|
|
}
|
|
|
|
|
|
// 寻找最近的敌人
|
|
|
private findNearestEnemy() {
|
|
|
- console.log('🔍 子弹开始寻找最近的敌人...');
|
|
|
-
|
|
|
- // 主要路径:Canvas/GameLevelUI/enemyContainer
|
|
|
- const primaryPath = 'Canvas/GameLevelUI/enemyContainer';
|
|
|
- let enemyContainer = find(primaryPath);
|
|
|
-
|
|
|
- if (enemyContainer) {
|
|
|
- console.log('✅ 找到主要敌人容器:', primaryPath);
|
|
|
- } else {
|
|
|
- console.log('❌ 主要路径未找到,尝试备用路径...');
|
|
|
- // 备用路径
|
|
|
- const backupPaths = [
|
|
|
- 'Canvas/enemyContainer',
|
|
|
- 'Canvas/EnemyContainer',
|
|
|
- 'Canvas/GameLevelUI/EnemyContainer',
|
|
|
- 'Canvas/GameArea/enemyContainer'
|
|
|
- ];
|
|
|
-
|
|
|
- for (const path of backupPaths) {
|
|
|
- enemyContainer = find(path);
|
|
|
- if (enemyContainer) {
|
|
|
- console.log('✅ 找到备用敌人容器:', path);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
+ const enemyContainer = find('Canvas/GameLevelUI/enemyContainer');
|
|
|
if (!enemyContainer) {
|
|
|
- console.log('❌ 未找到任何敌人容器,尝试在整个场景中搜索敌人节点...');
|
|
|
- // 如果找不到容器,尝试直接搜索敌人节点
|
|
|
- const allEnemies = this.findAllEnemyNodes();
|
|
|
- if (allEnemies.length > 0) {
|
|
|
- this.selectNearestFromEnemies(allEnemies);
|
|
|
- } else {
|
|
|
- console.log('❌ 场景中没有找到任何敌人,子弹将随机发射');
|
|
|
- }
|
|
|
+ console.log('❌ 未找到敌人容器');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // 打印容器详细信息
|
|
|
- console.log('敌人容器详细信息:', {
|
|
|
- name: enemyContainer.name,
|
|
|
- childrenCount: enemyContainer.children.length,
|
|
|
- children: enemyContainer.children.map(child => ({
|
|
|
- name: child.name,
|
|
|
- active: child.active,
|
|
|
- hasEnemyInstance: !!child.getComponent('EnemyInstance'),
|
|
|
- hasEnemyComponent: !!child.getComponent('EnemyComponent')
|
|
|
- }))
|
|
|
- });
|
|
|
-
|
|
|
- // 在容器中查找敌人 - 使用更宽泛的匹配条件
|
|
|
- const enemies = enemyContainer.children.filter(child => {
|
|
|
- if (!child.active) return false; // 跳过非激活的节点
|
|
|
-
|
|
|
- const name = child.name.toLowerCase();
|
|
|
- const hasEnemyComponent = child.getComponent('EnemyInstance') !== null ||
|
|
|
- child.getComponent('EnemyComponent') !== null;
|
|
|
-
|
|
|
- return name === 'enemy' ||
|
|
|
- name.includes('enemy') ||
|
|
|
- name.includes('敌人') ||
|
|
|
- hasEnemyComponent ||
|
|
|
- name.startsWith('enemy') ||
|
|
|
- name.endsWith('enemy');
|
|
|
- });
|
|
|
-
|
|
|
- console.log(`在容器中找到 ${enemies.length} 个敌人:`, enemies.map(e => ({
|
|
|
- name: e.name,
|
|
|
- position: e.worldPosition
|
|
|
- })));
|
|
|
+ const enemies = enemyContainer.children.filter(child =>
|
|
|
+ child.active &&
|
|
|
+ (child.name.toLowerCase().includes('enemy') ||
|
|
|
+ child.getComponent('EnemyInstance') !== null)
|
|
|
+ );
|
|
|
|
|
|
if (enemies.length === 0) {
|
|
|
- console.log('❌ 敌人容器中没有找到敌人,尝试将所有子节点作为潜在目标...');
|
|
|
- // 如果没有找到明确的敌人,尝试将所有活动的子节点作为目标
|
|
|
- const allActiveChildren = enemyContainer.children.filter(child => child.active);
|
|
|
- if (allActiveChildren.length > 0) {
|
|
|
- console.log(`🔍 将 ${allActiveChildren.length} 个活动子节点作为潜在目标`);
|
|
|
- this.selectNearestFromEnemies(allActiveChildren);
|
|
|
- } else {
|
|
|
- console.log('❌ 容器中没有任何活动节点,子弹将随机发射');
|
|
|
- }
|
|
|
+ console.log('❌ 没有找到敌人');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- this.selectNearestFromEnemies(enemies);
|
|
|
- }
|
|
|
-
|
|
|
- // 在整个场景中搜索敌人节点
|
|
|
- private findAllEnemyNodes(): Node[] {
|
|
|
- const canvas = find('Canvas');
|
|
|
- if (!canvas) return [];
|
|
|
-
|
|
|
- const enemies: Node[] = [];
|
|
|
- this.searchEnemiesRecursive(canvas, enemies);
|
|
|
- console.log(`在整个场景中找到 ${enemies.length} 个敌人`);
|
|
|
- return enemies;
|
|
|
- }
|
|
|
-
|
|
|
- // 递归搜索敌人节点
|
|
|
- private searchEnemiesRecursive(node: Node, enemies: Node[]) {
|
|
|
- // 检查当前节点是否为敌人
|
|
|
- if (node.active) {
|
|
|
- const name = node.name.toLowerCase();
|
|
|
- const hasEnemyComponent = node.getComponent('EnemyInstance') !== null ||
|
|
|
- node.getComponent('EnemyComponent') !== null;
|
|
|
-
|
|
|
- if (name === 'enemy' ||
|
|
|
- name.includes('enemy') ||
|
|
|
- name.includes('敌人') ||
|
|
|
- hasEnemyComponent ||
|
|
|
- name.startsWith('enemy') ||
|
|
|
- name.endsWith('enemy')) {
|
|
|
- enemies.push(node);
|
|
|
- console.log(`🎯 找到敌人节点: ${node.name} (路径: ${this.getNodePath(node)})`);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 递归搜索子节点
|
|
|
- for (const child of node.children) {
|
|
|
- this.searchEnemiesRecursive(child, enemies);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 获取节点的完整路径
|
|
|
- private getNodePath(node: Node): string {
|
|
|
- let path = node.name;
|
|
|
- let current = node;
|
|
|
-
|
|
|
- while (current.parent) {
|
|
|
- current = current.parent;
|
|
|
- path = current.name + '/' + path;
|
|
|
- }
|
|
|
-
|
|
|
- return path;
|
|
|
- }
|
|
|
-
|
|
|
- // 从敌人列表中选择最近的
|
|
|
- private selectNearestFromEnemies(enemies: Node[]) {
|
|
|
let nearestEnemy: Node = null;
|
|
|
let nearestDistance = Infinity;
|
|
|
-
|
|
|
const bulletPos = this.node.worldPosition;
|
|
|
- console.log('子弹当前位置:', bulletPos);
|
|
|
|
|
|
for (const enemy of enemies) {
|
|
|
- try {
|
|
|
- const enemyPos = enemy.worldPosition;
|
|
|
- const distance = Vec3.distance(bulletPos, enemyPos);
|
|
|
- console.log(`敌人 ${enemy.name} 距离: ${distance.toFixed(1)}`);
|
|
|
-
|
|
|
- if (distance < nearestDistance) {
|
|
|
- nearestDistance = distance;
|
|
|
- nearestEnemy = enemy;
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('计算敌人距离时出错:', error);
|
|
|
+ const distance = Vec3.distance(bulletPos, enemy.worldPosition);
|
|
|
+ if (distance < nearestDistance) {
|
|
|
+ nearestDistance = distance;
|
|
|
+ nearestEnemy = enemy;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (nearestEnemy) {
|
|
|
this.targetEnemy = nearestEnemy;
|
|
|
- console.log(`✅ 选定目标敌人: ${nearestEnemy.name}, 距离: ${nearestDistance.toFixed(1)}`);
|
|
|
- } else {
|
|
|
- console.log('❌ 无法确定最近敌人,子弹将随机发射');
|
|
|
+ console.log(`🎯 锁定目标: ${nearestEnemy.name}`);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -456,255 +178,89 @@ export class BulletController extends Component {
|
|
|
private setDirectionToTarget() {
|
|
|
if (!this.targetEnemy) return;
|
|
|
|
|
|
- try {
|
|
|
- const targetPos = this.targetEnemy.worldPosition;
|
|
|
- const currentPos = this.node.worldPosition;
|
|
|
-
|
|
|
- console.log('子弹位置:', currentPos);
|
|
|
- console.log('目标敌人位置:', targetPos);
|
|
|
-
|
|
|
- this.direction = targetPos.subtract(currentPos).normalize();
|
|
|
- console.log('计算出的发射方向:', this.direction);
|
|
|
- } catch (error) {
|
|
|
- console.error('❌ 计算子弹方向时出错:', error);
|
|
|
- // 备用方案:随机方向
|
|
|
- const randomAngle = Math.random() * Math.PI * 2;
|
|
|
- this.direction = new Vec3(Math.cos(randomAngle), Math.sin(randomAngle), 0);
|
|
|
- console.log('使用备用随机方向:', this.direction);
|
|
|
- }
|
|
|
+ const targetPos = this.targetEnemy.worldPosition;
|
|
|
+ const currentPos = this.node.worldPosition;
|
|
|
+
|
|
|
+ this.direction = targetPos.subtract(currentPos).normalize();
|
|
|
+ console.log('🎯 方向已设置,朝向目标敌人');
|
|
|
}
|
|
|
|
|
|
- // 更新子弹速度方向
|
|
|
- updateVelocity() {
|
|
|
- if (!this.rigidBody) {
|
|
|
- console.error('❌ 刚体组件不存在,无法设置速度');
|
|
|
+ // 应用速度
|
|
|
+ private applyVelocity() {
|
|
|
+ if (!this.rigidBody || !this.direction) {
|
|
|
+ console.error('❌ 无法应用速度:缺少刚体或方向');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (!this.direction) {
|
|
|
- console.error('❌ 子弹方向未设置,无法更新速度');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 确保方向向量已归一化
|
|
|
- const normalizedDirection = this.direction.clone().normalize();
|
|
|
-
|
|
|
- // 计算速度向量
|
|
|
const velocity = new Vec2(
|
|
|
- normalizedDirection.x * this.speed,
|
|
|
- normalizedDirection.y * this.speed
|
|
|
+ this.direction.x * this.speed,
|
|
|
+ this.direction.y * this.speed
|
|
|
);
|
|
|
|
|
|
- console.log('🎯 设置子弹速度:', {
|
|
|
- direction: normalizedDirection,
|
|
|
- speed: this.speed,
|
|
|
- velocity: velocity
|
|
|
- });
|
|
|
-
|
|
|
- // 强制设置速度
|
|
|
this.rigidBody.linearVelocity = velocity;
|
|
|
-
|
|
|
- // 立即验证速度是否设置成功
|
|
|
- const actualVelocity = this.rigidBody.linearVelocity;
|
|
|
- const actualSpeed = Math.sqrt(actualVelocity.x * actualVelocity.x + actualVelocity.y * actualVelocity.y);
|
|
|
-
|
|
|
- console.log('✅ 子弹速度验证:', {
|
|
|
- 设置的速度: velocity,
|
|
|
- 实际速度: actualVelocity,
|
|
|
- 速度大小: actualSpeed.toFixed(2)
|
|
|
- });
|
|
|
-
|
|
|
- if (Math.abs(actualSpeed - this.speed) > 10) {
|
|
|
- console.warn('⚠️ 速度设置可能失败,实际速度与期望不符');
|
|
|
- // 尝试再次设置
|
|
|
- this.scheduleOnce(() => {
|
|
|
- this.rigidBody.linearVelocity = velocity;
|
|
|
- console.log('🔄 重新设置子弹速度');
|
|
|
- }, 0.01);
|
|
|
- }
|
|
|
+ console.log('✅ 子弹速度已应用:', velocity);
|
|
|
}
|
|
|
|
|
|
// 碰撞检测
|
|
|
onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
|
|
|
const otherNode = otherCollider.node;
|
|
|
+ console.log('💥 子弹碰撞:', otherNode.name);
|
|
|
|
|
|
- console.log('💥 子弹发生碰撞:', {
|
|
|
- 子弹: selfCollider.node.name,
|
|
|
- 碰撞对象: otherNode.name,
|
|
|
- 碰撞对象路径: this.getNodePath(otherNode),
|
|
|
- 碰撞组: otherCollider.group
|
|
|
- });
|
|
|
-
|
|
|
- // 检查是否击中敌人 - 使用更灵活的匹配条件
|
|
|
- const isEnemy = this.isEnemyNode(otherNode);
|
|
|
-
|
|
|
- if (isEnemy) {
|
|
|
- console.log('🎯 子弹击中敌人:', otherNode.name);
|
|
|
-
|
|
|
- // 尝试对敌人造成伤害
|
|
|
+ // 检查是否击中敌人
|
|
|
+ if (this.isEnemyNode(otherNode)) {
|
|
|
+ console.log('🎯 击中敌人:', otherNode.name);
|
|
|
this.damageEnemy(otherNode);
|
|
|
-
|
|
|
- // 销毁子弹
|
|
|
- console.log('💥 子弹击中目标,即将销毁');
|
|
|
this.node.destroy();
|
|
|
- } else {
|
|
|
- console.log('🚫 子弹击中非敌人对象:', otherNode.name);
|
|
|
- // 如果击中墙体或其他障碍物,也销毁子弹
|
|
|
- if (otherNode.name.includes('Wall') || otherNode.name.includes('wall')) {
|
|
|
- console.log('🧱 子弹击中墙体,销毁子弹');
|
|
|
- this.node.destroy();
|
|
|
- }
|
|
|
+ } else if (otherNode.name.toLowerCase().includes('wall')) {
|
|
|
+ console.log('🧱 击中墙体,销毁子弹');
|
|
|
+ this.node.destroy();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 判断节点是否为敌人
|
|
|
+ // 判断是否为敌人节点
|
|
|
private isEnemyNode(node: Node): boolean {
|
|
|
const name = node.name.toLowerCase();
|
|
|
- const hasEnemyComponent = node.getComponent('EnemyInstance') !== null ||
|
|
|
- node.getComponent('EnemyComponent') !== null;
|
|
|
-
|
|
|
- const isEnemyByName = name === 'enemy' ||
|
|
|
- name.includes('enemy') ||
|
|
|
- name.includes('敌人') ||
|
|
|
- name.startsWith('enemy') ||
|
|
|
- name.endsWith('enemy');
|
|
|
-
|
|
|
- console.log('🔍 敌人判定:', {
|
|
|
- 节点名称: node.name,
|
|
|
- 名称匹配: isEnemyByName,
|
|
|
- 组件匹配: hasEnemyComponent,
|
|
|
- 最终判定: isEnemyByName || hasEnemyComponent
|
|
|
- });
|
|
|
-
|
|
|
- return isEnemyByName || hasEnemyComponent;
|
|
|
+ return name.includes('enemy') ||
|
|
|
+ name.includes('敌人') ||
|
|
|
+ node.getComponent('EnemyInstance') !== null ||
|
|
|
+ node.getComponent('EnemyComponent') !== null;
|
|
|
}
|
|
|
|
|
|
// 对敌人造成伤害
|
|
|
private damageEnemy(enemyNode: Node) {
|
|
|
- console.log('⚔️ 开始对敌人造成伤害:', enemyNode.name);
|
|
|
+ console.log('⚔️ 对敌人造成伤害:', this.damage);
|
|
|
|
|
|
- // 方法1: 尝试通过EnemyController造成伤害
|
|
|
- const enemyController = find('Canvas/GameLevelUI/EnemyController')?.getComponent('EnemyController') as any;
|
|
|
- if (enemyController && typeof enemyController.damageEnemy === 'function') {
|
|
|
- try {
|
|
|
- enemyController.damageEnemy(enemyNode, this.damage);
|
|
|
- console.log('✅ 通过EnemyController造成伤害:', this.damage);
|
|
|
- return;
|
|
|
- } catch (error) {
|
|
|
- console.error('❌ EnemyController.damageEnemy调用失败:', error);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 方法2: 尝试直接调用敌人组件的受伤方法
|
|
|
+ // 尝试调用敌人的受伤方法
|
|
|
const enemyInstance = enemyNode.getComponent('EnemyInstance') as any;
|
|
|
if (enemyInstance) {
|
|
|
if (typeof enemyInstance.takeDamage === 'function') {
|
|
|
- try {
|
|
|
- enemyInstance.takeDamage(this.damage);
|
|
|
- console.log('✅ 通过EnemyInstance.takeDamage造成伤害:', this.damage);
|
|
|
- return;
|
|
|
- } catch (error) {
|
|
|
- console.error('❌ EnemyInstance.takeDamage调用失败:', error);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (typeof enemyInstance.damage === 'function') {
|
|
|
- try {
|
|
|
- enemyInstance.damage(this.damage);
|
|
|
- console.log('✅ 通过EnemyInstance.damage造成伤害:', this.damage);
|
|
|
- return;
|
|
|
- } catch (error) {
|
|
|
- console.error('❌ EnemyInstance.damage调用失败:', error);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 方法3: 尝试其他可能的敌人组件
|
|
|
- const enemyComponent = enemyNode.getComponent('EnemyComponent') as any;
|
|
|
- if (enemyComponent && typeof enemyComponent.takeDamage === 'function') {
|
|
|
- try {
|
|
|
- enemyComponent.takeDamage(this.damage);
|
|
|
- console.log('✅ 通过EnemyComponent.takeDamage造成伤害:', this.damage);
|
|
|
+ enemyInstance.takeDamage(this.damage);
|
|
|
return;
|
|
|
- } catch (error) {
|
|
|
- console.error('❌ EnemyComponent.takeDamage调用失败:', error);
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- // 方法4: 简单的生命值减少(如果敌人有health属性)
|
|
|
- if (enemyInstance && typeof enemyInstance.health === 'number') {
|
|
|
- enemyInstance.health -= this.damage;
|
|
|
- console.log('✅ 直接减少敌人生命值:', {
|
|
|
- 伤害: this.damage,
|
|
|
- 剩余生命: enemyInstance.health
|
|
|
- });
|
|
|
-
|
|
|
- // 如果生命值归零,尝试销毁敌人
|
|
|
- if (enemyInstance.health <= 0) {
|
|
|
- console.log('💀 敌人生命值归零,尝试销毁');
|
|
|
- if (typeof enemyInstance.die === 'function') {
|
|
|
- enemyInstance.die();
|
|
|
- } else {
|
|
|
+ if (typeof enemyInstance.health === 'number') {
|
|
|
+ enemyInstance.health -= this.damage;
|
|
|
+ if (enemyInstance.health <= 0) {
|
|
|
enemyNode.destroy();
|
|
|
}
|
|
|
+ return;
|
|
|
}
|
|
|
- return;
|
|
|
}
|
|
|
|
|
|
- console.warn('⚠️ 无法找到合适的方法对敌人造成伤害');
|
|
|
- console.log('敌人节点信息:', {
|
|
|
- name: enemyNode.name,
|
|
|
- components: enemyNode.components.map(c => c.constructor.name),
|
|
|
- EnemyInstance: !!enemyNode.getComponent('EnemyInstance'),
|
|
|
- EnemyComponent: !!enemyNode.getComponent('EnemyComponent')
|
|
|
- });
|
|
|
+ // 备用方案:通过EnemyController
|
|
|
+ const enemyController = find('Canvas/GameLevelUI/EnemyController')?.getComponent('EnemyController') as any;
|
|
|
+ if (enemyController && typeof enemyController.damageEnemy === 'function') {
|
|
|
+ enemyController.damageEnemy(enemyNode, this.damage);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
update(dt: number) {
|
|
|
- // 更新生命周期
|
|
|
+ // 只有在初始化完成后才进行更新
|
|
|
+ if (!this.isInitialized) return;
|
|
|
+
|
|
|
+ // 生命周期倒计时
|
|
|
this.lifeTimer -= dt;
|
|
|
if (this.lifeTimer <= 0) {
|
|
|
this.node.destroy();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 定期检查速度是否正确(每0.1秒检查一次)
|
|
|
- if (this.rigidBody && this.direction) {
|
|
|
- const currentVelocity = this.rigidBody.linearVelocity;
|
|
|
- const currentSpeed = Math.sqrt(currentVelocity.x * currentVelocity.x + currentVelocity.y * currentVelocity.y);
|
|
|
-
|
|
|
- // 如果速度过低,重新设置
|
|
|
- if (currentSpeed < this.speed * 0.8) {
|
|
|
- console.log('🔧 检测到子弹速度过低,重新设置...');
|
|
|
- this.updateVelocity();
|
|
|
- }
|
|
|
}
|
|
|
-
|
|
|
- // 注意:不再每帧更新速度方向,子弹将沿初始方向直线移动
|
|
|
- }
|
|
|
-
|
|
|
- // 调试碰撞组信息
|
|
|
- private debugCollisionGroups() {
|
|
|
- // 直接从根节点获取组件,不再寻找Icon子节点
|
|
|
- const collider = this.node.getComponent(Collider2D);
|
|
|
- const rigidBody = this.node.getComponent(RigidBody2D);
|
|
|
-
|
|
|
- console.log('🔧 子弹碰撞组调试信息:', {
|
|
|
- 节点名称: this.node.name,
|
|
|
- 碰撞体组: collider?.group,
|
|
|
- 刚体组: rigidBody?.group,
|
|
|
- 预期组: 3, // BULLET组
|
|
|
- 组名: 'BULLET'
|
|
|
- });
|
|
|
-
|
|
|
- // 检查碰撞矩阵
|
|
|
- const bulletGroup = 3; // BULLET
|
|
|
- const enemyGroup = 4; // ENEMY
|
|
|
-
|
|
|
- console.log('🎯 碰撞组配置:', {
|
|
|
- 子弹组: bulletGroup,
|
|
|
- 敌人组: enemyGroup,
|
|
|
- 提示: '子弹组3应该能与敌人组4碰撞'
|
|
|
- });
|
|
|
}
|
|
|
-}
|
|
|
+}
|