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

本项目使用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.mfbz.cn/a/35719.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FreeRTOS 低功耗模式设计 STM32平台

1. STM32F105RBT6 的三种低功耗模式 1.1 sleep睡眠模式、stop停机模式、standby 待机模式 1.2 STM32中文参考手册有介绍STM32 低功耗模式的介绍 2. FreeRTOS 采用的是时间片轮转的抢占式任务调度机制&#xff0c;其低功耗设计思路一般是&#xff1a; ① 当运行空闲任务&#…

第三章 SSD存储介质:闪存 3.1

3.1 闪存物理结构 闪存芯片从小到大依此是由&#xff1a;cell&#xff08;单元&#xff09;、page&#xff08;页&#xff09;、block&#xff08;块&#xff09;、plane&#xff08;平面&#xff09;、die&#xff08;核心&#xff09;、NAND flash&#xff08;闪存芯片&#…

C/C++指针从0到99(详解)

目录 一&#xff0c;指针的基础理解 二&#xff0c;指针的基本使用 三&#xff0c;为什么要用指针 四&#xff0c;指针与数组的联系 五&#xff0c;指针的拓展使用 1&#xff09;指针数组 2)数组指针 3&#xff09;函数指针 结构&#xff1a;返回类型 &#xff08;*p)…

算法的时间复杂度

算法的时间复杂度 什么是时间复杂度 时间复杂度是衡量算法执行时间随输入规模增长而增长的度量标准。它描述了算法运行时间与问题规模之间的关系&#xff0c;用于评估算法的效率和性能。 通常情况下&#xff0c;时间复杂度表示为大O符号&#xff08;O&#xff09;&#xff0…

K8S调度管理

调度管理 1.1 调度框架1.1.1 调度体系1.1.2 资源调度 1.2 资源调度1.2.1 节点调度1.2.2 节点亲和1.2.3 Pod亲和1.2.4 Pod反亲和1.2.5 污点&容忍度1.2.6 污点实践 1.3 流量调度1.3.1 Ingress基础1.3.2 Ingress实践1.3.3 Ingress进阶1.3.4 Ingress认证1.3.5 Ingress扩展 1.1 …

mac与pd虚拟机之间不能粘贴文字或粘贴文件

首先确保共享打开&#xff1a; 然后检查虚拟机的Parallels Tools是否正常 一个简单的判断方式就是&#xff0c;退出虚拟机全屏之后&#xff0c;如果能够正常进入融合模式&#xff0c;那么Parallels Tools可用&#xff0c;否则就要排查问题 检查Parallels Tools是否随系统正常启…

SELF-ATTENTION DOES NOT NEED O(n2) MEMORY

背景 主要是要解决self-attention空间复杂度的问题&#xff0c;因为对于gpu计算来说&#xff0c;内存空间非常宝贵&#xff0c;序列长度较长的时候会出现oom问题。 用线性时间解决self-attention问题 解决数据稳定问题 因为由于进行求和计算&#xff0c;容易导致浮点数超过最…

Redis【实战篇】---- 分布式锁

Redis【实战篇】---- 分布式锁 1. 基本原理和实现方式对比2. Redis分布式锁的实现核心思路3. 实现分布式锁版本一4. Redis分布式锁误删情况说明5. 解决Redis分布式锁误删问题6. 分布式锁的原子性问题7. Lua脚本解决多条命令原子性问题8. 利用Java代码调试Lua脚本改造分布式锁 1…

AIGC - Stable Diffusion WebUI 图像生成工具的环境配置

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/131528224 Stable Diffusion WebUI 是一款基于深度学习的图像生成工具&#xff0c;根据用户的输入文本或图像&#xff0c;生成高质量的新图像&…

【Docker 部署Minio】

Docker 部署Minio 一、拉取Minio镜像二、配置1、创建如下目录2、创建容器并运行 三、访问 一、拉取Minio镜像 访问Docker Hub镜像站找到自己需要的Minio镜像 运行以下命令 sudo docker pull minio/minio二、配置 1、创建如下目录 mkdir -p /home/zx/minio/config mkdir -p…

k8s概念介绍

目录 一 整体架构和组件基本概念 1.1 组件 1.1.1 master节点 1.1.2 node节点 1.1.3 附加组件 二 资源和对象 2.1 资源分类 2.2 元数据资源 Horizontal Pod Autoscaler&#xff08;HPA&#xff09; PodTemplate LimitRange 2.3 集群资源 namespace Node ClusterRo…

Linux下GO IDE安装和配置(附快捷键)

目前&#xff0c;GoLand、VSCode 这些 IDE 都很优秀&#xff0c;但它们都是 Windows 系统下的 IDE。在 Linux 系统下我们可以选择将 Vim 配置成 Go IDE。熟练 Vim IDE 操作之后&#xff0c;开发效率不输 GoLand 和 VSCode。有多种方法可以配置一个 Vim IDE&#xff0c;这里我选…

华为、阿里巴巴、字节跳动 100+ Python 面试问题总结(二)

系列文章目录 个人简介&#xff1a;机电专业在读研究生&#xff0c;CSDN内容合伙人&#xff0c;博主个人首页 Python面试专栏&#xff1a;《Python面试》此专栏面向准备面试的2024届毕业生。欢迎阅读&#xff0c;一起进步&#xff01;&#x1f31f;&#x1f31f;&#x1f31f; …

【5G PHY】5G控制资源集CORESET介绍

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

SpringBoot(六)SpringBoot项目部署到腾讯云服务器

这篇文章&#xff0c;可以说是干货满满。关注我的同学应该直到&#xff0c;之前我有几篇SpringBoot的文章&#xff0c;介绍了如何搭建本地服务器&#xff08;没看过的同学可以系统地看下我的SpringBoot专栏&#xff0c;保证你会有很多的收获&#xff09;。但我们那都是在本地玩…

Qt(Day2)

实现登录框中&#xff0c;当登录成功时&#xff0c;关闭登录界面&#xff0c;并跳转到其他界面&#xff1a;

前端面试题-HTML、HTTP、web综合问题(三)

26 你做的⻚⾯在哪些流览器测试过&#xff1f;这些浏览器的内核分别是什么? IE : trident 内核Firefox &#xff1a; gecko 内核Safari : webkit 内核Opera :以前是 presto 内核&#xff0c; Opera 现已改⽤Google - Chrome 的 Blink 内核Chrome:Blink (基于 webkit &#xf…

服务器搭建oracle,并远程连接教程

下载两个压缩包&#xff0c;然后上传到服务器&#xff0c; 软件安装09&#xff1a;CentOS安装Oracle - 虚拟机 - 5997CK - 欢迎您! (hezhilin.online) 这里有全部步骤&#xff0c;反正过了几天我也会忘记&#xff0c;不赘述了。 直接上拆的坑&#xff1a; 开启服务器端口后…

【数据结构】24王道考研笔记——串

四、串 串的定义 串&#xff08;字符串&#xff09;是由零个或多个字符组成的有限序列。 子串&#xff1a;串中任意个连续的字符组成的子序列主串&#xff1a;包含子串的串字符在主串中的位置&#xff1a;字符在串中的序号子串在主串中的位置&#xff1a;子串的第一个字符在…

【Spring】项目创建和使用

一、Spring 的概念 Spring : 包含众多工具方法的 IoC 容器。 Spring 的核心 &#xff1a;IoC &#xff08;控制反转&#xff09;&#xff0c; DI (依赖注入)。 loC &#xff08;Inversion of Control&#xff09;翻译成中文就是 “控制反转” 的意思&#xff0c;控制反转一种…
最新文章