介绍
欢迎回来!在上一篇博客文章中,您绘制了游戏板,并通过单击板列,使游戏可玩。在这篇文章中,您将在四轮比赛中添加状态区域组件。
分解状态区域组件
让我们参考完成游戏的模型:
状态面积在顶部。它分为两个部分:
- 播放器转弯指标:通过显示的相关玩家的颜色指示当前玩家的转弯。
- 状态消息:描述游戏的每个阶段发生了什么(转弯是谁?哪个球员赢了?等) )
他们一起将游戏中发生的事情告知玩家和观众。
创建现状课程
要开始,在称为StatusArea.js
的src/components
目录下创建一个文件。
在您刚创建的文件中,创建一个名为StatusArea
的空类。此类将继承GameObject
:
import GameObject from "./GameObject.js";
export default class StatusArea extends GameObject {
}
播放器转向指示器
播放器转向指示器是一个在状态区域中出现的小圆圈。它可能具有这些状态:
- 黄色 - 在黄色的球员轮到或黄色玩家赢得了比赛时,具有黄色。
- 红色 - 当轮到红色玩家或红色玩家赢得了比赛时,有红色。
- 看不见 - 游戏以平局结束时,指示器不可见。
现在您知道玩家转动指示器的行为,下一步是将其添加到游戏中。
开始绘制播放器转弯指示器
首先,从gameLogic
目录以及StatusAreaConfig
和TokenColor
从constants
目录导入Constants
对象:
import { Constants } from "../gameLogic/index.js";
import { StatusAreaConfig, TokenColor } from "../constants/index.js";
import GameObject from "./GameObject.js";
export default class StatusArea extends GameObject {
}
然后,将renderPlayerTurnIndicator()
添加到StatusArea
类:
export default class StatusArea extends GameObject {
renderPlayerTurnIndicator(indicatorColor) {
let indicatorColorValue;
switch (indicatorColor) {
case Constants.PlayerColor.YELLOW:
indicatorColorValue = TokenColor.YELLOW;
break;
case Constants.PlayerColor.RED:
indicatorColorValue = TokenColor.RED;
break;
default:
// Unknown color. Do not attempt to render player turn indicator.
return;
}
this.context.fillStyle = indicatorColorValue;
const indicatorY = this.y + StatusAreaConfig.INDICATOR_WIDTH / 2 + StatusAreaConfig.PADDING_TOP;
this.context.beginPath();
// Draws a circle using CanvasDrawingContext2D.arc(): https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arc
this.context.arc(this.width / 2, indicatorY, StatusAreaConfig.INDICATOR_WIDTH / 2, 0, Math.PI * 2);
this.context.closePath();
this.context.fill();
}
}
接下来,将render()
添加到StatusArea
类:
export default class StatusArea extends GameObject {
render(indicatorColor) {
this.context.save();
this.clear();
if (indicatorColor !== Constants.PlayerColor.NONE) {
this.renderPlayerTurnIndicator(indicatorColor);
}
this.context.restore();
}
// ..
}
在继续进一步之前,您需要将StatusArea
类作为模块通过src/components/index.js
:
import Board from "./Board.js";
import StatusArea from './StatusArea.js';
export { Board, StatusArea };
现在您实现了播放器转向指示器的渲染逻辑,并通过components
目录公开了StatusArea
类,您现在准备开始渲染播放器转向指示器。
渲染播放器转向指示器
在src/FrontEnd.js
inoctem inocte StatusAreaConfig
和StatusArea
:
import { FrontEndConfig, BoardConfig, StatusAreaConfig } from "./constants/index.js";
import { Board, StatusArea } from "./components/index.js";
import { Constants } from "./gameLogic/index.js";
export default class FrontEnd {
// ..
}
接下来,将statusArea
字段添加到FrontEnd
类:
export default class FrontEnd {
game;
canvas;
width;
height;
context;
board;
statusArea;
gameOver;
// ..
}
将createStatusArea()
添加到FrontEnd
类:
export default class FrontEnd {
// ..
createStatusArea() {
let statusArea = new StatusArea(this.context, 0, 0, this.width, StatusAreaConfig.HEIGHT);
statusArea.render(this.game.currentTurn);
return statusArea;
}
}
在start()
中,将statusArea
设置为从createStatusArea()
返回的新状态区域:
export default class FrontEnd {
// ..
start() {
this.statusArea = this.createStatusArea();
this.board = this.createBoard();
document.body.addEventListener('click', (clickEvent) => {
this.board.handleClick(clickEvent);
});
}
}
将determineIndicatorColor()
添加到FrontEnd
类:
export default class FrontEnd {
// ..
determineIndicatorColor(moveResult) {
if (moveResult.status.value === Constants.MoveStatus.DRAW) {
return Constants.PlayerColor.NONE
} else if (moveResult.status.value === Constants.MoveStatus.WIN) {
return moveResult.winner;
} else {
return this.game.currentTurn;
}
}
}
然后,更新processMoveResult()
,以便它也确定下一个播放器的回合,并以下一个播放器的颜色传递到statusArea
上的render()
方法的呼叫:
export default class FrontEnd {
// ..
processMoveResult(moveResult) {
if (this.gameOver || moveResult.status.value === Constants.MoveStatus.INVALID) {
return;
}
const indicatorColor = this.determineIndicatorColor(moveResult);
this.statusArea.render(indicatorColor);
this.board.render(this.game.currentBoard);
if (moveResult.status.value === Constants.MoveStatus.WIN || moveResult.status.value === Constants.MoveStatus.DRAW) {
this.gameOver = true;
}
}
}
最后,在createBoard()
中,将调用对Board
构造函数更新为,以便在渲染游戏板时板,它的位置下降到状态下:
export default class FrontEnd {
// ..
createBoard() {
let board = new Board(this.context, BoardConfig.MARGIN_LEFT, this.statusArea.height + BoardConfig.MARGIN_TOP, BoardConfig.WIDTH, BoardConfig.HEIGHT);
board.setColumnSelectionHandler((columnIndex) => this.playMove(columnIndex));
board.render(this.game.currentBoard);
return board;
}
}
如果服务器运行时在浏览器中检查游戏,则会看到指示器颜色正在呈现。
它将根据游戏的当前状态进行更新。
状态消息
状态消息是状态区域的文本部分。
它用于:
- 显示当前玩家的转弯
- 揭示哪个玩家赢得了游戏
- 显示游戏何时在抽奖中结束
开始实施状态消息
将renderMessage()
方法添加到StatusArea
类:
export default class FrontEnd {
// ..
renderMessage(message) {
this.context.fillStyle = "white";
this.context.font = "bold 16px Arial";
this.context.textBaseline = "top";
this.context.textAlign = "center"; // Default value had vertical alignment issues. "center" fixes those in this case
const messageY = this.y + StatusAreaConfig.PADDING_TOP + StatusAreaConfig.INNER_MARGIN;
this.context.fillText(message, this.width / 2, messageY);
}
}
继续将message
参数添加到render()
方法,然后在render()
中调用renderMessage()
:
export default class StatusArea extends GameObject {
render(indicatorColor, message) {
this.context.save();
this.clear();
if (indicatorColor !== Constants.PlayerColor.NONE) {
this.renderPlayerTurnIndicator(indicatorColor);
}
this.renderMessage(message);
this.context.restore();
}
// ..
完成此操作后,切换回src/FrontEnd.js
文件。从constants
目录导入StatusMessages
:
import { FrontEndConfig, BoardConfig, StatusAreaConfig, StatusMessages } from "./constants/index.js";
import { Board } from "./components/index.js";
import { Constants } from "./gameLogic/index.js";
现在,添加逻辑,该逻辑根据游戏的当前状态确定要显示的状态消息。将pickStatusMessage()
添加到FrontEnd
类:
export default class FrontEnd {
// ..
pickStatusMessage(status) {
switch (status) {
case Constants.GameStatus.WIN:
// The game is on the the next turn but the somebody has won from the previous turn. The winning player is the opposite of the player who currently has a turn.
return this.game.currentTurn === Constants.PlayerColor.YELLOW ? StatusMessages.RED_WIN : StatusMessages.YELLOW_WIN;
case Constants.GameStatus.DRAW:
return StatusMessages.DRAW;
}
// At this point, we can assume that the game is either has just started
// or is still in progress.
return this.game.currentTurn === Constants.PlayerColor.YELLOW ? StatusMessages.YELLOW_TURN : StatusMessages.RED_TURN;
}
}
然后在processMoveResult()
方法中,在statusArea
上更新render()
呼叫,以便它传递了一个其他参数。此参数是对pickStatusMessage()
的方法调用,其中moveResult
的状态值传递给了:
export default class FrontEnd {
// ..
processMoveResult(moveResult) {
if (this.gameOver || moveResult.status.value === Constants.MoveStatus.INVALID) {
return;
}
const indicatorColor = this.determineIndicatorColor(moveResult);
this.statusArea.render(indicatorColor, this.pickStatusMessage(moveResult.status.value))
this.board.render(this.game.currentBoard);
if (moveResult.status.value === Constants.MoveStatus.WIN || moveResult.status.value === Constants.MoveStatus.DRAW) {
this.gameOver = true;
}
}
}
最后在createStatusArea()
中重复相同的更改,只是传递给pickStatusMessage()
的参数将是游戏的当前状态:
export default class FrontEnd {
// ..
createStatusArea() {
let statusArea = new StatusArea(this.context, 0, 0, this.width, StatusAreaConfig.HEIGHT);
statusArea.render(this.game.currentTurn, this.pickStatusMessage(this.game.status));
return statusArea;
}
}
如果您使用运行服务器在浏览器中检查游戏,您会发现状态区域将始终反映游戏的当前状态:
- 播放器转弯指示器将出现在特定玩家的状态中
- 状态消息将描述游戏的当前状态
结论
恭喜您走了这么远!现在很清楚地了解游戏过程中发生了什么。
不幸的是,当游戏结束时,就无法启动新游戏而不刷新页面。
在本教程的下一个(也是最后)部分中,您将通过在游戏中添加最终组件,再次播放按钮来解决此问题。
感谢您的阅读! ð