アクションゲームの作成ログ
状況
なんとか、ここに載せられる程度には整理できた。まだ不十分だけど。
プリミティブ描画を廃して、AAベースの描画に移行中。これだけでもなんだか雰囲気が変わる。
あと、位置だけでなく回転もスプライトに反映させるようにした。そのため、弾が回転しておかしく見える状況がある。
明日は、コードをもう少し整理する予定。
コードは長いので、「続きを読む」で折りたたんでおく。
swf
- 左右キー
- 移動
- 上キー
- 浮遊
- Sキー
- ショット
コード
//mxmlc Main.as //author Show=O=Healer package { import Box2D.Dynamics.*; 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 { // private var m_world:b2World; // private var m_physScale:Number = 10; static public const GRAVITY:Number = 10.0; //Player // private var m_Player:b2Body; private var m_Player:CPlayer; //弾 private var m_BulletBd:b2BodyDef; private var m_Bullet:Array = new Array; //Enemy private var m_Enemy:b2Body; //Param public static const FLOOR_W:Number = 400; public static const FLOOR_H:Number = 10; public static const FLOOR_Y:Number = 250; public static const BLOCK_W:Number = 40; public static const BLOCK_H:Number = 20; public static const BLOCK_X:Number = 300; public static const PLAYER_X:Number = 50; public static const PLAYER_Y:Number = 220; public static const PLAYER_R:Number = 10; public static const PLAYER_V:Number = 10; public static const BULLET_X:Number = 50; public static const BULLET_Y:Number = 220; public static const BULLET_R:Number = 5; public static const BULLET_V:Number = 200; public static const ENEMY_X:Number = 100; public static const ENEMY_Y:Number = 220; public static const ENEMY_R:Number = 20; //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; 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; }); } //どうも、クリックでフォーカスを当てないとBox2Dがめりこむので、コンストラクタから分離 private function Init():void { {//Init Box2D //temp // m_world = PhysManager.Instance.m_World; {//Floor var floor:CFloor = new CFloor(); floor.SetPos(FLOOR_W/2, FLOOR_Y + FLOOR_H/2); // GameObjectManager.Regist(floor); floor.OnRegist(this); } {//Player m_Player = new CPlayer(stage); m_Player.SetPos(PLAYER_X, PLAYER_Y); // GameObjectManager.Regist(m_Player); m_Player.OnRegist(this); } {//Blocks for (var i:int = 0; i < 10; i++) { var block:CBlock = new CBlock(); block.SetPos(BLOCK_X, FLOOR_Y - BLOCK_H/2 - i*BLOCK_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(); }); } //in_textを(Bitmapによって)表示するSpriteを作成する private 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; text_field.width = 100; text_field.height = 100; } //その内容をビットマップデータとして取り込む var bmp_data : BitmapData = new BitmapData( 100 , 100 , 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,400,300); 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 = -text_field.textWidth*0.5; bmp_obj.y = -text_field.textHeight*0.5; } //さらにそれを表示するスプライトを作成して返す var sprite:Sprite = new Sprite(); { stage.addChild(sprite); sprite.addChild(bmp_obj); } return sprite; } //=Update= public function Update():void { var deltaTime:Number = 1.0 / 30.0; m_Player.Update(deltaTime); /* {//bullet update //重力を相殺するように力を加える m_Bullet.forEach( function(bullet:b2Body, index:int, arr:Array):void{ bullet.m_force.Add(b2Math.MulFV(bullet.GetMass(), new b2Vec2(0.0, -GRAVITY))); } ); } /*/ BulletManager.Update(deltaTime); //*/ //Box2D Update PhysManager.Update(deltaTime); /* // Render graphics.clear(); for (var bb:b2Body = m_world.m_bodyList; bb; bb = bb.m_next) { //primitive描画っぽいもの for (var s:b2Shape = bb.GetShapeList(); s != null; s = s.GetNext()) { DrawShape(s); } //Spriteへの位置の反映 if(bb.m_userData != null){ bb.m_userData.x = bb.m_position.x * m_physScale; bb.m_userData.y = bb.m_position.y * m_physScale; bb.m_userData.rotation = bb.GetRotation() * 360/(2*3.14); } } //*/ } //=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 * m_physScale, tV.y * m_physScale); 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 * m_physScale, v.y * m_physScale); } graphics.lineTo(tV.x * m_physScale, tV.y * m_physScale); graphics.endFill(); }; drawBox(); } } //*/ } } import Box2D.Dynamics.*; 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; // public static 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); // return m_World.CreateBody(in_BodyDef); } public static function Update(in_DeltaTime:Number):void{ Instance.m_World.Step(in_DeltaTime, 10); // m_World.Step(in_DeltaTime, 10); for (var bb:b2Body = Instance.m_World.m_bodyList; bb; bb = bb.m_next) { //Spriteへの位置の反映 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 IObject extends Sprite { //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 private var m_BodyDef:b2BodyDef; protected var m_Body:b2Body; //Create Collision 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); } } 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 Param protected var m_AA:String = "X"; protected var m_Graphic:Sprite; //Set AA protected function SetAA(in_AA:String):void{ m_AA = in_AA; } //in_textを(Bitmapによって)表示するSpriteを作成する private 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; text_field.width = 999; text_field.height = 999; } //その内容をビットマップデータとして取り込む 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,400,300); 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 = -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; } //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); } } //ゲーム内に出現する時の処理 public function OnRegist(in_Parent:DisplayObjectContainer):void{ m_Graphic = CreateAASprite(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); } //毎フレーム呼ばれる public function Update(in_DeltaTime:Number):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; BulletManager.CreateBullet(stage, x, y, vx, vy); } } //!(プレイヤーの)弾の生成と、重力相殺処理などを担当 class BulletManager { //=Singleton= private static var __Instance:BulletManager; public static function get Instance():BulletManager { if(__Instance == null){ __Instance = new BulletManager(); } return __Instance; } //できれば、これを使って弾のコリジョンを生成したい private var m_BulletBd:b2BodyDef; private var m_BulletArray:Array = new Array; public static const BULLET_R:Number = 5; //Constructor public function BulletManager(){ {//Bullet Init //Shape var ballSd:b2CircleDef = new b2CircleDef(); { ballSd.radius = BULLET_R / PhysManager.PHYS_SCALE; ballSd.density = 10; ballSd.friction = 0.2; ballSd.restitution = 0.9; ballSd.categoryBits = IObject.CATEGORY_PLAYER_BULLET; ballSd.maskBits = IObject.CATEGORY_TERRAIN | IObject.CATEGORY_BLOCK | IObject.CATEGORY_ENEMY; } //Body m_BulletBd = new b2BodyDef(); { m_BulletBd.AddShape(ballSd); } } } //=Static Function= public static function CreateBullet(in_Parent:DisplayObjectContainer, in_X:Number, in_Y:Number, in_VX:Number, in_VY:Number):void{ var bullet:CBullet = new CBullet(); bullet.SetPos(in_X, in_Y); bullet.SetVel(in_VX, in_VY); // GameObjectManager.Regist(m_Player); bullet.OnRegist(in_Parent); Instance.m_BulletArray.push(bullet); } public static function Update(in_DeltaTime:Number):void{ {//bullet update //重力を相殺するように力を加える Instance.m_BulletArray.forEach( function(bullet:CBullet, index:int, arr:Array):void{ bullet.AntiGravity(PhysManager.GRAVITY); } ); } } } //!プレイヤーの弾 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"); } override public function Update(in_DeltaTime:Number):void{ } public function AntiGravity(in_Gravity:Number):void{ m_Body.m_force.Add(b2Math.MulFV(m_Body.GetMass(), new b2Vec2(0.0, -in_Gravity))); } } //!敵 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 { //=Param= static public const COLLISION_W:Number = 400.0; static public const COLLISION_H:Number = 10.0; public function CFloor() { //Collision m_PhysParam_Density = 0.0;//Fix m_PhysParam_Friction = 0.0; // m_PhysParam_Restitution = 0.9; m_PhysParam_CategoryBits = CATEGORY_TERRAIN; CreateCollision_Box(COLLISION_W, COLLISION_H); //Graphic SetAA("__________________________________\n\^o^/^o^\^o^/^o^\^o^/^o^\^o^/^o^\^o^/^o^\^o^/^o^\^o^/\n/^o^\^o^/^o^\^o^/^o^\^o^/^o^\^o^/^o^\^o^/^o^\^o^/^o^\"); } } //!ブロック 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 "); } }