1 /**
  2  * @fileOverview
  3  * PhySprite.enchant.js v1.30
  4  *
  5  * The MIT License
  6  *
  7  * Copyright (c) 2012/01/26 kassy708
  8  *
  9  * Permission is hereby granted, free of charge, to any person obtaining a copy
 10  * of this software and associated documentation files (the "Software"), to deal
 11  * in the Software without restriction, including without limitation the rights
 12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 13  * copies of the Software, and to permit persons to whom the Software is
 14  * furnished to do so, subject to the following conditions:
 15  *
 16  * The above copyright notice and this permission notice shall be included in
 17  * all copies or substantial portions of the Software.
 18  *
 19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 22  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 25  * THE SOFTWARE.
 26  *
 27  * 物理演算用のSprite
 28  *
 29  * @author kassy708 http://twitter.com/kassy708
 30  *
 31  * このプラグインではBox2dWeb.jsを用いています。
 32  * 最新のBox2dWeb.jsは下記アドレスからダウンロードしてください。
 33  * http://www.gphysics.com
 34  */
 35 
 36 
 37 if (!Box2D)throw new Error("box2d.enchant.js must be loaded after Box2dWeb.js");
 38 
 39 /* export */
 40 var b2Vec2 = Box2D.Common.Math.b2Vec2
 41     , b2AABB = Box2D.Collision.b2AABB
 42     , b2BodyDef = Box2D.Dynamics.b2BodyDef
 43     , b2Body = Box2D.Dynamics.b2Body
 44     , b2FixtureDef = Box2D.Dynamics.b2FixtureDef
 45     , b2Fixture = Box2D.Dynamics.b2Fixture
 46     , b2World = Box2D.Dynamics.b2World
 47     , b2MassData = Box2D.Collision.Shapes.b2MassData
 48     , b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
 49     , b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
 50     , b2DebugDraw = Box2D.Dynamics.b2DebugDraw
 51     , b2MouseJointDef = Box2D.Dynamics.Joints.b2MouseJointDef;
 52 
 53 /**
 54  * @type {Object}
 55  */
 56 enchant.box2d = {};
 57 
 58 (function() {
 59     var WORLD_SCALE = 32;
 60     var world;
 61 
 62     /**
 63      * Spriteの種類(スタティック)
 64      * @type {Number}
 65      */
 66     enchant.box2d.STATIC_SPRITE = 0;
 67     /**
 68      * Spriteの種類(ダイナミック)
 69      * @type {Number}
 70      */
 71     enchant.box2d.DYNAMIC_SPRITE = 2;
 72 
 73     /**
 74      * @scope enchant.box2d.PhysicsWorld.prototype
 75      */
 76     enchant.box2d.PhysicsWorld = enchant.Class.create({
 77         /**
 78          * 物理シミュレーションを行う世界のクラス
 79          * @example
 80          *   //y軸方向へ重力加速度9.8m/s^2
 81          *   var physicsWorld = new PhysicsWorld(0, 9.8);
 82          *   //無重力
 83          *   var physicsWorld = new PhysicsWorld(0, 0);
 84          *
 85          * @param {Number} [gravityX] x軸方向への引力.
 86          * @param {Number} [gravityY] y軸方向への引力.
 87          * @constructs
 88          */
 89         initialize: function(gravityX, gravityY) {
 90             /**
 91              * 物理シミュレーションの精度
 92              * @type {Nunber}
 93              */
 94             this.iterations = 10;
 95             world = new b2World(
 96                 new b2Vec2(gravityX, gravityY)  //gravity
 97                 , true                          //allow sleep
 98             );
 99         },
100         /**
101          * 物理シミュレーション内の時間を進める
102          * @param {b2Vec2} [pos] Spriteの座標.
103          */
104         step: function(fps) {
105             world.Step(1 / fps, this.iterations, this.iterations);
106         },
107         /**
108          * 物体の当たり判定
109          * @example
110          *   //ぶつかった2つのSpriteを消す
111          *   physicsWorld.contact(function (sprite1, sprite2) {
112          *       sprite1.destroy();
113          *       sprite2.destroy();
114          *   });
115          *
116          * @param {function(sprite1:enchant.box2d.PhySprite,sprite2:enchant.box2d.PhySprite)} [func] 当たり判定時の処理
117          */
118         contact: function(func) {
119             var c = world.m_contactList;
120             if (c) {
121                 for (var contact = c; contact; contact = contact.m_next) {
122                     var pos1 = contact.m_fixtureA.m_body.GetPosition().Copy();
123                     pos1.Subtract(contact.m_fixtureB.m_body.GetPosition());
124                     pos1.Multiply(WORLD_SCALE);
125                     var r1 = (contact.m_fixtureA.m_body.m_userData.width + contact.m_fixtureB.m_body.m_userData.width) / 2;
126                     var r2 = (contact.m_fixtureA.m_body.m_userData.height + contact.m_fixtureB.m_body.m_userData.height) / 2;
127                     if (Math.abs(pos1.x) <= r1 && Math.abs(pos1.y) <= r2) {
128                         func(contact.m_fixtureA.m_body.m_userData, contact.m_fixtureB.m_body.m_userData);
129                     }
130                 }
131             }
132         }
133     });
134 
135 
136     /**
137      * @scope enchant.box2d.PhySprite.prototype
138      */
139     enchant.box2d.PhySprite = enchant.Class.create(enchant.Sprite, {
140         /**
141          * 画像表示機能を持った物理シミュレーションクラス.
142          * @param {Number} [width] Spriteの横幅.
143          * @param {Number} [height] Spriteの高さ.
144          * @constructs
145          * @extends enchant.Sprite
146          */
147         initialize: function(width, height) {
148             this.body;
149             /**
150              * 静的オブジェクトか動的オブジェクトか
151              */
152             this.staticOrDynamic;
153             enchant.Sprite.call(this, width, height);
154 
155             var time = 0;
156             this.addEventListener(enchant.Event.ENTER_FRAME, function(e) {
157                 this.x = this.x;
158                 this.y = this.y;
159                 this.rotation = this.angle;
160                 time++;
161                 time = time % 2;
162             });
163         },
164         /**
165          * 四角形の物理シミュレーション用Sprite生成.
166          * @param {Boolean} staticOrDynamic 静止するか動くか.
167          * @param {Number} density Spriteの密度.
168          * @param {Number} friction Spriteの摩擦.
169          * @param {Number} restitution Spriteの反発.
170          * @param {Boolean} isSleeping Spriteが初めから物理演算を行うか.
171          */
172         createPhyBox: function(staticOrDynamic, density, friction, restitution, awake) {
173             this.staticOrDynamic = staticOrDynamic;
174             var fixDef = new b2FixtureDef;
175             fixDef.density = (density != null ? density : 1.0);             // 密度
176             fixDef.friction = (friction != null ? friction : 0.5);          // 摩擦
177             fixDef.restitution = (restitution != null ? restitution : 0.3); // 反発
178             fixDef.shape = new b2PolygonShape;
179             fixDef.shape.SetAsBox(this.width / 2 / WORLD_SCALE, this.height / 2 / WORLD_SCALE);
180             var bodyDef = new b2BodyDef;
181             bodyDef.type = staticOrDynamic;
182             bodyDef.position.x = 0;
183             bodyDef.position.y = 0;
184             bodyDef.awake = (awake != null ? awake : true);
185             bodyDef.userData = this;
186             return world.CreateBody(bodyDef).CreateFixture(fixDef);
187         },
188         /**
189          * 円形の物理シミュレーション用Sprite生成.
190          * @param {Boolean} staticOrDynamic 静止するか動くか.
191          * @param {Number} density Spriteの密度.
192          * @param {Number} friction Spriteの摩擦.
193          * @param {Number} restitution Spriteの反発.
194          * @param {Boolean} isSleeping Spriteが初めから物理演算を行うか.
195          */
196         createPhyCircle: function(staticOrDynamic, density, friction, restitution, awake) {
197             this.staticOrDynamic = staticOrDynamic;
198             var fixDef = new b2FixtureDef;
199             fixDef.density = (density != null ? density : 1.0);             // 密度
200             fixDef.friction = (friction != null ? friction : 0.5);          // 摩擦
201             fixDef.restitution = (restitution != null ? restitution : 0.3); // 反発
202             fixDef.shape = new b2CircleShape(this.width / 2 / WORLD_SCALE);
203             var bodyDef = new b2BodyDef;
204             bodyDef.type = staticOrDynamic;
205             bodyDef.position.x = 0;
206             bodyDef.position.y = 0;
207             bodyDef.awake = (awake != null ? awake : true);
208             bodyDef.userData = this;
209             return world.CreateBody(bodyDef).CreateFixture(fixDef);
210         },
211         /**
212          * Spriteのタイプ 静的(STATIC_SPRITE)か動的(DYNAMIC_SPRITE)か
213          * @type {bool}
214          */
215         type: {
216             get: function() {
217                 if (this.body.m_body.GetType() == b2Body.b2_staticBody)
218                     return enchant.box2d.STATIC_SPRITE;
219                 return enchant.box2d.DYNAMIC_SPRITE;
220             },
221             set: function(staticOrDynamic) {
222                 this.staticOrDynamic = staticOrDynamic;
223                 this.body.m_body.SetType(staticOrDynamic);
224             }
225         },
226         /**
227          * Spriteのx座標.
228          * @type {Number}
229          */
230         x: {
231             get: function() {
232                 return this.body.m_body.GetPosition().x * WORLD_SCALE - this.width / 2;
233             },
234             set: function(x) {
235                 this._x = x;
236                 x += this.width / 2;
237                 this.body.m_body.SetPosition(new b2Vec2(x / WORLD_SCALE, this.body.m_body.GetPosition().y));
238                 this._dirty = true;
239             }
240         },
241         /**
242          * Spriteのy座標.
243          * @type {Number}
244          */
245         y: {
246             get: function() {
247                 return this.body.m_body.GetPosition().y * WORLD_SCALE - this.height / 2;
248             },
249             set: function(y) {
250                 this._y = y;
251                 y += this.height / 2;
252                 this.body.m_body.SetPosition(new b2Vec2(this.body.m_body.GetPosition().x, y / WORLD_SCALE));
253                 this._dirty = true;
254             }
255         },
256         /**
257          * Spriteの中心のx座標.
258          * @type {Number}
259          */
260         centerX: {
261             get: function() {
262                 return this.x + this.width / 2;
263             },
264             set: function(x) {
265                 this.x = x - this.width / 2;
266             }
267         },
268         /**
269          * Spriteの中心のy座標.
270          * @type {Number}
271          */
272         centerY: {
273             get: function() {
274                 return this.y + this.height / 2;
275             },
276             set: function(y) {
277                 this.y = y - this.height / 2;
278             }
279         },
280         /**
281          * Spriteの中心座標ベクトル.
282          * @type {b2Vec2}
283          */
284         position: {
285             get: function() {
286                 var pos = this.body.m_body.GetPosition().Copy();
287                 pos.Multiply(WORLD_SCALE);
288                 return pos;
289             },
290             set: function(pos) {
291                 this.centerX = pos.x;
292                 this.centerY = pos.y;
293                 this.body.m_body.SetPosition(new b2Vec2(pos.x / WORLD_SCALE, pos.y / WORLD_SCALE));
294             }
295         },
296         /**
297          * Spriteのx座標の速度(単位はpx/s).
298          * @type {Number}
299          */
300         vx: {
301             get: function() {
302                 return this.body.m_body.GetLinearVelocity().x * WORLD_SCALE;
303             },
304             set: function(x) {
305                 this.body.m_body.SetLinearVelocity(new b2Vec2(x / WORLD_SCALE, this.body.m_body.GetLinearVelocity().y));
306             }
307         },
308         /**
309          * Spriteのy座標の速度(単位はpx/s).
310          * @type {Number}
311          */
312         vy: {
313             get: function() {
314                 return this.body.m_body.GetLinearVelocity().y * WORLD_SCALE;
315             },
316             set: function(y) {
317                 this.body.m_body.SetLinearVelocity(new b2Vec2(this.body.m_body.GetLinearVelocity().x, y / WORLD_SCALE));
318             }
319         },
320         /**
321          * Spriteの速度(単位はpx/s).
322          * @type {b2Vec2}
323          */
324         velocity: {
325             get: function() {
326                 var v = this.body.m_body.GetLinearVelocity().Copy();
327                 v.Multiply(WORLD_SCALE);
328                 return v;
329             },
330             set: function(v) {
331                 this.body.m_body.SetLinearVelocity(new b2Vec2(v.x / WORLD_SCALE, v.y / WORLD_SCALE));
332             }
333         },
334         /**
335          * Spriteの角度 (度数法)..
336          * @type {Number}
337          */
338         angle: {
339             get: function() {
340                 return this.body.m_body.GetAngle() * (180 / Math.PI);
341             },
342             set: function(angle) {
343                 this.rotation = angle;
344                 this.body.m_body.SetAngle(angle * (Math.PI / 180));
345             }
346         },
347         /**
348          * Spriteの角速度(単位はdeg/s).
349          * @type {b2Vec2}
350          */
351         angularVelocity: {
352             get: function() {
353                 return this.body.m_body.GetAngularVelocity() * (180 / Math.PI);
354             },
355             set: function(omega) {
356                 this.setAwake(true);
357                 this.body.m_body.SetAngularVelocity(omega * (Math.PI / 180));
358             }
359         },
360         /**
361          * 継続的な力を加える
362          * @param {b2Vec2} force 加える力のベクトル
363          */
364         applyForce: function(force) {
365             this.setAwake(true);
366             this.body.m_body.ApplyForce(force, this.body.m_body.GetPosition());
367         },
368         /**
369          * 瞬間的な力を加える
370          * @param {b2Vec2} impulse 加える力のベクトル
371          */
372         applyImpulse: function(impulse) {
373             this.setAwake(true);
374             this.body.m_body.ApplyImpulse(impulse, this.body.m_body.GetPosition());
375         },
376         /**
377          * 継続的な回転力を与える
378          * @param {Number} torque 加える回転力
379          */
380         applyTorque: function(torque) {
381             this.setAwake(true);
382             this.body.m_body.ApplyTorque(torque);
383         },
384         /**
385          * 物理シミュレーションされているか
386          * @type {Boolean}
387          */
388         sleep: {
389             get: function() {
390                 return this.body.m_body.IsSleepingAllowed();
391             },
392             set: function(flag) {
393                 this.setAwake(true);
394                 this.body.m_body.SetSleepingAllowed(flag);
395             }
396         },
397         /**
398          * 物理シミュレーションされていない時、物理シミュレーションを行う(sleep時は動かなくなるので)
399          * @param {Boolean} flag 物理シミュレーションを行うかどうか
400          */
401         setAwake: function(flag) {
402             this.body.m_body.SetAwake(flag);
403         },
404         /**
405          * 衝突判定
406          * @example
407          *   //bearに当たったSpriteを消す
408          *   bear.contact(function (sprite) {
409          *      sprite.destroy();
410          *   });
411          *
412          * @param {function(sprite:enchant.box2d.PhySprite)} [func] ぶつかったSpriteを引数とする関数
413          */
414         contact: function(func) {
415             var c = this.body.m_body.m_contactList;
416             if (c) {
417                 for (var contact = c.contact; contact; contact = contact.m_next) {
418                     var pos1 = contact.m_fixtureA.m_body.GetPosition().Copy();
419                     pos1.Subtract(contact.m_fixtureB.m_body.GetPosition());
420                     pos1.Multiply(WORLD_SCALE);
421                     var r1 = (contact.m_fixtureA.m_body.m_userData.width + contact.m_fixtureB.m_body.m_userData.width) / 1.5;
422                     var r2 = (contact.m_fixtureA.m_body.m_userData.height + contact.m_fixtureB.m_body.m_userData.height) / 1.5;
423                     if (Math.abs(pos1.x) <= r1 && Math.abs(pos1.y) <= r2) {
424                         //片方が自分ならもう片方をぶつかった相手として処理する
425                         if (this.body.m_body == contact.m_fixtureA.m_body)
426                             func(contact.m_fixtureB.m_body.m_userData);
427                         else if (this.body.m_body == contact.m_fixtureB.m_body)
428                             func(contact.m_fixtureA.m_body.m_userData);
429                     }
430                 }
431             }
432         },
433         /**
434          * 物体の削除
435          * removeChildではなくこちらでSpriteを取り除く
436          */
437         destroy: function() {
438             world.DestroyBody(this.body.m_body);
439             this.body.Destroy();
440             if (this.parentNode !== null) {
441                 this.parentNode.removeChild(this);
442             }
443         }
444 
445     });
446 
447     /**
448      * @scope enchant.PhyBoxSprite.prototype
449      */
450     enchant.box2d.PhyBoxSprite = enchant.Class.create(enchant.box2d.PhySprite, {
451         /**
452          * 四角形の物理シミュレーション用Sprite
453          * @example
454          *   var bear = new PhyBoxSprite(32, 32, enchant.box2d.DYNAMIC_SPRITE, 1.0, 0.5, 0.3, true);
455          *   bear.image = core.assets['chara1.gif'];
456          *
457          * @param {Number} [width] Spriteの横幅.
458          * @param {Number} [height] Spriteの高さ.
459          * @param {Boolean}   [staticOrDynamic] 静止するか動くか.
460          * @param {Number} [density] Spriteの密度.
461          * @param {Number} [friction] Spriteの摩擦.
462          * @param {Number} [restitution] Spriteの反発.
463          * @param {Boolean}   [isSleeping] Spriteが初めから物理演算を行うか.
464          * @constructs
465          * @extends enchant.box2d.PhySprite
466          */
467         initialize: function(width, height, staticOrDynamic, density, friction, restitution, isSleeping) {
468             enchant.box2d.PhySprite.call(this, width, height);
469 
470             //物理オブジェクトの生成
471             this.body = this.createPhyBox(staticOrDynamic, density, friction, restitution, isSleeping);
472         }
473     });
474 
475 
476     /**
477      * @scope enchant.PhyCircleSprite.prototype
478      */
479     enchant.box2d.PhyCircleSprite = enchant.Class.create(enchant.box2d.PhySprite, {
480         /**
481          * 円の物理シミュレーション用Sprite
482          * @example
483          *   var bear = new PhyCircleSprite(16, enchant.box2d.DYNAMIC_SPRITE, 1.0, 0.5, 0.3, true);
484          *   bear.image = core.assets['chara1.gif'];
485          *
486          @param {Number} [radius] Spriteの半径.
487          * @param {Boolean}   [staticOrDynamic] 静止するか動くか.
488          * @param {Number} [density] Spriteの密度.
489          * @param {Number} [friction] Spriteの摩擦.
490          * @param {Number} [restitution] Spriteの反発.
491          * @param {Boolean}   [isSleeping] Spriteが初めから物理演算を行うか.
492          * @constructs
493          * @extends enchant.box2d.PhySprite
494          */
495         initialize: function(radius, staticOrDynamic, density, friction, restitution, isSleeping) {
496             enchant.box2d.PhySprite.call(this, radius * 2, radius * 2);
497 
498             //物理オブジェクトの生成
499             this.body = this.createPhyCircle(staticOrDynamic, density, friction, restitution, isSleeping);
500         }
501     });
502 
503 })();
504