#夏日挑战赛# OpenHarmony基于JS实现的贪吃蛇
Posted 开源基础软件社区官方
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#夏日挑战赛# OpenHarmony基于JS实现的贪吃蛇相关的知识,希望对你有一定的参考价值。
前言
不知道干啥,那就敲代码吧,写个贪吃蛇,很显然,被自己菜哭了,自己写的贪吃蛇自己都不会玩(ps:我曾经可是在不会死亡的情况下完了好长时间>_<)
实现效果如下:
::: hljs-center
:::
具体实现思路
容器初始化
- 在onShow钩子函数那里获取到游戏容器的宽高,其实我是不想在这里获取的,但没办法,好像getBoundingClientRect()需要挂载后才能拿到值,在这之前的钩子函数中都拿不到具体的属性值(ps:这是我猜的,因为文档写的很...我找不到只能挨个试)
- 拿到容器宽高后,根据蛇的身体长度(就是每个小圆点)来确定要划分多少个格子,形成一个坐标轴,后面食物,蛇的移动都根据这坐标轴来确定
onShow() // 第一次初始化
this.conH = this.$refs["con"].getBoundingClientRect().height ;
this.conW = this.$refs["con"].getBoundingClientRect().width ;
this.h = Math.floor(this.conH / this.snakeBody);
this.w = Math.floor(this.conW / this.snakeBody);
for (var i = 0; i < this.w; i++) //绘制网格
this.grid.push([])
for (var j = 0; j < this.h; j++)
this.grid[i].push(
x: i,
y: j
);
this.init(); //初始化函数
蛇
用一个数组实现,数组索引0为蛇的尾巴,索引length-1为头
init()
const snakePos = [ //蛇的初始化的身体
x : 0,
y : 0,
flag : b,
id : Math.random()
,
x : 1,
y : 0,
flag : b,
id : Math.random()
,
x : 2,
y : 0,
flag : h,
id : Math.random()
];
this.snake.snakePos = snakePos; //把初始化的身体赋给蛇
this.randomFood(); //随机生成食物
食物
食物随机生成,位置在网格中任意位置,但不能生成在蛇的身体位置中
randomFood() //随机生成食物
while(true)
let x = Math.floor(Math.random() * this.conW);
let y = Math.floor(Math.random() * this.conH);
x = x - (x % this.snakeBody); //x,y化为和蛇身体倍数的坐标
y = y - (y % this.snakeBody);
let is = this.snake.snakePos.find((item)=>
return item.x == x && item.y == y;
)
this.food.x = x;
this.food.y = y;
if(!is) //当食物的位置不为蛇不和蛇的位置重叠就跳出结束死循环
break;
蛇的移动
蛇的移动是通过对数组的push和shift实现,蛇有移动的方向,根据方向来修改新增蛇头的x和y的值
des://蛇的方向
"-20": // 向上移动一位
x:0,
y:-1,
flag:
,
"20"://向下
x:0,
y:1,
flag:
,
"10": //右
x:1,
y:0,
flag:
,
"-10": //左
x:-1,
y:0,
flag:
,
addHead(des)
//添加蛇头 des为蛇的方向,一共有四个方向上下左右,每次移动是都会传一个方向过来
const oHead = this.snake.snakePos[this.snake.snakePos.length -1];
const newHead =
x : oHead.x + des.x,
y : oHead.y + des.y,
flag : h,
id : Math.random()
this.isEnd(newHead);
this.snake.snakePos.push(newHead);
oHead.flag = b;
,
move(des) // 蛇移动时,原头变身体,原尾巴去掉,也就是push一个头,shift一个尾巴
this.addHead(des);
this.snake.snakePos.shift();
,
移动图如下
::: hljs-center
:::
蛇的死亡判定
当蛇头的x >= 地图的x最大值 || x < 0 || 蛇头的Y >= 地图的Y最大值 || Y < 0 || 蛇头的(x,y) == 蛇身体任意一个 (x,y)
isEnd(newHead) // 判定蛇是是否死亡
if(newHead.x >= this.w || newHead.x < 0 || newHead.y >= this.h || newHead.y < 0)
this.setIsEnd();
let is = this.snake.snakePos.find((item)=> //循环查询是否撞到自己
return item.x == newHead.x && item.y == newHead.y;
)
if(is)
this.setIsEnd(); //结束游戏
,
setIsEnd()
clearInterval(this.timeId); //清除蛇的移动定时器
this.isEndP = true; //这个属性是用来是否显示游戏结果界面
操作蛇的移动
-20,20,10,-10,原本是一开用来判定是否当前移动的方向是否和原来的方向冲突,后来发现还是用坐标轴香,也就懒得改了
intervalMove(d) // 自动跑
if(!this.isStart) return;//判定是否开始
clearInterval(this.timeId); //清除以前的定时时器
this.timeId = setInterval(()=>
const head = this.snake.snakePos[this.snake.snakePos.length - 1];
this.move(d);
if(this.snakeBody * head.x == this.food.x && this.food.y == this.snakeBody * head.y ) //蛇吃到食物
this.addHead(d); //新增蛇头,这个不去除尾巴
this.randomFood(); //再次重新生成食物
this.result++; //分数
,1000/this.level); //this.level级别,决定蛇移动的速度
,
isCuurDes(value = ,x1,x2)
// 判断当前蛇的方向,x1 为新方向,x2为以前的方向,主要是判断点击的按钮是否左右,上下冲突
if((+x1 + +x2) == 0 ) return false; //这里+x1,+x2 是用来把字符串转成数字
if(this.isEndP) return;//当游戏结束无法再修改方向
this.currDes = value; //存下方向
return true;
,
clickBut(m)// 点击按钮
const value = m.target.dataSet.value;
switch(value)
case "-20": //上
//判断方向是否相反,如果相反则不切换方向
this.isCuurDes(this.des[value],this.des[value].y,this.currDes.y)
&& this.intervalMove(this.des[value]);
break;
case "20":// 下
this.isCuurDes(this.des[value],this.des[value].y,this.currDes.y)
&& this.intervalMove(this.des[value]);
break;
case "-10": //左
this.isCuurDes(this.des[value],this.des[value].x,this.currDes.x)
&& this.intervalMove(this.des[value]);
break;
case "10": // 右
this.isCuurDes(this.des[value],this.des[value].x,this.currDes.x)
&& this.intervalMove(this.des[value]);
break;
case "1": //开始或暂停
if(this.isEndP) return
this.isStart = !this.isStart;
if(this.isStart && !this.isEndP)
this.intervalMove(this.currDes);
else
clearInterval(this.timeId);
break;
具体属性和方法
属性
名称 | 类型 | 备注 |
---|---|---|
result | number | 分数 |
conW | number | 容器宽度 |
conH | number | 容器高度 |
snakeBody | number | 蛇身体单位 |
h | number | 网格的y长度 |
w | number | 网格的x长度 |
grid | Array<Array> | 网格地图 |
food | object | 食物 |
timeId | number | 定时器id |
level | number | 游戏难度级别 |
des | Object<Object> | 蛇的四个方向 |
isStart | Boolean | 判断是否开始 |
snake | Object<Object> | 蛇 |
currDes | object | 当前蛇前进的方向 |
isEndP | Boolean | 判断游戏是否结束 |
方法
名称 | 参数 | 备注 |
---|---|---|
init | 无 | 初始化函数 |
onShow | 无 | 框架生命周期钩子函数 |
isEnd | newHead : object | 判断游戏是否结束 |
setIsEnd | 无 | 设置游戏结束相关数据 |
randomFood | 无 | 随机生成食物 |
addHead | des : object | 增加新头 |
move | des : object | 蛇的移动 |
intervalMove | d :object | 蛇自动移动 |
isCuurDes | value:object ,x1:string,x2:string | 定时器id |
clickBut | m:object | 操作蛇的移动的点击事件 |
reInit | 无 | 重新开始游戏 |
代码
html
```html/xml
<div class="container">
<div class="game-container" id="con1" ref="con" >
<div class="food" style="left: food.x px ;top: food.y px;" ></div>
<div for="snake.snakePos" tid="id"
class=" $item.flag == b ?snake-body : snake-head "
style="left: $item.x snakeBodypx;top:$item.y snakeBody;"
左
</button>
<button data-value="1" class="but" @click="clickBut">
isStart ? 暂停 : 开始
</button>
<button data-value="10" class="but" @click="clickBut">
右
</button>
</div>
<div class="center">
<button data-value="20" class="but" @click="clickBut">
下
</button>
</div>
</div>
<div class="end" if="isEndP == true">
<div>
<text>
游戏结束!
</text>
</div>
<div>
<text>
当前得分:result 分
</text>
</div>
<div @click="reInit"
style="width: 120px;margin: 10px 0 0 30px;
border-radius: 5px;">
<text>
点击重开
</text>
</div>
</div>
<div class="result">
<text>
分数:result
</text>
</div>
</div>
## css
```html/xml
.container
height: 100%;
width: 100%;
.container,.buts, .end
display: flex;
flex-direction: column;
position: relative;
.game-container
width: 100%;
height: 540px;
position: relative;
background-color: #85ce61;
.snake-head
height: 20px;
width: 20px;
position: absolute;
border-radius: 10px;
background-color: red;
.snake-body
height: 20px;
width: 20px;
position: absolute;
border-radius: 10px;
background-color: white;
.food
height: 20px;
width: 20px;
position: absolute;
border-radius: 10px;
background-color: black;
.center
display: flex;
justify-content:center;
height: 50px;
margin:2px 0;
.but
width: 50px;
border-radius: 5px;
font-weight: 700;
margin: 0 2px ;
background-color: #85ce61;
.buts
margin-top:10px ;
.end
position: fixed;
top: 50%;
left: 50%;
transform: translate(-25%,-100%);
.result
position: fixed;
top: 0;
left: 260px;
JS
export default
data:
result:0,
conW : 0,// 游戏界面宽
conH : 0,
snakeBody: 20,
h:0,
w:0,
grid:[],
food:
x:0,
y:0
,
timeId:null, // 计时器id
level:5,//档位
des://蛇的方向
"-20": // 向上移动一位
x:0,
y:-1,
flag:
,
"20"://向下
x:0,
y:1,
flag:
,
"10": //右
x:1,
y:0,
flag:
,
"-10": //左
x:-1,
y:0,
flag:
,
isStart:false,
snake:
snakePos:null
,
currDes:x:1,y:0,
isEndP:false
,
init()
const snakePos = [
x : 0,
y : 0,
flag : b,
id : Math.random()
,
x : 1,
y : 0,
flag : b,
id : Math.random()
,
x : 2,
y : 0,
flag : h,
id : Math.random()
];
this.snake.snakePos = snakePos
this.randomFood()
,
onShow() // 第一次初始化
this.conH = this.$refs["con"].getBoundingClientRect().height ;
this.conW = this.$refs["con"].getBoundingClientRect().width ;
this.h = Math.floor(this.conH / this.snakeBody);
this.w = Math.floor(this.conW / this.snakeBody);
for (var i = 0; i < this.w; i++)
this.grid.push([])
for (var j = 0; j < this.h; j++)
this.grid[i].push(
x: i,
y: j
);
this.init();
,
isEnd(newHead)
if(newHead.x >= this.w || newHead.x < 0 || newHead.y >= this.h || newHead.y < 0)
this.setIsEnd();
let is = this.snake.snakePos.find((item)=>
return item.x == newHead.x && item.y == newHead.y
)
if(is)
this.setIsEnd();
,
setIsEnd()
clearInterval(this.timeId)
this.isEndP = true
,
randomFood() //随机生成食物
while(true)
let x = Math.floor(Math.random() * this.conW);
let y = Math.floor(Math.random() * this.conH);
x = x - (x % this.snakeBody);
y = y - (y % this.snakeBody);
let is = this.snake.snakePos.find((item)=>
return item.x == x && item.y == y
)
this.food.x = x;
this.food.y = y;
if(!is)
break;
,
addHead(des)
const oHead = this.snake.snakePos[this.snake.snakePos.length -1];
const newHead =
x : oHead.x + des.x,
y : oHead.y + des.y,
flag : h,
id : Math.random()
this.isEnd(newHead);
this.snake.snakePos.push(newHead);
oHead.flag = b;
,
move(des) // 蛇移动时,原头变身体,原尾巴去掉,也就是push一个头,shift一个尾巴
this.addHead(des);
this.snake.snakePos.shift();
,
intervalMove(d) // 自动跑
if(!this.isStart) return
clearInterval(this.timeId);
this.timeId = setInterval(()=>
const head = this.snake.snakePos[this.snake.snakePos.length - 1];
this.move(d);
if(this.snakeBody * head.x == this.food.x && this.food.y == this.snakeBody * head.y )
this.addHead(d);
this.randomFood();
this.result++;
,1000/this.level);
,
isCuurDes(value = ,x1,x2)
// 判断当前蛇的方向,x1 为新方向,x2为以前的方向,主要是判断点击的按钮是否左右,上下冲突
if((+x1 + + x2) == 0 ) return false;
if(this.isEndP) return ;
this.currDes = value; //存下方向
return true;
,
clickBut(m)// 点击按钮
const value = m.target.dataSet.value;
switch(value)
case "-20": //上
//判断方向是否相反,如果相反则不切换方向
this.isCuurDes(this.des[value],this.des[value].y,this.currDes.y)
&& this.intervalMove(this.des[value]);
break;
case "20":// 下
this.isCuurDes(this.des[value],this.des[value].y,this.currDes.y)
&& this.intervalMove(this.des[value]);
break;
case "-10": //左
this.isCuurDes(this.des[value],this.des[value].x,this.currDes.x)
&& this.intervalMove(this.des[value]);
break;
case "10": // 右
this.isCuurDes(this.des[value],this.des[value].x,this.currDes.x)
&& this.intervalMove(this.des[value]);
break;
case "1": //开始或暂停
if(this.isEndP) return
this.isStart = !this.isStart;
if(this.isStart && !this.isEndP)
this.intervalMove(this.currDes);
else
clearInterval(this.timeId);
break;
,
reInit()
this.init();
this.isEndP = false;
this.isStart = false;
this.currDes=x:1,y:0;
最后
嗯...问就是要优化
附件链接:https://ost.51cto.com/resource/2199
以上是关于#夏日挑战赛# OpenHarmony基于JS实现的贪吃蛇的主要内容,如果未能解决你的问题,请参考以下文章
#夏日挑战赛# OpenHarmony - ArkUI(TS)开发翻页时钟
#夏日挑战赛# 用OpenHarmony eTS 实现一个Huawei app标准布局
#夏日挑战赛# OpenHarmony HiSysEvent打点调用实践(L2)