天津做网站报价,一个网站包括,wordpress ssh,适合小学生摘抄的新闻2022年大家都玩过贪吃蛇小游戏#xff0c;控制一条蛇去吃食物#xff0c;然后蛇在吃到食物后会变大。本篇博客将会实现贪吃蛇小游戏的功能。 1.实现效果 2.整体布局
/*** 游戏区域样式*/
const gameBoardStyle {gridTemplateColumns: repeat(${width}, 1fr),gridTemplateRows: re… 大家都玩过贪吃蛇小游戏控制一条蛇去吃食物然后蛇在吃到食物后会变大。本篇博客将会实现贪吃蛇小游戏的功能。 1.实现效果 2.整体布局
/*** 游戏区域样式*/
const gameBoardStyle {gridTemplateColumns: repeat(${width}, 1fr),gridTemplateRows: repeat(${height}, 1fr),display: grid,border: 1px solid #000,width: 500px,height: 500px,backgroundColor: #488cfa
};/*** 小蛇样式*/
const snakeBodyStyle (segment) ({gridRowStart: segment.y,gridColumnStart: segment.x,backgroundColor: green
})/*** 食物样式*/
const foodStyle {gridRowStart: food.current.y,gridColumnStart: food.current.x,backgroundColor: red
}div className{snake-game}div className{game-board} style{gameBoardStyle}{/*蛇身体*/}{snake.map((segment, idx) div key{idx} className{snake-body} style{snakeBodyStyle(segment)}/)}{/*食物*/}div className{food} style{foodStyle}/div/div/div 采用grid 布局整个游戏区域划分为width*height个小块小蛇身体的每一部分对应一小块食物对应一小块。
3.技术实现
a.数据结构 小蛇的数据结构是个坐标数组snake[0]是蛇头snake[snake.length-1]是蛇尾巴。snake[i].x表示第i块位置的x坐标snake[i].y表示第i块位置的y坐标。 食物的数据结构是坐标。 游戏区域是一个width*height的虚拟空间。
b.场景
一、小蛇如何移动以及移动方式 1. 通过设置监听键盘的上下左右事件来触发小蛇的移动。 2. 通过定时器实现小蛇沿着当前方向移动 // 移动方向上下左右
const directions [[0, -1], [0, 1], [-1, 0], [1, 0]];
// 当前移动方向
const [currentDirection, setCurrentDirection] useState(3);// 小蛇移动
function move() {const direction directions[currentDirection];// 更新上一次蛇尾巴lastTail.current {x: snake[snake.length - 1].x, y: snake[snake.length - 1].y};const head snake[0];// 移动小蛇将数组后移动for (let i snake.length - 1; i 0; i--) {snake[i].x snake[i - 1].x;snake[i].y snake[i - 1].y;}// 更新蛇头head.x direction[0];head.y direction[1];// 触发渲染setSnake([...snake]);
}const [click, setClick] useState(0)
// 设置键盘监听函数
useEffect(() {document.addEventListener(keydown, function (event) {const key event.key;if (key ArrowUp) {// 监听到了向上箭头键的按下操作setCurrentDirection(0)setClick((c)c1);} else if (key ArrowDown) {// 监听到了向下箭头键的按下操作setCurrentDirection(1)setClick((c)c1);} else if (key ArrowLeft) {// 监听到了向左箭头键的按下操作setCurrentDirection(2)setClick((c)c1);} else if (key ArrowRight) {// 监听到了向右箭头键的按下操作setCurrentDirection(3)setClick((c)c1);}});
}, [])/*** 设定定时器每1s向当前方向移动小蛇* 如果敲键盘或者吃到食物需要更新定时器* tips: 吃到食物更新是因为定时器晚执行可能会有并发问题*/
useEffect(() {console.log(click)move()const timer setInterval(() {move();}, 1000);return () {clearInterval(timer);};
}, [click, snake.length]);
二、游戏结束判断
1.游戏成功判断若无发生成新的食物则游戏成功
2.游戏失败判断若小蛇出边界或者小蛇撞到自己则游戏失败。
// 每次渲染后判断小蛇状态
useEffect(() {// 判断小蛇撞出边界if (head.x 0 || head.x width || head.y 0 || head.y height) {console.log(游戏失败)alert(出界游戏失败);reset();return;}// 判断小蛇撞到自己for (let i 1; i snake.length; i) {if (head.x snake[i].x head.y snake[i].y) {console.log(游戏失败)console.log(snake: JSON.stringify(snake))alert(撞到自己了游戏失败);reset();return;}}})
三、食物生成以及吃食物操作 1.食物需要在区域内随机生成并且不能生成在小蛇身体上若无地方生成则游戏通关。 2.吃食物操作会增长小蛇的长度在小蛇的尾巴添加一截需要存储前一个路径的尾巴位置。
// 随机生成食物
function generateFood(snake) {const x Math.floor(Math.random() * width);const y Math.floor(Math.random() * height);// 如果蛇长等于宽高说明蛇占满了整个区域已成功if (snake.length width * height) {return null;}// 判断食物是否在蛇身上for (let node of snake) {if (node.x x node.y y) {// 重新生成食物return generateFood(snake);}}return {x, y};
}// 蛇尾巴
const lastTail useRef(null);// 每次渲染后判断小蛇状态
useEffect(() {const head snake[0];// 小蛇吃到食物if (head.x food.current.x head.y food.current.y) {console.log(eat food!)// 添加上次蛇尾巴let nTail {...lastTail.current};snake.push(nTail);lastTail.current nTail;// 重新生成食物food.current generateFood(snake);if (food.current null) {console.log(恭喜已通过)alert(恭喜已经通关);reset();return;}// 发起渲染console.log(newsnake: JSON.stringify(snake))setSnake([...snake]);return;}
});
c.整体代码
const {useState, useRef, useEffect} require(react);const Snake ({width, height}) {// 移动方向上下左右const directions [[0, -1], [0, 1], [-1, 0], [1, 0]];// 当前移动方向const [currentDirection, setCurrentDirection] useState(3);// 初始小蛇const initialSnake [{x: 0, // pos xy: 0, // pos y}];// 蛇身体const [snake, setSnake] useState(initialSnake);// 食物const food useRef(null);// 初始化食物if (food.current null) {food.current generateFood(snake);}// 随机生成食物function generateFood(snake) {const x Math.floor(Math.random() * width);const y Math.floor(Math.random() * height);// 如果蛇长等于宽高说明蛇占满了整个区域已成功if (snake.length width * height) {return null;}// 判断食物是否在蛇身上for (let node of snake) {if (node.x x node.y y) {// 重新生成食物return generateFood(snake);}}return {x, y};}// 蛇尾巴const lastTail useRef(null);// 小蛇移动function move() {const direction directions[currentDirection];// 更新蛇尾巴lastTail.current {x: snake[snake.length - 1].x, y: snake[snake.length - 1].y};const head snake[0];for (let i snake.length - 1; i 0; i--) {snake[i].x snake[i - 1].x;snake[i].y snake[i - 1].y;}head.x direction[0];head.y direction[1];setSnake([...snake]);}// 游戏结束后重置function reset() {setSnake([...initialSnake]);setCurrentDirection(3);lastTail.current null;}// 判断是否游戏结束useEffect(() {const head snake[0];// 判断小蛇撞出边界if (head.x 0 || head.x width || head.y 0 || head.y height) {console.log(游戏失败)alert(出界游戏失败);reset();return;}// 判断小蛇撞到自己for (let i 1; i snake.length; i) {if (head.x snake[i].x head.y snake[i].y) {console.log(游戏失败)console.log(snake: JSON.stringify(snake))alert(撞到自己了游戏失败);reset();return;}}})// 判断是否吃到食物useEffect((){const head snake[0];// 小蛇吃到食物if (head.x food.current.x head.y food.current.y) {console.log(eat food!)// 添加上次蛇尾巴let nTail {...lastTail.current};snake.push(nTail);lastTail.current nTail;// 重新生成食物food.current generateFood(snake);if (food.current null) {console.log(恭喜已通过)alert(恭喜已经通关);reset();return;}// 发起渲染console.log(newsnake: JSON.stringify(snake))setSnake([...snake]);return;}})const [click, setClick] useState(0)// 设置键盘监听函数useEffect(() {document.addEventListener(keydown, function (event) {const key event.key;if (key ArrowUp) {// 监听到了向上箭头键的按下操作setCurrentDirection(0)setClick((c)c1);} else if (key ArrowDown) {// 监听到了向下箭头键的按下操作setCurrentDirection(1)setClick((c)c1);} else if (key ArrowLeft) {// 监听到了向左箭头键的按下操作setCurrentDirection(2)setClick((c)c1);} else if (key ArrowRight) {// 监听到了向右箭头键的按下操作setCurrentDirection(3)setClick((c)c1);}});}, [])/*** 设定定时器每1s向当前方向移动小蛇* 如果敲键盘或者吃到食物需要更新定时器* tips: 吃到食物由于定时器晚执行可能会用老的state覆盖*/useEffect(() {console.log(click)move()const timer setInterval(() {move();}, 1000);return () {clearInterval(timer);};}, [click, snake.length]);/*** 游戏区域样式*/
const gameBoardStyle {gridTemplateColumns: repeat(${width}, 1fr),gridTemplateRows: repeat(${height}, 1fr),display: grid,border: 1px solid #000,width: 500px,height: 500px,backgroundColor: #488cfa
};/*** 小蛇样式*/
const snakeBodyStyle (segment) ({gridRowStart: segment.y,gridColumnStart: segment.x,backgroundColor: green
})/*** 食物样式*/
const foodStyle {gridRowStart: food.current.y,gridColumnStart: food.current.x,backgroundColor: red
}// 小蛇组成return (div className{snake-game}div className{game-board} style{gameBoardStyle}{/*蛇身体*/}{snake.map((segment, idx) div key{idx} className{snake-body} style{snakeBodyStyle(segment)}/)}{/*食物*/}div className{food}style{foodStyle}/div/div/div/)
}export default Snake