Sweshi's Tutorials

Cocoos Creator Mind Your Step 3D Game Tutorial

Animating the road

To begin working on the road, firstly, right-click the space in the Node hierarchy ->Create->3D Object->Cube. The cube will be the road in this case. The figure below shows what the heirarchy section should have at this point.

Cocos Creator 3D Tutorial: Mind Your Step  - creating the cube.

We will also create a Game Manager. In most games, we need some node that can be incharge of generating other nodes, keeping score and so on. We normally use a Game Manager for such a role. So we will create one in this game that will be responsible for generating the road. Go to the node heiarchy and in the empty space, right click ->Create->Empty Node. Name the node as "GameManager". The figure below shows the nodes we should have at this point.

Cocos Creator 3D Tutorial: Mind Your Step  - Creating the Game Manager.

We will also create a script for the Game Manager. Go to the assets section and to the "Scripts" folder we created earlier. Right Click on the scripts folder->Create->TypeScript->NewComponent. Name the Script as "GameManager". The figure below shows the scripts that we should have at this point.

Cocos Creator 3D Tutorial: Mind Your Step  - scripts.

Now click on the GameManager Node and go to the inspector. Click on "Add Component" and search for "GameManager". This will show the script you created and you should select it. This will add the script to the GameManager node as shown in the figure.

Cocos Creator 3D Tutorial: Mind Your Step  - attaching the script to the game manager

Open the Game Manager Script so that we can add some of the basic code needed to generate the road

Game Manager Script
import { _decorator, Component, Prefab } from 'cc'; const { ccclass, property } = _decorator; // Track grid type, pit (BT_NONE) or solid road (BT_STONE) enum BlockType { BT_NONE, BT_STONE, }; @ccclass("GameManager") export class GameManager extends Component { // The runway prefab @property({type: Prefab}) public cubePrfb: Prefab | null = null; // Total road length @property public roadLength = 50; private _road: BlockType[] = []; start () { this.generateRoad(); } generateRoad() { } }
explanation of the code

The Game Manager will have to generate cubes (the road) but it also has to leave some empty spaces so that the player can jump over them. Because of this, we start by creating an enum that has two block types, BT_NONE represents the empty space and BT_STONE represents the cube.

enum BlockType{ BT_NONE, BT_STONE, };

We will need a cube prefab to generate the road so we make sure we create a property for the cube prefab. This will open an empty space for us to drag a cube prefab to the script in the editor. More on this a little later on.

// The runway prefab @property({type: Prefab}) public cubePrfb: Prefab | null = null;

The road will have a length of 50 spaces on the X axis so we create a roadLength property with the same value. When automatically generating the road, we will need to know what block type we are placing between the BT_STONE or the BT_NONE. So we create the _road array which will store what will be placed on all the 50 spaces. Think of it this way, the enum has BT_STONE=1 and BT_NONE=0. There are 50 road spaces to fill with either 1 or 0. The script will have to generate something close to this shown below.

1 2 3 4 5 6 7 8 9 10 Will continue up to 50 (A.K.A roadLength)
1 0 1 1 0 1 0 1 1 0 Will continue for all 50 road spaces
@property public roadLength = 50; private _road: BlockType[] = [];

So in short, each single space out of the 50 spaces will need either a 1 or 0 to mean a block or an empty space. The logic just needs to make sure that the cube will always be the first space and that after each empty space (0) a cube (1) will follow so that the player always has somewhere to land.

We will need a function for generating the road so this has been created. For now its empty. This will be run when the game is loading and so it is called in the "start()" function.

start () { this.generateRoad(); } generateRoad() { }

Update the generateRoad() function as shown below

generateRoad() { // Prevent the track from being the old track when the game is restarted // Therefore, the old track needs to be removed and the old track data cleared this.node.removeAllChildren(); this._road = []; // Make sure that the character is standing on the real road when the game is running this._road.push(BlockType.BT_STONE); // Determine the type of track for each frame for (let i = 1; i < this.roadLength; i++) { // If the previous track is a an empty space, then //the next one should be a cube if (this._road[i-1] === BlockType.BT_NONE) { this._road.push(BlockType.BT_STONE); } else { this._road.push(Math.floor(Math.random() * 2)); } } // Generate tracks based on runway for (let j = 0; j < this._road.length; j++) { let block: Node = this.spawnBlockByType(this._road[j]); // Determine if a road was generated, //as spawnBlockByType may return a pit (with a value of null) if (block) { this.node.addChild(block); block.setPosition(j, -1.5, 0); } } }

The comments are self explanatory but to summarize, we start by removing any other cubes from the scene by removing all children and emptying the road array. We then make sure that we add a cube (BT_STONE) as the first space in the 50 spaces "this.road.push(BlockType.BT_STONE)"

We then use a for loop to determine a 1 (cube) or 0 (empty space) for all the remaining 49 spaces. We also make sure that if the previous space was an empty space then the next is a cube otherwise we randomise

if (this._road[i-1] === BlockType.BT_NONE) { this._road.push(BlockType.BT_STONE); } else { this._road.push(Math.floor(Math.random() * 2)); } }

We then generate the track based on these spaces again using a for loop. We dertemine using spawnBlockByType the item on a specific position and store it as a node. we check if this node is a cube and if it is we add it to the scene. But at this point, we don't have the spawnBlockByType function so we create it.

spawnBlockByType(type: BlockType) { //check if we have not recieved a cube prefab //if we havent, we return null if (!this.cubePrfb) { return null; } //otherwise we instantiate the the cube and return it let block: Node | null = null; // The runway is generated only for real roads switch(type) { case BlockType.BT_STONE: block = instantiate(this.cubePrfb); break; } return block; }

The complete script is as shown below.

Complete GameManager Script
import { _decorator, Component, Prefab, instantiate, Node, CCInteger } from 'cc'; const { ccclass, property } = _decorator; // The runway type, pit (BT_NONE) or solid road (BT_STONE) enum BlockType { BT_NONE, BT_STONE, }; @ccclass("GameManager") export class GameManager extends Component { // The stone prefab @property({type: Prefab}) public cubePrfb: Prefab | null = null; // Length of the runway @property public roadLength = 50; private _road: BlockType[] = []; start () { this.generateRoad(); } generateRoad() { // Prevent the track from being the old track when the game is restarted // Therefore, the old track needs to be removed and the old track data cleared this.node.removeAllChildren(); this._road = []; // Make sure that the character is standing on the real road when the game is running this._road.push(BlockType.BT_STONE); // Determine the type of track for each frame for (let i = 1; i < this.roadLength; i++) { // If the previous track is a pit, then this one must not be a pit if (this._road[i-1] === BlockType.BT_NONE) { this._road.push(BlockType.BT_STONE); } else { this._road.push(Math.floor(Math.random() * 2)); } } // Generate tracks based on runway for (let j = 0; j < this._road.length; j++) { let block: Node = this.spawnBlockByType(this._road[j]); // Determine if a road was generated, // as spawnBlockByType may return a pit (with a value of null) if (block) { this.node.addChild(block); block.setPosition(j, -1.5, 0); } } } spawnBlockByType(type: BlockType) { if (!this.cubePrfb) { return null; } let block: Node | null = null; // The runway is generated only for real roads switch(type) { case BlockType.BT_STONE: block = instantiate(this.cubePrfb); break; } return block; } // update (deltaTime: number) { // // Your update function goes here. // } }

In the assets section, create a new folder named "Prefabs". Drag the cube from the Node heirarchy to the Prefabs folder. The folder should look like the one shown below.

Cocos Creator 3D Tutorial: Mind Your Step  - cube prefab.

Now click on the GameManager node and go to the inspector. Go to the script. There will be an empty space for the "Cube Prfb". Drag the Cube prefab from the prefabs folder straight to this empty space. It should look as shown in the figure below.

Cocos Creator 3D Tutorial: Mind Your Step  -cube added as a prefab to game manager.

At this point, you can try running the Game and seeing if the road is generated. At this point it should have the road automatically creating.



Videos