使用TypeScript实现贪吃蛇小游戏(网页版)

news2024/9/20 14:51:44

本项目使用webpack+ts所编写

下边是项目的文件目录

/src下边的index.html页面是入口文件

index.ts是引入所有的ts文件

/modules文件夹是用来存放所有类的

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.ts

import "./style/index.less"

import GameControl from "./modules/GameControl"
new GameControl()

 /style/less

@bg-color: #b7d4a8;

// 清楚默认样式
*{
    padding: 0;
    margin: 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;
    justify-content: space-around;
    align-items: center;

    #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: #000;
                transform: rotate(45deg);

            }
        }
    }
    #score-panel{
        width: 300px;
        display: flex;
        justify-content: space-between;
    }
}

/modules/Food.ts

// 定义一个食物 food类
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(){
        // 生产一个随机的位置
        // 食物最小的位置是0   最大是300-10=290    
        // 蛇移动一次就是一格(10),所以食物的坐标必须是整10

       let top =  Math.round(Math.random()*29)*10     //0-29之间的数,然后是每10倍
       let left =  Math.round(Math.random()*29)*10     //0-29之间的数,然后是每10倍

       this.element.style.top = top +'px'
       this.element.style.left = left +'px'


    }
}
export default Food

/modules/GameControl.ts

// 引入其他的类
import Food from "./Food";
import Snake from "./snake";
import ScorePanel from "./ScorePanel";

// 游戏控制器,控制所有的类
class GameControl{
    // 定义三个属性 
    food:Food;
    snake:Snake;
    scorePanel: ScorePanel;

    // 创建一个属性用来存储蛇的移动方向(也就是按键的方向)
    direction:string = "";

    // 创建一个属性用来记录游戏是否结束
    isLIve = true

    constructor(){
        this.food = new Food()
        this.snake = new Snake()
        this.scorePanel = new ScorePanel()

        this.init()
    }
    // 游戏的初始化方法,调用后游戏即开始
    init(){
        document.addEventListener('keydown',this.keydownHandler.bind(this ))
        // 调用run方法,让蛇移动
        this.run()
    }

    // 创建一个键盘按下的响应函数
    /*
        ArrowUp    Up 
        ArrowDown   Down
        ArrowLeft   Left
        ArrowRight   Right
    */
    keydownHandler(event:KeyboardEvent){
        // 修改direction的属性
        this.direction = event.key
    }

    // 创建一个控制蛇移动的方法
    run(){
        // 获取蛇现在的坐标
        let X = this.snake.X
        let Y = this.snake.Y

        switch(this.direction){
            case "ArrowUp":
            case "Up":
                // 向上移动   top减少
                Y -= 10
                break;
            case "ArrowDown":
            case "Down":
                // 向下移动   top增加
                Y += 10
                break;
            case "ArrowLeft":
            case "Left":
                // 向左移动   left减少
                X -= 10
                break;
            case "ArrowRight":
            case "Right":
                // 向左移动   left减少
                X += 10 
                break;
        }

        // 检查蛇是否吃到了食物
        this.checkEat(X,Y)

        // 修改蛇的值
        try{
            this.snake.X = X
            this.snake.Y = Y 
        }catch (e) {
            console.log(e,'1212')
            // 进入catch,捕获异常,弹出信息
            alert(e + 'GOME OVER')
            // 将isLIve设置为false
            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

/modules/ScorePanel.ts

// 定义计分牌
class ScorePanel{
    // score和level用来记录分数和等级
    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.levelUp()
        }
    }
    // 提升等级的方法
    levelUp(){

        if(this.level < this.maxLevel ){
            this.levelEle.innerHTML = ++ this.level +'';
        }
    }

}

export default ScorePanel

/modules/Snake.ts

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

    constructor(){
        // 使用断言定义head的类型
        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
        }
        // X的值合法范围  0-290
        if(value <0 || value > 290){
            //说明蛇撞墙了
            throw new Error('蛇撞墙了!')

        }

        // 修改X时,是在修改水平坐标,蛇在左右移动,蛇在向左移动时,不能向右掉头,反之亦然
        if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === value){
            // 掉头了,让蛇向反向继续移动
            if(value > this.X){
                //如果新值value大于旧值,则说明向右走,发生掉头,应该继续向左走
                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
        }
        // Y的值合法范围  0-290
        if(value <0 || value > 290){
            //说明蛇撞墙了
            throw new Error('蛇撞墙了!')

        }
        
        // 修改Y时,是在修改垂直坐标,蛇在上下移动,蛇在向上移动时,不能向下掉头,反之亦然
        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 bd = this.bodies[i] as HTMLElement
            if(this.X === bd.offsetLeft && this.Y === bd.offsetTop){
                // 进入判断说明蛇头撞倒了身体,游戏结束
                throw new Error('撞到自己了')
            }

        }
    }
}

export default Snake

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/729745.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VoxaMech 的机甲装备 — NFT 系列

一套新的机甲装备即将诞生&#xff1a;帝国甲虫套装是无与伦比的权威和威望的象征&#xff0c;体现了古代帝国甲虫的雄伟壮观。其华丽的铠甲散发着帝王般的优雅气息&#xff0c;其威严的外观在战场上令人肃然起敬。 每套装备由手臂部件、胸甲、头盔、腿部件和剑组成。每件装备单…

SpringBoot——业务层测试事务回滚

事务回滚 关于事务回滚的概念我们之前在学习数据库的时候已经提到过了&#xff0c;这里我们再次强化一下记忆。所谓的事务回滚就是在执行多条SQL语句的时候&#xff0c;如果其中一条SQL出现了异常导致执行失败&#xff0c;则数据库的状态回滚到执行多条SQL语句之前的状态&…

第六章:YOLO v1网络详解(统一的实时目标检测)

(目标检测篇&#xff09;系列文章目录 第一章:R-CNN网络详解 第二章:Fast R-CNN网络详解 第三章:Faster R-CNN网络详解 第四章:SSD网络详解 第五章:Mask R-CNN网络详解 第六章:YOLO v1网络详解 第七章:YOLO v2网络详解 第八章:YOLO v3网络详解 文章目录 系列文章目录技…

一起学SF框架系列5.7-模块Beans-BeanDefinition定义

在SF下&#xff0c;开发人员用xml或注解模式定义bean&#xff0c;框架把这些定义转化为内部BeanDefinition类&#xff0c;然后通过BeanDefinition类实现Bean的管理&#xff08;包括初始化、依赖注入及生命周期管理&#xff09;&#xff0c;因此了解Bean的定义、解析、使用过程非…

[kafka] windows下安装kafka(含安装包)

[kafka] windows下安装kafka&#xff08;含安装包&#xff09; 目录 前言 一、下载kafka安装包 1&#xff09;下载安装包 2&#xff09;解压安装包 二、运行zookeeper 1.运行zookeeper&#xff08;因为kafka必须要和zookeeper一起运行&#xff09; 三、运行kafka 四、使用fafka…

web3创业有哪些机会及具体案列(二)

目录 1. 去中心化金融&#xff08;DeFi&#xff09;&#xff1a;2. 去中心化身份验证和数字身份&#xff1a;3. 去中心化市场和电子商务&#xff1a;4. 区块链游戏和虚拟资产&#xff1a;5. 数据隐私和安全&#xff1a;6. 去中心化社交媒体&#xff1a;7. 去中心化能源交易&…

Spring容器扩展点在微服务中的使用

(20230306)Spring容器扩展点在微服务中的使用 文章目录 1. Spring扩展点梳理2.Spring扩展点应用场景2.1 整合NacosApplicationListener扩展场景——监听容器中发布的事件Lifecycle扩展场景——管理具有启动、停止生命周期需求的对象 2.2 整合RibbonSmartInitializingSingleton扩…

定时器中断实验(stm32)

目录 TIME的代码time.ctime.h main.c TIME的代码 time.c void TIM3_Int_Init(u16 arr,u16 psc) {TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能//定时器TIM3初始化…

uview u-input 点击清除按钮,数据清空但视图未清空

问题描述&#xff1a; 点击 uview 的 u-input 输入框自带的清除按钮&#xff0c;v-model 绑定的数据清空了&#xff0c;但是输入框内还显示着之前的数据 解决方案&#xff1a; 将 v-model 绑定的值写到 data 初始变量中声明 原始代码&#xff1a; <u-input v-model"…

Spring对事务的支持

目录 事务的传播行为 一、什么是事务的传播行为 二、7种事务传播行为 1. REQUIRED 2. REQUIRES_NEW 3. NESTED 4. 总结 三、事务的传播行为&#xff08;理解记忆&#xff09; 转载&#xff1a;一个99%的人都说不清楚知识点——Spring 事务传播行为 - 哔哩哔哩 事务属性…

2023.7.7HCIA中静态路由

一、题目要求 需求&#xff1a; 1.所有节点的IP地址均属于192.168.1.0 24--子网划分 2.全网可达--静态 3.加快收敛&#xff08;尽量汇总&#xff09; 5.R6存在环回接口--6.6.6.6 24&#xff0c;不能直接写去往6.6.6.0网段的路由--缺省 6.没有环路--NUll 0 7.正常流量走100M&am…

什么是ERC20?

ERC20 是以太坊区块链上最常见的代币标准之一。它是以太坊上智能合约的一种协议&#xff0c;定义了一套规则和接口&#xff0c;使得在以太坊网络上创建和管理代币变得更加简单和标准化。 ERC20 代币标准定义了一组功能和方法&#xff0c;以便代币合约可以与其他合约和钱包进行…

dxSpreadSheetReportDesigner使用笔记

通过该控件达到显示主从表,效果如下图所示. 在界面上放置以下控件 1.新建主从表主表为tab1,从表为tab2,二表通过设置从表的mastersource及MasterFields与从表联动. 2.设置dxspreadsheetreportdesigner与主从表关联 在DataBinding中的datasource关联主从. 在details中关联从表…

UE5、CesiumForUnreal接入XYZ格式地图瓦片如高德地图、OSM、ArcGIS等

文章目录 1.实现目标2.实现过程2.1 XYZ与TMS对比2.1 cesium-native改造2.3 CesiumForUnreal插件改造2.4 XYZ瓦片加载测试3.参考资料1.实现目标 通过改造cesium-native和cesiumforunreal插件,参考tms的栅格地图瓦片加载逻辑,实现在UE5、CesiumForUnreal中接入XYZ格式的地图瓦片…

-Xmx20m -Xms5m

-Xmsjava程序启动时初始堆的大小&#xff0c;默认是物理内存的1/64-Xmxjava程序能获得的最大堆的大小&#xff0c;默认为物理内存的1/4 验证默认值 winr → systeminfo 配置JVM启动参数&#xff1a;-XX:PrintCommandLineFlags-XX:ConcGCThreads3 -XX:G1ConcRefinementThreads…

UEditor v1.4.3.3 .net版本任意文件上传 漏洞复现

为方便您的阅读&#xff0c;可点击下方蓝色字体&#xff0c;进行跳转↓↓↓ 01 漏洞描述02 影响范围03 验证方式04 利用方式05 实战案例06 修复方案 01 漏洞描述 UEditor是一款所见即所得的开源富文本编辑器&#xff0c;具有轻量、可定制、用户体验优秀等特点&#xff0c;被广大…

flutter聊天界面-加号【➕】更多展开相机、相册等操作Panel

flutter聊天界面-加号【➕】更多展开相机、相册等操作Panel 在之前实现了flutter聊天界面的自定义表情的展示&#xff0c;这里记录一下更多操作展开的相机、相册等操作功能实现。 一、查看效果 更多操作展开的相机、相册等操作功能实现。 二、代码实现 展开的操作按钮可能比…

前端(四)——vue.js、vue、vue2、vue3

&#x1f60a;博主&#xff1a;小猫娃来啦 &#x1f60a;文章核心&#xff1a;vue.js、vue、vue2、vue3从全局到局部 文章目录 vue.js、vue、vue2、vue3是什么关系?Vue.js简介发展历程特点与优势生态系统Vue.js基础知识安装与配置 基本语法Vue.js主要版本解析Vue.js 2.x vue2…

python 第十一章 文件操作

系列文章目录 第一章 初识python 第二章 变量 第三章 基础语句 第四章 字符串str 第五章 列表list [] 第六章 元组tuple ( ) 第七章 字典dict {} 第八章 集合set {} 第九章 常用操作 第十章 函数 文章目录 系列文章目录11.1文件操作的作用11.2文件的基本操作打开打开文件模式 …

string底层是如何实现的

前言 在我们学习的时候总是会用到string&#xff0c;知道它具备各种功能&#xff0c;它也是一种很强大的模板&#xff0c;那么有没有想过&#xff0c;我们天天都在使用的它&#xff0c;它的底层又是怎么样的&#xff0c;它又是如何实现的呢。这里讲挑选几个比较常用的一个功能…