Box2Dで、迷○組曲ボーナスステージもどき制作日記の続きです。
今回はFlashで作成したアニメーションのシンボルをリンケージさせて、
実際に表示されるキャラクターを動かしてみました。
このキャラクターは、前作ったコレで使ったやつの使い回しで、我らがドットりえッチです。
今回もまだエンドレスですw
画面をクリックして、「←」「→」で左右移動、「s」でジャンプです。
今回は、Flashでリンケージさせたシンボルを組み込んで、
Flex3でコンパイルする際に組み込んでいます。
- [Embed(source = 'LabylinsSeed.swf', symbol = 'riecchi') ]
- private var Riecchi:Class;
- 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() させるかを指示しています。
- var vel_x = Math.abs(square.m_linearVelocity.x);
- var status:String = "stop";
- if( vel_x <0.5) status = "stop";
- else if(vel_x <2) status = "low";
- else if(vel_x <5) status = "middle";
- else status = "high"
- if(status != square.m_userData.speed_status){
- riecchi.gotoAndStop(status);
- square.m_userData.speed_status = status;
- }
こうすることで、速度によってアニメーションを変えることができました。
ちなみにmaxは使わなかった。
方向転換時に慣性のような力を残す、フラットをとると減点なども追加しました。
前のコードに試行錯誤しながら作ったので、ソース汚いっす。
次回はグラフィックをもっと追加してみようかな・・・続く!!
↓勢いのあるソース全部
- package {
- import Box2D.Collision.Shapes.b2PolygonDef;
- import Box2D.Collision.Shapes.b2Shape;
- import Box2D.Collision.b2AABB;
- import Box2D.Common.Math.b2Vec2;
- import Box2D.Common.Math.b2XForm;
- import Box2D.Dynamics.Contacts.b2ContactEdge;
- import Box2D.Dynamics.b2Body;
- import Box2D.Dynamics.b2BodyDef;
- import Box2D.Dynamics.b2DebugDraw;
- import Box2D.Dynamics.b2World;
- import flash.display.Bitmap;
- import flash.display.MovieClip;
- import flash.display.Sprite;
- import flash.display.StageAlign;
- import flash.events.Event;
- import flash.events.KeyboardEvent;
- import flash.events.MouseEvent;
- import flash.text.TextField;
- import flash.ui.Keyboard;
- import flash.utils.*;
- /**
- * @author okm
- */
- public class LabyrinsSuite extends Sprite{
- private var world:b2World;
- private var square:b2Body;
- private var floor:b2Body;
- // 床と箱生命体が接触しているか
- private var ground:Boolean = false;
- [Embed(source='hako.png')]
- private var hakoImg:Class;
- private var spt:Sprite;
- [Embed(source = 'onpu.png')]
- private var onpuImg:Class;
- [Embed(source = 'flat.png')]
- private var flatImg:Class;
- [Embed(source = 'LabylinsSeed.swf', symbol = 'riecchi') ]
- private var Riecchi:Class;
- private var riecchi:MovieClip;
- private var onpArea:Sprite;
- private var onpCnt:Number = 0;
- private var onpCntText:TextField;
- private var timer:Number;
- public function LabyrinsSuite() {
- stage.scaleMode = "noScale";
- stage.align = StageAlign.TOP_LEFT;
- var textField:TextField = new TextField;
- textField.text = "click me ";
- textField.x = 100;
- textField.y = 100;
- textField.selectable = false;
- this.addChild(textField);
- // 音符とった数のカウント表示用
- onpCntText = new TextField;
- onpCntText.x = 10;
- onpCntText.y = 10;
- onpCntText.text = "0";
- addChild(onpCntText);
- //var bmp:Bitmap = new hakoImg();
- // 中心座標と重なるように。
- //bmp.x = -18;
- //bmp.y = -36;
- riecchi = new Riecchi();
- riecchi.gotoAndStop("low");
- riecchi.x = -15.5;
- riecchi.y = -29;
- spt = new Sprite;
- spt.visible = false;
- spt.addChild(riecchi);
- addChild(spt);
- stage.addEventListener(MouseEvent.CLICK, go);
- }
- public function go(event:MouseEvent):void {
- clearInterval(timer);
- spt.visible = true;
- // 音符取得数初期化
- onpCntText.text = "0";
- onpCnt = 0;
- if (onpArea != null) this.removeChild(onpArea);
- onpArea = new Sprite;
- this.addChild(onpArea);
- stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
- stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
- ////////////////////////////////////////
- // 物理エンジンのセットアップ
- // 縦横200メートルの世界を作成
- var worldAABB:b2AABB = new b2AABB();
- worldAABB.lowerBound.Set(-100, -100);
- worldAABB.upperBound.Set(100, 100);
- // 重力を下方向に10m/s^2とする
- var gravity:b2Vec2 = new b2Vec2(0, 10);
- // 外枠と重力を指定して、物理エンジン全体をセットアップする
- world = new b2World(worldAABB, gravity, true);
- /////////////////////////////////////////////
- // 部屋の作成 横幅 12M 高さ 8M の部屋を作成
- /////////////////////////////////////////////
- // 床
- // 天井の高さは 8M 床の 中心X座標は 7.5M(真ん中)
- var floorBodyDef:b2BodyDef = new b2BodyDef();
- // 床のIDを floor としておく
- floorBodyDef.userData = new Object;
- floorBodyDef.userData.spt = new Sprite;
- floorBodyDef.userData.id = "floor";
- floorBodyDef.position.Set(6, 8);
- // 床の形を、幅12m、厚さ20cmとする
- // 指定するのはその半分の値
- var floorShapeDef:b2PolygonDef = new b2PolygonDef();
- floorShapeDef.friction = 0.1;
- floorShapeDef.SetAsBox(6, 0.1);
- // 床のcategoryBits/maskBits
- floorShapeDef.filter.maskBits = 0x01;
- floorShapeDef.filter.categoryBits = 0x01;
- // 床を動かない物体として作る
- floor = world.CreateBody(floorBodyDef);
- floor.CreateShape(floorShapeDef);
- // 両端の壁 高さは 8M 幅 10cm
- // 左壁
- var leftWallBodyDef:b2BodyDef = new b2BodyDef();
- leftWallBodyDef.userData = new Object;
- leftWallBodyDef.userData.id = "left";
- leftWallBodyDef.userData.spt = new Sprite;
- leftWallBodyDef.position.Set(0.05, 4);
- var wallShapeDef:b2PolygonDef = new b2PolygonDef();
- wallShapeDef.friction = 0;
- wallShapeDef.SetAsBox(0.05, 4);
- var leftWall:b2Body = world.CreateBody(leftWallBodyDef);
- leftWall.CreateShape(wallShapeDef);
- // 右壁
- var rightWallBodyDef:b2BodyDef = new b2BodyDef();
- rightWallBodyDef.userData = new Object;
- rightWallBodyDef.userData.id = "right";
- rightWallBodyDef.userData.spt = new Sprite;
- rightWallBodyDef.position.Set(11.95, 4);
- var rightWall:b2Body = world.CreateBody(rightWallBodyDef);
- rightWall.CreateShape(wallShapeDef);
- ////////////////////////////////////////
- // 箱生命体
- ////////////////////////////////////////
- // 箱の位置を左から2.5m、上から1mとする
- var boxBodyDef:b2BodyDef = new b2BodyDef();
- boxBodyDef.userData = new Object;
- boxBodyDef.userData.id = "hako";
- boxBodyDef.userData.spt = spt;
- boxBodyDef.userData.speed_status = "low";
- boxBodyDef.position.Set(2.5, 1);
- // 箱の幅 40cm 高さ 80cm 重さは 30kg とする
- var boxShapeDef:b2PolygonDef= new b2PolygonDef();
- //boxShapeDef.SetAsOrientedBox(0.3, 0.2, new b2Vec2(0, 0), 0.8);
- boxShapeDef.SetAsBox(0.32, 0.57);
- boxShapeDef.density = 80; // 密度
- boxShapeDef.friction = 0.6; // 摩擦係数
- boxShapeDef.restitution = 0; // 反発係数、通常は0~1
- // 箱を動く物体として作る
- var boxBody:b2Body = world.CreateBody(boxBodyDef);
- boxBody.CreateShape(boxShapeDef);
- // SetMassFromShapes で重力が有効になる
- boxBody.SetMassFromShapes();
- square = boxBody;
- ////////////////////////////////////////
- // 描画設定
- var debugDraw:b2DebugDraw = new b2DebugDraw();
- debugDraw.m_sprite = this;
- debugDraw.m_drawScale = 45; // 1mを45ピクセルにする
- debugDraw.m_fillAlpha = 0; // 不透明度
- //debugDraw.m_lineThickness = 1; // 線の太さ
- //debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit;
- //world.SetDebugDraw(debugDraw);
- this.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
- timer = setInterval(dropBox, 1200);
- }
- private function dropBox():void {
- var dropbox_x:Number = this.xRandomInt(11, 1);
- var dropboxBodyDef:b2BodyDef = new b2BodyDef();
- dropboxBodyDef.userData = new Object;
- dropboxBodyDef.userData.id = "dropbox";
- var onpuBmp:Bitmap;
- var onpuType:String;
- if(this.xRandomInt(10,0)> 8){
- onpuBmp = new flatImg();
- onpuType = "flat";
- }else{
- onpuBmp = new onpuImg();
- onpuType = "normal";
- }
- onpuBmp.x = -5;
- onpuBmp.y = -7.5;
- var onpu_spt:Sprite = new Sprite;
- onpu_spt.visible = false;
- onpu_spt.addChild(onpuBmp);
- dropboxBodyDef.userData.spt = onpu_spt;
- dropboxBodyDef.userData.crash = 0;
- dropboxBodyDef.userData.onpuType = onpuType;
- dropboxBodyDef.angle = this.xRandomInt(5, 0) / 10;
- dropboxBodyDef.position.Set(dropbox_x, 0.5);
- onpArea.addChild(dropboxBodyDef.userData.spt);
- // 箱の幅 10cm 高さ 15cm 重さは 1kg とする
- var dropboxShapeDef:b2PolygonDef= new b2PolygonDef();
- //boxShapeDef.SetAsOrientedBox(0.3, 0.2, new b2Vec2(0, 0), 0.8);
- dropboxShapeDef.SetAsBox(0.1, 0.15);
- dropboxShapeDef.density = 25; // 密度
- dropboxShapeDef.friction = 0.2; // 摩擦係数
- dropboxShapeDef.restitution = 0.7; // 反発係数、通常は0~1
- // 箱を動く物体として作る
- dropboxShapeDef.filter.categoryBits = 0x01;
- dropboxShapeDef.filter.maskBits = 0x01;
- var dropbox:b2Body = world.CreateBody(dropboxBodyDef);
- dropbox.CreateShape(dropboxShapeDef);
- dropbox.m_linearVelocity.x = this.xRandomInt(2, -2);
- dropbox.SetMassFromShapes();
- }
- private var m_InputL:Number = 0;
- private var m_InputR:Number = 0;
- private var m_InputS:Number = 0;
- private var m_Dir:Number = 0;
- private var moveTime:Number = 0;
- // キーが押されたときの処理
- private function keyDownHandler(event:KeyboardEvent):void {
- square.WakeUp();
- if(event.keyCode == Keyboard.LEFT && m_InputL != 1){
- m_InputL = 1;
- m_InputR = 0;
- m_Dir = -1.0;
- moveTime = 0;
- }
- if(event.keyCode == Keyboard.RIGHT && m_InputR != 1){
- m_InputR = 1;
- m_InputL = 0;
- m_Dir = 1.0;
- moveTime = 0;
- }
- if(event.keyCode == 83){ // s
- m_InputS = 1;
- }
- }
- private function keyUpHandler(event:KeyboardEvent):void {
- if(event.keyCode == Keyboard.LEFT){
- m_InputL = 0;
- }
- if(event.keyCode == Keyboard.RIGHT){
- m_InputR = 0;
- }
- if(event.keyCode == 83){ // s
- m_InputS = 0;
- }
- }
- private function enterFrameHandler(event:Event):void {
- for (var bb:b2Body = world.m_bodyList; bb; bb = bb.m_next) {
- if (bb.m_userData != null && bb.m_userData.spt is Sprite) {
- if (bb.m_userData.id == "dropbox" ) {
- // なんか音符たまに下で死んでるからさよならする
- if(bb.IsFrozen()){
- shape = bb.GetShapeList();
- shape.m_filter.maskBits = 0x00;
- bb.m_linearVelocity.y = 0.2;
- }
- var onp_spt:Sprite = bb.m_userData.spt;
- onp_spt.visible = true;
- // もし箱生命体と触れていたら消す
- if(onp_spt.hitTestObject(square.m_userData.spt)){
- onpArea.removeChild(onp_spt);
- world.DestroyBody(bb);
- // 音符取得数追加
- if(bb.m_userData.onpuType == "normal") onpCnt++;
- else if(bb.m_userData.onpuType == "flat") onpCnt--;
- onpCntText.text = onpCnt.toString();
- }
- bb.m_userData.spt.rotation = bb.GetAngle() * (180 / Math.PI);
- }
- bb.m_userData.spt.x = bb.GetPosition().x * 45;
- bb.m_userData.spt.y = bb.GetPosition().y * 45;
- }
- }
- // 箱生命体は、決して倒れない!
- var xform:b2XForm = square.GetXForm();
- square.SetXForm(xform.position, 0);
- // 衝突判定 床
- var shape:b2Shape;
- ground = false;
- for (var ce:b2ContactEdge = floor.m_contactList; ce != null; ce = ce.next) {
- var obj1:b2Body = ce.contact.GetShape1().m_body;//1つ目のオブジェクト
- var obj2:b2Body = ce.contact.GetShape2().m_body;//2つ目のオブジェクト
- if(obj1.m_userData && obj2.m_userData && ce.contact.GetManifoldCount() == 1){
- if(obj2.m_userData.id == "dropbox"){
- if (obj2.m_userData.crash> 2){
- shape = obj2.GetShapeList();
- shape.m_filter.maskBits = 0x00;
- }else {
- obj2.m_userData.crash++;
- }
- }else if (obj2.m_userData.id == "hako") { // 地面とプレーヤーが接触
- ground = true;
- }
- }
- }
- if (m_InputS == 1 && ground ) {
- square.m_linearVelocity.y = -7;
- }
- if (m_InputL> 0) {
- var initForce:Number = 0.3;
- if( square.m_linearVelocity.x> 0){
- initForce -= square.m_linearVelocity.x;
- }
- var leftForce:Number = this.easeInOutQuad(moveTime, initForce, 6, 30);
- if (moveTime <30) moveTime++;
- square.m_linearVelocity.x = -leftForce;
- square.m_userData.spt.scaleX = -1;
- }
- if (m_InputR> 0) {
- var initForce:Number = 0.3;
- if( square.m_linearVelocity.x <0){
- initForce += square.m_linearVelocity.x;
- }
- var rightForce:Number = this.easeInOutQuad(moveTime, initForce, 6, 30);
- if (moveTime <30) moveTime++;
- square.m_linearVelocity.x = rightForce;
- square.m_userData.spt.scaleX = 1;
- }
- if (world == null) {
- return;
- }
- //MovieClip描画
- // 床にいないときはジャンプ
- if(!ground){
- riecchi.gotoAndStop("jump");
- square.m_userData.speed_status = "jump";
- }
- else{
- var vel_x = Math.abs(square.m_linearVelocity.x);
- var status:String = "stop";
- if( vel_x <0.5) status = "stop";
- else if(vel_x <2) status = "low";
- else if(vel_x <5) status = "middle";
- else status = "high"
- if(status != square.m_userData.speed_status){
- riecchi.gotoAndStop(status);
- square.m_userData.speed_status = status;
- }
- }
- // Flashはデフォルトで秒間24フレームなので、
- // 物理シミュレーションを1/24秒進める
- world.Step(1 / 24, 10);
- }
- /**
- * t = time
- * b = beginning value
- * c = change in value
- * d = duration
- */
- public function easeInOutQuad(t:Number, b:Number, c:Number, d:Number):Number {
- if ((t/=d/2) <1) return c/2*t*t + b;
- return -c/2 * ((--t)*(t-2) - 1) + b;
- };
- public function xRandomInt (nMax:Number, nMin:Number):Number {
- // nMinからnMaxまでのランダムな整数を返す
- var nRandomInt:Number = Math.floor(Math.random()*(nMax-nMin+1))+nMin;
- return nRandomInt;
- }
- }
- }
- Newer: 2Dのバスケ フリースローゲーム思いついてみる。
- Older: ベジェ曲線と、すごい単純な波の表現
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
-
おおーよかったっす。
ちょっと俺もひさびさにゲーム的なもの作りたくなってきたところです。
お互いがんばりましょう!
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
-
[...] これの続きです。 ってマジで久しぶりだ(^-^;; [...]


