前の記事:
- ActionScript3.0でビットマップ画像を表示する http://itouhiro.hatenablog.com/entry/20130516/flash
の続き
STGをFlashで作る。
マウス動かして自機を操作。マウスクリックで弾を発射。GAME OVERしたあとゲームを再プレイするには、このWebページをリロード。
拡大版 https://sites.google.com/site/itouhiro/2013/20130517stg.swf?attredirects=0
だと画面横の制限なく移動できてしまうな‥‥。
使用した背景画像はNASA。
フォントはPress Start 2P http://www.google.com/fonts/specimen/Press+Start+2P
(c) 2011 Cody "CodeMan38" Boisclair. Released under the SIL Open Font License.
ソースは以下。状態遷移を考えてないフラグ立てプログラミングなので、GAME OVERのあとリスタートできない。
Stg201305.as (Main)
package { import flash.automation.ActionGenerator; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.GradientType; import flash.display.InterpolationMethod; import flash.display.PixelSnapping; import flash.display.Shape; import flash.display.SpreadMethod; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Matrix; import flash.text.StyleSheet; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; /** * ... * @author itouhiro */ [SWF(width="480",height="480",backgroundColor="0x000000",frameRate="60")] public class Stg201305 extends Sprite { private var myShip:MyShip; private var count:int = 0; private var gameOverCount:int; private var scoreTxt:TextField; private var statusTxt:TextField; private var backgroundImg:Bitmap; public var enemies:Array = []; public function Stg201305():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point //背景 backgroundImg = new Bitmap(new BackgroundImg()); addChild(backgroundImg); setChildIndex(backgroundImg, 0); //グラデ背景 //var matrix:Matrix = new Matrix(); //matrix.createGradientBox(stage.stageWidth, stage.stageHeight * 0.8, Math.PI / 3, 0, 0); //graphics.beginGradientFill( //GradientType.LINEAR, //[0x030108, 0x100830], //[0xffffff, 0x999999], //[1, 1], //[0, 255], //matrix, //SpreadMethod.PAD, //InterpolationMethod.RGB, //0); //graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight); //graphics.endFill(); //score表示欄 scoreTxt = new TextField(); var theFont:NamcoFont = new NamcoFont(); /* Font: (c) 2011 Cody "CodeMan38" Boisclair. Released under the SIL Open Font License. */ scoreTxt.defaultTextFormat = new TextFormat(theFont.fontName, 16, 0xEEEEEE); scoreTxt.embedFonts = true; scoreTxt.x = 0; scoreTxt.width = stage.stageWidth; scoreTxt.autoSize = TextFieldAutoSize.CENTER; scoreTxt.y = 10; addChild(scoreTxt); //gameOver表示欄 statusTxt = new TextField(); statusTxt.defaultTextFormat = new TextFormat(theFont.fontName, 16, 0xEEEEEE); statusTxt.embedFonts = true; statusTxt.x = 0; statusTxt.width = stage.stageWidth; statusTxt.autoSize = TextFieldAutoSize.CENTER; statusTxt.y = stage.stageHeight / 2 - 16; //16はフォントの縦幅 statusTxt.text = ''; addChild(statusTxt); //自機を配置 myShip = new MyShip(stage.stageWidth / 2, stage.stageHeight / 2, stage.frameRate, enemies, scoreTxt); addChild(myShip); gameOverCount = stage.frameRate * 2; count = 0; addEventListener(Event.ENTER_FRAME, enterFrameHandler); } private function enterFrameHandler(e:Event):void { var i:int; moveBackground(null); if ( ! myShip.gactive) { //自機爆破中 gameOverCount--; statusTxt.text = 'GAME OVER'; for (i = 0; i < enemies.length; i++) { enemies[i].sgameOver(); enemies[i].alpha = gameOverCount / (stage.frameRate * 2); } //自機爆破終了 if (gameOverCount <= 0) { removeEventListener(Event.ENTER_FRAME, enterFrameHandler); //背景の動作のみ残す addEventListener(Event.ENTER_FRAME, moveBackground); for (i = 0; i < enemies.length; i++) { enemies[i].deleteMyself(); enemies[i] = null; delete enemies[i]; } } return; } //適当に敵を出現 if (count % 6 === 0) { if (Math.random() > 0.75) { var enemy:Enemy = new Enemy(int(stage.stageWidth / 3 * 2 * Math.random()), stage.frameRate, myShip); addChild(enemy); enemies.push(enemy); } } } private function moveBackground(e:Event):void { count++; backgroundImg.y = Math.sin((count % 360) * Math.PI / 180) * 20 + 20; // 0~40 } } }
MyShip.as
package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.PixelSnapping; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.media.Sound; import flash.text.TextField; /** * ... * @author ... */ public class MyShip extends Sprite { private var img:Bitmap; private var backfire:Bitmap; private var bulletCount:int = 0; //弾を射出してからのカウント private var enemies:Array = []; private var bomberCount:int = 0; private var active:Boolean = true; private var score:int = 0; private var scoreTxt:TextField; var imgSrc:Array = []; var count:int = 0; var displayBackFire:Boolean = true; var fps:int = 0; public function MyShip(_x:int, _y:int, _fps:int, _enemies:Array, _scoreTxt:TextField) { x = _x; y = _y; fps = _fps; enemies = _enemies; scoreTxt = _scoreTxt; //自機 imgSrc[0] = new MyShip1(); imgSrc[1] = new MyShip2(); img = new Bitmap(imgSrc[0], PixelSnapping.AUTO, false); img.scaleX = img.scaleY = 4.0; img.x = -(img.width / 2); //マウスカーソルが指すのをimg画像「中央」にする。0の場合、画像「左上端」を指す img.y = -(img.height / 2); addChild(img); imgSrc[2] = new Bang1(); imgSrc[3] = new Bang2(); //自機のロケット噴射 backfire = new Bitmap(new MyShipBackFire(), PixelSnapping.AUTO, false); backfire.scaleX = backfire.scaleY = 4.0; backfire.x = -(img.width / 2) + 1; //素材が1pixelずれてるので補正 backfire.y = -(img.height / 2) + img.height; //myShipImgの画面下部に位置するよう指定 backfire.alpha = 0.8; displayBackFire = true; addChild(backfire); score = 0; setScore(score); count = 0; addEventListener(Event.ENTER_FRAME, enterFrameHandler); bulletCount = 0; addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); } private function mouseDownHandler(e:MouseEvent):void { //弾を発射 var bullet:MyBullet = new MyBullet(x, y - img.height / 2, fps, enemies, this); parent.addChild(bullet); //myShipの子ではなく、stageの子にする parent.setChildIndex(bullet, 1); //背面に配置 bulletCount = fps / 4; } private function enterFrameHandler(e:Event):void { if (bomberCount > 1) { img.alpha = bomberCount / 120; bomberCount--; if (bomberCount === 1) { //消去 removeEventListener(Event.ENTER_FRAME, enterFrameHandler); removeChild(img); visible = false; } //爆発パターンを切り替え count++; if (count >= fps / 2) { count = 0; displayBackFire = ! displayBackFire; if (displayBackFire){ img.bitmapData = imgSrc[2]; }else { img.bitmapData = imgSrc[3]; } } return; } x += (parent.mouseX - x) / 10; //parent. つけないと値がおかしい y += (parent.mouseY - y) / 10; count++; if (count >= fps / 2) { //半秒間ごとにロケット噴射の描写を切り替え count = 0; displayBackFire = ! displayBackFire; backfire.visible = displayBackFire; } if (bulletCount > 0) { //弾を射出してから1/4秒は、自機の画像を別のに変更 img.bitmapData = imgSrc[1]; bulletCount--; }else { img.bitmapData = imgSrc[0]; } } public function get gx():int { //gx .. get x の略 return x; } public function get gy():int { return y; } //爆発 public function bomber():void { img.bitmapData = imgSrc[2]; displayBackFire = true; active = false; bomberCount = 120; removeChild(backfire); var snd:Sound = new MyShipBomber(); snd.play(0); snd.play(50); snd.play(100); } public function get gactive():Boolean { return active; } public function setScore(plusScore:int):void { score += plusScore; scoreTxt.text = 'SCORE ' + ('0000000' + score).slice( -7); } } }
MyBullet.as
package { import flash.display.Bitmap; import flash.display.Sprite; import flash.events.Event; /** * ... * @author ... */ public class MyBullet extends Sprite { private var img:Bitmap; private var count:int = 0; private var fps:int; private var enemies:Array; private var myShip:MyShip; //自機の射出した弾 public function MyBullet(_x:int, _y:int, _fps:int, _enemies:Array, _myShip:MyShip) { x = _x; y = _y; fps = _fps; enemies = _enemies; myShip = _myShip; img = new Bitmap(new MyFire()); img.scaleX = img.scaleY = 4.0; img.x = -(img.width / 2); img.y = -(img.height / 2); addChild(img); addEventListener(Event.ENTER_FRAME, enterFrameHandler); } private function enterFrameHandler(e:Event):void { y -= fps * 0.05; //当たり判定 if (myShip.gactive) { var elen:int = enemies.length; for (var i:int = 0; i < elen; i++) { if (Math.abs(enemies[i].x - x) < 48 && Math.abs(enemies[i].y - y) < 48) { enemies[i].bomber(); enemies.splice(i, 1); myShip.setScore(100); deleteMyself(); return; } } } if (y < -img.height * 2) { //画面外に出たら消去 deleteMyself(); } } private function deleteMyself():void { removeEventListener(Event.ENTER_FRAME, enterFrameHandler); removeChild(img); parent.removeChild(this); delete this; } } }
Enemy.as
package { import adobe.utils.CustomActions; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.PixelSnapping; import flash.display.Sprite; import flash.events.Event; import flash.media.Sound; /** * ... * @author ... */ public class Enemy extends Sprite { private var img:Bitmap; private var imgSrc:Array = []; private var fps:int = 0; private var count:int = 0; private var index:int = 0; private var tendencyX:Number = 0; private var tendencyY:Number = 0; private var myShip:MyShip; private var bomberCount:int = 0; private var gameOver:Boolean = false; public function Enemy(_x:int, _fps:int, _myShip:MyShip) { imgSrc[0] = new Enemy1(); imgSrc[1] = new Enemy2(); img = new Bitmap(imgSrc[0], PixelSnapping.AUTO, false); img.scaleX = img.scaleY = 4.0; img.x = -(img.width / 2); //マウスカーソルが指すのをimg画像「中央」にする。0の場合、画像「左上端」を指す img.y = -(img.height / 2); imgSrc[2] = new Bang1(); imgSrc[3] = new Bang2(); x = _x; y = -img.height; fps = _fps; myShip = _myShip; addChild(img); //tendency ‥‥ どちらに移動するかの傾向 tendencyX = (Math.random() * 3.0) - 1.0; tendencyY = Math.random() * 3.0; count = 0; index = 0; addEventListener(Event.ENTER_FRAME, enterFrameHandler); } private function enterFrameHandler(e:Event):void { x += (Math.random() * 2.0) * tendencyX; y += Math.random() * tendencyY; count++; if (bomberCount <= 0) { //撃墜されてない。通常運行 if (count % (fps / 2) === 0) { //半秒間ごとに画像パターンを切り替え index = (index + 1) % 2; img.bitmapData = imgSrc[index]; //弾を撃つ if ( ! gameOver && Math.random() * 10.0 + (tendencyY*0.5) > 9.1) { var blt:Bullet = new Bullet(x, y, fps, Math.atan2(myShip.gy - y, myShip.gx - x), myShip); parent.addChild(blt); parent.setChildIndex(blt, 2); } } if (count % (fps * 2) === 0) { //移動方向変わるかも tendencyX = (Math.random() * 3.0) - 1.0; } } else { //自機に撃墜されて爆発中 if (count % (fps / 4) === 0) { index = (index + 1) % 2; img.bitmapData = imgSrc[index + 2]; } img.alpha = (bomberCount * 3) / 100; bomberCount--; } if (outFromStage()){ //画面外に出たらor爆発終了したら消去 deleteMyself(); removeChild(img); parent.removeChild(this); delete this; } } public function deleteMyself():void { removeEventListener(Event.ENTER_FRAME, enterFrameHandler); } private function outFromStage():Boolean { if (y > stage.stageHeight + img.height || y < -img.height * 2 || x > stage.stageWidth + img.width * 2 || x < -img.width * 2 || bomberCount === 1) { return true; } return false; } public function bomber():void { img.bitmapData = imgSrc[index + 2]; bomberCount = 30; var snd:Sound = new MyShipBomber(); snd.play(); } public function sgameOver():void { gameOver = true; } } }
Bullet.as
package { import flash.display.Bitmap; import flash.display.Sprite; import flash.events.Event; /** * ... * @author ... */ public class Bullet extends Sprite { private var img:Bitmap; private var fps:int = 0; private var radian:Number; private var myShip:MyShip; //敵弾 public function Bullet(_x:int, _y:int, _fps:int, _radian:Number, _myShip:MyShip) { x = _x; y = _y; fps = _fps; radian = _radian; myShip = _myShip; img = new Bitmap(new EnemyFire()); img.scaleX = img.scaleY = 4.0; img.x = -(img.width / 2); img.y = -(img.height / 2); addChild(img); addEventListener(Event.ENTER_FRAME, enterFrameHandler); } private function enterFrameHandler(e:Event):void { x += Math.cos(radian) * fps * 0.03; y += Math.sin(radian) * fps * 0.03; //当たり判定 if (myShip.gactive && Math.abs(myShip.gx - x) < 24 && Math.abs(myShip.gy - y) < 24) { myShip.bomber(); removeEventListener(Event.ENTER_FRAME, enterFrameHandler); removeChild(img); parent.removeChild(this); delete this; return; } if (outFromStage()) { //画面外に出たら消去 removeEventListener(Event.ENTER_FRAME, enterFrameHandler); removeChild(img); parent.removeChild(this); delete this; } } private function outFromStage():Boolean { if (y > stage.stageHeight + img.height || y < -img.height * 2 || x > stage.stageWidth + img.width * 2 || x < -img.width * 2) { return true; } return false; } } }
状態遷移を考えて作り直したい。あと同じようなコードが複数ファイルにあるのでまとめたい。