React-Redux自制简易贪吃蛇小游戏
Posted 安之ccy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React-Redux自制简易贪吃蛇小游戏相关的知识,希望对你有一定的参考价值。
初始化
用脚手架新建一个react项目:
npx create-react-app snake-demo
关于安装和创建、启动,可以看我的这篇文章:React入门:基本环境搭建
首先画一个框,当作边界,再初始化贪吃蛇,就像这样:
左上角两个小黑方块就是贪吃蛇了
这里贪吃蛇的移动是以网格做单位的,把游戏区域的宽高各分成100份,贪吃蛇每次移动就前进2个网格,即2%
先写一个具体的div来测试一下将要实现的效果和样式
<div className="App">
<div className="snake-dot" style=top:0, left:0></div>
<div className="snake-dot" style=top:0, left:'2%'></div>
</div>
样式:
.App
height: 600px;
width: 600px;
border: 1px solid #ccc;
margin: 10px auto;
position: relative;
.snake-dot
height: 2%;
width: 2%;
background-color: black;
border: 1px solid #fff;
position: absolute;
得到上述图片的效果后,我们把这个初始snake的定位数据放到state中,方便管理;并把snake-dot单独拎出来,改成动态生成
import React, Component from 'react'
import Snake from './components/Snake';
const initState =
snakeDots:[
[0, 0],
[2, 0]
]
class App extends Component
state = initState;
render()
return (
<div className="App">
<Snake snakeDots=this.state.snakeDots />
</div>
);
export default App;
把snake-dot的动态生成放到子组件Snake.js中:
import React from 'react'
export const Snake = (snakeDots) =>
return (
<>
snakeDots.map((dot, index)=>
let dotStyle =
left:`$dot[0]%`,
top:`$dot[1]%`
console.log(dotStyle)
return (
<div className="snake-dot" key=index style=dotStyle></div>
)
)
</>
)
这样初始化就完成了
贪吃蛇的移动
贪吃蛇的上下左右移动根据键盘输入的上下左右(↑↓←→)键来确定,初始化时默认是向右移动的
先来看看键盘上下左右键的code值
Key | Code |
---|---|
左箭头 | 37 |
右箭头 | 39 |
上箭头 | 38 |
下箭头 | 40 |
游戏开始时,默认是向右走的,因此,我们增加一个表示方向的state:direction,走的速度是每隔200ms前进一步
const initState =
snakeDots: [
[0, 0],
[2, 0]
],
direction: "RIGHT",
speed: 200
当检测到上下左右键按下时,更新state中的方向,贪吃蛇沿该方向前进一步;
若没有按键按下,就沿着当前方向一路前进
贪吃蛇的头部(head)是小黑方块的最后一块,不断地删去第一个小黑方块并在direction方向上新增一小黑方块;视觉上,贪吃蛇就有不停向前移动的效果
componentDidMount()
// 监听键盘按下事件
document.onkeydown = this.onkeydown;
// 每隔200ms前进一步
setInterval(this.onMove, this.state.speed);
onkeydown = (e) =>
// 根据按下的键盘键更新direction
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;
default:
this.setState( direction: "RIGHT" );
onMove = () =>
// 根据direction指示的方向前进
let newSnakeDots = [...this.state.snakeDots];
let header = newSnakeDots[newSnakeDots.length - 1];
switch (this.state.direction)
case "UP":
header = [header[0], header[1] - 2];
break;
case "DOWN":
header = [header[0], header[1] + 2];
break;
case "LEFT":
header = [header[0] - 2, header[1]];
break;
case "RIGHT":
header = [header[0] + 2, header[1]];
break;
default:
header = [header[0] + 2, header[1]];
// 删去第一个小黑块,并在direction方向上新增新小黑块,更新snakeDots
newSnakeDots.shift();
newSnakeDots.push(header);
this.setState( snakeDots: newSnakeDots )
效果:
边界限制与得分计算
当触碰边界或者撞击到自身时,游戏结束,弹窗提示,分数为贪吃蛇的长度-2(初始长度为2,其余均是吃到食物后变长的,吃一个食物长长1格)
componentDidUpdate()
this.checkIfBordered();
checkIfBordered = () =>
let newSnakeDots = [...this.state.snakeDots];
let header = newSnakeDots[newSnakeDots.length - 1];
if (header[0] < 0 || header[0] > 98 || header[1] < 0 || header[1] > 98)
alert(`触碰边界,游戏结束,你的得分是:$newSnakeDots.length - 2`);
this.setState(initState);
食物随机出现
由于我们规定,贪吃蛇触碰到边界时算游戏结束,因此,食物不能出现在边界上,否则,一吃到食物就代表触壁结束游戏(可以改进为:贪吃蛇每次前进1%,当食物被触碰比例达50%时可以当作贪吃蛇吃掉该食物,马上掉头避免触壁,以后再改吧~)
因此,食物出现的范围在top:2%~96%,left的范围也是如此
const getRandomFood = ()=>
// 食物出现的范围是:[2, 96] 推算过程:[0,95)->[2, 97)->[1, 48.5]->[1,48]->[2,96]
let max = 95;
let min = 2;
let x = Math.floor((Math.random() * max + min) / 2) * 2;
let y = Math.floor((Math.random() * max + min) / 2) * 2;
return [x,y]
const initState =
snakeDots: [
[0, 0],
[2, 0]
],
direction: "RIGHT",
speed: 200,
food:getRandomFood() // 随机生成食物的位置
食物的展示:
import Food from './components/Food';
render()
return (
<div className="App">
<Snake snakeDots=this.state.snakeDots />
<Food food=this.state.food />
</div>
);
import React from 'react'
export const Food = (food) =>
let foodStyle =
left:`$food[0]%`,
top:`$food[1]%`
return (
<div className="food" style=foodStyle></div>
)
食物的样式:
.food
height: 2%;
width: 2%;
background-color: red;
border: 1px solid #fff;
position: absolute;
判断是否吃到食物
贪吃蛇的头(head)和食物重叠时,代表吃到
贪吃蛇变长,食物的位置变成新的head,重新放置食物
componentDidUpdate()
// 每次更新都判断一下
this.checkIfBordered();
this.checkIfEated();
checkIfEated = ()=>
let newSnakeDots = [...this.state.snakeDots];
let header = newSnakeDots[newSnakeDots.length - 1];
// head与食物重叠时,代表吃到食物
if(header[0]==this.state.food[0] && header[1]==this.state.food[1])
console.log("吃到了")
newSnakeDots.unshift([]);
this.setState(snakeDots:newSnakeDots);
this.setState(food:getRandomFood());
效果:
完整代码:
新建文件夹component存放组件:
App.js
import React, Component from 'react'
import Snake from './components/Snake';
import Food from './components/Food';
// 随机获取食物位置
const getRandomFood = ()=>
// [2, 96]: [0,95)->[2, 97)->[1, 48.5]->[1,48]->[2,96]
let max = 95;
let min = 2;
let x = Math.floor((Math.random() * max + min) / 2) * 2;
let y = Math.floor((Math.random() * max + min) / 2) * 2;
console.log("food-position",x,y)
return [x,y]
// 另存state以便重新初始化
const initState =
snakeDots: [
[0, 0],
[2, 0]
],
direction: "RIGHT",
speed: 200,
food:getRandomFood()
class App extends Component
state = initState;
// 监听与计时器
componentDidMount()
document.onkeydown = this.onkeydown;
setInterval(this.onMove, this.state.speed);
// 更新时检测是否出界或吃到食物
componentDidUpdate()
this.checkIfBordered();
this.checkIfEated();
// 判断键盘按键,更新direction
onkeydown = (e) =>
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;
default:
this.setState( direction: "RIGHT" );
// 根据direction移动
onMove = () =>
let newSnakeDots = [...this.state.snakeDots];
let header = newSnakeDots[newSnakeDots.length - 1];
switch (this.state.direction)
case "UP":
header = [header[0], header[1] - 2];
break;
case "DOWN":
header = [header[0], header[1] + 2];
break;
case "LEFT":
header = [header[0] - 2, header[1]];
break;
case "RIGHT":
header = [header[0] + 2, header[1]];
break;
default:
header = [header[0] + 2, header[1]];
newSnakeDots.shift();
newSnakeDots.push(header);
this.setState( snakeDots: newSnakeDots )
// console.log(this.state.snakeDots)
// 判断是否出界
checkIfBordered = () =>
let newSnakeDots = [...this.state.snakeDots];
let header = newSnakeDots[newSnakeDots.length - 1];
if (header[0] < 0 || header[0] > 98 || header[1] < 0 || header[1] > 98)
alert(`触碰边界,游戏结束,你的得分是:$newSnakeDots.length - 2`);
this.setState(initState);
// 判断是否吃到食物
checkIfEated = ()=>
React-Redux自制简易贪吃蛇小游戏