[TypeScript][WebGL][Three.js] Boids (an artificial life program) —shoal simulator written in TypeScript—

Shos.Boids

Boids とは

Boids は、Craig Reynolds 氏によって作られた人工生命のシミュレーションだ。
Boid の名は、「Bird (鳥) oid (もどき)」から来ており、元は鳥の群れの動きを真似ている。

それぞれの個体を次に上げる 3 つの規則にしたがって動かすというだけで、群れのような動きを実現している:

Boids の 3 つの規則
分離 (Separation): 近くの仲間にぶつからない方向に移動
分離 (Separation)
分離 (Separation)
整列 (Alignment): 近くの仲間が平均的に向かっている方に向かう
整列 (Alignment)
整列 (Alignment)
集結 (Cohesion): 近くの仲間の中心方向に移動する
集結 (Cohesion)
集結 (Cohesion)

参考ページ

デモ

TypeScript などで Boids のデモを 3 種類作成してみた。

鳥の群れではなく、魚の群れを意識した。

※ 各画像をクリックするとそれぞれのページへ飛ぶ

2D版: Boids (an artificial life program) —shoal simulator written in TypeScript
Boids (an artificial life program) —shoal simulator written in TypeScript
3D版: Boids (an artificial life program) 3D —shoal simulator written in TypeScript with Three.js—
Boids (an artificial life program) 3D —shoal simulator written in TypeScript with Three.js—
3D 裸眼立体視版: Boids (an artificial life program) 3D (Cross-eyed/Parallel Free-viewing Stereoscopy) —shoal simulator written in TypeScript with Three.js—
Boids (an artificial life program) 3D (Cross-eyed/Parallel Free-viewing Stereoscopy) —shoal simulator written in TypeScript with Three.js—

2D版では2次元ベクトル、3D版では3次元ベクトルで、個体の位置や速度を計算している。

2つの3D版は、Three.js というライブラリーを使って WebGL で動かしている。

参考:

3D 裸眼立体視版は、裸眼立体視 (交差法または平行法) ができる人向けだ。
立体視の方法については、次のページなどが参考になる。

参考:

なお、各版ともパラメーターを色々と変更できるので試してほしい。

これらのデモ ページの実装については、ソースコードを参照してほしい。

ソースコード

ソースコードは、GitHub に上げた:

開発環境/言語/ライブラリー

以下を使って作った:

Visual Studio Code

上記デモは、Visual Studio Code 上で作成した。

Visual Studio Code は、プログラミングに最適な Microsoft 製のテキスト エディターで、Windows 版、macOS 版、Linux 版がある。

HTML や CSS はもちろんのこと、JavaScript や TypeScript、C# などの多種多様なプログラミング言語にも対応している。

Git クライアント、ビルド、シンタックス ハイライト、リファクタリング、インテリセンスなどの機能を持ち、とても使いやすいのでお薦めだ。

どのような機能や言語が使えるかは、Extensions for Visual Studio family of products | Visual Studio Marketplace が参考になる。

参考:

TypeScript

TypeScript は、設定を変えることにより、”ES3″、”ES5″、”ES2015″、”ES2016″、”ES2017″、”ES2018″、”ES2019″ にコンパイルすることができる。

デモでは、”ES5″ を使っている。

TypeScript のコンパイル オプションは、”tsconfig.json”というファイルで行う。
今回は次のような内容とした。

tsconfig.json


{
  "compilerOptions": {
    /* Basic Options */
    "target": "ES5",        /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
    "module": "commonjs",   /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    "sourceMap": true,      /* Generates corresponding '.map' file. */

    /* Strict Type-Checking Options */
    "strict": true,         /* Enable all strict type-checking options. */

    /* Module Resolution Options */
    "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
  }
}

TypeScript は、Visual Studio Code から簡単に JavaScript にコンパイルできる。

Visual Studio Code でのコンパイル
Visual Studio Code でのコンパイル

ちなみに、次のような TypeScript の場合:

class Vector3D {
    x: number;
    y: number;
    z: number;

    get absoluteValue(): number {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
    }

    constructor(x: number = 0, y: number = 0, z: number = 0) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    clone(): Vector3D {
        return new Vector3D(this.x, this.y, this.z);
    }

    plus(another: Vector3D): Vector3D {
        return new Vector3D(this.x + another.x, this.y + another.y, this.z + another.z);
    }

    plusEqual(another: Vector3D): void {
        this.x += another.x;
        this.y += another.y;
        this.z += another.z;
    }

    minus(another: Vector3D): Vector3D {
        return new Vector3D(this.x - another.x, this.y - another.y, this.z - another.z);
    }

    minusEqual(another: Vector3D): void {
        this.x -= another.x;
        this.y -= another.y;
        this.z -= another.z;
    }

    multiply(value: number): Vector3D {
        return new Vector3D(this.x * value, this.y * value, this.z * value);
    }

    innerProduct(another: Vector3D): Vector3D {
        return new Vector3D(this.x * another.x, this.y * another.y, this.z * another.z);
    }

    divideBy(value: number): Vector3D {
        return new Vector3D(this.x / value, this.y / value, this.z / value);
    }

    divideByEqual(value: number): void {
        this.x /= value;
        this.y /= value;
        this.z /= value;
    }

    getDistance(another: Vector3D): number {
        return this.minus(another).absoluteValue;
    }
}

“ES5” でコンパイルしてできた JavaScript はこんな感じ:

"use strict";
var Vector3D = /** @class */ (function () {
    function Vector3D(x, y, z) {
        if (x === void 0) { x = 0; }
        if (y === void 0) { y = 0; }
        if (z === void 0) { z = 0; }
        this.x = x;
        this.y = y;
        this.z = z;
    }
    Object.defineProperty(Vector3D.prototype, "absoluteValue", {
        get: function () {
            return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
        },
        enumerable: true,
        configurable: true
    });
    Vector3D.prototype.clone = function () {
        return new Vector3D(this.x, this.y, this.z);
    };
    Vector3D.prototype.plus = function (another) {
        return new Vector3D(this.x + another.x, this.y + another.y, this.z + another.z);
    };
    Vector3D.prototype.plusEqual = function (another) {
        this.x += another.x;
        this.y += another.y;
        this.z += another.z;
    };
    Vector3D.prototype.minus = function (another) {
        return new Vector3D(this.x - another.x, this.y - another.y, this.z - another.z);
    };
    Vector3D.prototype.minusEqual = function (another) {
        this.x -= another.x;
        this.y -= another.y;
        this.z -= another.z;
    };
    Vector3D.prototype.multiply = function (value) {
        return new Vector3D(this.x * value, this.y * value, this.z * value);
    };
    Vector3D.prototype.innerProduct = function (another) {
        return new Vector3D(this.x * another.x, this.y * another.y, this.z * another.z);
    };
    Vector3D.prototype.divideBy = function (value) {
        return new Vector3D(this.x / value, this.y / value, this.z / value);
    };
    Vector3D.prototype.divideByEqual = function (value) {
        this.x /= value;
        this.y /= value;
        this.z /= value;
    };
    Vector3D.prototype.getDistance = function (another) {
        return this.minus(another).absoluteValue;
    };
    return Vector3D;
}());

class がないので、prototype と function での実装だ。

一方、”ES2019″ でコンパイルしてできた JavaScript はこんな感じ:

"use strict";
class Vector3D {
    constructor(x = 0, y = 0, z = 0) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
    get absoluteValue() {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
    }
    clone() {
        return new Vector3D(this.x, this.y, this.z);
    }
    plus(another) {
        return new Vector3D(this.x + another.x, this.y + another.y, this.z + another.z);
    }
    plusEqual(another) {
        this.x += another.x;
        this.y += another.y;
        this.z += another.z;
    }
    minus(another) {
        return new Vector3D(this.x - another.x, this.y - another.y, this.z - another.z);
    }
    minusEqual(another) {
        this.x -= another.x;
        this.y -= another.y;
        this.z -= another.z;
    }
    multiply(value) {
        return new Vector3D(this.x * value, this.y * value, this.z * value);
    }
    innerProduct(another) {
        return new Vector3D(this.x * another.x, this.y * another.y, this.z * another.z);
    }
    divideBy(value) {
        return new Vector3D(this.x / value, this.y / value, this.z / value);
    }
    divideByEqual(value) {
        this.x /= value;
        this.y /= value;
        this.z /= value;
    }
    getDistance(another) {
        return this.minus(another).absoluteValue;
    }
}

こちらは class があるので TypeScript のコードとあまり変わらない。”ES5″ とは、まるで別の言語のように見えるのが面白い。