swf
「敵を倒した時のセリフ」「アレな床」に対応。
- 左右キー
- 移動
- 上キー
- ジャンプ
- Sキー
- ショット
- Rキー
- リセット
状況
そもそもActionScriptにはイベント処理用の何かが用意されているではないだろうか、と作ってる間に気付いたが、とりあえず作ってしまう。
今回のイベントまわりの設計は以下のようになっている。
- 概要
- 全てのイベントはIEventを継承する
- IEventは「何のイベントか」を表すm_Typeを文字列で持つ
- オブジェクトはその「何のイベントか(文字列)」と「それに対応する処理(関数)」のマップを内部で持つ
- オブジェクトマネージャは、イベントを受け取ったらそのm_Typeをもとに、各オブジェクトが対応する処理を持っているかチェックし、持っていたら実行する
- 流れ
- 投げる側&届くまで
- オブジェクトがDispatchEventという関数で、オブジェクトマネージャにイベントを渡す
- オブジェクトマネージャは、一旦それをリストとして保持する
- オブジェクトマネージャのUpdateの時に、それらのイベントを一つ一つ、以下のように処理する
- イベントが共通で持っているm_Typeを持ってくる
- オブジェクトの「文字列→関数」のマップのキーとしてそれを試す
- 対応する関数があれば、その関数にイベントを引数として渡して実行する
- 受け取る側
- 関数を用意する
- RegisterEventFuncで、「イベント名→その関数」となるように登録
- オブジェクトマネージャから、イベントが届いたら対応する関数が呼ばれる
- 投げる側&届くまで
もう少し綺麗な説明ができればよいが、ひとまずこんな感じで。
あと、「Sleepを活かしたオブジェクト」を作ってみたかったので作った。これは、既存のオブジェクトをベースに、b2BodyDefのisSleepingをtrueにしてからコリジョンを作るだけでOK。最初はSleepで登場するので静止しているが、なんらかのコリジョンに接すればその後は普通の物理挙動をする。なので、上のような「触れると落下する床」が作れる。ショットを連射すればわかるが、敵の破片に触れても落下する。プレイヤーのショットに触れても落下する。ここらへんを活かしたギミックが作れるはず。
やることはわりと残っているが、土日のコーディングとの兼ね合いでまずは「セーブ&ロードがしやすいようにコードの整理」と「ステージエディットで色々試せるようにオブジェクトを作成」を中心に進める予定。プレイヤーまわりの処理はGUIとかセーブとか関係ないから後回し。1日1個オブジェクトを作って、セーブ&ロードまわりの整理をする感じになると思う。
追記:まだ処理落ち時にコリジョンをすり抜けるバグが残っている模様。これは無理にin_DeltaTimeを正確にするのではなく、in_DeltaTimeの最大時間を制限すべきかもしれない。
コードはいつものように折りたたみ。
//mxmlc Main.as //author Show=O=Healer package { import Box2D.Dynamics.*; import Box2D.Dynamics.Contacts.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; import flash.events.Event; import flash.display.*; import flash.text.TextField; import flash.utils.*; //Input import flash.ui.Keyboard; import flash.events.KeyboardEvent; //AA import flash.geom.*; [SWF(backgroundColor="#ffffff", width="400", height="300")] public class Main extends Sprite { //Base Sprite private var m_SpriteGame:Sprite;//プレイヤーなどを表示するためのスプライト private var m_SpriteInterface:Sprite;//HPの残量などを表示するためのスプライト //Player private var m_Player:CPlayer; //Param //ステージの範囲 public static const RANGE_LX:Number = 0; public static const RANGE_RX:Number = 600; public static const RANGE_UY:Number = 0; public static const RANGE_DY:Number = 300; //画面の大きさ public static const CAMERA_W:Number = 400; public static const CAMERA_H:Number = 300; public static const FLOOR_UY:Number = 250; public static const FLOOR_DY:Number = 370; public static const FLOOR_DAMAGE_LX:Number = 100; public static const FLOOR_DAMAGE_RX:Number = 1000;//900; public static const BLOCK_X:Number = 275; public static const BLOCK_NUM:int = 6; public static const PLAYER_X:Number = 50; public static const PLAYER_Y:Number = 220; public static const KEY_R:int = 82; public function Main() { stage.scaleMode = "noScale"; stage.align = "TL"; var text:TextField = new TextField(); text.text = "CLICK TO START!!!"; text.x = text.y = 100; addChild(text); stage.addEventListener("click", function(event:Event):void { if(text.visible){ Init(); } text.visible = false; }); } //とりあえず、ちゃんとフォーカスがもらえるように、クリックで開始することにする private function Init():void { m_SpriteGame = new Sprite(); addChild(m_SpriteGame); m_SpriteInterface = new Sprite(); addChild(m_SpriteInterface); Reset(); //毎フレームUpdateを呼ぶ addEventListener("enterFrame", function(event:Event):void { Update(); }); //リスタートなどのキーボード処理 stage.addEventListener(KeyboardEvent.KEY_DOWN, OnKeyDown); } //リスタートのために、初期化処理と共通部分を分離 private function Reset():void { ObjectManager.Reset(); PhysManager.Reset(); var CenterX:Number = (RANGE_LX + RANGE_RX)/2.0; {//Floor Left var floorL:CFloor = new CFloor(RANGE_LX + (RANGE_RX-RANGE_LX)*0/7, RANGE_LX + (RANGE_RX-RANGE_LX)*1/7, FLOOR_UY, FLOOR_DY); floorL.OnRegist(m_SpriteGame); } {//Floor Center var floorC:CFloor = new CFloor(RANGE_LX + (RANGE_RX-RANGE_LX)*2/7, RANGE_LX + (RANGE_RX-RANGE_LX)*3/7, FLOOR_UY, FLOOR_UY+CBlock.COLLISION_H); floorC.OnRegist(m_SpriteGame); } {//Fall Block var startSleep:Boolean = true; var block:CBlock = new CBlock(startSleep); block.SetPos(RANGE_LX + (RANGE_RX-RANGE_LX)*4.5/7, FLOOR_UY + CBlock.COLLISION_H/2); block.OnRegist(m_SpriteGame); } {//Floor Right var floorR:CFloor = new CFloor(RANGE_LX + (RANGE_RX-RANGE_LX)*6/7, RANGE_LX + (RANGE_RX-RANGE_LX)*7/7, FLOOR_UY, FLOOR_DY); floorR.OnRegist(m_SpriteGame); } // {//Floor Damage // var floorDamage:CFloor_Damage = new CFloor_Damage(FLOOR_DAMAGE_LX, FLOOR_DAMAGE_RX, FLOOR_UY, FLOOR_DY); // floorDamage.OnRegist(m_SpriteGame); // } {//Wall Left var wallL:CFloor = new CFloor(RANGE_LX-10, RANGE_LX, RANGE_UY, RANGE_DY); wallL.OnRegist(m_SpriteGame); } {//Wall Right var wallR:CFloor = new CFloor(RANGE_RX, RANGE_RX+10, RANGE_UY, RANGE_DY); wallR.OnRegist(m_SpriteGame); } // {//Center Wall // var wallC:CFloor = new CFloor(CenterX-10, CenterX+10, FLOOR_UY-40, FLOOR_UY-20); // wallC.OnRegist(m_SpriteGame); // } {//Player m_Player = new CPlayer(stage); m_Player.SetPos(PLAYER_X, PLAYER_Y); m_Player.OnRegist(m_SpriteGame); } // {//Blocks // var block:CBlock = new CBlock(); // block.SetPos(FLOOR_DAMAGE_LX - 10, FLOOR_UY - CBlock.COLLISION_H/2); // block.OnRegist(m_SpriteGame); // } /* {//Enemy const ENEMY_NUM:int = 10; for(var e:int = 0; e < ENEMY_NUM; ++e){ var enemy_x:Number = RANGE_LX +0.2*(RANGE_RX - RANGE_LX) + 0.8*(RANGE_RX - RANGE_LX) * e/ENEMY_NUM; var enemy_y:Number = FLOOR_UY - 20; var enemy:CEnemy = new CEnemy(); enemy.SetPos(enemy_x, enemy_y); enemy.OnRegist(m_SpriteGame); } } //*/ {//Enemy1 var enemy1:CEnemy = new CEnemy(); enemy1.SetPos(RANGE_LX + (RANGE_RX-RANGE_LX)*2.5/7, FLOOR_UY - CBlock.COLLISION_H/2 - 20); enemy1.OnRegist(m_SpriteGame); } // {//Enemy2 // var enemy2:CEnemy = new CEnemy(); // enemy2.SetPos(CenterX, FLOOR_UY - 50); // enemy2.OnRegist(m_SpriteGame); // } {//Goal var goal:CGoal = new CGoal(m_Player); goal.SetPos(RANGE_RX-50, FLOOR_UY-50); goal.OnRegist(m_SpriteGame); } //タイマーのリセット m_PreTime = getTimer(); } private function OnKeyDown(event:KeyboardEvent):void{ if(event.keyCode == KEY_R){ Reset(); } } //=Update= private var m_PreTime:Number = 0.0; public function Update():void { // var deltaTime:Number = 1.0 / 30.0; var nowTime:Number = getTimer(); var deltaTime:Number = (nowTime - m_PreTime) / 1000.0;//ミリ秒→秒 m_PreTime = nowTime; //=Object //プレイヤや弾などのUpdateを呼ぶ ObjectManager.Update(deltaTime); //=Physics //物理エンジンを進める PhysManager.Update(deltaTime); //=Camera UpdateCamera(); /* //=Render graphics.clear(); for (var bb:b2Body = PhysManager.Instance.m_World.m_bodyList; bb; bb = bb.m_next) { //primitive描画っぽいもの for (var s:b2Shape = bb.GetShapeList(); s != null; s = s.GetNext()) { DrawShape(s); } } //*/ } private function UpdateCamera():void{ var trgX:Number = m_Player.x - CAMERA_W/2.0; var trgY:Number = m_Player.y - CAMERA_H/2.0; if(trgX < RANGE_LX){ trgX = RANGE_LX; } if(trgY < RANGE_UY){ trgY = RANGE_UY; } if(trgX > RANGE_RX - CAMERA_W){ trgX = RANGE_RX - CAMERA_W; } if(trgY > RANGE_DY - CAMERA_H){ trgY = RANGE_DY - CAMERA_H; } m_SpriteGame.x = -trgX; m_SpriteGame.y = -trgY; } //=Render= /* //判定の可視化などで重宝するので、もうしばらく残しておく public function DrawShape(shape:b2Shape):void { //box if(shape.m_type == b2Shape.e_polyShape) { var drawBox:Function = function():void{ var poly:b2PolyShape = shape as b2PolyShape; var tV:b2Vec2 = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i])); graphics.beginFill(0x999999, 1); graphics.lineStyle(1,0xffffff,1); graphics.moveTo(tV.x * PhysManager.PHYS_SCALE, tV.y * PhysManager.PHYS_SCALE); for (var i:int = 0; i < poly.m_vertexCount; ++i) { var v:b2Vec2 = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i])); graphics.lineTo(v.x * PhysManager.PHYS_SCALE, v.y * PhysManager.PHYS_SCALE); } graphics.lineTo(tV.x * PhysManager.PHYS_SCALE, tV.y * PhysManager.PHYS_SCALE); graphics.endFill(); }; drawBox(); } } //*/ } } import Box2D.Dynamics.*; import Box2D.Dynamics.Contacts.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; import flash.events.Event; import flash.display.*; import flash.text.TextField; import flash.utils.*; //Input import flash.ui.Keyboard; import flash.events.KeyboardEvent; //AA import flash.geom.*; //!物理エンジンのWrapper class PhysManager { //=Phys= // private var m_World:b2World; public var m_World:b2World; //=Param= static public const PHYS_SCALE:Number = 10; static public const GRAVITY:Number = 20.0; //=Singleton= private static var __Instance:PhysManager; public static function get Instance():PhysManager { if(__Instance == null){ __Instance = new PhysManager(); } return __Instance; } //=Init Physics= public function PhysManager(){ {//Common //AABB var worldAABB:b2AABB = new b2AABB(); // worldAABB.minVertex.Set(-100.0, -100.0); // worldAABB.maxVertex.Set(100.0, 100.0); worldAABB.minVertex.Set((Main.RANGE_LX - 10.0)/PHYS_SCALE, (Main.RANGE_UY - 10.0)/PHYS_SCALE); worldAABB.maxVertex.Set((Main.RANGE_RX + 10.0)/PHYS_SCALE, (Main.RANGE_DY + 10.0)/PHYS_SCALE); //Gravity var g:b2Vec2 = new b2Vec2(0.0, GRAVITY); //Sleep var useSleep:Boolean = true; //World m_World = new b2World(worldAABB, g, useSleep); } } //=Static Function= //コリジョンを実際に作成する public static function CreateBody(in_BodyDef:b2BodyDef):b2Body{ return Instance.m_World.CreateBody(in_BodyDef); } //コリジョンを削除する public static function DestroyBody(in_Body:b2Body):void{ Instance.m_World.DestroyBody(in_Body); } //毎フレーム呼んでもらう public static function Update(in_DeltaTime:Number):void{ //物理エンジンをin_DeltaTimeだけ進める Instance.m_World.Step(in_DeltaTime, 10); //ぶつかっているものを見つけて、対応関数を呼ぶ for(var iter:b2Contact = Instance.m_World.GetContactList(); iter != null; iter = iter.GetNext()){ if(iter.GetManifoldCount() == 0){ continue; } var Obj1:IObject = iter.GetShape1().m_body.m_userData as IObject; var Obj2:IObject = iter.GetShape2().m_body.m_userData as IObject; var Nrm:b2Vec2 = iter.GetManifolds()[0].normal; Obj1.OnContact(Obj2, Nrm.x, Nrm.y); Obj2.OnContact(Obj1, -Nrm.x, -Nrm.y); } //Spriteへの位置の反映 for (var bb:b2Body = Instance.m_World.m_bodyList; bb; bb = bb.m_next) { if(bb.m_userData != null){ bb.m_userData.x = bb.m_position.x * PHYS_SCALE; bb.m_userData.y = bb.m_position.y * PHYS_SCALE; bb.m_userData.m_VX = bb.m_linearVelocity.x * PHYS_SCALE; bb.m_userData.m_VY = bb.m_linearVelocity.y * PHYS_SCALE; bb.m_userData.rotation = bb.GetRotation() * 360/(2*3.14); } } } //全てのオブジェクトを消す public static function Reset():void{ Instance.m_World.CleanBodyList(); } } //!オブジェクトの全体的な管理 class ObjectManager { //=Singleton= private static var __Instance:ObjectManager; public static function get Instance():ObjectManager { if(__Instance == null){ __Instance = new ObjectManager(); } return __Instance; } // private var m_HeadObj:IObject; //Objectを登録し、Updateが呼ばれるようにする public static function Register(in_Obj:IObject):void{ if(Instance.m_HeadObj == null){ Instance.m_HeadObj = in_Obj; }else{ Instance.m_HeadObj.m_PrevObj = in_Obj; in_Obj.m_NextObj = Instance.m_HeadObj; Instance.m_HeadObj = in_Obj; } } //上ではこれを呼び、各ObjectのUpdateを呼び出す。ついでに消失管理もここで。 public static function Update(in_DeltaTime:Number):void{ //* //Event var updateEvent:Function = function():void{ for(var iter:IObject = Instance.m_HeadObj; iter != null; iter = iter.m_NextObj){ Instance.m_EventArray.forEach( function(evt:IEvent, index:int, arr:Array):void{ var callback:Function = iter.m_EventFuncMap[evt.m_Type]; if(callback != null){ callback(evt); } } ); } //Reset Instance.m_EventArray = new Array(); }; updateEvent(); //*/ //Update Object for(var iter:IObject = Instance.m_HeadObj; iter != null; iter = iter.m_NextObj){ //共通Update iter.CommonUpdate(in_DeltaTime); //独自Update iter.Update(in_DeltaTime); if(iter.IsKilled()){ if(iter.m_PrevObj == null){ Instance.m_HeadObj = iter.m_NextObj; iter.m_NextObj.m_PrevObj = null; iter.m_NextObj = null; }else{ iter.m_PrevObj.m_NextObj = iter.m_NextObj; iter.m_NextObj.m_PrevObj = iter.m_PrevObj; iter.m_NextObj = null; } iter.Destroy(); } } } //全てのオブジェクトを消す public static function Reset():void{ for(var iter:IObject = Instance.m_HeadObj; iter != null; iter = iter.m_NextObj){ iter.Destroy(); } Instance.m_HeadObj = null; } //=Event= private var m_EventArray:Array = new Array(); public static function PushEvent(in_Event:IEvent):void{ Instance.m_EventArray.push(in_Event); } } //!プレイヤー、敵、弾の共通インタフェース class IObject extends Sprite { //==Object== //=生成されたらマネージャに自分を登録し、Updateが呼ばれるようにしておく public function IObject(){ ObjectManager.Register(this); } //=消える処理まわり private var m_KillFlag:Boolean = false; public function Kill():void{//オブジェクト側は消えたい時にコレを呼ぶ m_KillFlag = true; } public function IsKilled():Boolean{ return m_KillFlag; } //Destroy時に呼び出して欲しい処理はこれをオーバライドする protected function OnDestruct():void{ } public function Destroy():void{//システム側が、消す時にこれを呼ぶ OnDestruct(); if(m_Body != null){ PhysManager.DestroyBody(m_Body); } parent.removeChild(this); } //=消失管理&Updateの連鎖などで使うためのリスト用パラメータ //双方向である必要はない気もする public var m_NextObj:IObject; public var m_PrevObj:IObject; //=ゲーム内に出現する時の処理 public function OnRegist(in_Parent:DisplayObjectContainer):void{ m_Graphic = CreateAASprite(m_AA);//m_AAをもとに、実際に描画するものを作成 if(m_BodyDef != null){ m_BodyDef.userData = this;//m_Graphic;//物理エンジンに合わせて座標などを更新するために登録 //Create m_BodyDef.position.Set(this.x / PhysManager.PHYS_SCALE, this.y / PhysManager.PHYS_SCALE); m_Body = PhysManager.CreateBody(m_BodyDef);//コリジョンの実際の生成 } in_Parent.addChild(this); } //共通の毎回処理する処理 public function CommonUpdate(in_DeltaTime:Number):void{ CheckBaloon(in_DeltaTime); } //=HP= //自分のHP public var m_HP:int = 1; //相手に与えるダメージ public var m_Damage:int = 0; //自分のタイプ public var m_Type:int = 0; //タイプ一覧 public static const TYPE_PLAYER:int = 0x0001; public static const TYPE_PLAYER_BULLET:int = 0x0002; public static const TYPE_ENEMY:int = 0x0004; public static const TYPE_ENEMY_BULLET:int = 0x0008; public static const TYPE_TERRAIN:int = 0x0010; public static const TYPE_TERRAIN_DAMAGE:int = 0x0011; public static const TYPE_PIECE:int = 0x0012; //==Collision== //Collision Category public static const CATEGORY_PLAYER:int = 0x0001; public static const CATEGORY_PLAYER_BULLET:int = 0x0002; public static const CATEGORY_TERRAIN:int = 0x0004; public static const CATEGORY_BLOCK:int = 0x0008; public static const CATEGORY_ENEMY:int = 0x0010; //Collision Param protected var m_PhysParam_Density:Number = 1.0; protected var m_PhysParam_Friction:Number = 0.0; // protected var m_PhysParam_Restitution:Number = 0.0; protected var m_PhysParam_CategoryBits:int = 1;//test protected var m_PhysParam_MaskBits:int = ~0; protected var m_PhysParam_AllowSleep:Boolean = true; protected var m_PhysParam_StartSleep:Boolean = false; //Collision Body protected var m_BodyDef:b2BodyDef; protected var m_Body:b2Body; //Create Collision //円のコリジョンの作成(準備だけで、実際の生成はOnRegistで行う) protected function CreateCollision_Circle(in_R:Number):void{ //Shape var shapeSd:b2CircleDef = new b2CircleDef(); { shapeSd.radius = in_R / PhysManager.PHYS_SCALE; shapeSd.density = m_PhysParam_Density; shapeSd.friction = m_PhysParam_Friction; // shapeSd.restitution = m_PhysParam_Restitution; shapeSd.categoryBits = m_PhysParam_CategoryBits; shapeSd.maskBits = m_PhysParam_MaskBits; } //Body m_BodyDef = new b2BodyDef(); { m_BodyDef.AddShape(shapeSd); m_BodyDef.linearVelocity.Set(m_VX / PhysManager.PHYS_SCALE, m_VY / PhysManager.PHYS_SCALE); if(m_PhysParam_StartSleep){ m_BodyDef.isSleeping = true; }else{ m_BodyDef.allowSleep = m_PhysParam_AllowSleep; } } } //四角のコリジョンの作成(準備だけで、実際の生成はOnRegistで行う) protected function CreateCollision_Box(in_W:Number, in_H:Number):void{ //Shape var shapeSd:b2BoxDef = new b2BoxDef(); { shapeSd.extents.Set(in_W/2 / PhysManager.PHYS_SCALE, in_H/2 / PhysManager.PHYS_SCALE); shapeSd.density = m_PhysParam_Density; shapeSd.friction = m_PhysParam_Friction; // shapeSd.restitution = m_PhysParam_Restitution; shapeSd.categoryBits = m_PhysParam_CategoryBits; shapeSd.maskBits = m_PhysParam_MaskBits; } //Body m_BodyDef = new b2BodyDef(); { m_BodyDef.AddShape(shapeSd); m_BodyDef.linearVelocity.Set(m_VX / PhysManager.PHYS_SCALE, m_VY / PhysManager.PHYS_SCALE); if(m_PhysParam_StartSleep){ m_BodyDef.isSleeping = true; }else{ m_BodyDef.allowSleep = m_PhysParam_AllowSleep; } } } //==Graphic== //Graphic Param protected var m_AA:String = "X";//グロ・ラグドールのためにも、文字列は別で記憶しておく protected var m_Graphic:Sprite;//上のAAをもとに生成されたビットマップ protected var m_GraphicW:Number = -1.0;//とりこむ幅(マイナスなら、テキストの大きさに合わせる) protected var m_GraphicH:Number = -1.0; //Set AA protected function SetAA(in_AA:String):void{ m_AA = in_AA; } //in_Textを(Bitmapによって)表示するSpriteを作成する protected function CreateAASprite(in_Text:String):Sprite{ //まずは文字列を普通に表示するものを作成 var text_field:TextField = new TextField(); { text_field.text = in_Text; text_field.border = false; text_field.x = 0; text_field.y = 0; if(m_GraphicW > 0.0){ text_field.width = m_GraphicW; text_field.height = m_GraphicH; }else{ text_field.width = 999; text_field.height = 999; } } //その内容をビットマップデータとして取り込む var bmp_data : BitmapData; if(m_GraphicW > 0.0){ bmp_data = new BitmapData(m_GraphicW, m_GraphicH, true , 0x00000000); }else{ bmp_data = new BitmapData(text_field.textWidth+5, text_field.textHeight+5, true , 0x00000000); } { var matrix : Matrix = new Matrix(1,0,0,1,0,0); var color : ColorTransform = new ColorTransform(1,1,1,1,0,0,0,0); var rect : Rectangle = new Rectangle(0,0,bmp_data.width,bmp_data.height); bmp_data.draw(text_field, matrix, color, BlendMode.NORMAL, rect, true); } //取り込んだビットマップデータを表示 var bmp_obj:Bitmap = new Bitmap( bmp_data , PixelSnapping.AUTO , true); { if(m_GraphicW > 0.0){ bmp_obj.x = -m_GraphicW*0.5; bmp_obj.y = -m_GraphicH*0.5; }else{ bmp_obj.x = -text_field.textWidth*0.5; bmp_obj.y = -text_field.textHeight*0.5; } } //さらにそれを表示するスプライトを作成して返す var sprite:Sprite = new Sprite(); { addChild(sprite); sprite.addChild(bmp_obj); } return sprite; } // protected function CreateGroRagdoll(in_Text:String):void{//grotesque var i:int; //まず、in_Textの文字を一つ一つTextFieldにする var text_field_array:Array = new Array(); for(i = 0; i < in_Text.length; ++i){ var text_field:TextField = new TextField(); { text_field.text = in_Text.charAt(i); text_field.border = false; text_field.x = 0; text_field.y = 0; } text_field_array.push(text_field); } //TextFieldを元に、グラフィックを計算したり、幅を計算して位置を求めたりする // var bmp_x:Number = this.x + -this.width*0.5; // var bmp_y:Number = this.y + -this.height*0.5; //本体の中心位置に対する破片の相対位置 var offset_x:Number = -this.width*0.5; var offset_y:Number = -this.height*0.5; var max_h:Number = 0.0;//下方向へのオフセットを決定するため、一番大きい高さを毎回記録する for(i = 0; i < text_field_array.length; ++i){ if(text_field_array[i].text == "\r"){//"\n"じゃ識別できなかった //改行なので、次の行のためのリセット。改行は表示しないので、そこら辺の処理は飛ばす // bmp_x = this.x + -this.width*0.5; // bmp_y += max_h; offset_x = -this.width*0.5; offset_y += max_h; max_h = 0.0; continue; } //空白は表示しても仕方がないので生成しない(文字間の幅の計算には必要なので、その処理は行う) var create_bmp_flag:Boolean = true; if(text_field_array[i].text == " "){ create_bmp_flag = false; } if(text_field_array[i].text == " "){ create_bmp_flag = false; } //見た目の大きさ var w:Number = text_field_array[i].textWidth; var h:Number = text_field_array[i].textHeight; //実際の見た目よりコリジョンは小さくする var col_w:Number = 0.5*w; var col_h:Number = 0.5*h; //破片の作成 if(create_bmp_flag){ var piece:CRagdollPiece = new CRagdollPiece(text_field_array[i].text, col_w, col_h); // piece.SetPos(bmp_x+0.5*w, bmp_y+0.5*h); //破片を実際の画像の位置にあわせる piece.SetPos(this.x + offset_x+0.5*w, this.y + offset_y+0.5*h); //破片を以前の速度をベースにしつつ飛び散らせる piece.SetVel(m_VX + 1.0*offset_x, m_VY + 1.0 + 1.0*offset_y); piece.OnRegist(this.parent); //一定確率で、破片を別のものにする if(Math.random() < 0.5){ piece.GrowWorse(); } } //次の破片位置のオフセットのための各種処理 //横方向 // bmp_x += w; offset_x += w; //縦方向 if(max_h < h){ max_h = h; } } } //==Coordinate== //Set Position public function SetPos(in_X:Number, in_Y:Number):void{ this.x = in_X; this.y = in_Y; } public var m_VX:Number = 0.0; public var m_VY:Number = 0.0; //Set Velocity public function SetVel(in_VX:Number, in_VY:Number):void{ m_VX = in_VX; m_VY = in_VY; if(m_BodyDef != null){ m_BodyDef.linearVelocity.Set(in_VX / PhysManager.PHYS_SCALE, in_VY / PhysManager.PHYS_SCALE); } } //==Baloon== public var m_TopBaloon:CBaloon; public function PushBaloonText(in_Text:String):CBaloon{ PushBaloonText_Timer(in_Text, -1.0); return m_TopBaloon; } public function PushBaloonText_Timer(in_Text:String, in_Time:Number):void{ var newBaloon:CBaloon = new CBaloon(in_Text, in_Time); addChild(newBaloon); newBaloon.x = 0.5*this.width; newBaloon.y = -0.5*newBaloon.height; if(m_TopBaloon == null){ m_TopBaloon = newBaloon; }else{ removeChild(m_TopBaloon); newBaloon.m_NextBaloon = m_TopBaloon; m_TopBaloon.m_PrevBaloon = newBaloon; m_TopBaloon = newBaloon; } } private function CheckBaloon(in_DeltaTime:Number):void{ for(var iter:CBaloon = m_TopBaloon; iter != null; iter = iter.m_NextBaloon){ //時間制限式のやつのチェック if(iter.m_RestTime >= 0.0){ iter.m_RestTime -= in_DeltaTime; if(iter.m_RestTime <= 0.0){ iter.m_KillFlag = true; } } //時間や外部のイベントでフラグが立っていたら消す if(iter.m_KillFlag){ //トップを消すなら、次のトップに表示を差し替える if(iter == m_TopBaloon){ m_TopBaloon = iter.m_NextBaloon; removeChild(iter); if(m_TopBaloon != null){ addChild(m_TopBaloon); } }else{ iter.m_PrevBaloon.m_NextBaloon = iter.m_NextBaloon; } if(iter.m_NextBaloon != null){ iter.m_NextBaloon.m_PrevBaloon = iter.m_PrevBaloon; } } } } //=Event= public var m_EventFuncMap:Object = new Object(); //in_EventTypeのイベントが送られてきたら、in_Funcを実行するようにする public function RegisterEventFunc(in_EventType:String, in_Func:Function):void{ m_EventFuncMap[in_EventType] = in_Func; } //他のオブジェクトにメッセージを送る public function DispatchEvent(in_Event:IEvent):void{ ObjectManager.PushEvent(in_Event); } //=virtual function= //毎フレーム呼ばれる public function Update(in_DeltaTime:Number):void{ } //衝突した時に呼ばれる public function OnContact(in_Obj:IObject, in_NrmX:Number, in_NrmY:Number):void{ //in_NrmX > 0.0//自分の右に当たった //in_NrmX < 0.0//自分の右に当たった //in_NrmY > 0.0//自分の下に当たった //in_NrmY < 0.0//自分の上に当たった } } //!ラグドール時の破片 class CRagdollPiece extends IObject { public function CRagdollPiece(in_Text:String, in_W:Number, in_H:Number){ //Collision m_PhysParam_Density = 1.0; m_PhysParam_Friction = 1.0; // m_PhysParam_Restitution = 0.9; m_PhysParam_CategoryBits = CATEGORY_BLOCK; CreateCollision_Box(in_W, in_H); //Graphic SetAA(in_Text); //Type m_Type = TYPE_PIECE; } //破片のグラフィックをさらに細かくなるように変更 public function GrowWorse():void{ //グラフィックを変更 //Default var nextAA:String = "*"; //*からの連鎖 if(m_AA == "*"){ nextAA = ":"; } if(m_AA == ":"){ nextAA = "."; } //独自 if(m_AA == "o"){ nextAA = "A"; } if(m_AA == "^"){ nextAA = ";"; } if(m_AA == "^"){ nextAA = ";"; } //Reset if(m_Graphic){ removeChild(m_Graphic); } //Create m_Graphic = CreateAASprite(nextAA);//m_AAをもとに、実際に描画するものを作成 } //衝突した時に呼ばれる override public function OnContact(in_Obj:IObject, in_NrmX:Number, in_NrmY:Number):void{ if(in_Obj.m_Type == TYPE_PLAYER_BULLET){ GrowWorse(); } } } //!セリフ class CBaloon extends Sprite { public var m_RestTime:Number = -1.0;//マイナスなら、イベントでオフにされるまでずっと表示 public var m_KillFlag:Boolean = false; public var m_NextBaloon:CBaloon = null; public var m_PrevBaloon:CBaloon = null; public function CBaloon(in_Text:String, in_Time:Number){ {//in_Text //まずは文字列を普通に表示するものを作成 var text_field:TextField = new TextField(); { text_field.text = in_Text; text_field.border = false; // text_field.x = 0; // text_field.y = 0; } //その内容をビットマップデータとして取り込む var bmp_data:BitmapData = new BitmapData(text_field.textWidth+5, text_field.textHeight+5, true , 0x00000000); { var matrix : Matrix = new Matrix(1,0,0,1,0,0); var color : ColorTransform = new ColorTransform(1,1,1,1,0,0,0,0); var rect : Rectangle = new Rectangle(0,0,bmp_data.width,bmp_data.height); bmp_data.draw(text_field, matrix, color, BlendMode.NORMAL, rect, true); } //取り込んだビットマップデータを表示 var bmp_obj:Bitmap = new Bitmap( bmp_data , PixelSnapping.AUTO , true); //セリフをセット addChild(bmp_obj); } {//m_RestTime m_RestTime = in_Time; } } } //!プレイヤー class CPlayer extends IObject { //=Param= static public const COLLISION_R:Number = 15.0; static public const PLAYER_VX:Number = 100; static public const PLAYER_VY:Number = 150; static public const BULLET_V:Number = 200; //溜めショットの溜め時間 static public const SUPER_SHOT_TIME:Number = 2.0; //=Input= private var m_InputL:int = 0; private var m_InputR:int = 0; private var m_InputU:int = 0; private var m_InputD:int = 0; private var m_InputSpace:int = 0; static public const KEY_S:int = 83; //== protected var m_Dir:Number = 1.0;//右向きなら1、左向きなら-1 protected var m_GroundFlag:Boolean = false; protected var m_BaseVX:Number = 0.0; protected var m_BaseVY:Number = 0.0; protected var m_ShotTimer:Number = -1.0;//マイナスの時はボタンを押していない protected var m_Baloon_Charge:CBaloon; public function CPlayer(in_Stage:Stage) { //Collision m_PhysParam_Density = 2; m_PhysParam_Friction = 0.0; // m_PhysParam_Restitution = 0.0; m_PhysParam_CategoryBits = CATEGORY_PLAYER; m_PhysParam_MaskBits = CATEGORY_TERRAIN | CATEGORY_BLOCK | CATEGORY_ENEMY; m_PhysParam_AllowSleep = false; CreateCollision_Circle(COLLISION_R); //Graphic SetAA("┏(^o^)┛\n ┛┓"); //Type m_Type = TYPE_PLAYER; {//Init Input in_Stage.addEventListener(KeyboardEvent.KEY_DOWN, OnKeyDown); in_Stage.addEventListener(KeyboardEvent.KEY_UP, OnKeyUp); //プレイヤが消える時に、removeしたい } {//Init EventFunc RegisterEventFunc("EnemyDead", OnEnemyDead); } } override protected function OnDestruct():void{ stage.removeEventListener(KeyboardEvent.KEY_DOWN, OnKeyDown); stage.removeEventListener(KeyboardEvent.KEY_UP, OnKeyUp); } override public function Update(in_DeltaTime:Number):void{ {//player update //入力にしたがって動かす m_Body.m_linearVelocity.x = (m_BaseVX + PLAYER_VX * (m_InputR - m_InputL))/PhysManager.PHYS_SCALE; if(m_GroundFlag && m_InputU > 0){ m_Body.m_linearVelocity.y = (m_BaseVY - PLAYER_VY)/PhysManager.PHYS_SCALE; } m_GroundFlag = false; // m_BaseVX = 0.0; // m_BaseVY = 0.0; } {//Power Shot if(m_ShotTimer >= 0.0){ m_ShotTimer += in_DeltaTime; } if(m_ShotTimer >= SUPER_SHOT_TIME && m_Baloon_Charge == null){ m_Baloon_Charge = PushBaloonText("ウーイ"); } //チャージショットを発射したら、セリフを差し替える if(m_ShotTimer < 0.0 && m_Baloon_Charge != null){ m_Baloon_Charge.m_KillFlag = true; m_Baloon_Charge = null; PushBaloonText_Timer("シネ!", 1.0); } } //死亡判定 var deadFlag:Boolean = false; if(m_HP <= 0){ //ダメージ死 deadFlag = true; } if(this.y > Main.RANGE_DY){ //落下死 deadFlag = true; } if(deadFlag){ //ティウンティウンティウンを表示 for(var i:int = 0; i < 8; ++i){ var v:Number = 50.0; var vx:Number = v * Math.cos(2.0*Math.PI * i/8.0); var vy:Number = v * Math.sin(2.0*Math.PI * i/8.0); var playerDeadEffect:CPlayerDeadEffect = new CPlayerDeadEffect(vx, vy); playerDeadEffect.SetPos(this.x, this.y); // GameObjectManager.Regist(playerDeadEffect); playerDeadEffect.OnRegist(parent); } Kill(); } } public function OnGoal():void{ //ゴール判定に触れたら呼ばれる //昇天グラフィックを表示 var playerOnGoal:CPlayerOnGoal = new CPlayerOnGoal(); playerOnGoal.SetPos(this.x, this.y); // GameObjectManager.Regist(playerOnGoal); playerOnGoal.OnRegist(parent); //こいつ自身は消える Kill(); } protected function OnEnemyDead(in_Event:CEventEnemyDead):void{ PushBaloonText_Timer("ヤッタネ", 1.0); } //=Input= private function OnKeyDown(event:KeyboardEvent):void{ if(event.keyCode == Keyboard.LEFT){ m_InputL = 1; m_Dir = -1.0; m_Graphic.scaleX = -1; } if(event.keyCode == Keyboard.RIGHT){ m_InputR = 1; m_Dir = 1.0; m_Graphic.scaleX = 1; } if(event.keyCode == Keyboard.UP){ m_InputU = 1; } if(event.keyCode == Keyboard.DOWN){ m_InputD = 1; } if(event.keyCode == Keyboard.SPACE){ m_InputSpace = 1; } if(event.keyCode == KEY_S){ //ここで生成してしまう // Shot(); if(m_ShotTimer < 0.0){ //溜めを開始 m_ShotTimer = 0.0; } } } private function OnKeyUp(event:KeyboardEvent):void{ if(event.keyCode == Keyboard.LEFT){ m_InputL = 0; } if(event.keyCode == Keyboard.RIGHT){ m_InputR = 0; } if(event.keyCode == Keyboard.UP){ m_InputU = 0; } if(event.keyCode == Keyboard.DOWN){ m_InputD = 0; } if(event.keyCode == Keyboard.SPACE){ m_InputSpace = 0; } if(event.keyCode == KEY_S){ //キーを放したら発射 if(m_ShotTimer >= 0.0){ Shot(); m_ShotTimer = -1.0; } } } //=Shot= //弾の生成 private function Shot():void{ var x:Number = this.x + m_Dir*160.0/PhysManager.PHYS_SCALE; var y:Number = this.y - (COLLISION_R + 80.0)/PhysManager.PHYS_SCALE; var vx:Number = m_Dir * BULLET_V; var vy:Number = 0.0; var text:String = "o"; var damage:int = 1; if(m_ShotTimer >= SUPER_SHOT_TIME){ text = "◎"; damage = 5; } var bullet:CBullet = new CBullet(text, damage); bullet.SetPos(x, y); bullet.SetVel(vx, vy); bullet.OnRegist(parent); } //衝突した時に呼ばれる override public function OnContact(in_Obj:IObject, in_NrmX:Number, in_NrmY:Number):void{ if(in_NrmY > 0.707){//45度より平坦な坂の上でないとジャンプできない m_GroundFlag = true; m_BaseVX = in_Obj.m_VX; m_BaseVY = in_Obj.m_VY; } } } //!プレイヤー死亡時のエフェクト(ティウンティウンティウン) class CPlayerDeadEffect extends IObject { //=Param= static public const ALPHA_INTERVAL:Number = 0.5; private var m_Timer:Number = 0.0; public function CPlayerDeadEffect(in_VX:Number, in_VY:Number) { m_VX = in_VX; m_VY = in_VY; //Collision //なし //Graphic SetAA("◎"); } override public function Update(in_DeltaTime:Number):void{ this.x += m_VX * in_DeltaTime; this.y += m_VY * in_DeltaTime; m_Timer += in_DeltaTime; this.alpha = 0.6 - 0.4*Math.cos(2.0*Math.PI * m_Timer/ALPHA_INTERVAL); } } //!ゴール時のプレイヤー表示 class CPlayerOnGoal extends IObject { //=Param= static public const HEAVEN_VY:Number = -80.0; static public const HEAVEN_H:Number = -15.0; public function CPlayerOnGoal() { //Collision //なし //Graphic SetAA(" ⊂⊃ \n\(^o^)/"); } override public function Update(in_DeltaTime:Number):void{ if(this.y < HEAVEN_H){ return; } this.y += HEAVEN_VY * in_DeltaTime; if(this.y < HEAVEN_H){ CreateAASprite("\n\n\n\n/\\nオワタ!"); } } } //!プレイヤーの弾 class CBullet extends IObject { //=Param= static public const COLLISION_R:Number = 5.0; public function CBullet(in_Text:String, in_Damage:int) { //Collision m_PhysParam_Density = 10; m_PhysParam_Friction = 0.2; // m_PhysParam_Restitution = 0.9; m_PhysParam_CategoryBits = CATEGORY_PLAYER_BULLET; m_PhysParam_MaskBits = CATEGORY_TERRAIN | CATEGORY_BLOCK | CATEGORY_ENEMY; CreateCollision_Circle(COLLISION_R); //Graphic SetAA(in_Text); //Type m_Type = TYPE_PLAYER_BULLET; //Damage m_Damage = in_Damage; } //何かに当たっても少しの間だけ残って、他の物体を動かすようにする override public function Update(in_DeltaTime:Number):void{ //重力を相殺するような力をかけることで浮きながら移動 AntiGravity(PhysManager.GRAVITY); } //浮きながら移動するために、重力と反対方向の力をかける public function AntiGravity(in_Gravity:Number):void{ m_Body.m_force.Add(b2Math.MulFV(m_Body.GetMass(), new b2Vec2(0.0, -in_Gravity))); } //衝突した時に呼ばれる override public function OnContact(in_Obj:IObject, in_NrmX:Number, in_NrmY:Number):void{ if(in_Obj.m_Type == TYPE_ENEMY){ in_Obj.m_HP -= m_Damage; } if(in_Obj.m_Type == TYPE_PIECE){ //破片には当たっても消えない return; } Kill(); } } //!敵 class CEnemy extends IObject { //=Param= static public const COLLISION_R:Number = 20.0; public function CEnemy() { //Collision m_PhysParam_Density = 20; m_PhysParam_Friction = 1.0; // m_PhysParam_Restitution = 0.9; m_PhysParam_CategoryBits = CATEGORY_ENEMY; m_PhysParam_MaskBits = CATEGORY_TERRAIN | CATEGORY_BLOCK | CATEGORY_PLAYER | CATEGORY_PLAYER_BULLET; m_PhysParam_AllowSleep = false; CreateCollision_Circle(COLLISION_R); //Graphic SetAA(" / ̄\ \n| ^o^ |\n \_/"); //Type m_Type = TYPE_ENEMY; //HP m_HP = 3; //Damage m_Damage = 1; } override public function Update(in_DeltaTime:Number):void{ //死亡判定 if(m_HP <= 0){ Kill(); CreateGroRagdoll(m_AA); DispatchEvent(new CEventEnemyDead()); } } //衝突した時に呼ばれる override public function OnContact(in_Obj:IObject, in_NrmX:Number, in_NrmY:Number):void{ if(in_Obj.m_Type == TYPE_PLAYER){ in_Obj.m_HP -= m_Damage; } } } //!床 class CFloor extends IObject { public function CFloor(in_LX:Number, in_RX:Number, in_UY:Number, in_DY:Number) { var width:Number = in_RX - in_LX; var height:Number = in_DY - in_UY; m_GraphicW = width; // m_GraphicH = height + 25; m_GraphicH = height; //Collision { m_PhysParam_Density = 0.0;//Fix m_PhysParam_Friction = 1.0; // m_PhysParam_Restitution = 0.9; m_PhysParam_CategoryBits = CATEGORY_TERRAIN; CreateCollision_Box(width, height); } //Graphic { var aa:String = ""; //幅に応じて、AA用の文字列を供給する // var floor_top:String = "___"; var floor1:String = "\^o^/"; var floor2:String = "/^o^\"; // var floor_bottom:String = " ̄ ̄ ̄"; for(var w:Number = 40.0; w <= width; w += 60.0){ // floor_top += "_____"; floor1 += "^o^\^o^/"; floor2 += "^o^/^o^\"; // floor_bottom += " ̄ ̄ ̄ ̄ ̄"; } // aa += floor_top; // aa += "\n"; var flag:Boolean = true; // for(var h:Number = 5.0; h <= height; h += 15.0) for(var h:Number = 0.0; h <= height; h += 15.0) { if(flag){ aa += floor1; }else{ aa += floor2; } aa += "\n"; flag = !flag; } // aa += floor_bottom; SetAA(aa); } {//枠 var shape:Shape = new Shape(); addChild(shape); shape.x = -width/2.0; shape.y = -height/2.0; shape.graphics.lineStyle(1, 0x00000000, 1.0); shape.graphics.beginFill(0xFFFFFF, 0.0); shape.graphics.drawRect(0, 0, width, height); } //Type { m_Type = TYPE_TERRAIN; } //Position { SetPos((in_LX+in_RX)/2.0, (in_UY+in_DY)/2.0); } } } //!ダメージ床 class CFloor_Damage extends IObject { public function CFloor_Damage(in_LX:Number, in_RX:Number, in_UY:Number, in_DY:Number) { var width:Number = in_RX - in_LX; var height:Number = in_DY - in_UY; m_GraphicW = width; // m_GraphicH = height + 25; m_GraphicH = height; //Collision { m_PhysParam_Density = 0.0;//Fix m_PhysParam_Friction = 0.0; // m_PhysParam_Restitution = 0.9; m_PhysParam_CategoryBits = CATEGORY_TERRAIN; CreateCollision_Box(width, height); } //Graphic { var aa:String = ""; //幅に応じて、AA用の文字列を供給する var floor_top:String = "△△△△△"; var floor1:String = "\^o^/"; var floor2:String = "/^o^\"; // var floor_bottom:String = " ̄ ̄ ̄"; for(var w:Number = 40.0; w <= width; w += 60.0){ floor_top += "△△△△△△"; floor1 += "^o^\^o^/"; floor2 += "^o^/^o^\"; // floor_bottom += " ̄ ̄ ̄ ̄ ̄"; } aa += floor_top; aa += "\n"; var flag:Boolean = true; // for(var h:Number = 5.0; h <= height; h += 15.0) for(var h:Number = 0.0; h <= height; h += 15.0) { if(flag){ aa += floor1; }else{ aa += floor2; } aa += "\n"; flag = !flag; } // aa += floor_bottom; SetAA(aa); } {//枠 var shape:Shape = new Shape(); addChild(shape); shape.x = -width/2.0; shape.y = -height/2.0; /* shape.graphics.lineStyle(1, 0x00000000, 1.0); shape.graphics.beginFill(0xFFFFFF, 0.0); shape.graphics.drawRect(0, 0, width, height); /*/ shape.graphics.lineStyle(1, 0x00000000, 1.0); shape.graphics.moveTo( 0, 0); shape.graphics.lineTo( 0, height); shape.graphics.lineTo(width, height); shape.graphics.lineTo(width, 0); //*/ } //Type { m_Type = TYPE_TERRAIN_DAMAGE; } //Position { SetPos((in_LX+in_RX)/2.0, (in_UY+in_DY)/2.0); } } //衝突した時に呼ばれる override public function OnContact(in_Obj:IObject, in_NrmX:Number, in_NrmY:Number):void{ if(in_Obj.m_Type == TYPE_PLAYER){ in_Obj.m_HP = 0; } } } //!ブロック class CBlock extends IObject { //=Param= static public const COLLISION_W:Number = 40.0; static public const COLLISION_H:Number = 20.0; public function CBlock(in_StartSleep:Boolean = false) { //Collision m_PhysParam_Density = 10.0;//1.0; m_PhysParam_Friction = 0.5; // m_PhysParam_Restitution = 0.9; m_PhysParam_CategoryBits = CATEGORY_BLOCK; m_PhysParam_StartSleep = in_StartSleep; CreateCollision_Box(COLLISION_W, COLLISION_H); //Graphic SetAA("___\n| ^o^ |\n ̄ ̄ ̄"); //Type m_Type = TYPE_TERRAIN; } } //!ゴール class CGoal extends IObject { // private var m_Player:CPlayer; //=Param= static public const COLLISION_R:Number = 20.0; public function CGoal(in_Player:CPlayer) { m_Player = in_Player; //Collision //なし //Graphic SetAA("____\n| GOAL |\n ̄ ̄ ̄ ̄"); } override public function Update(in_DeltaTime:Number):void{ if(m_Player == null){ //すでにゴールした return; } var gapX:Number = m_Player.x - this.x; var gapY:Number = m_Player.y - this.y; if(Math.sqrt(gapX*gapX + gapY*gapY) < COLLISION_R){ m_Player.OnGoal(); m_Player = null; } } } //! class IEvent { public var m_Type:String = ""; } //! class CEventEnemyDead extends IEvent { static public const EVENT_TYPE:String = "EnemyDead"; public function CEventEnemyDead(){ m_Type = EVENT_TYPE; } }