TypeScript实战(贪吃蛇小游戏)

Food类

在游戏页面随机生成食物,通过设置坐标轴和Math.random()随机生成坐标点来模拟食物随机生成。

// 食物类
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, 最大是290
        // 蛇移动一次就是一格,一格大小就是10,所以食物的坐标必须被10整除

       let top = Math.round(Math.random() * 29) * 10;
       let left = Math.round(Math.random() * 29) * 10;

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

export default Food;

Snake类

在页面设计中通过div嵌套,每个div由10px的小格组成来作为蛇,设置border为1px的背景色来方便区分蛇的长度。通过判断蛇头的坐标是否在0~290范围内来说明蛇是否撞墙。获取蛇身体全部div检查每个div的坐标是否重合来判断是否撞到自己。要实现蛇移动,需要遍历蛇身体每个div,并依次最坐标值增加10,用过在结束标签后添加div标签来实现身体增加。

class Snake{
    // 表示蛇头的元素
    head: HTMLElement;

    // 蛇的身体(包括蛇头)
    bodies: HTMLCollection;

    // 获取蛇的容器
    element: HTMLElement;

    constructor(){
        this.element = this.element = document.getElementById('snake')!;
	//控制每一个div
        this.head = document.querySelector('#snake > div') as HTMLElement;
        // document.querySelectorAll('#snake > div');// nodeList
        this.bodies = this.element.getElementsByTagName('div');
    }

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

    // 获取蛇的Y轴坐标
    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) {
            // console.log('水平方向发生了掉头');
            // 如果发生了掉头,让蛇向方向继续移动
            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;
        }
        // Y值的合法范围0-290之间
        if (value <0 || value > 290 ) {
            // 进入判断说明蛇撞墙了
            throw new Error("蛇撞墙了~~");
        }
        
        // 修改Y时,是在修改水平坐标,蛇在上下移动,蛇在向上移动时,不能向下掉头,反之亦然
        if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === value) {
            // console.log('垂直方向发生了掉头');
            // 如果发生了掉头,让蛇向方向继续移动
            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(){
        /* 
            从后往前改
            将后面的身体设置为前面身体的位置
                举例子:
                    第4节 = 第3节的位置
                    第3节 = 第2节的位置
                    第2节 = 蛇头的位置
        */
        //遍历获取所有的身体
        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;

ScorePanel类

记分牌类,用来设置最高等级和分数自增函数,以及等级计算方法。

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.score++;
        // this.scoreEle.innerHTML = 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;

GameControl类

引入上面三个类,来进行游戏控制和事务处理。用键盘响应函数限制仅方向键操作,使用事件监听器监听方向键按下后游戏初始化并开始游戏。在run()方法中判断游戏正在进行后回调来实现蛇向指定方向自己前进。蛇吃到食物后调用Food类中的change()函数来刷新食物、调用ScorePanel类中的addScore()来加分、调用Snake类中的addBody()函数增加蛇身。

import Snake from "./Snake";
import Food from "./Food";
import ScorePanel from "./ScorePanel";

// 游戏控制器,控制其他的所有类
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(10, 2);

        this.init();
    }

    // 游戏的初始化方法,调用后游戏即开始
    init() {
        // 绑定键盘按下的事件
        document.addEventListener('keydown', this.keydownHandler.bind(this));
        // 涉及到this和bind知识

        // 调用run()方法,使蛇移动
        this.run();

    }

    /* 
        ArrowUp Up
        ArrowDown Down
        ArrowRight Right
        ArrowLeft Left
    */
    // 创建一个键盘按下的响应函数
    keydownHandler(event: KeyboardEvent) {
        // console.log(this);
        // 需要检查event.key的值是否合法(用户是否按了正确的按键)
        // 修改direction属性
        this.direction = event.key
        // console.log(event.key);
    }

    // 创建一个控制蛇移动的方法
    run() {
        /* 
            根据方向(this.direction)来使蛇的位置改变
            向上 top 减少
            向下 top 增加
            向左 left 减少
            向右 left 增加 
        */
        // 获取蛇现在的坐标
        let X = this.snake.X;
        let Y = this.snake.Y;


        // 根据按键方向修改X值和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);
        // if (this.checkEat(X, Y)) {
        //     console.log('吃到食物了~~');
        //     // 食物的位置进行重置
        //     this.food.change();
        //     // 分数增加
        //     this.scorePanel.addScore();
        //     // 蛇要增加一节
        //     this.snake.addBody();
        // }

        // 修改蛇的X和Y值
        try {
            this.snake.X = X;
            this.snake.Y = Y;
        } catch (e) {
            // 进入到catch, 说明出现了异常,游戏结束,弹出一个提示信息
            // TODO
            alert('GAME OVER!');
            // 将isLive设置为false
            this.isLive = false;
        }
    

        // 开启一个定时调用
        clearTimeout(1);
        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) {
            console.log('吃到食物了~~');
            // 食物的位置进行重置
            this.food.change();
            // 分数增加
            this.scorePanel.addScore();
            // 蛇要增加一节
            this.snake.addBody();
        } 
    }
}
export default GameControl;

猜你喜欢

转载自blog.csdn.net/qq_43774332/article/details/125651451
今日推荐