前の記事:
- 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;
}
}
}状態遷移を考えて作り直したい。あと同じようなコードが複数ファイルにあるのでまとめたい。