Home > Flash | WEB > Box2D リンケージシンボルでアニメーションさせてダッシュ&ジャンプ

Box2D リンケージシンボルでアニメーションさせてダッシュ&ジャンプ

Box2Dで、迷○組曲ボーナスステージもどき制作日記の続きです。

今回はFlashで作成したアニメーションのシンボルをリンケージさせて、
実際に表示されるキャラクターを動かしてみました。
このキャラクターは、前作ったコレで使ったやつの使い回しで、我らがドットりえッチです。
今回もまだエンドレスですw

画面をクリックして、「←」「→」で左右移動、「s」でジャンプです。

今回は、Flashでリンケージさせたシンボルを組み込んで、
Flex3でコンパイルする際に組み込んでいます。

ACTIONSCRIPT:
  1. [Embed(source = 'LabylinsSeed.swf', symbol = 'riecchi') ]
  2.         private var Riecchi:Class;
  3.         private var riecchi:MovieClip;

外部のFlashを組み込んで、シンボルをクラスとして使えるようにするには
[Embed(source='読み込むファイル名', symbol='リンケージ時に指定したクラス名')]

でOKです。

今回読み込んだLabylinsSeed.swfは以下のようなFlashです。

上からシンボル化したMovieClipで、それぞれを
"stop", "low", "middle", "high", "max", 飛んでるのが "jump"と命名してあります。

それとは別の、riecchiというMovieClipをシンボル化しています。
このMovieClipは、同じように"stop", "low","middle" ... と名前をつけたフレームをもち、
それぞれのフレームに上記でシンボル化したMovieClipをおいています。

↓現在加わっている力から、どのフレームのアニメーションを動かすか、
どのフレームで gotoAndStop() させるかを指示しています。

ACTIONSCRIPT:
  1. var vel_x = Math.abs(square.m_linearVelocity.x);
  2.                 var status:String = "stop";
  3.                 if( vel_x <0.5) status = "stop";
  4.                 else if(vel_x <2) status = "low";
  5.                 else if(vel_x <5) status = "middle";
  6.                 else status = "high"
  7.                
  8.                 if(status != square.m_userData.speed_status){
  9.                     riecchi.gotoAndStop(status);
  10.                     square.m_userData.speed_status = status;
  11.                 }

こうすることで、速度によってアニメーションを変えることができました。
ちなみにmaxは使わなかった。

方向転換時に慣性のような力を残す、フラットをとると減点なども追加しました。
前のコードに試行錯誤しながら作ったので、ソース汚いっす。

次回はグラフィックをもっと追加してみようかな・・・続く!!

↓勢いのあるソース全部

ACTIONSCRIPT:
  1. package {
  2.     import Box2D.Collision.Shapes.b2PolygonDef;
  3.     import Box2D.Collision.Shapes.b2Shape;
  4.     import Box2D.Collision.b2AABB;
  5.     import Box2D.Common.Math.b2Vec2;
  6.     import Box2D.Common.Math.b2XForm;
  7.     import Box2D.Dynamics.Contacts.b2ContactEdge;
  8.     import Box2D.Dynamics.b2Body;
  9.     import Box2D.Dynamics.b2BodyDef;
  10.     import Box2D.Dynamics.b2DebugDraw;
  11.     import Box2D.Dynamics.b2World;
  12.    
  13.     import flash.display.Bitmap;
  14.     import flash.display.MovieClip;
  15.     import flash.display.Sprite;
  16.     import flash.display.StageAlign;
  17.     import flash.events.Event;
  18.     import flash.events.KeyboardEvent;
  19.     import flash.events.MouseEvent;
  20.     import flash.text.TextField;
  21.     import flash.ui.Keyboard;
  22.     import flash.utils.*;
  23.    
  24.     /**
  25.     * @author okm
  26.     */
  27.     public class LabyrinsSuite extends Sprite{
  28.         private var world:b2World;
  29.         private var square:b2Body;
  30.         private var floor:b2Body;
  31.         // 床と箱生命体が接触しているか
  32.         private var ground:Boolean = false;
  33.        
  34.         [Embed(source='hako.png')]
  35.         private var hakoImg:Class;
  36.         private var spt:Sprite;
  37.  
  38.         [Embed(source = 'onpu.png')]
  39.         private var onpuImg:Class;
  40.        
  41.         [Embed(source = 'flat.png')]
  42.         private var flatImg:Class;
  43.        
  44.         [Embed(source = 'LabylinsSeed.swf', symbol = 'riecchi') ]
  45.         private var Riecchi:Class;
  46.         private var riecchi:MovieClip;
  47.        
  48.         private var onpArea:Sprite;
  49.         private var onpCnt:Number = 0;
  50.         private var onpCntText:TextField;
  51.        
  52.         private var timer:Number;
  53.        
  54.         public function LabyrinsSuite() {
  55.             stage.scaleMode = "noScale";
  56.             stage.align = StageAlign.TOP_LEFT;
  57.  
  58.             var textField:TextField = new TextField;
  59.             textField.text = "click me ";
  60.             textField.x = 100;
  61.             textField.y = 100;
  62.            
  63.             textField.selectable = false;
  64.             this.addChild(textField);
  65.            
  66.             // 音符とった数のカウント表示用
  67.             onpCntText = new TextField;
  68.             onpCntText.x = 10;
  69.             onpCntText.y = 10;
  70.             onpCntText.text = "0";
  71.             addChild(onpCntText);
  72.            
  73.             //var bmp:Bitmap = new hakoImg();
  74.             // 中心座標と重なるように。
  75.             //bmp.x = -18;
  76.             //bmp.y = -36;
  77.             riecchi = new Riecchi();
  78.             riecchi.gotoAndStop("low");
  79.             riecchi.x = -15.5;
  80.             riecchi.y = -29;
  81.             spt = new Sprite;
  82.             spt.visible = false;
  83.             spt.addChild(riecchi);
  84.             addChild(spt);
  85.  
  86.             stage.addEventListener(MouseEvent.CLICK, go);
  87.         }
  88.        
  89.         public function go(event:MouseEvent):void {
  90.             clearInterval(timer);
  91.             spt.visible = true;
  92.             // 音符取得数初期化
  93.             onpCntText.text = "0";
  94.             onpCnt = 0;
  95.            
  96.             if (onpArea != null) this.removeChild(onpArea);
  97.             onpArea = new Sprite;
  98.             this.addChild(onpArea);
  99.            
  100.             stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
  101.             stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
  102.             ////////////////////////////////////////
  103.             // 物理エンジンのセットアップ
  104.            
  105.             // 縦横200メートルの世界を作成
  106.             var worldAABB:b2AABB = new b2AABB();
  107.             worldAABB.lowerBound.Set(-100, -100);
  108.             worldAABB.upperBound.Set(100, 100);
  109.            
  110.             // 重力を下方向に10m/s^2とする
  111.             var gravity:b2Vec2 = new b2Vec2(0, 10);
  112.            
  113.             // 外枠と重力を指定して、物理エンジン全体をセットアップする
  114.             world = new b2World(worldAABB, gravity, true);
  115.            
  116.             /////////////////////////////////////////////
  117.             // 部屋の作成 横幅 12M 高さ 8M の部屋を作成
  118.             /////////////////////////////////////////////
  119.             // 床
  120.             // 天井の高さは 8M 床の 中心X座標は 7.5M(真ん中)
  121.             var floorBodyDef:b2BodyDef = new b2BodyDef();
  122.             // 床のIDを floor としておく
  123.             floorBodyDef.userData = new Object;
  124.             floorBodyDef.userData.spt = new Sprite;
  125.             floorBodyDef.userData.id = "floor";
  126.             floorBodyDef.position.Set(6, 8);
  127.            
  128.             // 床の形を、幅12m、厚さ20cmとする
  129.             // 指定するのはその半分の値
  130.             var floorShapeDef:b2PolygonDef = new b2PolygonDef();
  131.             floorShapeDef.friction = 0.1;
  132.             floorShapeDef.SetAsBox(6, 0.1);
  133.            
  134.             // 床のcategoryBits/maskBits
  135.             floorShapeDef.filter.maskBits = 0x01;
  136.             floorShapeDef.filter.categoryBits = 0x01;
  137.            
  138.             // 床を動かない物体として作る
  139.             floor = world.CreateBody(floorBodyDef);
  140.             floor.CreateShape(floorShapeDef);
  141.            
  142.             // 両端の壁 高さは 8M 幅 10cm
  143.             // 左壁
  144.             var leftWallBodyDef:b2BodyDef = new b2BodyDef();
  145.             leftWallBodyDef.userData = new Object;
  146.             leftWallBodyDef.userData.id = "left";
  147.             leftWallBodyDef.userData.spt = new Sprite;
  148.             leftWallBodyDef.position.Set(0.05, 4);
  149.             var wallShapeDef:b2PolygonDef = new b2PolygonDef();
  150.             wallShapeDef.friction = 0;
  151.             wallShapeDef.SetAsBox(0.05, 4);
  152.            
  153.             var leftWall:b2Body = world.CreateBody(leftWallBodyDef);
  154.             leftWall.CreateShape(wallShapeDef);
  155.            
  156.             // 右壁
  157.             var rightWallBodyDef:b2BodyDef = new b2BodyDef();
  158.             rightWallBodyDef.userData = new Object;
  159.             rightWallBodyDef.userData.id = "right";
  160.             rightWallBodyDef.userData.spt = new Sprite;
  161.             rightWallBodyDef.position.Set(11.95, 4);
  162.             var rightWall:b2Body = world.CreateBody(rightWallBodyDef);
  163.             rightWall.CreateShape(wallShapeDef);
  164.            
  165.             ////////////////////////////////////////
  166.             // 箱生命体
  167.             ////////////////////////////////////////
  168.            
  169.             // 箱の位置を左から2.5m、上から1mとする
  170.             var boxBodyDef:b2BodyDef = new b2BodyDef();
  171.             boxBodyDef.userData = new Object;
  172.             boxBodyDef.userData.id = "hako";
  173.             boxBodyDef.userData.spt = spt;
  174.             boxBodyDef.userData.speed_status = "low";
  175.             boxBodyDef.position.Set(2.5, 1);
  176.            
  177.             // 箱の幅 40cm 高さ 80cm 重さは 30kg とする
  178.             var boxShapeDef:b2PolygonDef= new b2PolygonDef();
  179.             //boxShapeDef.SetAsOrientedBox(0.3, 0.2, new b2Vec2(0, 0), 0.8);
  180.             boxShapeDef.SetAsBox(0.32, 0.57);
  181.             boxShapeDef.density = 80;        // 密度
  182.             boxShapeDef.friction = 0.6;      // 摩擦係数
  183.             boxShapeDef.restitution = 0;   // 反発係数、通常は0~1
  184.            
  185.             // 箱を動く物体として作る
  186.             var boxBody:b2Body = world.CreateBody(boxBodyDef);
  187.             boxBody.CreateShape(boxShapeDef);
  188.             // SetMassFromShapes で重力が有効になる
  189.             boxBody.SetMassFromShapes();
  190.             square = boxBody;
  191.                        
  192.             ////////////////////////////////////////
  193.             // 描画設定
  194.             var debugDraw:b2DebugDraw = new b2DebugDraw();
  195.             debugDraw.m_sprite = this;
  196.             debugDraw.m_drawScale = 45; // 1mを45ピクセルにする
  197.             debugDraw.m_fillAlpha = 0; // 不透明度
  198.             //debugDraw.m_lineThickness = 1; // 線の太さ
  199.             //debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit;
  200.            
  201.             //world.SetDebugDraw(debugDraw);
  202.            
  203.             this.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
  204.             timer = setInterval(dropBox, 1200);
  205.         }
  206.        
  207.         private function dropBox():void {
  208.            
  209.             var dropbox_x:Number = this.xRandomInt(11, 1);
  210.            
  211.             var dropboxBodyDef:b2BodyDef = new b2BodyDef();
  212.             dropboxBodyDef.userData = new Object;
  213.             dropboxBodyDef.userData.id = "dropbox";
  214.            
  215.             var onpuBmp:Bitmap;
  216.             var onpuType:String;
  217.             if(this.xRandomInt(10,0)> 8){
  218.                 onpuBmp = new flatImg();
  219.                 onpuType = "flat";
  220.             }else{
  221.                 onpuBmp = new onpuImg();
  222.                 onpuType = "normal";
  223.             }
  224.             onpuBmp.x = -5;
  225.             onpuBmp.y = -7.5;
  226.             var onpu_spt:Sprite = new Sprite;
  227.             onpu_spt.visible = false;
  228.             onpu_spt.addChild(onpuBmp);
  229.             dropboxBodyDef.userData.spt = onpu_spt;
  230.             dropboxBodyDef.userData.crash = 0;
  231.             dropboxBodyDef.userData.onpuType = onpuType;
  232.             dropboxBodyDef.angle = this.xRandomInt(5, 0) / 10;
  233.             dropboxBodyDef.position.Set(dropbox_x, 0.5);
  234.             onpArea.addChild(dropboxBodyDef.userData.spt);
  235.            
  236.             // 箱の幅 10cm 高さ 15cm 重さは 1kg とする
  237.             var dropboxShapeDef:b2PolygonDef= new b2PolygonDef();
  238.             //boxShapeDef.SetAsOrientedBox(0.3, 0.2, new b2Vec2(0, 0), 0.8);
  239.             dropboxShapeDef.SetAsBox(0.1, 0.15);
  240.             dropboxShapeDef.density = 25;        // 密度
  241.             dropboxShapeDef.friction = 0.2;      // 摩擦係数
  242.             dropboxShapeDef.restitution = 0.7;   // 反発係数、通常は0~1
  243.            
  244.             // 箱を動く物体として作る
  245.             dropboxShapeDef.filter.categoryBits = 0x01;
  246.             dropboxShapeDef.filter.maskBits = 0x01;
  247.            
  248.             var dropbox:b2Body = world.CreateBody(dropboxBodyDef);
  249.             dropbox.CreateShape(dropboxShapeDef)
  250.             dropbox.m_linearVelocity.x = this.xRandomInt(2, -2);
  251.             dropbox.SetMassFromShapes();
  252.         }
  253.        
  254.         private var m_InputL:Number = 0;
  255.         private var m_InputR:Number = 0;
  256.         private var m_InputS:Number = 0;
  257.         private var m_Dir:Number = 0;
  258.         private var moveTime:Number = 0;
  259.        
  260.         // キーが押されたときの処理
  261.         private function keyDownHandler(event:KeyboardEvent):void {
  262.             square.WakeUp();
  263.             if(event.keyCode == Keyboard.LEFT && m_InputL != 1){
  264.                 m_InputL = 1;
  265.                 m_InputR = 0;
  266.                 m_Dir = -1.0;
  267.                 moveTime = 0;
  268.             }
  269.             if(event.keyCode == Keyboard.RIGHT && m_InputR != 1){
  270.                 m_InputR = 1;
  271.                 m_InputL = 0;
  272.                 m_Dir = 1.0;
  273.                 moveTime = 0;
  274.             }
  275.             if(event.keyCode == 83){ // s
  276.                 m_InputS = 1;
  277.             }
  278.         }
  279.         private function keyUpHandler(event:KeyboardEvent):void {
  280.             if(event.keyCode == Keyboard.LEFT){
  281.                 m_InputL = 0;
  282.             }
  283.             if(event.keyCode == Keyboard.RIGHT){
  284.                 m_InputR = 0;
  285.             }
  286.             if(event.keyCode == 83){ // s
  287.                 m_InputS = 0;
  288.             }
  289.         }
  290.  
  291.         private function enterFrameHandler(event:Event):void {
  292.            
  293.             for (var bb:b2Body = world.m_bodyList; bb; bb = bb.m_next) {
  294.                 if (bb.m_userData != null && bb.m_userData.spt is Sprite) {
  295.                     if (bb.m_userData.id == "dropbox" ) {
  296.                         // なんか音符たまに下で死んでるからさよならする
  297.                         if(bb.IsFrozen()){
  298.                             shape = bb.GetShapeList();
  299.                             shape.m_filter.maskBits = 0x00;
  300.                             bb.m_linearVelocity.y = 0.2;
  301.                         }
  302.                         var onp_spt:Sprite = bb.m_userData.spt;
  303.                         onp_spt.visible = true;
  304.                         // もし箱生命体と触れていたら消す
  305.                         if(onp_spt.hitTestObject(square.m_userData.spt)){
  306.                             onpArea.removeChild(onp_spt);
  307.                             world.DestroyBody(bb);
  308.                            
  309.                             // 音符取得数追加
  310.                             if(bb.m_userData.onpuType == "normal")  onpCnt++;
  311.                             else if(bb.m_userData.onpuType == "flat") onpCnt--;
  312.                             onpCntText.text = onpCnt.toString();
  313.                         }
  314.                        
  315.                         bb.m_userData.spt.rotation = bb.GetAngle() * (180 / Math.PI);
  316.                     }   
  317.                     bb.m_userData.spt.x = bb.GetPosition().x * 45;
  318.                     bb.m_userData.spt.y = bb.GetPosition().y * 45;
  319.                 }
  320.             }
  321.            
  322.             // 箱生命体は、決して倒れない!
  323.             var xform:b2XForm = square.GetXForm();
  324.             square.SetXForm(xform.position, 0);
  325.  
  326.             // 衝突判定 床
  327.             var shape:b2Shape;
  328.             ground = false;
  329.             for (var ce:b2ContactEdge = floor.m_contactList; ce != null; ce = ce.next) { 
  330.                 var obj1:b2Body = ce.contact.GetShape1().m_body;//1つ目のオブジェクト
  331.                 var obj2:b2Body = ce.contact.GetShape2().m_body;//2つ目のオブジェクト
  332.                 if(obj1.m_userData && obj2.m_userData && ce.contact.GetManifoldCount() == 1){ 
  333.                     if(obj2.m_userData.id == "dropbox"){
  334.                         if (obj2.m_userData.crash> 2){
  335.                             shape = obj2.GetShapeList();
  336.                             shape.m_filter.maskBits = 0x00;
  337.                         }else {
  338.                             obj2.m_userData.crash++;
  339.                         }
  340.                     }else if (obj2.m_userData.id == "hako") { // 地面とプレーヤーが接触
  341.                         ground = true;
  342.                     }
  343.                 }
  344.             }
  345.            
  346.             if (m_InputS == 1 && ground ) {
  347.                 square.m_linearVelocity.y = -7;
  348.             }
  349.             if (m_InputL> 0) {
  350.                 var initForce:Number = 0.3;
  351.                 if( square.m_linearVelocity.x> 0){
  352.                     initForce -= square.m_linearVelocity.x;
  353.                 }
  354.                 var leftForce:Number = this.easeInOutQuad(moveTime, initForce, 6, 30);
  355.                 if (moveTime <30) moveTime++;
  356.                 square.m_linearVelocity.x = -leftForce;
  357.                 square.m_userData.spt.scaleX = -1;
  358.             }
  359.             if (m_InputR> 0) {
  360.                 var initForce:Number = 0.3;
  361.                 if( square.m_linearVelocity.x <0){
  362.                     initForce += square.m_linearVelocity.x;
  363.                 }
  364.                 var rightForce:Number = this.easeInOutQuad(moveTime, initForce, 6, 30);
  365.                 if (moveTime <30) moveTime++;
  366.                 square.m_linearVelocity.x = rightForce;
  367.                 square.m_userData.spt.scaleX = 1;
  368.             }
  369.             if (world == null) {
  370.                 return;
  371.             }
  372.            
  373.             //MovieClip描画
  374.             // 床にいないときはジャンプ
  375.             if(!ground){
  376.                 riecchi.gotoAndStop("jump");
  377.                 square.m_userData.speed_status = "jump";
  378.             }
  379.             else{
  380.                 var vel_x = Math.abs(square.m_linearVelocity.x);
  381.                 var status:String = "stop";
  382.                 if( vel_x <0.5) status = "stop";
  383.                 else if(vel_x <2) status = "low";
  384.                 else if(vel_x <5) status = "middle";
  385.                 else status = "high"
  386.                
  387.                 if(status != square.m_userData.speed_status){
  388.                     riecchi.gotoAndStop(status);
  389.                     square.m_userData.speed_status = status;
  390.                 }
  391.             }
  392.            
  393.            
  394.             // Flashはデフォルトで秒間24フレームなので、
  395.             // 物理シミュレーションを1/24秒進める
  396.             world.Step(1 / 24, 10);
  397.         }
  398.        
  399.         /**
  400.          * t = time
  401.          * b = beginning value
  402.          * c = change in value
  403.          * d = duration
  404.          */
  405.         public function easeInOutQuad(t:Number, b:Number, c:Number, d:Number):Number {
  406.             if ((t/=d/2) <1) return c/2*t*t + b;
  407.             return -c/2 * ((--t)*(t-2) - 1) + b;
  408.         };
  409.        
  410.         public function xRandomInt (nMax:Number, nMin:Number):Number {
  411.             // nMinからnMaxまでのランダムな整数を返す
  412.             var nRandomInt:Number = Math.floor(Math.random()*(nMax-nMin+1))+nMin;
  413.             return nRandomInt;
  414.         }
  415.     }
  416. }

Comments:4

ninben 09-10-31 (土) 5:46

初めまして。
古い記事なので気付いてもらえるか分かりませんが、この記事を参考にflashで2Dアクション
アドベンチャー的な物を試作しようとしているものです。
ここのソースでは、Embedを使用していますが、自分はflaからムービークリップをリンケージして、
指定のフレームラベルで切り替えをしようとしています。
しかしどうもうまくいきません…。
上記の制作方法の場合、gotoAndPlay等はどう指定するのでしょう?
もしお手すきの時間があれば教えていただきたいです。
ぶしつけな質問ですいません。

okm 09-11-02 (月) 4:27

メールが来るので気づきますよ~はじめまして。

クラス名をつけたリンケージシンボルのインスタンスさえ作ってしまえば、
そのインスタンスで gotoAndPlayできないですかね。。。

ちょっと最近忙しいのでスキをみて私も試してみます。
また余裕があればみてやってください~

ninben 09-11-03 (火) 5:23

すいません、ありがとうございます。
なんとか出来ました!
未熟なせいで、どう出来たのか言葉に出来ないんですけども…!

okm 09-11-04 (水) 10:56

おおーよかったっす。
ちょっと俺もひさびさにゲーム的なもの作りたくなってきたところです。
お互いがんばりましょう!

Comment Form
Remember personal info

Trackbacks:2

Trackback URL for this entry
http://parpue.net/web/46/trackback
Listed below are links to weblogs that reference
Box2D リンケージシンボルでアニメーションさせてダッシュ&ジャンプ from parpue.net
pingback from parpue.net - Box2D でバスケのフリースローゲーム 08-07-24 (木) 3:34

[...] 別で作っている組曲ボーナスのFlashのコードが基本です。 今回のは勢いでばばーっと作ったので、とてもソース全公開できない、、、。 [...]

pingback from parpue.net - Box2D オブジェクトにオブジェクトをあてて回転させる 08-10-01 (水) 4:04

[...] これの続きです。 ってマジで久しぶりだ(^-^;; [...]

Home > Flash | WEB > Box2D リンケージシンボルでアニメーションさせてダッシュ&ジャンプ

リンク
chocolataste-planner
millon

サーチ
Feeds
Meta
blog ranking ブログランキング・にほんブログ村へ
にほんブログ村 テクノラティのお気に入りに追加する

Return to page top