最終課題:ミニゲーム「さめがめ」
ActionScript入門Wikiのゲーム制作「さめがめの作り方」をforkしたものになります。
3色のブロックがあり、プレイヤーは、画面上のブロックを一つずつ選んで消していき、本来は得点を競うゲームです。
ブロックの消え方には、以下のようなルールがあります。
・プレイヤーが選んだブロックに接する同じ色のブロックは、同時に消えます。同じ色のブロックが接していない、孤立したブロックは消すことができません。
・本来は同時に消えるブロックが多ければ多いほど、得点が大きくなります。
・消えたブロックの上にあったブロックは、下に落ちてきます。
・縦一列のブロックが消えると、その右隣の列のブロックは、左に移動します。
・消せるブロックがなくなった時点でゲーム終了となります。
/**
* Copyright rxheart257 ( http://wonderfl.net/user/rxheart257 )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/iTPe
*/
package
{
import flash.display.Sprite;
public class Main extends Sprite
{
public function Main()
{
// 背景を真っ黒に
graphics.beginFill(0x0);
graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
graphics.endFill();
var panel:Panel = new Panel();
addChild(panel);
}
}
}
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.filters.BevelFilter;
import flash.geom.Point;
import org.libspark.betweenas3.BetweenAS3;
class Panel extends Sprite // ブロックはパネルに貼る
{
public static const WIDTH:int = 15; // ブロックの数 - 横
public static const HEIGHT:int = 15; // ブロックの数 - 縦
private var blocks:Array;
public function Panel()
{
createBlocks();
}
private function createBlocks():void
{
blocks = [];
for (var y:int = 0; y < HEIGHT; y++)
{
blocks[y] = [];
for (var x:int = 0; x < WIDTH; x++)
{
var block:Block = new Block();
block.x = x * Block.WIDTH;
block.y = y * Block.HEIGHT;
addChild(block);
blocks[y][x] = block;
}
}
}
// 二次元配列から引数のブロック位置を検索。
// 見つかったらPoint(x, y)で返却。
// 見つからなかったらnullを返す
public function searchBlock(block:Block):Point
{
for (var y:int = 0; y < HEIGHT; y++)
{
for (var x:int = 0; x < WIDTH; x++)
{
if (blocks[y][x] == block)
{
return new Point(x, y); // ブロックが見つかったので、Pointで返却。
}
}
}
return null; // 見つからなかったのでnullを返す。
}
// ブロックを消す処理
// block[ty][tx].colorが引数で渡されたcolorと同じなら削除
// 上下左右のブロックを調べる
public function deleteBlock(tx:int, ty:int, color:int):void
{
if (tx < 0 || WIDTH <= tx ||
ty < 0 || HEIGHT <= ty) return; // 配列外ならリターン
if (blocks[ty][tx] == null) return; // nullだったらリターン
if (blocks[ty][tx].color != color) return; // クリックしたブロックの色と違っていたらリターン
// 条件は満たしたのでブロックを消す
removeChild(blocks[ty][tx]);
blocks[ty][tx] = null;
// 周りの色を調べる
deleteBlock(tx - 1, ty, color); // 左へ
deleteBlock(tx + 1, ty, color); // 右へ
deleteBlock(tx, ty - 1, color); // 上へ
deleteBlock(tx, ty + 1, color); // 下へ
}
// ブロックを消すと隙間が空くので縦に詰める処理
// 左から右へ、下から上へ走査する
public function vPack():void
{
for (var x:int = 0; x < WIDTH; x++)
{
for (var y:int = HEIGHT - 1; y >= 0; y--)
{
if (blocks[y][x] == null) // 隙間を見つけた
{
for (var yy:int = y - 1; yy >= 0; yy--) // その一つ上から縦に走査
{
if (blocks[yy][x] != null) // ブロックを見つけたので[y][x]まで詰めなければならない
{
blocks[y][x] = blocks[yy][x]; // 配列の位置を変更
blocks[yy][x] = null; // 元にあった位置は削除しておく
// 0.3秒かけて下にずらす。
BetweenAS3.tween(blocks[y][x], {y:y * Block.HEIGHT}, null, 0.3).play();
break;
}
}
}
}
}
}
public function hPack():void
{
var y:int = HEIGHT - 1; // 一番下の段だけ走査
for (var x:int = 0; x < WIDTH; x++)
{
if (blocks[y][x] == null) // 一番下の段に何もないということは、ここに右のブロックを詰めなければならない
{
for (var xx:int = x + 1; xx < WIDTH; xx++) // その一つ右から走査。
{
if (blocks[y][xx] != null) // ブロックを見つけた
{
for (var yy:int = HEIGHT - 1; yy >= 0; yy--) // 縦の一段をまるまる左に詰める処理
{
if (blocks[yy][xx] == null) break; // もうずらすブロックが無いということでbreak
blocks[yy][x] = blocks[yy][xx]; // 配列の位置を変更
blocks[yy][xx] = null; // 元の位置にnullを入れておく
// 0.3秒かけて左にずらす。
BetweenAS3.tween(blocks[yy][x], {x:x * Block.WIDTH}, null, 0.3).play();
}
break;
}
}
}
}
}
// 周りに自分の色と同じマスが1つでもあったらtrue, なかったらfalse.
public function colorCheck(tx:int, ty:int, color:int):Boolean
{
var check:Boolean = false;
if (0 <= tx - 1 && blocks[ty][tx - 1] != null && blocks[ty][tx - 1].color == color) check = true;
else if (tx + 1 < WIDTH && blocks[ty][tx + 1] != null && blocks[ty][tx + 1].color == color) check = true;
else if (0 <= ty - 1 && blocks[ty - 1][tx] != null && blocks[ty - 1][tx].color == color) check = true;
else if (ty + 1 < HEIGHT && blocks[ty + 1][tx] != null && blocks[ty + 1][tx].color == color) check = true;
return check;
}
// もう消せるブロックが無いならtrue, まだ消せるブロックがあるようならfalseを返す
public function endCheck():Boolean
{
for (var y:int = 0; y < HEIGHT; y++)
{
for (var x:int = 0; x < WIDTH; x++)
{
if (blocks[y][x] == null) continue;
if (colorCheck(x, y, blocks[y][x].color)) return false; // 周りに同じ色が一つでもあったら
}
}
return true;
}
}
class Block extends Sprite
{
public static const COLORS:Array = [0xED1A3D, 0x00B16B, 0x007DC5];
public static const WIDTH:int = 31; // ブロックの横幅
public static const HEIGHT:int = 31; // ブロックの縦幅
public static const MARGIN_W:int = 1;
public static const MARGIN_H:int = 1;
public static const ELLIPSE_WIDTH:int = 15;
public static const ELLIPSE_HEIGHT:int = 15;
public var color:int;
public function Block()
{
graphics.beginFill(0x0, 0);
graphics.drawRect(0, 0, WIDTH, HEIGHT);
graphics.endFill();
color = COLORS[int(Math.random() * COLORS.length)];
graphics.beginFill(color); // 色をランダムで指定
graphics.drawRoundRect(MARGIN_W, MARGIN_H, WIDTH - MARGIN_W * 2, HEIGHT - MARGIN_H * 2, ELLIPSE_WIDTH, ELLIPSE_HEIGHT);
graphics.endFill();
// ベベルフィルターでブロックに質感を持たせる
this.filters = [new BevelFilter(4, 45, 0xFFFFFF, 1, 0x0, 1, 20, 20, 1, 3, "inner")];
addEventListener(MouseEvent.CLICK, onMouseDown);
}
private function onMouseDown(event:MouseEvent):void
{
var panel:Panel = this.parent as Panel
var point:Point = panel.searchBlock(this);
if (point)
{
if (!panel.colorCheck(point.x, point.y, color)) return;
panel.deleteBlock(point.x, point.y, this.color);
panel.vPack();
panel.hPack();
// 終了判定
if (panel.endCheck())
{
trace("END");
}
}
}
}