import { _decorator, Component, Node, Vec2, Vec3, UITransform, Collider2D, Contact2DType, IPhysics2DContact, RigidBody2D, Prefab, instantiate, find, CircleCollider2D, PhysicsSystem2D } from 'cc'; import { BulletController } from './BulletController'; import { WeaponBlockExample } from './WeaponBlockExample'; const { ccclass, property } = _decorator; @ccclass('BallController') export class BallController extends Component { // 球的预制体 @property({ type: Prefab, tooltip: '拖拽Ball预制体到这里' }) public ballPrefab: Prefab = null; // 已放置方块容器节点 @property({ type: Node, tooltip: '拖拽PlacedBlocks节点到这里(Canvas/GameLevelUI/PlacedBlocks)' }) public placedBlocksContainer: Node = null; // 球的移动速度 @property public speed: number = 10; // 反弹随机偏移最大角度(弧度) @property public maxReflectionRandomness: number = 0.2; // 当前活动的球 private activeBall: Node = null; // 球的方向向量 private direction: Vec2 = new Vec2(); // GameArea区域边界 private gameBounds = { left: 0, right: 0, top: 0, bottom: 0 }; // 球的半径 private radius: number = 0; // 是否已初始化 private initialized: boolean = false; // 子弹预制体 @property({ type: Prefab, tooltip: '拖拽子弹预制体到这里' }) public bulletPrefab: Prefab = null; // 小球是否已开始运动 private ballStarted: boolean = false; // 在类字段区添加 private blockFireCooldown: Map = new Map(); private FIRE_COOLDOWN = 0.05; start() { // 如果没有指定placedBlocksContainer,尝试找到它 if (!this.placedBlocksContainer) { this.placedBlocksContainer = find('Canvas/GameLevelUI/PlacedBlocks'); if (!this.placedBlocksContainer) { console.warn('找不到PlacedBlocks节点,某些功能可能无法正常工作'); } } // 只进行初始设置,不创建小球 console.log('🎮 BallController 已准备就绪,等待确定按钮触发'); this.calculateGameBounds(); // 延迟执行一些检查方法,但不创建小球 this.scheduleOnce(() => { this.logCollisionMatrix(); }, 1.0); } // 计算游戏边界(使用GameArea节点) calculateGameBounds() { // 获取GameArea节点 const gameArea = find('Canvas/GameLevelUI/GameArea'); if (!gameArea) { console.error('找不到GameArea节点'); return; } const gameAreaUI = gameArea.getComponent(UITransform); if (!gameAreaUI) { console.error('GameArea节点没有UITransform组件'); return; } // 获取GameArea的尺寸 const areaWidth = gameAreaUI.width; const areaHeight = gameAreaUI.height; // 获取GameArea的世界坐标位置 const worldPos = gameArea.worldPosition; // 计算GameArea的世界坐标边界 this.gameBounds.left = worldPos.x - areaWidth / 2; this.gameBounds.right = worldPos.x + areaWidth / 2; this.gameBounds.bottom = worldPos.y - areaHeight / 2; this.gameBounds.top = worldPos.y + areaHeight / 2; console.log('GameArea Bounds:', this.gameBounds); } // 创建小球 createBall() { if (!this.ballPrefab) { console.error('未设置Ball预制体'); return; } // 如果已经有活动的球,先销毁它 if (this.activeBall && this.activeBall.isValid) { this.activeBall.destroy(); } // 实例化小球 this.activeBall = instantiate(this.ballPrefab); // 将小球添加到GameArea中 const gameArea = find('Canvas/GameLevelUI/GameArea'); if (gameArea) { gameArea.addChild(this.activeBall); } else { this.node.addChild(this.activeBall); } // 随机位置小球 this.positionBallRandomly(); // 设置球的半径 const transform = this.activeBall.getComponent(UITransform); if (transform) { this.radius = transform.width / 2; console.log('小球半径设置为:', this.radius); } else { this.radius = 25; // 默认半径 console.warn('无法获取小球UITransform组件,使用默认半径:', this.radius); } // 确保有碰撞组件 this.setupCollider(); // 注意:不在这里初始化方向,等待 startBall() 调用 console.log('⏸️ 小球已创建但暂未开始运动,等待启动指令'); this.initialized = true; console.log('小球创建完成:', { position: this.activeBall.position, radius: this.radius, initialized: this.initialized, hasMovement: false // 表示尚未开始运动 }); } // 检查所有已放置方块的碰撞体组件 private checkBlockColliders() { console.log('🔍 === 检查方块碰撞体组件 ==='); if (!this.placedBlocksContainer) { console.log('❌ PlacedBlocks容器未设置'); return; } if (!this.placedBlocksContainer.isValid) { console.log('❌ PlacedBlocks容器已失效'); return; } const blocks = []; for (let i = 0; i < this.placedBlocksContainer.children.length; i++) { const block = this.placedBlocksContainer.children[i]; if (block.name.includes('Block') || block.getChildByName('B1')) { blocks.push(block); } } console.log(`找到 ${blocks.length} 个方块`); let fixedCount = 0; for (let i = 0; i < blocks.length; i++) { const block = blocks[i]; console.log(`\n检查方块 ${i + 1}: ${block.name}`); console.log('方块路径:', this.getNodePath(block)); // 检查方块本身的碰撞体 const blockCollider = block.getComponent(Collider2D); console.log('方块有碰撞体:', !!blockCollider); if (blockCollider) { console.log('碰撞体类型:', blockCollider.constructor.name); console.log('碰撞组:', blockCollider.group); console.log('碰撞标签:', blockCollider.tag); console.log('是否为传感器:', blockCollider.sensor); // 🔧 自动修复碰撞组设置 if (blockCollider.group !== 2) { console.log(`🔧 修复方块碰撞组: ${blockCollider.group} -> 2`); blockCollider.group = 2; // 设置为Block组 fixedCount++; } // 确保不是传感器 if (blockCollider.sensor) { console.log(`🔧 修复方块传感器设置: true -> false`); blockCollider.sensor = false; } } // 检查B1子节点的碰撞体 const b1Node = block.getChildByName('B1'); if (b1Node) { const b1Collider = b1Node.getComponent(Collider2D); console.log('B1子节点有碰撞体:', !!b1Collider); if (b1Collider) { console.log('B1碰撞体类型:', b1Collider.constructor.name); console.log('B1碰撞组:', b1Collider.group); console.log('B1碰撞标签:', b1Collider.tag); console.log('B1是否为传感器:', b1Collider.sensor); // 🔧 修复B1子节点的碰撞设置 if (b1Collider.group !== 2) { console.log(`🔧 修复B1碰撞组: ${b1Collider.group} -> 2`); b1Collider.group = 2; // 设置为Block组 fixedCount++; } // 确保B1不是传感器(需要实际碰撞) if (b1Collider.sensor) { console.log(`🔧 修复B1传感器设置: true -> false`); b1Collider.sensor = false; } } } // 检查Weapon子节点 const weaponNode = this.findWeaponNode(block); console.log('方块有Weapon节点:', !!weaponNode); if (weaponNode) { console.log('Weapon节点路径:', this.getNodePath(weaponNode)); } } if (fixedCount > 0) { console.log(`✅ 已修复 ${fixedCount} 个碰撞组设置`); } console.log('🔍 === 方块碰撞体检查完成 ==='); } // 随机位置小球 positionBallRandomly() { if (!this.activeBall) return; const transform = this.activeBall.getComponent(UITransform); const ballRadius = transform ? transform.width / 2 : 25; // 计算可生成的范围(考虑小球半径,避免生成在边缘) const minX = this.gameBounds.left + ballRadius + 20; // 额外偏移,避免生成在边缘 const maxX = this.gameBounds.right - ballRadius - 20; const minY = this.gameBounds.bottom + ballRadius + 20; const maxY = this.gameBounds.top - ballRadius - 20; // 获取GameArea节点 const gameArea = find('Canvas/GameLevelUI/GameArea'); if (!gameArea) { console.error('找不到GameArea节点'); return; } // 查找PlacedBlocks节点,它包含所有放置的方块 if (!this.placedBlocksContainer) { console.log('找不到PlacedBlocks节点,使用默认随机位置'); this.setRandomPositionDefault(minX, maxX, minY, maxY); return; } if (!this.placedBlocksContainer.isValid) { console.log('PlacedBlocks容器已失效,使用默认随机位置'); this.setRandomPositionDefault(minX, maxX, minY, maxY); return; } // 获取所有已放置的方块 const placedBlocks = []; for (let i = 0; i < this.placedBlocksContainer.children.length; i++) { const block = this.placedBlocksContainer.children[i]; // 检查是否是方块节点(通常以Block命名或有特定标识) if (block.name.includes('Block') || block.getChildByName('B1')) { placedBlocks.push(block); } } console.log(`找到 ${placedBlocks.length} 个已放置的方块`); // 如果没有方块,使用默认随机位置 if (placedBlocks.length === 0) { this.setRandomPositionDefault(minX, maxX, minY, maxY); return; } // 尝试找到一个不与任何方块重叠的位置 let validPosition = false; let attempts = 0; const maxAttempts = 50; // 最大尝试次数 let randomX, randomY; while (!validPosition && attempts < maxAttempts) { // 随机生成位置 randomX = Math.random() * (maxX - minX) + minX; randomY = Math.random() * (maxY - minY) + minY; // 检查是否与任何方块重叠 let overlapping = false; for (const block of placedBlocks) { // 获取方块的世界坐标 const blockWorldPos = block.worldPosition; // 计算小球与方块的距离 const distance = Math.sqrt( Math.pow(randomX - blockWorldPos.x, 2) + Math.pow(randomY - blockWorldPos.y, 2) ); // 获取方块的尺寸 const blockTransform = block.getComponent(UITransform); const blockSize = blockTransform ? Math.max(blockTransform.width, blockTransform.height) / 2 : 50; // 如果距离小于小球半径+方块尺寸的一半+安全距离,认为重叠 const safeDistance = 20; // 额外安全距离 if (distance < ballRadius + blockSize + safeDistance) { overlapping = true; break; } } // 如果没有重叠,找到了有效位置 if (!overlapping) { validPosition = true; } attempts++; } // 如果找不到有效位置,使用默认位置(游戏区域底部中心) if (!validPosition) { console.log(`尝试 ${maxAttempts} 次后未找到有效位置,使用默认位置`); randomX = (this.gameBounds.left + this.gameBounds.right) / 2; randomY = this.gameBounds.bottom + ballRadius + 50; // 底部上方50单位 } // 将世界坐标转换为相对于GameArea的本地坐标 const localPos = gameArea.getComponent(UITransform).convertToNodeSpaceAR(new Vec3(randomX, randomY, 0)); this.activeBall.position = localPos; console.log('小球位置已设置:', { worldX: randomX, worldY: randomY, localX: localPos.x, localY: localPos.y, overlapsWithBlock: !validPosition }); } // 设置默认随机位置 setRandomPositionDefault(minX, maxX, minY, maxY) { // 随机生成位置 const randomX = Math.random() * (maxX - minX) + minX; const randomY = Math.random() * (maxY - minY) + minY; // 将世界坐标转换为相对于GameArea的本地坐标 const gameArea = find('Canvas/GameLevelUI/GameArea'); if (gameArea) { const localPos = gameArea.getComponent(UITransform).convertToNodeSpaceAR(new Vec3(randomX, randomY, 0)); this.activeBall.position = localPos; } else { // 直接设置位置(不太准确,但作为后备) this.activeBall.position = new Vec3(randomX - this.gameBounds.left, randomY - this.gameBounds.bottom, 0); } console.log('使用默认随机位置设置小球:', { worldX: randomX, worldY: randomY }); } // 设置碰撞组件 setupCollider() { if (!this.activeBall) return; console.log('⚙️ === 开始设置小球碰撞组件 ==='); console.log('小球节点:', this.activeBall.name); // 确保小球有刚体组件 let rigidBody = this.activeBall.getComponent(RigidBody2D); if (!rigidBody) { console.log('🔧 添加RigidBody2D组件...'); rigidBody = this.activeBall.addComponent(RigidBody2D); rigidBody.type = 2; // Dynamic rigidBody.gravityScale = 0; // 不受重力影响 rigidBody.enabledContactListener = true; // 启用碰撞监听 rigidBody.fixedRotation = true; // 固定旋转 rigidBody.allowSleep = false; // 不允许休眠 rigidBody.linearDamping = 0; // 无线性阻尼,保持速度不衰减 rigidBody.angularDamping = 0; // 无角阻尼 console.log('✅ RigidBody2D组件已添加'); } else { console.log('✅ RigidBody2D组件已存在,更新设置...'); // 确保已有的刚体组件设置正确 rigidBody.enabledContactListener = true; rigidBody.gravityScale = 0; rigidBody.linearDamping = 0; // 确保无线性阻尼,保持速度不衰减 rigidBody.angularDamping = 0; // 确保无角阻尼 rigidBody.allowSleep = false; // 不允许休眠 } // 确保小球有碰撞组件 let collider = this.activeBall.getComponent(CircleCollider2D); if (!collider) { console.log('🔧 添加CircleCollider2D组件...'); collider = this.activeBall.addComponent(CircleCollider2D); collider.radius = this.radius || 25; // 使用已计算的半径或默认值 collider.tag = 1; // 小球标签 collider.group = 1; // 碰撞组1 - Ball组 collider.sensor = false; // 非传感器(实际碰撞) collider.friction = 0; // 无摩擦 collider.restitution = 1; // 完全弹性碰撞 console.log('✅ CircleCollider2D组件已添加'); } else { console.log('✅ CircleCollider2D组件已存在,更新设置...'); // 确保已有的碰撞组件设置正确 collider.sensor = false; collider.restitution = 1; collider.group = 1; // 确保是Ball组 collider.tag = 1; // 确保标签正确 } console.log('🔧 小球碰撞组件设置完成:', { hasRigidBody: !!rigidBody, hasCollider: !!collider, radius: collider ? collider.radius : 'unknown', contactListener: rigidBody ? rigidBody.enabledContactListener : false, group: collider ? collider.group : 'unknown', tag: collider ? collider.tag : 'unknown' }); // === 使用全局回调监听器 === if (PhysicsSystem2D.instance) { // 先移除旧监听,避免重复注册 PhysicsSystem2D.instance.off(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this); PhysicsSystem2D.instance.off(Contact2DType.END_CONTACT, this.onEndContact, this); PhysicsSystem2D.instance.off(Contact2DType.PRE_SOLVE, this.onPreSolve, this); PhysicsSystem2D.instance.off(Contact2DType.POST_SOLVE, this.onPostSolve, this); PhysicsSystem2D.instance.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this); PhysicsSystem2D.instance.on(Contact2DType.END_CONTACT, this.onEndContact, this); PhysicsSystem2D.instance.on(Contact2DType.PRE_SOLVE, this.onPreSolve, this); PhysicsSystem2D.instance.on(Contact2DType.POST_SOLVE, this.onPostSolve, this); console.log('✅ 已注册全局碰撞监听器'); } console.log('✅ 小球碰撞组件设置完成'); console.log('⚙️ === 碰撞组件设置完成 ==='); } // 碰撞回调 - 简化版本用于测试 onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) { console.log('🎯 碰撞成功...'); console.log('碰撞对象:', { self: selfCollider.node.name, other: otherCollider.node.name, otherGroup: otherCollider.group, selfGroup: selfCollider.group }); // 判断哪个是小球,哪个是方块 let ballNode: Node = null; let blockNode: Node = null; // 检查self是否为小球(组1) if (selfCollider.group === 1) { ballNode = selfCollider.node; blockNode = otherCollider.node; } // 检查other是否为小球(组1) else if (otherCollider.group === 1) { ballNode = otherCollider.node; blockNode = selfCollider.node; } // 如果没有找到小球,跳过处理 if (!ballNode || !blockNode) { console.log('❌ 未检测到小球参与的碰撞'); return; } console.log('✅ 检测到小球与方块碰撞:', { ball: ballNode.name, block: blockNode.name }); // 检查碰撞对象是否为方块 const nodeName = blockNode.name; const nodePath = this.getNodePath(blockNode); // 检查是否有Weapon子节点(判断是否为方块) const hasWeaponChild = blockNode.getChildByName('Weapon') !== null; const isBlock = nodeName.includes('Block') || nodePath.includes('Block') || hasWeaponChild; console.log('判定为方块:', isBlock); if (isBlock) { console.log(`🎯 检测到方块碰撞: ${nodeName}, 路径: ${nodePath}`); console.log('🚀 方块武器被激活,发射子弹攻击敌人...'); // 计算碰撞世界坐标,默认用接触点 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 = blockNode.worldPosition.clone(); } const now = performance.now(); const lastTime = this.blockFireCooldown.get(blockNode.uuid) || 0; if (now - lastTime > this.FIRE_COOLDOWN * 1000) { this.blockFireCooldown.set(blockNode.uuid, now); this.fireBulletAt(blockNode, contactPos); } } } // 碰撞结束事件 - 简化版本 onEndContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) { console.log('🔚 碰撞结束:', { self: selfCollider.node.name, other: otherCollider.node.name }); } // 碰撞预处理事件 - 简化版本 onPreSolve(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) { console.log('⚙️ 碰撞预处理:', otherCollider.node.name); } // 碰撞后处理事件 - 简化版本 onPostSolve(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) { console.log('✅ 碰撞后处理:', otherCollider.node.name); } // 计算反射向量 calculateReflection(direction: Vec2, normal: Vec2): Vec2 { // 使用反射公式: R = V - 2(V·N)N const dot = direction.x * normal.x + direction.y * normal.y; const reflection = new Vec2( direction.x - 2 * dot * normal.x, direction.y - 2 * dot * normal.y ); reflection.normalize(); // 添加一些随机性,避免重复的反弹路径 const randomAngle = (Math.random() - 0.5) * this.maxReflectionRandomness; // 随机角度 const cos = Math.cos(randomAngle); const sin = Math.sin(randomAngle); // 应用随机旋转 const randomizedReflection = new Vec2( reflection.x * cos - reflection.y * sin, reflection.x * sin + reflection.y * cos ); // 确保反射方向不会太接近水平或垂直方向 // 这有助于避免球在水平或垂直方向上来回反弹 const minAngleFromAxis = 0.1; // 约5.7度 // 检查是否接近水平方向 if (Math.abs(randomizedReflection.y) < minAngleFromAxis) { // 调整y分量,使其远离水平方向 const sign = randomizedReflection.y >= 0 ? 1 : -1; randomizedReflection.y = sign * (minAngleFromAxis + Math.random() * 0.1); // 重新归一化 randomizedReflection.normalize(); } // 检查是否接近垂直方向 if (Math.abs(randomizedReflection.x) < minAngleFromAxis) { // 调整x分量,使其远离垂直方向 const sign = randomizedReflection.x >= 0 ? 1 : -1; randomizedReflection.x = sign * (minAngleFromAxis + Math.random() * 0.1); // 重新归一化 randomizedReflection.normalize(); } randomizedReflection.normalize(); console.log('反射方向:', { original: direction, normal: normal, reflection: reflection, randomized: randomizedReflection }); return randomizedReflection; } /** * 从方块武器发射子弹攻击敌人 - 重构版本 * 现在直接创建子弹实例并使用BulletController的实例方法 * @param blockNode 激活的方块节点 */ fireBullet(blockNode: Node) { console.log('🔫 === 方块武器发射子弹流程 ==='); console.log('激活的方块:', blockNode.name); console.log('方块路径:', this.getNodePath(blockNode)); // 检查子弹预制体是否存在 if (!this.bulletPrefab) { console.error('❌ 子弹预制体未设置'); return; } // 查找方块中的Weapon节点 console.log('🔍 开始查找方块中的Weapon节点...'); const weaponNode = this.findWeaponNode(blockNode); if (!weaponNode) { console.error('❌ 在方块中找不到Weapon节点:', blockNode.name); console.log('方块结构:'); this.logNodeStructure(blockNode, 0); // 如果找不到Weapon节点,使用方块本身的位置作为发射位置 console.log('🔧 使用方块本身作为发射位置'); const blockWorldPos = blockNode.worldPosition; console.log('方块世界坐标:', blockWorldPos); // 直接创建子弹并设置位置 this.createAndFireBullet(blockWorldPos); console.log('🔫 === 方块武器发射子弹流程结束 ==='); return; } console.log('✅ 找到Weapon节点:', weaponNode.name); console.log('Weapon节点路径:', this.getNodePath(weaponNode)); // 获取武器的世界坐标作为发射位置 let firePosition: Vec3; try { firePosition = weaponNode.worldPosition; console.log('武器世界坐标:', firePosition); } catch (error) { console.error('❌ 获取武器坐标失败:', error); // 备用方案:使用方块坐标 firePosition = blockNode.worldPosition; console.log('使用方块坐标作为备用:', firePosition); } // 创建并发射子弹 console.log('🚀 创建子弹...'); this.createAndFireBullet(firePosition); console.log('🔫 === 方块武器发射子弹流程结束 ==='); } /** * 创建并发射子弹 - 安全版本 * @param firePosition 发射位置(世界坐标) */ private createAndFireBullet(firePosition: Vec3) { console.log('🔫 === 开始创建子弹(安全模式)==='); // 创建子弹实例 const bullet = instantiate(this.bulletPrefab); if (!bullet) { console.error('❌ 子弹实例创建失败'); return; } console.log('✅ 子弹实例创建成功:', bullet.name); // 获取子弹控制器并设置发射位置 const bulletController = bullet.getComponent(BulletController); if (!bulletController) { console.error('❌ 子弹预制体缺少BulletController组件'); bullet.destroy(); return; } // 查找GameArea const gameArea = find('Canvas/GameLevelUI/GameArea'); if (!gameArea) { console.error('❌ 找不到GameArea节点'); bullet.destroy(); return; } // 计算放置位置(先转换为本地坐标,避免出现原点闪烁) const gameAreaTrans = gameArea.getComponent(UITransform); const localPos = gameAreaTrans ? gameAreaTrans.convertToNodeSpaceAR(firePosition) : new Vec3(); // 在添加到场景之前,先禁用可能引起问题的物理组件 const rigidBody = bullet.getComponent(RigidBody2D); const collider = bullet.getComponent(Collider2D); if (rigidBody) { rigidBody.enabled = false; } if (collider) { collider.enabled = false; } // 设置位置再添加到场景,避免视觉闪烁 bullet.position = localPos; gameArea.addChild(bullet); // 设定发射位置供 BulletController 使用 bulletController.setFirePosition(firePosition); // 延迟启用物理组件,确保 Box2D world 已就绪,避免 m_world 报错 this.scheduleOnce(() => { if (!bullet || !bullet.isValid) return; if (rigidBody && rigidBody.isValid) { rigidBody.enabled = true; } if (collider && collider.isValid) { collider.enabled = true; } }, 0.05); console.log('✅ 子弹生成完毕并将在 0.05s 后激活物理组件'); console.log('🔫 === 子弹创建完成 ==='); } // 递归查找Weapon节点 private findWeaponNode(node: Node): Node | null { console.log(`🔍 在节点 ${node.name} 中查找Weapon...`); // 先检查当前节点是否有Weapon子节点 const weaponNode = node.getChildByName('Weapon'); if (weaponNode) { console.log(`✅ 在 ${node.name} 中找到Weapon节点`); return weaponNode; } // 如果没有,递归检查所有子节点 for (let i = 0; i < node.children.length; i++) { const child = node.children[i]; console.log(` 🔍 检查子节点: ${child.name}`); const foundWeapon = this.findWeaponNode(child); if (foundWeapon) { console.log(`✅ 在 ${child.name} 的子节点中找到Weapon`); return foundWeapon; } } // 如果都没找到,返回null console.log(`❌ 在 ${node.name} 及其子节点中未找到Weapon`); return null; } // 辅助方法:打印节点结构 private logNodeStructure(node: Node, depth: number) { const indent = ' '.repeat(depth); console.log(`${indent}${node.name}`); for (let i = 0; i < node.children.length; i++) { this.logNodeStructure(node.children[i], depth + 1); } } // 获取节点的完整路径 private getNodePath(node: Node): string { let path = node.name; let current = node; while (current.parent) { current = current.parent; path = current.name + '/' + path; } return path; } update(dt: number) { // 只有当小球已启动时才执行运动逻辑 if (!this.ballStarted || !this.initialized || !this.activeBall || !this.activeBall.isValid) { return; } // 使用刚体组件控制小球移动,而不是直接设置位置 const rigidBody = this.activeBall.getComponent(RigidBody2D); if (rigidBody) { // 获取当前速度并确保方向向量与实际速度方向一致 const currentVelocity = rigidBody.linearVelocity; const speed = Math.sqrt(currentVelocity.x * currentVelocity.x + currentVelocity.y * currentVelocity.y); // 确保方向向量与实际速度方向一致 if (speed > 1.0) { this.direction.x = currentVelocity.x / speed; this.direction.y = currentVelocity.y / speed; } // 如果速度过低,重新设置速度以维持恒定运动 if (speed < this.speed * 0.9) { rigidBody.linearVelocity = new Vec2( this.direction.x * this.speed, this.direction.y * this.speed ); } // 定期检查小球是否接近方块但没有触发碰撞(调试用) this.debugCheckNearBlocks(); } } // 调试方法:检查小球是否接近方块但没有触发物理碰撞 private debugCheckCounter = 0; private debugCheckNearBlocks() { this.debugCheckCounter++; // 每60帧(约1秒)检查一次 if (this.debugCheckCounter % 60 !== 0) return; const ballPos = this.activeBall.worldPosition; if (!this.placedBlocksContainer || !this.placedBlocksContainer.isValid) return; let nearestDistance = Infinity; let nearestBlock = null; for (let i = 0; i < this.placedBlocksContainer.children.length; i++) { const block = this.placedBlocksContainer.children[i]; if (block.name.includes('Block') || block.getChildByName('B1')) { const blockPos = block.worldPosition; const distance = Math.sqrt( Math.pow(ballPos.x - blockPos.x, 2) + Math.pow(ballPos.y - blockPos.y, 2) ); if (distance < nearestDistance) { nearestDistance = distance; nearestBlock = block; } } } if (nearestBlock && nearestDistance < 100) { console.log(`⚠️ 小球接近方块但无物理碰撞: 距离=${nearestDistance.toFixed(1)}, 方块=${nearestBlock.name}`); console.log('小球位置:', ballPos); console.log('方块位置:', nearestBlock.worldPosition); // 检查小球的碰撞体状态 console.log('=== 小球碰撞体详细检查 ==='); const ballCollider = this.activeBall.getComponent(Collider2D); console.log('小球有碰撞体:', !!ballCollider); if (ballCollider) { console.log('小球碰撞体类型:', ballCollider.constructor.name); console.log('小球碰撞组:', ballCollider.group); console.log('小球碰撞标签:', ballCollider.tag); console.log('小球是否为传感器:', ballCollider.sensor); console.log('小球碰撞体是否启用:', ballCollider.enabled); if (ballCollider instanceof CircleCollider2D) { console.log('小球碰撞半径:', ballCollider.radius); } } // 检查小球的刚体状态 const ballRigidBody = this.activeBall.getComponent(RigidBody2D); console.log('小球有刚体:', !!ballRigidBody); if (ballRigidBody) { console.log('小球刚体类型:', ballRigidBody.type); console.log('小球刚体启用碰撞监听:', ballRigidBody.enabledContactListener); console.log('小球刚体是否启用:', ballRigidBody.enabled); } // 检查最近的方块碰撞体 console.log('=== 方块碰撞体详细检查 ==='); const blockCollider = nearestBlock.getComponent(Collider2D); console.log('方块有碰撞体:', !!blockCollider); if (blockCollider) { console.log('方块碰撞体类型:', blockCollider.constructor.name); console.log('方块碰撞组:', blockCollider.group); console.log('方块碰撞标签:', blockCollider.tag); console.log('方块是否为传感器:', blockCollider.sensor); console.log('方块碰撞体是否启用:', blockCollider.enabled); } // 检查碰撞矩阵 console.log('=== 碰撞矩阵检查 ==='); if (ballCollider && blockCollider) { console.log(`小球组${ballCollider.group} 是否能与方块组${blockCollider.group} 碰撞: ${this.checkCollisionMatrix(ballCollider.group, blockCollider.group)}`); } // 检查物理引擎状态 console.log('=== 物理引擎状态检查 ==='); console.log('物理引擎是否启用:', PhysicsSystem2D.instance.enable); console.log('物理引擎重力:', PhysicsSystem2D.instance.gravity); } } // 检查碰撞矩阵 private checkCollisionMatrix(group1: number, group2: number): boolean { // 根据项目配置检查碰撞矩阵 // "collisionMatrix": { "0": 3, "1": 39, "2": 6, "3": 16, "4": 40, "5": 18 } const collisionMatrix: { [key: string]: number } = { "0": 3, "1": 39, "2": 6, "3": 16, "4": 40, "5": 18 }; const mask1 = collisionMatrix[group1.toString()]; const mask2 = collisionMatrix[group2.toString()]; console.log(`组${group1}的碰撞掩码: ${mask1} (二进制: ${mask1?.toString(2)})`); console.log(`组${group2}的碰撞掩码: ${mask2} (二进制: ${mask2?.toString(2)})`); // 检查group1是否能与group2碰撞 const canCollide1to2 = mask1 && (mask1 & (1 << group2)) !== 0; // 检查group2是否能与group1碰撞 const canCollide2to1 = mask2 && (mask2 & (1 << group1)) !== 0; console.log(`组${group1}->组${group2}: ${canCollide1to2}`); console.log(`组${group2}->组${group1}: ${canCollide2to1}`); return canCollide1to2 && canCollide2to1; } // 初始化方向 initializeDirection() { // 随机初始方向 const angle = Math.random() * Math.PI * 2; // 0-2π之间的随机角度 this.direction.x = Math.cos(angle); this.direction.y = Math.sin(angle); this.direction.normalize(); console.log('Ball initialized with direction:', this.direction); // 设置初始速度 if (this.activeBall) { const rigidBody = this.activeBall.getComponent(RigidBody2D); if (rigidBody) { rigidBody.linearVelocity = new Vec2( this.direction.x * this.speed, this.direction.y * this.speed ); console.log('Ball initial velocity set:', rigidBody.linearVelocity); } else { console.error('无法找到小球的RigidBody2D组件'); } } } // 初始化球的参数 - 公开方法,供GameManager调用 public initialize() { console.log('🎮 === BallController 初始化 ==='); this.calculateGameBounds(); this.createBall(); // 检查方块碰撞体(延迟执行,确保方块已放置) this.scheduleOnce(() => { this.checkBlockColliders(); }, 0.5); console.log('🎮 === BallController 初始化完成 ==='); } // 启动小球 - 公开方法,在确定按钮点击后调用 public startBall() { console.log('🚀 === 启动小球系统 ==='); // 如果还没有初始化,先初始化 if (!this.initialized) { this.initialize(); } // 确保小球存在且有效 if (!this.activeBall || !this.activeBall.isValid) { console.log('⚠️ 小球不存在,重新创建...'); this.createBall(); } // 重新定位小球,避免与方块重叠 console.log('📍 重新定位小球位置...'); this.positionBallRandomly(); // 确保物理组件设置正确 this.setupCollider(); // 初始化运动方向并开始运动 this.initializeDirection(); // 设置运动状态 this.ballStarted = true; console.log('🚀 === 小球系统启动完成,开始运动 ==='); } // 输出碰撞矩阵调试信息 private logCollisionMatrix() { // 根据项目配置检查碰撞矩阵 // "collisionMatrix": { "0": 3, "1": 39, "2": 6, "3": 16, "4": 40, "5": 18 } const collisionMatrix: { [key: string]: number } = { "0": 3, "1": 39, "2": 6, "3": 16, "4": 40, "5": 18 }; console.log('🔍 === 碰撞矩阵配置 ==='); for (const group in collisionMatrix) { const mask = collisionMatrix[group]; console.log(`组 ${group}: 掩码 ${mask} (二进制: ${mask?.toString(2)})`); } // 测试Ball组(1)和Block组(2)是否能碰撞 const ballMask = collisionMatrix["1"]; // 39 const blockMask = collisionMatrix["2"]; // 6 const ballCanCollideWithBlock = (ballMask & (1 << 2)) !== 0; // 检查Ball能否与组2碰撞 const blockCanCollideWithBall = (blockMask & (1 << 1)) !== 0; // 检查Block能否与组1碰撞 console.log('🎯 碰撞测试结果:'); console.log(`Ball组(1) -> Block组(2): ${ballCanCollideWithBlock}`); console.log(`Block组(2) -> Ball组(1): ${blockCanCollideWithBall}`); console.log(`双向碰撞可用: ${ballCanCollideWithBlock && blockCanCollideWithBall}`); console.log('🔍 === 碰撞矩阵分析完成 ==='); } /** * 从给定世界坐标发射子弹 */ private fireBulletAt(blockNode: Node, fireWorldPos: Vec3) { console.log('🔫 === 方块武器发射子弹流程 ==='); console.log('激活的方块:', blockNode.name); console.log('方块路径:', this.getNodePath(blockNode)); // 检查子弹预制体是否存在 if (!this.bulletPrefab) { console.error('❌ 子弹预制体未设置'); return; } // 直接使用碰撞世界坐标作为发射点 this.createAndFireBullet(fireWorldPos); console.log('🔫 === 方块武器发射子弹流程结束 ==='); } }