Help us understand the problem. What is going on with this article?

phina.jsとProtonでパーティクルしてみる。その1「Protonの使い方」

More than 1 year has passed since last update.

はじめに

phina.jsでゲームを作ろうとするとエフェクトが欲しくなりますよね。
「図形を組み合わせて作ることもできるけど、パーティクルを使いたい!」
そこで見つけたのが「Proton」というパーティクルエンジンです。

Protonとは?

Proton is a lightweight and powerful javascript particle engine.
With it you can easily create countless cool effects.

Protonは軽くて強いJavaScript製パーティクルエンジン!
簡単にクールなエフェクトが沢山作れるよ!

  • Seven kinds of renderers
    • canvas - CanvasRenderer
    • dom - DomRenderer
    • webgl - WebGLRenderer
    • pixel - PixelRenderer
    • easeljs - EaselRenderer
    • pixi.js - PixiRenderer
    • custom - CustomRenderer

7種類のレンダラーがあるよ!

  • Integratable into any game engine.

どのゲームエンジンでも使えるよ!

proton.min.jsのファイルサイズを確認してみると64.6KB!確かに軽いです。
なんだかphina.jsとも仲良くしてくれそうなので、とりあえず試してみます。

サンプルを動かす

このページに書いてあるコードを写してphina.jsで動くようにしてみます。

main.js
var SCREEN_W = 640;
var SCREEN_H = 640;

phina.globalize();

phina.define('MainScene', {
  superClass: 'DisplayScene',
  init: function(options) {
    this.superInit(options);
    this.backgroundColor = 'black';

    var plainElement = PlainElement({
      width: SCREEN_W,
      height: SCREEN_H
    }).setPosition(SCREEN_W/2, SCREEN_H/2).addChildTo(this);

    // plainElementからcanvasを取得する
    var canvas = plainElement.canvas.domElement;

    // Proton本体を作る
    var proton = new Proton();
    // エミッター(パーティクルの発生源)を作る
    var emitter = new Proton.Emitter();
    // rateを設定する
    emitter.rate = new Proton.Rate(Proton.getSpan(10, 20), 0.1);
    // Initializeを追加する
    emitter.addInitialize(new Proton.Radius(1, 22));
    emitter.addInitialize(new Proton.Life(2, 4));
    emitter.addInitialize(new Proton.Velocity(2, Proton.getSpan(0, 360), 'polar'));
    // Behaviourを追加する
    emitter.addBehaviour(new Proton.Color('ff0000', 'random'));
    emitter.addBehaviour(new Proton.Alpha(1, 0));
    // エミッターの座標を設定する
    emitter.p.x = canvas.width / 2;
    emitter.p.y = canvas.height / 2;
    emitter.emit();
    // Proton本体にエミッターを追加する
    proton.addEmitter(emitter);
    // canvas rendererを追加する
    var renderer = new Proton.CanvasRenderer(canvas);
    proton.addRenderer(renderer);

    this.on('enterframe', function(e) {
      // 毎フレーム proton.update()を呼んであげる
      proton.update();
    });
  }
});

phina.main(function() {
  var app = GameApp({
    startLabel: 'main',
    width: SCREEN_W,
    height: SCREEN_H,
    fps: 60
  });
  app.run();
});

get-started
こんな感じに動きました。

どうやらエミッターにInitializeとBehaviourを追加してパーティクルに動きを加えていくようです。
チュートリアルが用意されていなかったので、試しながら使い方をまとめていきます。

Protonの使い方

初期設定する「Initialize」たち。

new Proton.Body(image, w, h)

パーティクル本体の画像を指定します。
引数のimageは画像のURLかImageオブジェクト。whは省略可能です。
画像の指定が無かった場合、普通の円形のパーティクルになります。

new Proton.B()と省略できます。

ex.
var image = AssetManager.get('image', 'particle').domElement;

var bodyEx1 = new Proton.Body(image, 100, 100);
var bodyEx2 = new Proton.B('./image/particle.png');

new Proton.Life(a, b, c)

パーティクルの生存時間を指定します。
abに数を指定した場合はa~bのからランダムな値(小数含む)になります。
cは省略可能ですがtrueを指定すると、ランダムの範囲がa-b~a+bに変わります。
aのみに数を指定するとaの値固定になり、配列を指定するとその中からランダムにピックアップされます。

new Proton.L()と省略できます。

ex.
var lifeEx1 = new Proton.Life(1, 4);       // 1~4 からランダム
var lifeEx2 = new Proton.Life(3, 1, true); // 2~4 からランダム
var lifeEx3 = new Proton.Life(2);          // 2 固定
var lifeEx4 = new Proton.L([1, 3, 5]);     // 1 か 3 か 5

new Proton.Mass(a, b, c)

パーティクルの質量を指定します。
指定方法は上記のProton.Lifeと同じです。

new Proton.M()と省略できます。

new Proton.Position(zone)

パーティクルを発生させる範囲をZoneで指定します。
Zoneについては「発生・行動範囲を決める「Zone」たち。」で説明します。

new Proton.P()と省略できます。

new Proton.Radius(a, b, c)

パーティクルの半径を指定します。
指定方法は上記のProton.Lifeと同じです。

new Proton.R()と省略できます。

new Proton.Rate(numpan, timepan)

パーティクルを1度に発生させる個数と、発生の間隔を指定します。
numpantimepanはそれぞれProton.Lifeのように指定します。ただ少しだけ方法が違い、Proton.getSpan(a, b, c)というものを使って指定します。具体的にはこんな感じです。

ex.
// 発生個数: 10~20, 発生間隔: 0.1~0.25
var rateEx1 = new Proton.Rate(Proton.getSpan(10, 20), Proton.getSpan(.1, .25));

// 発生個数: 4 or 8 or 16 or 32, 発生間隔: 0.5
var rateEx2 = new Proton.Rate([4, 8, 16, 32], .5);

// 発生個数: 5~15, 発生間隔: 0.2
var rateEx3 = new Proton.Rate(new Proton.Span(10, 5, true), .2);

emitter.rate = rateEx1;

この子だけ特殊で、エミッターのaddInitialize()ではなくrateプロパティに渡してあげます。

rateEx2のように数や配列で指定したり、rateEx3のようにProton.getSpan()ではなくnew Proton.Span()を使っても同じ表現ができます。

また、Proton.LifeなどもこのSpanを利用できます。

ex.
var lifeEx5 = new Proton.Life(Proton.getSpan(2, 4));
var lifeEx6 = new Proton.Life(new Proton.Span(1, 3));

new Proton.Velocity(rpan, thapan, type)

パーティクルの発生方向や強さを指定します。
指定方法は2種類あります。
1つ目はnew Proton.Velocity(x速度, y速度)
x速度とy速度をそれぞれ数か配列かSpanで指定します。

2つ目はnew Proton.Velocity(速度, 角度, 'polar')
速度と角度をそれぞれ数か配列かSpanで指定します。
'polar'ではなく'p''P'と省略できます。

velocityイメージ
1つ目はxとyの速度を直接指定するのに対して、2つ目は放射状にパーティクルを発生させます。※画像上のコードは略しています。
個人的には後者の方が馴染みがあるので好きです。

new Proton.V()と省略できます。

ex.
var velocityEx1 = new Proton.Velocity(2, 2);
var velocityEx2 = new Proton.Velocity(2, Proton.getSpan(0, 360), 'polar');
var velocityEx3 = new Proton.V(Proton.getSpan(1, 3), Proton.getSpan(-45, 45), 'p');

動きを加える「Behaviour」たち。

new Proton.Alpha(a, b, life, easing)

alpha
透明度をaからbに変化させます。
lifeはこの動きを加え続ける時間で省略可能。デフォルトではInfinityが設定されます。
easingも省略可能でProton.easeLinearがデフォルトで設定されます。
phina.jsのtweenerで使えるイージングの名前がそのまま使えます。

new Proton.A()と省略できます。

ex.
var alphaEx1 = new Proton.Alpha(1, 0);
var alphaEx2 = new Proton.A(.8, 0, Infinity, Proton.easeInSine);

lifeeasingはbehaviourの引数の後ろ2つに必ずありますが、全て同じなので以降は説明を省きます。

new Proton.Attraction(targetPosition, force, radius, life, easing)

attraction
パーティクルを引きつける力を加える場所を作ります。
targetPositionには引きつける中心の座標をnew Proton.Vector2D(x座標, y座標)で指定します。new Proton.Vector(x座標, y座標)でもOKです。
forceradiusはそのまま力と半径の意味で省略可能です。デフォルトでは100と1000が設定されます。

ex.
var attractionEx1 = new Proton.Attraction(new Proton.Vector2D(100, 100));
var attractionEx2 = new Proton.Attraction(new Proton.Vector(10, 10), 15, 300);

new Proton.Collision(emitter, mass, callback, life, easing)

collision
パーティクルに衝突判定を追加します。
emitterにエミッターを指定します。masscallbackは省略可能です。
massにはtrueorfalseで指定します。デフォルトではtrueでパーティクルの質量が影響しますが、falseにすると質量に影響しなくなります。
callbackには衝突が起きた時にコールバックする関数を指定します。

ex.
var collisionEx1 = new Proton.Collision(emitter);

var callback = function(particle) {
  particle.radius += 0.1;
};
var collisionEx2 = new Proton.Collision(emitter, false, callback);

new Proton.Color(color1, color2, life, easing)

color
color1からcolor2に色を変化させます。
色は16進数カラーコード(#は省略可能)か、ランダムな色になる'random'が使えます。
複数の色を指定したい場合は配列を使い、color1のみ指定した場合は単色になります。

ex.
var colorEx1 = new Proton.Color('#ffff00', '#ff0000');
var colorEx2 = new Proton.Color(['ffffff', 'ffff00'], 'random');
var colorEx3 = new Proton.Color('00ffff');

new Proton.CrossZone(zone, crossType, life, easing)

crosszone
パーティクルの行動可能な範囲を指定します。
zoneにはZoneで範囲を指定します。
crossTypeには'dead''bound''cross'を指定します。デフォルトはdeadです。
deadはZoneの外に出たらパーティクルが消えます。
boundはZoneの端でパーティクルが跳ね返るようになります。
crossはZoneの端から出ると反対の端から出てきます。(RectZoneのみ対応)

ex.
var zone = new Proton.RectZone(0, 0, canvas.width, canvas.height);
var crossZoneEx1 = new Proton.CrossZone(zone, 'bound');

new Proton.Force(fx, fy, life, easing)

force
パーティクルに力を加えます。
fxfyにそれぞれの方向の力を数で指定します。

new Proton.F()と省略できます。

ex.
var forceEx1 = new Proton.Force(2, 0);
var forceEx2 = new Proton.F(-3, 2, Infinity, Proton.easeInSine);

new Proton.Gravity(g, life, easing)

gravity
パーティクルに重力を加えます。
gに重力の強さを数で指定します。

new Proton.G()と省略できます。

ex.
var gravityEx1 = new Proton.Gravity(1);
var gravityEx2 = new Proton.G(5, Infinity, Proton.easeInSine);

new Proton.GravityWell(centerPointopt, force, life, easing)

gravity-well
パーティクルに拡散させるような力を加える場所を作ります。
GravityWellは「重力井戸」という意味のようですが、よく分かりませんでしたw
centerPointにはProton.Vector2Dで位置を指定します。
forceは省略可能で、デフォルトで100が設定されます。

ex.
var gravityWellEx1 = new Proton.GravityWell(new Proton.Vector2D(10, 10));
var gravityWellEx2 = new Proton.GravityWell(new Proton.Vector(10, 10), 20);

new Proton.RandomDrift(driftX, driftY, delay, life, easing)

random-drift
パーティクルを上下左右に揺らします。
driftXdriftYにそれぞれの方向の大きさを数で指定します。
delayにも数を指定しますが、値が小さい方が頻繁に揺れるようになります。

ex.
var randomDriftEx1 = new Proton.RandomDrift(10, 0, .035);
var randomDriftEx2 = new Proton.RandomDrift(0, 10, .1);

new Proton.Repulsion(targetPosition, force, radius, life, easing)

repulsion
パーティクルを反発させる力を加える場所を作ります。
targetPositionにはProton.Vector2Dで位置を指定します。
forceradiusは省略可能で100と1000がデフォルトで設定されます。

ex.
var repulsionEx1 = new Proton.Repulsion(new Proton.Vector2D(10, 10));
var repulsionEx2 = new Proton.Repulsion(new Proton.Vector(10, 10), 10, 200);

new Proton.Rotate(a, b, style, life, easing)

rotate
パーティクルを回転させます。トマピコかわいい。
指定方法は3種類あります。
1つ目はnew Proton.Rotate(始まりの回転速度, 終わりの回転速度, 'to')
回転速度を決めて回転させます。回転速度は数か配列かSpanで指定します。
styleには'to''To''_'を、省略した場合もこの'to'になります。

2つ目はnew Proton.Rotate(始まりの角度, 回転速度, 'add')
始まりの角度と回転速度を決めて回転させます。1つ目は回転速度が変化するのに対して、回転速度が固定になります。

3つ目はnew Proton.Rotate('Velocity')
パーティクルの移動する方向に合わせて自動で向きを変えます。
'Velocity'ではなく'V''v'でも、全部省略してnew Proton.Rotate()ともできます。

ex.
var rotateEx1 = new Proton.Rotate(0, [-15, 15]);
var rotateEx2 = new Proton.Rotate(Proton.getSpan(0, 360), [-3, 3], 'add');
var rotateEx3 = new Proton.Rotate('V');

new Proton.Scale(a, b, life, easing)

scale
パーティクルの大きさをaからbに変化させます。
abにはそれぞれ数か配列かSpanで指定します。

ex.
var scaleEx1 = new Proton.Scale(1, Proton.getSpan(0, 1));
var scaleEx2 = new Proton.Scale(1, 0, Infinity, Proton.easeInCubic);

発生・行動範囲を決める「Zone」たち。

new Proton.CircleZone(x, y, radius)

円形のZoneを作ります。
xyに円の中心の座標、radiusに円の半径を指定します。

new Proton.ImageZone(imageData, x, y, d)

画像からZoneを作ります。画像の透明ではない場所がZoneの範囲になります。
xyは左上のx座標とy座標を指定します。中心ではないので注意が必要です。
dには1以上の数を指定します。大きいほど範囲が荒くなります。省略可能でデフォルトでは2が設定されます。

imageDataの取得方法がイマイチですが、上手く動いた例を書いておきます。

ex.
var context = canvas.getContext('2d');
var image = AssetManager.get('image', 'zone').domElement;

// new Proton.Rectangle(x, y, width, height)で getImageData に必要な rect を作る
var rect = new Proton.Rectangle(0, 0, image.width, image.height);

// Proton.Util.getImageData(context, image, rect)は imageData を返してくれる関数
var imageData = Proton.Util.getImageData(context, image, rect);

var imageZone = new Proton.ImageZone(imageData, 0, 0);

new Proton.LineZone(x1, y1, x2, y2, direction)

線形のZoneを作ります。
x1y1に始点の座標、x2y2に終点の座標を指定します。
directionは省略可能です。CrossZoneで利用時にcrossTypeがdeadのときのみ影響します。
線に対してパーティクルが右か下方向に来る場合に'>''R''right''down'を使い、左か上方向に来る場合はそれ以外('<''L''left''up'が良いかも)を使います。
デフォルトでは'>'が設定されます。

new Proton.PointZone(x, y)

点のZoneを作ります。Positionでしか利用できません。
xyに座標を指定します。

new Proton.RectZone(x, y, width, height)

矩形のZoneを作ります。
xyに左上の座標を、widthheightに横幅と高さを指定します。
xとyが中心の座標ではなく、左上の座標なことに注意してください。

さいごに

Protonの使い方をまとめただけで長くなってしまったので、今回はここまでにします。
「phina.jsでエフェクトとして使う」という目標を達成できていないので、今度別の記事を書こうと思います。

Runstantに簡単なサンプルを置いておきました。
皆さんもphina.jsとProtonで遊んでみてくださいね!

matsu7089
駆け出しWebエンジニア?phina.jsを勉強中。
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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした