import { _decorator, Component, Prefab, Sprite, SpriteFrame, instantiate, Node, Vec3, assetManager, AssetManager, resources, randomRangeInt, randomRange, UITransform, Vec2 } from "cc"; import FindItem, { ItemState } from "./FindItem"; import { LayerMgr } from "../../script/Manager/LayerMgr"; import { CCFloat } from "cc"; import EventMgr from "../../script/Manager/EventMgr"; import { Difficulty, Skin } from "../../script/Manager/LocalDataMgr"; const { ccclass, property } = _decorator; // 关卡模式枚举 export enum LevelMode { EDIT, // 编辑模式 NORMAL // 正常选择模式 } @ccclass('LevelContainer') export class LevelContainer extends Component { @property({ type: Node, tooltip: '遮罩' }) mask: Node = null; @property({ type: Sprite, tooltip: '背景图' }) lvlBg: Sprite = null; @property({ type: Prefab, tooltip: '物品预制体' }) findItemPrefab: Prefab = null; @property({ type: CCFloat, tooltip: '最小缩放比例' }) minScale: number = 0.1; @property({ type: CCFloat, tooltip: '最大缩放比例' }) maxScale: number = 1.5; @property({ type: Node, tooltip: '物品容器' }) itemContainer: Node = null; _findItems: FindItem[] = []; _currentMode: LevelMode = LevelMode.NORMAL; _totalItems: number = 0; _foundItems: number = 0; _onLevelComplete: Function = null; _bgPath: string = ""; _itemSpinePaths: string[] = []; _itemcnt: number = 0; _itemScales: number[] = []; _defaultScales: number[] = [1.18, 1.16, 1.14, 0.3976, 0.3690, 0.3029, 0.0667, 0.0766, 0.0531, 0.0716]; start() { // LayerMgr.instance.loadBundle("editor", () => { // this.genLvlContent("image/bg1", ["prefab/spine/spine01"], 10); // }); this.addResetListener() } /** * 切换编辑模式 * @param isEdit 是否为编辑模式 */ swtichEditMode(isEdit: boolean) { this._currentMode = isEdit ? LevelMode.EDIT : LevelMode.NORMAL; // 更新所有物品的状态 this._findItems.forEach(item => { if (isEdit) { item.enterEditMode(); } else { item.exitEditMode(); } }); // 重置找到的物品计数 if (!isEdit) { this._foundItems = 0; } } /** * 生成关卡内容 * @param bgPath 背景图路径 * @param Items 物品预制体路径数组 * @param itemcnt 物品数量 * @param itemScales 物品缩放数组,为空时随机缩放 */ public genLvlContent(bgPath: string, itemSpinePaths: string[], itemcnt: number, itemScales: number[] = null) { // 清空现有物品 this.clearItems(); this._bgPath = bgPath; this._totalItems = itemcnt; this._itemSpinePaths = itemSpinePaths; this._itemcnt = itemcnt; this._itemScales = itemScales || this._defaultScales; // 加载背景图 this.loadSprite(bgPath, (spriteFrame: SpriteFrame) => { console.log("loadSprite", bgPath, spriteFrame); if (this.lvlBg && spriteFrame) { this.lvlBg.spriteFrame = spriteFrame; } }); var randomIndex = randomRangeInt(0, itemSpinePaths.length); // console.log("物品数量", itemcnt,"物品缩放数组长度",Difficulty.difficutyData.scales.length,"动画路径长度",itemSpinePaths.length); // 生成物品 for (let i = 0; i < itemcnt; i++) { // 随机选择一个物品 randomIndex = Skin.skinData.singleSelect == "true"?randomIndex: randomRangeInt(0, itemSpinePaths.length); this.createItem(itemSpinePaths[randomIndex], Difficulty.difficutyData.scales[i]); } } /** * 创建物品 * @param itemPrefab 物品预制体 * @param scale 缩放值,null时随机缩放 */ private createItem(itemPrefabPath: string, scale: number | null) { console.log("当前spine路径", itemPrefabPath); const itemNode = instantiate(this.findItemPrefab); itemNode.parent = this.itemContainer || this.node; const findItem = itemNode.getComponent(FindItem); if (findItem) { var size = this.mask.getComponent(UITransform).contentSize; // 设置缩放 const finalScale = scale !== null ? scale : randomRange(this.minScale, this.maxScale); // 设置随机位置, 不要出边框 var itemSize = itemNode.getComponent(UITransform).contentSize; var w = itemSize.width * finalScale; var h = itemSize.height * finalScale; // 随机,并尽量不要重叠 let tryCount = 0; let maxTry = 50; let pos: Vec3; let overlap = false; do { overlap = false; const randomX = randomRange(-size.width / 2 + w / 2, size.width / 2 - w / 2); const randomY = randomRange(-size.height / 2 + h / 2, size.height / 2 - h / 2); pos = new Vec3(randomX, randomY, 0); // 检查与已放置物品是否重叠 for (let other of this._findItems) { let otherPos = other.node.getPosition(); let otherScale = other.node.scale.x; let otherSize = other.node.getComponent(UITransform).contentSize; let otherW = otherSize.width * otherScale; let otherH = otherSize.height * otherScale; // 简单的AABB重叠检测 if ( Math.abs(pos.x - otherPos.x) < (w + otherW) / 2 && Math.abs(pos.y - otherPos.y) < (h + otherH) / 2 ) { overlap = true; break; } } tryCount++; } while (overlap && tryCount < maxTry); itemNode.setPosition(pos); findItem.setData({ position: itemNode.getPosition(), scale: finalScale }); // 添加spine内容 this.loadPrefab(itemPrefabPath, (prefab: Prefab) => { findItem.addSpineContentByPrefab(prefab); }); // 设置状态 findItem.setState(this._currentMode === LevelMode.EDIT ? ItemState.EDIT : ItemState.NORMAL); // 添加到物品列表 this._findItems.push(findItem); // 监听物品状态变化 this.setupItemListeners(findItem); } } /** * 设置物品监听器 * @param findItem 物品组件 */ private setupItemListeners(findItem: FindItem) { // 监听物品点击事件 findItem.node.on("itemFound", () => { if (this._currentMode === LevelMode.NORMAL) { this._foundItems++; console.log("itemFound", this._foundItems, this._totalItems); // 检查是否所有物品都被找到 if (this._foundItems >= this._totalItems) { this.onLevelComplete(); } } }); } /** * 关卡完成回调 */ private onLevelComplete() { if (this._onLevelComplete) { this._onLevelComplete(); } // 可以在这里添加完成动画或音效 console.log("关卡完成!所有物品都已找到"); } /** * 清空所有物品 */ private clearItems() { // this._findItems.forEach(item => { // if (item && item.node) { // item.node.destroy(); // } // }); let count = this._findItems.length; for(let i = 0; i < count; i++) { const item = this._findItems.pop(); if (item && item.node) { item.node.destroy(); } } this._findItems = []; this._foundItems = 0; this._totalItems = 0; } /** * 设置关卡完成回调 * @param callback 完成回调函数 */ public setLevelCompleteCallback(callback: Function) { this._onLevelComplete = callback; } /** * 显示提示(高亮未找到的物品) */ public showHint() { this._findItems.forEach(item => { if (item.getState() === ItemState.NORMAL) { item.showHint(); } }); } public showOneHint() { for(let i = 0; i < this._findItems.length; i++) { if(this._findItems[i].getState() === ItemState.NORMAL) { this._findItems[i].showHint() break } } } public reset() { this.clearItems(); this.swtichEditMode(false); this.genLvlContent(this._bgPath, this._itemSpinePaths, this._itemcnt, this._itemScales); } /** * 获取关卡数据(用于保存) */ public getLevelData() { return { items: this._findItems.map(item => item.getData()), totalItems: this._totalItems }; } /** * 加载精灵图片 * @param path 图片路径 * @param callback 回调函数 */ loadSprite(path: string, callback: (spriteFrame: SpriteFrame) => void) { LayerMgr.instance.loadSprite("editor", path, callback); } prefabMap: Map = new Map(); /** * 加载预制体 * @param path 预制体路径 * @param callback 回调函数 */ loadPrefab(path: string, callback: (prefab: Prefab) => void) { if (this.prefabMap.has(path)) { callback(this.prefabMap.get(path)); return; } LayerMgr.instance.loadPrefab("editor", path, (prefab: Prefab) => { this.prefabMap.set(path, prefab); callback(prefab); }); } addResetListener() { EventMgr.ins.addEventListener("regenerate",this.regenerate,this) } regenerate() { this.clearItems(); this.swtichEditMode(false); this.genLvlContent(this._bgPath, Skin.getSelectSkinPath(), Difficulty.difficutyData.difficutyCount, this._itemScales); } showOrHideScale(isShow: boolean) { for(let i = 0; i < this._findItems.length; i++) { this._findItems[i].showOrHideScale(isShow) } } }