Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

p5.js向け 有用無用道具箱

More than 1 year has passed since last update.

■ はじめに

p5.js でスケッチを書く(描く?)のを何度か繰り返していると、なんだか同じようなコードを何度も書いてるなあ、と思う場面に遭遇します。そんなときは、再利用できそうな形でコードを書いて、また今度も使えるようにストックしておくと良いかもしれません。もし、そのストックを使う場面が再び訪れ、そのおかげで思いついたスケッチをスパッと作れたりすると、勝ったなガハハという気分になれます。1

ここでは、私がよく使ったり使わなかったりするいくつかの共通処理を、個別の記事に分けて紹介していきたいと思います。一つ一つはあまり大した内容ではありませんが……。これ使うといいよ! というよりは、こんなふうに自分で武器を揃えていくと楽しいかもしれませんよ、という提案です。

この記事はいったん目次だけですが、人によっていろいろ前提が違うと思うので、下の方にもろもろの前提知識(主に文法)についても書きました。

■ 目次

(仮)
(個別の記事はこれからです)

  • ランダム編 (2019-12-05 投稿)
  • イージング
  • タイマー
  • 配列要素の組み合わせ
  • 曲線
  • 図形のトリミング
  • ピクセル操作
  • キーボードで移動
  • 背景画像
  • 画面拡大

■ 環境

  • JavaScript
    • ES2015(後述)
  • p5.js
    • v0.10.2
    • 簡単のため、global mode(要するに普通のモード)を前提とします。
       参考: Global and instance mode · processing/p5.js Wiki
      instance mode がよく分からんという人でも大丈夫ですが、global mode がどう動くのかは知っておいたほうがいいこともあるかも(補足を追記するかもしれません)
  • ブラウザ
    • たぶんどれでも大丈夫だとは思うけど、私は Chrome で確認しています。
    • 経験上 Edge はたまに怪しい
    • あとブラウザの話じゃないけど Retina ディスプレイとかもたまに怪しい

■ 注意事項

書こうとしている内容ですが、実際のところほとんどが

  • もっとうまくやってくれるライブラリが存在する
  • そもそも p5.js が向いていないかもしれない

のどちらかな気がします(ちゃんと調べてませんがたぶん)。

よって、主な対象読者は、寿命が無限であるなどの理由で長時間の無駄を許容できる仙人か、効率を度外視して過程を楽しむことを優先する変態か、もしくはその両方です。これはすなわち合理的経済人と対を成す理想的存在であり、現実のあなたにはそれぞれ個別の事情があることでしょう。(?)

■ 前提知識

ぜんぶ分かってないとダメとかではなくて(そんなに大層なコードは出てこない)、
初めて出くわしたときにギョッとしなければいいな、という諸々です。

ググるのが面倒な人のために例と説明も添えましたが、もっと優れた解説が多々あります。
しかも動作確認してないので、なんか違ったら後で直すかも。

JavaScript の基本文法

基礎的な概念やキーワードとその使い方

変数、関数(引数・戻り値)、データ型、演算子(算術・比較・論理)、
ifforswitchnullundefined、……

オブジェクト型

これは p5.js のサンプルコードで(表面的には)あまり出てこないかも? と思ったので書きます。
var はこの連載では実際には使いません、後述の「ES2015」参照

object-example.js
// 座標オブジェクト
var coordinates = {
  x: 20,
  y: 40
};

仮にこう書いたとき、coordinates.xcoordinates.y で値を参照できます。
xy のところは変数名と同様、好きな名前を使える。

意味のある単位でひとまとめにしたいときとか、
名前空間を区切りたい(変数名とかが衝突するのを防ぎたい)ときとかに使えます。

参考: オブジェクトの基本 - JavaScript | MDN

三項演算子

条件次第で値を変えたいとき、ifよりシンプルに書けるやつです。
(条件) ? (条件がtrueのときの値) : (条件がfalseのときの値)

var flag = true;
var answer = flag ? "YES" : "NO";
// flag が true なら "YES", でなければ "NO" が結果として answer に代入される

if で書くとこうなっちゃう。2

var flag = true;
var answer;
if (flag) answer = "YES";
else answer = "NO";
// flag が true なら "YES", でなければ "NO" が結果として answer に代入される

参考: 条件 (三項) 演算子 - JavaScript | MDN

今回(たぶん)使わないもの

prototype とか this とか new とかはたぶん出ない。3

p5.js の基本

setupdraw、座標システム、色、いろいろな図形の描画、
frameCountwidthheight、その他各種変数・関数、などなど。

何度か自分で書いた経験があって、
例えばまあ、こういった基本的なサンプルコードを見たときに、何をやってるのかすぐ理解できると良いです。
Coordinates | examples | p5.js

p5.js の関数その他についてはリファレンスを見よう!
reference | p5.js

3D(WebGL)とかはたぶんやらないと思います。

高階関数

引数が別の関数だったり、戻り値が別の関数だったりするような関数のことです。

※ 説明のため、いったん function宣言で例を書きますが、
  実際にはこの連載では後述の「アロー関数」で書きます。

引数が別の関数

:arrow_down: この例では引数 doSomething がそれですが、こういうのをコールバック関数と言います。
  参考: コールバック関数 - JavaScript | MDN

callback-function.js
// doSomethingは任意の関数
function runGivenFunction(doSomething) {
  doSomething(200);
}

// 使うとき: たとえばこのような関数があったとして、
function drawCircleAtMyPlace(size) {
  circle(100, 100, size); // p5.js の circle 関数
}

// こうすると、結果的に drawCircleAtMyPlace(200); が実行されるのと同じことになる
runGivenFunction(drawCircleAtMyPlace);

返り値が別の関数

:arrow_down: ちなみにこの例では、minusOneを外から参照することはできませんが、
  coolFunctionに入れた関数が存在する限りminusOneの宣言も内部的には残り続けています。
  こういうのをクロージャと言います。 参考: クロージャ - JavaScript | MDN

return-function.js
// これの返り値は、値を入れるとそれに-1を掛け算して返してくれる関数
function getCoolFunction() {
  var minusOne = -1;

  return function(value) {
    return minusOne * value;
  };
}

// 使うとき:
var coolFunction = getCoolFunction();
var result = coolFunction(100); // この結果は -100

// 私はあまりやらないけど、getCoolFunction()(100); などのように一度に書くことも可能

配列の処理

配列を使った操作のうち、上述のコールバック関数を使うものがいくつかあり、
その中でも forEach map filter あたりは使う可能性が高いです。4

const array = [10, 20, 30];

array.forEach(someFunc); // 各要素を引数としてsomeFuncを実行
array.map(someFunc);     // 各要素を引数としてsomeFuncを実行し、それらの結果を新しい配列で返す
array.filter(someFunc);  // 各要素を引数としてsomeFuncを実行し、結果trueの要素だけ新しい配列で返す

// 例えば
array.forEach(print); // print(10); print(20); print(30); が順に実行される
array.map(sq); // sq は p5.js の関数で、数値を二乗する。こうすると結果 [100, 400, 900] が返る

function greaterThan15(value) { return value > 15; } // 15より大きければ true を返す関数
array.filter(greaterThan15); // こうすると結果として、条件に合致した要素の配列 [20, 30] が返る

参考: Array - JavaScript | MDN

ES2015

JavaScript の文法(というか仕様)は年々拡張されており、
特に「ES5」以前と「ES2015」以降とで大きく様変わりしています。
参考: ES2015 (ES6) についてのまとめ - Qiita

p5.js はそれなりに歴史があるので、ES5 以前の書き方のサンプルコードがたくさんあります。
その関係で、新たに書く場合でもあえて ES5 以前の書き方で書く、という人もいるかもしれません。
あと、ES2015 が各種ブラウザでちゃんと動作するようになったのは割と最近のことであるらしい。

そんな事情なので人によっては見慣れないかもですが、私の場合は ES2015 を取り入れて書きたいと思います。
中でもよく使うと思われるものを挙げます。

変数宣言

定数やオブジェクトや配列は基本的に const(再代入不可)、
あとで再び代入する予定の変数だけ let で宣言します。
ES2015より前は var しかありませんでした。

var とその他の厳密な違いとしては、スコープとかホイスティング(巻き上げ)とかがありますが5
そもそも var を使わなければあまり意識しないと思われます。

for...of ループ

こういうやつです。

const array = [10, 20, 30];

// 普通のfor
for (let i = 0; i < array.length; i += 1) print(array[i]);

// for...of(結果は上と同じ)
for (const element of array) print(element);

普通の forfor...of、あるいは先述の forEach、どれがいいのかは文脈次第。

参考: for...of - JavaScript | MDN

アロー関数

:arrow_down: 関数は function で定義できますが、

// [1] 引数2つの関数
function doSomethingCrazy(someString, someValue) {
  // ...
}

// [2] 引数1つで、計算した値を返す単純な関数
function calculateElegantly(value) {
  return value * 7777777;
}

// [3] 引数なしで、関数の返り値が別の新たな関数
function getCoolFunction() {
  return function(value) {
    return -value;
  };
}

:arrow_down: アロー関数という構文で、このようにも書ける。

// [1] 引数2つの関数
const doSomethingCrazy = (someString, someValue) => {
  // ...
};

// [2] このケースでは () も {} も return も省略できる
const calculateElegantly = value => value * 7777777;

// [3] 引数なしなら () => ... となる。
// value => -value の部分が返り値であり、function(value) { return -value; } に相当する
const getCoolFunction = () => value => -value;

短くすっきりしてて良いのですが、知らないと、慣れるのにちょっとかかるかもしれませんね。
参考: アロー関数 - JavaScript | MDN

あと、上述の var と似たような話(ホイスティングとか)が function にも言えますが、
基本的に function は使わず、変数でも関数でも const(たまに let )で宣言したいと思います。

[追記]
p5.js で特別な意味を持っている setup とか draw とかは例外で、function で宣言しようと思います。
グローバルに定義しないと p5.js のグローバルモードが作動しないため。
参考: ES6(ES2015)でグローバルに定義したlet,const,classはグローバルオブジェクトのプロパティにならない - Qiita

分割代入

:arrow_down: こういうオブジェクトがあったとして、

const fantasticObject = {
  greatNumber: 1984,
  awesomeString: "WTF"
};

:arrow_down: こんな感じで中身を取り出したいとき、

const greatNumber = fantasticObject.greatNumber;
const awesomeString = fantasticObject.awesomeString;

:arrow_down: こうも書ける、ということ。

const { greatNumber, awesomeString } = fantasticObject;

取り出すときに別の名前にしたり、深いところから一気に取ったりもできます。
参考: 分割代入 - JavaScript - JavaScript | MDN

デフォルト引数

引数に何も渡さなかったときのデフォルト値を決めておくやつです。

const printName = (name = "no name") => print(name);

printName("John"); // print("John"); が実行される

printName(); // print("no name"); が実行される
printName(undefined); // 同上

参考: デフォルト引数 - JavaScript | MDN

今回(たぶん)使わないもの

単なる最近の私の好みですが、ひとまず表面的には class は使う機会が少ないと思います。

モジュール機能の import export も、普段は使うけどここでは使わない。

イテレーターやジェネレーター、yieldPromise、とかは私自身も分かってない。

とりあえず

このくらいでしょうか。
習うより慣れろでもいいと思います。

▶ 目次にもどる


  1. 二度と使わず死蔵してしまうことも多いわけですが、それも経験ということで。 

  2. ifswitch が式になってくれれば言うことないんですが! var answer = if (flag) "YES" else "NO"; みたいなやつ。 

  3. prototypeとかthisとかが何なのか(どう動くのか)は知っておく価値がありますが、とりあえず今回のところは大丈夫だと思います。私も怪しいし。 

  4. 処理効率をキリキリ上げたいときはforEach系は遅いので避けると思いますが、今回そんな部分がネックになることはないので、気にしないこととします。 

  5. 私もあんまり分かってなかったんですが、var以外は巻き上げが起こらない、という単純な話でもないらしいです。参考: JavaScript - letとconstの巻き上げは起こりますか? | teratail 

FAL
個人でゲームを作ったり、プログラミングでお絵かきしたりしています。 @falworks_ja
http://www.fal-works.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away