状況
だんだんと仕事が忙しくなってきて、休日も趣味のコーディングがあまりできなさそう。最悪、夏の終わりまでこんな状況が続く。
とりあえず、他のコリジョンに触れたらOnContactを呼ぶ、という処理を追加した。
これで弾を消せるようになったが、すぐに消してしまうと他のコリジョンを押したりしないようなので、当たったら微小時間後に消えるようにした。
ある程度コードも整ってきたので、あとはだいたい以下のものを組み込んだら土台作りは終了ということで。
- ダメージ判定
- プレイヤーとエネミー、プレイヤーの弾とエネミーなどのダメージのやり取り
- プレイヤーの浮遊処理をジャンプにする
- 足元にコリジョンがある場合のみジャンプ
思い出したらなあとで追加するかも。
弾が消える処理を入れただけなので、またコードと一緒に「続きを読む」で折りたたんでおく(主に、一覧表示でのロードの遅さの緩和のため)。
swf
コード
//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; //Input import flash.ui.Keyboard; import flash.events.KeyboardEvent; //AA import flash.geom.*; [SWF(backgroundColor="#ffffff", width="350", height="200")] public class Main extends Sprite { //Player private var m_Player:CPlayer; //Param public static const FLOOR_LX:Number = 0; public static const FLOOR_RX:Number = 350; public static const FLOOR_UY:Number = 250; public static const FLOOR_DY:Number = 370; public static const BLOCK_X:Number = 300; public static const PLAYER_X:Number = 50; public static const PLAYER_Y:Number = 220; public static const ENEMY_X:Number = 100; public static const ENEMY_Y:Number = 220; 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 { {//Floor var floor:CFloor = new CFloor(FLOOR_LX, FLOOR_RX, FLOOR_UY, FLOOR_DY); // GameObjectManager.Regist(floor); floor.OnRegist(this); } {//Player var player:CPlayer = new CPlayer(stage); player.SetPos(PLAYER_X, PLAYER_Y); // GameObjectManager.Regist(m_Player); player.OnRegist(this); } {//Blocks for (var i:int = 0; i < 10; i++) { var block:CBlock = new CBlock(); block.SetPos(BLOCK_X, FLOOR_UY - CBlock.COLLISION_H/2 - i*CBlock.COLLISION_H); // GameObjectManager.Regist(floor); block.OnRegist(this); } } {//Enemy var enemy:CEnemy = new CEnemy(); enemy.SetPos(ENEMY_X, ENEMY_Y); // GameObjectManager.Regist(enemy); enemy.OnRegist(this); } //毎フレームUpdateを呼ぶ addEventListener("enterFrame", function(event:Event):void { Update(); }); } //=Update= public function Update():void { var deltaTime:Number = 1.0 / 30.0; //=Object //プレイヤや弾などのUpdateを呼ぶ ObjectManager.Update(deltaTime); //=Physics //物理エンジンを進める PhysManager.Update(deltaTime); /* //=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); } } //*/ } //=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; //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 = 10.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); //Gravity var g:b2Vec2 = new b2Vec2(0.0, GRAVITY); //World m_World = new b2World(worldAABB, g, false); } } //=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()){ var Obj1:IObject = iter.GetShape1().m_body.m_userData as IObject; var Obj2:IObject = iter.GetShape2().m_body.m_userData as IObject; Obj1.OnContact(Obj2); Obj2.OnContact(Obj1); } //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.rotation = bb.GetRotation() * 360/(2*3.14); } } } } //!オブジェクトの全体的な管理 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{ for(var iter:IObject = Instance.m_HeadObj; iter != null; iter = iter.m_NextObj){ 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(); } } } } //!プレイヤー、敵、弾の共通インタフェース 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; } public function Destroy():void{//システム側が、消す時にこれを呼ぶ 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をもとに、実際に描画するものを作成 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); } //==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; //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); } } //四角のコリジョンの作成(準備だけで、実際の生成は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); } } //==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; } //==Coordinate== //Set Position public function SetPos(in_X:Number, in_Y:Number):void{ this.x = in_X; this.y = in_Y; } protected var m_VX:Number = 0.0; protected 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); } } //=virtual function= //毎フレーム呼ばれる public function Update(in_DeltaTime:Number):void{ } //衝突した時に呼ばれる public function OnContact(in_Obj:IObject):void{ } } //!プレイヤー class CPlayer extends IObject { //=Param= static public const COLLISION_R:Number = 15.0; static public const PLAYER_V:Number = 10; public static const BULLET_V:Number = 200; //=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 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; CreateCollision_Circle(COLLISION_R); //Graphic SetAA("┏(^o^)┛\n ┛┓"); {//Init Input in_Stage.addEventListener(KeyboardEvent.KEY_DOWN, OnKeyDown); in_Stage.addEventListener(KeyboardEvent.KEY_UP, OnKeyUp); } } override public function Update(in_DeltaTime:Number):void{ {//player update //入力にしたがって動かす m_Body.m_linearVelocity.x = PLAYER_V * (m_InputR - m_InputL); if(m_InputU > 0){ m_Body.m_linearVelocity.y = -PLAYER_V; } } } //=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(); } } 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; } } //=Shot= //弾の生成 private function Shot():void{ var x:Number = this.x + m_Dir*20.0/PhysManager.PHYS_SCALE; var y:Number = this.y - COLLISION_R/PhysManager.PHYS_SCALE; var vx:Number = m_Dir * BULLET_V; var vy:Number = 0.0; var bullet:CBullet = new CBullet(); bullet.SetPos(x, y); bullet.SetVel(vx, vy); // GameObjectManager.Regist(m_Player); bullet.OnRegist(stage); } } //!プレイヤーの弾 class CBullet extends IObject { //=Param= static public const COLLISION_R:Number = 5.0; public function CBullet() { //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("o"); } //何かに当たっても少しの間だけ残って、他の物体を動かすようにする private var m_KillTimer_Bullet:Number = -1.0;//マイナス:オフ、0:タイマのスタート、プラス:タイマ進行中 override public function Update(in_DeltaTime:Number):void{ //重力を相殺するような力をかけることで浮きながら移動 AntiGravity(PhysManager.GRAVITY); //消失すべきなら消失する if(m_KillTimer_Bullet >= 0.0){ m_KillTimer_Bullet += in_DeltaTime; if(m_KillTimer_Bullet >= 0.05){ Kill(); } } } 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):void{ if(m_KillTimer_Bullet < 0.0){ m_KillTimer_Bullet = 0.0; } // Kill(); } } //!敵 class CEnemy extends IObject { //=Param= static public const COLLISION_R:Number = 20.0; public function CEnemy() { //Collision m_PhysParam_Density = 2; m_PhysParam_Friction = 0.0; // m_PhysParam_Restitution = 0.9; m_PhysParam_CategoryBits = CATEGORY_ENEMY; m_PhysParam_MaskBits = CATEGORY_TERRAIN | CATEGORY_BLOCK | CATEGORY_PLAYER_BULLET; CreateCollision_Circle(COLLISION_R); //Graphic SetAA(" / ̄\ \n| ^o^ |\n \_/"); } override public function Update(in_DeltaTime:Number):void{ } } //!床 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; //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^\"; for(var w:Number = 40.0; w <= width; w += 60.0){ floor_top += "_____"; floor1 += "^o^\^o^/"; floor2 += "^o^/^o^\"; } aa += floor_top; aa += "\n"; var flag:Boolean = true; for(var h:Number = 5.0; h <= height; h += 15.0){ if(flag){ aa += floor1; }else{ aa += floor2; } aa += "\n"; flag = !flag; } SetAA(aa); } //Position { SetPos((in_LX+in_RX)/2.0, (in_UY+in_DY)/2.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() { //Collision m_PhysParam_Density = 1.0; m_PhysParam_Friction = 0.5; // m_PhysParam_Restitution = 0.9; m_PhysParam_CategoryBits = CATEGORY_BLOCK; CreateCollision_Box(COLLISION_W, COLLISION_H); //Graphic SetAA("___\n| ^o^ |\n "); } }