| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- import { _decorator, Component, Node, Vec2, Vec3, UITransform, Collider2D, Contact2DType, IPhysics2DContact, RigidBody2D, Prefab, instantiate, find, CircleCollider2D } from 'cc';
- const { ccclass, property } = _decorator;
- @ccclass('BallController')
- export class BallController extends Component {
- // 球的预制体
- @property({
- type: Prefab,
- tooltip: '拖拽Ball预制体到这里'
- })
- public ballPrefab: Prefab = null;
- // 球的移动速度
- @property
- public speed: number = 10;
- // 当前活动的球
- 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;
- // 子弹速度
- @property
- public bulletSpeed: number = 50;
- start() {
- // 计算游戏边界
- this.calculateGameBounds();
- }
- // 计算游戏边界(使用GameArea节点)
- calculateGameBounds() {
- // 获取GameArea节点
- const gameArea = find('Canvas/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/GameArea');
- if (gameArea) {
- gameArea.addChild(this.activeBall);
- } else {
- this.node.addChild(this.activeBall);
- }
- // 随机位置(在GameArea范围内)
- this.positionBallRandomly();
- // 设置球的半径
- const transform = this.activeBall.getComponent(UITransform);
- if (transform) {
- this.radius = transform.width / 2;
- }
- // 确保有碰撞组件
- this.setupCollider();
- // 初始化方向
- this.initializeDirection();
- this.initialized = true;
- }
- // 随机位置小球
- 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;
- // 随机生成位置
- const randomX = Math.random() * (maxX - minX) + minX;
- const randomY = Math.random() * (maxY - minY) + minY;
- // 将世界坐标转换为相对于GameArea的本地坐标
- const gameArea = find('Canvas/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);
- }
- }
- // 设置碰撞组件
- setupCollider() {
- if (!this.activeBall) return;
- // 确保小球有刚体组件
- let rigidBody = this.activeBall.getComponent(RigidBody2D);
- if (!rigidBody) {
- rigidBody = this.activeBall.addComponent(RigidBody2D);
- rigidBody.type = 2; // Dynamic
- rigidBody.gravityScale = 0; // 不受重力影响
- rigidBody.enabledContactListener = true; // 启用碰撞监听
- rigidBody.bullet = true; // 高精度碰撞检测
- rigidBody.fixedRotation = false; // 允许旋转
- rigidBody.allowSleep = true;
- }
- // 注册碰撞事件
- this.activeBall.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
- }
- // 初始化方向
- 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);
- }
- // 初始化球的参数 - 公开方法,供GameManager调用
- initialize() {
- this.calculateGameBounds();
- this.createBall();
- }
- update(dt: number) {
- // 如果使用物理引擎,不要手动更新位置
- if (!this.initialized || !this.activeBall || !this.activeBall.isValid) return;
-
- // 使用刚体组件控制小球移动,而不是直接设置位置
- const rigidBody = this.activeBall.getComponent(RigidBody2D);
- if (rigidBody) {
- // 设置线性速度而不是位置
- rigidBody.linearVelocity = new Vec2(
- this.direction.x * this.speed,
- this.direction.y * this.speed
- );
- }
- }
- // 碰撞回调中,只需要更新方向向量,物理引擎会处理实际的反弹
- onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
- // 获取碰撞点和法线
- if (!contact) return;
- // 获取碰撞点的世界坐标
- const worldManifold = contact.getWorldManifold();
- if (!worldManifold) return;
- // 获取碰撞法线
- const normal = worldManifold.normal;
-
- // 更新方向向量,但让物理引擎处理实际的反弹
- this.direction = this.calculateReflection(this.direction, normal);
-
- // 如果碰撞的是方块,发射子弹
- if (otherCollider.node.name.includes('Block')) {
- this.fireBullet(otherCollider.node);
- }
- }
- // 计算反射向量
- 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();
- return reflection;
- }
- // 发射子弹
- fireBullet(blockNode: Node) {
- console.log('发射子弹!击中方块:', blockNode.name);
-
- // 检查是否有子弹预制体
- if (!this.bulletPrefab) {
- console.error('未设置子弹预制体');
- return;
- }
-
- // 查找Weapon节点
- const weaponNode = blockNode.getChildByName('Weapon');
- if (!weaponNode) {
- console.warn(`方块 ${blockNode.name} 没有Weapon子节点`);
- return;
- }
-
- // 实例化子弹
- const bullet = instantiate(this.bulletPrefab);
-
- // 将子弹添加到GameArea中
- const gameArea = find('Canvas/GameArea');
- if (gameArea) {
- gameArea.addChild(bullet);
-
- // 设置子弹初始位置为Weapon节点的位置
- const weaponWorldPos = weaponNode.worldPosition;
- bullet.worldPosition = weaponWorldPos;
-
- // 计算子弹方向 - 从Weapon指向小球
- const ballPos = this.activeBall.worldPosition;
- const direction = new Vec2(
- ballPos.x - weaponWorldPos.x,
- ballPos.y - weaponWorldPos.y
- );
- direction.normalize();
-
- // 添加刚体组件控制子弹移动
- let rigidBody = bullet.getComponent(RigidBody2D);
- if (!rigidBody) {
- rigidBody = bullet.addComponent(RigidBody2D);
- rigidBody.type = 2; // Dynamic
- rigidBody.gravityScale = 0; // 不受重力影响
- rigidBody.fixedRotation = true; // 固定旋转
- rigidBody.allowSleep = false; // 不允许休眠
- }
-
- // 设置子弹速度
- rigidBody.linearVelocity = new Vec2(
- direction.x * this.bulletSpeed,
- direction.y * this.bulletSpeed
- );
-
- // 确保子弹有碰撞体
- let collider = bullet.getComponent(CircleCollider2D);
- if (!collider) {
- collider = bullet.addComponent(CircleCollider2D);
- collider.radius = 5; // 设置适当的半径
- collider.tag = 3; // 子弹标签
- collider.group = 1; // 与其他物体同组
- collider.sensor = false;
- collider.friction = 0;
- collider.restitution = 1;
- }
-
- // 添加自动销毁逻辑
- this.scheduleOnce(() => {
- if (bullet && bullet.isValid) {
- bullet.destroy();
- }
- }, 5); // 5秒后销毁子弹
- }
- }
- }
|