我们游戏有三种状态,初始化、游戏中、游戏重置或者结算,和下棋类似,大部分游戏都可以粗略分解为这样的三个状态。
因此我们也可以定义这样的枚举来描述游戏状态。
enum GameState{
GS_INIT,
GS_PLAYING,
GS_END,
};
将上述的代码放在枚举 BlockType 附近。
这里我们为 GameManager 添加一个 setCurState 的方法提供给外界,使其可以用于控制游戏的状态:
setCurState (value: GameState) {
switch(value) {
case GameState.GS_INIT:
break;
case GameState.GS_PLAYING:
break;
case GameState.GS_END:
break;
}
}
添加一个 init 方法用于表示进入到 GS_INIT 时游戏的处理:
init() {}
同时在 setCurState 的时候调用它:
setCurState (value: GameState) {
switch(value) {
case GameState.GS_INIT:
this.init();
break;
case GameState.GS_PLAYING:
break;
case GameState.GS_END:
break;
}
}
为了在游戏开始时不让用户操作角色,而在游戏进行时让用户操作角色,我们需要动态地开启和关闭角色对鼠标消息的监听。在 PlayerController 脚本中做如下修改:
start () {
//input.on(Input.EventType.MOUSE_UP, this.onMouseUp, this);
}
setInputActive(active: boolean) {
if (active) {
input.on(Input.EventType.MOUSE_UP, this.onMouseUp, this);
} else {
input.off(Input.EventType.MOUSE_UP, this.onMouseUp, this);
}
}
此时的 GameManager 看起来是这样的:
import { _decorator, CCInteger, Component, instantiate, Node, Prefab } from 'cc';
import { BLOCK_SIZE, PlayerController } from './PlayerController';
const { ccclass, property } = _decorator;
enum BlockType{
BT_NONE,
BT_STONE,
};
enum GameState{
GS_INIT,
GS_PLAYING,
GS_END,
};
@ccclass('GameManager')
export class GameManager extends Component {
@property({type: Prefab})
public boxPrefab: Prefab|null = null;
@property({type: CCInteger})
public roadLength: number = 50;
private _road: BlockType[] = [];
start() {
this.setCurState(GameState.GS_INIT); // 第一初始化要在 start 里面调用
}
init() {
this.generateRoad();
}
setCurState (value: GameState) {
switch(value) {
case GameState.GS_INIT:
this.init();
break;
case GameState.GS_PLAYING:
break;
case GameState.GS_END:
break;
}
}
generateRoad() {
this.node.removeAllChildren();
this._road = [];
// startPos
this._road.push(BlockType.BT_STONE);
for (let i = 1; i < this.roadLength; i++) {
if (this._road[i - 1] === BlockType.BT_NONE) {
this._road.push(BlockType.BT_STONE);
} else {
this._road.push(Math.floor(Math.random() * 2));
}
}
for (let j = 0; j < this._road.length; j++) {
let block: Node | null = this.spawnBlockByType(this._road[j]);
if (block) {
this.node.addChild(block);
block.setPosition(j * BLOCK_SIZE, 0, 0);
}
}
}
spawnBlockByType(type: BlockType) {
if (!this.boxPrefab) {
return null;
}
let block: Node | null = null;
switch (type) {
case BlockType.BT_STONE:
block = instantiate(this.boxPrefab);
break;
}
return block;
}
}
接下来我们分析下在每个状态下所需要处理的事情:
GS_INIT:状态下需要初始化地图、将角色放回到初始点、显示游戏的UI,因此在属性中下列属性:
@property({ type: Node })
public startMenu: Node | null = null; // 开始的 UI
@property({ type: PlayerController })
public playerCtrl: PlayerController | null = null; // 角色控制器
@property({type: Label})
public stepsLabel: Label|null = null; // 计步器
在 init 方法中需要做如下的处理:
init() {
if (this.startMenu) {
this.startMenu.active = true;
}
this.generateRoad();
if (this.playerCtrl) {
this.playerCtrl.setInputActive(false);
this.playerCtrl.node.setPosition(Vec3.ZERO);
this.playerCtrl.reset();
}
}
init 时我们先显示 StartMenu、创建地图以及重设角色的为和状态并禁用角色输入。
GS_PLAYING:在状态下隐藏 StartMenu、重设计步器的数值以及启用用户输入:
if (this.startMenu) {
this.startMenu.active = false;
}
if (this.stepsLabel) {
this.stepsLabel.string = '0'; // 将步数重置为0
}
setTimeout(() => { //直接设置active会直接开始监听鼠标事件,做了一下延迟处理
if (this.playerCtrl) {
this.playerCtrl.setInputActive(true);
}
}, 0.1);
GS_END:暂时没有什么好添加的,当然您可以根据喜好添加一些结算用的逻辑让游戏看起来更完善
回到编辑器,绑定好 GameManager 需要的属性:
暂无相关推荐.