セリフ・システムの作成ログ
更新ログ
開始時にプレイヤーがすぐに死ぬバグの修正
設計
さっそく単純なものを作ろうとしたところで、「死亡時のセリフは、すでに本体がないのでどうするか問題」につきあたる。妥当なところでは「時限式のオブジェクトとしてセリフを作成する」あたりだが、セリフの差し替えはどうしよう。保持しといて、必要なときにKillしたりするだけで良いのだろうか。良くないな。保持してるときに時限式でセリフがKillされる可能性がある。
本体にくっついて移動すべき「セリフ」がある以上、本体が保持する(本体の子供にする)必要はあるだろう。ならば、「死亡時のセリフ」は別物として扱うべき、という結論に至った。
で、また考えていくと、今度はセリフの優先順位の問題が出てきた。溜めショットで「ウーイ」と言ってる間に、前のショットで敵が倒れて「ヤッタネ」というセリフを吐かせる場合など。セリフ用の状態遷移機械を作るか、と考えたがいちいち設定するのは面倒だ。スタックにセリフを積んでトップのセリフを採用する、というのをメインにして考えた方が良さそう。思ってたより面倒なことになってきた。
とりあえず、途中経過を下に載せておく。
swf
- 左右キー
- 移動
- 上キー
- ジャンプ
- Sキー
- ショット(押しっぱなしで離すと溜めショット)
- Rキー
- リセット
主な変更点は以下の通り。
- 動く足場に乗ってジャンプしたら、その時の速度を維持しながらジャンプするようにした
- 敵の破片に乗ってもそうなるので、そっちの方は抑制したい。
- 溜めショットに対応
- 溜めショットの弾のグラフィックも変更
- 一撃で敵を倒せるようにした
- 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 = 1000; 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 ENEMY_X:Number = 250; public static const ENEMY_Y:Number = 220; public static const GOAL_X:Number = 950; public static const GOAL_Y:Number = 200; 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, FLOOR_UY, FLOOR_DY); floorL.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(FLOOR_DAMAGE_LX - 10, 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(GOAL_X, GOAL_Y); 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{ 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(); } } } //全てのオブジェクトを消す public static function Reset():void{ for(var iter:IObject = Instance.m_HeadObj; iter != null; iter = iter.m_NextObj){ iter.Destroy(); } Instance.m_HeadObj = null; } } //!プレイヤー、敵、弾の共通インタフェース 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); } //=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; //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); 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); 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_Baloon:Bitmap; public function SetBaloonText(in_Text:String):void{ //まずは文字列を普通に表示するものを作成 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); { bmp_obj.x = 0.5*this.width;//-text_field.textWidth*0.5; bmp_obj.y = -text_field.textHeight*0.5; } if(m_Baloon){ //前のセリフはオフ removeChild(m_Baloon); } //新しいセリフをセット m_Baloon = bmp_obj; addChild(m_Baloon); } public function ResetBaloonText():void{ if(m_Baloon){ //セリフをオフ removeChild(m_Baloon); m_Baloon = null; } } //=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 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;//マイナスの時はボタンを押していない 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したい } } 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 == null){ SetBaloonText("ウーイ"); } //発射後はセリフを消したいが、他の要因によってセットされたセリフである可能性がある if(m_ShotTimer < 0.0 && m_Baloon != null){ ResetBaloonText(); } } //死亡判定 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(); } //=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); } } //衝突した時に呼ばれる 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() { //Collision m_PhysParam_Density = 10.0;//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 ̄ ̄ ̄"); //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; } } }