使用React制作一個(gè)貪吃蛇游戲的代碼詳解
在 React 中創(chuàng)建貪吃蛇游戲
Snake Game 使用 ReactJS 項(xiàng)目實(shí)現(xiàn)功能組件并相應(yīng)地管理狀態(tài)。開(kāi)發(fā)的游戲允許用戶使用箭頭鍵控制蛇或觸摸屏幕上顯示的按鈕來(lái)收集食物并增長(zhǎng)長(zhǎng)度。游戲的目標(biāo)是在不與墻壁或蛇自己的身體碰撞的情況下吃盡可能多的食物。
最終輸出預(yù)覽: 讓我們看看我們的最終項(xiàng)目會(huì)是什么樣子。

創(chuàng)建貪吃蛇游戲的方法:
給定的代碼代表使用 ReactJS 的貪吃蛇游戲項(xiàng)目。它涉及設(shè)置蛇、食物、按鈕和菜單的組件。游戲以初始狀態(tài)初始化,處理蛇運(yùn)動(dòng)的用戶輸入,檢測(cè)碰撞,并相應(yīng)地更新游戲板。渲染和用戶界面的實(shí)現(xiàn)是為了顯示游戲元素。游戲流程包括菜單和游戲玩法的過(guò)渡。
創(chuàng)建貪吃蛇游戲的步驟:
步驟 1: 在 VSCode IDE 中使用以下命令設(shè)置 React 項(xiàng)目。
?npx create-react-app snack_game
步驟 2: 執(zhí)行以下命令導(dǎo)航到新創(chuàng)建的項(xiàng)目文件夾。
?cd snack_game
步驟 3: 創(chuàng)建一個(gè)名為 Components 的文件夾。我們將在此組件文件夾中創(chuàng)建各種組件及其樣式文件,例如 Button.js、Food.js、Menu.js、Snake.js、Menu.css 和 Button.css。
貪吃蛇游戲的項(xiàng)目結(jié)構(gòu):
package.json中更新后的依賴項(xiàng)將如下所示:
?"dependencies": {
? ? ?"@testing-library/jest-dom": "^5.16.5",
? ? ?"@testing-library/react": "^13.4.0",
? ? ?"@testing-library/user-event": "^13.5.0",
? ? ?"react": "^18.2.0",
? ? ?"react-dom": "^18.2.0",
? ? ?"react-scripts": "5.0.1",
? ? ?"web-vitals": "^2.1.4"
?}
示例: 在上述目錄結(jié)構(gòu)中提到的App.js 和 index.css文件中插入以下代碼
App.js
?// App.js
?import React, { Component } from "react";
?import Snake from "./Components/Snake";
?import Food from "./Components/Food";
?import Button from "./Components/Button";
?import Menu from "./Components/Menu";
?import "./App.css";
?const getRandomFood = () => {
? let min = 1;
? let max = 98;
? let x = Math.floor((Math.random() * (max - min + 1) + min) / 2) * 2;
? let y = Math.floor((Math.random() * (max - min + 1) + min) / 2) * 2;
? return [x, y];
?};
??
?const initialState = {
? food: getRandomFood(),
? direction: "RIGHT",
? speed: 100,
? route: "menu",
? snakeDots: [
? [0, 0],
? [0, 2],
? ],
?};
??
?class App extends Component {
? constructor() {
? super();
? this.state = initialState;
? }
??
? componentDidMount() {
? setInterval(this.moveSnake, this.state.speed);
? document.onkeydown = this.onKeyDown;
? }
??
? componentDidUpdate() {
? this.onSnakeOutOfBounds();
? this.onSnakeCollapsed();
? this.onSnakeEats();
? }
??
? onKeyDown = (e) => {
? e.preventDefault();
? e = e || window.event;
? switch (e.keyCode) {
? case 37:
? this.setState({ direction: "LEFT" });
? break;
? case 38:
? this.setState({ direction: "UP" });
? break;
? case 39:
? this.setState({ direction: "RIGHT" });
? break;
? case 40:
? this.setState({ direction: "DOWN" });
? break;
? }
? };
??
? moveSnake = () => {
? let dots = [...this.state.snakeDots];
? let head = dots[dots.length - 1];
? if (this.state.route === "game") {
? switch (this.state.direction) {
? case "RIGHT":
? head = [head[0] + 2, head[1]];
? break;
? case "LEFT":
? head = [head[0] - 2, head[1]];
? break;
? case "DOWN":
? head = [head[0], head[1] + 2];
? break;
? case "UP":
? head = [head[0], head[1] - 2];
? break;
? }
? dots.push(head);
? dots.shift();
? this.setState({
? snakeDots: dots,
? });
? }
? };
??
? onSnakeOutOfBounds() {
? let head = this.state.snakeDots[this.state.snakeDots.length - 1];
? if (this.state.route === "game") {
? if (
? head[0] >= 100 ||
? head[1] >= 100 ||
? head[0] < 0 ||
? head[1] < 0
? ) {
? this.gameOver();
? }
? }
? }
??
? onSnakeCollapsed() {
? let snake = [...this.state.snakeDots];
? let head = snake[snake.length - 1];
? snake.pop();
? snake.forEach((dot) => {
? if (head[0] == dot[0] && head[1] == dot[1]) {
? this.gameOver();
? }
? });
? }
??
? onSnakeEats() {
? let head = this.state.snakeDots[this.state.snakeDots.length - 1];
? let food = this.state.food;
? if (head[0] == food[0] && head[1] == food[1]) {
? this.setState({
? food: getRandomFood(),
? });
? this.increaseSnake();
? this.increaseSpeed();
? }
? }
??
? increaseSnake() {
? let newSnake = [...this.state.snakeDots];
? newSnake.unshift([]);
? this.setState({
? snakeDots: newSnake,
? });
? }
??
? increaseSpeed() {
? if (this.state.speed > 10) {
? this.setState({
? speed: this.state.speed - 20,
? });
? }
? }
??
? onRouteChange = () => {
? this.setState({
? route: "game",
? });
? };
??
? gameOver() {
? alert(`GAME OVER, your score is ${this.state.snakeDots.length - 2}`);
? this.setState(initialState);
? }
??
? onDown = () => {
? let dots = [...this.state.snakeDots];
? let head = dots[dots.length - 1];
??
? head = [head[0], head[1] + 2];
? dots.push(head);
? dots.shift();
? this.setState({
? direction: "DOWN",
? snakeDots: dots,
? });
? };
??
? onUp = () => {
? let dots = [...this.state.snakeDots];
? let head = dots[dots.length - 1];
??
? head = [head[0], head[1] - 2];
? dots.push(head);
? dots.shift();
? this.setState({
? direction: "UP",
? snakeDots: dots,
? });
? };
??
? onRight = () => {
? let dots = [...this.state.snakeDots];
? let head = dots[dots.length - 1];
??
? head = [head[0] + 2, head[1]];
? dots.push(head);
? dots.shift();
? this.setState({
? direction: "RIGHT",
? snakeDots: dots,
? });
? };
??
? onLeft = () => {
? let dots = [...this.state.snakeDots];
? let head = dots[dots.length - 1];
??
? head = [head[0] - 2, head[1]];
? dots.push(head);
? dots.shift();
? this.setState({
? direction: "LEFT",
? snakeDots: dots,
? });
? };
??
? render() {
? const { route, snakeDots, food } = this.state;
? return (
? <div>
? {route === "menu" ? (
? <div>
? <Menu onRouteChange={this.onRouteChange} />
? </div>
? ) : (
? <div>
? <div className="game-area">
? <Snake snakeDots={snakeDots} />
? <Food dot={food} />
? </div>
? <Button
? onDown={this.onDown}
? onLeft={this.onLeft}
? onRight={this.onRight}
? onUp={this.onUp}
? />
? </div>
? )}
? </div>
? );
? }
?}
??
?export default App;
??
index.css
?/* index.css */
?body {
? background-color: #1e1e1e;
?}
??
?.game-area {
? position: relative;
? width: 600px;
? height: 500px;
? border: 2px solid #dc042c;
? border-radius: 10px;
? margin: 50px auto;
? display: flex;
? flex-wrap: wrap;
? box-shadow: 0 0 10px #abbfc0;
?}
??
?@media only screen and (max-width: 800px) {
? .game-area {
? position: relative;
? width: 350px;
? height: 300px;
? }
??
? .snake {
? width: 12px;
? height: 12px;
? }
?}
??
?.snake {
? position: absolute;
? width: 2%;
? height: 2%;
? background-color: #dc042c;
? border: 1px solid white;
? z-index: 2;
?}
??
?.food {
? position: absolute;
? width: 12px;
? height: 12px;
? background-color: white;
? border-radius: 20px;
? z-index: 1;
?}
??
在不同的文件中編寫(xiě)以下提到的代碼(每個(gè)代碼塊的第一行都提到了文件名)
- Button.js: Button.js表示React功能組件,用于在snake游戲中渲染控制蛇移動(dòng)的按鈕。
- Menu.js: Menu.js文件代碼為Snake Game呈現(xiàn)菜單。它顯示一個(gè)“開(kāi)始游戲”按鈕,并在單擊時(shí)觸發(fā)onRouteChange功能。菜單的樣式使用“menu.CSS”文件中的CSS
- Food.js 是一個(gè)React組件,它根據(jù)提供的坐標(biāo)在游戲中呈現(xiàn)食物。
- Snake.js:Snake.js文件代碼是一個(gè)React組件,它在游戲中基于表示蛇點(diǎn)的坐標(biāo)數(shù)組來(lái)渲染蛇。
//Button.js
?//Button.js
?import React from "react";
?import "./Button.css";
??
?const Button = ({ onUp, onDown, onLeft, onRight }) => {
? return (
? <div className="buttons">
? <div className="upwards">
? <input className="up" onClick={onUp} type="button" value="UP" />
? </div>
? <div className="sideways">
? <input
? className="left"
? onClick={onLeft}
? type="button"
? value="LEFT"
? />
? <input
? className="right"
? onClick={onRight}
? type="button"
? value="RIGHT"
? />
? </div>
? <div className="downwards">
? <input
? className="down"
? onClick={onDown}
? type="button"
? value="DOWN"
? />
? </div>
? </div>
? );
?};
?export default Button;
??
Food.js
?//Food.js
?import React from "react";
??
?const Food = (props) => {
? const style = {
? left: `${props.dot[0]}%`,
? top: `${props.dot[1]}%`,
? };
? return <div className="food" style={style} />;
?};
??
?export default Food;
??
Menu.js
?//Menu.js
?import React from "react";
?import "./Menu.css";
??
?const Menu = ({ onRouteChange }) => {
? return (
? <div className="wrapper">
? <div>
? <input
? onClick={onRouteChange}
? className="start"
? type="button"
? value="start game"
? />
? </div>
? </div>
? );
?};
??
?export default Menu;
??
Snake.js
?//Snake.js
?import React from "react";
??
?const Snake = (props) => {
? return (
? <div>
? {props.snakeDots.map((dot, i) => {
? const style = {
? left: `${dot[0]}%`,
? top: `${dot[1]}%`,
? };
? return <div className="snake" key={i} style={style} />;
? })}
? </div>
? );
?};
?export default Snake;
??
Button.css
?/* Button.css */
?.upwards,
?.downwards {
? display: flex;
? justify-content: center;
?}
??
?.sideways {
? display: flex;
? justify-content: center;
? margin: 10px;
?}
??
?.left,
?.right {
? margin: 50px;
? padding: 20px;
? border: 0px solid;
? border-radius: 20px;
? border: 1px solid white;
? color: #dc042c;
? background: #1e1e1e;
? box-shadow: 5px -5px 10px rgba(0, 0, 0, 0.6);
?}
??
?.up,
?.down {
? padding: 20px;
? border: 0px solid;
? border-radius: 20px;
? border: 1px solid white;
? color: #dc042c;
? background: #1e1e1e;
? box-shadow: 5px -5px 10px rgba(0, 0, 0, 0.6);
?}
??
Menu.css
?/* Menu.css */
?.wrapper {
? position: relative;
? width: 200px;
? height: 250px;
? border: 2px solid #dc042c;
? /* border-radius: 10px; */
? margin: 50px auto;
? margin-top: 200px;
? display: flex;
? flex-wrap: wrap;
? justify-content: center;
? /* box-shadow: 0 0 10px #abbfc0; */
?}
??
?.start {
? margin: 100px;
? background: #1e1e1e;
? color: white;
? border-radius: 7px;
? border: 0px;
? padding: 10px;
? font-size: 1.2em;
? box-shadow: 0 0 70px #abbfc0;
? font-family: "Courier New", Courier, monospace;
?}
??
啟動(dòng)我們的程序:
Step 1: 在命令行執(zhí)行一下命令啟動(dòng)項(xiàng)目程序
?npm start
Step 2: 在瀏覽器地址中打開(kāi)一下地址
輸出:

以上就是使用React制作一個(gè)貪吃蛇游戲的代碼詳解的詳細(xì)內(nèi)容,更多關(guān)于React貪吃蛇游戲的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
react執(zhí)行【npx create-react-app my-app】出現(xiàn)常見(jiàn)錯(cuò)誤的解決辦法
文章主要介紹了在使用npx創(chuàng)建React應(yīng)用時(shí)可能遇到的幾種常見(jiàn)錯(cuò)誤及其解決方法,包括缺少依賴、網(wǎng)絡(luò)問(wèn)題和npx解析錯(cuò)誤等,并提供了相應(yīng)的解決措施,此外,還提到了使用騰訊云云產(chǎn)品來(lái)支持React應(yīng)用開(kāi)發(fā)2024-11-11
react-three/postprocessing庫(kù)的參數(shù)中文含義使用解析
這篇文章主要介紹了react-three/postprocessing庫(kù)的參數(shù)中文含義使用總結(jié),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05
簡(jiǎn)單的React SSR服務(wù)器渲染實(shí)現(xiàn)
這篇文章主要介紹了簡(jiǎn)單的React SSR服務(wù)器渲染實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
redux功能強(qiáng)大的Middleware中間件使用學(xué)習(xí)
這篇文章主要為大家介紹了redux功能強(qiáng)大的Middleware中間件使用學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
React實(shí)現(xiàn)錨點(diǎn)跳轉(zhuǎn)組件附帶吸頂效果的示例代碼
這篇文章主要為大家詳細(xì)介紹了React如何實(shí)現(xiàn)移動(dòng)端錨點(diǎn)跳轉(zhuǎn)組件附帶吸頂效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-01-01
在?React?Native?中使用?CSS?Modules的配置方法
有些前端工程師希望也能像開(kāi)發(fā) web 應(yīng)用那樣,使用 CSS Modules 來(lái)開(kāi)發(fā) React Native,本文將介紹如何在 React Native 中使用 CSS Modules,需要的朋友可以參考下2022-08-08
React中用@符號(hào)編寫(xiě)文件路徑實(shí)現(xiàn)方法介紹
在Vue中,我們導(dǎo)入文件時(shí),文件路徑中可以使用@符號(hào)指代src目錄,極大的簡(jiǎn)化了我們對(duì)路徑的書(shū)寫(xiě)。但是react中,要想實(shí)現(xiàn)這種方式書(shū)寫(xiě)文件路徑,需要寫(xiě)配置文件來(lái)實(shí)現(xiàn)2022-09-09
React實(shí)現(xiàn)下拉框的key,value的值同時(shí)傳送
這篇文章主要介紹了React實(shí)現(xiàn)下拉框的key,value的值同時(shí)傳送方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08

