Role.ts 14 KB

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