// 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)); } } '15 puzzle'にコメントを付けてみました