HOME > ActionScript > スライドアルゴリズム > ピースを動かす

ピースを動かす

定義と仕様

次はピースを動かせるようにしていきます。
具体的にはイベントリスナーを登録し、それを骨格として必要な関数などを定義して行く事になります。
処理の大まかな流れは次の様な感じになります。
(右の図は大雑把なフローチャート)

  1. マウスプレス検出(mouseDownイベント)
  2. マウスの移動(スライド操作)を検出(mouseMoveイベント)
  3. プレスされたピースがある行もしくは列に存在するピース群をグループ化
  4. グループ化したピース群をマウスの移動に合わせて移動(enterFrameイベント)
  5. マウスリリース検出(mouseUpイベント)
  6. 移動したピース群を元の位置(初期配置座標)に戻す

今回のコーディング進行はここまでです。

ソースファイル slide03.zip

「元の位置に戻すってなんだ」と思われるかもしれませんので、簡単にそれ以降の処理にも触れておきます。
マウスリリース後にピースの位置を元に戻す際、マウスポインタが移動した移動距離を計測します。するとその移動距離からピースを何セル動かしたかを導き出す事が出来るので、フィールドデータのどれを入れ替えれば良いか(どのピースをどこへ動かしたか)を判別する事が出来ます。
それが判れば後はフィールドデータの更新、そして最後にそのデータを元にピースの絵柄を更新して一連のスライド操作が完了します。

処理以外での要点としましては、操作の状況を常に把握する為のフラグを用意しています。

priveta var ctrlStatus:uint;

このフラグ変数にはビットマスクを使用するのですが、規模や仕様、使い方によっては単純にBoolean型で個別にフラグを用意したほうが良い場合もあるようです。ただ個人的には一つの変数でフラグをまとめられるのは、増え始めると煩雑になりやすいフラグ関連の変数管理においては可読性だけを考慮しても、積極的に使用して行っても良いのではないかなと考えております。
ここでビットマスクについての詳しい説明は割愛いたしますが、使う頻度にかかわらず是非理解しておくことをお勧めします。

簡単にビットマスクの使い方の例だけを以下に上げます。

/* まず使用する定数、変数の定義。 */
// 1番右のビットを立てた定数を宣言。マウスプレスしたかどうかの真偽に使用。
const PIECE_PRESS:uint  = 0x00000001;

// ユーザの操作状況を表すフラグ変数を宣言。必ず0でリセット。
var ctrlStatus:uint = 0;



/* 処理の中での使用例。 */
// ctrlStatusの1番右のビットが立っているかどうかを調べる。立っていると真が返る。
if (ctrlStatus & PIECE_PRESS) {.... }

// ctrlStatusの1番右のビットを立てる。
ctrlStatus |= PIECE_PRESS;

// ctrlStatusの1番右のビットを0にする。
ctrlStatus &= ~PIECE_PRESS;

それと定義ではありませんが念のために考え分けをしておきますと、ここで言うスライドとは「行為」であり、スクロールとは「現象」です。スクロールというのはあくまでもスライドという操作によって引き起こされる処理という位置づけでここでは考えております。この後の説明でその辺りの区別を混同されませんようご注意願います。

スライドの起点

最初に登録しておくイベントリスナーはmouseDownイベントだけです(Main.as 101行目)。
この時のイベントターゲットはピースインスタンスではなく、全てのピースを内包している表示コンテナであるfieldインスタンスです。今回は基本的にイベントリスナーの登録は全てfieldインスタンスをターゲットにします。
fieldインスタンスをターゲットにした場合に「どのピース上でマウスダウンされたか」を検出するには、リスナー関数に渡されるイベントオブジェクトのtargetプロパティの値からそれを識別する事が出来ます。
またcurrentTargetプロパティですと、イベントリスナーのターゲットになっているfieldインスタンスの参照が得られます。

ピースをマウスプレスされたら、いきなりピースを動かす処理には移行しません。
まず「その後に直ぐリリースされる」操作と、「スライド操作される」事を監視するために、二つのイベントリスナーを登録します(Main.as 398行目 onPiecePress)。

mouseUpイベント

マウスリリースされたかどうかを監視するにはイベントリスナーにmouseUpイベントを登録しておきます(Main.as 413行目)。
mouseUpイベントのリスナー関数(Main.as 422行目 onPieceRelease)の役割は、不要なイベントの削除とフラグの初期化、及びfieldインスタンスに再びmouseDownイベントだけが登録された状態に戻す事です。

ただしマウスリリースされるタイミングは大きく2種類存在します。
ピースをプレスされた後に直ぐにマウスリリースされた場合と、スライド操作を行ってからマウスリリースされた場合です。ですのでこの2つの状況に対応した処理を記述しておきます。

mouseMoveイベント

スライド操作されたかどうかを監視するにはmouseMoveイベントを設定しておきます(Main.as 417行目)。
このスライド操作の検出には「何ピクセル動いたらスライド開始と認識するか」の基準値と「縦と横のどちらにスライドされたか」の判断が必要になります。
今回はスライド開始を認識する値として5px(5px以上動いたらスライド操作されたと認識)、縦横の判別については難しく考えずに、縦横の移動量で先に5px以上動いた方で確定してしまっています。
5pxという値は実際に動かしてみて違和感のない値がそれくらいでした。また縦横の判別では、処理上は縦の方を先にチェックしていますが、こちらも違和感はないはずです。

この5pxという値は定数で用意しているので、値を変更して試してみて欲しいです。

private const SLIDE_ALLOW:uint = 5;

mouseMoveイベントのリスナー関数(Main.as 447行目 onPieceSlideKeeper)では上記のスライド操作検出などを判別後にスクロール処理へ移行する為の初期化を実行します。

スクロール処理へ移行

スライド操作を感知した場合、そこでやっとスクロール処理へ移行します。
まずスクロール処理の為に必要なデータの用意と初期化を実行し、その後enterFrameイベントを登録するという流れです(Main.as 484行目 piecesScrollInit)。
また同時にスクロール処理を終わらせるために必要なデータも用意しておきます。

必要なデータ

ピースを動かす為に必要なデータはプレスされたピース(マウスダウンされた時にマウスポインタの真下にあったピース)のインデックスと、それが存在している行もしくは列に並んでいる全てのピースのインデックス情報です(表示エリア、スクロールエリア全て含む)。それらインデックス情報を抽出しVector配列にグループ化します。
その他に必要なデータはプレスした直後の「現在のマウスの座標」を保存しておけば動かす為のデータの準備はOKです。

次にスクロール処理を終了させるためのデータですが、スクロール処理が終わるタイミングは第一に「スライド操作を止めた」時です。つまりマウスリリースを検出した場合になります。要するに「スライド操作の終了 = スクロール処理の終了となります。当たり前ですみません。

それともう一つ、マウスプレスされたままでもスクロール処理を終了するタイミングを用意しておく必要があります(しなくても良いと言えば良いのですが今回は用意しました)。そのもう一つの終了タイミングが「スクロール処理が発生するマウスポインタ有効領域」という要素になります。
回りくどく言っておりますが、スクロールの為に決まった領域を用意しておいてマウスポインタがその領域外へはみ出たらスクロール処理終了にするという事です。

有効領域の設定例としては下の図の様になります。
マウスプレスして横にスライド操作をした場合、スクロール処理で動くピースの高さと、表示エリアの幅のサイズ内が有効領域になります。

有効領域をはみ出した場合でも、処理の結果としてはマウスリリースを検出してスクロール処理を終了した場合と同じなのですが、マウスリリースで終了した場合はユーザが操作を止めたかどうかで終了しているのに対し、マウスポインタが有効領域外へはみ出して終了した場合は、極端に言いますと「スクロール処理の強制終了、それに伴ってスライドも終了」という感じです。
などと二つはすごく違うような事を言っていますが、実際には実行タイミングが違うだけで同じ処理を実行していたりします。

グループ化

スクロールさせるピースのグループ化はslideGroupSearch()(Main.as 157行目)が行います。
この関数はマウスプレスされているピースのインデックスと、縦移動かどうかの真偽値を渡すと、そのピースが存在する行もしくは列上にある全てのピースのインデックスをVector配列にまとめて返してきます。この時返してくるインデックスにはもちろん表示エリアの他にスクロールエリア内のピースのインデックスも含まれています。

処理自体はもっとシンプルにできるのですが、Vector配列に収めるインデックスの順序をセルの並びの上から下、もしくは左から右という用に整列された状態で収めるようにしているのでループを3回行っています。
ただ動かすだけなら実のところ順序の整列は必要ありません。グループ化さえされていれば良いので1回のループで達成できます。もし、その処理の後で「ピースの柄が揃っていたら消す」など何かしらのゲーム性を持たせて行くようなギミックを追加していく場合には、整列されていた方が色々と扱いやすかったりします。
とりあえず今回は必要はないのですが、こういう方法があると言う事で組み込んであります。
また、整列の方法も配列のソートメソッドなどを使う方法もありますが、その場合はおそらくshiftやunshiftなども利用する必要が出てくると思いますので、少し速度が犠牲になります(ActionScriptのshift()、unshift()は重いです)。

ピースを動かす

スクロール処理に必要なデータの用意が整うと、enterFrameイベントを登録(Main.as 511行目)してループ処理を開始します。

スクロール

enterFrameイベントのリスナー関数であるonPiecesScroll()(Main.as 309行目)の、ピースを動かす処理の理屈はわかりやすいはずです。マウスの移動した量をフレーム毎に検出してその移動量をピースの座標値に加算もしくは減算して行くだけです。

重要な点はリスナー関数の中に前項で触れた2種類の「処理終了の評価」を入れておくことです。
一つはリスナー関数末にあるブロック(Main.as 353行目)で、マウスリリースイベントを検出する(マウスプレスのフラグが偽になる)とフレームループ処理が終了します。

そしてもう一つはマウスポインタが有効領域外へ出たかどうかを評価しているブロック(Main.as 320行目)です。こちらの評価を通った場合、このブロックでも実はmouseUpイベントのリスナー関数を実行しているので処理の経路としてはマウスリリースを検出した場合と同じく、リスナー関数末のブロックを通過します。
このような処理の連動性を取った理由は、マウスポインタが有効領域外へ出てスクロール処理が終了した場合でも移動させたピースを元に戻す必要があるためです。

スライド終了

スライド操作が終了すると後はピースを元の位置に戻す処理を実行します(Main.as 355行目 piecesPutBack())。
piecesPutBackメソッド(Main.as 361行目)では、forループでゴニョゴニョと実行していますが、実のところこの元の座標値もテーブルにして用意しておけば1~2で済む処理です。
今回は何をしているのかを把握してもらうためにループ処理しています。

動作確認

適当なピースをドラッグすると一連のピースが動きますが、マウスリリースすると元に戻るので悩ましいです。

Flashファイルをご覧いただくためには、
アドビシステム株式会社のフラッシュプレーヤー(Flash Player)が必要です。
インストールされていない方は下のボタンから、最新版が無償で入手できます。

Adobe Flash Playerのダウンロード

ソースファイルについて

ダウンロードリンクにあるzipファイルの中にはドキュメントクラスとなるMain.asファイルの他に、CS5とCS4形式で保存したflaファイルが一つずつ入っています。これらのflaファイルはCS4とCS5のバージョン以外では開けません。中のフォルダ構成がそのままならflaファイルをFlash CS5もしくはCS4で開いてパブリッシュすれば動作するはずですが、フォルダやファイルを個別に移動した場合はそれに合わせて適宜flaファイルの設定を変更して下さい。

  • PiecesSlide.fla (CS5用)
  • PiecesSlide_forCS4.fla (CS4用)
  • src/Main.as
  • bin/

基本的にはCS5用のファイルを使用する事が前提で進めますが、FlashCS4を使用している方はCS4用の方を使用して下さい。
ただしCS4用のflaファイルについてはFlash CS5でCS4用に保存したものですので動作確認はしておりません。開けなかったり使用できない場合があるかもしれませんが、その際はどうぞご容赦願います。
またファイルの設定などはWindows環境での作業を想定していますので、Flash CS5を使用していてもそのままの設定では使えない場合があります。適宜設定を変更するなどしてご使用願います。
flaファイルの設定などがわからない場合は公式リファレンスを参考にして下さい。申し訳ありませんがご質問等にはお答えできません。

TOP