介绍
在上一篇博客文章中,您将状态区域添加到了游戏中。现在,玩家和观众清楚地了解游戏期间的任何时候发生了什么。
在这篇文章中,您将添加重新启动游戏的能力而无需刷新浏览器。
您将通过在游戏中添加“再次播放”按钮来做到这一点。
什么是播放按钮?
“再次播放”按钮是一个按钮:
- 只有在游戏结束时才能看到(玩家获胜或以平局结束)。
- 单击时重新启动游戏并消失。
创建Playagainbutton课程
就像您到目前为止添加的其他组件一样,“再次播放”按钮也是游戏对象。
在src/components
目录中,创建一个文件创建一个名为PlayAgainButton.js
的文件。
在该文件中,创建一个名为PlayAgainButton
的类,该类从GameObject
延伸3:
import GameObject from "./GameObject.js";
export default class PlayAgainButton extends GameObject {
}
您也将需要一些特定于此组件的常数。导入PlayAgainButtonConfig
:
import GameObject from "./GameObject.js";
import { PlayAgainButtonConfig } from "../constants/index.js";
export default class PlayAgainButton extends GameObject {
}
接下来,为按钮的背景添加图形逻辑。将renderBackground()
添加到PlayAgainButton
类:
export default class PlayAgainButton extends GameObject {
renderBackground() {
const backgroundGradient = this.context.createLinearGradient(this.x, this.y, this.x, this.y + this.height);
backgroundGradient.addColorStop(0, PlayAgainButtonConfig.BACKGROUND_START_COLOR);
backgroundGradient.addColorStop(1, PlayAgainButtonConfig.BACKGROUND_END_COLOR);
this.context.fillStyle = backgroundGradient;
this.context.strokeStyle = `${PlayAgainButtonConfig.BORDER_WIDTH}px black`;
this.context.fillRect(this.x, this.y, this.width, this.height);
this.context.strokeRect(this.x, this.y, this.width, this.height);
}
}
然后,从GameObject
覆盖clear()
方法也考虑到按钮的边界:
export default class PlayAgainButton extends GameObject {
// ..
clear() {
const clearRectX = this.x - PlayAgainButtonConfig.BORDER_WIDTH;
const clearRectY = this.y - PlayAgainButtonConfig.BORDER_WIDTH;
const clearRectWidth = this.width + PlayAgainButtonConfig.BORDER_WIDTH * 2;
const clearRectHeight = this.height + PlayAgainButtonConfig.BORDER_WIDTH * 2;
this.context.clearRect(clearRectX, clearRectY, clearRectWidth, clearRectHeight);
}
}
现在要将文本添加到按钮,将renderText()
添加到PlayAgainButton
类:
export default class PlayAgainButton extends GameObject {
// ..
renderText() {
this.context.fillStyle = "white";
this.context.font = "16px Arial";
this.context.textAlign = "center";
this.context.textBaseline = "top";
const textMetrics = this.context.measureText(PlayAgainButtonConfig.TEXT);
const textHeight = textMetrics.actualBoundingBoxDescent;
// Calculation ensures that text is displayed at the vertical center of the button
const finalTextY = this.y + (this.height / 2) - textHeight / 2;
this.context.fillText(PlayAgainButtonConfig.TEXT, this.x + PlayAgainButtonConfig.WIDTH / 2, finalTextY);
}
}
将render()
方法添加到PlayAgainButton
类:
export default class PlayAgainButton extends GameObject {
render() {
this.context.save();
this.renderBackground();
this.context.restore();
this.context.save();
this.renderText();
this.context.restore();
}
// ..
}
渲染按钮
现在您将在游戏中添加“再次播放”按钮。
首先,将PlayAgainButton
类添加到src/components/index.js
,以便可以从那里导入它:
import Board from "./Board.js";
import StatusArea from './StatusArea.js';
import PlayAgainButton from "./PlayAgainButton.js";
export { Board, StatusArea, PlayAgainButton };
现在切换到src/FrontEnd.js
。导入PlayAgainButton
和PlayAgainButtonConfig
:
import { FrontEndConfig, BoardConfig, StatusAreaConfig, StatusMessages, PlayAgainButtonConfig } from "./constants/index.js";
import { Board, StatusArea, PlayAgainButton } from "./components/index.js";
import { Constants } from "./gameLogic/index.js";
export default class FrontEnd {
// ..
}
将playAgainButton
字段添加到FrontEnd
类:
export default class FrontEnd {
game;
canvas;
width;
height;
context;
board;
statusArea;
playAgainButton;
gameOver;
// ..
}
然后将createPlayAgainButton()
添加到FrontEnd
类:
export default class FrontEnd {
// ..
createPlayAgainButton() {
let buttonX = this.width / 2 - PlayAgainButtonConfig.WIDTH / 2;
let buttonY = this.height - PlayAgainButtonConfig.MARGIN_BOTTOM;
let button = new PlayAgainButton(this.context, buttonX, buttonY, PlayAgainButtonConfig.WIDTH, PlayAgainButtonConfig.HEIGHT);
button.render();
return button;
}
}
在start()
方法中,将playAgainButton
字段设置为从createPlayAgainButton()
返回的值:
export default class FrontEnd {
// ..
start() {
this.statusArea = this.createStatusArea();
this.board = this.createBoard();
this.playAgainButton = this.createPlayAgainButton();
document.body.addEventListener('click', (clickEvent) => {
this.board.handleClick(clickEvent);
});
}
}
如果您使用运行服务器在浏览器中检查游戏,则您将在画布上看到“再次播放”按钮:
出现按钮很棒,但是当您单击它时什么也不会发生。它不应该在游戏的这个阶段出现。接下来您将解决这些问题。
处理输入
“再次播放”按钮只能在游戏结束时可见。另外,PlayAgainButton
类处理与Board
类同样的单击。
要开始这一点,请返回src/components/PlayAgainButton.js
。将buttonClicked
和isEnabled
字段添加到PlayAgainButton
类:
export default class PlayAgainButton extends GameObject {
buttonClicked;
isEnabled;
// ..
}
然后将构造函数添加到PlayAgainButton
类中。它将调用父构建器,然后将isEnabled
字段设置为false
:
export default class PlayAgainButton extends GameObject {
constructor(context, x, y, width, height) {
super(context, x, y, width, height);
this.isEnabled = false;
}
// ..
}
更新render()
,以便将isEnabled
字段设置为true
:
export default class PlayAgainButton extends GameObject {
// ..
render() {
this.context.save();
this.renderBackground();
this.context.restore();
this.context.save();
this.renderText();
this.context.restore();
this.isEnabled = true;
}
}
要设置单击按钮时运行的逻辑,请将setClickHandler()
添加到PlayAgainButton
类:
export default class PlayAgainButton extends GameObject {
// ..
setClickHandler(handler) {
this.buttonClicked = handler;
}
}
添加handleClick()
来处理将传递的点击事件:
export default class PlayAgainButton extends GameObject {
// ..
handleClick(clickEvent) {
if (!this.isEnabled) {
return;
}
const wasButtonClicked = clickEvent.offsetX >= this.x
&& clickEvent.offsetX <= this.x + this.width
&& clickEvent.offsetY >= this.y
&& clickEvent.offsetY <= this.y + this.height;
if (!wasButtonClicked) {
return;
}
this.buttonClicked();
}
}
它检查播放器点击的位置是否实际上在按钮的边界内。
现在还有一件事要添加到PlayAgainButton
类。这是hide()
方法:
export default class PlayAgainButton extends GameObject {
// ..
hide() {
this.isEnabled = false;
this.clear();
}
}
您添加了很多东西到PlayAgainButton
类。类似于您处理板上的点击时,在使用这些更改的FrontEnd
类中,这一切都会有意义。
在FrontEnd
类中,更新start()
方法,以便单击事件处理程序回调还会在playAgainButton
字段上拨打handleClick()
,然后单击事件传递:
export default class FrontEnd {
// ..
start() {
this.statusArea = this.createStatusArea();
this.board = this.createBoard();
this.playAgainButton = this.createPlayAgainButton();
document.body.addEventListener('click', (clickEvent) => {
this.board.handleClick(clickEvent);
this.playAgainButton.handleClick(clickEvent);
});
}
}
然后在processMoveResult()
中,如果游戏结束了,请在playAgainButton
字段上致电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.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;
}
if (this.gameOver) {
this.playAgainButton.render();
}
}
}
另外,导入FourInARowGame
:
import { FrontEndConfig, BoardConfig, StatusAreaConfig, StatusMessages, PlayAgainButtonConfig } from "./constants/index.js";
import { Board, StatusArea, PlayAgainButton } from "./components/index.js";
import { Constants, FourInARowGame } from "./gameLogic/index.js";
export default class FrontEnd {
// ..
}
然后,在FrontEnd
类中添加reset()
方法:
export default class FrontEnd {
// ..
reset() {
this.game = new FourInARowGame();
this.gameOver = false;
this.playAgainButton.hide();
this.statusArea.render(this.game.currentTurn, this.pickStatusMessage(this.game.status));
this.board.render(this.game.currentBoard);
}
}
这是重新启动游戏的主要方法。
最后,在createPlayAgainButton()
中,删除了button
上render()
的呼叫,然后在button
上致电setClickHandler()
。在回调中致电reset()
:
export default class FrontEnd {
// ..
createPlayAgainButton() {
let buttonX = this.width / 2 - PlayAgainButtonConfig.WIDTH / 2;
let buttonY = this.height - PlayAgainButtonConfig.MARGIN_BOTTOM;
let button = new PlayAgainButton(this.context, buttonX, buttonY, PlayAgainButtonConfig.WIDTH, PlayAgainButtonConfig.HEIGHT);
button.setClickHandler(() => this.reset());
return button;
}
}
现在,如果您使用运行服务器在浏览器中检查游戏,则您将拥有一个完全可玩的四英寸游戏游戏,可以在完成游戏后重播。
结论
恭喜!您已经完成了游戏。随时可以随意修改它。也许会更改一些组件的外观?您也可以添加动画!
游戏的源代码可在此处找到:https://github.com/colinkiama/four-in-a-row-game-walkthrough
感谢您的阅读! ð