import { _decorator, Animation, AnimationClip, AnimationState, BoxCollider2D, Component, find, instantiate, ITriggerEvent, Node, ObjectCurve, PhysicsSystem2D, SpriteFrame, Vec2, Vec3 } from 'cc'; import { Tools } from '../Tools/Tools'; import { RoleData } from '../../DataItem/ItemData'; import { resMgr } from '../../Frames/ResourcesMgr'; import { Bullet } from './GameScene/Bullet'; import { LifeBar } from './GameScene/LifeBar'; import { BulletPool } from './GameScene/BulletPool'; const { ccclass, property } = _decorator; export enum RoleState { Attack, Move, Idle, Die } @ccclass('Role') export class Role extends Component { //基础数据 hp: number = null; //血量 atk: number = null; //攻击力 atkLength: number = null; //攻击距离 moveSpeed: number = null; //移速 //向左-1 向右1 direction: number = 1; //是否停下 //isStop: boolean = true; //攻击目标 targetNode: Node = null; //战斗系统 private _attackTimer: number = 0; private _attackInterval: number = 3;//攻击间隔 private currentTarget: Role | null = null;//当前目标 //子弹 private _bullet: Node | null = null; private _bulletLayer: Node | null = null; private _bulletPool: BulletPool = null; //动画管理 private _animations: Map = new Map(); private _moveFrames: SpriteFrame[] = []; private _atkFrames: SpriteFrame[] = []; private _idleFrames: SpriteFrame[] = []; private _dieFrames: SpriteFrame[] = []; private _explodeframes: SpriteFrame[] = []; private _bulletFrames: SpriteFrame[] = []; private _animation: Animation = null; //角色数据 private _roleData: RoleData = null; //状态管理 _state: RoleState = null; protected init(name: string, pos: Vec3, roleDatas: RoleData[], dir?: number) { this._bulletPool = this.node.getComponent(BulletPool); this._bulletPool.init(); this._animation = this.node.getComponent(Animation) || this.node.addComponent(Animation); this.direction = dir; let whichData: number = -1; for (let i = 0; i < roleDatas.length; i++) { if (!roleDatas[i]) { console.log(null) } if (roleDatas[i].imgName === name) { this._roleData = roleDatas[i]; whichData = i; break; } } if (whichData != -1) { //获取move精灵帧 this._getFrames(this._roleData.moveCount, this._moveFrames, this._roleData.moveImg, 1); //attack this._getFrames(this._roleData.atkCount, this._atkFrames, this._roleData.atkImg, 1); //idle this._getFrames(this._roleData.idleCount, this._idleFrames, this._roleData.idleImg, 1) //die this._getFrames(this._roleData.dieCount, this._dieFrames, this._roleData.dieImg, 1) //动画 this._collectAni(); //子弹精灵帧 this._getFrames(this._roleData.bulletCount, this._bulletFrames, this._roleData.bulletImg, 1) //子弹爆炸精灵帧 this._getFrames(this._roleData.bulletCount, this._explodeframes, this._roleData.bulletExplodeImg, 1) //设置基础数据 this._setRoleData(this._roleData); this.playAnimation(RoleState.Move); } //位置 this.node.setWorldPosition(pos); this._bulletLayer = find("Canvas/GameRoot/BulletLayer"); this._setupPhysics(); } private _setRoleData(roleData: RoleData) { this.hp = roleData.hp; this.atk = roleData.atk; this.atkLength = roleData.atkLength + 100; this.moveSpeed = roleData.moveSpeed; } private _setupPhysics() { //确保有碰撞组件 const collider2D = this.getComponent(BoxCollider2D) || this.addComponent(BoxCollider2D); collider2D.group = this._getCollisionGroup(); collider2D.apply(); } //获取精灵帧组 /* count -> 精灵帧的数量 imgType -> 图片类型(atk、walk、idle、die) startIdx -> 索引起始数 */ private _getFrames(count: number, frames: SpriteFrame[], imgType: string, startIdx: number) { for (let i = startIdx; i <= count; i++) { if (count > 1) { frames.push(resMgr.getSpriteFrame(imgType + ' (' + i + ')')); } else { frames.push(resMgr.getSpriteFrame(imgType + ' (1)')); } } } update(deltaTime: number) { if (this._state === RoleState.Die) return; if (!this.currentTarget) { this._handleMovement(deltaTime); this._detectEnemies(); } else { this._handleAttack(deltaTime); } } //移动 private _handleMovement(dt: number) { if (this._state !== RoleState.Move) return; let x = this.node.position.x; let y = this.node.position.y; let z = this.node.position.z; x = x + this.moveSpeed * this.direction * dt this.node.setPosition(x, y, z); this.stop(); } //寻敌 private _detectEnemies() { if (!this.node.isValid) return; //const startPos = new Vec2(this.node.position.x, this.node.position.y); const startPos = this.node.position.clone(); const endPos = new Vec2((this.direction * this.atkLength) + this.node.position.x, this.node.position.y); const results = PhysicsSystem2D.instance.raycast(startPos, endPos); if (results?.length) { for (const result of results) { const target = result.collider.node.getComponent(Role); if (target && this._isValidTarget(target)) { this._setTarget(target); break; } } } } //设置攻击目标 private _setTarget(target: Role) { this.currentTarget = target; this.playAnimation(RoleState.Attack); // 使得第一次update就会触发attackTimer >= attackInterval条件 this._attackTimer = this._attackInterval - 0.001; //监听目标销毁事件 target.node.once(Node.EventType.NODE_DESTROYED, this._onTargetDestroyed, this); } //攻击 private _handleAttack(deltaTime: number) { //目标是否可以攻击 if (!this._validateTarget()) { this._clearTarget(); return; } /* //立即攻击判断 if (this._attackTimer === 0) { this._createBullet(); this._attackTimer += deltaTime; return; } //后续间隔攻击 this._attackTimer += deltaTime; if (this._attackTimer >= this._attackInterval) { this._createBullet(); this._attackTimer = 0; //重置计时器 } */ // this._createBullet(); // this.schedule(() => { // this._createBullet(); // }, this.attackInterval); } //目标是否可以攻击 hp大于0 并且 在攻击范围内 -> 可以攻击 private _validateTarget(): boolean { return !!this.currentTarget?.node?.isValid && this.currentTarget.hp > 0 && this._getDistanceToTarget() <= this.atkLength; } //攻击目标与自身的距离 private _getDistanceToTarget(): number { return Math.abs(this.node.getWorldPosition().x - this.currentTarget.node.getWorldPosition().x); } //销毁目标 private _onTargetDestroyed() { this._clearTarget(); if (this.node === null) return; this._detectEnemies();//立即检测新目标 } //清除目标 关闭触发事件 当前目标置空 设置移动状态 播放移动动画 private _clearTarget() { this.currentTarget?.node.off(Node.EventType.NODE_DESTROYED, this._onTargetDestroyed, this); this.currentTarget = null; this.playAnimation(RoleState.Move); } stop() { } //将各个动画存储起来 private _collectAni() { // const moveAni: Animation = Tools.createAnimation(this._moveFrames, 8, this.node, AnimationClip.WrapMode.Loop) // this._animations.set(RoleState.Move, moveAni); // const atkAni: Animation = Tools.createAnimation(this._atkFrames, 8, this.node, AnimationClip.WrapMode.Loop) // this._animations.set(RoleState.Attack, atkAni); // const idleAni: Animation = Tools.createAnimation(this._idleFrames, 8, this.node, AnimationClip.WrapMode.Loop) // this._animations.set(RoleState.Idle, idleAni); // const dieAni: Animation = Tools.createAnimation(this._dieFrames, 8, this.node, AnimationClip.WrapMode.Normal) // this._animations.set(RoleState.Die, dieAni); this._createClip(RoleState.Move, this._moveFrames, 8); this._createClip(RoleState.Attack, this._atkFrames, 8); this._createClip(RoleState.Idle, this._idleFrames, 8); this._createClip(RoleState.Die, this._dieFrames, 9); } //创建动画剪辑 fps越大 速度越快 private _createClip(state: RoleState, frames: SpriteFrame[], fps: number) { const clip: AnimationClip = AnimationClip.createWithSpriteFrames(frames, fps); clip.name = RoleState[state]; clip.wrapMode = state === RoleState.Die ? AnimationClip.WrapMode.Normal : AnimationClip.WrapMode.Loop; if(clip.name === 'Attack'){ clip.events = [{ frame: 0.5, func: "onTriggered", params: ["a"] //向func传递的参数 }] } this._animation.addClip(clip, clip.name); this._animations.set(state, clip.name); } onTriggered(){ this._createBullet(); } //设置动画 public playAnimation(state: RoleState) { if (!this._animation || this._state === state) return; const clipName = this._animations.get(state); if (!clipName) return; this._state = state; this._animation.stop(); this._animation.play(clipName); if (state === RoleState.Die) { this.node.removeAllChildren(); this._animation.once(Animation.EventType.FINISHED, () => { this.node.destroy(); }); } // if (this._animations === null) { // Error("No found" + state); // return; // } // if (this._state === state) return; // this._state = state; // this._animations.get(state).play(); // if (state === RoleState.Die) { // this._animations.get(state).once(Animation.EventType.FINISHED, () => { // this.node.destroy(); // }) // } // this._animations.forEach((anim, key) => { // if(key === state){ // anim.play(); // if(key === RoleState.Die){ // anim.once(Animation.EventType.FINISHED,()=>{ // this.node.destroy(); // }) // } // } // //key === state ? anim.play() : anim.stop(); // }) } //判断是否攻击 // getEnemiesAhead(parent: Node) { // for (const node of parent.children) { // const enemyY = node.getWorldPosition().y; // const enemyX = node.getWorldPosition().x; // const y = this.node.getWorldPosition().y; // const x = this.node.getWorldPosition().x; // if (enemyY === y) { // const distance = Math.abs(enemyX - x); // if (distance <= this.atkLength) { // //开始攻击 // this.isAttacking = true; // //设置攻击目标 // this.targetNode = node; // //停止移动 // this.isStop = true; // //开始开火 // this.isFire = true; // this.attack(this.targetNode, this.isFire); // break; // } // } // } // } // attack(targetNode: Node, isFire: boolean) { // if (!targetNode) return; // const targetNodeTS = targetNode.getComponent(Role); // if (isFire && targetNodeTS.hp > 0) { // this._createBullet(); // this.schedule(() => { // this._createBullet(); // }, 3) // } // } //创建子弹 private _createBullet() { // if (this.direction === -1) { // this._bullet = instantiate(resMgr.getPrefab("BulletEnemy")); // } else if (this.direction === 1) { // this._bullet = instantiate(resMgr.getPrefab("Bullet")); // } const isEnemy = this.direction === -1; this._bullet = this._bulletPool.getBullet(isEnemy); this._bullet.parent = this._bulletLayer; this._bullet.setWorldPosition(this.node.getWorldPosition()); const bulletTS = this._bullet.getComponent(Bullet); bulletTS.reset({ pool: this._bulletPool, isEnemy: isEnemy, direction: this.direction, bulletFrames: this._bulletFrames, explodeFrames: this._explodeframes, targetNode: this.currentTarget.node, atk: this.atk, }) // bulletTS.direction = this.direction; // bulletTS.bulletFrames = this._bulletFrames; // bulletTS.explodeframes = this._explodeframes; // bulletTS.targetNode = this.currentTarget.node; // bulletTS.atk = this.atk; } //要求子类实现的碰撞分组和阵营判断方法,确保不同阵营角色可以正确交互 protected _getCollisionGroup(): number { throw new Error("Method not implemented"); } protected _isValidTarget(target: Role): boolean { throw new Error("Method not implemented"); } }