Cocos2d-JS 触り始めた4

だいぶやりたいことが見つからない感じになってきたので、cc.ParticleSystem を使って適当に何かを散らしてみようかと思った(やったほうがいいことはもちろんまだたくさんあるが面倒くさい)。


今回作ったものはこちら。ホーミングミサイル的なものが出るけど、ミサイルとの当たり判定は特に作ってないのでヒットせずにぐるぐる回り続ける。

作業環境である Windows 8.1 で使えるエディタを適当に検索し見つけたこの Particle Editor を使って plist ファイルを書き出し、それを読み込み、適当に作ったホーミングミッソーの後ろに向けて噴出する感じで動くようにしてみた。
diff --git a/res/FireParticle.plist b/res/FireParticle.plist
new file mode 100644
index 0000000..bab965a
--- /dev/null
+++ b/res/FireParticle.plist
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+<key>angle</key>
+<real>0</real>
+<key>angleVariance</key>
+<real>0</real>
+<key>blendFuncDestination</key>
+<integer>1</integer>
+<key>blendFuncSource</key>
+<integer>1</integer>
+<key>duration</key>
+<real>-1</real>
+<key>emitterType</key>
+<real>0</real>
+<key>finishColorAlpha</key>
+<real>0</real>
+<key>finishColorBlue</key>
+<real>0</real>
+<key>finishColorGreen</key>
+<real>0</real>
+<key>finishColorRed</key>
+<real>0</real>
+<key>finishColorVarianceAlpha</key>
+<real>0</real>
+<key>finishColorVarianceBlue</key>
+<real>0</real>
+<key>finishColorVarianceGreen</key>
+<real>0</real>
+<key>finishColorVarianceRed</key>
+<real>0</real>
+<key>finishParticleSize</key>
+<real>20</real>
+<key>finishParticleSizeVariance</key>
+<real>12</real>
+<key>gravityx</key>
+<real>0</real>
+<key>gravityy</key>
+<real>0</real>
+<key>maxParticles</key>
+<real>20</real>
+<key>maxRadius</key>
+<real>0</real>
+<key>maxRadiusVariance</key>
+<real>0</real>
+<key>minRadius</key>
+<real>0</real>
+<key>particleLifespan</key>
+<real>0.3</real>
+<key>particleLifespanVariance</key>
+<real>0.1</real>
+<key>radialAccelVariance</key>
+<real>0</real>
+<key>radialAcceleration</key>
+<real>0</real>
+<key>rotatePerSecond</key>
+<real>0</real>
+<key>rotatePerSecondVariance</key>
+<real>0</real>
+<key>sourcePositionVariancex</key>
+<real>0</real>
+<key>sourcePositionVariancey</key>
+<real>0</real>
+<key>sourcePositionx</key>
+<real>0</real>
+<key>sourcePositiony</key>
+<real>0</real>
+<key>speed</key>
+<real>80.0</real>
+<key>speedVariance</key>
+<real>20.0</real>
+<key>startColorAlpha</key>
+<real>1</real>
+<key>startColorBlue</key>
+<real>1</real>
+<key>startColorGreen</key>
+<real>1</real>
+<key>startColorRed</key>
+<real>1</real>
+<key>startColorVarianceAlpha</key>
+<real>1</real>
+<key>startColorVarianceBlue</key>
+<real>1</real>
+<key>startColorVarianceGreen</key>
+<real>0</real>
+<key>startColorVarianceRed</key>
+<real>0</real>
+<key>startParticleSize</key>
+<real>0</real>
+<key>startParticleSizeVariance</key>
+<real>0</real>
+<key>tangentialAccelVariance</key>
+<real>0</real>
+<key>tangentialAcceleration</key>
+<real>0</real>
+<key>textureFileName</key>
+<string>stripes.png</string>
+<key>textureImageData</key>
+<string></string>
+</dict>
+</plist>
diff --git a/res/stripes.png b/res/stripes.png
new file mode 100644
index 0000000..f67ac0b
Binary files /dev/null and b/res/stripes.png differ
diff --git a/src/app.js b/src/app.js
index e625506..e0d3a27 100644
--- a/src/app.js
+++ b/src/app.js
@@ -162,6 +162,51 @@ var BlockLayer = cc.Layer.extend({
     }
 });

+var HomingMissoLayer = cc.Layer.extend({
+    target:null,
+    particle:null,
+    d:null,
+    a:0,
+    vx:0,
+    vy:0,
+    speed:6,
+    angle:0.3,
+    ctor:function () {
+      this._super();
+      this.scheduleUpdate();
+      this.d = cc.DrawNode.create();
+      this.d.drawRect(cc.p(-8, -2), cc.p(8, 2), cc.color(255,255,255,255));
+      this.addChild(this.d, 0);
+      var bbox = cc.size(16, 16);
+      this.d.setPosition(bbox.width * 0.5, bbox.height * 0.5);
+      this.setContentSize(bbox);
+      this.particle = cc.ParticleSystem.create(res.FireParticle_plist);
+      this.addChild(this.particle);
+      return true;
+    },
+    update:function(dt) {
+      var bb = this.target.getBoundingBox();
+      var dx = this.d.x, dy = this.d.y;
+      var vx = this.vx, vy = this.vy;
+      var tx = bb.x + bb.width*0.5 - dx, ty = bb.y + bb.height*0.5 - dy;
+      var tl = Math.sqrt(tx*tx+ty*ty), a = this.a;
+      if ((tx*vx+ty*vy)/(tl*Math.sqrt(vx*vx+vy*vy)) < 0) {
+        a += this.angle;
+      } else {
+        a += this.angle * (vx*ty - vy*tx)/tl/this.speed;
+      }
+      this.a = a;
+      var cosa = Math.cos(a), sina = Math.sin(a), r = a*180/Math.PI;
+      this.vx = vx = this.speed * cosa;
+      this.vy = vy = this.speed * sina;
+      this.d.x = dx += vx;
+      this.d.y = dy += vy;
+      this.d.rotation = -r;
+      this.particle.setSourcePosition(cc.p(dx + -6 * cosa, dy + -6 * sina));
+      this.particle.angle = 180+r;
+    }
+});
+
 var HelloWorldScene = cc.Scene.extend({
     myShip:null,
     blocks:null,
@@ -200,6 +245,19 @@ var HelloWorldScene = cc.Scene.extend({
           this.blocks.push(bl);
         }

+        var ml = new HomingMissoLayer();
+        ml.attr({target: this.blocks[0]});
+        this.addChild(ml);
+        var ml = new HomingMissoLayer();
+        ml.attr({target: this.blocks[1]});
+        this.addChild(ml);
+        var ml = new HomingMissoLayer();
+        ml.attr({target: this.blocks[2]});
+        this.addChild(ml);
+        var ml = new HomingMissoLayer();
+        ml.attr({target: this.blocks[3]});
+        this.addChild(ml);
+
         this.el = cc.EventListener.create({
             event: cc.EventListener.TOUCH_ONE_BY_ONE,
             swallowTouches: true,
diff --git a/src/resource.js b/src/resource.js
index 574ab3e..24c26df 100644
--- a/src/resource.js
+++ b/src/resource.js
@@ -9,7 +9,9 @@ var res = {
     restart_normal_png : "res/restart_normal.png",
     restart_selected_png : "res/restart_selected.png",
     return_to_title_normal_png : "res/return_to_title_normal.png",
-    return_to_title_selected_png : "res/return_to_title_selected.png"
+    return_to_title_selected_png : "res/return_to_title_selected.png",
+    FireParticle_plist : "res/FireParticle.plist",
+    stripes_png : "res/stripes.png",
 };

 var g_resources = [
@@ -24,9 +26,11 @@ var g_resources = [
     res.restart_normal_png,
     res.restart_selected_png,
     res.return_to_title_normal_png,
-    res.return_to_title_selected_png
+    res.return_to_title_selected_png,
+    res.stripes_png,

     //plist
+    res.FireParticle_plist,

     //fnt
気をつける必要があるのは cc.ParticleSystemaddChild した親のレイヤーなどを移動すると全体が動いてしまい、出力されているパーティクルに本来存在しない動きが加わってしまい予め設定していた振る舞いと若干異なる挙動になってしまう(もちろんわかった上でやる分には問題ない)。

なので、今回のミッソーのように噴出口自体は動くものの噴出したパーティクルは出力時の動きを維持したいようなケースの場合、setSourcePositionangle を使って正しい位置に合わせる必要があるようだった。

その辺りを把握していれば動かす上で障壁になるようなものはあまりない気がするので、非常に効率よく映えるエフェクトを追加できて便利なので積極的に使っていきたい仕組みだった。

plist ファイルに仕組みを追い出しておくことによってプログラマーとデザイナーで作業もある程度分担しやすくなるだろうしとても良い。