前回の『phina.jsとProtonでパーティクルしてみる。その1「Protonの使い方」』の続きになります。
この記事を書いた後、ProtonのREADMEにリンクが貼られていました!少し貢献できた気がして嬉しいです。(どうやってこの記事みつけたのかな…)
Protonの開発者であるa-jieさん、ありがとうございます!
さて、今回はphina.jsでProtonを簡単に扱えるようなプラグインを作ってみました。
プラグインの使い方、オリジナルエミッターの作り方をまとめたいと思います。
プラグインを読み込む
<script src="https://rawgit.com/matsu7089/phina-proton/master/phina.proton.js"></script>
htmlに上の一行を追加するか、ここを右クリックして「リンク先を保存」して、お好きなように読み込んでください。
プラグインの使い方
var SCREEN_W = 320;
var SCREEN_H = 320;
phina.globalize();
phina.define('MainScene', {
superClass: 'DisplayScene',
init: function(options) {
this.superInit(options);
this.backgroundColor = 'black';
var protonLayer = ProtonLayer({
width: SCREEN_W,
height: SCREEN_H
}).setPosition(SCREEN_W/2, SCREEN_H/2).addChildTo(this);
var emitter = ProtonEmitter({
rate: new Proton.Rate(Proton.getSpan(10, 20), 0.1),
initialize: [
new Proton.Radius(1, 22),
new Proton.Life(2, 4),
new Proton.Velocity(2, Proton.getSpan(0, 360), 'polar')
],
behaviour: [
new Proton.Color('ff0000', 'random'),
new Proton.Alpha(1, 0)
]
}).setPosition(SCREEN_W/2, SCREEN_H/2);
emitter.addChildTo(protonLayer).emit();
}
});
phina.main(function() {
var app = GameApp({
startLabel: 'main',
width: SCREEN_W,
height: SCREEN_H,
fps: 60
});
app.run();
});
プラグインを読み込むとProtonLayer
とProtonEmitter
が使えるようになります。(phina.globalize()
してない時は、phina.proton.
を前に付け加えてください)
では、使い方を簡単に説明していきます。
ProtonLayer
var protonLayer = ProtonLayer({
width: SCREEN_W,
height: SCREEN_H
}).setPosition(SCREEN_W/2, SCREEN_H/2).addChildTo(this);
ProtonLayer
はProton描画専用のレイヤーです。
特にこだわりが無ければ、サンプルのように画面サイズと同じwidth
とheight
をオプションで渡して、画面の中心に移動させてあげると上手く動きます。
Proton本体が用意されていて、ProtonEmitter
を子要素に追加するだけで表示してくれます。
ProtonEmitter
var emitter = ProtonEmitter({
rate: new Proton.Rate(Proton.getSpan(10, 20), 0.1),
initialize: [
new Proton.Radius(1, 22),
new Proton.Life(2, 4),
new Proton.Velocity(2, Proton.getSpan(0, 360), 'polar')
],
behaviour: [
new Proton.Color('ff0000', 'random'),
new Proton.Alpha(1, 0)
]
}).setPosition(SCREEN_W/2, SCREEN_H/2);
phina.jsっぽくエミッターを作れるようにしました。前回のプログラムと比べると、見やすくなりましたかね?
ご覧の通りrate
、initialize
、behaviour
をオプションで渡す事が出来ます。
var emitter = ProtonEmitter().setPosition(SCREEN_W/2, SCREEN_H/2);
emitter.rate = new Proton.Rate(Proton.getSpan(10, 20), 0.1);
var initializes = [
new Proton.Radius(1, 22),
new Proton.Life(2, 4),
new Proton.Velocity(2, Proton.getSpan(0, 360), 'polar')
];
emitter
.addInitialize(initializes)
.addBehaviour(new Proton.Alpha(1, 0))
.addBehaviour(new Proton.Color('ff0000', 'random'));
もちろん、オプションに渡さず後から追加することもできます。
rate
は直接rateプロパティに渡してあげてください。
initialize
、behaviour
はaddInitialize()
、addBehaviour()
を使って追加してあげてください。
どちらも上記のプログラムのinitializeのように配列でまとめて追加するか、behaviourのように1つずつ追加することができます。
他にもいくつかのメソッドが利用出来ます。
method | description |
---|---|
emit() | パーティクルを放射 |
stop() | 放射をストップ |
removeAllParticles() | 全てのパーティクルを削除 |
removeInitialize(initialize) | 指定のinitializeを削除 |
removeAllInitializers() | 全てのinitializeを削除 |
removeBehaviour(behaviour) | 指定のbehaviourを削除 |
removeAllBehaviours() | 全てのbehaviourを削除 |
説明も書いてみましたが、メソッドの名前だけで大体分かりますね!w
オリジナルエミッターを作る
さて、ここからはProtonEmitter
を拡張してオリジナルエミッターを作ってみましょう
クリックでbehaviourを切り替える
var SCREEN_W = 320;
var SCREEN_H = 320;
phina.globalize();
// MyEmitterを定義する
phina.define('MyEmitter', {
superClass: 'ProtonEmitter',
init: function() {
this.superInit({
rate: new Proton.Rate(Proton.getSpan(10, 20), 0.2),
initialize: [
new Proton.Radius(1, 10),
new Proton.Life(2, 4),
new Proton.Velocity(1, Proton.getSpan(0, 360), 'polar')
],
behaviour: [
new Proton.Alpha(1, 0),
new Proton.Color(['e91e63', '2196f3', 'ffeb3b']),
new Proton.CrossZone(new Proton.RectZone(0, 0, SCREEN_W, SCREEN_H), 'bound')
]
});
this.setPosition(SCREEN_W/2, SCREEN_H/3).emit();
var center = new Proton.Vector2D(SCREEN_W/2, SCREEN_H/2);
// 切り替えに使うbehaviourの配列
this.behaviours = [
new Proton.Attraction(center, 25, 150),
new Proton.Collision(this.protonEmitter),
new Proton.Force(30, 0),
new Proton.Gravity(1),
new Proton.GravityWell(center, 200),
new Proton.RandomDrift(20, 0, .035),
new Proton.Repulsion(center, 10, 150),
new Proton.Scale(1, 3)
];
this.index = 0;
this.addBehaviour(this.behaviours[this.index]);
},
/**
* behaviourを切り替える
*/
changeBehaviour: function() {
// 前のbehaviourを取り除く
this.removeBehaviour(this.behaviours[this.index]);
// 新しいbehaviourを追加する
this.index = ++this.index % 8;
this.addBehaviour(this.behaviours[this.index]);
}
});
phina.define('MainScene', {
superClass: 'DisplayScene',
init: function(options) {
this.superInit(options);
this.backgroundColor = '#f0f0f0';
var protonLayer = ProtonLayer({
width: SCREEN_W,
height: SCREEN_H
}).setPosition(SCREEN_W/2, SCREEN_H/2).addChildTo(this);
// MyEmitterをprotonLayerに追加する
var emitter = MyEmitter().addChildTo(protonLayer);
this.setInteractive(true);
this.on('pointstart', function() {
// 画面がクリックされた時にMyEmitterのchangeBehaviour()を呼ぶ
emitter.changeBehaviour();
});
}
});
phina.main(function() {
var app = GameApp({
startLabel: 'main',
width: SCREEN_W,
height: SCREEN_H,
fps: 60
});
app.run();
});
See the Pen my-emitter by matsu (@matsu7089) on CodePen.
どうでしょう!簡単に実装することができました
エミッターの動きを切り替える必要がある時は、superInitのオプションには渡さず、後から追加する方が便利です。
ただ、今回は変える必要のないbehaviour(alpha, color, crosszone)があったので、それだけオプションに渡しています。
花火っぽい何か
var SCREEN_W = 320;
var SCREEN_H = 320;
phina.globalize();
// FireworksEmitterを定義する
phina.define('FireworksEmitter', {
superClass: 'ProtonEmitter',
init: function() {
this.superInit({
rate: new Proton.Rate(Proton.getSpan(20, 30)),
initialize: [
new Proton.Radius(10, 15),
new Proton.Life(2, 4),
],
behaviour: [
new Proton.Gravity(1.5),
new Proton.Scale(1, 0)
]
});
// 使用する色のリスト
this.colors = [
'1abc9c', '2ecc71', '3498db', '9b59b6',
'f1c40f', 'e67e22', 'e74c3c'
];
// velocityとcolorをエミッターに追加する
this.velocity = new Proton.Velocity();
this.color = new Proton.Color();
this.addInitialize(this.velocity).addBehaviour(this.color);
// tweenerを利用して一定のタイミングで_emit()を呼ぶ
this.tweener
.call(function() { this._emit(); }, this).wait(100)
.call(function() { this._emit(); }, this).wait(100)
.call(function() { this._emit(); }, this).wait(1300)
.setLoop(true);
},
_emit: function() {
// 放射速度を変える
this.velocity.reset(Math.random()+0.5, Proton.getSpan(0, 360), 'p');
// 色を変える
this.color.reset(this.colors.pickup());
// 位置を変える
this.setPosition(Math.random()*SCREEN_W, Math.random()*SCREEN_H/2);
// 放射する間隔に関係なく、一度だけ放射する
this.emit('once');
}
});
phina.define('MainScene', {
superClass: 'DisplayScene',
init: function(options) {
this.superInit(options);
this.backgroundColor = '#f0f0f0';
var protonLayer = ProtonLayer({
width: SCREEN_W,
height: SCREEN_H
}).setPosition(SCREEN_W/2, SCREEN_H/2).addChildTo(this);
// FireworksEmitterをprotonLayerに追加する
var emitter = FireworksEmitter().addChildTo(protonLayer);
}
});
phina.main(function() {
var app = GameApp({
startLabel: 'main',
width: SCREEN_W,
height: SCREEN_H,
fps: 60
});
app.run();
});
See the Pen fireworks-emitter by matsu (@matsu7089) on CodePen.
どうでしょう!花火っぽい何かを作れました
phina.jsのtweenerを利用して一定のタイミングでパーティクルを放射しています。
_emit: function() {
// 放射速度を変える
this.velocity.reset(Math.random()+0.5, Proton.getSpan(0, 360), 'p');
// 色を変える
this.color.reset(this.colors.pickup());
// 位置を変える
this.setPosition(Math.random()*SCREEN_W, Math.random()*SCREEN_H/2);
// 放射する間隔に関係なく、一度だけ放射する
this.emit('once');
}
この部分を少し説明します。
initializeとbehaviourにはreset()
が用意されていて、値を再設定することができます。
今回はこれを利用して放射毎に速度と色を変更しています。
ちなみに、色を変える所でpickup()
というのがありますが、これはphina.jsが用意している便利メソッドで、配列の中からランダムに値を取り出してくれます。
this.emit('once')
の所はコメント文にある通り、rateの放射間隔に関係無く一度だけ放射する事ができます。
また、emit(秒数)
で「何秒間だけ放射する」ということができます。
オリジナルエミッター作りはここまで!
さいごに
最後まで閲覧頂きありがとうございます。
phina.jsとProton、少しでも面白そうと思って頂けたら嬉しいです。
今のところ「その3」の予定はありませんが、エミッターのレシピが貯まったら記事にまとめるかもしれません。
では皆さん、楽しいphina.js&Protonライフを!