在游戏中,我们可以通过手动布置 Box 节点来生成地图,但是这样的话地图就是固定了,为了让每次开始游戏的地图有变化并为玩家提供一些惊喜,可以选择通过动态生成方块的方式来创建地图。
这样我们就需要将生成的过程和结果保存起来,一般情况为了保存游戏的数据,我们需要创建一些类来辅助这类工作。这样的类我们称之为 Manager 管理器。
在 资源管理器 的 Scripts 目录内,点击右键创建新的 TypeScript 组件并将其命名为: GameManager。
在 Cocos Creator 内创建组件时会同时确定组件内根据模板生成的内容。 如果您在不熟悉的情况下输入了错误的名字,可以选择删除再重新创建一个新的文件。 如果只是修改文件名,不修改里面的内容,会导致类名与文件名不一致,而无法在 属性检查器 内找到对应的类。
创建好 GameManager 之后,我们可以将其挂在在场景内任何一个节点上,但出于清晰的考虑我们一般会选择创建一个同名的节点,并将 GameManager 挂在在他上面:
首先我们需要让 GameManager 知道他应该用那个资源作为地图块来创建,因此我们可以在代码中添加 boxPrefab 来指向我们之前已经创建好的 Box 预制体。
@property({type: Prefab})
public boxPrefab: Prefab|null = null;
@property 依旧是装饰器的用法,如果你不记得了,可以回到之前角色 播放动画 部分。
将上述的代码添加下如下位置:
import { _decorator, Component, Prefab } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('GameManager')
export class GameManager extends Component {
@property({type: Prefab})
public boxPrefab: Prefab|null = null;
start(){}
update(dt: number): void {
}
}
之后回到编辑器并将 Box 预制体拖拽到 GameManager 上:
我们可以用一个数值类型的数组来存储当前的位置到底是方块还是坑,但实际上有更好的办法,我们声明如下的枚举,用 BT_NONE 来表示坑,而 BT_STONE 来表示方块,这样的表示会让我们的代码更加的易读。
enum BlockType{
BT_NONE,
BT_STONE,
};
在 TypeScript 里面您可以将这个枚举放在类的上面,这样可以确保 GameManager 可以访问他,同时由于没有添加 export 关键字,这意味着这个枚举只有在 GameManager.ts 这个模块内才可以访问。
接下来我们需要生成并记录下地图的生成情况,可以声明如下的成员变量来存储它们,同时如果想要在编辑器里面配置初始化时道路的长度,可以声明一个变量 roadLength 来记录:
import { _decorator, CCInteger, Component, Prefab } from 'cc';
const { ccclass, property } = _decorator;
enum BlockType{
BT_NONE,
BT_STONE,
};
@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() {
}
}
用数组来存储这些地图数据是很好的主意,因为数组可以进行快速的访问,我们可以通过索引很快查询到某个位置是方块还是坑。
填充地图的流程是这样的:
每次生成时,需要将上次的结果清除
第一个地块永远是方块,保证角色不会掉下去
由于我们的角色可以选择跳 1 个方块或者 2 个方块,和某个戴红帽子穿背带裤家伙比起来太弱鸡了,因此坑最多不应该连续超过 2 个,也就意味着如果前面 1 个地块是坑,那么接下来的地块必须是方块
接下来为 GameManager 添加几个方法:
生成地图的方法:
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);
}
}
}
Math.floor: 这个方法是 TypeScript 数学库的方法之一:我们知道 floor 是地板的意思,这表示取这个方法参数的 “地板”,也就是向下取整。 Math.random:同样 random 也是标准数学库的方法之一,用于随机一个 0 到 1 之间的小数,注意取值范围是 [0, 1)。 所以 Math.floor(Math.random() * 2) 这段代码的意思很简单,就是从 [0, 2) 中随机取 1个数并向下取整,得到的结果是 0 或者 1,恰好和 枚举 BlockType 中声明的 BT_NONE 和 BT_STONE 对应。 顺便说一句,在 TypeScript 的枚举中,如果你没有给枚举赋值,那么枚举的值会顺序的从 0 开始分配。
通过 spawnBlockByType 来生成新的方块并将他通过 setPosition 方法放置到合适的位置。
在 Cocos Creator 中,设置节点的位置需要使用 setPosition 方法或者 set position 这样的读取器。
根据 BlockType 生成方块:
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;
}
通过 BlockType 来确定是否要真的创建这个方块,当然只在 type 为 BT_STONE 的时候我们通过 instantiate 方法来创建方块,其他情况下,返回一个空值。
instantiate: 是 Cocos Creator 提供的克隆预制体的方法。当然它不仅能克隆预制体,你甚至可以用它克隆别的类型比如某个对象!
此时如果我们在 GameManager 的 start 内调用 generateRoad 来创建地图:
start() {
this.generateRoad()
}
运行游戏后可以观察到地图的生成的情况:
暂无相关推荐.