LevelConfigManager.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  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. backgroundImage?: string; // 背景图片路径
  12. weapons: string[];
  13. waves: WaveConfig[];
  14. levelSettings?: {
  15. initialHealth?: number;
  16. timeLimit?: number;
  17. difficulty?: string;
  18. energyMax?: number;
  19. healthMultiplier?: number;
  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. // 检查是否所有关卡都有配置
  97. for (let i = 1; i <= this.totalLevels; i++) {
  98. const config = this.levelConfigs.find(c => c.levelId === i);
  99. if (!config) {
  100. console.warn(`关卡 ${i} 缺少配置`);
  101. } else if (!config.configFile) {
  102. console.warn(`关卡 ${i} 缺少JSON文件`);
  103. }
  104. }
  105. // 检查是否有重复的关卡ID
  106. const levelIds = this.levelConfigs.map(c => c.levelId);
  107. const duplicates = levelIds.filter((id, index) => levelIds.indexOf(id) !== index);
  108. if (duplicates.length > 0) {
  109. console.error(`发现重复的关卡ID: ${duplicates.join(', ')}`);
  110. }
  111. }
  112. /**
  113. * 获取关卡配置
  114. */
  115. public async getLevelConfig(levelId: number): Promise<LevelConfig | null> {
  116. // 先从缓存中查找
  117. if (this.loadedConfigs.has(levelId)) {
  118. return this.loadedConfigs.get(levelId);
  119. }
  120. // 查找对应的配置项
  121. const configItem = this.levelConfigs.find(c => c.levelId === levelId);
  122. if (!configItem || !configItem.configFile) {
  123. console.error(`关卡 ${levelId} 配置不存在或缺少JSON文件`);
  124. return null;
  125. }
  126. try {
  127. // 解析JSON数据
  128. const jsonData = configItem.configFile.json;
  129. if (!jsonData) {
  130. console.error(`关卡 ${levelId} JSON文件解析失败`);
  131. return null;
  132. }
  133. // 验证JSON数据结构
  134. const levelConfig = this.validateLevelConfigData(jsonData, levelId);
  135. if (levelConfig) {
  136. // 缓存配置
  137. this.loadedConfigs.set(levelId, levelConfig);
  138. return levelConfig;
  139. }
  140. } catch (error) {
  141. console.error(`关卡 ${levelId} 配置加载失败:`, error);
  142. }
  143. return null;
  144. }
  145. /**
  146. * 验证关卡配置数据结构
  147. */
  148. private validateLevelConfigData(jsonData: any, levelId: number): LevelConfig | null {
  149. try {
  150. // 基本字段验证
  151. const weapons = jsonData.weapons || jsonData.availableWeapons;
  152. if (!jsonData.name || !jsonData.scene || !weapons) {
  153. console.error(`关卡 ${levelId} JSON数据缺少必要字段`);
  154. if (!jsonData.name) console.error(` 缺少字段: name`);
  155. if (!jsonData.scene) console.error(` 缺少字段: scene`);
  156. if (!weapons) console.error(` 缺少字段: weapons 或 availableWeapons`);
  157. return null;
  158. }
  159. // 检查武器列表是否为空
  160. if (!Array.isArray(weapons) || weapons.length === 0) {
  161. console.error(`关卡 ${levelId} 没有配置武器列表`);
  162. return null;
  163. }
  164. // 构建关卡配置对象
  165. const levelConfig: LevelConfig = {
  166. levelId: levelId,
  167. name: jsonData.name,
  168. scene: jsonData.scene,
  169. description: jsonData.description,
  170. backgroundImage: jsonData.backgroundImage, // 添加背景图片字段
  171. weapons: weapons,
  172. waves: [],
  173. levelSettings: {
  174. timeLimit: jsonData.timeLimit,
  175. difficulty: jsonData.difficulty,
  176. healthMultiplier: jsonData.healthMultiplier,
  177. ...jsonData.levelSettings
  178. }
  179. };
  180. // 验证波次配置
  181. if (Array.isArray(jsonData.waves)) {
  182. for (const waveData of jsonData.waves) {
  183. const wave: WaveConfig = {
  184. waveId: waveData.waveId || 0,
  185. enemies: []
  186. };
  187. if (Array.isArray(waveData.enemies)) {
  188. for (const enemyData of waveData.enemies) {
  189. const enemy: EnemyWaveConfig = {
  190. enemyType: enemyData.enemyType || '',
  191. count: enemyData.count || 1,
  192. spawnInterval: enemyData.spawnInterval || 5,
  193. characteristics: enemyData.characteristics || []
  194. };
  195. wave.enemies.push(enemy);
  196. }
  197. }
  198. levelConfig.waves.push(wave);
  199. }
  200. }
  201. return levelConfig;
  202. } catch (error) {
  203. console.error(`关卡 ${levelId} 数据验证失败:`, error);
  204. return null;
  205. }
  206. }
  207. /**
  208. * 获取所有可用关卡ID列表
  209. */
  210. public getAvailableLevels(): number[] {
  211. return this.levelConfigs
  212. .filter(config => config.configFile !== null)
  213. .map(config => config.levelId)
  214. .sort((a, b) => a - b);
  215. }
  216. /**
  217. * 获取关卡名称
  218. */
  219. public getLevelName(levelId: number): string {
  220. const config = this.levelConfigs.find(c => c.levelId === levelId);
  221. return config ? config.levelName : `关卡 ${levelId}`;
  222. }
  223. /**
  224. * 检查关卡是否已配置
  225. */
  226. public isLevelConfigured(levelId: number): boolean {
  227. const config = this.levelConfigs.find(c => c.levelId === levelId);
  228. return config !== undefined && config.configFile !== null;
  229. }
  230. /**
  231. * 获取总关卡数
  232. */
  233. public getTotalLevels(): number {
  234. return this.totalLevels;
  235. }
  236. /**
  237. * 重新加载所有关卡配置(开发调试用)
  238. */
  239. public reloadAllConfigs() {
  240. this.loadedConfigs.clear();
  241. this.validateConfigs();
  242. }
  243. /**
  244. * 创建示例关卡配置(开发辅助)
  245. */
  246. public generateSampleConfig(levelId: number): LevelConfig {
  247. const samples = {
  248. 1: {
  249. name: '新手引导(草地平原)',
  250. scene: 'grassland',
  251. backgroundImage: 'images/LevelBackground/BG1',
  252. weapons: ['毛豆射手', '尖胡萝卜'],
  253. waves: [
  254. {
  255. waveId: 1,
  256. enemies: [
  257. { enemyType: '普通僵尸', count: 8, spawnInterval: 5, characteristics: ['中速移动', '无技能'] }
  258. ]
  259. },
  260. {
  261. waveId: 2,
  262. enemies: [
  263. { enemyType: '普通僵尸', count: 10, spawnInterval: 5, characteristics: ['中速移动', '无技能'] }
  264. ]
  265. },
  266. {
  267. waveId: 3,
  268. enemies: [
  269. { enemyType: '路障僵尸', count: 5, spawnInterval: 6, characteristics: ['高生命', '慢速移动'] }
  270. ]
  271. }
  272. ]
  273. },
  274. 2: {
  275. name: '丛林探险(森林场景)',
  276. scene: 'forest',
  277. backgroundImage: 'images/LevelBackground/BG2',
  278. weapons: ['锯齿草', '西瓜炸弹', '毛豆射手'],
  279. waves: [
  280. {
  281. waveId: 1,
  282. enemies: [
  283. { enemyType: '普通僵尸', count: 10, spawnInterval: 4, characteristics: ['中速移动', '无技能'] }
  284. ]
  285. },
  286. {
  287. waveId: 2,
  288. enemies: [
  289. { enemyType: '漫步僵尸', count: 6, spawnInterval: 5, characteristics: ['左右摇摆', '近战范围大'] }
  290. ]
  291. }
  292. ]
  293. }
  294. };
  295. return samples[levelId] || samples[1];
  296. }
  297. /**
  298. * 调试:打印所有关卡配置信息
  299. */
  300. public debugPrintAllConfigs() {
  301. console.log(`=== 所有关卡配置信息 ===`);
  302. console.log(`总关卡数: ${this.totalLevels}`);
  303. for (const config of this.levelConfigs) {
  304. console.log(`关卡 ${config.levelId}: ${config.levelName}`);
  305. console.log(` JSON文件: ${config.configFile ? config.configFile.name : '未设置'}`);
  306. }
  307. }
  308. }