TypeScript realizes the snake game

TypeScript implements snake eating

introduce

In the previous article, we just learned the basic knowledge of TS, so let's test the learning results. Let's use TypeScript to complete a small snake project. The corresponding files are as follows:

insert image description here

Project build

The corresponding configuration file is as follows (see my previous article for a more detailed introduction to the settings of this configuration file. This part of the configuration file introduces a css plug-in on the basis of the previous one to realize the processing of css.) :

package.json

{
    
    
  "name": "chapter02",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    
    
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "start": "webpack serve --open"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    
    
    "@babel/core": "^7.22.9",
    "@babel/preset-env": "^7.22.9",
    "babel-loader": "^9.1.3",
    "clean-webpack-plugin": "^4.0.0",
    "core-js": "^3.32.0",
    "css-loader": "^6.8.1",
    "html-webpack-plugin": "^5.5.3",
    "less-loader": "^11.1.3",
    "postcss": "^8.4.27",
    "postcss-loader": "^7.3.3",
    "postcss-preset-env": "^9.1.0",
    "style-loader": "^3.3.3",
    "ts-loader": "^9.4.4",
    "typescript": "^5.1.6",
    "webpack": "^5.88.2",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^4.15.1"
  }
}

tsconfig.json

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

webpack.config.js

const path=require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const {
    
     CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports={
    
    
    mode:'development',
    entry:"./src/index.ts",
    output:{
    
    
        path:path.resolve(__dirname,'dist'),
        filename:"bundle.js",
        environment:{
    
    
            arrowFunction:false,
            const:false
        }
    },
    module:{
    
    
        rules:[
            {
    
    
                test:/\.ts$/,
                use:[
                    {
    
    
                        loader:"babel-loader",
                        options:{
    
    
                            presets:[
                                [
                                    "@babel/preset-env",
                                    {
    
    
                                        targets:{
    
    
                                            "chrome":"88"
                                        },
                                        "corejs":"3",
                                        "useBuiltIns":"usage"
                                    }
                                ]
                            ]
                        }

                    },
                    'ts-loader',
            ],
                exclude:/node-modules/
            },
            {
    
    
                test:/\.less$/,
                use:[
                    "style-loader",
                    "css-loader",
                    {
    
    
                        loader:"postcss-loader",
                        options:{
    
    
                            postcssOptions:{
    
    
                                plugins:[
                                    [
                                        "postcss-preset-env",
                                        {
    
    
                                            browsers:'last 2 versions'
                                        }
                                    ]
                                ]
                            }
                        }

                    },
                    "less-loader"
                ]
            }
        ]
    },
    plugins:[
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
    
    
            // title:"这是一个自定义的title"
            template:"./src/index.html"
        }),
    ],
    resolve:{
    
    
        extensions:[".ts",'.js']
    }
} 

Then download the dependencies, the relevant commands are as follows: npm i

Page and Style Design

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 id="main">
        <!-- 设置游戏舞台 -->
        <div id="stage">
            <!-- 设置蛇 -->
            <div id="snake">
                <div></div>
            </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>
            <div>
                level:<span id="level">1</span>
            </div>

        </div>
    </div>
</body>
</html>

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: 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:#000;
                border: 1px solid @bg-color;
                position: absolute;
            }
           
        }
        // 设置食物
        #food{
            width: 10px;
            height: 10px;
            position: absolute;
            left: 40px;
            top:100px;
            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;
    }
}

Design of classes and corresponding methods

Food.ts

// 定义食物类
class Food{
    
    
    element:HTMLElement;
    constructor(){
    
    
        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;

        this.element.style.left=left+'px';
        this.element.style.top=top+'px';
    }
}
export default Food;

GameControl.ts

import Snake from "./Snake";
import Food from "./Food";
import ScorePanel from "./ScorePanel";
// 游戏控制器
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(10,2);
        this.init();

    }
    // 游戏初始化方法,调用后游戏即开始
    init(){
    
    
        // 绑定键盘按键按下的事件
        document.addEventListener('keydown',this.keydownHandler.bind(this));
        // 调用run方法,使蛇移动
        this.run();


    }
    // 创建一个键盘按下的响应函数
    keydownHandler(event:KeyboardEvent){
    
    
        // 需要检查event.key的值是否合法(用户是否按了正确的按键)

        // 修改directipn属性
        this.direction=event.key;

    }
    // 创建一个控制蛇移动的方法
    run(){
    
    
        // 获取蛇现在的坐标
        let X=this.snake.X;
        let Y=this.snake.Y;
        // 根据按键方向来计算x和y值
        switch(this.direction){
    
    
            case "ArrowUp":
            case "Up":
                Y -=10;
                break;
            case "ArrowDown":
            case "Down":
                Y +=10;
                break;
            case "ArrowLeft":
            case "Left":
                X -=10;
                break;
            case "ArrowRight":
            case "Right":
                X +=10;
                break;
        }
        // 检查蛇是否吃到了食物
        this.checkEat(X,Y);
        // 修改蛇的X和Y值
        try{
    
    
            this.snake.X=X;
            this.snake.Y=Y; 
        }catch(e){
    
    
            alert((e as Error).message+'GAME OVER!');
            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;

ScorePanel.ts


// 定义表示计分盘的类
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.leveUp();
        }
    }
    // 提升等级方法
    leveUp(){
    
    
        if(this.level<this.maxLevel){
    
    
            this.levelEle.innerHTML=++this.level+'';
        }
    }
}

export default ScorePanel;

Snake.ts

class Snake{
    
    
    head:HTMLElement;
    bodies:HTMLCollection;
    // 获取蛇的容器
    element:HTMLElement;
    constructor(){
    
    
        this.element=document.getElementById("snake")!;
        this.head=document.querySelector('#snake>div') as HTMLElement;
        this.bodies=document.getElementById("snake")!.getElementsByTagName("div");
    }
    // 获取蛇的坐标
    get X(){
    
    
        return this.head.offsetLeft;
    }
    // 获取蛇的Y轴坐标
    get Y(){
    
    
        return this.head.offsetTop;
    }
    // 设置蛇头的坐标
    set X(value:number){
    
    
        // 如果新值和旧值相同,则直接返回不再修改。
        if(this.X===value){
    
    
            return;
        }
        if(value<0 || value>290){
    
    
            // 说明蛇撞墙
            throw new Error("蛇撞墙了!")
        }
        // 蛇在左右移动,蛇在向左移动时,不能向右调头,反之亦然
        if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft===value){
    
    
            // 如果发生了掉头,让蛇向反方向继续移动
            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;
        }
        if(value<0 || value>290){
    
    
            // 说明蛇撞墙
            throw new Error("蛇撞墙了!")
        }
        if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop===value){
    
    
            // 如果发生了掉头,让蛇向反方向继续移动
            if(value>this.Y){
    
    
                value=this.Y-10;
            }else{
    
    
                value=this.Y+10;
            }

        }
        this.movebody();
        this.head.style.top=value+'px';
        this.checkHeadBody();
    }
    // 蛇添加身体的方法
    addBody(){
    
    
        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 db=this.bodies[i] as HTMLElement;
            if(this.X===db.offsetLeft && this.Y===db.offsetTop){
    
    
                throw new Error("撞到自己了```");

            }

        }
    }

}
export default Snake;

index.ts

import "./style/index.less";
import GameControl from "./moduls/GameControl";
new GameControl();

achieve effect

insert image description here
insert image description here
insert image description here
insert image description here

Guess you like

Origin blog.csdn.net/weixin_51735748/article/details/132100915