Role.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. import { _decorator, Animation, AnimationClip, Component, find, Node, PhysicsSystem2D, SpriteFrame, Vec2, Vec3 } from 'cc';
  2. import { RoleData } from '../../DataItem/ItemData';
  3. import { resMgr } from '../../Frames/ResourcesMgr';
  4. import { Bullet } from './GameScene/Bullet';
  5. import { LifeBar } from './GameScene/LifeBar';
  6. import { BulletPool } from './GameScene/BulletPool';
  7. import { GameInfo } from '../../GameInfo';
  8. const { ccclass, property } = _decorator;
  9. export enum RoleState {
  10. Attack,
  11. Move,
  12. Idle,
  13. Die
  14. }
  15. @ccclass('Role')
  16. export class Role extends Component {
  17. //基础数据
  18. hp: number = null; //血量
  19. atk: number = null; //攻击力
  20. atkLength: number = null; //攻击距离
  21. moveSpeed: number = null; //移速
  22. //向左-1 向右1
  23. direction: number = 1;
  24. //是否停下
  25. isStop: boolean = false;
  26. //攻击目标
  27. targetNode: Node = null;
  28. bulletLayer: Node | null = null;
  29. //战斗系统
  30. private _attackTimer: number = 0;
  31. private _attackInterval: number = 3;//攻击间隔
  32. private currentTarget: Role = null;//当前目标
  33. //子弹
  34. private _bullet: Node | null = null;
  35. private _bulletPool: BulletPool = null;
  36. //动画管理
  37. private _animations: Map<RoleState, string> = new Map();
  38. private _moveFrames: SpriteFrame[] = [];
  39. private _atkFrames: SpriteFrame[] = [];
  40. private _idleFrames: SpriteFrame[] = [];
  41. private _dieFrames: SpriteFrame[] = [];
  42. private _explodeframes: SpriteFrame[] = [];
  43. private _bulletFrames: SpriteFrame[] = [];
  44. private _animation: Animation = null;
  45. //角色数据
  46. private _roleData: RoleData = null;
  47. //状态管理
  48. _state: RoleState = null;
  49. //被回收时,重置属性
  50. protected onDisable(): void {
  51. this._clearData();
  52. }
  53. //重新激活时
  54. protected onEnable(): void {
  55. }
  56. init(name: string, pos: Vec3, roleDatas: RoleData[], dir?: number) {
  57. this._bulletPool = this.node.getComponent(BulletPool);
  58. this._bulletPool.init();
  59. this._animation = this.node.getComponent(Animation) || this.node.addComponent(Animation);
  60. this.direction = dir;
  61. let whichData: number = -1;
  62. for (let i = 0; i < roleDatas.length; i++) {
  63. if (!roleDatas[i]) {
  64. console.log(null)
  65. }
  66. if (roleDatas[i].imgName === name) {
  67. this._roleData = roleDatas[i];
  68. whichData = i;
  69. break;
  70. }
  71. }
  72. if (whichData != -1) {
  73. //获取move精灵帧
  74. this._getFrames(this._roleData.moveCount, this._moveFrames, this._roleData.moveImg, 1);
  75. //attack
  76. this._getFrames(this._roleData.atkCount, this._atkFrames, this._roleData.atkImg, 1);
  77. //idle
  78. this._getFrames(this._roleData.idleCount, this._idleFrames, this._roleData.idleImg, 1)
  79. //die
  80. this._getFrames(this._roleData.dieCount, this._dieFrames, this._roleData.dieImg, 1)
  81. //动画
  82. this._collectAni();
  83. //子弹精灵帧
  84. this._getFrames(this._roleData.bulletCount, this._bulletFrames, this._roleData.bulletImg, 1)
  85. //子弹爆炸精灵帧
  86. this._getFrames(this._roleData.bulletCount, this._explodeframes, this._roleData.bulletExplodeImg, 1)
  87. //设置基础数据
  88. this._setRoleData(this._roleData);
  89. this.playAnimation(RoleState.Move);
  90. }
  91. //位置
  92. this.node.setWorldPosition(pos);
  93. this.bulletLayer = find("Canvas/GameRoot/BulletLayer");
  94. }
  95. private _setRoleData(roleData: RoleData) {
  96. this.hp = roleData.hp;
  97. this.atk = roleData.atk;
  98. this.atkLength = roleData.atkLength;
  99. this.moveSpeed = roleData.moveSpeed;
  100. }
  101. //获取精灵帧组
  102. /*
  103. count -> 精灵帧的数量
  104. imgType -> 图片类型(atk、walk、idle、die)
  105. startIdx -> 索引起始数
  106. */
  107. private _getFrames(count: number, frames: SpriteFrame[], imgType: string, startIdx: number) {
  108. for (let i = startIdx; i <= count; i++) {
  109. if (count > 1) {
  110. frames.push(resMgr.getSpriteFrame(imgType + ' (' + i + ')'));
  111. }
  112. else {
  113. frames.push(resMgr.getSpriteFrame(imgType + ' (1)'));
  114. }
  115. }
  116. }
  117. update(deltaTime: number) {
  118. if (this._state === RoleState.Die) return;
  119. //只有游戏结束才能执行后续操作
  120. if (GameInfo.Instance.getIsGameOver()) return;
  121. if (!this.currentTarget) {
  122. this._handleMovement(deltaTime);
  123. this._detectEnemies();
  124. } else {
  125. this._handleAttack(deltaTime);
  126. }
  127. }
  128. //移动
  129. private _handleMovement(dt: number) {
  130. if (this._state !== RoleState.Move) return;
  131. let x = this.node.position.x;
  132. let y = this.node.position.y;
  133. let z = this.node.position.z;
  134. x = x + this.moveSpeed * this.direction * dt
  135. this.node.setPosition(x, y, z);
  136. }
  137. //寻敌
  138. private _detectEnemies() {
  139. //节点不可用,不寻敌
  140. if (!this.node.isValid) return;
  141. //游戏结束,不寻敌
  142. if (GameInfo.Instance.getIsGameOver()) return;
  143. //const startPos = new Vec2(this.node.position.x, this.node.position.y);
  144. const startPos = this.node.position.clone();
  145. const endPos = new Vec2((this.direction * this.atkLength) + this.node.position.x, this.node.position.y);
  146. const results = PhysicsSystem2D.instance.raycast(startPos, endPos);
  147. if (results?.length) {
  148. for (const result of results) {
  149. const target = result.collider.node.getComponent(Role);
  150. if (target && this._isValidTarget(target)) {
  151. this._setTarget(target);
  152. break;
  153. }
  154. }
  155. }
  156. }
  157. //设置攻击目标
  158. private _setTarget(target: Role) {
  159. this.currentTarget = target;
  160. this.playAnimation(RoleState.Attack);
  161. // 使得第一次update就会触发attackTimer >= attackInterval条件
  162. this._attackTimer = this._attackInterval - 0.001;
  163. //监听目标销毁事件
  164. target.node.once(Node.EventType.NODE_DESTROYED, this._onTargetDestroyed, this);
  165. }
  166. //攻击
  167. private _handleAttack(deltaTime: number) {
  168. //目标是否可以攻击
  169. if (!this._validateTarget()) {
  170. this._clearTarget();
  171. return;
  172. }
  173. /*
  174. //立即攻击判断
  175. if (this._attackTimer === 0) {
  176. this._createBullet();
  177. this._attackTimer += deltaTime;
  178. return;
  179. }
  180. //后续间隔攻击
  181. this._attackTimer += deltaTime;
  182. if (this._attackTimer >= this._attackInterval) {
  183. this._createBullet();
  184. this._attackTimer = 0; //重置计时器
  185. }
  186. */
  187. // this._createBullet();
  188. // this.schedule(() => {
  189. // this._createBullet();
  190. // }, this.attackInterval);
  191. }
  192. //目标是否可以攻击 hp大于0 并且 在攻击范围内 -> 可以攻击
  193. private _validateTarget(): boolean {
  194. return !!this.currentTarget?.node?.isValid &&
  195. this.currentTarget.node.getComponent(LifeBar)._curHp > 0 &&
  196. this._getDistanceToTarget() <= this.atkLength;
  197. }
  198. //攻击目标与自身的距离
  199. private _getDistanceToTarget(): number {
  200. return Math.abs(this.node.getWorldPosition().x - this.currentTarget.node.getWorldPosition().x);
  201. }
  202. //销毁目标
  203. private _onTargetDestroyed() {
  204. this._clearTarget();
  205. if (this.node === null) return;
  206. this._detectEnemies();//立即检测新目标
  207. }
  208. //清除目标 关闭触发事件 当前目标置空 设置移动状态 播放移动动画
  209. private _clearTarget() {
  210. this.currentTarget?.node.off(Node.EventType.NODE_DESTROYED, this._onTargetDestroyed, this);
  211. this.currentTarget = null;
  212. this.playAnimation(RoleState.Move);
  213. }
  214. //将各个动画存储起来
  215. private _collectAni() {
  216. this._createClip(RoleState.Move, this._moveFrames, 8);
  217. this._createClip(RoleState.Attack, this._atkFrames, 6);
  218. this._createClip(RoleState.Idle, this._idleFrames, 8);
  219. this._createClip(RoleState.Die, this._dieFrames, 9);
  220. }
  221. //创建动画剪辑 fps越大 速度越快
  222. private _createClip(state: RoleState, frames: SpriteFrame[], fps: number) {
  223. const clip: AnimationClip = AnimationClip.createWithSpriteFrames(frames, fps);
  224. clip.name = RoleState[state];
  225. clip.wrapMode = state === RoleState.Die ?
  226. AnimationClip.WrapMode.Normal :
  227. AnimationClip.WrapMode.Loop;
  228. if (clip.name === 'Attack') {
  229. clip.events = [{
  230. frame: 0.5,
  231. func: "onTriggered",
  232. params: [""] //向func传递的参数
  233. }]
  234. }
  235. this._animation.addClip(clip, clip.name);
  236. this._animations.set(state, clip.name);
  237. }
  238. onTriggered() {
  239. this._createBullet();
  240. }
  241. //设置动画
  242. public playAnimation(state: RoleState) {
  243. if (!this._animation || this._state === state) return;
  244. const clipName = this._animations.get(state);
  245. if (!clipName) return;
  246. //如果当前有动画正在播放,等待其完成后再切换
  247. if (this._animation.getState(String(this._state))?.isPlaying) {
  248. this._animation.once(Animation.EventType.FINISHED, () => {
  249. this._switchState(state, clipName);
  250. });
  251. } else {
  252. this._switchState(state, clipName);
  253. }
  254. }
  255. private _switchState(state: RoleState, clipName: string) {
  256. this._state = state;
  257. //this._animation.stop();
  258. this._animation.play(clipName);
  259. if (state === RoleState.Die) {
  260. this.node.removeAllChildren();
  261. this._animation.once(Animation.EventType.FINISHED, () => {
  262. this.node.destroy();
  263. });
  264. }
  265. }
  266. private _createBullet() {
  267. const isEnemy = this.direction === -1;
  268. this._bullet = this._bulletPool.getBullet(isEnemy);
  269. this._bullet.parent = this.bulletLayer;
  270. this._bullet.setWorldPosition(this.node.getWorldPosition());
  271. const bulletTS = this._bullet.getComponent(Bullet);
  272. bulletTS.reset({
  273. pool: this._bulletPool,
  274. isEnemy: isEnemy,
  275. direction: this.direction,
  276. bulletFrames: this._bulletFrames,
  277. explodeFrames: this._explodeframes,
  278. targetNode: this.currentTarget.node,
  279. atk: this.atk,
  280. })
  281. }
  282. private _clearData() {
  283. // 重置基础数据
  284. this.hp = null;
  285. this.atk = null;
  286. this.atkLength = null;
  287. this.moveSpeed = null;
  288. // 重置方向和状态
  289. this.direction = 1;
  290. this.isStop = false;
  291. // 清除战斗系统状态
  292. this._attackTimer = 0;
  293. this.targetNode = null;
  294. this.currentTarget = null;
  295. // 释放子弹资源
  296. if (this._bullet) {
  297. // 如果使用了对象池,将子弹放回池
  298. if (this._bulletPool) {
  299. const isEnemy = this.direction === -1;
  300. this._bulletPool.recycle(this._bullet, isEnemy);
  301. } else {
  302. // 否则直接销毁
  303. this._bullet.destroy();
  304. }
  305. this._bullet = null;
  306. }
  307. // 重置动画状态
  308. if (this._animation) {
  309. this._animation.stop();
  310. }
  311. // 重置状态机
  312. this._state = null;
  313. // 清除节点引用
  314. //this.bulletLayer = null;
  315. }
  316. //要求子类实现的碰撞分组和阵营判断方法,确保不同阵营角色可以正确交互
  317. protected _getCollisionGroup(): number {
  318. throw new Error("Method not implemented");
  319. }
  320. protected _isValidTarget(target: Role): boolean {
  321. throw new Error("Method not implemented");
  322. }
  323. }