セーブ&ロードに対応中

会社のPCの乗り換え作業のため、今日はあまり時間が取れない。コードの整理をかねつつ、セーブ&ロードに対応するのみ。


一応、連想配列からステージをロードする部分が半分くらい完了。ステージの構造は前回と同じなので、今回はコードのみ上げておく(下に折りたたんでおく)。


ロードの構造は、「連想配列のリスト」をひとつのステージとみなし、各「連想配列」がひとつのオブジェクトに相当。「連想配列」は共通で「name」を持ち、これをもとに何を生成するかを決定。残りの要素をパラメータとして扱う。
こんな感じの構造にしたので、「valueの型が一定でない連想配列って何に使うんだろう」という以前からの疑問が解決した感じ。「型が一定じゃない配列」も、もしかしたら似たような感じで、ある種の必然性があるのかもしれない。


明日中に、今やってるところを終えて、新しいオブジェクトを一個作って、またサンプルステージを作りたい。その後は、ステージエディット用のGUIまわりかな。



コード

//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 STAGE_PARAM:Array = [
			{
				name:"Floor",
				lx:RANGE_LX + (RANGE_RX-RANGE_LX)*0/7,
				rx:RANGE_LX + (RANGE_RX-RANGE_LX)*1/7,
				uy:FLOOR_UY,
				dy:FLOOR_DY
			},
			{
				name:"Floor",
				lx:RANGE_LX + (RANGE_RX-RANGE_LX)*2/7,
				rx:RANGE_LX + (RANGE_RX-RANGE_LX)*3/7,
				uy:FLOOR_UY-20,
				dy:FLOOR_DY
			},
			{
				name:"Floor",
				lx:RANGE_LX + (RANGE_RX-RANGE_LX)*4/7,
				rx:RANGE_LX + (RANGE_RX-RANGE_LX)*5/7,
				uy:FLOOR_UY-40,
				dy:FLOOR_DY
			},
			{
				name:"Floor",
				lx:RANGE_LX + (RANGE_RX-RANGE_LX)*6/7,
				rx:RANGE_LX + (RANGE_RX-RANGE_LX)*7/7,
				uy:FLOOR_UY-60,
				dy:FLOOR_DY
			},
//*
			{
				name:"Block",
				lx:RANGE_LX + (RANGE_RX-RANGE_LX)*4/7,
				rx:RANGE_LX + (RANGE_RX-RANGE_LX)*5/7,
				uy:FLOOR_UY-180,
				dy:FLOOR_UY-120,
				density:20.0,
//				category_bits:IObject.CATEGORY_TERRAIN,
				category_bits:0x0004,
				start_sleep:true
			},
//*/
		];

		public static const FLOOR_UY:Number = 200;
		public static const FLOOR_DY:Number = 370;
		public static const FLOOR_L_RX:Number = 300;
		public static const FLOOR_R_LX:Number = 700;

		public static const BLOCK_W:Number = 50;

		public static const PLAYER_X:Number = 50;
		public static const PLAYER_Y:Number = 180;

		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;
			
			STAGE_PARAM.forEach(
				function(param:Object, index:int, arr:Array):void{
					var obj:IObject;
					if(param.name == "Floor"){
						obj = new CFloor(param.lx, param.rx, param.uy, param.dy);
					}
					if(param.name == "Block"){
						var blockParam:ObjectParam = new ObjectParam();
						{
							blockParam.m_LX = param.lx;
							blockParam.m_RX = param.rx;
							blockParam.m_UY = param.uy;
							blockParam.m_DY = param.dy;
							ObjectParam.PARAM_NAME.forEach(
								function(block_param_name:String, block_index:int, block_arr:Array):void{
									if(param.hasOwnProperty(block_param_name)){
										blockParam[block_param_name] = param[block_param_name];
									}
								}
							);
						}
						obj = new CBlock(blockParam);
					}
					if(obj != null){
						obj.OnRegist(m_SpriteGame);
					}
				}
			);

			{//Floor Upper
				var floorU:CFloor = new CFloor(RANGE_LX + (RANGE_RX-RANGE_LX)*0/7, RANGE_LX + (RANGE_RX-RANGE_LX)*4/7, FLOOR_UY-140, FLOOR_UY-120);
				floorU.OnRegist(m_SpriteGame);
			}
/*
			{//Block Sleeping
				var blockParam4:ObjectParam = new ObjectParam();
				{
					blockParam4.m_LX = RANGE_LX + (RANGE_RX-RANGE_LX)*4/7;
					blockParam4.m_RX = RANGE_LX + (RANGE_RX-RANGE_LX)*5/7;
					blockParam4.m_UY = FLOOR_UY-180;
					blockParam4.m_DY = FLOOR_UY-120;
					blockParam4.m_PhysParam_Density = 20.0;//Movable
					blockParam4.m_PhysParam_CategoryBits = IObject.CATEGORY_TERRAIN;
					blockParam4.m_PhysParam_StartSleep = true;
				}
				var block4:CBlock = new CBlock(blockParam4);
				block4.OnRegist(m_SpriteGame);
			}
//*/
/*
			{//Block Fixed
				var blockParam1:ObjectParam = new ObjectParam();
				{
					blockParam1.m_LX = FLOOR_L_RX - BLOCK_W*3;
					blockParam1.m_RX = FLOOR_L_RX - BLOCK_W*1;
					blockParam1.m_UY = FLOOR_UY - BLOCK_W*1;
					blockParam1.m_DY = FLOOR_UY - BLOCK_W*0;
					blockParam1.m_PhysParam_Density = 0.0;//Fixed
					blockParam1.m_PhysParam_CategoryBits = IObject.CATEGORY_TERRAIN;
				}
				var block1:CBlock = new CBlock(blockParam1);
				block1.OnRegist(m_SpriteGame);
			}
			{//Block Breakable
				var blockParam2:ObjectParam = new ObjectParam();
				{
					blockParam2.m_LX = FLOOR_L_RX - BLOCK_W*1;
					blockParam2.m_RX = FLOOR_L_RX - BLOCK_W*0;
					blockParam2.m_UY = FLOOR_UY - BLOCK_W*3;
					blockParam2.m_DY = FLOOR_UY - BLOCK_W*0;
					blockParam2.m_PhysParam_Density = 0.0;//Fixed
					blockParam2.m_PhysParam_CategoryBits = IObject.CATEGORY_TERRAIN;
					blockParam2.m_BecomeGro = true;
				}
				var block2:CBlock = new CBlock(blockParam2);
				block2.OnRegist(m_SpriteGame);
			}
			{//Block AntiGravity
				var blockParam3:ObjectParam = new ObjectParam();
				{
					blockParam3.m_LX = FLOOR_L_RX - BLOCK_W*2;
					blockParam3.m_RX = FLOOR_L_RX - BLOCK_W*1;
					blockParam3.m_UY = FLOOR_UY - BLOCK_W*2;
					blockParam3.m_DY = FLOOR_UY - BLOCK_W*1;
					blockParam3.m_PhysParam_Density = 20.0;//Movable
					blockParam3.m_PhysParam_CategoryBits = IObject.CATEGORY_TERRAIN;
					blockParam3.m_AntiGravity = true;
				}
				var block3:CBlock = new CBlock(blockParam3);
				block3.OnRegist(m_SpriteGame);
			}
//*/
/*
			{//Block
				var blockParam:ObjectParam = new ObjectParam();
				{
					blockParam.m_LX = RANGE_LX + (RANGE_RX-RANGE_LX)*4/7;
					blockParam.m_RX = RANGE_LX + (RANGE_RX-RANGE_LX)*5/7;
					blockParam.m_UY = FLOOR_UY;
					blockParam.m_DY = FLOOR_UY+20;
					blockParam.m_PhysParam_Density = 5.0;
					blockParam.m_PhysParam_CategoryBits = IObject.CATEGORY_TERRAIN;
					blockParam.m_PhysParam_StartSleep = true;
				}
				var block:CBlock = new CBlock(blockParam);
//				block.SetPos(RANGE_LX + (RANGE_RX-RANGE_LX)*4.5/7, FLOOR_UY + CBlock.COLLISION_H/2);
				block.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.2/7, FLOOR_UY-40);
				enemy1.OnRegist(m_SpriteGame);
			}
			{//Enemy2
				var enemy2:CEnemy = new CEnemy();
				enemy2.SetPos(RANGE_LX + (RANGE_RX-RANGE_LX)*2.8/7, FLOOR_UY-40);
				enemy2.OnRegist(m_SpriteGame);
			}
			{//Enemy3
				var enemy3:CEnemy = new CEnemy();
				enemy3.SetPos(RANGE_LX + (RANGE_RX-RANGE_LX)*4.2/7, FLOOR_UY-60);
				enemy3.OnRegist(m_SpriteGame);
			}
			{//Enemy4
				var enemy4:CEnemy = new CEnemy();
				enemy4.SetPos(RANGE_LX + (RANGE_RX-RANGE_LX)*4.8/7, FLOOR_UY-60);
				enemy4.OnRegist(m_SpriteGame);
			}
			{//Enemy9
				var enemy9:CEnemy = new CEnemy();
				enemy9.SetPos(RANGE_LX + (RANGE_RX-RANGE_LX)*6.2/7, FLOOR_UY-80);
				enemy9.OnRegist(m_SpriteGame);
			}
			{//EnemyA
				var enemyA:CEnemy = new CEnemy();
				enemyA.SetPos(RANGE_LX + (RANGE_RX-RANGE_LX)*6.8/7, FLOOR_UY-80);
				enemyA.OnRegist(m_SpriteGame);
			}
			{//Enemy5
				var enemy5:CEnemy = new CEnemy();
				enemy5.SetPos(RANGE_LX + (RANGE_RX-RANGE_LX)*0.5/7, FLOOR_UY-160);
				enemy5.OnRegist(m_SpriteGame);
			}
			{//Enemy6
				var enemy6:CEnemy = new CEnemy();
				enemy6.SetPos(RANGE_LX + (RANGE_RX-RANGE_LX)*1.5/7, FLOOR_UY-160);
				enemy6.OnRegist(m_SpriteGame);
			}
			{//Enemy7
				var enemy7:CEnemy = new CEnemy();
				enemy7.SetPos(RANGE_LX + (RANGE_RX-RANGE_LX)*2.5/7, FLOOR_UY-160);
				enemy7.OnRegist(m_SpriteGame);
			}
			{//Enemy8
				var enemy8:CEnemy = new CEnemy();
				enemy8.SetPos(RANGE_LX + (RANGE_RX-RANGE_LX)*3.5/7, FLOOR_UY-160);
				enemy8.OnRegist(m_SpriteGame);
			}

			{//Goal
				var goal:CGoal = new CGoal(m_Player);
				goal.SetPos(RANGE_LX+50, FLOOR_UY-180);
				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;
		static public const MAX_DELTA_TIME:Number = 1.0/15.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;
			if(deltaTime > MAX_DELTA_TIME){
				deltaTime = MAX_DELTA_TIME;//一応制限してみる(コリジョンのすりぬけ防止)
			}
			

			//=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;

	static public const RANGE_LX:Number = Main.RANGE_LX - 30.0;
	static public const RANGE_RX:Number = Main.RANGE_RX + 30.0;
	static public const RANGE_UY:Number = Main.RANGE_UY - 30.0;
	static public const RANGE_DY:Number = Main.RANGE_DY + 30.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(RANGE_LX/PHYS_SCALE, RANGE_UY/PHYS_SCALE);
			worldAABB.maxVertex.Set(RANGE_RX/PHYS_SCALE, RANGE_DY/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{
		//毎フレーム、関数オブジェクトを生成してるはずなのでいけてない(staticで持っておきたい)

		//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();

		//Chack Range
		var checkRange:Function = function():void{
			for(var iter:IObject = Instance.m_HeadObj; iter != null; iter = iter.m_NextObj){
				if(iter.x <= PhysManager.RANGE_LX || iter.x >= PhysManager.RANGE_RX || iter.y <= PhysManager.RANGE_UY || iter.y >= PhysManager.RANGE_DY){
					//レンジ外のものは削除
					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();
				}
			}
		};
		checkRange();

		//Update Object
		var updateObject:Function = function():void{
			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();
				}
			}
		};
		updateObject();
	}

	//全てのオブジェクトを消す
	public static function Reset():void{
		for(var iter:IObject = Instance.m_HeadObj; iter != null; iter = iter.m_NextObj){
			iter.Destroy_Reset();
		}

		Instance.m_HeadObj = null;
		Instance.m_EventArray = new Array();
	}


	//=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;
	private var m_ResetDestroyFlag:Boolean = false;

	public function Kill():void{//オブジェクト側は消えたい時にコレを呼ぶ
		m_KillFlag = true;
	}

	public function IsKilled():Boolean{
		return m_KillFlag;
	}
	public function IsReset():Boolean{
		return m_ResetDestroyFlag;
	}

	//Destroy時に呼び出して欲しい処理はこれをオーバライドする
	protected function OnDestruct():void{
	}

	public function Destroy():void{//システム側が、消す時にこれを呼ぶ
		OnDestruct();
		if(m_Body != null){
			PhysManager.DestroyBody(m_Body);
		}
		parent.removeChild(this);
	}

	public function Destroy_Reset():void{//リセット用のDestroy
		m_ResetDestroyFlag = true;
		Destroy();
	}

	//=消失管理&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;
		}
	}

	//本体が死んでも残り、画面内に表示されようとするセリフオブジェクトを生成する
	public function CreateBaloonObj(in_Text:String, in_Time:Number):void{
		if(IsReset()){
			return;
		}

		var trgX:Number = this.x;
		var trgY:Number = this.y;
		if(trgX < Main.RANGE_LX){trgX = Main.RANGE_LX;}
		if(trgX > Main.RANGE_RX){trgX = Main.RANGE_RX;}
		if(trgY < Main.RANGE_UY){trgY = Main.RANGE_UY;}
		if(trgY > Main.RANGE_DY){trgY = Main.RANGE_DY;}
		var baloon:CBaloonObj = new CBaloonObj(trgX, trgY, in_Text, in_Time);
		baloon.OnRegist(this.parent);
	}

	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 CBaloonObj extends IObject
{
	//本来出現させるべき位置
	protected var m_OriX:Number;
	protected var m_OriY:Number;

	//表示の残り時間
	protected var m_RestTime:Number;

	public function CBaloonObj(in_X:Number, in_Y:Number, in_Text:String, in_Time:Number){
		m_OriX = in_X;
		m_OriY = in_Y;
		SetPos(in_X, in_Y);

		//Graphic
		SetAA(in_Text);

		m_RestTime = in_Time;
	}

	override public function Update(in_DeltaTime:Number):void{
		{//残り時間のチェック
			m_RestTime -= in_DeltaTime;

			if(m_RestTime <= 0.0){
				Kill();
			}
		}

		{//画面内に居ようとする処理
/*
		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;
		}
//*/
			var absX:Number = this.parent.x + m_OriX;
			var absY:Number = this.parent.y + m_OriY;

			var trgX:Number = this.x;
			var trgY:Number = this.y;
			if(absX - 0.5*this.width < 0){
				trgX = -this.parent.x + 0.5*this.width;
			}
			if(absX + 0.5*this.width > Main.CAMERA_W){
				trgX = -this.parent.x - 0.5*this.width + Main.CAMERA_W;
			}
			if(absY - 0.5*this.height < 0){
				trgY = -this.parent.y + 0.5*this.height;
			}
			if(absY + 0.5*this.height > Main.CAMERA_H){
				trgY = -this.parent.y - 0.5*this.height + Main.CAMERA_H;
			}

			this.x = trgX;
			this.y = trgY;
		}
	}
}


//!プレイヤー
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;

			if(in_Obj.m_Type != TYPE_PIECE){
				//破片の慣性は受けないことにする
				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 = 5;
		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 | CATEGORY_ENEMY;
		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();
		}
	}

	//衝突した時に呼ばれる
	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;
		}
	}

	override protected function OnDestruct():void{
		if(m_HP <= 0){
			CreateGroRagdoll(m_AA);
		}

		CreateBaloonObj("ウワー!", 1.0);

		DispatchEvent(new CEventEnemyDead());
	}	
}


//!床
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 ObjectParam extends Object
{//ブロックを生成する際に渡すパラメータ
	//ブロックの範囲(絶対座標で端を設定)
	public var m_LX:Number = 0;
	public var m_RX:Number = 0;
	public var m_UY:Number = 0;
	public var m_DY:Number = 0;
/*
	//コリジョンまわりのパラメータ
	public var m_PhysParam_Density:Number = 1.0;//密度
	public var m_PhysParam_Friction:Number = 0.0;//摩擦
//	public var m_PhysParam_Restitution:Number = 0.0;//
	public var m_PhysParam_CategoryBits:int = 1;//自分のカテゴリー
	public var m_PhysParam_MaskBits:int = ~0;//どのカテゴリーにぶつかるか
	public var m_PhysParam_AllowSleep:Boolean = true;//スリープ状態になるか否か
	public var m_PhysParam_StartSleep:Boolean = false;//スリープ状態で始まるか否か

	//破壊されてグロ・ラグドールになるか
	public var m_BecomeGro:Boolean = false;
	//重力を無視するか
	public var m_AntiGravity:Boolean = false;
/*/
	static public const PARAM_NAME:Array = [
		"density",
		"friction",
		"restitution",
		"category_bits",
		"mask_bits",
		"allow_sleep",
		"start_sleep",

		"become_gro",
		"anti_gravity",
	];

//	static public const DEFAULT_PARAM:Object = {
//		density:1.0,
//		friction:0.0,
//		restitution:0.0,
//		category_bits:1,
//		mask_bits:~0,
//		allow_sleep:true,
//		start_sleep:false,
//
//		become_gro:false,
//		anti_gravity:false
//	};
	public var density:Number = 1.0;
	public var friction:Number = 0.0;
	public var restitution:Number = 0.0;
	public var category_bits:int = 1;
	public var mask_bits:int = ~0;
	public var allow_sleep:Boolean = true;
	public var start_sleep:Boolean = false;

	public var become_gro:Boolean = false;
	public var anti_gravity:Boolean = false;
//*/
};

//!ブロック
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;

	}
/*/
	//
	protected var m_BecomeGro:Boolean = false;
	//
	protected var m_AntiGravity:Boolean = false;

//	public function CBlock(in_LX:Number, in_RX:Number, in_UY:Number, in_DY:Number, in_StartSleep:Boolean = false)
	public function CBlock(in_Param:ObjectParam)
	{
		//Common
		var width:Number = in_Param.m_RX - in_Param.m_LX;
		var height:Number = in_Param.m_DY - in_Param.m_UY;

		//Param
		m_GraphicW = width;
//		m_GraphicH = height + 25;
		m_GraphicH = height;

/*
		m_BecomeGro = in_Param.m_BecomeGro;
		m_AntiGravity = in_Param.m_AntiGravity;
/*/
		m_BecomeGro = in_Param["become_gro"];
		m_AntiGravity = in_Param["anti_gravity"];
//*/

		//Collision
		{
/*
			m_PhysParam_Density = in_Param.m_PhysParam_Density;//5.0;
			m_PhysParam_Friction = in_Param.m_PhysParam_Friction;//1.0;
	//		m_PhysParam_Restitution = 0.9;
			m_PhysParam_CategoryBits = in_Param.m_PhysParam_CategoryBits;//CATEGORY_TERRAIN;
			m_PhysParam_MaskBits = in_Param.m_PhysParam_MaskBits;//in_StartSleep;
			m_PhysParam_AllowSleep = in_Param.m_PhysParam_AllowSleep;//in_StartSleep;
			m_PhysParam_StartSleep = in_Param.m_PhysParam_StartSleep;//in_StartSleep;

			if(m_AntiGravity){
				m_PhysParam_AllowSleep = false;
			}
/*/
			m_PhysParam_Density = in_Param["density"];//5.0;
			m_PhysParam_Friction = in_Param["friction"];//1.0;
	//		m_PhysParam_Restitution = in_Param["restitution"];
			m_PhysParam_CategoryBits = in_Param["category_bits"];//CATEGORY_TERRAIN;
			m_PhysParam_MaskBits = in_Param["mask_bits"];//in_StartSleep;
			m_PhysParam_AllowSleep = in_Param["allow_sleep"];//in_StartSleep;
			m_PhysParam_StartSleep = in_Param["start_sleep"];//in_StartSleep;

			if(m_AntiGravity){
				m_PhysParam_AllowSleep = false;
			}
//*/

			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_Param.m_LX+in_Param.m_RX)/2.0, (in_Param.m_UY+in_Param.m_DY)/2.0);
		}
	}
//*/

	//毎フレーム呼ばれる処理
	override public function Update(in_DeltaTime:Number):void{
		if(m_AntiGravity){
			//重力を相殺するような力をかけることで浮きながら移動
			AntiGravity(PhysManager.GRAVITY);
		}

		//死亡判定
		if(m_HP <= 0){
			Kill();

			CreateGroRagdoll(m_AA);

//			DispatchEvent(new CEventEnemyDead());
		}
	}

	//浮きながら移動するために、重力と反対方向の力をかける
	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(m_BecomeGro){
			if(in_Obj.m_Type == TYPE_PLAYER_BULLET){
				m_HP = 0;
			}
		}
	}
}


//!ゴール
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;
	}
}