ActionScript3でパックマンっぽいのを作ってみた2
パック○ン風ゲームの更新です。
今回はソースコード付きです。
ソースコード一式のダウンロードはこちら↓
http://www.geocities.jp/finnissy/Blog/as3/pac_src.zip
ColorDefined.as
package game { import adobe.utils.CustomActions; import Math; /** * ... * @author Korsakov */ public final class ColorDefined { public static const COLOR_RED:uint = 0xFF0000; public static const COLOR_BLUE:uint = 0x0000ff; public static const COLOR_GREEN:uint = 0x00ff00; public static const COLOR_PINK:uint = 0xff1493; public static const COLOR_WHITE:uint = 0xFFFFFF; public static const COLOR_BLACK:uint = 0x000000; public static const COLOR_YELLOW:uint = 0xFFFF00; public static const COLOR_SKYBLUE:uint = 0x0033FF; public static const COLOR_ORANGE:uint = 0xFF9900; public static const COLOR_GRAY:uint = 0xDDDDDD; public static const COLOR_TMP:uint = 0x6a5acd; public function ColorDefined() { } public static function choiceRandomColor(idx:int = 0):uint { var color:uint = 0x000000; idx = idx * (Math.random() * 10); trace(idx); switch (idx % 10) { case 0: color = ColorDefined.COLOR_BLACK; break; case 1: color = ColorDefined.COLOR_BLUE; break; case 3: color = ColorDefined.COLOR_GREEN; break; case 4: color = ColorDefined.COLOR_PINK; break; case 5: color = ColorDefined.COLOR_RED; break; case 6: color = ColorDefined.COLOR_WHITE; break; case 7: color = ColorDefined.COLOR_YELLOW; break; case 8: color = ColorDefined.COLOR_ORANGE; break; case 9: color = ColorDefined.COLOR_TMP; break; } trace("color = " + color); return color; } } }
Enemy.as
package game { import flash.display.ActionScriptVersion; import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.Event; import flash.geom.Point; import flash.display.Loader; /** * ... * @author Korsakov */ public class Enemy extends PacObj { private static var imgType1:Vector.<BitmapData>; private static var imgType2:Vector.<BitmapData>; private static var imgType3:Vector.<BitmapData>; private static var imgType4:Vector.<BitmapData>; private static var imgType5:Vector.<BitmapData>; private static var eIndex:int; private static const ENEMY_MAX:int = 4; private static const LONG_DISTANCE:int = 1024*11; //適当... private static const MID_DISTANCE:int = 1024*7; private static const SHOUT_DISTANCE:int = 1024 * 4; private static const STATE_HOME:int = 0; private static const STATE_ATTACK:int = 1; private static const STATE_GETAWAY:int = 2; private static const STATE_RETURN:int = 3; private static const STATE_RUN:int = 3; private var img:Vector.<BitmapData>; private var type:int; private var dumyPoint:Point; private var ePosition:Point; private var pacp:Point; private var funcArray:Array; private var state:int; private var doorEv:Event; private var death:Event; private var enemyEating:Event; //////////////////////////////////////////////////////////////////////////// // ここからパブリックメソッド //////////////////////////////////////////////////////////////////////////// public function Enemy(x:int, y:int) { old_direction = now_direction = Field.LEFT; super(x, y); type = eIndex++ % ENEMY_MAX; setImage(type); ePosition = new Point(); pacp = new Point(); //関数ポインタ配列 funcArray = new Array(); funcArray.push(home); funcArray.push(getNewDirection); funcArray.push(returnHome); funcArray.push(home); funcArray.push(home); //巣の入り口を設定 dumyPoint = homePosition(); state = STATE_HOME; doorEv = new GameEvent(GameEvent.DOOR, 0); death = new GameEvent(GameEvent.DEATH, 0); enemyEating = new GameEvent(GameEvent.EATING, 1000); } public static function makeImage(loadObj:Loader):void { var bmp:BitmapData = new BitmapData(loadObj.width, loadObj.height, true, 0x00000000); bmp.draw(loadObj); imgType1 = new Vector.<BitmapData>(); imgType1 = makeImageSub(bmp, 0, 4 * 16, 16, 16); imgType2 = makeImageSub(bmp, 4 * 16, 8 * 16, 16, 16); imgType3 = makeImageSub(bmp, 0, 4 * 16, 32, 16); imgType4 = makeImageSub(bmp, 4 * 16, 8 * 16, 32, 16); imgType5 = makeImageSub(bmp, 0, 4 * 16, 48, 16); } override public function draw():void { var i:int; switch(now_direction) { case Field.LEFT: i = 0; break; case Field.UP: i = 1; break; case Field.DOWN: i = 2; break; case Field.RIGHT: i = 3; break; default: trace("imageUpdate NG");break; } if (this.canvas == null || this.img == null) return; canvas.copyPixels(img[i], img[i].rect, new Point(x, y)); } override public function updatePosition():Boolean { var r:Boolean = super.updatePosition(); if (r == false) { now_direction = changeDrc(now_direction, false); r = super.updatePosition(); if (r == false) { now_direction = changeDrc(now_direction, false); r = super.updatePosition(); if (r == false) { now_direction = changeDrc(now_direction, true); r = super.updatePosition(); } } } switch (state) { case STATE_HOME: if (++numCycle > 60 + (type * 30)) { Field.door = false; state = STATE_RUN; dispatchEvent(doorEv); dumyPoint = doorPosition(); } break; case STATE_RUN: if (isHit(dumyPoint, position)) { state = STATE_ATTACK; Field.door = true; dispatchEvent(doorEv); } break; case STATE_ATTACK: if (isHit(pacp, position)) { //TODO:死んだときのイベントを実装する state = STATE_HOME; dispatchEvent(death); } break; case STATE_GETAWAY: if (isHit(pacp, position)) { state = STATE_RETURN; dumyPoint = new Point(Field.DOOR_Y + 1, Field.DOOR_X - 1); dispatchEvent(enemyEating); } break; case STATE_RETURN: if (isHit(dumyPoint, position)) { state = STATE_ATTACK; dumyPoint = doorPosition(); dispatchEvent(enemyEating); } break; default: break; } return r; } private function changeDrc(drc:int,drcFlg:Boolean):int { switch (drc) { case Field.LEFT: case Field.RIGHT: if (drcFlg == false) { return Field.UP; } return Field.DOWN; case Field.DOWN: case Field.UP: if (drcFlg == false) { return Field.LEFT; } return Field.RIGHT; default: return drc; } } public function updateDirection():void { reloadPacposition(); now_direction = funcArray[state](pacp, position); } public function resetState():void { state = STATE_HOME; dumyPoint = homePosition(); } //////////////////////////////////////////////////////////////////////////// // ここからプライベートメソッド //////////////////////////////////////////////////////////////////////////// private function getNewDirection(pac:Point, ene:Point):int { switch (this.type) { case 0: return ambush(pac, ene); break; case 1: return free(pac, ene); break; case 2: return intercept(pac, ene); break; case 3: return pursuit(pac, ene); break; default: break; } return now_direction; } //ひたすら追跡型 private function pursuit(pac:Point, ene:Point):int //int... return value is New Direction { var t1:int = now_direction; var t2:int; if (Math.abs(pac.x - this.x) < Math.abs(pac.y - this.y)) { if ((t2 = checkLR(pac, ene)) != t1) return t2; else return checkUD(pac, ene); } else { if ((t2 = checkUD(pac, ene)) != t1) return t2; else return checkLR(pac, ene); } } //迎撃型 private function intercept(pac:Point, ene:Point):int { var distance:Number = Math.pow(pac.x - ene.x, 2) + Math.pow(pac.y - ene.y, 2); if (distance <= SHOUT_DISTANCE || distance > MID_DISTANCE) { return pursuit(pac, ene); } return now_direction; } //自由型 private function free(pac:Point, ene:Point):int { if (!(numCycle % 10)) { trace("dumy update"); dumyPoint = pointGenerator(); } if (Math.abs(pac.x - this.x) < Math.abs(pac.y - this.y)) { return checkLR(pac, ene); } else { return checkUD(pac, ene); } } //待ち伏せ型 private function ambush(pac:Point, ene:Point):int { var distance:Number = Math.pow(pac.x - ene.x, 2) + Math.pow(pac.y - ene.y, 2); if (distance <= SHOUT_DISTANCE) { return pursuit(pac, ene); } else { return free(pac, ene); } } //ひたすら逃げる private function getaway(pac:Point, ene:Point):int //int... return value is New Direction { var t1:int = now_direction; var t2:int; if (Math.abs(pac.x - this.x) < Math.abs(pac.y - this.y)) { if ((t2 = getawayLR(pac, ene)) != t1) return t2; else return getawayUD(pac, ene); } else { if ((t2 = getawayUD(pac, ene)) != t1) return t2; else return getawayLR(pac, ene); } } //巣の中をうろつく private function home(pac:Point, ene:Point):int { var dumy:Point = dumyPoint; return pursuit(dumyPoint, ene); } //巣に帰る private function returnHome(ene:Point):int { return pursuit(dumyPoint, ene); } private function checkLR(pp:Point, pe:Point):int { if (pp.x < pe.x && now_direction != Field.RIGHT && Field.moving(pe.x - move_value, pe.y, Field.LEFT)) return Field.LEFT; else if (pp.x > pe.x && now_direction != Field.LEFT && Field.moving(pe.x + move_value, pe.y, Field.RIGHT)) return Field.RIGHT; return now_direction; } private function checkUD(pp:Point, pe:Point):int { if (pp.y < pe.y && now_direction != Field.DOWN && Field.moving(pe.x, pe.y - move_value, Field.UP)) return Field.UP; else if (pp.y > pe.y && now_direction != Field.UP && Field.moving(pe.x, pe.y + move_value, Field.LEFT)) return Field.DOWN; return now_direction; } private function getawayLR(pp:Point, pe:Point):int { if (pp.x < pe.x && now_direction != Field.LEFT) return Field.RIGHT; else if (pp.x > pe.x && now_direction != Field.RIGHT)return Field.LEFT; return now_direction; } private function getawayUD(pp:Point, pe:Point):int { if (pp.y < pe.y && now_direction != Field.UP) return Field.DOWN; else if (pp.y > pe.y && now_direction != Field.DOWN) return Field.UP; return now_direction; } private function pointGenerator():Point { var x:int = int(Math.random() * Field.FRAME_W) / Field.FIELD_GRID; var y:int = int(Math.random() * Field.FRAME_H) / Field.FIELD_GRID; var f:int = Field.field[y][x]; if (f == Field.NOTHING || f == Field.AFTER_EATING) { trace(x, y); return new Point(x, y); } return pointGenerator(); } private function doorPosition():Point { return new Point(Field.DOOR_Y * Field.FIELD_GRID, Field.DOOR_X * Field.FIELD_GRID); } private function homePosition():Point { return new Point((Field.DOOR_Y + 2) * Field.FIELD_GRID, (Field.DOOR_X + 2) * Field.FIELD_GRID); } private function isHit(pac:Point,ene:Point):Boolean { var x:int = pac.x - ene.x; var y:int = pac.y - ene.y; var g:int = Field.FIELD_GRID / 2; if (x * x + y * y < g * g) return true; return false; } private function get position():Point { ePosition.x = Number(this.x); ePosition.y = Number(this.y); return ePosition; } private function reloadPacposition():Point { var target:Array = new Array(); for each(var p:Point in game.Pacman.position) { var X:int = Math.abs(p.x - this.x); var Y:int = Math.abs(p.y - this.y); target.push(int(Math.pow(X, 2) + Math.pow(Y, 2))); } var t:Array = target.sort(function(p1:int, p2:int):int { return p1 - p2; }); pacp = Pacman.position[target.indexOf(t[0])]; return pacp; } private function setImage(type:int):void { switch (type) { case 0: this.img = imgType1; break; case 1: this.img = imgType2; break; case 2: this.img = imgType3; break; case 3: this.img = imgType4; break; case 4: this.img = imgType5; break; default: break; } } } }
Field.as
package game { import RangeError; /** * 移動可能な範囲を定義したクラス * オブジェクトを移動させる場合は、moving()でチェック */ public class Field { /******************************************************* * 定数宣言領域 ******************************************************/ public static const WINDOW_COLOR:uint = 0xFF000000; public static const WINDOW_W:uint = 465; public static const WINDOW_H:uint = 465; public static const FIELD_W:uint = 25; public static const FIELD_H:uint = 27; public static const FIELD_GRID:int = 16; public static const FRAME_W:uint = FIELD_GRID * FIELD_W; public static const FRAME_H:uint = FIELD_GRID * FIELD_H; public static const UP:int = 1; public static const LEFT:int = 2; public static const DOWN:int = 4; public static const RIGHT:int = 8; public static const NOTHING:int = 0; public static const WALL:int = 1; public static const AFTER_EATING:int = -1; public static const ENEMY_HOME:int = -2; public static const DOOR_X:int = 11; public static const DOOR_Y:int = 11; public static const OUTDOOR_X:int = 12; public static const OUTDOOR_Y:int = 12; public static const DOOR_CLOSE:int = 2; /******************************************************* * 変数宣言領域 ******************************************************/ //注意 上記パラメーターを変えた場合、以下のフィールド変数も更新すること public static var field:Array = [ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ],//0 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ],//1 [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ],//2 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ],//3 [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ],//4 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ],//5 [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ],//6 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ],//7 [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ],//8 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ],//9 [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ],//10 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ],//11 [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 2, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ],// 12 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,-2,-2,-2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ],//13 [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ],// 14 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ],//15 [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ],// 16 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ],//17 [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ],// 18 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ],//19 [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ],// 20 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ],//21 [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ],// 22 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ],//23 [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ],// 24 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ],//25 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ],//26 ]; public function Field() { } /*********************************************************************** * 進行方向に進めるか調べる * 進行可能: true * 進行不可能: false **********************************************************************/ public static function moving(x:int, y:int, dirc:int):Boolean { var x1:int = x / FIELD_GRID; var y1:int = y / FIELD_GRID; var x2:int = (x + FIELD_GRID-1) / (FIELD_GRID); var y2:int = (y + FIELD_GRID-1) / (FIELD_GRID); var l1:Boolean, l2:Boolean, r1:Boolean, r2:Boolean; var u1:Boolean, u2:Boolean, d1:Boolean, d2:Boolean; var fi:Array = [field[y1][x1], field[y2][x1], field[y1][x2], field[y2][x2]]; l1 = (fi[0] == NOTHING) || (fi[0] == AFTER_EATING) || (fi[0] == ENEMY_HOME); l2 = (fi[1] == NOTHING) || (fi[1] == AFTER_EATING) || (fi[1] == ENEMY_HOME); r1 = (fi[2] == NOTHING) || (fi[2] == AFTER_EATING) || (fi[2] == ENEMY_HOME); r2 = (fi[3] == NOTHING) || (fi[3] == AFTER_EATING) || (fi[3] == ENEMY_HOME); u1 = l1; u2 = r1; d1 = l2; d2 = r2; switch( dirc ) { case LEFT: return (l1 && l2); case RIGHT: return (r1 && r2); case UP: return (u1 && u2); case DOWN: return (d1 && d2); default: break; } return false; } public static function set door(toggle:Boolean):void { field[OUTDOOR_Y][OUTDOOR_X] = (toggle == false ? NOTHING:DOOR_CLOSE); } public static function get isdoor():Boolean { return (field[OUTDOOR_Y][OUTDOOR_X] == NOTHING ? false:true); } } }
GameEvent.as
package game { import flash.events.Event; /** * ... * @author Korsakov */ public class GameEvent extends Event { public static const UPDATE:String = "update"; public static const POWERUP:String = "powerup"; public static const GAME_OVER:String = "gameover"; public static const GAME_CLEAR:String = "gameclear"; public static const DOOR:String = "doorevent"; public static const DEATH:String = "death"; public static const EATING:String = "eating"; public var value:int; public function GameEvent(type:String, value:int,bubbles:Boolean = false) { super(type, bubbles); this.value = value; } public override function clone():Event { return new GameEvent(type, value); } public override function toString():String { return formatToString("GameEvent", "type", "bubbles", "cancelable", "eventPhase"); } } }
Main.as
package game { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BlendMode; import flash.display.InteractiveObject; import flash.display.Sprite; import flash.display.GradientType; import flash.events.Event; import flash.events.MouseEvent; import flash.events.KeyboardEvent; import flash.events.SecurityErrorEvent; import flash.geom.Matrix; import flash.geom.Point; import flash.net.URLRequest; import flash.net.URLRequestHeader; import flash.text.TextField; import flash.display.Stage; import flash.display.StageAlign; import flash.display.Loader; import flash.system.LoaderContext; import flash.geom.Rectangle; import flash.text.TextFormat; import flash.text.TextFormatAlign; /** * ... * @author Korsakov */ [SWF(width="465", height="465", frameRate="30", backgraundColor="0x000000")] public class Main extends Sprite { /******************************************************* * 変数宣言領域 ******************************************************/ private var play_field:PlayField; private var demonstration:Sprite; private var reposeDisplay:Sprite; private var gameComplete:Sprite; private var pacman:Pacman; private var enemys:Vector.<Enemy>; private var help:TextField; private var scoreText:TextField; private var loader:Loader; private var playing:Boolean; private var isdead:Boolean; private var score:int = 0; private var playerNum:int = 4; private var waitcnt:int; private static const speed:int = 4;//1,2,4,8のみ対応 private static const PAC_INITPOSTION_X:int = 12; private static const PAC_INITPOSTION_Y:int = 19; private static const WAIT_NUM:int = 60; /******************************************************* * 関数定義 ******************************************************/ public function Main():void { help = new TextField(); help.defaultTextFormat = new TextFormat("_sans", 10, 0xCCDDEE); scoreText = new TextField(); scoreText.defaultTextFormat = new TextFormat("_sans", 20, 0xCCDDEE); if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); var context:LoaderContext = new LoaderContext(true); loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadingComplete); loader.load(new URLRequest("http://www.geocities.jp/finnissy/Blog/as3/pac.png"),context); //loader.load(new URLRequest("file:///E:/ブログ/pac.png")); } private function loadingComplete(ev:Event):void { //パックマンのモジュールを初期化 pacInit(); //タイトル画面の作成 demonstration = new Sprite(); demonstration.x = 25; demonstration.y = 15; var pacTitle:TextField = new TextField(); pacTitle.defaultTextFormat = new TextFormat("_sans", 30, 0xFF0000, null,null,null,null,null,TextFormatAlign.CENTER); pacTitle.width = 400; pacTitle.y = 50; pacTitle.text = "パクマン"; var ctl:TextField = new TextField(); ctl.defaultTextFormat = new TextFormat("_sans", 12, 0x3322AA, null, null, null, null, null, TextFormatAlign.CENTER); ctl.width = 400; ctl.y = 100; ctl.text = "Enterを押すとゲーム開始"; //休憩画面の作成 reposeDisplay = new Sprite(); reposeDisplay.x = 25; reposeDisplay.y = 15; var reposetitle:TextField = new TextField(); reposetitle.defaultTextFormat = new TextFormat("_sans", 30, 0xFF3300, null,null,null,null,null,TextFormatAlign.CENTER); reposetitle.width = 400; reposetitle.y = 50; reposetitle.text = "Dead..."; //ゲームクリアーの画面 gameComplete = new Sprite(); gameComplete.x = 25; gameComplete.y = 15; var endtitle:TextField = new TextField(); endtitle.defaultTextFormat = new TextFormat("_sans", 30, 0x0055EE, null, null, null, null, null, TextFormatAlign.CENTER); endtitle.multiline = true; endtitle.y = 100; endtitle.width = 400; endtitle.height = 100; endtitle.text = "Complete Stage!!\n(;´Д`)ハァハァ"; demonstration.addChild(pacTitle); demonstration.addChild(ctl); addChild(demonstration); reposeDisplay.addChild(reposetitle); gameComplete.addChild(endtitle); playing = true; isdead = false; waitcnt = WAIT_NUM; addEventListener(Event.ENTER_FRAME, title, false, 0); stage.addEventListener(KeyboardEvent.KEY_DOWN, gameStartAction); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); //ヘルプ画面作成 help.width = 60; help.y = 10; help.x = 400; help.text = "移動\n ←↑↓→\n\n\n終了 Esc"; scoreText.x = 16; scoreText.y = Field.FRAME_H; scoreText.text = "" + 0; } private function pacInit():void { Pacman.makeImage(loader); Enemy.makeImage(loader); play_field = new PlayField(Field.FRAME_W, Field.FRAME_H, Field.WINDOW_COLOR, loader); pacman = new Pacman(12, 19); enemys = new Vector.<Enemy>(); for (var i:int = 0; i < 4; i++) { enemys.push(new Enemy(11 + (i % 3), 13)); } play_field.addEventListener(GameEvent.UPDATE, updateScore); play_field.addEventListener(GameEvent.GAME_CLEAR, gameClear); play_field.addEventListener(GameEvent.DEATH, deathGame); pacman.setMoveValue = speed;//パックの移動量 play_field.addPacObj(pacman);//パックをプレイフィールドに追加 play_field.putPacImg(playerNum); for each(var em:Enemy in enemys) { play_field.addPacObj(em); } } private function resetGame():void { score = 0; scoreText.text = ""; play_field.resetField(true); play_field.putPacImg(playerNum); } private function deathGame(e:Event):void { playing = false; isdead = true; if (playerNum < 1) { resetGame(); } else { play_field.resetField(false); play_field.putPacImg(playerNum); pacman.changeDirection(Field.LEFT); } } private function gameClear(e:Event):void { removeChild(play_field); removeChild(help); removeChild(scoreText); removeEventListener(Event.ENTER_FRAME, drawing); stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyboardEvent); playing = true; addChild(gameComplete); addEventListener(Event.ENTER_FRAME, complete, false, 0); stage.addEventListener(KeyboardEvent.KEY_DOWN, gameRotation); } private function updateScore(e:GameEvent):void { score += e.value; scoreText.text = "" + score; } //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ // KeyboardEvent private function gameStartAction(ev:KeyboardEvent):void { switch(ev.keyCode) {//Enter,Z case 13:case 90: playing = false; break; } } private function keyboardEvent(ev:KeyboardEvent):void { switch(ev.keyCode) {//Escape case 27: resetGame(); playing = false; break; } pacman.updateDirection(ev); } private function gameRotation(ev:KeyboardEvent):void { switch(ev.keyCode) {//Escape case 13:case 27: playing = false; break; } } //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ // EnterFrameEvent private function title(ev:Event):void { // TODO:デモ画面作成 if (!playing) { //ゲーム開始画面に遷移 removeChild(demonstration); removeEventListener(Event.ENTER_FRAME, title); stage.removeEventListener(KeyboardEvent.KEY_DOWN, gameStartAction); playing = true; addChild(play_field); addChild(help); addChild(scoreText); addEventListener(Event.ENTER_FRAME, drawing, false, 0); stage.addEventListener(KeyboardEvent.KEY_DOWN, keyboardEvent); } } private function drawing(ev:Event):void { //FPS30で毎フレーム実行される処理 play_field.updateAll(); if (!playing) { if (isdead) { //休憩画面に切り替える removeChild(play_field); removeChild(help); removeChild(scoreText); removeEventListener(Event.ENTER_FRAME, drawing); stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyboardEvent); addChild(reposeDisplay); addEventListener(Event.ENTER_FRAME, repose, false, 0); } else { //タイトル画面に切り替える removeChild(play_field); removeChild(help); removeChild(scoreText); removeEventListener(Event.ENTER_FRAME, drawing); stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyboardEvent); playing = true; addChild(demonstration); addEventListener(Event.ENTER_FRAME, title, false, 0); stage.addEventListener(KeyboardEvent.KEY_DOWN, gameStartAction); } } } private function repose(ev:Event):void { if (--waitcnt < 1) //30fps = 1sec... { playing = true; isdead = false; waitcnt = WAIT_NUM; removeChild(reposeDisplay); removeEventListener(Event.ENTER_FRAME, repose); if (playerNum > 0) { //プレイ画面に戻す playerNum--; addChild(play_field); addChild(help); addChild(scoreText); addEventListener(Event.ENTER_FRAME, drawing, false, 0); stage.addEventListener(KeyboardEvent.KEY_DOWN, keyboardEvent); } else { //残奇数ゼロならタイトル画面に遷移 playerNum = 4; addChild(demonstration); addEventListener(Event.ENTER_FRAME, title, false, 0); stage.addEventListener(KeyboardEvent.KEY_DOWN, gameStartAction); } } } private function complete(ev:Event):void { if (!playing) { //タイトル画面に切り替える removeChild(gameComplete); removeEventListener(Event.ENTER_FRAME, complete); stage.removeEventListener(KeyboardEvent.KEY_DOWN, gameRotation); playing = true; resetGame(); addChild(demonstration); addEventListener(Event.ENTER_FRAME, title, false, 0); stage.addEventListener(KeyboardEvent.KEY_DOWN, gameStartAction); } } //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ } }
Pacman.as
package game { import flash.display.BitmapData; import flash.display.IBitmapDrawable; import flash.display.Loader; import flash.display.Shape; import flash.events.KeyboardEvent; import flash.filters.BlurFilter; import flash.geom.Matrix; import flash.geom.Point; import flash.display.BlendMode; import flash.geom.Rectangle; /** * ... * @author Korsakov */ public class Pacman extends PacObj { private static var imgBase:Vector.<BitmapData>; public static var position:Vector.<Point>; public static const MOTION:int = 2; private var motionIndex:int; public var pacIdx:int; public function Pacman(x:int, y:int) { old_direction = now_direction = Field.LEFT; super(x, y); if (position == null) position = new Vector.<Point>(); pacIdx = position.length; position.push(new Point(this.x, this.y)); } public static function makeImage(loadObj:Loader):void { var bmp:BitmapData = new BitmapData(loadObj.width, loadObj.height, true, 0x00000000); bmp.draw(loadObj); imgBase = makeImageSub(bmp, 0, 8 * 16, 0, 16); } override public function draw():void { var i:int; switch(now_direction) { case Field.LEFT: i = 0; break; case Field.UP: i = 2; break; case Field.DOWN: i = 4; break; case Field.RIGHT: i = 6; break; default: trace("imageUpdate NG");break; } if (this.canvas == null || imgBase == null) return; i += motionIndex; canvas.copyPixels(imgBase[i], imgBase[i].rect, new Point(x, y)); if (++numCycle % updateCycle == 0) { motionIndex = (motionIndex + 1) % MOTION; } } public function updateDirection(ev:KeyboardEvent):Boolean { switch(ev.keyCode) { case 38: now_direction = Field.UP; break; //up key case 40: now_direction = Field.DOWN; break; //down key case 37: now_direction = Field.LEFT; break; //left key case 39: now_direction = Field.RIGHT;break; //right key default: return false; } trace("0: up, 1: down, 2: left, 3: rgith "); trace("updateDirection ok" + now_direction); return true; } public function changeDirection(dr:int):void { switch (dr) { case Field.UP: now_direction = Field.UP; break; //up key case Field.DOWN: now_direction = Field.DOWN; break; //down key case Field.LEFT: now_direction = Field.LEFT; break; //left key case Field.RIGHT: now_direction = Field.RIGHT;break; //right key default: break; } } override public function updatePosition():Boolean { var bool:Boolean = super.updatePosition(); if (bool) { position[pacIdx].x = this.x; position[pacIdx].y = this.y; } return bool; } } }
PacObj.as
package game { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Loader; import flash.display.Sprite; import flash.display.GradientType; import flash.events.EventDispatcher; import flash.geom.Point; import flash.geom.Rectangle; /** * ... * @author Korsakov * 動き回るオブジェクトの基底クラス */ public class PacObj extends EventDispatcher { public static const updateCycle:int = 2; //アニメーションする頻度 protected var x:int; protected var y:int; protected var numCycle:uint; protected var move_value:int; //移動量 protected var now_direction:int; //方向(現在) protected var old_direction:int; //方向(1フレーム前) protected var old_rect:Rectangle; //前回描画された矩形情報 protected var canvas:BitmapData = null; //描画対象となるキャンバス public var x_init:int; public var y_init:int; public static function makeImageSub(bmp:BitmapData, sx:int, ex:int, y:int, spacing:int):Vector.<BitmapData> { var v:Vector.<BitmapData> = new Vector.<BitmapData>(); for (var i:int = sx; i < ex; i += spacing) { v.push(clipImage(bmp, i, y)); } return v; } public static function clipImage(bmp:BitmapData, x:int, y:int):BitmapData { var b:BitmapData = new BitmapData(16, 16, true, 0x00000000); b.copyPixels(bmp, new Rectangle(x, y, 16, 16), new Point(0, 0)); return b; } public function PacObj(x:int, y:int, move_value:int = 4) { this.x = (x % Field.FRAME_W) * Field.FIELD_GRID; this.y = (y % Field.FRAME_H) * Field.FIELD_GRID; this.move_value = move_value % Field.FIELD_GRID; this.old_rect = new Rectangle(Number(this.x), Number(this.y), Number(Field.FIELD_GRID), Number(Field.FIELD_GRID)); this.x_init = this.x; this.y_init = this.y; } public function set setCanvas(canvas:BitmapData):void { this.canvas = canvas; } public function set setMoveValue(val:int):void { this.move_value = val % Field.FIELD_GRID; } public function setMovePosition(x:int, y:int):void { this.x = x; this.y = y; } public function draw():void {} public function get rectInfo():Rectangle { return this.old_rect; } /**************************************************** * updatePosition() * 返却値 : 更新OK...true, 更新NG...false * 概要 : directionで決められた方向に直進する ****************************************************/ public function updatePosition():Boolean { var x:int = this.x; var y:int = this.y; switch (this.now_direction) { case Field.UP: y -= move_value; break; case Field.DOWN: y += move_value; break; case Field.LEFT: x -= move_value; break; case Field.RIGHT: x += move_value; break; default: break; } { //無条件でオブジェクトを更新する。 //オブジェクトが動いた時だけ更新する場合は以下の2行を消す this.old_rect.x = Number(this.x); this.old_rect.y = Number(this.y); } var result:Boolean = false; /*******************目的の進行方向にトライ*******************/ if(result = Field.moving(x, y, now_direction) ) { /* 注意 x,y変数を更新する前に前回の描画箇所をoldに記録する */ this.old_rect.x = Number(this.x), this.old_rect.y = Number(this.y); old_direction = now_direction; this.x = x, this.y = y; } else if(old_direction != now_direction) //前回と今回の進行方向が同じ場合は無理 { x = this.x, y = this.y; var mv:int = move_value; var drc:int = old_direction; /*******************前回の進行方向にトライ*******************/ switch(drc) { case Field.LEFT:if (result = Field.moving(x - mv, y, drc) ) this.x -= mv; break; case Field.RIGHT:if (result = Field.moving(x + mv, y, drc) ) this.x += mv; break; case Field.UP: if (result = Field.moving(x, y - mv, drc) ) this.y -= mv; break; case Field.DOWN:if (result = Field.moving(x, y + mv, drc) ) this.y += mv; break; default: break; } if (result) { this.old_rect.x = Number(x); this.old_rect.y = Number(y); } } return result; } public function resetCycle():void { numCycle = 0; } } }
PlayField.as
package game { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Loader; import flash.display.Shape; import flash.display.Stage; import flash.display.Sprite; import flash.events.Event; import flash.events.EventDispatcher; import flash.events.IEventDispatcher; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import flash.display.GradientType; import flash.text.TextField; import flash.text.TextFormat; import flash.utils.ByteArray; /** * ... * @author Korsakov */ // TODO:プレイヤーの残機数を表示 public class PlayField extends Bitmap { private const foodSize:int = Field.FIELD_GRID / 10; private const SCORE_FOOD:int = 100; private var w:uint, h:uint; private var num_obj:Vector.<PacObj>; private var back_color:uint; private var back_buffer:BitmapData = null; private var scoreEvent:Event; private var gameclearEvent:Event; private var foodnum:int; private var img_pacman:BitmapData; private var img_fruit_01:BitmapData; private var img_fruit_02:BitmapData; private var img_fruit_03:BitmapData; private var img_door_open:BitmapData; private var img_door_close:BitmapData; //////////////////////////////////////////////////////////////////////////// // ここからパブリックメソッド //////////////////////////////////////////////////////////////////////////// public function PlayField(w:uint, h:uint, color:uint, loadImage:Loader = null) { this.w = w; this.h = h; this.back_color = color; var field:BitmapData = new BitmapData(w, h, false, color); super(field); num_obj = new Vector.<PacObj>(); scoreEvent = new GameEvent(GameEvent.UPDATE, SCORE_FOOD); gameclearEvent = new GameEvent(GameEvent.GAME_CLEAR, 0); if (loadImage != null) { makeImage(loadImage); makeBackBuffer(w, h); } } public function addPacObj(obj:PacObj):void { num_obj.push(obj); // ドアオープンイベント PacObj(num_obj[num_obj.indexOf(obj)]). addEventListener(GameEvent.DOOR, toggleDoorEvent); // 死亡したときのイベント PacObj(num_obj[num_obj.indexOf(obj)]). addEventListener(GameEvent.DEATH, deathEvent); //追加したオブジェクトの描画先を、自クラスのbitmapDataに設定 obj.setCanvas = this.bitmapData; } public function resetField(foodclear:Boolean):void { Field.door = true; //ドアを閉じる toggleDoor(); //ドア画像を更新 resetPacObjAll(); //パック達を元に戻す if (foodclear) resetFood(Field.FRAME_W, Field.FRAME_H); //餌を戻す else cleanDisplay(Field.FRAME_W, Field.FRAME_H); } public function updateAll():void { var rects:Vector.<Rectangle> = new Vector.<Rectangle>(); for each(var pc:PacObj in num_obj) { if (pc is Enemy) { Enemy(pc).updateDirection(); } } for (var i:int = 0; i < num_obj.length; i++) { var b:Boolean = num_obj[i].updatePosition(); //オブジェクトを更新 rects.push(num_obj[i].rectInfo); //trace("rect push"); if (num_obj[i] is Pacman) { eating(num_obj[i].rectInfo); } } for (i = 0; i < rects.length; i++) { /*********************************** ** 更新された箇所のみ背景色で塗りつぶす ***********************************/ if (back_buffer != null) { this.bitmapData.copyPixels(back_buffer, rects[i], new Point(rects[i].x, rects[i].y)); } } for (i = 0; i < num_obj.length; i++) { /*********************************** ** 全てのオブジェクトを再描画 ***********************************/ num_obj[i].draw(); } } public function putPacImg(num:int):void { var sh:Shape = makeColorRectShape (back_color,Field.FRAME_W,Field.FRAME_H); var mx:Matrix = makeGridMatrix(64, Field.FRAME_H); this.bitmapData.draw(sh, mx); for (var i:int = 0; i < num; i++) { this.bitmapData.copyPixels( img_pacman, new Rectangle(0, 0, Field.FIELD_GRID, Field.FIELD_GRID), new Point(64 + (i*Field.FIELD_GRID), Field.FRAME_H) ); } } //////////////////////////////////////////////////////////////////////////// // ここからプライベートメソッド //////////////////////////////////////////////////////////////////////////// private function toggleDoorEvent(e:GameEvent):void { toggleDoor(); } private function deathEvent(ev:GameEvent):void { dispatchEvent(new GameEvent(GameEvent.DEATH, 0)); } //ドアを開けたり閉めたりする private function toggleDoor():void { var x:int = Field.OUTDOOR_X; var y:int = Field.OUTDOOR_Y; var p:Point = new Point( x * Field.FIELD_GRID,y * Field.FIELD_GRID); if (Field.field[y][x] == Field.DOOR_CLOSE) { fillBackbuffer(x, y); back_buffer.copyPixels(img_door_close, img_door_close.rect,p); bitmapData.copyPixels(img_door_close, img_door_close.rect,p); }else { fillBackbuffer(x, y); back_buffer.copyPixels(img_door_open, img_door_open.rect,p); bitmapData.copyPixels(img_door_open, img_door_open.rect,p); } } private function makeImage(loadObj:Loader):void { var bmp:BitmapData = new BitmapData(loadObj.width, loadObj.height, true, 0x00000000); bmp.draw(loadObj); img_door_open = PacObj.clipImage(bmp, 16, 64); img_door_close = PacObj.clipImage(bmp, 0, 64); img_fruit_01 = PacObj.clipImage(bmp, 64, 64); img_fruit_02 = PacObj.clipImage(bmp, 80, 64); img_fruit_03 = PacObj.clipImage(bmp, 96, 64); img_pacman = PacObj.clipImage(bmp, 0, 0); } private function makeBackBuffer(w:uint, h:uint):void { back_buffer = new BitmapData(w, h, false, 0xFF000000); var s:Sprite = new Sprite(); var r:Rectangle = new Rectangle(); var color:uint = 0x90c012; var inc:Number = 0.5; //下地作成 s.graphics.beginFill(back_color); s.graphics.drawRect(0, 0, Field.FRAME_W, Field.FRAME_H); s.graphics.endFill(); for (var y:int = 0; y < Field.FIELD_H; y++) { for (var x:int = 0; x < Field.FIELD_W; x++) { var v:int = Field.field[y][x]; if (v == Field.NOTHING) //餌を置く { r.x = Number(x * Field.FIELD_GRID) + (Field.FIELD_GRID/2); r.y = Number(y * Field.FIELD_GRID) + (Field.FIELD_GRID/2); s.graphics.beginFill(0xEEFF00, 1); s.graphics.drawCircle(r.x, r.y, foodSize); s.graphics.endFill(); foodnum++; } else if (v == Field.WALL){ //壁を置く r.x = Number(x * Field.FIELD_GRID); r.y = Number(y * Field.FIELD_GRID); r.width = Field.FIELD_GRID; r.height = Field.FIELD_GRID; s.graphics.beginFill(color, 1); s.graphics.drawRoundRect (r.x, r.y, r.width, r.height, r.width / 3, r.height / 3); s.graphics.endFill(); color++; } else if (v == Field.DOOR_CLOSE) { //扉 s.graphics.beginBitmapFill(img_door_close); s.graphics.drawRect(x * Field.FIELD_GRID, y * Field.FIELD_GRID, Field.FIELD_GRID, Field.FIELD_GRID); s.graphics.endFill(); } } } back_buffer.draw(s); this.bitmapData.draw(s); } // 餌を食う private function eating(rect:Rectangle):void { var x:int = (rect.x / Field.FIELD_GRID) % Field.FIELD_W; var y:int = (rect.y / Field.FIELD_GRID) % Field.FIELD_H; if (Field.field[y][x] == Field.NOTHING) { Field.field[y][x] = Field.AFTER_EATING; fillBackbuffer(x, y); dispatchEvent(scoreEvent); //餌が0になったらゲームクリアー if (--foodnum < 1) dispatchEvent(gameclearEvent); } } private function fillBackbuffer(x:int, y:int):void { var r:int = Field.FIELD_GRID; var sh:Shape = makeColorRectShape(back_color, r, r); var mx:Matrix = makeGridMatrix(x, y); back_buffer.draw(sh, mx); this.bitmapData.draw(sh, mx); } private function makeColorRectShape(color:int, w:int, h:int):Shape { var sh:Shape = new Shape(); sh.graphics.beginFill(color); sh.graphics.drawRect(0,0, w, h); sh.graphics.endFill(); return sh; } private function makeGridMatrix(x:int, y:int):Matrix { var mx:Matrix = new Matrix(); mx.tx = x * Field.FIELD_GRID; mx.ty = y * Field.FIELD_GRID; return mx; } private function resetFood(w:int, h:int):void { foodnum = 0; //餌の数をリセット for (var y:int = 0; y < Field.FIELD_H; y++) { for (var x:int = 0; x < Field.FIELD_W; x++) { if (Field.field[y][x] == Field.AFTER_EATING) //食後なら { Field.field[y][x] = Field.NOTHING; } } } makeBackBuffer(w, h); } private function cleanDisplay(w:int, h:int):void { this.bitmapData.draw(back_buffer); } private function resetPacObjAll():void { for each(var pc:PacObj in num_obj) { pc.setMovePosition(pc.x_init, pc.y_init); if (pc is Enemy) { Enemy(pc).resetCycle(); Enemy(pc).resetState(); } } } } }