Now that we can move forward through infinite rows of obstacles let’s add a score indicator to keep track of how many rows we’ve crossed.
Now that we can move forward through infinite rows of obstacles let’s add a score indicator to keep track of how many rows we’ve crossed.
Let’s add a new div element with id score
to our HTML file. This simple div contains the number 0 as the initial value. We will update this value as the player progresses.
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite App</title> </head> <body> <canvas class="game"></canvas> <div id="controls"> <div> <button id="forward">▲</button> <button id="left">◀</button> <button id="backward">▼</button> <button id="right">▶</button> </div> </div> <div id="score">0</div> <script type="module" src="/src/main.ts"></script> </body></html>
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite App</title> </head> <body> <canvas class="game"></canvas> <div id="controls"> <div> <button id="forward">▲</button> <button id="left">◀</button> <button id="backward">▼</button> <button id="right">▶</button> </div> </div> <div id="score">0</div> <script type="module" src="/src/main.js"></script> </body></html>
We also style the score indicator. We position the score in the top-left corner of the screen with absolute positioning. We also load the Press Start 2P
font from Google Fonts and set it as our font.
@import url("https://fonts.googleapis.com/css?family=Press+Start+2P");
body { margin: 0; display: flex; font-family: "Press Start 2P", cursive;}
27 collapsed lines
#controls { position: absolute; bottom: 0; min-width: 100%; display: flex; align-items: flex-end; justify-content: center;}
#controls div { display: grid; grid-template-columns: 50px 50px 50px; grid-template-rows: auto auto; grid-column-gap: 10px; grid-row-gap: 10px; margin-bottom: 20px;}
#controls button { width: 100%; background-color: white; border: 1px solid lightgray;}
#controls button:first-of-type { grid-column: 1/-1;}
#score { position: absolute; top: 20px; left: 20px;
font-size: 2em; color: white;}
Once the player completes a step, the stepCompleted
function gets called. This is a good place to update our score indicator.
We get a reference to the score element by id. Then, we set its innerText
value to the current row. Feel free to refine this to update the value only if the new value is higher than the previous one.
63 collapsed lines
import * as THREE from "three";import { endsUpInValidPosition } from "../utilities/endsUpInValidPosition";import { metadata as rows, addRows } from "./Map";import type { MoveDirection } from "../types";
export const player = Player();
function Player() { const player = new THREE.Group();
const body = new THREE.Mesh( new THREE.BoxGeometry(15, 15, 20), new THREE.MeshLambertMaterial({ color: "white", flatShading: true, }) ); body.castShadow = true; body.receiveShadow = true; body.position.z = 10; player.add(body);
const cap = new THREE.Mesh( new THREE.BoxGeometry(2, 4, 2), new THREE.MeshLambertMaterial({ color: 0xf0619a, flatShading: true, }) ); cap.position.z = 21; cap.castShadow = true; cap.receiveShadow = true; player.add(cap);
const playerContainer = new THREE.Group(); playerContainer.add(player);
return playerContainer;}
export const position: { currentRow: number; currentTile: number;} = { currentRow: 0, currentTile: 0,};
export const movesQueue: MoveDirection[] = [];
export function queueMove(direction: MoveDirection) { const isValidMove = endsUpInValidPosition( { rowIndex: position.currentRow, tileIndex: position.currentTile, }, [...movesQueue, direction] );
if (!isValidMove) return;
movesQueue.push(direction);}
export function stepCompleted() { const direction = movesQueue.shift();
if (direction === "forward") position.currentRow += 1; if (direction === "backward") position.currentRow -= 1; if (direction === "left") position.currentTile -= 1; if (direction === "right") position.currentTile += 1;
// Add new rows if the player is running out of them if (position.currentRow > rows.length - 10) addRows();
const scoreDOM = document.getElementById("score"); if (scoreDOM) scoreDOM.innerText = position.currentRow.toString();}
59 collapsed lines
import * as THREE from "three";import { endsUpInValidPosition } from "../utilities/endsUpInValidPosition";import { metadata as rows, addRows } from "./Map";
export const player = Player();
function Player() { const player = new THREE.Group();
const body = new THREE.Mesh( new THREE.BoxGeometry(15, 15, 20), new THREE.MeshLambertMaterial({ color: "white", flatShading: true, }) ); body.castShadow = true; body.receiveShadow = true; body.position.z = 10; player.add(body);
const cap = new THREE.Mesh( new THREE.BoxGeometry(2, 4, 2), new THREE.MeshLambertMaterial({ color: 0xf0619a, flatShading: true, }) ); cap.position.z = 21; cap.castShadow = true; cap.receiveShadow = true; player.add(cap);
const playerContainer = new THREE.Group(); playerContainer.add(player);
return playerContainer;}
export const position = { currentRow: 0, currentTile: 0,};
export const movesQueue = [];
export function queueMove(direction) { const isValidMove = endsUpInValidPosition( { rowIndex: position.currentRow, tileIndex: position.currentTile, }, [...movesQueue, direction] );
if (!isValidMove) return;
movesQueue.push(direction);}
export function stepCompleted() { const direction = movesQueue.shift();
if (direction === "forward") position.currentRow += 1; if (direction === "backward") position.currentRow -= 1; if (direction === "left") position.currentTile -= 1; if (direction === "right") position.currentTile += 1;
// Add new rows if the player is running out of them if (position.currentRow > rows.length - 10) addRows();
const scoreDOM = document.getElementById("score"); if (scoreDOM) scoreDOM.innerText = position.currentRow.toString();}