'15 puzzle'にコメントを付けてみました
photo: Ville Miettinen's Grundvik main house
http://www.flickr.com/photos/wili/214317898/
コメントを付けたり、関数をまとめたりしながら勉強させてもらっています
/**
* Copyright ongaeshi ( http://wonderfl.net/user/ongaeshi )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/29bH
*/
// forked from 5ivestar's forked from: 15 puzzle
// forked from flashrod's 15 puzzle
// photo: Ville Miettinen's Grundvik main house
// http://www.flickr.com/photos/wili/214317898/
// コメントを付けたり、関数をまとめたりしながら勉強させてもらっています
package {
import flash.display.Sprite;
import flash.display.Loader;
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import caurina.transitions.Tweener;
public class Fifteen extends Sprite {
// 画像サイズ(渡された画像が大きかったり小さかった場合は、圧縮をかけてこのサイズにする)
private static const W:int = 465;
private static const H:int = 465;
// ピースサイズ
private static const U:int = int(W/4);
private static const V:int = int(H/4);
// ボード情報
private var board:Array = [];
// ローダー
private var loader:Loader;
// コンストラクタ
//
// Webから画像を読み込む
// 画像読み込みにはそれなりに制約があり、単純にURLを変えても絵が変わらない
//
// Loader ... ロードする人
// LoaderContext ... ロード時のオプション設定等
// Loader.contentLoaderInfo ... ロードの進行状況に関する情報と、ロードされたファイルに関する統計
// URLRequest ... URL処理
public function Fifteen() {
loader = new Loader();
var context:LoaderContext = new LoaderContext(true);
loader.contentLoaderInfo.addEventListener("complete", loadingComplete);
loader.load(new URLRequest("http://farm1.static.flickr.com/57/214317898_596c96ecb6.jpg"), context);
}
// ロード完了後に呼び出されるコールバック
//
public function loadingComplete(e:Event):void {
// 大元の一枚絵
var source:BitmapData = new BitmapData(W, H, false);
// 画像のスケーリング
scalePicture(source);
// ピースの生成
createPiece(source);
// 適当な回数パネルを動かし、画像がグチャグチャになった状態から始める
shuffle(0);
// マウスがクリックされた時に、スライドさせるためのコールバック
stage.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void {
shift(e.stageX/U, e.stageY/V);
repaint();
});
// 再描画
repaint();
}
// 画像のスケール調整
//
// 色々な方法があると思うが、ここでは画像のアスペクト比を維持しながら、
// 余白を作らない方法でスケーリングしている。
//
// MaxOSXの言葉で言うと、'Aspect Fill'
// Aspect Fill以外の例もいくつか挙げておく
//
private function scalePicture(source:BitmapData):void {
// 目標画像サイズ / 元画像サイズ
var sx:Number = W / loader.width;
var sy:Number = H / loader.height;
// 一枚絵の生成
//
// Matrixは a, b, c, d, tx, ty の順番であることに注意、行列で書くと・・・
// [a, b, tx]
// [c, d, ty]
// えー。
var mtx:Matrix;
// 行列を使ってスケール調整
// Aspect Fill
if (sx > sy) {
// [sx, 0, 0]
// [ 0, sx, (H - loader.height * sx) / 2)]
mtx = new Matrix(sx, 0, 0, sx, 0, (H - loader.height * sx) / 2);
source.draw(loader, mtx, null, null, null, true);
} else {
// [sy, 0, (W - loader.width * sy) / 2]
// [ 0, sy, 0]
mtx = new Matrix(sy, 0, 0, sy, (W - loader.width * sy) / 2, 0);
source.draw(loader, mtx, null, null, null, true);
}
/*
// Scale To Fit
mtx = new Matrix(sx, 0, 0, sy, 0, 0);
source.draw(loader, mtx, null, null, null, true);
*/
/*
// Aspect Fit
if (sx > sy) {
mtx = new Matrix(sy, 0, 0, sy, 0, 0);
source.draw(loader, mtx, null, null, null, true);
} else {
mtx = new Matrix(sx, 0, 0, sx, 0, 0);
source.draw(loader, mtx, null, null, null, true);
}
*/
}
// パネルの生成
private function createPiece(source:BitmapData):void {
for (var k:int = 1; k < 17; ++k) {
var p:Piece = new Piece(k, source);
addChild(p);
board.push(p);
}
}
// シャッフル
private function shuffle(num:int):void {
var i:int, j:int = 3;
for (var k:int = 0; k < num; ++k) {
i = int(Math.random()*3);
shift(i, j);
j = int(Math.random()*3);
shift(i, j);
}
}
// パネルの移動処理
private function shift(x:int, y:int):void {
if (x>=0 && x<4 && y>=0 && y<4) {
// 横方向へのスライド処理
for (var i:int = 0; i < 4; ++i) {
var p:Piece = getPiece(i, y);
if (p.value == 16) {
for (; i>x; --i) setPiece(i, y, getPiece(i - 1, y));
for (; i<x; ++i) setPiece(i, y, getPiece(i + 1, y));
setPiece(x, y, p);
return;
}
}
// 縦方向へのスライド処理
for (var j:int = 0; j<4; ++j) {
p = getPiece(x, j);
if (p.value == 16) {
for (; j>y; --j) setPiece(x, j, getPiece(x, j - 1));
for (; j<y; ++j) setPiece(x, j, getPiece(x, j + 1));
setPiece(x, y, p);
return;
}
}
}
}
private function setPiece(x:int, y:int, p:Piece):void {
board[y*4+x] = p;
}
private function getPiece(x:int, y:int):Piece {
return board[y*4+x];
}
// 再描画
private function repaint():void {
for (var j:int = 0; j < 4; ++j) {
for (var i:int = 0; i < 4; ++i) {
var p:Piece = board[j*4+i];
// こっちだとダイレクト移動
// p.x = i*U;
// p.y = j*V;
// こっちだと補完付き移動
Tweener.addTween(p, {x:i*U, y:j*V, time:0.1, transition:"easeOutQuad"});
}
}
}
}
}
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;
// 15パズルにおける、一つのピースを表す
class Piece extends Sprite {
public var value:int;
// コンストラクタ
//
// @param value 大元の絵を16分割した際、何番目のピースかを表すインデックス(1〜16)
// @param source 大元の一枚絵
public function Piece(value:int, source:BitmapData) {
this.value = value;
// 16は空の意味、ビットマップは作成しない
if (value == 16) return;
// このピース用のビットマップのサイズ
var w:Number = source.width / 4;
var h:Number = source.height / 4;
// このピース用のビットマップ
var bitmap:BitmapData = new BitmapData(w, h, false);
// 切り取り用の矩形
// 1〜15のインデックスに対して、切り取り開始位置を決定
var rect:Rectangle = new Rectangle((value-1)%4*w, Math.floor(value/4)*h, w, h);
// ビットマップを作成!!
bitmap.copyPixels(source, rect, new Point());
// 自身の子供に、作成したビットマップを接続
addChild(new Bitmap(bitmap));
}
}