游戏效果
文件目录
准备1:新建index.html,编写游戏静态页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>贪吃蛇</title>
</head>
<body>
<div class="main">
<div class="stage">
<div id="snake">
<div></div>
</div>
<div id="food">
<div></div>
</div>
</div>
<div class="score-panel">
<div>
SCORE: <span id="score">0</span>
</div>
<div>
level: <span id="level">1</span>
</div>
</div>
</div>
</body>
</html>
准备2:使用less,修改样式,编写CSS
//设置变量
@bg-color : #b7d4a8;
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font:bold 20px "Courier"
}
.main {
width: 360px;
height: 420px;
background-color: @bg-color;
margin: 100px auto;
border: 10px solid black;
border-radius: 40px;
display: flex;
flex-flow: column;
align-items: center;
justify-content: space-around;
//游戏舞台
.stage {
width: 304px;
height: 304px;
border: 2px solid black;
position: relative;
//蛇的样式
#snake{
&>div{
width: 10px;
height: 10px;
background-color: black;
border: 1px solid @bg-color;
//绝对定位
position: absolute;
}
}
#food{
position: absolute;
left: 40px;
top: 100px;
background-color: rebeccapurple;
display: flex;
flex-flow: row wrap;
justify-content: space-between;
align-content: space-between;
&>div{
width: 4px;
height: 4px;
background-color: black;
transform: rotate(45deg);
}
}
}
//记分牌
.score-panel {
width: 300px;
display: flex;
justify-content: space-between;
}
}
准备3:创建4个类:食物类-Food、记分牌等级类-ScorePanel、蛇类-Snake、操控类-GameControl
//食物类Food
class Food{
//定义一个属性表示食物所对应的元素
element : HTMLElement;
constructor(){
//获取页面中的food元素并将其赋值给element
this.element = document.getElementById('food') ! ;
}
//定义一个获取食物X轴坐标的方法
get X (){
return this.element.offsetLeft
}
//定义一个获取食物Y轴坐标的方法
get Y (){
return this.element.offsetTop
}
//修改食物的位置
change(){
//生成一个随机的位置
let top = Math.round(Math.random()*29)*10
let left = Math.round(Math.random()*29)*10
//食物的位置最小的0,最大是290
//蛇移动一次就是一格,一格的大小就是10,所以就要求食物的
this.element.style.left = left + 'px'
this.element.style.top = top + 'px'
}
}
export default Food
//记分牌的类
class ScorePanel {
score = 0;
level = 1
//分数和等级所在的元素,在构造函数中进行初始化
scoreEle:HTMLElement
levelEle:HTMLElement
//设置一个变量限制等级
maxLevel :number
//设置一个变量表示多少分时升级
upScore : number
constructor(maxLevel:number = 10,upScore:number = 10){
this.scoreEle = document.getElementById('score')!
this.levelEle = document.getElementById('level')!
this.maxLevel = maxLevel
this.upScore = upScore
}
//设置一个加分的方法
addScore(){
//使分数自增
this.scoreEle.innerHTML = ++this.score + ''
if(this.score % this.upScore ===0){
this.levelUp()
}
}
//提升等级方法
levelUp(){
if(this.level < this.maxLevel){
this.levelEle.innerHTML = ++this.level + ''
}
}
}
export default ScorePanel
//蛇类-Snake
class Snake {
//表示蛇头的元素
head : HTMLElement
//蛇的身体
bodies: HTMLCollection
//获取蛇的容器
element :HTMLElement
constructor(){
this.element = document.getElementById('snake')!
this.head = document.querySelector('#snake >div') as HTMLElement
this.bodies = this.element.getElementsByTagName('div')
}
//获取蛇(蛇头)的坐标
get X (){
return this.head.offsetLeft
}
get Y(){
return this.head.offsetTop
}
//设置蛇头的坐标
set X(value:number){
//如果新值和旧值相同,则直接返回不再修改
if(this.X ===value){
return
}
//X的值的合法范围在0-290之间
if(value <0 || value >290){
//进入判断说明蛇撞墙了
throw new Error('蛇撞墙了')
}
//修改X时,是在修改水平坐标,蛇在左右移动,蛇向左移动时,不能向右移动,反之亦然
if(this.bodies[1]&&(this.bodies[1] as HTMLElement).offsetLeft ===value){
//如果发送了掉头,让蛇向反方向继续移动
if(value >this.X){
//如果新值value大于旧值X,则说明蛇在向右走,此时发送掉头,应该使蛇继续向左走
value = this.X - 10;
}else{
//向左走
value = this.X + 10
}
}
//移动身体
this.moveBody()
this.head.style.left = value + 'px'
this.checkHeadBody()
}
set Y(value:number){
if(this.Y ===value){
return
}
if(value <0 || value >290){
//进入判断说明蛇撞墙了
throw new Error('蛇撞墙了')
}
//修改Y时,是在修改垂直坐标,蛇在上下移动,蛇向上移动时,不能向下移动,反之亦然
if(this.bodies[1]&&(this.bodies[1] as HTMLElement).offsetTop ===value){
//如果发送了掉头,让蛇向反方向继续移动
if(value >this.Y){
//如果新值value大于旧值Y,则说明蛇在向下走,此时发送掉头,应该使蛇继续向上走
value = this.Y - 10;
}else{
//向左走
value = this.Y + 10
}
}
this.moveBody()
this.head.style.top = value + 'px'
//检查有没有撞到自己
this.checkHeadBody()
}
//蛇增加身体的方法
addBody(){
//向element中添加一个div
this.element.insertAdjacentHTML("beforeend","<div></div>")
}
//添加一个蛇身体移动的方法
moveBody(){
//将后边的身体设置为前边身体的位置
//遍历获取所有的身体
for(let i = this.bodies.length-1;i>0;i--){
//获取前边身体的位置
let X= (this.bodies[i-1] as HTMLElement).offsetLeft;
let Y= (this.bodies[i-1] as HTMLElement).offsetTop;
//将值设置到当前身体上
(this.bodies[i] as HTMLElement).style.left = X + 'px';
(this.bodies[i] as HTMLElement).style.top = Y + 'px';
}
}
//检查蛇头是否撞到身体上
checkHeadBody(){
//获取所有身体,检查是否和蛇头的坐标发生重叠
for(let i = 1 ;i<this.bodies.length;i++){
let bd = this.bodies[i] as HTMLElement
if(this.X ===bd.offsetLeft && this.Y === bd.offsetTop){
//进入判断说明蛇头撞到了身体,游戏结束
throw new Error('撞到了自己!!!')
}
}
}
}
export default Snake
//控制类
import Food from './Food'
import ScorePanel from './ScorePanel'
import Snake from './Snake'
//游戏控制器,控制其他的所有类
class GameControl{
//定义一个属性
//蛇
snake :Snake
//食物
food:Food
//记分牌
scorePanel:ScorePanel
//创建一个属性来存储蛇的移动方向(也就是按键的方向)
direction :string = ''
//创建一个属性用来记录游戏是否结束
isLive = true
constructor(){
this.snake = new Snake()
this.food = new Food()
this.scorePanel = new ScorePanel()
this.init()
}
//游戏的初始化方法,调用后游戏即开始
init(){
//绑定键盘按键按下的事件
document.addEventListener('keydown',this.keydownHandler.bind(this))
//调用run方法,使蛇移动
this.run()
}
//创建一个键盘按下的响应函数
keydownHandler(event:KeyboardEvent){
//需要检查event.key的值是否合法(用户是否按了正确的按键)
//修改direction属性
this.direction = event.key
}
//创建一个控制蛇移动的方法
run (){
/*
* 根据方向(this.direction)来使蛇的位置改变
* 向上 top减少
* 向下top增加
* 向左 left减少
* 向右left 增加
*/
//获取蛇现在的坐标
let X = this.snake.X
let Y = this.snake.Y
//根据按键方向
switch(this.direction){
case "ArrowUp":
case 'Up':
//向上移动top减少
Y -=10;
break;
case 'ArrowDown':
case 'Down':
//向下移动top增加
Y+=10;
break;
case 'ArrowLeft':
case 'Left':
//向左移动left 减少
X-=10;
break;
case 'ArrowRight':
case 'Right':
//向右移动left 增加
X+=10;
break;
}
//检查蛇是否吃到食物
this.checkEat(X,Y)
//修改蛇的X、Y方向
try {
this.snake.X = X;
this.snake.Y = Y ;
} catch (e:any) {
//进入到catch,说明出现了异常,游戏结束,弹出一个提示信息
alert(e.message + 'GAME OVER')
//将isLive设置为false
this.isLive = false;
}
//开启一个定时调用
this.isLive && setTimeout(this.run.bind(this),300 -(this.scorePanel.level-1)*30)
}
//定义一个方法,用来检查蛇吃到食物
checkEat(X:number,Y:number){
if (X===this.food.X && Y===this.food.Y) {
//食物对的位置要进行重置
this.food.change()
//分数增加
this.scorePanel.addScore()
//蛇要增加一节
this.snake.addBody()
}
}
}
export default GameControl
准备4:创建index.ts文件,执行游戏
import './style/index.less'
import GameControl from './moduls/GameControl'
new GameControl()