はじめに
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で動くようにしてみます。
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();
});
どうやらエミッターにInitializeとBehaviourを追加してパーティクルに動きを加えていくようです。
チュートリアルが用意されていなかったので、試しながら使い方をまとめていきます。
Protonの使い方
初期設定する「Initialize」たち。
new Proton.Body(image, w, h)
パーティクル本体の画像を指定します。
引数のimageは画像のURLかImageオブジェクト。wとhは省略可能です。
画像の指定が無かった場合、普通の円形のパーティクルになります。
new Proton.B()と省略できます。
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)
パーティクルの生存時間を指定します。
aとbに数を指定した場合はa~bのからランダムな値(小数含む)になります。
cは省略可能ですがtrueを指定すると、ランダムの範囲がa-b~a+bに変わります。
aのみに数を指定するとaの値固定になり、配列を指定するとその中からランダムにピックアップされます。
new Proton.L()と省略できます。
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度に発生させる個数と、発生の間隔を指定します。
numpanとtimepanはそれぞれProton.Lifeのように指定します。ただ少しだけ方法が違い、Proton.getSpan(a, b, c)というものを使って指定します。具体的にはこんな感じです。
// 発生個数: 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を利用できます。
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'と省略できます。

1つ目はxとyの速度を直接指定するのに対して、2つ目は放射状にパーティクルを発生させます。※画像上のコードは略しています。
個人的には後者の方が馴染みがあるので好きです。
new Proton.V()と省略できます。
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)

透明度をaからbに変化させます。
lifeはこの動きを加え続ける時間で省略可能。デフォルトではInfinityが設定されます。
easingも省略可能でProton.easeLinearがデフォルトで設定されます。
phina.jsのtweenerで使えるイージングの名前がそのまま使えます。
new Proton.A()と省略できます。
var alphaEx1 = new Proton.Alpha(1, 0);
var alphaEx2 = new Proton.A(.8, 0, Infinity, Proton.easeInSine);
lifeとeasingはbehaviourの引数の後ろ2つに必ずありますが、全て同じなので以降は説明を省きます。
new Proton.Attraction(targetPosition, force, radius, life, easing)

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

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

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

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

パーティクルに力を加えます。
fxとfyにそれぞれの方向の力を数で指定します。
new Proton.F()と省略できます。
var forceEx1 = new Proton.Force(2, 0);
var forceEx2 = new Proton.F(-3, 2, Infinity, Proton.easeInSine);
new Proton.Gravity(g, life, easing)

パーティクルに重力を加えます。
gに重力の強さを数で指定します。
new Proton.G()と省略できます。
var gravityEx1 = new Proton.Gravity(1);
var gravityEx2 = new Proton.G(5, Infinity, Proton.easeInSine);
new Proton.GravityWell(centerPointopt, force, life, easing)

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

パーティクルを上下左右に揺らします。
driftXとdriftYにそれぞれの方向の大きさを数で指定します。
delayにも数を指定しますが、値が小さい方が頻繁に揺れるようになります。
var randomDriftEx1 = new Proton.RandomDrift(10, 0, .035);
var randomDriftEx2 = new Proton.RandomDrift(0, 10, .1);
new Proton.Repulsion(targetPosition, force, radius, life, easing)

パーティクルを反発させる力を加える場所を作ります。
targetPositionにはProton.Vector2Dで位置を指定します。
forceとradiusは省略可能で100と1000がデフォルトで設定されます。
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)

パーティクルを回転させます。トマピコかわいい。
指定方法は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()ともできます。
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)

パーティクルの大きさをaからbに変化させます。
aとbにはそれぞれ数か配列かSpanで指定します。
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を作ります。
xとyに円の中心の座標、radiusに円の半径を指定します。
new Proton.ImageZone(imageData, x, y, d)
画像からZoneを作ります。画像の透明ではない場所がZoneの範囲になります。
xとyは左上のx座標とy座標を指定します。中心ではないので注意が必要です。
dには1以上の数を指定します。大きいほど範囲が荒くなります。省略可能でデフォルトでは2が設定されます。
imageDataの取得方法がイマイチですが、上手く動いた例を書いておきます。
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を作ります。
x1とy1に始点の座標、x2とy2に終点の座標を指定します。
directionは省略可能です。CrossZoneで利用時にcrossTypeがdeadのときのみ影響します。
線に対してパーティクルが右か下方向に来る場合に'>'か'R'か'right'か'down'を使い、左か上方向に来る場合はそれ以外('<'か'L'か'left'か'up'が良いかも)を使います。
デフォルトでは'>'が設定されます。
new Proton.PointZone(x, y)
点のZoneを作ります。Positionでしか利用できません。
xとyに座標を指定します。
new Proton.RectZone(x, y, width, height)
矩形のZoneを作ります。
xとyに左上の座標を、widthとheightに横幅と高さを指定します。
xとyが中心の座標ではなく、左上の座標なことに注意してください。
さいごに
Protonの使い方をまとめただけで長くなってしまったので、今回はここまでにします。
「phina.jsでエフェクトとして使う」という目標を達成できていないので、今度別の記事を書こうと思います。
Runstantに簡単なサンプルを置いておきました。
皆さんもphina.jsとProtonで遊んでみてくださいね!
