コンテンツにスキップ

命令をまとめてプログラムを整理する

この章では自分で関数を作りプログラムを構造化する方法を学びます。 プログラムの構造化とは、一連の処理を意味のあるまとまりごとに関数として分け整理することです。 関数を作ってプログラムを構造化すると、同じことを何度も書かなくてよくなります。 そのため、より短い記述で見通しのよいプログラムを書くことができるようになります。

関数の作り方と使い方

これまで私たちはp5.jsに用意されているcreateCamvasrectprintなどの関数を使ってプログラムを作ってきました。 はじめから用意されている関数はとても基本的なものばかりですが、一連の処理をまとめて一つのより複雑な関数を定義することができます。 次のプログラムでは、二つの異なる"顔を指定した座標に描く"という意味のある一連の処理をまとめてdrawRoundFace, drawAngularFace という関数として切り出しています。

[ 例5-1. 関数を作る ]

function setup() {
    createCanvas(windowWidth, windowHeight);
}

function draw() {
    background(100);
    drawRoundFace(400, 100); // x = 400, y = 100 に丸い顔を描く
    drawAngularFace(50, 50); // x = 50, y = 50 に四角い顔を描く
}

function drawRoundFace(x, y) {
    noStroke();

    fill(249,205,173);//rosy beige
    circle(x, y, 100);

    //Eye 1
    fill(30);//dark gray
    circle(x, y+10, 10);

    //Eye 2
    circle(x+20, y+10, 10);

    //Mouth
    fill(252,157,154);//light pink
    arc(x, y+25, 30, 30, 0, radians(180), PIE);
}

function drawAngularFace(x,y) {
    strokeWeight(5);  //line weight
    stroke(50, 200, 50);  //line color RGB
    fill(255);

    rect( x, y, 150, 125);
    rect( x+25, y+50, 15, 15);//left eye
    rect( x+110, y+50, 15, 15);//right eye
    rect( x+40, y+90, 75, 10);//mouth
}

arc は円弧や扇形を描きます。 https://p5js.org/reference/#/p5/arc

また多くの場合、角度を与えるときには弧度法(ラジアン)での数値を使います。 radians(degree) で度数法(°)から変換することができます。

関数を作るときには、関数名引数処理の内容を以下の形式にあてはめて定義します。 引数は括弧 () の中に好きな個数だけ定義することができ、呼び出し時の入力を表す変数としてブロック内で自由に使うことができます。

function 関数名 (引数1, 引数2, 引数3, ...){
    処理...
}

関数を作る意味

ここで、上の例5-1のdrawのブロックを見てみましょう。 drawの中では1. 背景を塗りつぶす, 2. 丸い顔を描く, 3.四角い顔を描く という3段階の処理があることを簡単に読み取ることができるでしょう。 もちろん次に示すコードのように関数を使わずにすべての図形をdrawブロック内に書いても同じ絵を作ることができます。 しかし、構造化をしない書き方ではどこが丸い顔を描く処理でどこが四角い顔を描く処理かを読み取ることが難しいです。 一方で処理を関数に分割すると、 draw の処理を考えているときは顔をどこに配置するかだけを考えればよく、それぞれの顔のパーツをどうするかという細かいことを気にする必要がなくなります。 そして関数の中身は別の時にゆっくり考えればよいので、問題を把握することが楽になります。 処理に関する思考を適切に切り分けてプログラムを分かりやすくすることが関数を作る一つの意味です。

[構造化をしないと処理の意味がわかりにくい]

function setup() {
    createCanvas(windowWidth, windowHeight);
}

function draw() {
    background(100);

    noStroke();

    fill(249,205,173);//rosy beige
    circle(400, 100, 100);

    //Eye 1
    fill(30);//dark gray
    circle(400, 110, 10);

    //Eye 2
    circle(420, 110, 10);

    //Mouth
    fill(252,157,154);//light pink
    arc(400, 125, 30, 30, 0, radians(180), PIE);

    strokeWeight(5);  //line weight
    stroke(50, 200, 50);  //line color RGB
    fill(255);

    rect( 50, 50, 150, 125);
    rect( 75, 100, 15, 15);//left eye
    rect( 160, 100, 15, 15);//right eye
    rect( 90, 140, 75, 10);//mouth
}

また、引数を変えて同じ処理を実行することが簡単にできます。 次の例はfor文を使いたくさんの顔を描くプログラムです。(ブラウザの横幅によっては見切れるかもしれません)

function setup() {
    createCanvas(windowWidth, windowHeight);
}

function draw() {
    background(100);

    for(let i=0; i<5; i++){
        drawRoundFace(i*160+125, 100);
        drawAngularFace(i*160+50, 200);
    }
}

function drawRoundFace(x, y) {
    noStroke();

    fill(249,205,173);//rosy beige
    circle(x, y, 100);

    //Eye 1
    fill(30);//dark gray
    circle(x, y+10, 10);

    //Eye 2
    circle(x+20, y+10, 10);

    //Mouth
    fill(252,157,154);//light pink
    arc(x, y+25, 30, 30, 0, radians(180), PIE);
}

function drawAngularFace(x,y) {
    strokeWeight(5);  //line weight
    stroke(50, 200, 50);  //line color RGB
    fill(255);

    rect( x, y, 150, 125);
    rect( x+25, y+50, 15, 15);//left eye
    rect( x+110, y+50, 15, 15);//right eye
    rect( x+40, y+90, 75, 10);//mouth
}

返り値のある関数

数値計算のように、関数になにか処理をさせてその結果を受け取ることもできます。 これまで扱った中だと、 let c = color(200, 50, 50) のように color 関数で色の情報を作って変数 c に代入するというような場面でこの種類の関数が登場しました。 結果を関数の呼び出し元に返すには、関数の中でreturnを使います。 注意点として、一つの関数からは一つの変数しか返すことができないという決まりがあります。

function setup() {
    createCanvas(windowWidth, windowHeight);
    background(50);
    noStroke();
}

function draw() {
    let c = createRandomColor(); // 関数から色の情報を受け取る
    fill(c);
    circle(random(windowWidth), random(windowHeight), 30);
}

function createRandomColor() {
    // ランダムな色を作って返す関数
    colorMode(HSB);

    let h = random(360); // 0° - 360° 全ての色相
    let s = random(60); // 彩度 0% - 60% 薄い色
    let b = 80 + random(20); // 明度 80% - 100% 明るい色

    return color(h, s, b); // return で結果を返す
}

練習問題

練習1

指定した座標に指定した色で顔を描画する関数 face(x, y, c) を作ってください。 ただし、引数の意味は x, y がそれぞれ x 座標と y 座標, c が色とします。 また作った face 関数を下のプログラムの中で使用し、ランダムな色の顔が並んだ画像をつくってください。 以下の雛形を使用し、face の部分だけ作ってもよいです。

function setup() {
    createCanvas(windowWidth, windowHeight);
    noLoop(); // call draw once
}

function draw() {
    background(100);

    for(let i=0; i<5; i++){
        c = createRandomColor();
        face(i*160 + 50, 50, c);
    }
}

// define `face` here
function face ...

function createRandomColor() {
    // ランダムな色を作って返す関数
    colorMode(HSB);

    let h = random(360);
    let s = random(60);
    let b = 80 + random(20);

    return color(h, s, b);
}

練習2

4つの色を入力すると、その中からランダムに一つの色を選んで出力する関数chooseRandomColor(c1, c2, c3, c4)を作ってください。 乱数(random())と 条件分岐の組み合わせで実現できます。 できたら、次のプログラムで関数を使って動作を確認してください。

function setup() {
    createCanvas(windowWidth, windowHeight);
    background(100);
    noLoop();
}

function draw() {
    background(100);

    let color_1 = color(240, 80, 80);
    let color_2 = color( 80,230, 80);
    let color_3 = color( 80, 80,240);
    let color_4 = color(240,240, 80);

    for(let i=0; i<10; i++){
        let color = chooseRandomColor(color_1, color_2, color_3, color_4);
        fill(color);
        rect(i*windowWidth/10, windowHeight/2, 40, 40);
    }
}

function chooseRandomColor(c1, c2, c3, c4){
    // この中を考えてください。
}