EnemySpawnerExample.ts 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. import { _decorator, Component, Node, Prefab, instantiate, Vec3, Vec2, Sprite, SpriteFrame, resources, RigidBody2D, CCFloat, CCInteger } from 'cc';
  2. import { sp } from 'cc';
  3. import { ConfigManager, EnemyConfig } from '../Core/ConfigManager';
  4. import { EnemyComponent } from '../CombatSystem/EnemyComponent';
  5. const { ccclass, property } = _decorator;
  6. /**
  7. * 敌人生成器示例脚本
  8. * 展示如何使用ConfigManager来创建不同类型的敌人
  9. */
  10. @ccclass('EnemySpawnerExample')
  11. export class EnemySpawnerExample extends Component {
  12. @property({
  13. type: Prefab,
  14. tooltip: '基础敌人预制体'
  15. })
  16. public enemyPrefab: Prefab = null;
  17. @property({
  18. type: Node,
  19. tooltip: '敌人容器节点'
  20. })
  21. public enemyContainer: Node = null;
  22. @property({
  23. type: CCFloat,
  24. tooltip: '生成间隔(秒)'
  25. })
  26. public spawnInterval: number = 2.0;
  27. @property({
  28. type: CCInteger,
  29. tooltip: '最大敌人数量'
  30. })
  31. public maxEnemies: number = 10;
  32. private configManager: ConfigManager = null;
  33. private currentWave: number = 1;
  34. private currentEnemyCount: number = 0;
  35. private spawnTimer: number = 0;
  36. private isSpawning: boolean = false;
  37. start() {
  38. this.configManager = ConfigManager.getInstance();
  39. // 延迟开始生成敌人,等待配置加载
  40. this.scheduleOnce(() => {
  41. this.startSpawning();
  42. }, 1.0);
  43. }
  44. update(deltaTime: number) {
  45. if (!this.configManager || !this.configManager.isConfigLoaded()) {
  46. return;
  47. }
  48. // 更新生成计时器
  49. this.spawnTimer += deltaTime;
  50. if (this.spawnTimer >= this.spawnInterval && this.currentEnemyCount < this.maxEnemies) {
  51. this.spawnRandomEnemy();
  52. this.spawnTimer = 0;
  53. }
  54. }
  55. // 开始生成敌人
  56. startSpawning() {
  57. if (!this.configManager || !this.configManager.isConfigLoaded()) {
  58. return;
  59. }
  60. console.log('开始生成敌人');
  61. this.isSpawning = true;
  62. this.schedule(this.spawnRandomEnemy, this.spawnInterval);
  63. }
  64. // 生成随机敌人
  65. spawnRandomEnemy() {
  66. if (!this.isSpawning) return;
  67. const enemyConfig = this.configManager.getRandomEnemy();
  68. if (!enemyConfig) {
  69. return;
  70. }
  71. this.spawnEnemyWithConfig(enemyConfig);
  72. }
  73. // 根据类型生成敌人
  74. spawnEnemyByType(enemyType: string) {
  75. const enemyConfig = this.configManager.getEnemyById(enemyType);
  76. if (!enemyConfig) {
  77. return;
  78. }
  79. this.spawnEnemyWithConfig(enemyConfig);
  80. }
  81. // 根据稀有度生成敌人
  82. spawnEnemyByRarity(rarity: string) {
  83. const enemyConfig = this.configManager.getRandomEnemy(rarity);
  84. if (!enemyConfig) {
  85. return;
  86. }
  87. this.spawnEnemyWithConfig(enemyConfig);
  88. }
  89. // 根据敌人配置生成敌人
  90. private spawnEnemyWithConfig(enemyConfig: EnemyConfig) {
  91. if (!this.enemyPrefab) {
  92. return;
  93. }
  94. // 实例化敌人
  95. const enemyNode = instantiate(this.enemyPrefab);
  96. if (!enemyNode) {
  97. return;
  98. }
  99. // 设置敌人名称
  100. enemyNode.name = 'Enemy';
  101. // 添加到场景中
  102. this.node.addChild(enemyNode);
  103. // 设置随机位置
  104. const spawnRadius = 300;
  105. const angle = Math.random() * Math.PI * 2;
  106. const distance = Math.random() * spawnRadius;
  107. const x = Math.cos(angle) * distance;
  108. const y = Math.sin(angle) * distance;
  109. enemyNode.position = new Vec3(x, y, 0);
  110. // 添加敌人组件
  111. const enemyComponent = enemyNode.addComponent(EnemyComponent);
  112. enemyComponent.enemyConfig = enemyConfig;
  113. enemyComponent.spawner = this;
  114. console.log(`生成敌人: ${enemyConfig.name} (${enemyConfig.rarity})`);
  115. // 加载敌人动画
  116. this.loadEnemyAnimation(enemyNode, enemyConfig);
  117. this.currentEnemyCount++;
  118. }
  119. // 加载敌人动画
  120. private loadEnemyAnimation(enemyNode: Node, enemyConfig: EnemyConfig) {
  121. // 检查敌人配置中是否有Spine动画路径
  122. const animations = enemyConfig.visualConfig?.animations;
  123. if (!animations || !animations.idle) {
  124. console.log(`敌人 ${enemyConfig.name} 没有Spine动画路径`);
  125. return;
  126. }
  127. // 构建Spine动画资源路径
  128. const spineDataPath = animations.idle;
  129. // 加载Spine动画资源
  130. resources.load(spineDataPath, sp.SkeletonData, (err, skeletonData) => {
  131. if (err) {
  132. console.warn(`加载敌人Spine动画失败: ${spineDataPath}`, err);
  133. return;
  134. }
  135. // 查找或创建Spine组件
  136. let spineComponent = enemyNode.getComponent(sp.Skeleton);
  137. if (!spineComponent) {
  138. spineComponent = enemyNode.addComponent(sp.Skeleton);
  139. }
  140. // 设置Spine数据
  141. spineComponent.skeletonData = skeletonData;
  142. // 播放默认动画
  143. if (spineComponent.findAnimation('idle')) {
  144. spineComponent.setAnimation(0, 'idle', true);
  145. }
  146. console.log(`敌人 ${enemyConfig.name} Spine动画加载成功: ${spineDataPath}`);
  147. });
  148. }
  149. // 设置敌人外观
  150. private setupEnemyVisual(enemyNode: Node, enemyConfig: EnemyConfig) {
  151. // 查找Spine动画组件
  152. const spineComponent = enemyNode.getComponent(sp.Skeleton);
  153. if (spineComponent) {
  154. // 如果有Spine组件,设置Spine动画
  155. this.setupEnemySpineAnimation(spineComponent, enemyConfig);
  156. } else {
  157. // 如果没有Spine组件,使用Sprite组件作为后备
  158. const spriteComponent = enemyNode.getComponent(Sprite);
  159. if (spriteComponent) {
  160. // 根据稀有度设置颜色
  161. const rarityColors = {
  162. 'common': { r: 200, g: 200, b: 200, a: 255 }, // 灰色
  163. 'uncommon': { r: 255, g: 255, b: 0, a: 255 }, // 黄色
  164. 'rare': { r: 255, g: 100, b: 0, a: 255 }, // 橙色
  165. 'boss': { r: 255, g: 0, b: 0, a: 255 } // 红色
  166. };
  167. const color = rarityColors[enemyConfig.rarity] || rarityColors['common'];
  168. spriteComponent.color = spriteComponent.color.set(color.r, color.g, color.b, color.a);
  169. }
  170. }
  171. // 设置缩放
  172. enemyNode.setScale(enemyConfig.visualConfig.scale, enemyConfig.visualConfig.scale, 1);
  173. }
  174. // 设置敌人Spine骨骼动画
  175. private setupEnemySpineAnimation(spineComponent: sp.Skeleton, enemyConfig: EnemyConfig) {
  176. const spineDataPath = enemyConfig.visualConfig.spritePrefab;
  177. if (!spineDataPath) {
  178. console.warn(`敌人 ${enemyConfig.name} 没有Spine动画路径`);
  179. return;
  180. }
  181. // 加载Spine动画数据
  182. resources.load(spineDataPath, sp.SkeletonData, (err, skeletonData) => {
  183. if (err) {
  184. console.warn(`加载敌人Spine动画失败: ${spineDataPath}`, err);
  185. return;
  186. }
  187. // 设置Spine数据
  188. spineComponent.skeletonData = skeletonData;
  189. // 播放默认动画(如果有的话)
  190. const animations = enemyConfig.visualConfig.animations;
  191. if (animations && animations.idle) {
  192. spineComponent.setAnimation(0, animations.idle, true);
  193. } else if (animations && animations.walk) {
  194. spineComponent.setAnimation(0, animations.walk, true);
  195. }
  196. console.log(`敌人 ${enemyConfig.name} Spine动画加载成功: ${spineDataPath}`);
  197. });
  198. }
  199. // 设置敌人物理
  200. private setupEnemyPhysics(enemyNode: Node, enemyConfig: EnemyConfig) {
  201. const rigidBody = enemyNode.getComponent(RigidBody2D);
  202. if (rigidBody) {
  203. // 设置线性速度(向左移动)
  204. const speed = enemyConfig.stats.speed;
  205. rigidBody.linearVelocity = new Vec2(-speed, 0);
  206. }
  207. }
  208. // 敌人死亡回调
  209. public onEnemyDeath(enemyNode: Node) {
  210. this.currentEnemyCount--;
  211. // 移除敌人节点
  212. if (enemyNode && enemyNode.isValid) {
  213. enemyNode.destroy();
  214. }
  215. }
  216. // 设置当前波次
  217. public setCurrentWave(wave: number) {
  218. this.currentWave = wave;
  219. }
  220. // 获取当前波次
  221. public getCurrentWave(): number {
  222. return this.currentWave;
  223. }
  224. // 获取当前敌人数量
  225. public getCurrentEnemyCount(): number {
  226. return this.currentEnemyCount;
  227. }
  228. // 清除所有敌人
  229. public clearAllEnemies() {
  230. if (this.enemyContainer) {
  231. this.enemyContainer.destroyAllChildren();
  232. }
  233. this.currentEnemyCount = 0;
  234. }
  235. // 暂停生成
  236. public pauseSpawning() {
  237. this.spawnInterval = Number.MAX_VALUE;
  238. }
  239. // 恢复生成
  240. public resumeSpawning(interval: number = 2.0) {
  241. this.spawnInterval = interval;
  242. this.spawnTimer = 0;
  243. }
  244. }