/** * Copyright tenasaku ( http://wonderfl.net/user/tenasaku ) * MIT License ( http://www.opensource.org/licenses/mit-license.php ) * Downloaded from: http://wonderfl.net/c/4BrK */ /* ---------------------------------------------------------------- May 5 2010, by tenasaku ActionScript コード 部分は MITライセンスで公開します 効果音および BGM のライセンスは別に定めます ---------------------------------------------------------------- 「あのゲーム」の試作品です 対戦相手のAIを書く能力がまだ自分にはないので, ひたすら消してもらうだけです * ゲームを開始するにはステージをクリックしてください それ以後はすべてキーボードで操作します * キーバインドは次のとおりです [←] 左移動 [→] 右移動 [↑] 回転 [↓] 早落し [TAB] 一時停止 [Q] 効果音ボリュームダウン ( 0..6 の 7段階, デフォルト 4 ) [W] 効果音ボリュームアップ [E] BGM ボリュームダウン ( 0..6 の 7段階, デフォルト 2 ) [R] BGM ボリュームアップ [T] 全サウンドのミュート ---------------------------------------------------------------- 2010年5月5日 * 公開版の準備 - サウンドリソースをサーバにアップロード - リモートサーバからリソースを読むように変更 ◆ wonderfl に 開発いちだんらくバージョンとして公開 ---------------------------------------------------------------- 2010年5月4日 * サウンド周りの最終調整. - 堆積, 連鎖, レベルアップのサウンドリソースを変更 - BGMを変更 (教訓: サウンドエフェクトが完成してからBGMをつけるべきだ) - パニック BGM は占有率 65% に回復するまで続くように変更 ---------------------------------------------------------------- 2010年5月3日 * 音量調節機能 - BGM音量は Qキー/Wキー で -/+ できる - SE音量は Eキー/Rキー で -/+ できる - サウンド全般の ミュート/ミュート解除 は T キーでトグルする * 浮遊ぷよのグラフィクスを変更 ---------------------------------------------------------------- 2010年5月2日 * BGMを少しだけ改訂 ---------------------------------------------------------------- 2010年5月1日 * 全消しのとき ファンファーレが鳴るようにした * 全消しのとき ボーナスポイント加算 ( 10,000 + (レベル-1) x 200 ) * ステージが7割埋まるか, スタート地点の二つ下のマス目が埋まったときに BGM をパニック系の音楽に変更する * 不細工ながらサウンドファイルの読み込みエラーチェックを追加 ---------------------------------------------------------------- 2010年4月30日 * ゲームオーバー時のサウンドを変更 ---------------------------------------------------------------- 2010年4月29日 * サウンド周りの基本機能の実装とサウンドリソース作り - リソース作りには GarageBand, Audacity, その他を使用 ---------------------------------------------------------------- 2010年4月28日 * 点数計算式の再検討. - 消去時の得点は (消去ぷよ数 + 消去色数 - 4) x 10 x 2^(連鎖 - 1) - 長い連鎖と多色同時消しは高レート - 下向き矢印で一段下げるごとに 1 点加算 - レベルアップ時にボーナス加算 ( 新レベル x 100点 ) ◆ wonderfl に中間バージョンその3として公開 その後 * 予告ボックスとテキストフィールドの位置を入れ換え ---------------------------------------------------------------- 2010年4月27日 * レベルアップに応じて落下を加速 * ステージの deactivate イベントに反応して一時停止 * 背景色の変更 ---------------------------------------------------------------- 2010年4月26日 * ゲームオーバー後はステージをクリックで再度プレイ可能 * 下降タイミングを再度調整 ◆ wonderfl に中間バージョンその2として公開 ---------------------------------------------------------------- 2010年4月25日 * 一時停止可能に (TABキーで一時停止/再開) * グラフィックスの微修正 * 下降タイミングの改良 * 点数計算式の変更 ---------------------------------------------------------------- 2010年4月24日 数日前から「あのゲーム」を試作中です 自分のフィールドの操作がひととおり書けたので まだタイミングの調整とかいろいろ未熟ですがひとまず公開します 対戦相手の AI をまだ書けないので, ひたすら消してください ---------------------------------------------------------------- */ package { import flash.display.*; import flash.events.*; import flash.text.*; import flash.ui.*; import flash.utils.*; public class TenaPuyoMain extends Sprite { /* ---------------------------------------------------------------- グローバル定数の宣言 ---------------------------------------------------------------- */ // ステージ全体の背景色 private const STAGE_BGCOLOR:uint = 0x009999; // ゲーム開始時のフレームレート private const FRAMERATE_AT_START:int = 24; // ゲーム開始時のフレームレート private const FRAMERATE_AT_HIGHER_LEVEL:int = 36; // ぷよ消去動作時のフレームレート private const FRAMERATE_ON_ERASE:int = 12; // 浮遊ぷよが最初にフィールドにあらわれる場所 // ここに堆積ぷよが置かれたらゲームオーバー private const START_COL:int = Math.floor((Field.NUM_COLS-1)/2); private const START_ROW:int = Field.NUM_ROWS - 1; // keysPressed グローバル変数の各ビットの名前 private const UP_KEYMASK:int = 1; private const LEFT_KEYMASK:int = 2; private const DOWN_KEYMASK:int = 4; private const RIGHT_KEYMASK:int = 8; private const SEVOL_DOWN_KEYMASK:int = 16; private const SEVOL_UP_KEYMASK:int = 32; private const BGMVOL_DOWN_KEYMASK:int = 64; private const BGMVOL_UP_KEYMASK:int = 128; private const MUTE_KEYMASK:int = 256; /* ---------------------------------------------------------------- いつのまにこんなに増えやがったのか, グローバル変数どもの宣言 ---------------------------------------------------------------- */ private var field:Field; // フィールド. ここにぷよが堆積し, ゲームが進行する private var fieldMask:Shape; // フィールド の表示窓を決めるためのマスク private var gameState:int; // いま現在せにゃならん処理は何か, 俺は忘れっぽいのでここに書いてある private var erasedObjectCount:int; // これまでに消したぷよの総数 private var maximumChainCount:int; // これまでに実現した連鎖の最大回数 private var score:int; // 読んで字のごとく, スコア private var dropPeriod:int; // 浮遊ぷよが下降するタイミング(このフレーム数ごとに下降) private var gameLevel:int; // 読んで字のごとく, レベル private var nextLevelUp:int; // 次回レベルアップするための ぷよ消去数ノルマ private var floatingPhase:int; // == 0 なら新しい浮遊ぷよペア発生, それ以外は落下・左右移動・回転 private var keysPressed:uint; // その時点で押されているコントロールボタンのフラグ列 private var floating1:FloatingPuyo; // 浮遊ぷよ1 最初下にあり, 回転のとき軸になるほう private var floating2:FloatingPuyo; // 浮遊ぷよ2 最初上にあり, 回転のとき降り回されるほう private var next1:int; // 次回の浮遊ぷよ1 private var next1Shape:Shape; // そのグラフィクス private var next2:int; // 次回の浮遊ぷよ2 private var next2Shape:Shape; // そのグラフィクス private var dropCounter:int; // 浮遊ぷよツインズの年齢というか, フレーム齢 private var pilingUpPhase:int; // 積みぷよ処理の進行を制御する変数 private var eraseCount:int; // いまこの時に何個のぷよが消去されるか, 個数を保持 private var eraseColors:uint; // いまこの時に消去されるぷよの色をビット列として保持 private var chainReactionCount:int; // いまの消去が何度めの連鎖かを保持 private var frameCount:int; // 現在何フレームめ? (結局あんまり使ってない) private var lastTime:Number; // テスト運転用に時刻を記録する変数 private var meanFps:Number; // テスト運転用に過去10フレーム分の実 fps を保持 private var savedFrameRate:Number; // ぷよ消滅表示中に, その前後のフレームレートを保持 private var monitor:TextField; // ひとまずすべての文字表示をここに放りこむ private var suspending:Boolean; // 一時停止状態のとき true になる private var gameSound:GameSound; /* ---------------------------------------------------------------- ゲームの各段階での動作の定義 ---------------------------------------------------------------- */ // スコア加算とレベルアップ private function addScore( n:int ):void { score += n; if ( erasedObjectCount >= nextLevelUp ) { gameSound.playResource(GameSound.LEVEL_UP); gameLevel ++; score += 100*gameLevel; if ( gameLevel <= 7 ) { dropPeriod = 24 - 3*(gameLevel-1); nextLevelUp += 40; } else if ( gameLevel <= 14 ) { // フレームレートを上げ, 代わりに一旦落下周期を伸ばす if ( gameLevel == 8 ) { savedFrameRate = FRAMERATE_AT_HIGHER_LEVEL; } dropPeriod = 24 - 3*(gameLevel-8); nextLevelUp += 80; } else { // これ以上は加速しない dropPeriod = 5; nextLevelUp += 100; } } } // 次回の浮遊ぷよペアを発生させ, 予告窓に表示 private function setNextPair():void { next1 = Math.floor(PuyoType.RED+Math.random()*4.9999); next2 = Math.floor(PuyoType.RED+Math.random()*4.9999); PuyoGraphics.draw(next1Shape.graphics, next1); PuyoGraphics.draw(next2Shape.graphics, next2); } // 浮遊ぷよの回転 private function turn(direction:int):void { if ( gameState != GameState.FLOATING ) return; // stage.removeEventListener(Event.ENTER_FRAME, atEveryFrame); var a11:int,a12:int,a21:int,a22:int; // 高校の数学の時間に習う (はずの) 90度回転の行列 switch (direction) { case -2: a11 = a22 = -1; a12 = a21 = -1; break; case -1: a11 = a22 = 0; a12 = 1; a21 = -1; break; case 0: a11 = a22 = 1; a12 = a21 = 0; break; case 1: a11 = a22 = 0; a12 = -1; a21 = 1; break; case 2: a11 = a22 = -1; a12 = a21 = -1; break; default: a11 = a22 = 1; a12 = a21 = 0; } var dx:int = floating2.col - floating1.col; var dy:int = floating2.row - floating1.row; var newCol:int = floating1.col + a11*dx + a12*dy; var newRow:int = floating1.row + a21*dx + a22*dy; if ( ( newRow == Field.NUM_ROWS ) ||( field.occupant(newCol,newRow) == PuyoType.SPACE ) ) { // 普通に回転 floating2.disappear(field); floating1.disappear(field); floating2.locate(newCol,newRow); floating1.appear(field); floating2.appear(field); } else { // 普通に回転する余地がない場合. 軸が逆方向へ移動. // それすらできない場合, 二つの浮遊ぷよの位置を交換 var newCol2:int = floating1.col*2 - newCol; var newRow2:int = floating1.row*2 - newRow; if ( ( newRow2 == Field.NUM_ROWS )||( field.occupant(newCol2,newRow2) == PuyoType.SPACE ) ) { newCol = floating1.col; newRow = floating1.row; floating2.disappear(field); floating1.disappear(field); floating1.locate(newCol2,newRow2); floating2.locate(newCol,newRow); floating1.appear(field); floating2.appear(field); } else { newCol = floating1.col; newRow = floating1.row; floating1.disappear(field); floating2.disappear(field); floating1.locate(floating2.col,floating2.row); floating2.locate(newCol,newRow); floating2.appear(field); floating1.appear(field); } } // stage.addEventListener(Event.ENTER_FRAME, atEveryFrame); } // ぷよ浮遊状態の処理: ユーザのコントロールを受ける private function floatingProcess():void { if ( gameState != GameState.FLOATING ) return; var swingOK:Boolean; monitor.text = "Level "+gameLevel.toString() + "\n\nScore: " + score.toString() + "\nMax Chain: " + maximumChainCount.toString() + "\nErased: " + erasedObjectCount.toString() + ( (gameSound.muted)?"\n\nSound Off": "\n\nBGM volume " + gameSound.bgmVolume.toString() + "\nEffect volume " + gameSound.seVolume.toString() ); if ( floatingPhase == 0 ) { floating1 = new FloatingPuyo(next1); floating2 = new FloatingPuyo(next2); floatingPhase = 1; dropCounter = 0; setNextPair(); floating2.locate(START_COL,START_ROW+1); floating1.locate(START_COL,START_ROW); floating2.appear(field); floating1.appear(field); keysPressed = 0; } else { dropCounter++; // ユーザの操作への反応. // 下向きキーにはこの次のぷよ下降処理のブロックで対応する. if ( keysPressed&UP_KEYMASK ) { keysPressed &= ~UP_KEYMASK; gameSound.playResource(GameSound.TURN); turn(1); return; } // 左移動の条件 if ( ( (keysPressed&LEFT_KEYMASK) != 0 ) &&( field.occupant( floating1.col-1, floating1.row ) == PuyoType.SPACE ) &&( ( floating2.row == Field.NUM_ROWS ) ||( field.occupant( floating2.col-1, floating2.row ) == PuyoType.SPACE ) ) ) { // ビット演算子 ( & や | ) の優先度が 関係演算子 == や != より 低いというのは // どういう発想の仕様なんだろうか... ぶぅっ... keysPressed &= ~LEFT_KEYMASK; gameSound.playResource(GameSound.MOVE); floating1.disappear(field); floating2.disappear(field); floating1.goLeft(field); floating2.goLeft(field); floating1.appear(field); floating2.appear(field); return; } // 右移動の条件 if ( ( (keysPressed&RIGHT_KEYMASK) != 0 ) && ( field.occupant( floating1.col+1, floating1.row ) == PuyoType.SPACE ) &&( ( floating2.row == Field.NUM_ROWS ) ||( field.occupant( floating2.col+1, floating2.row ) == PuyoType.SPACE ) ) ) { keysPressed &= ~RIGHT_KEYMASK; gameSound.playResource(GameSound.MOVE); floating1.disappear(field); floating2.disappear(field); floating1.goRight(field); floating2.goRight(field); floating1.appear(field); floating2.appear(field); return; } if ( ( (dropCounter%dropPeriod) == 0 )||( keysPressed&DOWN_KEYMASK ) ) { // 可能なら浮遊ぷよ下降 if ( ( field.occupant(floating1.col,floating1.row-1) == PuyoType.SPACE ) &&( field.occupant(floating2.col,floating2.row-1) == PuyoType.SPACE ) ) { if ( keysPressed&DOWN_KEYMASK ) addScore(1); floating1.disappear(field); floating2.disappear(field); floating1.goDown(field); floating2.goDown(field); floating1.appear(field); floating2.appear(field); } else { // 下降できなければ堆積状態へ遷移 gameSound.playResource(GameSound.PILE); floating1.pileOn(field); floating2.pileOn(field); gameState = GameState.PILING_UP; pilingUpPhase = 0; eraseCount = 0; eraseColors = 0; chainReactionCount = 0; } } } } // 堆積状態: 積みぷよに対する処理 private function pilingUpProcess():void { if ( gameState != GameState.PILING_UP ) return; var scanResult:int = 0; switch(pilingUpPhase) { case 0: // 空白を詰める field.pack(); field.drawMembers(); break; case 1: // 消えるぷよがあるか判断 scanResult = field.scanPile(); if ( scanResult == 0 ) { // 処理完了. ゲームオーバー判定へ遷移 var objectCount:int = field.numObjects(); if ( objectCount == 0 ) { // 全消し gameSound.playResource(GameSound.CLEAR_UP); addScore( 10000 + (gameLevel-1)*200 ) } else if ( ( objectCount > Field.NUM_COLS*Field.NUM_ROWS*0.7 ) ||( field.occupant(START_COL,START_ROW-2) != PuyoType.SPACE ) ) { // そろそろ危いですよ if ( gameSound.bgmIndex == GameSound.GAME_BGM ) { gameSound.bgmStop(); gameSound.bgmStart(GameSound.PANIC_BGM); } } else { if ( ( gameSound.bgmIndex == GameSound.PANIC_BGM ) && ( objectCount <= Field.NUM_COLS*Field.NUM_ROWS*0.65 ) ) { gameSound.bgmStop(); gameSound.bgmStart(GameSound.GAME_BGM); } } gameState = GameState.JUDGING; } else { // 連鎖開始 // フレームレートを下げてアニメーションを効果的にする savedFrameRate = stage.frameRate; stage.frameRate = FRAMERATE_ON_ERASE; chainReactionCount++; if ( chainReactionCount > maximumChainCount ) maximumChainCount = chainReactionCount; eraseCount = scanResult&0x7fff; erasedObjectCount += eraseCount; eraseColors = (scanResult>>16)&255; // スコアを計算 var raise:int = 1; for ( var i:int = 1 ; i < chainReactionCount ; ++i ) raise *= 2; var setBits:uint = eraseColors; var colorCount:int = 0; while ( setBits != 0 ) { colorCount += setBits&1; setBits >>= 1; } addScore( (eraseCount + colorCount - 4)*10*raise ); // 表示 monitor.text = "Chain: " + chainReactionCount.toString(); } break; case 2: gameSound.playResource(GameSound.ERASE + Math.min( (chainReactionCount-1), 7 ) ); field.effectBeforeErase(0); break; case 3: field.effectBeforeErase(0); break; case 4: field.effectBeforeErase(1); break; case 5: field.effectBeforeErase(2); break; case 6: field.effectBeforeErase(3); break; case 7: field.effectBeforeErase(4); break; case 8: field.effectBeforeErase(5); break; case 9: field.effectBeforeErase(6); break; case 10: field.drawMembers(); stage.frameRate = savedFrameRate; break; } pilingUpPhase = (pilingUpPhase+1)%11; } private function judgementProcess():void {//ゲームオーバー判定 if ( gameState != GameState.JUDGING ) return; if ( field.occupant(START_COL,START_ROW) != PuyoType.SPACE ) { gameState = GameState.GAME_OVER; // ゲームオーバー状態へ遷移 } else { gameState = GameState.FLOATING; // ぷよ浮遊状態へ遷移 floatingPhase = 0; } } // ゲームオーバー状態の動作, というより停止処理の定義 private function gameOverProcess():void { if ( gameState != GameState.GAME_OVER ) return; gameSound.bgmStop(); gameSound.playResource(GameSound.GAME_OVER); monitor.text = "Level " + gameLevel.toString() + "\n\nScore: " + score.toString() + "\nMax Chain: " + maximumChainCount.toString() + "\nErased: " + erasedObjectCount.toString() + "\n\n** GAME OVER **" + "\n\nClick stage\nto play again"; stage.removeEventListener(Event.DEACTIVATE, onDeactivate) stage.removeEventListener(Event.ACTIVATE, onActivate) stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyPress); stage.removeEventListener(KeyboardEvent.KEY_UP, onKeyRelease); stage.removeEventListener(Event.ENTER_FRAME, atEveryFrame); stage.addEventListener(MouseEvent.CLICK, onClick); } // 一時停止 private function pauseOrResume(why:int):void { monitor.text = "** SUSPENDING **"; if ( why != 0 ) { // フォーカスが外れた monitor.appendText("\n\nResume: Click"); } else { // TABキーが押された monitor.appendText("\n\nResume: TAB key"); } keysPressed = 0; // キー入力をキャンセル suspending = !suspending; if ( suspending ) { // 一時停止 stage.removeEventListener(Event.ENTER_FRAME, atEveryFrame); gameSound.bgmStop(); this.removeChild(field); this.graphics.lineStyle(NaN); this.graphics.beginFill(PuyoGraphics.FIELD_BGCOLOR); this.graphics.drawRect(field.x,field.y,field.width,field.height); PuyoGraphics.draw(next1Shape.graphics, PuyoType.SPACE); PuyoGraphics.draw(next2Shape.graphics, PuyoType.SPACE); } else { // 一時停止解除 monitor.text = "Resumed..."; gameSound.bgmResume(); this.graphics.lineStyle(NaN); this.graphics.beginFill(STAGE_BGCOLOR); this.graphics.drawRect(field.x,field.y,field.width,field.height); PuyoGraphics.draw(next1Shape.graphics, next1); PuyoGraphics.draw(next2Shape.graphics, next2); this.addChild(field); stage.addEventListener(Event.ENTER_FRAME, atEveryFrame); } } private function checkSoundControl():void { if ( keysPressed&MUTE_KEYMASK ) { gameSound.toggleMute(); } if ( keysPressed&SEVOL_DOWN_KEYMASK ) { // 音量は下げるほう優先 keysPressed &= ~(SEVOL_DOWN_KEYMASK|SEVOL_UP_KEYMASK); gameSound.seVolumeDown(); } if ( keysPressed&SEVOL_UP_KEYMASK ) { keysPressed &= ~(SEVOL_DOWN_KEYMASK|SEVOL_UP_KEYMASK); gameSound.seVolumeUp(); } if ( keysPressed&BGMVOL_DOWN_KEYMASK ) { // 音量は下げるほう優先 keysPressed &= ~(BGMVOL_DOWN_KEYMASK|BGMVOL_UP_KEYMASK); gameSound.bgmVolumeDown(); } if ( keysPressed&BGMVOL_UP_KEYMASK ) { keysPressed &= ~(BGMVOL_DOWN_KEYMASK|BGMVOL_UP_KEYMASK); gameSound.bgmVolumeUp(); } } /* ---------------------------------------------------------------- イベント処理ルーチン群 ---------------------------------------------------------------- */ // 蛇足かもしれんがキーコードの定数定義 private static const A_KEYCODE:int = 65; private static const B_KEYCODE:int = 66; private static const C_KEYCODE:int = 67; private static const D_KEYCODE:int = 68; private static const E_KEYCODE:int = 69; private static const F_KEYCODE:int = 70; private static const G_KEYCODE:int = 71; private static const H_KEYCODE:int = 72; private static const I_KEYCODE:int = 73; private static const J_KEYCODE:int = 74; private static const K_KEYCODE:int = 75; private static const L_KEYCODE:int = 76; private static const M_KEYCODE:int = 77; private static const N_KEYCODE:int = 78; private static const O_KEYCODE:int = 79; private static const P_KEYCODE:int = 80; private static const Q_KEYCODE:int = 81; private static const R_KEYCODE:int = 82; private static const S_KEYCODE:int = 83; private static const T_KEYCODE:int = 84; private static const U_KEYCODE:int = 85; private static const V_KEYCODE:int = 86; private static const W_KEYCODE:int = 87; private static const X_KEYCODE:int = 88; private static const Y_KEYCODE:int = 89; private static const Z_KEYCODE:int = 90; // キーボードイベントへの反応: キーが押された private function onKeyPress(e:KeyboardEvent):void { switch(e.keyCode) { case Keyboard.LEFT: keysPressed |= LEFT_KEYMASK; break; case Keyboard.UP: keysPressed |= UP_KEYMASK; break; case Keyboard.RIGHT: keysPressed |= RIGHT_KEYMASK; break; case Keyboard.DOWN: keysPressed |= DOWN_KEYMASK; break; case Q_KEYCODE: keysPressed |= SEVOL_DOWN_KEYMASK; break; case W_KEYCODE: keysPressed |= SEVOL_UP_KEYMASK; break; case E_KEYCODE: keysPressed |= BGMVOL_DOWN_KEYMASK; break; case R_KEYCODE: keysPressed |= BGMVOL_UP_KEYMASK; break; case T_KEYCODE: keysPressed |= MUTE_KEYMASK; break; } checkSoundControl(); } // キーボードイベントへの反応: キーが離された private function onKeyRelease(e:KeyboardEvent):void { switch(e.keyCode) { case Keyboard.TAB: pauseOrResume(0); break; case Keyboard.LEFT: keysPressed &= ~LEFT_KEYMASK; break; case Keyboard.UP: keysPressed &= ~UP_KEYMASK; break; case Keyboard.RIGHT: keysPressed &= ~RIGHT_KEYMASK; break; case Keyboard.DOWN: keysPressed &= ~DOWN_KEYMASK; break; case Q_KEYCODE: keysPressed &= ~SEVOL_DOWN_KEYMASK; break; case W_KEYCODE: keysPressed &= ~SEVOL_UP_KEYMASK; break; case E_KEYCODE: keysPressed &= ~BGMVOL_DOWN_KEYMASK; break; case R_KEYCODE: keysPressed &= ~BGMVOL_UP_KEYMASK; break; case T_KEYCODE: keysPressed &= ~MUTE_KEYMASK; break; } } // deactivate イベントリスナ. 一時停止する private function onDeactivate(e:Event):void { suspending = false; pauseOrResume(1); } // activate イベントリスナ. 一時停止を解除する private function onActivate(e:Event):void { suspending = true; pauseOrResume(0); } // click マウスイベントリスナ. ゲーム開始処理 private function onClick(e:MouseEvent):void { if ( ! gameSound.loadComplete ) { if ( gameSound.loadError ) { monitor.text = "Sorry.\n\nError occured\n while loading\nsound files."; } else { monitor.text = "Sorry.\n\nSound files are\nstill being\nloaded..."; } return; } stage.removeEventListener(MouseEvent.CLICK, onClick); monitor.text = ""; keysPressed = 0; stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyPress); stage.addEventListener(KeyboardEvent.KEY_UP, onKeyRelease); stage.addEventListener(Event.DEACTIVATE, onDeactivate) stage.addEventListener(Event.ACTIVATE, onActivate) suspending = false; stage.frameRate = FRAMERATE_AT_START; frameCount = 0; floatingPhase = 0; erasedObjectCount = 0; maximumChainCount = 0; gameLevel = 1; nextLevelUp = 10; dropPeriod = 24; score = 0; gameState = GameState.FLOATING; field.clearMembers(); field.drawBackground(); field.drawMembers(); // gameSound.playResource(GameSound.GAME_START); gameSound.bgmStart(GameSound.GAME_BGM); lastTime = 0; stage.addEventListener(Event.ENTER_FRAME, atEveryFrame); } // enterFrame イベントリスナ. ここが主ループに相当 private function atEveryFrame(e:Event):void { switch(gameState) { case GameState.NO_PLAY: /* することがないなぁ... */ break; case GameState.FLOATING: floatingProcess(); break; case GameState.PILING_UP: pilingUpProcess(); break; case GameState.JUDGING: judgementProcess(); break; case GameState.GAME_OVER: gameOverProcess(); break; default://未定義の状態? なんぢゃそりゃ... } frameCount ++; if ( (frameCount%24) == 0 ) { // 24フレームごとに時刻を計って fps を計算 var deltaTime:Number = getTimer()-lastTime; meanFps = 2.4e+4/deltaTime; lastTime += deltaTime; } } /* ---------------------------------------------------------------- 初期化ルーチン群 ---------------------------------------------------------------- */ private function initializeNextBox():void { var nextBoxX:Number = 300; var nextBoxY:Number = field.y; this.graphics.lineStyle(1,PuyoGraphics.FIELD_LINECOLOR); this.graphics.beginFill(PuyoGraphics.FIELD_BGCOLOR); this.graphics.drawRect(nextBoxX-2,nextBoxY-2,76,76); this.graphics.endFill(); this.graphics.drawRect(nextBoxX,nextBoxY,72,72); next1Shape = new Shape(); next2Shape = new Shape(); next1Shape.x = nextBoxX + 36; next1Shape.y = nextBoxY + 36 + Field.GRID_SIZE/2; next2Shape.x = next1Shape.x; next2Shape.y = next1Shape.y - Field.GRID_SIZE; next1Shape.filters = PuyoGraphics.PUYOFILTERLIST; next2Shape.filters = PuyoGraphics.PUYOFILTERLIST; this.addChild(next2Shape); this.addChild(next1Shape); } private function initializeMonitorTextField():void { var fmt:TextFormat = new TextFormat(); fmt.font = "Courier"; fmt.size = 18; fmt.align = TextFormatAlign.CENTER; fmt.leftMargin = 2; fmt.rightMargin = 2; monitor.defaultTextFormat = fmt; monitor.x = field.x + field.width + 20; monitor.y = field.y + 72 + 40; monitor.width = (stage.stageWidth - monitor.x - 20); monitor.height = 200; monitor.textColor = 0xffffff; this.graphics.lineStyle(NaN); this.graphics.beginFill(0x000000); this.graphics.drawRect(monitor.x - 10, monitor.y - 10, monitor.width + 20, monitor.height + 20 ); this.graphics.endFill(); } private function initializeStageBackground():void { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; this.graphics.clear(); this.graphics.beginFill(STAGE_BGCOLOR); this.graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight); this.graphics.endFill(); } private function setupField():void { field.x = 32; field.y = (stage.stageHeight-Field.GRID_SIZE*Field.NUM_ROWS)/2; field.drawBackground(); field.drawMembers(); } private function setupFieldMask():void { if ( fieldMask == null ) { fieldMask = new Shape(); } fieldMask.graphics.clear(); fieldMask.graphics.beginFill(0xffffff); fieldMask.graphics.drawRect( -1, -1, Field.GRID_SIZE*Field.NUM_COLS+2, Field.GRID_SIZE*Field.NUM_ROWS+2 ); fieldMask.x = field.x; fieldMask.y = field.y; field.mask = fieldMask; } private function initializeSounds():void { gameSound = new GameSound(); gameSound.loadAllSounds(); } // プログラム全体の初期化 private function initialize(e:Event):void { this.removeEventListener(Event.ADDED_TO_STAGE, initialize); initializeStageBackground(); field = new Field(); setupField(); setupFieldMask(); this.addChild(field); initializeNextBox(); setNextPair(); monitor = new TextField(); initializeMonitorTextField(); this.addChild(monitor); initializeSounds(); monitor.text = "To start game,\nclick stage.\n\nUse arrow keys\nfor control."; stage.addEventListener(MouseEvent.CLICK, onClick); } // コンストラクタは initialize を呼び出すだけ public function TenaPuyoMain():void { if ( stage != null ) { initialize(null); } else { this.addEventListener(Event.ADDED_TO_STAGE, initialize); } } } // end of class TenaPuyoMain } // end of package /* ---------------------------------------------------------------- */ import flash.display.*; import flash.filters.*; import flash.utils.*; /* ---------------------------------------------------------------- フィールドの保持すべきデータと受け持つべき処理を定義 ---------------------------------------------------------------- */ class Field extends Sprite { public static const GRID_SIZE:int = 32; public static const NUM_ROWS:int = 12; public static const NUM_COLS:int = 6; private var memberList:Array; // 生き残っている積みぷよのリスト private var eraseList:Array; // これから消去されるべき積みぷよのリスト internal var shapeList:Array; // フィールドの各点ごとに, 表示用グラフィック・オブジェクト // 空白以外のオブジェクトの個数を返す public function numObjects():int { var count:int = 0; for ( var i:int = 0 ; i < memberList.length ; ++i ) { if ( memberList[i] != PuyoType.SPACE ) count++; } return count; } // 積みぷよの消滅にともなってできた空白に // 上にあるぷよを詰めなおす配列操作 public function pack():void { var col:int,row:int; for ( col = 0 ; col < NUM_COLS ; ++col ) { var _list:Array = new Array(NUM_ROWS); for ( row = 0 ; row < NUM_ROWS ; ++row ) { _list[row] = memberList[arrayIndex(col,row)]; } row = 0; while ( row < _list.length ) { if ( _list[row] == PuyoType.SPACE ) { _list.splice(row,1); } else { row++; } } while ( _list.length < NUM_ROWS ) { _list.push(PuyoType.SPACE); } for ( row = 0 ; row < NUM_ROWS ; ++row ) { memberList[arrayIndex(col,row)] = _list[row]; eraseList[arrayIndex(col,row)] = PuyoType.SPACE; } } } // 消去予定リストに載った位置のグラフィックに対して // 消え去る挙動を描画 public function effectBeforeErase(phase:int):void { for ( var i:int = 0 ; i < NUM_COLS*NUM_ROWS ; ++i ) { if ( eraseList[i] != PuyoType.SPACE ) { PuyoGraphics.drawBeforeErase(shapeList[i].graphics,eraseList[i],phase); } } } // 積みぷよを走査して // サイズ4以上のクラスターのメンバーを積みぷよリストから // 消去予定リストに移す // 積みぷよリストの該当位置は空白に置き換える // 返り値(uint型)の下位16ビットは 消えるぷよの個数 // 上位16ビットは, 消えるぷよの種類をあらわすビット列 // BIT 16: 赤ぷよ // BIT 17: 青ぷよ // BIT 18: 緑ぷよ // BIT 19: 黄ぷよ // BIT 20: 紫ぷよ // BIT 21: おじゃまぷよ // BIT 22..31 は 未定義でゼロ // ただし, いまのところ おじゃまぷよ の消滅には未対応... public function scanPile():uint { const CLUSTER_BIG_ENOUGH:int = 4; var pointStack:Array = []; var workList:Array = new Array(NUM_COLS*NUM_ROWS); var previousList:Array = new Array(NUM_COLS*NUM_ROWS); // eraseList を消去し, memberListのコピーをふたつ作る var i:int; for ( i = 0 ; i < NUM_COLS*NUM_ROWS ; ++i ) { eraseList[i] = PuyoType.SPACE; workList[i] = memberList[i]; previousList[i] = memberList[i]; } var col:int,row:int; for ( col = 0 ; col < NUM_COLS ; ++col ) for ( row = 0 ; row < NUM_ROWS ; ++row ) { // 位置 (col,row) のメンバーの属するクラスターを検出する var count:int = 0; var clusterValue:int = workList[arrayIndex(col,row)]; if ( clusterValue <= PuyoType.SPACE ) continue; if ( clusterValue >= PuyoType.NEUTRAL ) continue; var p0:Object = new Object(); p0.h = col; p0.v = row; pointStack.push(p0); while ( pointStack.length > 0 ) { var p:Object = pointStack.pop(); if ( workList[arrayIndex(p.h,p.v)] != clusterValue ) continue; workList[arrayIndex(p.h,p.v)] = PuyoType.SPACE; count++; var up:int = 1; while ( ( p.v+up < NUM_ROWS ) &&( workList[arrayIndex(p.h,p.v+up)] == clusterValue ) ) { workList[arrayIndex(p.h,p.v+up)] = PuyoType.SPACE; count++; up++; } up--; var down:int = 1; while ( ( p.v-down >= 0 ) &&( workList[arrayIndex(p.h,p.v-down)] == clusterValue ) ) { workList[arrayIndex(p.h,p.v-down)] = PuyoType.SPACE; count++; down++; } down--; for each ( var n:int in [-1,1]) { if ( ( p.h+n < 0 )||( p.h+n >= NUM_COLS ) ) continue; var inCluster:Boolean = false; for ( var k:int = p.v-down ; k <= p.v+up ; ++k ) { if ( ( !inCluster ) &&( workList[arrayIndex(p.h+n,k)] == clusterValue ) ) { var q:Object = new Object(); q.h = p.h+n; q.v = k; pointStack.push(q); } inCluster = ( workList[arrayIndex(p.h+n,k)] == clusterValue ); } } } // クラスターのサイズが大きければ次のターンに備えて作業領域のコピーを作る // クラスターのサイズが小さければ変更前の状態に戻す if ( count >= CLUSTER_BIG_ENOUGH ) { for ( i = 0 ; i < NUM_COLS*NUM_ROWS ; ++i ) { previousList[i] = workList[i]; } } else { for ( i = 0 ; i < NUM_COLS*NUM_ROWS ; ++i ) { workList[i] = previousList[i]; } } } // 処理結果を memberList と eraseList に書き込む // ついでにいくつ消えるか数えて値を返す count = 0; var colorBits:uint = 0; for ( i = 0 ; i < NUM_COLS*NUM_ROWS ; ++i ) { if ( workList[i] == memberList[i] ) { eraseList[i] = PuyoType.SPACE; memberList[i] = workList[i]; } else { eraseList[i] = memberList[i]; memberList[i] = workList[i]; count++; switch (eraseList[i]) { case PuyoType.RED: colorBits |= 1; break; case PuyoType.BLUE: colorBits |= 2; break; case PuyoType.GREEN: colorBits |= 4; break; case PuyoType.YELLOW: colorBits |= 8; break; case PuyoType.PURPLE: colorBits |= 16; break; case PuyoType.NEUTRAL: colorBits |= 32; break; } } } return (colorBits<<16)|count; } // フィールド位置にあるオブジェクトの種類を返す関数 public function occupant(col:int,row:int):int { if ( ( col < 0 ) || ( col >= NUM_COLS ) ) return PuyoType.WALL; if ( row >= NUM_ROWS ) return PuyoType.FLOOR; if ( row < 0 ) return PuyoType.CEILING; return memberList[arrayIndex(col,row)]; } // フィールド位置を指定して配列へメンバーを代入 public function occupy(col:int,row:int,puyoType:int):void { if ( ( col < 0 ) || ( col >= NUM_COLS ) ) return; if ( row >= NUM_ROWS ) return; if ( row < 0 ) return; var index:int = arrayIndex(col,row); memberList[index] = puyoType; } // 背景の再描画 public function drawBackground():void { this.graphics.clear(); this.graphics.beginFill(PuyoGraphics.FIELD_BGCOLOR); this.graphics.drawRect(0,0,GRID_SIZE*NUM_COLS+1,GRID_SIZE*NUM_ROWS+1); this.graphics.endFill(); this.graphics.lineStyle(1,PuyoGraphics.FIELD_LINECOLOR); for ( var n:int = 0 ; n <= NUM_COLS ; ++n ) { this.graphics.moveTo(n*GRID_SIZE,0); this.graphics.lineTo(n*GRID_SIZE, NUM_ROWS*GRID_SIZE); } for ( n = 0 ; n <= NUM_ROWS ; ++n ) { this.graphics.moveTo(0,n*GRID_SIZE); this.graphics.lineTo(NUM_COLS*GRID_SIZE,n*GRID_SIZE); } } // 積みぷよアイコンの再描画 public function drawMembers():void { for ( var index:int = 0 ; index < memberList.length ; ++index ) { PuyoGraphics.draw(shapeList[index].graphics, memberList[index]); } } // 列と行の二次元インデックスで一次元配列にアクセスするための // インデックスの換算 private function arrayIndex(_x:int,_y:int):int { return _x*NUM_ROWS+_y; } public function clearMembers():void { for ( var index:int = NUM_ROWS*NUM_COLS-1 ; index >= 0 ; --index ) { memberList[index] = 0; eraseList[index] = 0; } } // Fieldインスタンスのコンストラクタ public function Field() { memberList = new Array(NUM_ROWS*NUM_COLS); eraseList = new Array(NUM_ROWS*NUM_COLS); shapeList = new Array(NUM_ROWS*NUM_COLS); this.clearMembers(); for ( var index:int = NUM_ROWS*NUM_COLS-1 ; index >= 0 ; --index ) { shapeList[index] = new Shape(); shapeList[index].x = GRID_SIZE*(0.5+Math.floor(index/NUM_ROWS)); shapeList[index].y = GRID_SIZE*(-0.5+NUM_ROWS-(index%NUM_ROWS)); shapeList[index].filters = PuyoGraphics.PUYOFILTERLIST; this.addChild(shapeList[index]); } } } // Field クラスの定義おわり /* ---------------------------------------------------------------- 浮遊ぷよのクラス ---------------------------------------------------------------- */ class FloatingPuyo { private var _col:int; private var _row:int; private var _puyoType:int; public function get col():int { return _col; } public function get row():int { return _row; } public function get puyoType():int { return _puyoType; } public function locate(h:int,v:int):void { _col = h; _row = v; if ( _col < 0 ) _col = 0; if ( _col >= Field.NUM_COLS ) _col = Field.NUM_COLS-1; if ( _row < 0 ) _row = 0; if ( _row >= Field.NUM_ROWS ) _row = Field.NUM_ROWS; // 天井のひとつ上まで許す } public function goDown(f:Field):void { // 下降する _row--; } public function goLeft(f:Field):void { _col--; } public function goRight(f:Field):void { _col++; } public function pileOn(f:Field):void { // 自分自身を積みぷよとして field に固定する f.occupy(_col,_row,_puyoType); } public function appear(f:Field):void { // フィールドの指定された位置にあらわれる if ( ( _col < 0 )||( _col >= Field.NUM_COLS ) ) return; if ( ( _row < 0 )||( _row >= Field.NUM_ROWS ) ) return; var index:int = _col*Field.NUM_ROWS + _row; PuyoGraphics.drawFloating(f.shapeList[index].graphics, _puyoType); } public function disappear(f:Field):void { // 消える if ( ( _col < 0 )||( _col >= Field.NUM_COLS ) ) return; if ( ( _row < 0 )||( _row >= Field.NUM_ROWS ) ) return; var index:int = _col*Field.NUM_ROWS + _row; PuyoGraphics.draw(f.shapeList[index].graphics, f.occupant(_col,_row)); } public function FloatingPuyo(puyoType:int) { _puyoType = puyoType; _col = Math.floor(Field.NUM_COLS/2); _row = Field.NUM_ROWS-1; } } // FloatingPuyo クラスの定義おわり /* ---------------------------------------------------------------- ぷよやその他オブジェクトの種類を示す定数値に名前でアクセスするためのクラス ---------------------------------------------------------------- */ class PuyoType { public static const WALL:int = -3; // 左右の壁 public static const CEILING:int = -2; // 天井 (実際には天井はない) public static const FLOOR:int = -1; // 床 public static const SPACE:int = 0; // 空所 public static const RED:int = 1; // 赤 public static const BLUE:int = 2; // 青 public static const GREEN:int = 3; // 緑 public static const YELLOW:int = 4; // 黄色 public static const PURPLE:int = 5; // 紫 public static const NEUTRAL:int = 6; // おじゃまぷよ public static const ICE:int = 7; // 固ぷよ public static const GLOW:int = 16; // 光っているフラグ public static const BLINK:int = 32; // 目をつぶっているフラグ public static const MAD:int = 64; // 目を大きく開いているフラグ } /* ---------------------------------------------------------------- ぷよの絵柄を描画するための手続を(どこからでも呼べるよう)別クラスとして用意 ---------------------------------------------------------------- */ class PuyoGraphics { public static const FIELD_BGCOLOR:uint = 0xcccccc; public static const FIELD_LINECOLOR:uint = 0x9999cc; public static const FACECOLOR_RED:uint = 0xff0000; public static const FACECOLOR_BLUE:uint = 0x0000ff; public static const FACECOLOR_GREEN:uint = 0x00cc00; public static const FACECOLOR_YELLOW:uint = 0xffff00; public static const FACECOLOR_PURPLE:uint = 0x800099; public static const FACECOLOR_NEUTRAL:uint = 0xcccccc; public static const FACECOLOR_ICE:uint = 0x666666; public static const PUYOFILTERLIST:Array = [ new DropShadowFilter() ]; public static const DIAMETER:Number = 30; public static function draw(g:Graphics, puyoType:int):void { //....実際の描画手続き g.clear(); if ( puyoType <= PuyoType.SPACE ) return; // 壁や床 if ( (puyoType&15) > PuyoType.ICE ) return; // 存在しない種類のぷよ var cf:uint; // 塗り色 // 現時点では変形のフラグ (16,32,64)には対応しない switch (puyoType&15) { case PuyoType.RED: cf = FACECOLOR_RED; break; case PuyoType.BLUE: cf = FACECOLOR_BLUE; break; case PuyoType.GREEN: cf = FACECOLOR_GREEN; break; case PuyoType.YELLOW: cf = FACECOLOR_YELLOW; break; case PuyoType.PURPLE: cf = FACECOLOR_PURPLE; break; case PuyoType.NEUTRAL: cf = FACECOLOR_NEUTRAL; break; case PuyoType.ICE: cf = FACECOLOR_ICE; break; } g.beginFill(cf); g.drawCircle(0,0,DIAMETER/2); g.endFill(); g.lineStyle(0,0x000000); g.beginFill(0xffffff); g.drawCircle(-DIAMETER/4,0,DIAMETER/6); g.endFill(); g.beginFill(0xffffff); g.drawCircle( DIAMETER/4,0,DIAMETER/6); g.endFill(); g.beginFill(0x000000); g.drawCircle(-DIAMETER/5.2,0,DIAMETER/20); g.endFill(); g.beginFill(0x000000); g.drawCircle( DIAMETER/5.2,0,DIAMETER/20); g.endFill(); } public static function drawFloating(g:Graphics, puyoType:int):void { //浮遊ぷよのグラフィクスを描画 g.clear(); if ( puyoType <= PuyoType.SPACE ) return; // 壁や床 if ( (puyoType&15) > PuyoType.ICE ) return; // 存在しない種類のぷよ var cf:uint; // 塗り色 switch (puyoType&15) { case PuyoType.RED: cf = FACECOLOR_RED; break; case PuyoType.BLUE: cf = FACECOLOR_BLUE; break; case PuyoType.GREEN: cf = FACECOLOR_GREEN; break; case PuyoType.YELLOW: cf = FACECOLOR_YELLOW; break; case PuyoType.PURPLE: cf = FACECOLOR_PURPLE; break; case PuyoType.NEUTRAL: cf = FACECOLOR_NEUTRAL; break; case PuyoType.ICE: cf = FACECOLOR_ICE; break; } g.beginFill(cf); g.drawCircle(0,0,DIAMETER/2); g.endFill(); } public static function drawBeforeErase(g:Graphics, puyoType:int,phase:int):void { //消え去るぷよのグラフィクスを描画 g.clear(); if ( puyoType <= PuyoType.SPACE ) return; // 壁や床 if ( (puyoType&15) > PuyoType.ICE ) return; // 存在しない種類のぷよ var cf:uint; // 塗り色 // 現時点では変形のフラグ (16,32,64)には対応しない switch (puyoType&15) { case PuyoType.RED: cf = FACECOLOR_RED; break; case PuyoType.BLUE: cf = FACECOLOR_BLUE; break; case PuyoType.GREEN: cf = FACECOLOR_GREEN; break; case PuyoType.YELLOW: cf = FACECOLOR_YELLOW; break; case PuyoType.PURPLE: cf = FACECOLOR_PURPLE; break; case PuyoType.NEUTRAL: cf = FACECOLOR_NEUTRAL; break; case PuyoType.ICE: cf = FACECOLOR_ICE; break; } g.lineStyle(3,0xffcccc); g.beginFill(cf); switch(phase) { case 0: g.drawCircle(0,0,DIAMETER/2); break; case 1: g.drawCircle(0,0,DIAMETER/2*0.8); break; case 2: g.drawCircle(0,0,DIAMETER/2*0.8*0.8); break; case 3: g.drawCircle(0,0,DIAMETER/2*0.8*0.8*0.8); break; case 4: g.drawCircle(0,0,DIAMETER/2*0.8*0.8*0.8*0.8); break; case 5: g.drawCircle(0,0,DIAMETER/2*0.8*0.8*0.8*0.8*0.8); break; case 6: g.drawCircle(0,0,DIAMETER/2*0.8*0.8*0.8*0.8*0.8*0.8); break; default: g.drawCircle(0,0,1); } g.endFill(); if ( phase <= 1 ) { g.lineStyle(0,0x000000); g.beginFill(0xffffff); g.drawCircle(-DIAMETER/4,0,DIAMETER/4.5); g.endFill(); g.beginFill(0xffffff); g.drawCircle( DIAMETER/4,0,DIAMETER/4.5); g.endFill(); g.beginFill(0x000000); g.drawCircle(-DIAMETER/4,0,DIAMETER/9); g.endFill(); g.beginFill(0x000000); g.drawCircle( DIAMETER/4,0,DIAMETER/9); g.endFill(); } } } /* ---------------------------------------------------------------- サウンドまわりのことをここで定義... ---------------------------------------------------------------- */ import flash.events.*; import flash.net.*; import flash.media.*; import flash.text.*; class GameSound { public static const GAME_BGM:int = 0; public static const PANIC_BGM:int = 1; public static const GAME_OVER:int = 2; public static const LEVEL_UP:int = 3; public static const PILE:int = 4; public static const TURN:int = 5; public static const MOVE:int = 6; public static const CLEAR_UP:int = 7; public static const ERASE:int = 8; private const LOADER_BIT_MASK:uint = 0x1ffff; public static const SOUND_URL:Array = [ "http://tenasaku.com/flash/tenaPuyo/sound/mainLoop.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/panic.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/gameOver.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/levelUp.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/pileOn.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/turn.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/move.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/clearUp.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/chain-1.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/chain-2.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/chain-3.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/chain-4.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/chain-5.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/chain-6.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/chain-7.mp3", "http://tenasaku.com/flash/tenaPuyo/sound/chain-8.mp3" ]; private var soundResource:Array; private var channel:Array; public var bgmIndex:int; public var loadError:Boolean; private var muting:Boolean; private var numLoadedResource:int; private var bgmTransform:SoundTransform; private var seTransform:SoundTransform; public function loadAllSounds():void { var context:SoundLoaderContext = new SoundLoaderContext(1000, true); numLoadedResource = 0; loadError = false; for ( var i:int = 0 ; i < SOUND_URL.length ; ++i ) { soundResource[i] = new Sound(); soundResource[i].addEventListener(IOErrorEvent.IO_ERROR, onIOError); soundResource[i].addEventListener(Event.COMPLETE, onComplete); soundResource[i].load(new URLRequest(SOUND_URL[i]), context); } } public function get loadComplete():Boolean { return ( ( numLoadedResource == SOUND_URL.length )&&( !loadError ) ); } public function onComplete(e:Event):void { numLoadedResource++; } public function onIOError(e:IOErrorEvent):void { loadError = true; } public function stopEverySound():void { SoundMixer.stopAll(); } public function bgmStart(_index:int):void { bgmIndex = (_index<=0)?GAME_BGM:PANIC_BGM; channel[bgmIndex] = soundResource[bgmIndex].play(0,1000,bgmTransform); } public function bgmStop():void { channel[bgmIndex].stop(); } public function bgmResume():void { bgmStart(bgmIndex); } public function toggleMute():void { muting = !muting; if ( muting ) { stopEverySound(); } else { bgmStart(bgmIndex); } } public function playResource(resourceNumber:int):void { if ( muting ) return; if ( (resourceNumber < 0)||(resourceNumber>=soundResource.length) ) return; channel[resourceNumber] = soundResource[resourceNumber].play(0,1,seTransform); } public function get seVolume():int { return Math.floor(seTransform.volume*6+0.5); } public function get bgmVolume():int { return Math.floor(bgmTransform.volume*6+0.5); } public function get muted():Boolean { return muting; } public function seVolumeUp():void { muting = false; seTransform.volume = Math.min(seTransform.volume+1/6, 1.0); } public function seVolumeDown():void { muting = false; seTransform.volume = Math.max(seTransform.volume-1/6, 0.0); } public function bgmVolumeUp():void { muting = false; bgmTransform.volume = Math.min(bgmTransform.volume+1/6, 1.0); channel[bgmIndex].soundTransform = bgmTransform; } public function bgmVolumeDown():void { muting = false; bgmTransform.volume = Math.max(bgmTransform.volume-1/6, 0.0); channel[bgmIndex].soundTransform = bgmTransform; } public function GameSound() { bgmIndex = GAME_BGM; soundResource = new Array(); channel = new Array(); bgmTransform = new SoundTransform(); seTransform = new SoundTransform(); bgmTransform.volume = 2/6; seTransform.volume = 4/6; muting = false; } } /* ---------------------------------------------------------------- ゲームの進行状況をあらわす整数値に名前でアクセスできるようにするための定義 ---------------------------------------------------------------- */ class GameState { internal static const NO_PLAY:int = 0; internal static const FLOATING:int = 1; internal static const PILING_UP:int = 2; internal static const JUDGING:int = 3; internal static const GAME_OVER:int = 4; }