赞
踩
js已经学了大部分了,现在就利用我所学的js知识试试做贪吃蛇小游戏吧
以下部分相关图片以及思路笔记均出自渡一陈老师的视频
首先制作简单的静态页面,添加贪吃蛇移动的背景和相关图片,比如开始游戏等等
将各个功能均封装在函数中,利用主函数调用分函数会使结构更清晰
地图坐标
- //1.初始化地图
- for(let i=0;i<tr;i++){
- for(let j=0;j<td;j++){
- gridData.push({
- x:j,
- y:i
- })
- }
- }
- // 蛇的身体大小
- let snakeBody=20
- // 蛇相关的配置信息
- let snake={
- // 蛇的初始位置
- snakePos:[//domContent为蛇的相应dom元素 flag根据身体还是蛇头分别渲染
- {x:0,y:0,domContent:"",flag:'body'},
- {x:1,y:0,domContent:"",flag:'body'},
- {x:2,y:0,domContent:"",flag:'body'},
- {x:3,y:0,domContent:"",flag:'head'}
- ]
- }
- /**
- *绘制蛇的方法
- * @param {*} snake
- * */
- function drawSnake(snake){
- for(let i=0;i<snake.snakePos.length;i++){
- if(!snake.snakePos[i].domContent){
- // 如果进入此if,说明是第一次创建蛇
- snake.snakePos[i].domContent=document.createElement("div")
- snake.snakePos[i].domContent.style.position="absolute"
- snake.snakePos[i].domContent.style.width=snakeBody+"px"
- snake.snakePos[i].domContent.style.height=snakeBody+"px"
- snake.snakePos[i].domContent.style.left=snake.snakePos[i].x*snakeBody+"px"
- snake.snakePos[i].domContent.style.top=snake.snakePos[i].y*snakeBody+"px"
- if(snake.snakePos[i].flag==='head'){
- // 说明当前是蛇头
- snake.snakePos[i].domContent.style.background=`url("../images/snake.png") center/contain no-repeat`
- }else{
- // 说明是蛇身
- snake.snakePos[i].domContent.style.background="#9ddbb1"
- snake.snakePos[i].domContent.style.borderRadius='50%'
- }
- }
- // 需要将创建的DOM元素添加到container 容器上面
- document.querySelector(".container").append(snake.snakePos[i].domContent)
- }
- }
'运行
添加几个蛇身后效果如图所示:
- // 整个网格的行与列
- const tr=30
- const td=30// 食物相关的配置信息
- let food={
- x:0,y:0,domContent:""
- }function drawFood(){
- // 食物的坐标是随机的 但食物不可生成在蛇头 蛇身 以及container之外的区域
- while(true){
- // 构成一个死循环,直到生成符合要求的坐标才能退出该循环
- let isRepeat=false //默认生成的坐标符合要求
- // 随机生成一个坐标
- food.x=Math.floor(Math.random()*tr)
- food.y=Math.floor(Math.random()*td)
- // 查看坐标是否符合要求(遍历蛇)
- for(let i=0;i<snake.snakePos.length;i++){
- if(snake.snakePos[i].x===food.x&&snake.snakePos[i].y===food.y){
- // 进入此if 说明当前生成的食物坐标和蛇的位置冲突
- isRepeat=true
- break
- }
-
- }
- if(!isRepeat){
- break
- }
-
- }
- // 整个while 循环跳出之后,食物的坐标一定是OK的
-
- if(!food.domContent){
- food.domContent=document.createElement("div")
- food.domContent.style.width=snakeBody+"px"
- food.domContent.style.height=snakeBody+"px"
- food.domContent.style.position="absolute"
- food.domContent.style.background=`url("../images/food.png") center/contain no-repeat`
- document.querySelector('.container').append(food.domContent)
-
- }
- food.domContent.style.left=food.x*snakeBody+"px"
- food.domContent.style.top=food.y*snakeBody+"px"
- }
- // 要明确新的蛇头和旧的蛇头之间的位置关系
- // 我们在确定新的蛇头坐标的时候,会那下面的对象和旧蛇头做一个计算
-
- const directionNum={
- left:{x:-1,y:0,flag:'left'},
- right:{x:1,y:0,flag:'right'},
- top:{x:0,y:-1,flag:'top'},
- bottom:{x:0,y:1,flag:'bottom'}
- }
- // 蛇相关的配置信息
- let snake={
- // 蛇一开始移动的方向
- direction:directionNum.right,//一开始向右边移动
- // 蛇的初始位置
- snakePos:[//domContent为蛇的相应dom元素 flag根据身体还是蛇头分别渲染
- {x:0,y:0,domContent:"",flag:'body'},
- {x:1,y:0,domContent:"",flag:'body'},
- {x:2,y:0,domContent:"",flag:'body'},
- {x:3,y:0,domContent:"",flag:'head'}
- ]
- }
- /**
- * 绑定事件
- */
-
- function bindEvent(){
- document.addEventListener('keydown',function(e){
- // console.log(e.key)
- if(e.key==='ArrowUp'||e.key.toLocaleLowerCase()==='w'){
- // 用户按的是上
- snake.direction=directionNum.top
- }
- if(e.key==='ArrowDown'||e.key.toLocaleLowerCase()==='s'){
- // 用户按的是下
- snake.direction=directionNum.bottom
-
- }
- if(e.key==='ArrowLeft'||e.key.toLocaleLowerCase()==='a'){
- // 用户按的是左
- snake.direction=directionNum.left
-
- }
- if(e.key==='ArrowRight'||e.key.toLocaleLowerCase()==='d'){
- // 用户按的是右
- snake.direction=directionNum.right
-
- }
- snakeMove()
- })
- }
'运行
- /***
- * 碰撞检测
- */
- function isCollide(newHead){
- let collideCheckInfo={
- isCollide:false,//是否碰撞墙壁 蛇身
- isEat:false//是否吃到食物
- }
- // 1.检测是否碰到墙壁
- if(newHead.x<0||newHead.x>=td||newHead.y<0||newHead.y>=tr){
- collideCheckInfo.isCollide=true
- return collideCheckInfo
- }
- // 检测是是否碰到自己
- for(let i=0;i<snake.snakePos.length;i++){
- if(snake.snakePos[i].x===newHead.x&&snake.snakePos[i].y===newHead.y){
- collideCheckInfo.isCollide=true
- return collideCheckInfo
- }
- }
- // 检测是否吃到东西
- if(newHead.x===food.x&&newHead.y===food.y){
- collideCheckInfo.isEat=true
- score++
- }
- return collideCheckInfo
- }
-
- /**
- * 蛇的移动方法
- */
- function snakeMove(){
- let oldHead=snake.snakePos[snake.snakePos.length-1]
- // 根据方向计算出新的蛇头的坐标
- let newHead={
- domContent:"",
- x:oldHead.x+snake.direction.x,
- y:oldHead.y+snake.direction.y,
- flag:'head'
- }
- // 接下来我们首先要做碰撞检测
- // 看计算出来的蛇头有没有碰上食物
- let collideCheckResult=isCollide(newHead)
- if(collideCheckResult.isCollide){
- //进入此if 说明撞墙了
- window.confirm(`游戏结束,您当前的得分为${score}分,是否要重新开始游戏`)
- alert("撞墙了")
- }
- // 将旧的头修改为身体
- oldHead.flag='body'
- oldHead.domContent.style.background="#9ddbb1"
- oldHead.domContent.style.borderRadius="50%"
- snake.snakePos.push(newHead)
- // 判断是否吃到东西
- if(collideCheckResult.isEat){
- // 1.重新生成新的食物
- drawFood()
- }
- else{
- // 说明没有吃到食物
- // 移除最后一个元素
- document.querySelector(".container").removeChild(snake.snakePos[0].domContent)
- snake.snakePos.shift();//删除第一个元素
- }
- drawSnake(snake)
- }
'运行
相关效果如下所示:
代码:
index.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link rel="stylesheet" href="../css/index.css">
- <title>Document</title>
- </head>
- <body>
- <!-- 整个游戏按钮 -->
- <div class="container">
- <!-- 开始按钮 -->
- <button class="startBtn"></button>
- <!-- 暂停按钮 -->
- <button class="pauseBtn"></button>
- </div>
- <script src="../js/config.js"></script>
- <script src="../js/index.js"></script>
- </body>
- </html>
index.css
- *{
- /* 去除内外边距 */
- margin: 0;
- padding: 0;
- }
- /* 整体游戏容器样式 */
- .container {
- position: relative;
- display: flex;
- justify-content: center;
- align-items: center;
- width: 600px;
- height: 600px;
- background-color: #225675;
- border: 20px solid #7dd9ff;
- margin: 20px auto;
- }
- .container button {
- border: none;
- outline: none;
- }
- /* 开始按钮 */
- .startBtn {
- width: 200px;
- height: 80px;
- background: url(../images/startGame.png) center/contain no-repeat;
- display: block;
- }
- /* 暂停按钮 */
- .pauseBtn {
- width: 70px;
- height: 70px;
- background: url(../images/start.png) center/contain no-repeat;
- display: none;
- }
index.js
- /**
- *绘制蛇的方法
- * @param {*} snake
- * */
- function drawSnake(snake){
- for(let i=0;i<snake.snakePos.length;i++){
- if(!snake.snakePos[i].domContent){
- // 如果进入此if,说明是第一次创建蛇
- snake.snakePos[i].domContent=document.createElement("div")
- snake.snakePos[i].domContent.style.position="absolute"
- snake.snakePos[i].domContent.style.width=snakeBody+"px"
- snake.snakePos[i].domContent.style.height=snakeBody+"px"
- snake.snakePos[i].domContent.style.left=snake.snakePos[i].x*snakeBody+"px"
- snake.snakePos[i].domContent.style.top=snake.snakePos[i].y*snakeBody+"px"
- if(snake.snakePos[i].flag==='head'){
- // 说明当前是蛇头
- snake.snakePos[i].domContent.style.background=`url("../images/snake.png") center/contain no-repeat`
- // 根据方向进行一个旋转
- switch(snake.direction.flag){
- case 'top':{
- snake.snakePos[i].domContent.style.transform=`rotate(-90deg)`
- break
- }
- case 'bottom':{
- snake.snakePos[i].domContent.style.transform=`rotate(90deg)`
- break
- }
- case 'left':{
- snake.snakePos[i].domContent.style.transform=`rotate(-180deg)`
- break
- }
- case 'right':{
- snake.snakePos[i].domContent.style.transform=`rotate(0deg)`
- break
- }
- }
- }else{
- // 说明是蛇身
- snake.snakePos[i].domContent.style.background="#9ddbb1"
- snake.snakePos[i].domContent.style.borderRadius='50%'
- }
- }
- // 需要将创建的DOM元素添加到container 容器上面
- document.querySelector(".container").append(snake.snakePos[i].domContent)
- }
- }
- function drawFood(){
- // 食物的坐标是随机的 但食物不可生成在蛇头 蛇身 以及container之外的区域
- while(true){
- // 构成一个死循环,直到生成符合要求的坐标才能退出该循环
- let isRepeat=false //默认生成的坐标符合要求
- // 随机生成一个坐标
- food.x=Math.floor(Math.random()*tr)
- food.y=Math.floor(Math.random()*td)
- // 查看坐标是否符合要求(遍历蛇)
- for(let i=0;i<snake.snakePos.length;i++){
- if(snake.snakePos[i].x===food.x&&snake.snakePos[i].y===food.y){
- // 进入此if 说明当前生成的食物坐标和蛇的位置冲突
- isRepeat=true
- break
- }
-
- }
- if(!isRepeat){
- break
- }
-
- }
- // 整个while 循环跳出之后,食物的坐标一定是OK的
-
- if(!food.domContent){
- food.domContent=document.createElement("div")
- food.domContent.style.width=snakeBody+"px"
- food.domContent.style.height=snakeBody+"px"
- food.domContent.style.position="absolute"
- food.domContent.style.background=`url("../images/food.png") center/contain no-repeat`
- document.querySelector('.container').append(food.domContent)
-
- }
- food.domContent.style.left=food.x*snakeBody+"px"
- food.domContent.style.top=food.y*snakeBody+"px"
- }
- /***
- * 碰撞检测
- * @param {*} newHead 新计算出来的蛇头坐标
- */
- function isCollide(newHead){
- let collideCheckInfo={
- isCollide:false,//是否碰撞墙壁 蛇身
- isEat:false//是否吃到食物
- }
- // 1.检测是否碰到墙壁
- if(newHead.x<0||newHead.x>=td||newHead.y<0||newHead.y>=tr){
- collideCheckInfo.isCollide=true
- return collideCheckInfo
- }
- // 检测是是否碰到自己
- for(let i=0;i<snake.snakePos.length;i++){
- if(snake.snakePos[i].x===newHead.x&&snake.snakePos[i].y===newHead.y){
- collideCheckInfo.isCollide=true
- return collideCheckInfo
- }
- }
- // 检测是否吃到东西
- if(newHead.x===food.x&&newHead.y===food.y){
- collideCheckInfo.isEat=true
- score++
- }
- return collideCheckInfo
- }
- function move(e){
- console.log(e.key)
- if((e.key==='ArrowUp'||e.key.toLocaleLowerCase()==='w')&&snake.direction.flag!=="bottom"){
- // 用户按的是上
- snake.direction=directionNum.top
- }
- if((e.key==='ArrowDown'||e.key.toLocaleLowerCase()==='s')&&snake.direction.flag!=="top"){
- // 用户按的是下
- snake.direction=directionNum.bottom
-
- }
- if((e.key==='ArrowLeft'||e.key.toLocaleLowerCase()==='a')&&snake.direction.flag!=="right"){
- // 用户按的是左
- snake.direction=directionNum.left
-
- }
- if((e.key==='ArrowRight'||e.key.toLocaleLowerCase()==='d')&&snake.direction.flag!=="left"){
- // 用户按的是右
- snake.direction=directionNum.right
-
- }
- snakeMove()
- }
- /**
- * 蛇的移动方法
- */
- function snakeMove(){
- let oldHead=snake.snakePos[snake.snakePos.length-1]
- // 根据方向计算出新的蛇头的坐标
- let newHead={
- domContent:"",
- x:oldHead.x+snake.direction.x,
- y:oldHead.y+snake.direction.y,
- flag:'head'
- }
- // 接下来我们首先要做碰撞检测
- // 看计算出来的蛇头有没有碰上食物
- let collideCheckResult=isCollide(newHead)
- if(collideCheckResult.isCollide){
- //进入此if 说明撞墙了
- if(window.confirm(`游戏结束,您当前的得分为${score}分,是否要重新开始游戏`)){
- // 重新开始游戏
- document.querySelector('.container').innerHTML=`
- <!-- 开始按钮 -->
- <button class="startBtn" style="display:none"></button>
- <!-- 暂停按钮 -->
- <button class="pauseBtn" style="display:none"></button>
- `
- score=0
- console.log(score)
- // gridData=[]
- snake={
- // 蛇一开始移动的方向
- direction:directionNum.right,//一开始向右边移动
- // 蛇的初始位置
- snakePos:[//domContent为蛇的相应dom元素 flag根据身体还是蛇头分别渲染
- {x:0,y:0,domContent:"",flag:'body'},
- {x:1,y:0,domContent:"",flag:'body'},
- {x:2,y:0,domContent:"",flag:'body'},
- {x:3,y:0,domContent:"",flag:'head'}
- ]
- }
- food={
- x:0,y:0,domContent:""
- }
- console.log("已初始化")
- initGame()
- return
- // drawSnake(snake)
- }else {
- // 结束游戏
- document.removeEventListener('keydown',move)
- // console.log("取消")
- clearInterval(timerStop)
- return
-
- }
- // alert("撞墙了")
- }
- // 将旧的头修改为身体
- oldHead.flag='body'
- oldHead.domContent.style.background="#9ddbb1"
- oldHead.domContent.style.borderRadius="50%"
- snake.snakePos.push(newHead)
- // 判断是否吃到东西
- if(collideCheckResult.isEat){
- // 1.重新生成新的食物
- drawFood()
- }
- else{
- // 说明没有吃到食物
- // 移除最后一个元素
- document.querySelector(".container").removeChild(snake.snakePos[0].domContent)
- snake.snakePos.shift();//删除第一个元素
- }
- drawSnake(snake)
- }
-
-
- /**
- * 初始化游戏方法
- */
- function initGame(){
- //1.初始化地图
- for(let i=0;i<tr;i++){
- for(let j=0;j<td;j++){
- gridData.push({
- x:j,
- y:i
- })
- }
- }
- // 2.绘制蛇
- drawSnake(snake)
- // 3.绘制食物
- drawFood()
- }
- function startGame(){
- timerStop=setInterval(function(){
- snakeMove()
- },time)
- }
- /**
- * 绑定事件
- */
-
- function bindEvent(){
- // 1.键盘事件
- document.addEventListener('keydown',move)
- // 2.计时器自动调用蛇移动的方法
- startGame();
- // 3.点击整个容器的时候,可以暂停和重新开始游戏
- document.querySelector('.container').addEventListener('click',function(e){
- // 通过事件委托的形式,判断用户点击的是container还是暂停按钮
- if(e.target.className ==="container"){
- document.querySelector('.pauseBtn').style.display='block'
- clearInterval(timerStop)
-
- }else {
- document.querySelector('.pauseBtn').style.display='none'
- startGame()
- }
- })
- // 4.给暂停按钮绑定事件
- // 点击子元素事件,冒泡后也会触发父元素点击事件
- // document.querySelector('.pauseBtn').addEventListener('click',function(e){
- // e.stopPropagation()
-
- // })
- }
- /**
- * 游戏的主方法
- */
- function main(){
- // 用户点击了开始游戏之后,再做后续工作
- document.querySelector('.startBtn').addEventListener('click',function(e){
- e.stopPropagation()
- document.querySelector('.startBtn').style.display="none"
- // 1.首先初始化游戏
- initGame()
- // 2.绑定事件
- bindEvent()
- })
- }
-
- main()
config.js
- // 游戏相关配置文件
- let gridData=[]//存储地图对象
- // 整个网格的行与列
- const tr=30
- const td=30
- // 蛇的身体大小
- let snakeBody=20
-
- // 要明确新的蛇头和旧的蛇头之间的位置关系
- // 我们在确定新的蛇头坐标的时候,会那下面的对象和旧蛇头做一个计算
-
- const directionNum={
- left:{x:-1,y:0,flag:'left'},
- right:{x:1,y:0,flag:'right'},
- top:{x:0,y:-1,flag:'top'},
- bottom:{x:0,y:1,flag:'bottom'}
- }
-
-
- // 蛇相关的配置信息
- let snake={
- // 蛇一开始移动的方向
- direction:directionNum.right,//一开始向右边移动
- // 蛇的初始位置
- snakePos:[//domContent为蛇的相应dom元素 flag根据身体还是蛇头分别渲染
- {x:0,y:0,domContent:"",flag:'body'},
- {x:1,y:0,domContent:"",flag:'body'},
- {x:2,y:0,domContent:"",flag:'body'},
- {x:3,y:0,domContent:"",flag:'head'}
- ]
- }
- // 食物相关的配置信息
- let food={
- x:0,y:0,domContent:""
- }
- // 游戏分数
- let score=0
- // 停止计时器
- let timerStop=null
- // 计时器事件
- let time=300
'运行
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。