0_0_58

 obake   specification   version 

0_0_57D で stable。0_0_58 へマイナーバージョンを進める。

スワップ・重力キャンセラの機能テスト

スワップ・重力キャンセラはキチンと機能している。

落ち物パズルなら、アクティブスワップが無ければ相当作るのは楽になるのだけど、アクティブスワップがあるとタイミング制御は実装が難しくなってくる。GM:S1.4 では標準機能で多次元配列とデータ構造とまともなデバッガがあるおかげ、比較的楽に作れている。

MMF2/CF25 の頃から落ち物パズルは作成を試みていたのだけど、単純なスワップなら確実に実装できた、しかし同時に何箇所もタイルを入れ替えする動作=「アクティブスワップ」を作る場合、MMF2/CF25 の標準機能ではどう考えても実装が難しい。しかもかろうじて動くものが出来たー!思ったらエクスポート機能を使うと動作はバグる。標準機能で作っているのに他プラットフォームへまともにエクスポートできない。唯一まともに動いた SWF 出力には取れないバグもあった。デバッグが難しく、再現性もマチマチ。ツール側のバグなのか、開発者の仕込んだものなのか、何か回避策があるのかなど模索したが、結局解決できず。やむなく MMF2/CF25 を使って作るのを諦めた経緯がある。

参考( prester.org/cf2.5 )Zoo ぱく( SWF 版)

参考( wikipedia )Zoo keeper

アクティブスワップとは

タイルを同色で3つ揃えると、そのタイルは盤面から排除できる。除外されたタイルがあった空いた場所には、その上にあったタイルが落ちてきて隙間を埋める。盤面の上部に新しく出来た空きスペースには、新しいタイルが上から落下して補充される。

盤面は7*7マス、盤面には 49 個のタイルが置ける。

隣り合ったタイルを入れ替えする動作を単純に「ワンスワップ( 入れ替え ) 」という。

スワップは一組だけでなく、何組も同時にスワップ可能。これが「アクティブスワップ( マルチスワップ/アクティブマルチスワップ ) 」。複数のラインでタイルの交換が実行できるということ。

「アクティブスワップ」中に同色のタイルが三つ並ぶ「連鎖( いわゆる 3-Match ) 」が成立した場合、盤面のどこで連鎖が発生したかによってゲームエンジンの実行する処理が大幅に変わってくる。0_0_57D 以前からバグが取れなくて困っていたのはこの部分の処理に当たる。

連鎖が盤面下部で発生した場合、盤面上部でスワップ中のタイルがあった時はスワップがキャンセルされる。アクティブスワップは一秒間に可能な操作として最大三つ程度のキューを持てるように設計されている。それ以上のキューを蓄えることもおそらく可能だが、操作速度的限界から、人間には無理。

アクティブスワップはスワップ中に盤面下部で連鎖が成立した場合、盤面上部のタイルはスワップをキャンセルされるが、盤面は7*7マスなので、直接影響が無いマスで実行されているスワップはそのまま続行される。つまりアクティブスワップはキャンセルされた場合、スワップしていたタイル直下で同色タイルによる連鎖が成立したってこと。

連鎖が成立した columns は、成立したタイル位置より上部のマスが全て操作不可になるようロックされる。そしてスワップ中のタイルがあった場合にもスワップはキャンセルされる。

連鎖によって操作が制限されている行及び列があっても、連鎖成立の影響が無い columns と rows ではスワップ操作が可能となっている。スワップしている間に別の columns で連鎖が成立、これが連続的に発生すると「コンボ」と呼ばれるボーナスが付く。一定時間内に連続スワップして連鎖を次々と作る操作は難易度が高く、大きな「コンボ」ほど大きなボーナスを得る。

基本ゲームルールは説明するとこんな具合。いずれ文章化しなければいけないので、その練習のため一度ここにまとめてみた。もっと短く簡潔に説明できるようにしたい。

イメージとしてはジャグリングみたいな?

自分の中ではだが、アクティブスワップはジャグリング( juggling ) のイメージを持って作っている。トスジャグリングでは右手と左手同時に投げる動作をシンクロと呼び、複数オブジェクトをトスする動作をマルチと呼んでいる。アクティブスワップはジャグリングのマルチトス、右左で同時にトスするシンクロをマルチで実行していく処理。

しかし同時に実行すれば処理的な衝突が発生するのは必須なので、処理の衝突( 同時処理的な意味で破綻した動作 ) を検出したら操作をキャンセルして衝突を無理やり無かったことにしちゃうのが「スワップ・重力キャンセラー」というゲーム処理である。

スタートアップ処理に若干の変更

アプリケーションが Fatal Error でクラッシュしたり、異常終了した場合、正常に終了処理が行われない( 異常が発生して強制的にゲームが終了した時、データが適切に保存されないため、次回起動時にデータの読み出しに失敗する ) 場合があった。これを修正。

ini_open(IniFileName);

g = ini_read_real(D,m,o);/*  DebugRestart.APPCrash = ? ? ?  */

    if (g != n)
    {
        g = ini_read_string(D,E,o);/* DebugRestart.DebugFLAG = ? ? ? */
        
            if (g == H) /*    IF ( g == " O N " )  THEN     */
            {
                ini_write_string(D,E, F);/* DebugRestart.DebugFLAG = "OFF"; */
            }
            else
            {
                show_message("Notice: st  ->  DebugRestart.DebugFLAG = " + g);
            };
    };

g = ini_read_string(D,E,F);

    if (g == F) // if "OFF"
    {
        ini_write_string(D,E, H);/* DebugRestart.DebugFLAG = "ON";  */
          ini_write_real(D,m, 0);/* DebugRestart.APPCrash  = 0;     */
          
        ini_write_real(b,c, a);  /* InitGUI.Startup  = get_timer(); */
    }
    else        // if  "ON"
    {
        L = false;
    };
ini_close();

上記はスタートアップ処理。

正常にゲームが起動して正常に終了された場合には、セーブデータにも正常にアプリケーションが終了されたことを示すフラグを保存する。

SetFlagRestart 関数

///SetFlagRestart( boolean );
var a,b,c, D, e;
a = IniFileName;
b = "DebugRestart";
c = "DebugFLAG";
e = "APPCrash";


ini_open(a);

    if (argument0 == true) /* Game     R E S T A R T     */
    {
        //show_message("SetFlagRestart ON");
        D = "ON";
        ini_write_string(b,c, D);
        global.GAMERESTARTFLG = true;
    
    }
    else         /* if    F A L S E    -->    Game Exit. */
    {
        //show_message("SetFlagRestart OFF");
        D = "OFF";
        ini_write_string(b,c, D);
        ini_write_real(b,e, noone);
    };

ini_close();

SetFlagRestart 関数は Game End イベント から呼ばれる関数。この関数は各ルームでイベントを管理しているオブジェクトからゲーム終了時に必ず呼ばれる。

異常終了によってこのアクションが実行されなかった場合、正常終了したことを証明するフラグが保存データに残らないため、次回起動時には異常終了した場合の起動条件でスタートアップが動作する仕組み。




次へ

前へ