LevelConfigManager.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. import { _decorator, Component, JsonAsset, resources } from 'cc';
  2. const { ccclass, property } = _decorator;
  3. /**
  4. * 关卡配置数据结构
  5. */
  6. interface LevelConfig {
  7. levelId: number;
  8. name: string;
  9. scene: string;
  10. description?: string;
  11. weapons: string[];
  12. waves: WaveConfig[];
  13. levelSettings?: {
  14. initialHealth?: number;
  15. timeLimit?: number;
  16. difficulty?: string;
  17. starRequirements?: {
  18. [key: string]: any;
  19. };
  20. };
  21. }
  22. /**
  23. * 波次配置数据结构
  24. */
  25. interface WaveConfig {
  26. waveId: number;
  27. enemies: EnemyWaveConfig[];
  28. }
  29. /**
  30. * 敌人波次配置
  31. */
  32. interface EnemyWaveConfig {
  33. enemyType: string;
  34. count: number;
  35. spawnInterval: number;
  36. characteristics: string[];
  37. }
  38. /**
  39. * 关卡JSON文件配置项
  40. */
  41. @ccclass('LevelJsonConfig')
  42. export class LevelJsonConfig {
  43. @property({
  44. tooltip: '关卡ID'
  45. })
  46. public levelId: number = 1;
  47. @property({
  48. tooltip: '关卡名称'
  49. })
  50. public levelName: string = '';
  51. @property({
  52. type: JsonAsset,
  53. tooltip: '关卡配置JSON文件'
  54. })
  55. public configFile: JsonAsset = null;
  56. }
  57. /**
  58. * 关卡配置管理器
  59. * 让策划在编辑器中配置所有关卡的JSON文件
  60. */
  61. @ccclass('LevelConfigManager')
  62. export class LevelConfigManager extends Component {
  63. private static instance: LevelConfigManager = null;
  64. @property({
  65. tooltip: '总关卡数量'
  66. })
  67. public totalLevels: number = 5;
  68. @property({
  69. type: [LevelJsonConfig],
  70. tooltip: '关卡配置列表(拖入对应的JSON文件)'
  71. })
  72. public levelConfigs: LevelJsonConfig[] = [];
  73. // 缓存已加载的关卡配置
  74. private loadedConfigs: Map<number, LevelConfig> = new Map();
  75. onLoad() {
  76. if (LevelConfigManager.instance === null) {
  77. LevelConfigManager.instance = this;
  78. this.validateConfigs();
  79. } else if (LevelConfigManager.instance !== this) {
  80. this.destroy();
  81. return;
  82. }
  83. }
  84. onDestroy() {
  85. if (LevelConfigManager.instance === this) {
  86. LevelConfigManager.instance = null;
  87. }
  88. }
  89. public static getInstance(): LevelConfigManager {
  90. return LevelConfigManager.instance;
  91. }
  92. /**
  93. * 验证关卡配置的完整性
  94. */
  95. private validateConfigs() {
  96. console.log(`=== 验证关卡配置 ===`);
  97. console.log(`总关卡数: ${this.totalLevels}`);
  98. console.log(`已配置关卡数: ${this.levelConfigs.length}`);
  99. // 检查是否所有关卡都有配置
  100. for (let i = 1; i <= this.totalLevels; i++) {
  101. const config = this.levelConfigs.find(c => c.levelId === i);
  102. if (!config) {
  103. console.warn(`关卡 ${i} 缺少配置`);
  104. } else if (!config.configFile) {
  105. console.warn(`关卡 ${i} 缺少JSON文件`);
  106. } else {
  107. console.log(`✅ 关卡 ${i}: ${config.levelName} - ${config.configFile.name}`);
  108. }
  109. }
  110. // 检查是否有重复的关卡ID
  111. const levelIds = this.levelConfigs.map(c => c.levelId);
  112. const duplicates = levelIds.filter((id, index) => levelIds.indexOf(id) !== index);
  113. if (duplicates.length > 0) {
  114. console.error(`发现重复的关卡ID: ${duplicates.join(', ')}`);
  115. }
  116. }
  117. /**
  118. * 获取关卡配置
  119. */
  120. public async getLevelConfig(levelId: number): Promise<LevelConfig | null> {
  121. // 先从缓存中查找
  122. if (this.loadedConfigs.has(levelId)) {
  123. return this.loadedConfigs.get(levelId);
  124. }
  125. // 查找对应的配置项
  126. const configItem = this.levelConfigs.find(c => c.levelId === levelId);
  127. if (!configItem || !configItem.configFile) {
  128. console.error(`关卡 ${levelId} 配置不存在或缺少JSON文件`);
  129. return null;
  130. }
  131. try {
  132. // 解析JSON数据
  133. const jsonData = configItem.configFile.json;
  134. if (!jsonData) {
  135. console.error(`关卡 ${levelId} JSON文件解析失败`);
  136. return null;
  137. }
  138. // 验证JSON数据结构
  139. const levelConfig = this.validateLevelConfigData(jsonData, levelId);
  140. if (levelConfig) {
  141. // 缓存配置
  142. this.loadedConfigs.set(levelId, levelConfig);
  143. console.log(`✅ 关卡 ${levelId} 配置加载成功`);
  144. return levelConfig;
  145. }
  146. } catch (error) {
  147. console.error(`关卡 ${levelId} 配置加载失败:`, error);
  148. }
  149. return null;
  150. }
  151. /**
  152. * 验证关卡配置数据结构
  153. */
  154. private validateLevelConfigData(jsonData: any, levelId: number): LevelConfig | null {
  155. try {
  156. // 基本字段验证
  157. if (!jsonData.name || !jsonData.scene || !jsonData.weapons || !jsonData.waves) {
  158. console.error(`关卡 ${levelId} JSON数据缺少必要字段`);
  159. return null;
  160. }
  161. // 构建关卡配置对象
  162. const levelConfig: LevelConfig = {
  163. levelId: levelId,
  164. name: jsonData.name,
  165. scene: jsonData.scene,
  166. description: jsonData.description,
  167. weapons: jsonData.weapons || [],
  168. waves: [],
  169. levelSettings: jsonData.levelSettings
  170. };
  171. // 验证波次配置
  172. if (Array.isArray(jsonData.waves)) {
  173. for (const waveData of jsonData.waves) {
  174. const wave: WaveConfig = {
  175. waveId: waveData.waveId || 0,
  176. enemies: []
  177. };
  178. if (Array.isArray(waveData.enemies)) {
  179. for (const enemyData of waveData.enemies) {
  180. const enemy: EnemyWaveConfig = {
  181. enemyType: enemyData.enemyType || '',
  182. count: enemyData.count || 1,
  183. spawnInterval: enemyData.spawnInterval || 5,
  184. characteristics: enemyData.characteristics || []
  185. };
  186. wave.enemies.push(enemy);
  187. }
  188. }
  189. levelConfig.waves.push(wave);
  190. }
  191. }
  192. console.log(`关卡 ${levelId} 数据验证通过: ${levelConfig.waves.length} 个波次`);
  193. return levelConfig;
  194. } catch (error) {
  195. console.error(`关卡 ${levelId} 数据验证失败:`, error);
  196. return null;
  197. }
  198. }
  199. /**
  200. * 获取所有可用关卡ID列表
  201. */
  202. public getAvailableLevels(): number[] {
  203. return this.levelConfigs
  204. .filter(config => config.configFile !== null)
  205. .map(config => config.levelId)
  206. .sort((a, b) => a - b);
  207. }
  208. /**
  209. * 获取关卡名称
  210. */
  211. public getLevelName(levelId: number): string {
  212. const config = this.levelConfigs.find(c => c.levelId === levelId);
  213. return config ? config.levelName : `关卡 ${levelId}`;
  214. }
  215. /**
  216. * 检查关卡是否已配置
  217. */
  218. public isLevelConfigured(levelId: number): boolean {
  219. const config = this.levelConfigs.find(c => c.levelId === levelId);
  220. return config !== undefined && config.configFile !== null;
  221. }
  222. /**
  223. * 获取总关卡数
  224. */
  225. public getTotalLevels(): number {
  226. return this.totalLevels;
  227. }
  228. /**
  229. * 重新加载所有关卡配置(开发调试用)
  230. */
  231. public reloadAllConfigs() {
  232. this.loadedConfigs.clear();
  233. this.validateConfigs();
  234. console.log('所有关卡配置已重新加载');
  235. }
  236. /**
  237. * 创建示例关卡配置(开发辅助)
  238. */
  239. public generateSampleConfig(levelId: number): LevelConfig {
  240. const samples = {
  241. 1: {
  242. name: '新手引导(草地平原)',
  243. scene: 'grassland',
  244. weapons: ['毛豆射手', '尖胡萝卜'],
  245. waves: [
  246. {
  247. waveId: 1,
  248. enemies: [
  249. { enemyType: '普通僵尸', count: 8, spawnInterval: 5, characteristics: ['中速移动', '无技能'] }
  250. ]
  251. },
  252. {
  253. waveId: 2,
  254. enemies: [
  255. { enemyType: '普通僵尸', count: 10, spawnInterval: 5, characteristics: ['中速移动', '无技能'] }
  256. ]
  257. },
  258. {
  259. waveId: 3,
  260. enemies: [
  261. { enemyType: '路障僵尸', count: 5, spawnInterval: 6, characteristics: ['高生命', '慢速移动'] }
  262. ]
  263. }
  264. ]
  265. },
  266. 2: {
  267. name: '丛林探险(森林场景)',
  268. scene: 'forest',
  269. weapons: ['锯齿草', '西瓜炸弹', '毛豆射手'],
  270. waves: [
  271. {
  272. waveId: 1,
  273. enemies: [
  274. { enemyType: '普通僵尸', count: 10, spawnInterval: 4, characteristics: ['中速移动', '无技能'] }
  275. ]
  276. },
  277. {
  278. waveId: 2,
  279. enemies: [
  280. { enemyType: '漫步僵尸', count: 6, spawnInterval: 5, characteristics: ['左右摇摆', '近战范围大'] }
  281. ]
  282. }
  283. ]
  284. }
  285. };
  286. return samples[levelId] || samples[1];
  287. }
  288. /**
  289. * 调试:打印所有关卡配置信息
  290. */
  291. public debugPrintAllConfigs() {
  292. console.log(`=== 所有关卡配置信息 ===`);
  293. console.log(`总关卡数: ${this.totalLevels}`);
  294. for (const config of this.levelConfigs) {
  295. console.log(`关卡 ${config.levelId}: ${config.levelName}`);
  296. console.log(` JSON文件: ${config.configFile ? config.configFile.name : '未设置'}`);
  297. }
  298. console.log(`已缓存配置数: ${this.loadedConfigs.size}`);
  299. }
  300. }