p5.js①・図形描画とイベント処理
p5.jsとは
p5.jsは,芸術家やデザイナーのためのオープンソースのJavaScriptライブラリです. ウェブページに,キャンバスを設置し,図形やアニメーションを描画することができます. Processingと同等の機能を備えていることから,Processing経験者には学習コストが低く,プログラミングの初学者にも適したライブラリです. ここでは,vue-p5を利用することで,Vue.jsとp5.jsを共存させながら,「3目並べ」のWebアプリを開発することを目指します.
CodePenの準備
CodePenにアクセスして,Penを作成し,タイトルを設定しましょう. Penのタイトルは「Chapter11」に設定しましょう.
前回と同様にVue.jsのライブラリをペンに追加してください. Vue.jsのバージョンは2.6.11を採用します.
次に,Vue.jsとp5.jsを共存させるためのvue-p5を導入します.
検索してもCDNがリストアップされないので,https://unpkg.com/vue-p5
を直接入力します.
vue-p5
vue-p5を利用することで,Vueクラスのメソッドとしてシンプルにp5.jsを記述することができます.
HTMLは次のように記述します.
vue-p5
タグで,p5.jsの実装に必要な関数setup()
とdraw()
を宣言します.
次回利用するマウスの移動を検出するmouseMoved()
なども,必要があれば追加で宣言します.
<!-- HTML -->
<div id="app">
<h1>{{ title }}</h1>
<vue-p5
@setup="setup"
@draw="draw">
</vue-p5>
</div>
JavaScriptでは,Vueクラスを定義します.
このとき,methods
オプションに,上記で宣言したsetup()
とdraw()
を記述します.
これらの関数の引数であるsketch
を利用して,p5.jsの関数を呼び出します.
例えば,キャンバスの大きさを設定する関数はsketch.createCanvas(width, height)
,また,キャンバスの背景色を設定する関数はsketch.background(color)
のように記述します.
キャンバスの原点(0,0)
は左上であり,右方向にX軸,下方向にY軸が伸びています.
// JavaScript
new Vue({
el: '#app',
data: {
title: "3目並べ",
},
methods: {
setup(sketch){
sketch.createCanvas(500, 500); // キャンバスの大きさ
sketch.background("black"); // キャンバスの背景色
},
draw(sketch){
}
}
});
p5.js
における関数の実行順序に関して確認しておきましょう.
最初にsetup()
が1度だけ実行されます.
setup()には,キャンバスの大きさや背景色など,プログラム全体に関する設定を記述します.
その後で,draw()
が一定間隔で繰り返し実行されます.
draw()
には,図形・文字・画像など,キャンバスに表示する要素を記述します.
繰り返しごとに,位置を少し変えることで,パラパラ漫画と同じ仕組みでアニメーションを表現できます.
ファイルの読込など,プログラムの実行前に完了すべき操作はpreload()
に記述します.
イベント検出は,それぞれに関数が用意されており,例えばマウスの移動を検出するにはmouseMoved()
を定義します.
キーポイント
- vue-p5タグでp5.jsの関数を登録
- methodsオプションで関数を実装
- setup()は1回だけ実行
- draw()は繰り返し実行
図形描画
3目並べの盤を四角形で表現します.
盤の大きさは,幅と高さが同じ450とします.
四角形を描く関数はrect(x, y, width, height)
です.
引数のx
,y
は四角形の左上の座標,width
は幅,height
は高さです.
また,fill(color)
は,図形の塗りつぶしの色を設定します.
draw(sketch){
sketch.fill("green");
sketch.rect(25, 25, 450, 450);
}
3目並べのマス目を直線で表現します.
直線を描く関数はline(x1, y1, x2, y2)
です.
引数の(x1, y1)
から,(x2, y2)
の間に直線が描かれます.
また,strokeWeight(weight)
は直線の幅を設定します.
draw(sketch){
sketch.fill("green");
sketch.rect(25, 25, 450, 450);
sketch.stroke("black");
sketch.strokeWeight(5);
sketch.line(175, 0, 175, 500);
sketch.line(325, 0, 325, 500);
sketch.line(0, 175, 500, 175);
sketch.line(0, 325, 500, 325);
}
キーポイント
- 直線はline()
- 四角形はrect()
- 線の色はstroke()
- 線の太さはstrokeWeight()
- 塗りつぶしの色はfill()
盤の状態の表現
盤上のマス目をクリックすると,黒(先手)または白(後手)の石を置きます.
石を置く位置はマス目の中心とするため,先に中心の座標を計算しておきます.
data
オプションで,center_list
という名前で配列を定義します.
data: {
title: "3目並べ",
center_list: [],
}
マス目の中心座標は一度だけ求めれば良いため,setup()
で次のように算出します.
ここで,i
は行番号,j
は列番号を表しています.
center_list
の配列の要素として,座標[center_x,center_y]
を配列として追加しています(配列の入れ子構造になっていることに注意).
setup(sketch){
sketch.createCanvas(500, 500); // キャンバスの大きさ
sketch.background("black"); // キャンバスの背景色
for(let i=0; i<3; i++){
for(let j=0; j<3; j++){
let center_x = 100 + 150 * j;
let center_y = 100 + 150 * i;
this.center_list.push([center_x, center_y]);
// コンソールに出力
console.log(`${center_x},${center_y}`);
}
}
}
算出したマス目の中心座標は次のようになります.
"100,100"
"250,100"
"400,100"
"100,250"
"250,250"
"400,250"
"100,400"
"250,400"
"400,400"
次にマス目の状態を表すために,
data
オプションでstate_list
という名前の配列を定義します.
各要素はマス目の状態を表しており,
0
は石が置かれていない状態,1
は黒の石が置かれた状態,2
は白の石が置かれた状態を表します.
data: {
title: "3目並べ",
center_list: [],
state_list: [0, 0, 0, 0, 0, 0, 0, 0, 0],
}
上記で定義したstate_list
を利用して,盤上に石を円として描きます.
円を描く関数はcircle(x, y, diameter)
です.
引数のx
,y
は円の中心の座標,diameter
は直径です.
状態が1のときは黒色,状態が2のときは白色で円を描きます.
draw(sketch){
sketch.fill("green");
sketch.rect(25, 25, 450, 450);
sketch.stroke("black");
sketch.strokeWeight(5);
sketch.line(175, 0, 175, 500);
sketch.line(325, 0, 325, 500);
sketch.line(0, 175, 500, 175);
sketch.line(0, 325, 500, 325);
for(let i=0; i<this.state_list.length; i++){
let x = this.center_list[i][0];
let y = this.center_list[i][1];
// 黒の石(円)を描く
if(this.state_list[i] == 1){
sketch.fill("black");
sketch.noStroke(); // 線は描かない
sketch.circle(x, y, 140);
}
// 白の石(円)を描く
else if(this.state_list[i] == 2){
sketch.fill("white");
sketch.noStroke(); // 線は描かない
sketch.circle(x, y, 140);
}
}
},
state_list
を任意に変更して,盤面に石が配置されることを確認しましょう.
確認が終わったら,全ての状態を0
に戻しておきましょう.
state_list: [0, 0, 1, 0, 2, 0, 0, 1, 0]
キーポイント
- 円はcircle()
イベントの処理
マウスをクリックした位置に応じて石を配置します.
マウスのボタンが押されたことを検知するにはmousePressed()
を利用します.
HTMLのvue-p5
タグで,mousePressed()
を利用することを宣言します.
<!-- HTML -->
<div id="app">
<h1>{{ title }}</h1>
<vue-p5
@setup="setup"
@draw="draw"
@mousePressed="mousePressed">
</vue-p5>
</div>
methods
オプションで,mousePressed()
を定義します.
このとき,引数にはsketch
を設定します.
先に算出していた「マス目の中心座標」と「マウスがクリックされた座標」の距離(マンハッタン距離を採用)を計算します.
マウスがクリックされた座標はmouseX
とmouseY
で参照できます.
距離が最短となるマス目の状態を1
に設定します.
ここで,...
は配列を展開して引数として利用するための演算子です.
mousePressed(sketch){
// マス目の中心座標までの距離を算出
let distance_list = [];
for(let i=0; i<this.center_list.length; i++){
let x = this.center_list[i][0];
let y = this.center_list[i][1];
let distance = Math.abs(x - sketch.mouseX) + Math.abs(y - sketch.mouseY);
distance_list.push(distance);
}
// 距離が最短となるマス目の番号
let min_index = distance_list.indexOf(Math.min(...distance_list));
// 状態の変更
if(this.state_list[min_index] == 0){
this.state_list[min_index] = 1;
}
}
実行すると,マウスをクリックした位置に黒い石が置かれることが確認できます.
先手は黒の石,後手は白の石を置くように修正しましょう.
data
オプションに,打ち手の順番を表すturn
を宣言し,“black"を代入します.
data: {
title: "3目並べ",
center_list: [],
state_list: [0, 0, 0, 0, 0, 0, 0, 0, 0],
turn: "black",
}
上記のturn
がblack
のときは状態を1に変更し,turn
がwhite
のときは状態を2に変更します.
これで,先手は黒色で円が描かれ,後手は白色で円が描かれます.
mousePressed(sketch){
let distance_list = [];
for(let i=0; i<this.center_list.length; i++){
let x = this.center_list[i][0];
let y = this.center_list[i][1];
let distance = Math.abs(x - sketch.mouseX) + Math.abs(y - sketch.mouseY);
distance_list.push(distance);
}
let min_index = distance_list.indexOf(Math.min(...distance_list));
// 状態の変更
if(this.state_list[min_index] == 0){
if(this.turn == "black"){
this.state_list[min_index] = 1;
this.turn = "white"; // 順番を白に変更
}
else if(this.turn == "white"){
this.state_list[min_index] = 2;
this.turn = "black"; // 順番を黒に変更
}
}
}
キーポイント
- マウスのボタンが押されたことを検出するにはmousePressed()
- マウスの位置はmouseXとmouseY
アプリの確認
See the Pen chapter11 by Naoto Mukai (@nmukai) on CodePen.
課題
次の図を参考に,勝利したときは円の色を金色に変更してください.
課題を完成させたら,Penの ZIPファイル と リンク を提出してください. 提出方法は初回のWebアプリの開発を参考にしてください.