LoginSignup
7
6

More than 5 years have passed since last update.

phina.jsとProtonでパーティクルしてみる。その2「phina.jsと連携する」

Posted at

前回の『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に上の一行を追加するか、ここを右クリックして「リンク先を保存」して、お好きなように読み込んでください。

プラグインの使い方

main.js
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();
});

プラグインを読み込むとProtonLayerProtonEmitterが使えるようになります。(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描画専用のレイヤーです。
特にこだわりが無ければ、サンプルのように画面サイズと同じwidthheightをオプションで渡して、画面の中心に移動させてあげると上手く動きます。

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っぽくエミッターを作れるようにしました。前回のプログラムと比べると、見やすくなりましたかね?
ご覧の通りrateinitializebehaviourをオプションで渡す事が出来ます。

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プロパティに渡してあげてください。
initializebehaviouraddInitialize()addBehaviour()を使って追加してあげてください。
どちらも上記のプログラムのinitializeのように配列でまとめて追加するか、behaviourのように1つずつ追加することができます。

他にもいくつかのメソッドが利用出来ます。

method description
emit() パーティクルを放射
stop() 放射をストップ
removeAllParticles() 全てのパーティクルを削除
removeInitialize(initialize) 指定のinitializeを削除
removeAllInitializers() 全てのinitializeを削除
removeBehaviour(behaviour) 指定のbehaviourを削除
removeAllBehaviours() 全てのbehaviourを削除

説明も書いてみましたが、メソッドの名前だけで大体分かりますね!w

オリジナルエミッターを作る

さて、ここからはProtonEmitterを拡張してオリジナルエミッターを作ってみましょう:sparkles:

クリックでbehaviourを切り替える

main.js
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.

どうでしょう!簡単に実装することができました:sparkles:

エミッターの動きを切り替える必要がある時は、superInitのオプションには渡さず、後から追加する方が便利です。
ただ、今回は変える必要のないbehaviour(alpha, color, crosszone)があったので、それだけオプションに渡しています。

花火っぽい何か

main.js
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.

どうでしょう!花火っぽい何かを作れました:sparkles:

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ライフを!:wave:

7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6