《Learn JavaScript with p5.js》的讀書小結

Learn JavaScript with p5.js

此書提到的JavaScript語法沒有甚麼好寫的,太簡單,還沒有使用ES6標準,網上有一大堆更好的總結和cheat sheets,而p5.js的語法直接看官網即可。所以,本文只是在此書的最後project中加些中文注釋。

p5.js是一個JavaScript圖形庫,可畫出多種不同的圖形(和文字),為其設置各種動態效果,十分簡單易用。通過此書,可以快速地入門JavaScript和p5.js,幾天時間就能夠讀完。

p5.js的基本架構是setup和draw函數:

function setup() {
    createCanvas(800, 300);
}

function draw() {
    background(0);
}

Final project

// 全局變量,讓p5.js中的setup和draw函數也能使用這些變量
var guessItem = null;
var interval = 60;
var results = [];
var solution = null;
var gameOver = false;

// p5.js中的基本函數,只運行一次,無需主動調用
function setup() {
    // 設置圖的大小
    createCanvas(800, 300);
}

// p5.js中的基本函數,會根據frameRate(默認60)不斷反覆運行
// frameCount會自動累積,每過一frame加一
function draw() {
    var gameScore = getGameScore(results);
    if (gameScore.loss === 3 || gameScore.total === 10) {
        gameOver = true;
        displayGameOver(gameScore);
        return;
    }

    // 設定背景顏色為0, 0, 0,即黑色
    background(0);
    // frameCount % interval會循環得出0, 1, 2, ..., interval - 1的值
    if (frameCount === 1 || frameCount % interval === 0) {
        solution = null;
        // 根據定義建立一個新的GuessItem object
        guessItem = new GuessItem(width/2, height/2, 1);
    }

    if (guessItem) {
        guessItem.render();
    }

    if (solution === true || solution === false) {
        solutionMessage(gameScore.total, solution);
    }

}

function solutionMessage(seed, solution) {
    var trueMessages = [
        'GOOD JOB!',
        'DOING GREAT!',
        'OMG',
        'SUCH WIN!',
        'I APPRECIATE YOU',
        'IMPRESSIVE'
    ];

    var falseMessages = [
        'OH NO!',
        'BETTER LUCK NEXT TIME!',
        'PFTTTT',
        ':('
    ];

    var messages;

    push();
    textAlign(CENTER, CENTER);
    fill(237, 34, 93);
    // 文字大小
    textSize(36);
    randomSeed(seed * 10000);

    if (solution === true) {
        background(255);
        messages = trueMessages;
    } else if (solution === false) {
        background(0);
        messages = falseMessages;
    }

    // parseInt將浮點數轉為整數,做到去除小數點的效果
    text(messages[parseInt(random(messages.length), 10)], width/2, height/2);
    pop();
}

function displayGameOver(score) {
    push();
    background(255);
    textSize(24);
    textAlign(CENTER, CENTER);
    translate(width/2, height/2);
    fill(237, 34, 93);
    text('GAME OVER!', 0, 0);
    translate(0, 36);
    fill(0);
    text('You have ' + score.win + ' correct guesses', 0, 0);
    translate(0, 100);
    textSize(16);
    // sin函數得出-1~1之間的值,map函數將這個值由-1~1的區間變成0~255的區間
    var alternatingValue = map(sin(frameCount / 10), -1, 1, 0, 255);
    fill(237, 34, 93, alternatingValue);
    text('PRESS ENTER', 0, 0);
    pop();
}

function getGameScore(score) {
    var wins = 0;
    var losses = 0;
    var total = score.length;

    for (var i = 0; i < total; i++) {
        var item = score[i];
        if (item === true) {
            wins = wins + 1;
        } else {
            losses = losses + 1;
        }
    }

    return {'win': wins, 'loss': losses, 'total': total};
}

function restartTheGame() {
    results = [];
    solution = null;
    gameOver = false;
}

// 用於檢測鍵盤按鍵event的函數,不用主動調用
function keyPressed() {
    if (gameOver === true) {
        // 如果按的鍵是Enter
        if (keyCode === ENTER) {
            console.log('restart the game');
            restartTheGame();
            return;
        }
    }

    if (guessItem != null) {
        // key儲存是所按鍵的值(一個string)
        console.log('you pressed: ', key);
        solution = guessItem.solve(key);
        console.log(solution);
        if (solution) {
            // 加入一個值到results array
            results.push(true);
        } else {
            results.push(false);
        }
        guessItem = null;
    } else {
        console.log('nothing to be solved');
    }
}

// 通過constructor function定義JavaScript中的一個object
function GuessItem(x, y, scl) {
    this.x = x;
    this.y = y;
    this.scale = scl;
    this.scaleIncrement = 0.25;
    this.clr = 255;
    this.content = getContent();
    this.alpha = 255;
    this.alphaDecrement = 3;
    this.solved = null;
    this.contentMap = {
        '1': 'one',
        '2': 'two',
        '3': 'three',
        '4': 'four',
        '5': 'five',
        '6': 'six',
        '7': 'seven',
        '8': 'eight',
        '9': 'nine',
        '0': 'zero'
    };
    this.colors = [
        [63, 184, 175],
        [127, 199, 175],
        [218, 216, 167],
        [255, 158, 157],
        [255, 61, 127],
        [55, 191, 211],
        [159, 223, 82],
        [234, 209, 43],
        [250, 69, 8],
        [194, 13, 0]
    ];

    // object中的一個普通函數
    function getContent() {
        // 將整數變為string
        return String(parseInt(random(10), 10));
    }

    // object中的method
    this.solve = function(input) {
        if (input === this.content) {
            solved = true;
        } else {
            solved = false;
        }
        this.solved = solved;
        return solved;
    }

    this.drawEllipse = function(size, strkWeight, speedMultiplier, seed) {
        // push()和pop()同時出現,其中做的格式設置和變換不影響外界的格式設置和變換
        push();
        randomSeed(seed);
        translate(this.x, this.y);
        var ellipseSize = this.scale * speedMultiplier;
        scale(ellipseSize);
        var clr = this.colors[parseInt(random(this.colors.length), 10)];
        // 線條顏色
        stroke(clr);
        // 不填充
        noFill();
        // 線條粗幼
        strokeWeight(strkWeight);
        // 畫圓或橢圓
        ellipse(0, 0, size, size);
        pop();
    }

    this.render = function () {
        push();
        this.drawEllipse(100, 15, 2, 1 * this.content * 1000);
        this.drawEllipse(60, 7, 2, 1 * this.content * 2000);
        this.drawEllipse(35, 3, 1.2, 1 * this.content * 3000);
        pop();

        push();
        // 對文字填色,最後一個參數是其透明度
        fill(this.clr, this.alpha);
        // 坐標點處為文字中央
        textAlign(CENTER, CENTER);
        // 平移,文字的坐標點不再在左上角
        translate(this.x, this.y);
        // 縮放
        scale(this.scale);
        text(this.contentMap[this.content], 0, 0)
        this.scale = this.scale + this.scaleIncrement;
        this.alpha = this.alpha - this.alphaDecrement;
        pop();
    }
}
5 1 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments