TypeScript实现一个贪吃蛇小游戏

news2024/11/18 1:47:09

游戏效果

文件目录

准备1:新建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 class="main">
        <div class="stage">
            <div id="snake">
                <div></div>
            </div>
            <div id="food">
                <div></div>
            </div>
        </div>
        <div class="score-panel">
            <div>
                SCORE: <span id="score">0</span>
            </div>
            <div>
                level: <span id="level">1</span>
            </div>
        </div>
    </div>
</body>
</html>

准备2:使用less,修改样式,编写CSS

//设置变量
@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: black;
                border: 1px solid @bg-color;
                //绝对定位
                position: absolute;
            }
            
        }
        #food{
            position: absolute;
            left: 40px;
            top: 100px;
            background-color: rebeccapurple;
            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;

    }
}

准备3:创建4个类:食物类-Food、记分牌等级类-ScorePanel、蛇类-Snake、操控类-GameControl

//食物类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(){
        //生成一个随机的位置
        let top = Math.round(Math.random()*29)*10
        let left = Math.round(Math.random()*29)*10

        //食物的位置最小的0,最大是290
        //蛇移动一次就是一格,一格的大小就是10,所以就要求食物的
        this.element.style.left = left + 'px'
        this.element.style.top = top + 'px'
    }
}
export default Food
//记分牌的类
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.levelUp()
        }
    }
    //提升等级方法
    levelUp(){
        if(this.level < this.maxLevel){
            this.levelEle.innerHTML = ++this.level + ''
        }
    }
}
export default ScorePanel
//蛇类-Snake
class Snake {
    //表示蛇头的元素
    head : HTMLElement
    //蛇的身体
    bodies: HTMLCollection
    //获取蛇的容器
    element :HTMLElement 
    constructor(){
        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大于旧值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('蛇撞墙了')
        }
        //修改Y时,是在修改垂直坐标,蛇在上下移动,蛇向上移动时,不能向下移动,反之亦然
        if(this.bodies[1]&&(this.bodies[1] as HTMLElement).offsetTop ===value){
            //如果发送了掉头,让蛇向反方向继续移动
            if(value >this.Y){
                //如果新值value大于旧值Y,则说明蛇在向下走,此时发送掉头,应该使蛇继续向上走
                value = this.Y - 10;
            }else{
                //向左走
                value = this.Y + 10
            }
        }
        this.moveBody()
        this.head.style.top = value + 'px'
        //检查有没有撞到自己
        this.checkHeadBody()
    }
    //蛇增加身体的方法
    addBody(){
        //向element中添加一个div
        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
//控制类
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()
    }
    //游戏的初始化方法,调用后游戏即开始
    init(){
        //绑定键盘按键按下的事件
        document.addEventListener('keydown',this.keydownHandler.bind(this))
        //调用run方法,使蛇移动
        this.run()
    }
    //创建一个键盘按下的响应函数
    keydownHandler(event:KeyboardEvent){
        //需要检查event.key的值是否合法(用户是否按了正确的按键)
        //修改direction属性
        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":
            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)
        //修改蛇的X、Y方向
        try {
            this.snake.X = X;
            this.snake.Y = Y ;           
        } catch (e:any) {
            //进入到catch,说明出现了异常,游戏结束,弹出一个提示信息
            alert(e.message + 'GAME 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

准备4:创建index.ts文件,执行游戏

import './style/index.less'
import GameControl from './moduls/GameControl'

new GameControl()

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

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

相关文章

微店商品详情API(micro.item_get)的数据分析和挖掘

随着电商行业的迅猛发展&#xff0c;微店作为电商平台的重要组成部分&#xff0c;提供了丰富的API接口供开发者使用。其中&#xff0c;微店商品详情API&#xff08;micro.item_get&#xff09;是用于获取商品详情的接口&#xff0c;为数据分析和挖掘提供了大量有价值的数据源。…

域环境权限提升

Windows系统配置错误 在Windows系统中&#xff0c;攻击者通常会通过系统内核溢出漏来提权&#xff0c;但是如果碰到无法通过系统内核溢出漏洞法国提取所在服务器权限的情况&#xff0c;就会系统中的配置错误来提权。Windows系统中常见哦欸之错误包括管理员凭证配置错误&#x…

人才测评,招聘软件研发经理的胜任素质模型与任职资格

招聘软件研发经理的胜任素质模型和任职资格是确保能够招聘到胜任的人才的重要组成部分。以下是详细的说明&#xff1a; 一、胜任素质模型 1.技术能力 软件研发经理需要具备深厚的技术能力&#xff0c;对软件开发的各个方面有深入的理解和掌握。他们需要掌握多种编程语言和…

c# 视频播放之Vlc.DotNet.Forms

先说下优缺点 优点&#xff1a;与电脑无关&#xff0c;能播放主流编码格式视频。 缺点&#xff1a;只能播放本地视频&#xff0c;网络视频播放不了。 下面是具体操作和代码 1. 安装Vlc.DotNet.Forms 和 VideoLAN.LibVLC.Windows Vlc.DotNet.Forms 是播放库&#xff0c;Vid…

算法 动态分析 及Java例题讲解

动态规划 动态规划&#xff08;英语&#xff1a;Dynamic programming&#xff0c;简称 DP&#xff09;&#xff0c;是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的&#xff0c;通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适…

Ant Design Vue上传多个图片

模板代码&#xff1a; 定义变量&#xff1a; 文件限制的函数&#xff1a; 上传的函数&#xff1a; 样式函数&#xff1a; 完整代码&#xff1a; <template><div class"dialog-upload" v-if"showUploadDialog"><div class"dialog-uplo…

2018年认证杯SPSSPRO杯数学建模A题(第二阶段)海豚与沙丁鱼全过程文档及程序

2018年认证杯SPSSPRO杯数学建模 基于聚类分析的海豚捕食合作策略 A题 海豚与沙丁鱼 原题再现&#xff1a; 沙丁鱼以聚成大群的方式来对抗海豚的捕食。由于水下光线很暗&#xff0c;所以在距离较远时&#xff0c;海豚只能使用回声定位方法来判断鱼群的整体位置&#xff0c;难…

python|写一个简单的http服务器

本篇文章的python版本为: 什么是http http是一个应用层协议&#xff0c;准确的来说是基于TCP/IP4层网络协议中的传输层中的TCP应用层协议。 额&#xff0c;4层模型大概是这样的: 在网络通信中&#xff0c;用户的数据是以报文来传输的&#xff0c;但是在实际通信中&#xff0…

密码学学习笔记(二十四):TCP/IP协议栈

TCP/IP协议栈的基础结构包括应用层、传输层、网络层、数据链路层和物理层。 应用层 应用层位于TCP/IP协议栈的最顶层&#xff0c;是用户与网络通信的接口。这一层包括了各种高级应用协议&#xff0c;如HTTP&#xff08;用于网页浏览&#xff09;、FTP&#xff08;用于文件传输…

【安全篇 / FortiGuard】(7.4) ❀ 02. 独立VDOM下的FortiGuard服务升级 ❀ FortiGate 防火墙

【简介】由于业务的需要&#xff0c;创建两个独立VDOM&#xff0c;每个VDOM有各自的宽带&#xff0c;但是FortiGuard服务却无法升级&#xff0c;有什么办法解决吗&#xff1f; VDOM概念 首先我们看看什么是VDOM。 ① VDOM将你的FortiGate划分为多个逻辑设备&#xff0c;并将一个…

深入理解Linux文件系统

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;晴る—ヨルシカ 0:20━━━━━━️&#x1f49f;──────── 4:30 &#x1f504; ◀️ ⏸ ▶️ ☰ &…

力扣精选算法100题——长度最小的子数组(滑动窗口专题)

本题链接——长度最小的子数组 第一步&#xff1a;了解题意 给定一个数组&#xff0c;要求在这个数组中找到一个必须是连续的子数组并且这个子数组每个元素加起来>target并从找到的这些数组中取一个最短的数组。 第二步&#xff1a;算法原理 滑动窗口是一种在序列&#xff…

【开源】基于JAVA语言的河南软件客服系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统管理人员2.2 业务操作人员 三、系统展示四、核心代码4.1 查询客户4.2 新增客户跟进情况4.3 查询客户历史4.4 新增服务派单4.5 新增客户服务费 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的河…

中仕公考:贵州省统一面向社会公开招录公务员公告

2024年1月22日至1月25日期间,每日10:00、14:00及19:00对报名情况进行更新,供报考者参考。 资格初审时间为2024年1月22日9:00至1月28日17:00。 网上缴费时间为2024年1月22日9:00至1月29日17:00。 打印准考证时间2024年3月11日9:00至3月14日17:00。 公共科目笔试时间为: 202…

深度学习(2)--卷积神经网络(CNN)

卷积神经网络&#xff08;Convolutional Neural Networks&#xff09;是一种深度学习模型或类似于人工神经网络的多层感知器&#xff0c;常用来分析视觉图像。 一.卷积神经网络基础概念 传统网络是二维的&#xff0c;而卷积网络是三维的。 例如32x32x3的图片&#xff0c;在传…

Pure-admin框架 Pure-table中获取所选中的内容的信息

最近在尝试使用Pure-admin框架来进行开发&#xff0c;正好遇到了多选表格需要获取选中项的id的情况&#xff0c;因为平台介绍说是二次封装 element-plus 的 Table &#xff0c;直接拿el-table的方法来试 在table上设置属性ref"multipleTableRef" let idArr [];mult…

为什么需要放行回源IP

为什么需要放行回源IP 网站以“独享模式”成功接入WAF后&#xff0c;所有网站访问请求将先经过独享引擎配置的ELB然后流转到独享引擎实例进行监控&#xff0c;经独享引擎实例过滤后再返回到源站服务器&#xff0c;流量经独享引擎实例返回源站的过程称为回源。在服务器看来&…

从“渲个锤子”,浅析影视动画渲染的困难与挑战!

提起“雷神之锤”&#xff0c;相信大家都不陌生。它是漫威电影宇宙中“雷神”托尔所使用的武器。传说中&#xff0c;这把圣器可以承载魔法咒语&#xff0c;召唤风、雨、雷、电四种自然元素。它能在使用者的意念下对目标进行攻击&#xff0c;无视与使用者之间的距离和障碍物&…

C++进阶(五)二叉搜索树

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、二叉搜索树概念二、二叉搜索树操作三、二叉搜索树的实现四、二叉搜索树的应用五、二叉搜索…

(人才测评)招聘数据分析师的入职测评方案

现在是一个大数据的时代&#xff0c;我们的车载导航、淘宝购物、包括人才招聘、都是大数据的产物&#xff0c;然而光靠数据堆积是没用的&#xff0c;所以如何高效的进行数据挖掘与分析&#xff0c;就成了每一件企业思考的问题。 一、 数据分析师的基本工作标准 1、 平时需要…