iT邦幫忙

2021 iThome 鐵人賽

DAY 15
0
Modern Web

JavaScript 嗨起來用 JS 做動畫 ε= ᕕ( ᐛ )ᕗ系列 第 15

Day15 - 請蛇上台

class Snake {
    constructor() {
        // 蛇頭位子
        this.head = new Vector(0, 0);
        // 除了蛇頭外蛇身的位子
        this.body = [];
        // 蛇移動的步伐
        this.step = new Vector(1, 0);
        // 蛇頭加上蛇身長度
        this.maxLength = 3;
    }
    update() {
        // push head to body
        this.body.push(this.head);
        // generate new head
        // 整隻蛇往前一步,蛇頭當然也就往前一步
        this.head = this.head.add(this.step);
        // shift the tail
        // 整隻蛇往前一步,蛇尾當然也就往前一步,故刪除舊蛇尾位子
        while(this.body.length > this.maxLength){
            this.body.shift();
        }
    
    };
    setDirection(direction) {
        var newStep;
        switch(direction){
            case 'Right':
                newStep = new Vector(1,0);
                break
            case 'Left':
                newStep = new Vector(-1,0);
                break
            case 'Up':
                newStep = new Vector(0,-1);
                break
            case 'Down':
                newStep = new Vector(0,1);
                break
        };
        // 蛇本來往左走,如果玩家按下向右鍵,則不理玩家,蛇維持往左步伐
        if(!this.step.equal(newStep.mul(-1))){
            this.step = newStep;
        }
    };
    // 判斷蛇頭有沒有撞牆
    isWithinBoundary(boundary) {
        var xWithinBoundary = this.head.x >= 0 && this.head.x < boundary
        var yWithinBoundary = this.head.y >= 0 && this.head.y < boundary
    
        return xWithinBoundary && yWithinBoundary;
    
    };
};
class Game{
    constructor() {
        // 舞台由 20 格,每格 26px 的格子組成,且每個之間的間距為 2px
        this.blocks = 20;
        this.blockWidth = 26;
        this.blockSpace = 2;
        // 請蛇上台
        this.snake = new Snake();
        this.foods = [];
        this.generateFoods();
        this.start = true;
        this.bgm = new BGM();
        // init
        this.init();
    }
    init() {
        // set canvas
        this.canvas = document.getElementById('canvasSnake');
        this.canvas.width = this.blockWidth * this.blocks + this.blockSpace * (this.blocks - 1);
        this.canvas.height = this.canvas.width;
        this.ctx = this.canvas.getContext('2d');
    
        // render canvas view
        this.render();
        
        // update game data
        this.update();
    
    };
    
    render() {
        // bg
        this.ctx.fillStyle = '#dbe3c5';
        this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.width);
        // 畫出舞台格子
        for (var bX = 0; bX < this.blocks; bX++) {
            for (var bY = 0; bY < this.blocks; bY++) {
                this.drawBlock(new Vector(bX, bY), '#ced6b8')
            };
        };
    
        // snake
        this.snake.body.forEach(sbPosition => 
            this.drawBlock(sbPosition, `rgb(${144-this.snake.maxLength*3},${150+this.snake.maxLength},${128+this.snake.maxLength*2})`));
    
        this.foods.forEach(fPosition => {
            this.drawBlock(fPosition, 'rgb(24,174,198)');
        });
        // render again
        requestAnimationFrame(()=>this.render(),1);
    };
    // 計算我們畫的舞台格子,實際是在 canvas 上多少 px 的位子
    getPosition(blockXY) {
        return {
            x:blockXY.x * (this.blockWidth + this.blockSpace),
            y:blockXY.y * (this.blockWidth + this.blockSpace)
        }
    };
    // 畫一格(舞台格子的大小)
    drawBlock(blockPos, color) {
        var pos = this.getPosition(blockPos);
        this.ctx.fillStyle = color;
        this.ctx.fillRect(pos.x, pos.y, this.blockWidth, this.blockWidth);
    };
    update() {
        if(this.start){
            // 更新蛇的狀態
            this.snake.update();
            // 確認食物是不是被蛇吃了
            this.foods.forEach((food, i) => {
                if(food.equal(this.snake.head)){
                    this.generateFoods();
                    this.foods.splice(i, 1);
                    this.snake.maxLength ++;
                }
            });
            // 確認蛇是不是輸了
            // 蛇有超出邊界嗎
            if(!this.snake.isWithinBoundary(this.blocks)){
                this.endGame();
            };
            // 蛇有撞到自己嗎
            this.snake.body.forEach((sbPos) =>{
                if(sbPos.equal(this.snake.head)){
                    this.endGame();
                    console.log(this.snake.body,sbPos,this.snake.head)
                }
            })
        };
    
        // update again
        setTimeout(()=>this.update(),150);
    };
    generateFoods() {
        var x = parseInt(Math.random() * this.blocks);
        var y = parseInt(Math.random() * this.blocks);
        var newFood = new Vector(x, y);
        this.foods.push(newFood);
    };
    startGame () {
        this.snake = new Snake();
        this.start = true;
        this.bgm.play();
    };
    endGame () {
        this.start = false;
        this.bgm.pause();
    };
};

如果以上理解有所出入,求求俠客都在麻煩協助提點,感謝!! ε= ᕕ( ᐛ )ᕗ


上一篇
Day14 - 結果今天只做小蛇,小蛇還不貪
下一篇
Day16- 讓頁面不再被上下左右上下左右
系列文
JavaScript 嗨起來用 JS 做動畫 ε= ᕕ( ᐛ )ᕗ17

尚未有邦友留言

立即登入留言