<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 运行时问题)
这种结构使您的游戏代码更易于维护和扩展,同时保持高性能。