ts项目 --- 贪吃蛇

ts项目 — 贪吃蛇

  • querySelectorAll获取的元素是死的,页面变化需要重新获取
  • getElementsByTagName获取的元素是活的,页面变化不需要重新获取

1.项目初始化

  1. package.json

设置好这个文件终端输入npm i

{
  "name": "part2",
  "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"
  }
}
  1. tsconfig.json

    {
    “compilerOptions”: {
    “module”: “ES2015”,
    “target”: “ES2015”,
    “strict”: true,
    “noEmitOnError”: true
    }
    }

  2. 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']
    },
    mode:'development'
    

    };

  3. 新建src文件夹,创建index.tsindex.html

  4. src文件夹内,新增modules文件夹和style文件夹

2.编写结构和样式

在这里插入图片描述

src/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>

src/style/index.less

//设置颜色变量
@bg-color:#b7d4a8;

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

#main {
    display: flex;
    flex-flow: column;
    align-items: center;
    justify-content: space-around;
    margin: 0 auto;
    width: 360px;
    height: 420px;
    border: 10px solid #000;
    background-color: @bg-color;
    border-radius: 30px;

    #stage{
       position: relative;
        width: 304px;
        height: 304px;
        border: 2px solid #000;

        #snake{
            &>div{
                position: absolute;
                width: 10px;
                height: 10px;
                background-color: #000;
                border: 1px solid @bg-color;
                

            }

        }
        &>#food{
            display: flex;
            flex-flow: row wrap;
            justify-content: space-between;
            align-content: space-between;
            position: absolute;
            top: 10px;
            left: 10px;
            width: 10px;
            height: 10px;
            
            &>div{
                width: 4px;
                height: 4px;
                background: #000;

                transform: rotate(45deg);
            }
        }
    }

    #score-panel{
        width: 300px;
        display: flex;
        align-items: center;
        justify-content: space-between;
    }
}
body {
    font:bold 20px "Courier";
}

3.模块化

src/modules/Food.ts

class Food{
    element:HTMLElement;
    constructor(){
        //this.element=document.getElementById('food') as HTMLElement
        this.element=document.getElementById('food')!;
        
    }
    get X() {
        return this.element.offsetLeft
    }
    get Y() {
        return this.element.offsetTop
    }
    change() {
        this.element.style.left=Math.ceil(Math.random()*29)*10+'px';
        this.element.style.top=Math.ceil(Math.random()*29)*10+'px';
    }
}

export default Food;

src/modules/ScorePanel.ts

class ScorePanel{
    score:number=0;
    level:number=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.score++;
        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;

src/modules/Snake.ts

class Snake{
    head:HTMLElement;
    bodies:HTMLCollection;
    element:HTMLElement;
    constructor(){
        this.element = document.getElementById('snake')!;
        this.head = document.querySelector('#snake>div')!;
        this.bodies=this.element.getElementsByTagName('div')!;

    }

    
    public get X(){
        return this.head.offsetLeft;
    }
    
    public 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 -= 20
                
            }else{
                value += 20
            }
        }

        //移动身体
        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 -= 20
            }else{
                value += 20
            }
        }

        this.moveBody();
        this.head.style.top = value + 'px';

        this.checkHeadBody();
    }  
    
    addBody(){
        this.element.insertAdjacentElement('beforeend',document.createElement('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++) {
            if(this.X === (this.bodies[i] as HTMLElement).offsetLeft &&this.Y === (this.bodies[i] as HTMLElement).offsetTop){
                throw new Error('撞到自己了!')
            }
            
        }
    }
}

export default Snake

src/modules/GameControl.ts

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();
        this.run();
    }

    init(){
        //绑定键盘事件
        document.addEventListener('keydown',this.keydownHandler.bind(this))
        
        
    }

    //键盘响应函数
    //key:ArrowUp ArrowDown ArrowLeft ArrowRight
    // IE: Up Down Left Right
    keydownHandler(event:KeyboardEvent){
        const keyhefa = ['ArrowUp','ArrowDown','ArrowLeft','ArrowRight','Up','Down','Left','Right',' ']
        
        if (keyhefa.indexOf(event.key)>-1) {
            this.direction = event.key;
        }                   
    }

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

        //修改位置变量
        switch(this.direction){
            case 'ArrowUp':
            case 'UP':
                Y -= 10;
                break;
            case 'ArrowDown':
            case 'Down':
                Y +=10;
                break;
            case 'ArrowRight':
            case 'Right':
                X += 10;
                break;
            case 'ArrowLeft':
            case 'Left':
                X -= 10;
                break;
            case ' ':
                break;
            default:
                break;
        }
        //检查移动后会不会吃到食物
        this.eatHandler(X,Y)
        //移动蛇头和身体
        try{
            this.snake.X = X;
            this.snake.Y = Y;
        }catch (e:any){
            alert(e.message)
            this.isLive = false;
        }
        

        this.isLive && setTimeout(this.run.bind(this), 300-(this.scorePanel.level-1)*30)

    }

    //检查蛇是否吃到食物
    eatHandler(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

src/index.ts

import './style/index.less'

import GameControl from './modules/GameControl'

new GameControl();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值