如何使用typeScript实现贪吃蛇游戏?

1.配置文件

image-20221212153711209

配置文件写过一次之后,可以复制粘贴使用,修改部分细节就可以了。
package.json

{
    
    
  "name": "snake",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    
    
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "start": "webpack serve --open chrome.exe"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    
    
    "@babel/core": "^7.12.9",
    "@babel/preset-env": "^7.12.7",
    "babel-loader": "^8.2.2",
    "clean-webpack-plugin": "^3.0.0",
    "core-js": "^3.8.0",
    "css-loader": "^5.0.1",
    "html-webpack-plugin": "^4.5.0",
    "less": "^3.12.2",
    "less-loader": "^7.1.0",
    "postcss": "^8.1.13",
    "postcss-loader": "^4.1.0",
    "postcss-preset-env": "^6.7.0",
    "style-loader": "^2.0.0",
    "ts-loader": "^8.0.11",
    "typescript": "^4.1.2",
    "webpack": "^5.6.0",
    "webpack-cli": "^4.2.0",
    "webpack-dev-server": "^3.11.0"
  }
}

tsconfig.json

{
    
    
  "compilerOptions": {
    
    
    "module": "ES2015",
    "target": "ES2015",
    "strict": true,
    "noEmitOnError": true
  }
}

webpack.config.js

// 引入一个包
const path = require('path');
// 引入html插件
const HTMLWebpackPlugin = require('html-webpack-plugin');
// 引入clean插件
const {
    
     CleanWebpackPlugin } = require('clean-webpack-plugin');

// webpack中的所有的配置信息都应该写在module.exports中
module.exports = {
    
    
    // 指定入口文件
    entry: "./src/index.ts",

    // 指定打包文件所在目录
    output: {
    
    
        // 指定打包文件的目录
        path: path.resolve(__dirname, 'dist'),
        // 打包后文件的文件
        filename: "bundle.js",

        // 告诉webpack不使用箭头
        environment:{
    
    
            arrowFunction: false,
            const: false
        }
    },
    // 指定webpack打包时要使用模块
    module: {
    
    
        // 指定要加载的规则
        rules: [
            {
    
    
                // test指定的是规则生效的文件
                test: /\.ts$/,
                // 要使用的loader
                use: [
                     // 配置babel
                     {
    
    
                         // 指定加载器
                         loader:"babel-loader",
                         // 设置babel
                         options: {
    
    
                             // 设置预定义的环境
                             presets:[
                                 [
                                     // 指定环境的插件
                                     "@babel/preset-env",
                                     // 配置信息
                                     {
    
    
                                         // 要兼容的目标浏览器
                                         targets:{
    
    
                                             "chrome":"58",
                                             "ie":"11"
                                         },
                                         // 指定corejs的版本
                                         "corejs":"3",
                                         // 使用corejs的方式 "usage" 表示按需加载
                                         "useBuiltIns":"usage"
                                     }
                                 ]
                             ]
                         }
                     },
                    'ts-loader'
                ],
                // 要排除的文件
                exclude: /node-modules/
            },

            // 设置less文件的处理
            {
    
    
                test: /\.less$/,
                use:[
                    "style-loader",
                    "css-loader",

                    // 引入postcss
                    {
    
    
                        loader: "postcss-loader",
                        options: {
    
    
                            postcssOptions:{
    
    
                                plugins:[
                                    [
                                        "postcss-preset-env",
                                        {
    
    
                                            browsers: 'last 2 versions'
                                        }
                                    ]
                                ]
                            }
                        }
                    },
                    "less-loader"
                ]
            }
        ]
    },

    // 配置Webpack插件
    plugins: [
        new CleanWebpackPlugin(),
        new HTMLWebpackPlugin({
    
    
            // title: "这是一个自定义的title"
            template: "./src/index.html"
        }),
    ],

    // 用来设置引用模块
    resolve: {
    
    
        extensions: ['.ts', '.js']
    }

};

然后使用命令:npm install 下载依赖

然后创建 src 文件夹 创建index.html文件和index.ts文件

然后使用命令:npm run build 打包

下载样式解析器:npm i -D less less-loader css-loader style-loader

下载postcss做样式兼容:npm i -D postcss postcss-loader postcss-preset-env

在webpack.config.js做如下配置:

            // 设置less文件的处理
            {
    
    
                test: /\.less$/,
                use:[
                    "style-loader",
                    "css-loader",

                    // 引入postcss
                    {
    
    
                        loader: "postcss-loader",
                        options: {
    
    
                            postcssOptions:{
    
    
                                plugins:[
                                    [
                                        "postcss-preset-env",
                                        {
    
    
                                            browsers: 'last 2 versions'
                                        }
                                    ]
                                ]
                            }
                        }
2.搭建基础样式和结构

image-20221212154053119

src/index.html

<body>
    <div id="main">
        <!-- 游戏舞台 -->
        <div id="stage">
            <!-- 设置蛇 -->
            <div id="snake">
                <!-- 蛇体 -->
                <div></div>
                <!-- 食物 -->
                <div id="food">
                    <div></div>
                    <div></div>
                    <div></div>
                    <div></div>
                </div>
            </div>
        </div>
        <!-- 设置游戏记分牌 -->
        <div id="score-panel">
            <div>
                SCORE:<span id="score">0</span>
            </div>
            <div id="level">
                LEVEL:<span id="level"></span>1</span>
            </div>
        </div>
    </div>
</body>

src/style/index.less

//设置变量
@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: 15px;
  display: flex;
  // flex-flow: column;
  flex-direction: column;
  align-items: center;
  justify-content: space-around;
}

// 游戏舞台样式
#stage {
    
    
  width: 304px;
  height: 304px;
  border: 2px solid black;
  position: relative;
  // margin: 10px auto;
  // 设置蛇的样式
  #snake {
    
    
    & > div {
    
    
      width: 10px;
      height: 10px;
      background-color: black;
      border: 1px solid @bg-color;
      position: absolute;
    }
  }
  //   设置食物
  #food {
    
    
    width: 10px;
    height: 10px;
    position: absolute;
    left: 50px;
    top: 80px;

    display: flex;
    flex-flow: row wrap;
    justify-content: space-between;
    align-content: space-between;

    & > div {
    
    
      width: 4px;
      height: 4px;
      background-color: red;
      transform: rotate(45deg);
    }
  }
}

//游戏记分牌
#score-panel {
    
    
  width: 300px;
  display: flex;
  flex-direction: row;
  justify-content: space-around;
}

image-20221212154000151

3.实现food类

Food.ts

//定义食物类
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() {
    
    
      //随机生成
      //舞台长度是300,食物的位置最小是0,最大是290
      //蛇移动一次是一格,一格是10,所以要求食物的距离是整10
      let top = Math.round(Math.random() * 29) * 10; //round向上取整,取得到1
      let left = Math.round(Math.random() * 29) * 10;
  
      this.element.style.left = left + "px";
      this.element.style.top = top + "px";
    }
  }

  export default Food;
4.实现scorePanel类

scorePanel.ts

//定义记分牌类
class scorePanel {
    
    
    //分数和等级
    score = 0;
    level = 1;
    //分数和等级所在的元素,在构造函数中进行优化
    scorefile: HTMLElement;
    levelEle: HTMLElement;
  
    //设置一个变量限制等级
    maxLevel: number;
    //设置一个变量表示多少分升一级
    upScore:number;
  
    constructor(maxLevel: number = 10,upScore:number=10) {
    
    
      this.scorefile = document.getElementById("score")!;
      this.levelEle = document.getElementById("level")!;
      this.maxLevel = maxLevel;
      this.upScore=upScore;
    }
  
    //设置一个加分的方法
    addScore() {
    
    
      //使积分自增
      this.score++;
      this.scorefile.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;

index.ts

import "./style/index.less";
import Food from "./moduls/Food";
import scorePanel from "./moduls/ScorePanel";
5.初步完成snake类

Snake.ts

class Snake{
    
    
    //表示蛇头的元素
    head:HTMLElement;
    //表示蛇身体(包括蛇头)
    bodies:HTMLCollection;
    //表示蛇的容器
    element:HTMLElement;

    constructor(){
    
    
        this.element=document.getElementById("#snake")!;
        this.head=document.getElementById("#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){
    
    
        this.head.style.left=value+'px'
    }
    set Y(value:number){
    
    
        this.head.style.top=value+'px';
    }

    //蛇增加身体的方法
    addBody(){
    
    
        //向element中添加一个div
        this.element.insertAdjacentHTML('beforeend','<div></div>');
    }

}
export default Snake;
6.键盘事件GameControl
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));
    this.run();
  }

  //创建一个键盘按下的响应函数
  keydownHandler(event: KeyboardEvent) {
    
    
    //需要验证event.key是否合法,用户是否按下了正确的按键
    //修改direction的属性
    this.direction = event.key;
    console.log(event.key);
  }

  //获取蛇现在的坐标
  run() {
    
    
    let X = this.snake.X;
    let Y = this.snake.Y;

    switch (this.direction) {
    
    
      case "ArrowUp":
        Y -= 10;
        break;
      case "ArrowDown":
        Y += 10;
        break;
      case "ArrowLeft":
        X -= 10;
        break;
      case "ArrowRight":
        X += 10;
        break;
    }

    //判断蛇是否吃到食物,调用吃到食物的方法
    this.checkEat(X,Y);

    try {
    
    
      //修改蛇的X和Y
      this.snake.X = X;
      this.snake.Y = Y;
    } catch (error) {
    
    
      alert(error);
      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;

7.完善snake类

1.吃到食物之后,蛇体跟随增加,分数增加,食物变换位置

2.蛇撞墙游戏结束,蛇撞到自己身体,游戏结束

3.蛇在一个方向移动时不能掉头

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;
        }

        //蛇的合法值在0-290
        if(value<0||value>290){
    
    
            throw new Error("蛇撞墙了!!")
        }

        //修改x时,是在修改水平坐标,蛇在左右移动时,如果在左移动就不能向右掉头。反之亦然。
        if(this.bodies[1]&&(this.bodies[1] as HTMLElement).offsetLeft===value){
    
    
            // value大于当前的X,说明往右掉头了
            if(value>this.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;
        }

        //蛇的合法值在0-290
        if(value<0||value>290){
    
    
            throw new Error("蛇撞墙了!!")
        }

        //防止掉头
        if(this.bodies[1]&&(this.bodies[1] as HTMLElement).offsetTop===value){
    
    
            // value大于当前的Y,说明往上掉头了
            if(value>this.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;
            console.log(X,Y);
            
            //将值设置在当前的身体上
            (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;

image-20221215222820423

贪吃蛇项目就到这里啦!如果大家想要项目源文件的话,一键三连,评论区回复“学习”,看到就会发你哦

猜你喜欢

转载自blog.csdn.net/m0_46615524/article/details/128336461