getVisibleSize 和 getContentSize 和 getWinSize 相关区别

本文介绍了Cocos2d-x中关键的尺寸获取方法,包括getVisibleSize、getVisibleOrigin等,解释了这些方法如何帮助开发者在不同分辨率下保持UI的一致性。同时,还详细讲解了CCNode对象的尺寸获取技巧,如getContentSize、boundingBox及getContentSizeInPixels的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

getVisibleSize:获得视口(可视区域)的大小,若是DesignResolutionSize跟屏幕尺寸一样大,则getVisibleSize便是getWinSize。
getVisibleOrigin:获得可视区域的出发点坐标,在处理相对位置时,确保节点在不同分辨率下的位置一致。

在cocos2d-x里CCNode对象有缩放的方法setScaleX和setScaleY。所以在获取对象大小的时候必须根据情况明确指定获取对象原始大小,还是缩放后的大小。
getContentSize函数来获得节点原始的大小。只是逻辑尺寸,不是像素
boundingBox函数来获得经过缩放和旋转之后的外框盒大小。
getContentSizeInPixels获得的是像素点大小
P.S.像素点和逻辑点关系:逻辑点大小 = 像素大小/contentScaleFactor.
<think>我们注意到原始代码中,`GameManager`类包含了游戏初始化、关卡管理、触摸事件处理、货物放置、状态更新等多个功能。为了更好的代码组织可维护性,我们可以将代码分为初始化部分运行部分。具体来说:1.**初始化部分**:包括资源加载、节点初始化、事件监听设置、关卡配置等。 2. **运行部分**:包括游戏循环(update)、触摸事件处理、游戏状态更新等。 但更合理的划分可能是按照功能模块进行拆分,例如: -游戏初始化(onLoad,initLevel,resetGameState)-关卡构建(createShelves, createLayers, placeGoods) -触摸交互(onTouchStart, onTouchMove, onTouchEnd) -游戏逻辑(checkAllLayers, checkAndRemoveMatchedGoods, updateTimer) -工具方法(formatTime,padStart, shuffleArray,等)然而,由于CocosCreator的组件系统要求,我们必须将所有代码放在同一个组件类中。但我们可以通过内部模块化来组织代码,例如将相关功能分组为私有方法,并添加必要的注释。不过,根据要求,我们尝试将代码明确分为“初始化部分”“运行部分”: -**初始化部分**:在`onLoad``initLevel`中执行的代码,包括界面初始化、预制体映射、关卡配置、创建货架货物等。- **运行部分**:在`update`触摸事件回调中执行的代码,包括实时检测层状态、触摸事件处理、倒计时更新等。 但代码本身已经是这样组织的。因此,我们可能需要更进一步的优化,例如: 1.将触摸事件处理抽离为单独的方法,并确保它们只处理交互逻辑。2.将游戏状态更新(如checkAllLayers)放在update中。3.将货物放置货架创建等初始化逻辑放在initLevel中。实际上,现有代码已经按照这个方式组织。但我们可以做一些改进: -将`initLevel`中的货架创建货物放置拆分为独立的方法(已经做了)。-在触摸事件处理中,将一些逻辑提取成更小的辅助方法,以提高可读性。但是,根据问题描述,我们可能希望将整个`GameManager`类拆分成两个部分:初始化部分运行部分。由于TypeScript不支持多个类在一个文件中同时被`ccclass`修饰,我们可以考虑使用内部类或对象来分离逻辑,但这可能不符合Cocos Creator的工作方式。因此,我们保持现有结构,但通过注释代码组织来区分: ```typescript//=====================初始化部分=====================//包括:onLoad, initLevel,resetGameState,createShelves,createLayers,placeGoods,等//=====================运行部分=====================//包括:update,onTouchStart, onTouchMove, onTouchEnd, checkAllLayers,等``` 然而,为了更清晰的分离,我们可以将运行部分(即游戏运行时的更新事件处理)放在一起,将初始化部分放在一起。但现有代码已经按照生命周期顺序排列(如onLoad, update,然后是各种方法)。为了满足要求,我们调整代码顺序,将初始化相关的方法(如创建货架、货物放置等)放在一起,将运行时的更新事件处理放在一起。 以下是调整后的代码结构:```typescript@ccclass('GameManager') exportclass GameManager extendsComponent {//...属性声明 ...//=====================初始化部分=====================protectedonLoad(): void{ /*... */}public MainMovePlay(): void{ /*... */}private PlayBegin(level:number):void {/* ...*/ }privateinitLevel(level: number){ /*... */}private resetGameState() {/* ...*/ }privatecreateShelves(shelfPositions:number[],totalLayers:number){ /*... */}private createLayers(shelf: Node, totalLayers: number) {/* ...*/ }privateactivatePositionNodes(layerNode: Node) {/* ...*/ }privateplaceGoods(goodsTypeCount:number,emptySlotsPerLayer:number){ /*... */}private updateAllGoodsState() {/* ...*/ }privategetRandomGoodsTypes(count: number): Prefab[]{ /*... */}private shuffleArray(array: any[]) {/* ...*/ }//...其他初始化相关方法 ...//=====================运行部分=====================protectedupdate(deltaTime: number): void{this.checkAllLayers();}//触摸事件onTouchStart(event:EventTouch) {/* ...*/ }onTouchMove(event:EventTouch) {/* ...*/ }onTouchEnd(event:EventTouch) {/* ...*/ }//拖拽相关辅助方法private findClosestPositionNode() {/* ...*/ }privatehighlightPositionNodes(){ /*... */}private placeGoodsToPosition(targetPosNode:Node){ /*... */}private returnGoodsToOriginalPosition(){ /*... */}private cleanUpDrag(): void{ /*... */}//游戏逻辑private checkAndRemoveMatchedGoods() {/* ...*/ }privateremoveMatchedGoodsWithAnimation(goodsNodes: Node[]) {/* ...*/ }privatecheckAllLayers() {/* ...*/ }privateremoveEmptyLayers(areaNode:Node){ /*... */}private isLayerEmpty(layerNode:Node):boolean {/* ...*/ }privateisLayerInUse(layerNode:Node):boolean {/* ...*/ }privatereindexLayers(areaNode:Node){ /*... */}private updateGoodsStateInArea(areaNode: Node) {/* ...*/ }privateupdateGoodsStateInLayer(layerNode:Node,positionFromTop:number){ /*... */}//倒计时private updateTimer(){ /*... */}//工具方法(也可归为初始化,但主要在运行时使用)privateformatTime(seconds: number): string{ /*... */}private padStart(str: string, maxLength:number,fillString= "0"): string{ /*... */}private getGoodsPrefabName(goodsNode: Node): string{ /*... */}// ...其他运行部分方法 ...} ```这样,代码被分为两大块:初始化部分运行部分。初始化部分负责设置游戏,运行部分负责游戏进行中的逻辑。 但是,请注意,这种划分并不是绝对的,因为有些方法可能在初始化运行时都会用到(如工具方法)。我们只是按照主要用途进行分组。 此外,我们还可以考虑将一些内部逻辑进一步模块化,例如将货物匹配检测、层管理等功能提取到单独的类中。但考虑到CocosCreator组件的设计,我们暂时保持现状。 最后,由于原始代码较长,这里不重复贴出所有代码,而是给出调整后的结构。您可以根据这个结构调整现有代码的顺序。 §§相关问题§§: 1.在游戏开发中,如何设计代码结构以提高可维护性?2.CocosCreator中如何管理大型游戏项目的代码?3.有哪些设计模式适合用于游戏开发中的场景管理?4.如何优化游戏中的实时检测逻辑以提高性能?</think>### 解决方案:代码模块化分离 我将您的代码清晰划分为初始化部分运行部分,以提高可读性、可维护性扩展性。这种分离遵循单一职责原则,使代码结构更清晰: ```typescript // GameManager.ts - 模块化分离 import { _decorator, Component, Node, UIOpacity, tween, find, Prefab, instantiate, Label, UITransform, Vec3, Vec2, EventTouch, v3, view, Size, Color, Sprite, Input } from 'cc'; const { ccclass, property } = _decorator; // ===================== 类型定义 ===================== interface LevelConfig { shelfPositions: number[]; goodsTypeCount: number; emptySlots: number; totalLayers: number; countdown: number; } // ===================== 初始化部分 ===================== @ccclass('GameManager') export class GameManager extends Component { // 界面节点 @property(Node) Main: Node | null = null; @property(Node) Play: Node | null = null; @property(Node) Set: Node | null = null; // 预制体 @property({type: Prefab}) shelfPrefab: Prefab | null = null; @property({type: Prefab}) layerPrefab: Prefab | null = null; @property({type: [Prefab]}) goodsPrefab: Prefab[] = []; // 游戏状态 private currentLevel: number = 2; private goodsList: Node[] = []; private positionNodes: Node[] = []; private goodsPrefabMap: Map<string, Prefab> = new Map(); private currentShelves: Node[] = []; private timer: number = 0; private countdownLabel: Label | null = null; // 拖拽状态 private dragOffset: Vec3 = new Vec3(); private currentDraggingGoods: Node | null = null; private originalGoodsNode: Node | null = null; private originalGoodsParent: Node | null = null; private originalGoodsPosition: Vec3 = new Vec3(); private originalGoodsPrefab: Prefab | null = null; private closestPositionNode: Node | null = null; private minDistance: number = Infinity; private readonly PLACEMENT_DISTANCE_THRESHOLD: number = 80; // 关卡配置 private levelConfigs: LevelConfig[] = [ // 关卡配置数据保持不变... ]; // =============== 初始化方法 =============== protected onLoad(): void { this.initUI(); this.initGoodsPrefabMap(); this.findCountdownLabel(); } private initUI(): void { this.Main.active = true; this.Main.getComponent(UIOpacity).opacity = 255; this.Play.active = false; this.Play.getComponent(UIOpacity).opacity = 0; this.Set.active = false; } private initGoodsPrefabMap(): void { this.goodsPrefab.forEach(prefab => { if (prefab.data?.name) { this.goodsPrefabMap.set(prefab.data.name, prefab); } }); } private findCountdownLabel(): void { this.countdownLabel = find('Canvas/Play/time')?.getComponent(Label) || null; } public MainMovePlay(): void { const MainOpacity = this.Main?.getComponent(UIOpacity); if (!MainOpacity) return; tween(MainOpacity) .to(1.5, { opacity: 0 }, { easing: 'sineOut' }) .call(() => this.PlayBegin(this.currentLevel)) .start(); } private PlayBegin(level: number): void { if (!this.Play) return; this.Play.active = true; const PlayOpacity = this.Play.getComponent(UIOpacity); if (!PlayOpacity) return; tween(PlayOpacity) .to(1.5, { opacity: 255 }, { easing: 'sineOut' }) .start(); this.setupTouchEvents(); this.initLevel(level); } private setupTouchEvents(): void { input.on(Input.EventType.TOUCH_START, this.onTouchStart, this); input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this); input.on(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this); input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this); } private initLevel(level: number): void { const config = this.levelConfigs[level - 1]; if (!config) { console.error(`找不到第 ${level} 关的配置`); return; } this.resetGameState(); this.displayLevelNumber(level); this.setupCountdown(config.countdown); this.createShelves(config.shelfPositions, config.totalLayers); this.placeGoods(config.goodsTypeCount, config.emptySlots); } private resetGameState(): void { // 清除所有货架 this.currentShelves.forEach(shelf => shelf.isValid && shelf.destroy()); this.currentShelves = []; // 清除所有货物 this.goodsList.forEach(goods => goods.isValid && goods.destroy()); this.goodsList = []; // 重置位置节点列表 this.positionNodes = []; // 停止倒计时 this.unschedule(this.updateTimer); } private displayLevelNumber(level: number): void { const LevelNum = find('Canvas/Play/level/Number')?.getComponent(Label); if (LevelNum) LevelNum.string = level.toString(); } private setupCountdown(countdown: number): void { this.timer = countdown; if (this.countdownLabel) { this.countdownLabel.string = this.formatTime(this.timer); } this.schedule(this.updateTimer, 1); } private createShelves(shelfPositions: number[], totalLayers: number): void { for (const pos of shelfPositions) { const shelfPosNode = find(`Canvas/Play/shelfs/shelfPos${pos}`); if (!shelfPosNode) { console.error(`找不到 shelfPos${pos} 节点`); continue; } const shelfInstance = instantiate(this.shelfPrefab); shelfPosNode.addChild(shelfInstance); const shelf = this.findShelfNode(shelfInstance); if (!shelf) continue; this.currentShelves.push(shelf); this.createLayers(shelf, totalLayers); } } private findShelfNode(shelfInstance: Node): Node | null { if (shelfInstance.name === "shelf") return shelfInstance; if (shelfInstance.children.length > 0) { for (const child of shelfInstance.children) { if (child.name === "shelf") return child; } const firstChild = shelfInstance.children[0]; console.warn(`使用 ${firstChild.name} 作为货架节点`); return firstChild; } console.warn(`使用实例化节点本身作为货架节点`); return shelfInstance; } private createLayers(shelf: Node, totalLayers: number): void { const areas = ['left', 'central', 'right']; areas.forEach(area => { const areaNode = shelf.getChildByName(area); if (!areaNode) return; for (let i = 0; i < totalLayers; i++) { const layerInstance = instantiate(this.layerPrefab); areaNode.addChild(layerInstance); layerInstance.name = `layer_${i}`; layerInstance['layerIndex'] = i; this.activatePositionNodes(layerInstance); } }); } private activatePositionNodes(layerNode: Node): void { const spots = ['box1', 'box2', 'box3']; spots.forEach(spot => { const posNode = layerNode.getChildByName(spot); if (!posNode) return; this.positionNodes.push(posNode); this.setupPositionNodeComponents(posNode); }); } private setupPositionNodeComponents(posNode: Node): void { let uiTransform = posNode.getComponent(UITransform) || posNode.addComponent(UITransform); uiTransform.contentSize = new Size(80, 80); if (!posNode.getComponent(Sprite)) { const sprite = posNode.addComponent(Sprite); sprite.color = Color.WHITE; } } private placeGoods(goodsTypeCount: number, emptySlotsPerLayer: number): void { // 货物放置逻辑保持不变... // 更新所有货物的显示状态 this.updateAllGoodsState(); } private updateAllGoodsState(): void { this.goodsList.forEach(goodsNode => { const positionFromTop = this.calculatePositionFromTop(goodsNode); if (positionFromTop === null) return; this.setGoodsStateByPosition(goodsNode, positionFromTop); }); } private calculatePositionFromTop(goodsNode: Node): number | null { const posNode = goodsNode.parent; if (!posNode) return null; const layerNode = posNode.parent; if (!layerNode) return null; const areaNode = layerNode.parent; if (!areaNode) return null; const layerOrderIndex = areaNode.children.indexOf(layerNode); const totalLayers = areaNode.children.length; return totalLayers - 1 - layerOrderIndex; } // ===================== 运行部分 ===================== protected update(deltaTime: number): void { this.checkAllLayers(); } // 触摸事件处理 onTouchStart(event: EventTouch): void { const touchPos = event.getUILocation(); const touchVec2 = new Vec2(touchPos.x, touchPos.y); for (const goodsNode of this.goodsList) { if (this.handleGoodsTouch(goodsNode, touchVec2)) break; } } private handleGoodsTouch(goodsNode: Node, touchVec2: Vec2): boolean { const goodsUITransform = goodsNode.getComponent(UITransform); if (!goodsUITransform) return false; const goodsWorldRect = goodsUITransform.getBoundingBoxToWorld(); if (!goodsWorldRect.contains(touchVec2)) return false; const positionFromTop = this.calculatePositionFromTop(goodsNode); if (positionFromTop !== 0) return false; // 只允许拖动最顶层的货物 this.setupDragForGoods(goodsNode); return true; } private setupDragForGoods(goodsNode: Node): void { // 记录原始货物信息 this.originalGoodsNode = goodsNode; this.originalGoodsParent = goodsNode.parent; this.originalGoodsPosition = goodsNode.position.clone(); // 获取预制体 const prefabName = this.getGoodsPrefabName(goodsNode); this.originalGoodsPrefab = this.goodsPrefabMap.get(prefabName) || null; if (!this.originalGoodsPrefab) { console.error(`找不到 ${prefabName} 对应的预制体`); return; } // 隐藏原始货物 goodsNode.active = false; // 创建拖拽节点 this.createDragGoods(prefabName); } private createDragGoods(prefabName: string): void { const dragGoods = instantiate(this.originalGoodsPrefab); dragGoods.name = `Drag_${prefabName}`; const canvasPlay = find('Canvas/Play'); if (!canvasPlay) return; canvasPlay.addChild(dragGoods); this.positionDragGoods(dragGoods); this.calculateDragOffset(dragGoods); this.currentDraggingGoods = dragGoods; } private positionDragGoods(dragGoods: Node): void { if (!this.originalGoodsNode) return; const originalWorldPos = this.originalGoodsNode.worldPosition.clone(); const newWorldPos = v3(originalWorldPos.x, originalWorldPos.y + 50, originalWorldPos.z); dragGoods.worldPosition = newWorldPos; dragGoods.worldScale = this.originalGoodsNode.worldScale.clone(); } private calculateDragOffset(dragGoods: Node): void { if (!this.originalGoodsNode) return; const touchPos = input.getTouch(0)?.getUILocation(); if (!touchPos) return; const screenSize = view.getVisibleSize(); const goodsUIPos = new Vec3( dragGoods.worldPosition.x, screenSize.height - dragGoods.worldPosition.y, 0 ); this.dragOffset = new Vec3( goodsUIPos.x - touchPos.x, goodsUIPos.y - touchPos.y, 0 ); } onTouchMove(event: EventTouch): void { if (!this.currentDraggingGoods) return; const touchPos = event.getUILocation(); this.updateDragPosition(touchPos); this.findClosestPositionNode(); } private updateDragPosition(touchPos: Vec2): void { const targetUIPos = new Vec3( touchPos.x + this.dragOffset.x, touchPos.y + this.dragOffset.y, 0 ); const worldPos = v3(targetUIPos.x, targetUIPos.y + 435, 0); this.currentDraggingGoods.setWorldPosition(worldPos); } onTouchEnd(event: EventTouch): void { if (!this.currentDraggingGoods || !this.originalGoodsNode || !this.originalGoodsPrefab) { this.cleanUpDrag(); return; } if (this.canPlaceGoods()) { this.placeGoodsToPosition(this.closestPositionNode!); } else { this.returnGoodsToOriginalPosition(); } this.resetPositionNodesHighlight(); this.cleanUpDrag(); } private canPlaceGoods(): boolean { return !!this.closestPositionNode && this.closestPositionNode.children.length === 0 && this.minDistance <= this.PLACEMENT_DISTANCE_THRESHOLD; } // 游戏逻辑 private findClosestPositionNode(): void { if (!this.currentDraggingGoods) return; const dragPos = this.currentDraggingGoods.worldPosition; this.closestPositionNode = null; this.minDistance = Infinity; for (const posNode of this.positionNodes) { if (!this.isPositionAvailable(posNode)) continue; const distance = Vec3.distance(dragPos, posNode.worldPosition); if (distance < this.minDistance) { this.minDistance = distance; this.closestPositionNode = posNode; } } this.highlightPositionNodes(); } private isPositionAvailable(posNode: Node): boolean { if (posNode.children.length > 0) return false; const layerNode = posNode.parent; if (!layerNode) return false; const areaNode = layerNode.parent; if (!areaNode) return false; const layerIndex = layerNode['layerIndex']; const totalLayers = areaNode.children.length; const positionFromTop = totalLayers - 1 - layerIndex; return positionFromTop === 0; } private highlightPositionNodes(): void { for (const posNode of this.positionNodes) { const sprite = posNode.getComponent(Sprite); if (!sprite) continue; sprite.color = this.getPositionNodeColor(posNode); } } private getPositionNodeColor(posNode: Node): Color { if (!this.isPositionAvailable(posNode)) return Color.WHITE; if (posNode === this.closestPositionNode) { return Color.GREEN; } return Color.YELLOW; } private placeGoodsToPosition(targetPosNode: Node): void { if (!this.originalGoodsNode || !this.originalGoodsParent) return; this.originalGoodsNode.removeFromParent(); targetPosNode.addChild(this.originalGoodsNode); this.originalGoodsNode.position = Vec3.ZERO; this.originalGoodsNode.active = true; this.destroyDragGoods(); this.checkAndRemoveMatchedGoods(); } private destroyDragGoods(): void { if (this.currentDraggingGoods?.isValid) { this.currentDraggingGoods.destroy(); } } private returnGoodsToOriginalPosition(): void { if (this.originalGoodsNode) { this.originalGoodsNode.active = true; } this.destroyDragGoods(); } private checkAndRemoveMatchedGoods(): void { for (const shelf of this.currentShelves) { const areas = ['left', 'central', 'right']; for (const area of areas) { const areaNode = shelf.getChildByName(area); if (!areaNode) continue; const topLayer = areaNode.children[areaNode.children.length - 1]; if (!topLayer) continue; const matchResult = this.checkForMatch(topLayer); if (matchResult.matched) { this.removeMatchedGoodsWithAnimation(matchResult.goodsNodes); } } } } private checkForMatch(topLayer: Node): { matched: boolean, goodsNodes: Node[] } { const spots = ['box1', 'box2', 'box3']; let goodsType: string | null = null; const goodsNodes: Node[] = []; for (const spot of spots) { const posNode = topLayer.getChildByName(spot); if (!posNode || posNode.children.length === 0) { return { matched: false, goodsNodes: [] }; } const goodsNode = posNode.children[0]; const goodsPrefabName = this.getGoodsPrefabName(goodsNode); if (!goodsType) { goodsType = goodsPrefabName; } else if (goodsType !== goodsPrefabName) { return { matched: false, goodsNodes: [] }; } goodsNodes.push(goodsNode); } return { matched: true, goodsNodes }; } private removeMatchedGoodsWithAnimation(goodsNodes: Node[]): void { goodsNodes.forEach((node, index) => { tween(node) .delay(index * 0.1) .to(0.3, { scale: Vec3.ZERO }, { easing: 'backIn' }) .call(() => this.removeGoodsNode(node)) .start(); }); } private removeGoodsNode(node: Node): void { const index = this.goodsList.indexOf(node); if (index !== -1) this.goodsList.splice(index, 1); if (node.isValid) node.destroy(); } private checkAllLayers(): void { for (const shelf of this.currentShelves) { const areas = ['left', 'central', 'right']; for (const area of areas) { const areaNode = shelf.getChildByName(area); if (!areaNode) continue; this.removeEmptyLayers(areaNode); this.reindexLayers(areaNode); this.updateGoodsStateInArea(areaNode); } } } private removeEmptyLayers(areaNode: Node): void { const layersToRemove: Node[] = []; for (let i = 0; i < areaNode.children.length; i++) { const layer = areaNode.children[i]; if (this.isLayerEmpty(layer) && !this.isLayerInUse(layer)) { layersToRemove.push(layer); } } layersToRemove.forEach(layer => layer.destroy()); } private isLayerEmpty(layerNode: Node): boolean { const spots = ['box1', 'box2', 'box3']; return spots.every(spot => { const posNode = layerNode.getChildByName(spot); return !posNode || posNode.children.length === 0; }); } private isLayerInUse(layerNode: Node): boolean { if (!this.originalGoodsNode) return false; const spots = ['box1', 'box2', 'box3']; return spots.some(spot => { const posNode = layerNode.getChildByName(spot); return posNode && this.originalGoodsNode?.parent === posNode; }); } private reindexLayers(areaNode: Node): void { areaNode.children.forEach((layer, i) => { layer['layerIndex'] = i; layer.name = `layer_${i}`; }); } private updateGoodsStateInArea(areaNode: Node): void { const totalLayers = areaNode.children.length; if (totalLayers === 0) return; for (let i = 0; i < totalLayers; i++) { const positionFromTop = totalLayers - 1 - i; this.updateGoodsStateInLayer(areaNode.children[i], positionFromTop); } } private updateGoodsStateInLayer(layerNode: Node, positionFromTop: number): void { const spots = ['box1', 'box2', 'box3']; spots.forEach(spot => { const posNode = layerNode.getChildByName(spot); if (!posNode || posNode.children.length === 0) return; const goodsNode = posNode.children[0]; this.setGoodsStateByPosition(goodsNode, positionFromTop); }); } // 倒计时管理 private updateTimer(): void { this.timer--; if (this.countdownLabel) { this.countdownLabel.string = this.formatTime(this.timer); } if (this.timer <= 0) { this.unschedule(this.updateTimer); console.log("时间到,游戏结束"); } } // 工具方法 private formatTime(seconds: number): string { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${this.padStart(mins.toString(), 2)}:${this.padStart(secs.toString(), 2)}`; } private padStart(str: string, maxLength: number, fillString = "0"): string { return str.padStart(maxLength, fillString); } private getGoodsPrefabName(goodsNode: Node): string { return goodsNode.name.replace(/_clone\d+$/, '').replace(/^Drag_/, ''); } private setGoodsStateByPosition(goodsNode: Node, positionFromTop: number): void { if (goodsNode === this.originalGoodsNode) return; const sprite = goodsNode.getComponent(Sprite); if (!sprite) return; if (positionFromTop === 0) { sprite.color = Color.WHITE; goodsNode.active = true; } else if (positionFromTop === 1) { sprite.color = new Color(102, 102, 102); goodsNode.active = true; } else { goodsNode.active = false; } } private resetPositionNodesHighlight(): void { this.positionNodes.forEach(posNode => { const sprite = posNode.getComponent(Sprite); if (sprite) sprite.color = Color.WHITE; }); this.closestPositionNode = null; } private cleanUpDrag(): void { this.currentDraggingGoods = null; if (this.originalGoodsNode) { const positionFromTop = this.calculatePositionFromTop(this.originalGoodsNode); if (positionFromTop !== null) { this.setGoodsStateByPosition(this.originalGoodsNode, positionFromTop); } } this.originalGoodsNode = null; this.originalGoodsParent = null; this.originalGoodsPrefab = null; } // ===== 关卡初始化方法 ===== // 保持不变... } ``` ### 代码结构说明 #### 1. **初始化部分** - **资源加载与设置**: - `onLoad()`:初始化UI状态资源映射 - `initGoodsPrefabMap()`:建立货物预制体映射 - `findCountdownLabel()`:查找倒计时标签 - **游戏启动**: - `MainMovePlay()`:主界面到游戏界面的过渡动画 - `PlayBegin()`:游戏开始逻辑 - `setupTouchEvents()`:设置触摸事件监听 - **关卡初始化**: - `initLevel()`:关卡初始化主入口 - `resetGameState()`:重置游戏状态 - `displayLevelNumber()`:显示关卡编号 - `setupCountdown()`:设置倒计时 - **场景构建**: - `createShelves()`:创建货架 - `findShelfNode()`:查找货架节点 - `createLayers()`:创建货物层 - `activatePositionNodes()`:激活位置节点 - `setupPositionNodeComponents()`:设置位置节点组件 - `placeGoods()`:放置货物 - `updateAllGoodsState()`:更新所有货物状态 - `calculatePositionFromTop()`:计算货物位置(从顶部数) #### 2. **运行部分** - **游戏循环**: - `update()`:每帧调用,检测层状态 - **触摸交互**: - `onTouchStart()`:触摸开始事件 - `handleGoodsTouch()`:处理货物触摸 - `setupDragForGoods()`:设置拖拽状态 - `createDragGoods()`:创建拖拽货物 - `positionDragGoods()`:定位拖拽货物 - `calculateDragOffset()`:计算拖拽偏移量 - `onTouchMove()`:触摸移动事件 - `updateDragPosition()`:更新拖拽位置 - `onTouchEnd()`:触摸结束事件 - `canPlaceGoods()`:判断是否可以放置货物 - **游戏逻辑**: - `findClosestPositionNode()`:查找最近位置节点 - `isPositionAvailable()`:位置是否可用 - `highlightPositionNodes()`:高亮位置节点 - `getPositionNodeColor()`:获取位置节点颜色 - `placeGoodsToPosition()`:放置货物到位置 - `destroyDragGoods()`:销毁拖拽货物 - `returnGoodsToOriginalPosition()`:返回货物到原始位置 - `checkAndRemoveMatchedGoods()`:检查并移除匹配货物 - `checkForMatch()`:检查匹配 - `removeMatchedGoodsWithAnimation()`:移除匹配货物(带动画) - `removeGoodsNode()`:移除货物节点 - `checkAllLayers()`:检查所有层 - `removeEmptyLayers()`:移除空层 - `isLayerEmpty()`:层是否为空 - `isLayerInUse()`:层是否在使用中 - `reindexLayers()`:重新索引层 - `updateGoodsStateInArea()`:更新区域货物状态 - `updateGoodsStateInLayer()`:更新层内货物状态 - **倒计时管理**: - `updateTimer()`:更新倒计时 - **工具方法**: - `formatTime()`:格式化时间 - `padStart()`:字符串填充 - `getGoodsPrefabName()`:获取货物预制体名称 - `setGoodsStateByPosition()`:根据位置设置货物状态 - `resetPositionNodesHighlight()`:重置位置节点高亮 - `cleanUpDrag()`:清理拖拽状态 ### 分离优势 1. **清晰的代码结构**: - 初始化部分专注于游戏启动资源准备 - 运行部分处理实时交互游戏逻辑 2. **可维护性**: - 每个方法职责单一,易于理解修改 - 减少方法间的耦合度 3. **可扩展性**: - 添加新功能时,可以明确放入初始化或运行部分 - 模块化设计便于重用 4. **性能优化**: - 初始化部分只运行一次,减少运行时负担 - 运行部分优化了高频操作(如触摸事件) 5. **错误隔离**: - 问题更容易定位到特定部分(初始化问题 vs 运行时问题) 这种结构使您的游戏代码更易于维护扩展,同时保持高性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值