当前位置:   article > 正文

使用typescript完成简单的贪吃蛇功能_李立超ts贪吃蛇

李立超ts贪吃蛇

参考资料

开发工具

  • vscode
  • 推荐插件 Live Server 可以监控文件的变化,并进行刷新

界面效果

在这里插入图片描述

界面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>
  <link rel="stylesheet" href="css/index.css">
</head>
<body>
  <div id="main">
    <!-- 游戏的主要界面 -->
    <div id="stage">
      <!-- 设置蛇 -->
      <div id="snake">
        <!-- 表示蛇的各部分 -->
        <div></div>
      </div>
      <!--设置食物,4个div表示四个角-->
      <div id="food">
        <div></div>
        <div></div>
        <div></div>
        <div></div>
      </div>
    </div>

    <!-- 设置游戏的积分牌 -->
    <div id="score-panel">
      <!-- 积分 -->
      <div>SCORE:<span id="score">0</span></div>
      <!-- 等级,默认10, 等级高速度快 -->
      <div>LEVEL:<span id="level">1</span></div>
    </div>

  </div>
  <script src="TS/index.js"></script>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

index.css

  • 设置页面的样式,在模块化开发中可以使用less
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
} /* 清楚默认样式 */

body {
  font: bold 20px "Courier"
}

#main {
  width: 360px;
  height: 420px;
  background-color: #b7d4a8;
  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  #b7d4a8;
  position: absolute;
}

#food {
  width: 10px;
  height: 10px;
  position: absolute;
  left: 40px;
  top: 100px;
  display: flex;
  flex-flow: row wrap; /* 设置主轴为横轴 超出部分换行 */
  /* 设置主轴和侧轴的空白分配到元素之间 */
  justify-content: space-between;
  align-items: center;
}

#food > div {
  width: 4px;
  height: 4px;
  background-color: black;
  transform: rotate(45deg);
}

#score-panel {
  width: 300px;
  display: flex;
  justify-content: space-between;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

index.ts

  • 在模块化开发中可以把每个类单独放在一个文件中
  • 在这里我使用一个文件,可以使用 命令进行便于 tsc -t ES6 -w index.ts 表示使用ES6进行编译,并且对文件进行监控
class Food {
  element : HTMLElement
  width: number
  size: number 
  constructor (id:string, width = 29, size = 10 ) {
    this.element=document.getElementById(id)
    this.width = width
    this.size = size
  }
  get X(){
    return this.element.offsetLeft
  }
  get Y() {
    return this.element.offsetTop
  }
  change() {
    // 生成随机的位置 范围在[0,width*10] 但要求为整数 且是10的倍数
    this.element.style.left = Math.round(Math.random()*this.width)*this.size + 'px'
    this.element.style.top = Math.round(Math.random()*this.width)*this.size + 'px'
  } // 修改食物的位置
}

class ScorePanel {
  score = 0 
  level = 1
  scoreEle : HTMLElement
  levelEle : HTMLElement   
  static maxLevel = 10
  static upScore = 1 // 设置升1级所需要的分数
  constructor (scoreId:string, levelId:string) {
    this.scoreEle = document.getElementById(scoreId)
    this.levelEle = document.getElementById(levelId)
    this.scoreEle.innerText = this.score + ''
    this.levelEle.innerText = this.level + ''
  }
  addScore (d = 1) {
    this.score += 1
    this.scoreEle.innerText = this.score + ''
    if (this.score % ScorePanel.upScore === 0) this.addLevel()
  }
  addLevel () {
    if (this.level < ScorePanel.maxLevel) {
      this.levelEle.innerText = ++this.level + ''
    }
  }
}


class Snake {
  head: HTMLElement // 表示蛇头的元素
  bodies: HTMLCollection // 蛇的身体包含蛇头
  element: HTMLElement // 获取蛇的容器 
  range: number // 蛇头所能在的范围
  constructor(id, range = 290) {
    this.element = document.getElementById(id)
    this.element.innerHTML = '<div></div>' // 初始化
    this.bodies = this.element.getElementsByTagName('div')
    this.head = this.bodies[0] as HTMLElement
    this.range = range
  }
  get X() {
    return this.head.offsetLeft
  } // 获取蛇头x坐标
  get Y() {
    return this.head.offsetTop
  } // 获取蛇头y坐标

  set X(value:number) {
    if (this.X === value) return
    this.judge(value)
    this.move()
    this.head.style.left = value + 'px'
  }
  set Y(value:number) {
    if (this.Y === value) return
    this.judge(value)
    this.move()
    this.head.style.top = value + 'px'
    
  }
  addBody () {
    this.element.insertAdjacentHTML("beforeend","<div></div>")
  }
  move() {
    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'
    }
  }
  judge(value:number) {
    if ( value < 0 || value > this.range) {
      throw new Error('蛇撞墙了')
    }
    for(let i= this.bodies.length-1;i>0;--i) {
      let x = (this.bodies[i] as HTMLElement).offsetLeft
      let y = (this.bodies[i] as HTMLElement).offsetTop;
      if (x===this.X && y === this.Y) throw new Error('不能吃自己')
    }
  }
}

class GameControl {
  snake:Snake
  scorePanel: ScorePanel
  food: Food
  direction:string // 蛇头移动方向
  isLive = true // 标记当前蛇仍然存在=
  static directionKey = new Set(['w','a','s','d',' '])  // 蛇头移动方向可能取值
  static speed = 200 // 设置初始速度
  constructor() {
    this.scorePanel = new ScorePanel('score','level')
    this.food = new Food("food")
    this.snake = new Snake("snake", this.food.size * this.food.width)
    this.direction = ' '
    this.init()
  }
  init() {
    this.food.change()
    console.log(this.food.X,this.food.Y)
    document.addEventListener('keydown',this.keydownHandler.bind(this))
    this.run()
  }// 游戏初始化
  keydownHandler(event:KeyboardEvent) {
    let key = event.key
    if (GameControl.directionKey.has(key)){
      // 不能向反方向移动
      if (this.direction === 'w' && key === 's') return
      if (this.direction === 'a' && key === 'd') return
      if (this.direction === 's' && key === 'w') return
      if (this.direction === 'd' && key === 'a') return
      this.direction = event.key
    }
  }//创建一个键盘按下的响应的函数
  run(){
    let x = this.snake.X, y = this.snake.Y
    switch(this.direction) {
      case 'w': 
        y -= 10
        break // 向上移动
      case 'a':
        x -= 10
        break // 向左移动
      case 's':
        y += 10
        break // 向下移动
      case 'd':
        x += 10
        break // 向右移动
      case ' ':
        break // 空格表示暂停或者继续
    }
    try {
      this.checkEat(x, y)
      this.snake.X = x
      this.snake.Y = y
    }catch (err){
      this.isLive = false
      if (window.confirm(err.message + ' 是否继续游戏')) {
        game = new GameControl()
      }
      return
    }
  
    this.isLive && setTimeout(this.run.bind(this),GameControl.speed / this.scorePanel.level) // 开启定时调用
  }// 蛇移动的方法
  checkEat (x:number, y:number) {
    if  (x === this.food.X && y === this.food.Y) {
      this.food.change()
      this.scorePanel.addScore()
      this.snake.addBody()
    }
  } // 检查蛇是否迟到食物
} // 游戏控制器

let game: GameControl = new GameControl()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177

附录

  • 老师的webpack.config.js 配置文件,其他一部分是我添加
const path = require('path')
const HTMLWebpackPlugin = require('html-webpack-plugin')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')

module.exports = {
  context: path.resolve(__dirname, ''), //  基础目录,绝对路径,用于从配置中解析入口起点 (entry point) 和加载器 (loader)。
  entry: "./src/index.ts", // 指定入口文件
  output: {
    path: path.resolve(__dirname,'dist') , // 拼接目录
    filename: "bundle.js" ,// 打包后文件名
    environment: {
      arrowFunction: false
    } // 输出文件的语法环境
  }, // 指定打包文件所在目录
  module: {
    rules:[
      { 
        test: /\.tsx?$/, // 指定规则生效的文件
        use: [
          {
            loader: "babel-loader",
            options: {
              persets: [
                "@babel/preset-env", // 指定环境的插件
                {
                  targets: {
                    "chrome": "88",
                    "ie": "10"
                  }, // 要兼容的浏览器
                  "corejs": "3",
                  "useBuiltIns" : "usage", // 表示按需加载所需要的资源
                }
              ] // 设置预定义的环境 
            } // 对应加载器的配置项,每个加载器都有自己的配置项
          },
          "ts-loader"
        ] ,//  数组的方式使用多个加载器
        exclude: "/node-modules/" // 要排除的文件夹
      }
    ] // 指定要加载的规则
  }, // 指定webpack打包时要使用的模块
  resolve: {
    extensions: ["", ".webpack.js", ".web.js", ".ts", ".tsx", ".js"],
    alias: {
      '@': resolve('src')
    }, // 配置别名把原来导入路径映射成一个新的导入路径
  }, // 配置 webpack 如何寻找模块对应的文件
  plugins: [
    new CleanWebpackPlugin(),
    new HTMLWebpackPlugin({
      title : 'ts项目构建', // index.html 标题
      template: "", // 定义模板位置 
    })
  ],
  devServer: {
    
  } // 提供虚拟服务器,让我们进行开发和调试。需要安装对应插件 webpack-dev-server
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 部分package.json 所需要安装的依赖
{
  "script":{
    "build": "webpack",
    "start": "webpack serve --open chrome.exe"
  },
  "devDependencies": {
    "clean-webpack-plugin" : "", //  webpack中的清除插件,每次构建都会先清除目录
    "html-webpack-plugin":"", //  webpack中html插件,用来自动创建html文件
    "ts-loader":"", // ts加载器,用于在webpack中编译ts文件
    "typescript":"", //  ts编译器
    "webpack": "", //  构建工具webpack
    "webpack-cli": "", // webpack的命令行工具
    "webpack-dev-server": "",
    "@babel/core": "",
    "@babel/preset-enb": "",
    "babel-loader":"",
    "core-js": ""
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • webpack-dev-server : 拥有实时重载能力的静态资源服务器
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/1002554
推荐阅读
相关标签
  

闽ICP备14008679号