基于TypeScript的贪食蛇项目

基于TypeScript的贪食蛇项目

介绍

基于TypeScript的贪食蛇项目 此项目学习于-哔哩哔哩尚硅谷TypeScript教程(李立超老师TS新课)

软件架构

TypeScript
node版本 v18.17.0

安装教程

  1. npm install – 安装依赖
  2. npm run build – 打包
  3. npm run start – 运行

项目演示截图

项目演示

项目源码

https://gitee.com/shen_jinlong/snake-ts.git

目录结构

在这里插入图片描述

代码

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="screen">
            <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>
css代码 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;

    // 操作屏幕
    #screen {
        // 开启相对定位
        position: relative;
        margin-top: 20px;
        width: 304px;
        height: 304px;
        border: 2px solid black;

        #snake {
            &>div {
                // 开启绝对定位
                position: absolute;
                width: 10px;
                height: 10px;
                border: 1px solid @bg-color;
                background-color: #000;
            }
        }

        // 设置食物
        &>#food {
            position: absolute;
            left: 40px;
            top: 100px;
            display: flex;
            flex-flow: row wrap;
            justify-content: space-between;
            align-content: space-between;
            width: 10px;
            height: 10px;

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

    // 记分牌
    #score-panel {
        width: 300px;
        display: flex;
        // 设置主轴的对齐方式
        justify-content: space-around;
    }
}
食物类
/** 定义食物类 */
class Food {

    /* 属性 */
    element: HTMLElement;   // 食物元素实例

    constructor() {
        // 获取dom元素
        this.element = document.getElementById('food')!;
    }

    /* 方法 */
    /** 定义一个获取食物X轴坐标的方法 */
    get X() {
        return this.element.offsetLeft;
    }
    /** 定义一个获取食物Y轴坐标的方法 */
    get Y() {
        return this.element.offsetTop;
    }

    /** 生成随机位置 */
    change() {
        /**
         * 生成随机位置
         * 1. 取值范围(0~290)&& 10的倍数(配合蛇的运动)
         */
        let elementLeft = Math.round(Math.random() * 29) * 10;
        let elementTop = Math.round(Math.random() * 29) * 10;
        // Math.floor(Math.random() * 30) * 10; // 向下取整
        this.element.style.left = elementLeft + 'px';
        this.element.style.top = elementTop + 'px';
    }
}

export default Food;
记分牌类
/** 记分牌类 */
class ScorePanel {

    /* 属性 */
    score = 0;
    level = 1;
    scoreElement: HTMLElement;
    levelElement: HTMLElement;
    maxLevel: number; // 最大等级
    upScore: number; // 升级需要的分数

    constructor(maxLevel: number = 10, upScore: number = 10) {
        this.scoreElement = document.getElementById('score')!;
        this.levelElement = document.getElementById('level')!;
        this.maxLevel = maxLevel;
        this.upScore = upScore;
    }

    /** 方法 */
    /** 得分 */
    addScore() {
        this.scoreElement.innerHTML = ++this.score + '';
        if (this.score % this.upScore === 0) {
            this.upLevel();
            // this.upScore = this.upScore * 2;
        }
    }

    /** 升级 */
    upLevel() {
        if (this.level < this.maxLevel) {
            this.levelElement.innerHTML = ++this.level + '';
        }
    }

}

export default ScorePanel;
蛇类
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');
    }

    /** 获取蛇头坐标 */
    get X() {
        return this.head.offsetLeft;
    }

    /** 获取蛇的轴坐标 */
    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) {
            value += 10;
            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) {
            value += 10;
            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() {
        /**
         * 后一节的身体等于前一节的身体位置
         * eg:
         * 第四节 = 第三节位置
         * 第三节 = 第二节位置
         * 第二节 = 第一节位置
         * 第一节自己走了
         */
        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 Snake from "./Snake";
import Food from "./Food";
import ScorePanel from "./ScorePanel";

/** 游戏控制器,控制其他所有类 */
class GameControl {
    // 定义三个属性
    snake: Snake;               // 蛇
    food: Food;                 // 食物
    scorelPanel: ScorePanel;    // 记分牌
    direction: string = 'ArrowRight';     // 蛇的移动方向
    isLive: boolean = true; // 记录游戏是否结束

    constructor() {
        this.snake = new Snake();
        this.food = new Food();
        this.scorelPanel = new ScorePanel();
    }

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

    /** 创建一个键盘按下的响应函数 */
    keydownHandler = (event: KeyboardEvent) => {
        this.direction = event.key;
    }
    // keydownHandler(event: KeyboardEvent) {
    //     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":
                // 向上移动top减少
                Y -= 10;
                break;
            case "ArrowDown":
                // 向下移动top增加
                Y += 10;
                break;
            case "ArrowLeft":
                // 向左移动left减少
                X -= 10;
                break;
            case "ArrowRight":
                // 向右移动left增加
                X += 10;
                break;

        }

        // 吃到食物
        this.checkEat(this.snake.X, this.snake.Y);

        // 修改蛇的位置
        try {
            this.snake.X = X;
            this.snake.Y = Y;
        } catch (e: any) {
            // 进入到catch,出现异常,游戏结束,弹出一个提示
            alert(e.message);
            this.isLive = false;
        }
        // 开启定时调用
        this.isLive && setTimeout(this.run.bind(this), 300 - (this.scorelPanel.level - 1) * 30);
        // setTimeout(() => {
        //     this.run();
        // }, 300 - (this.scorelPanel.level - 1) * 30);

    }

    // 检测蛇是否吃到食物
    checkEat(X: number, Y: number) {
        if (X === this.food.X && Y === this.food.Y) {
            // 更新食物位置
            this.food.change();
            // 分数增加
            this.scorelPanel.addScore();
            // 增长蛇
            this.snake.addBody();
        }
    }

}

export default GameControl;
  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值