全金属贪吃蛇

jQuery 是真的好用

Posted by theVan on April 4, 2021

简介

全金属贪吃蛇

代码

        $(function() {
            const FRAME_INTERVAL = 25; // 相当于每秒刷新40帧
            const SPEED_DOWN = 4; // 速度相对于刷新频率进行减速
            const BLOCK_SIZE = 15;
            const MAP_SIZE_X = 20;
            const MAP_SIZE_Y = 20;
            const LABEL = 'metalSnake';
            const MAIN_COLOR = 'rgba(0,0,0,.5)';
            const SECOND_COLOR = 'rgba(0,0,0,.2)';
            const BG_COLOR = '#fff';
            const TIP_VALUE = {
                'first': 'PRESS ENTER TO START',
                'second': 'PRESS SPACE TO PAUSE',
                'third': 'GAME OVER \n PRESS ENTER TO RESTART'
            };
            const SNAKE_BODY_INIT = [{
                x: 2,
                y: 0
            }, {
                x: 1,
                y: 0
            }, {
                x: 0,
                y: 0
            }];
            const SNAKE_BODY_RESET = [{
                x: 2,
                y: 0
            }, {
                x: 1,
                y: 0
            }, {
                x: 0,
                y: 0
            }];

            new Screen().init();

            /**
             * Screen module is a manage module
             */
            function Screen() {
                this.screen = $('#screen');
                this.map = new Map();
                this.tip = new Tip(LABEL);
                this.snake = new Snake(SNAKE_BODY_INIT);
                this.apple = new Apple();
                this.isflash = true; // 设置 snake head 是否闪烁
                this.flashFlag = true;
                this.timer = null;
                this.appleCoords = this.apple.getAppleCoords(this.snake);
                this.count = 0; // 减速计数器
                this.isOver = false;


                // mixed area
                this.init = () => {
                    if (this.isOver) {
                        this.snake.bodys = SNAKE_BODY_RESET;
                    }
                    this.initMap();
                    this.initTip();
                    this.initInputListener()
                }

                this.initMap = () => {
                    this.screen.append(this.map.canvas);
                    this.clearMap();
                    this.render()
                }

                this.initTip = () => {
                    this.screen.append(this.tip.pad);
                    this.tip.setTipValue(TIP_VALUE.first);
                }

                this.initInputListener = () => {
                    $(window).on('keydown', (e) => {
                        switch (e.keyCode) {
                            case 32:
                                this.pause();
                                break;
                            case 13:
                                if (this.isOver) {
                                    this.init();
                                    this.isOver = false;
                                } else {
                                    this.start();
                                }
                                break;
                            case 37:
                                if (this.snake.direction != 'right') {
                                    this.snake.directionQ.push('left');
                                }
                                break;
                            case 38:
                                if (this.snake.direction != 'down') {
                                    this.snake.directionQ.push('up');
                                }
                                break;
                            case 39:
                                if (this.snake.direction != 'left') {
                                    this.snake.directionQ.push('right');
                                }
                                break;
                            case 40:
                                if (this.snake.direction != 'up') {
                                    this.snake.directionQ.push('down');
                                }
                                break;
                        }
                    })
                };

                this.start = () => {
                    this.pause();
                    this.timer = setInterval(this.run, FRAME_INTERVAL);
                    this.tip.setTipValue(TIP_VALUE.second);
                }

                this.pause = () => {
                    if (this.timer != null) {
                        clearInterval(this.timer);
                    }
                    this.tip.setTipValue(TIP_VALUE.first);
                }

                this.over = () => {
                    if (this.timer != null) {
                        clearInterval(this.timer);
                    }
                    this.tip.setTipValue(TIP_VALUE.third);
                    this.isOver = true;
                }


                this.run = () => {
                    if (this.count % SPEED_DOWN == 0) {
                        this.eatAppleCheck();
                        this.snake.moveCheck();
                        this.snake.borderCheck();
                        if (!this.snake.overCheck()) {
                            this.over();
                        }
                        this.count = 0;
                    }
                    this.count++;
                    this.render(this.isflash);
                }

                this.eatAppleCheck = () => {
                    if (this.snake.bodys[0].x == this.appleCoords.x && this.snake.bodys[0].y == this.appleCoords.y) {
                        this.snake.bodys.push({
                            x: null,
                            y: null
                        });
                        this.appleCoords = this.apple.getAppleCoords(this.snake);
                    }
                }

                // left area
                this.clearMap = () => {
                    for (let i = 0; i < MAP_SIZE_X; i++) {
                        for (let j = 0; j < MAP_SIZE_Y; j++) {
                            $('<div>').css({
                                'position': 'absolute',
                                'box-sizing': 'border-box',
                                'top': BLOCK_SIZE * j + 'px',
                                'left': BLOCK_SIZE * i + 'px',
                                'width': BLOCK_SIZE + 'px',
                                'height': BLOCK_SIZE + 'px',
                                'background-color': BG_COLOR
                            }).appendTo(this.map.canvas);
                        }
                    }
                }

                this.render = (isFlash = false) => {
                    let headColor = MAIN_COLOR;
                    if (isFlash) {
                        headColor = this.flashFlag ? MAIN_COLOR : SECOND_COLOR;
                        this.flashFlag = !this.flashFlag;
                    }
                    this.clearMap();
                    this.map.canvas.empty();
                    for (let i = 0; i < this.snake.bodys.length; i++) {
                        if (i == 0) {
                            $('<div>').css({
                                'position': 'absolute',
                                'top': this.snake.bodys[i].y * BLOCK_SIZE + 'px',
                                'left': this.snake.bodys[i].x * BLOCK_SIZE + 'px',
                                'width': BLOCK_SIZE,
                                'height': BLOCK_SIZE,
                                'background-color': headColor
                            }).appendTo(this.map.canvas);
                        } else {
                            $('<div>').css({
                                'position': 'absolute',
                                'top': this.snake.bodys[i].y * BLOCK_SIZE + 'px',
                                'left': this.snake.bodys[i].x * BLOCK_SIZE + 'px',
                                'width': BLOCK_SIZE,
                                'height': BLOCK_SIZE,
                                'background-color': MAIN_COLOR
                            }).appendTo(this.map.canvas);
                        }
                    }
                    $('<div>').css({
                        'position': 'absolute',
                        'top': this.appleCoords.y * BLOCK_SIZE + 'px',
                        'left': this.appleCoords.x * BLOCK_SIZE + 'px',
                        'width': BLOCK_SIZE,
                        'height': BLOCK_SIZE,
                        'background-color': MAIN_COLOR
                    }).appendTo(this.map.canvas);
                }

                //right area
            };

            // left map module
            function Map() {
                this.coords = {
                    x: MAP_SIZE_X,
                    y: MAP_SIZE_Y
                };
                this.canvas = $('<div>').css({
                    'position': 'absolute',
                    'box-sizing': 'border-box',
                    'left': '10px',
                    'top': '10px',
                    'width': '300px',
                    'height': '300px',
                    'background-color': BG_COLOR
                });
            }

            function Snake(initBody) {
                this.bodys = initBody;
                this.directionQ = [];
                this.direction = 'right';

                this.moveCheck = () => {
                    if (this.directionQ.length > 0) {
                        this.direction = this.directionQ.shift();
                    }
                    switch (this.direction) {
                        case 'left':
                            for (let i = this.bodys.length; i > 0; i--) {
                                if (i - 1 == 0) {
                                    this.bodys[i - 1].x--;

                                } else {
                                    this.bodys[i - 1].x = this.bodys[i - 1 - 1].x;
                                    this.bodys[i - 1].y = this.bodys[i - 1 - 1].y;
                                }
                            }
                            break;
                        case 'up':
                            for (let i = this.bodys.length; i > 0; i--) {
                                if (i - 1 == 0) {
                                    this.bodys[i - 1].y--;

                                } else {
                                    this.bodys[i - 1].x = this.bodys[i - 1 - 1].x;
                                    this.bodys[i - 1].y = this.bodys[i - 1 - 1].y;
                                }
                            }
                            break;
                        case 'right':
                            for (let i = this.bodys.length; i > 0; i--) {
                                if (i - 1 == 0) {
                                    this.bodys[i - 1].x++;

                                } else {
                                    this.bodys[i - 1].x = this.bodys[i - 1 - 1].x;
                                    this.bodys[i - 1].y = this.bodys[i - 1 - 1].y;
                                }
                            }
                            break;
                        case "down":
                            for (let i = this.bodys.length; i > 0; i--) {
                                if (i - 1 == 0) {
                                    this.bodys[i - 1].y++;

                                } else {
                                    this.bodys[i - 1].x = this.bodys[i - 1 - 1].x;
                                    this.bodys[i - 1].y = this.bodys[i - 1 - 1].y;
                                }
                            }
                            break;
                    }
                }

                this.borderCheck = () => {
                    if (this.bodys[0].x >= MAP_SIZE_X) {
                        this.bodys[0].x = 0;
                    } else if (this.bodys[0].x < 0) {
                        this.bodys[0].x = MAP_SIZE_X - 1;
                    } else if (this.bodys[0].y >= MAP_SIZE_Y) {
                        this.bodys[0].y = 0;
                    } else if (this.bodys[0].y < 0) {
                        this.bodys[0].y = MAP_SIZE_Y - 1;
                    }
                }


                this.overCheck = () => {
                    for (let i = 1; i < this.bodys.length; i++) {
                        if (this.bodys[0].x == this.bodys[i].x && this.bodys[0].y == this.bodys[i].y) {
                            return false;
                        }
                    }
                    return true;
                }

            }

            function Apple() {
                this.getAppleCoords = snake => {
                    let rX = Math.floor(Math.random() * MAP_SIZE_X);
                    let rY = Math.floor(Math.random() * MAP_SIZE_Y)
                    if (snake.bodys.every(e => {
                            return e.x != rX || e.y != rY;
                        })) {
                        return {
                            x: rX,
                            y: rY
                        };
                    } else {
                        this.getAppleCoords(snake);
                    }
                }
            }


            // right tips module
            function Tip(label) {
                this.pad = $('<div>').css({
                    'position': 'absolute',
                    'box-sizing': 'border-box',
                    'left': '320px',
                    'top': '10px',
                    'width': '150px',
                    'height': '300px',
                    'background-color': 'transparent'
                })
                this.topPad = $('<div>').css({
                    'box-sizing': 'border-box',
                    'width': '150px',
                    'height': '200px',
                    'border': '1px solid #ccc',
                    'background-color': BG_COLOR
                }).appendTo(this.pad);
                this.label = $('<p>').css({
                    'box-sizing': 'border-box',
                    'width': '150px',
                    'height': '100px',
                    'line-height': '100px',
                    'text-align': 'center',
                    'font-size': '24px',
                    'color': MAIN_COLOR,
                    'background-color': 'inherit'
                }).text(label).appendTo(this.pad);

                this.setTipValue = (value) => {
                    $('<p>').text(value).appendTo(this.topPad.empty());
                }
            }
        });