9.11 セーブデータの読み込みに対応する(その1)

やっとプラグインが完成したと思ったのにねぇ…
まぁ後はセーブデータの読み込みに対応すれば今度こそ完成だから。
でも対応するのって結構タイヘンなんだよね?
ん〜、スクリプトに追加しなくちゃいけないことは結構色々あるかな。
じゃセーブデータの読み込みに対応できるようにプラグインを改良していこうと思うんだけど…
うん?
その前にまず、セーブデータを読み込むとどんなふうにセーブされた時の前景レイヤの状態が復元されるか知ってる?
うーん、よく知らないけど…セーブデータにセーブされた時に表示してた画像の情報とかが書いてあって、 それを読み込んでるんじゃないの?
まぁ大体そんな感じだね。
もうちょっと詳しく言うと、こういう流れでセーブされた時の前景レイヤの状態を復元してるんだ。

<ロード時の前景レイヤ復元処理の流れ>

セーブデータを読み込むと、最初に CharacterLayer クラスの restore っていうメソッドが呼び出されて、 autohide 属性の値がセーブデータから読み込まれて復元されるの。
ちなみに CharacterLayer クラスは前景レイヤ用のクラスだよ。
CharacterLayer クラスの定義は system フォルダにある GraphicLayer.tjs の中に記述されています。
autohide 属性って layopt タグで設定できる属性のこと?
確か true にすると、 右クリックとかでメッセージレイヤを非表示にした時にそのレイヤも非表示になるんだよね。
ん、その autohide 属性のことだよ。
で、次に CharacterLayer クラスのスーパークラス、 GraphicLayer クラスの restore メソッドが呼び出されて、 クリッカブルマップ関係の設定が復元されるの。
GraphicLayer クラスの定義も system フォルダにある GraphicLayer.tjs の中に記述されています。
えっと、それってなんで CharacterLayer クラスじゃなくって GraphicLayer クラスの restore メソッドでやるの?
全部 CharacterLayer クラスの restore メソッドでやった方がわかりやすいと思うんだけど?
クリッカブルマップって背景レイヤにも作れるでしょ。
うん、前にクリッカブルマップ作った時は背景レイヤに作ったよね。
§6.2 参照。
背景レイヤ用のクラス(BaseLayer クラス)も GraphicLayer クラスがスーパークラスになってるんだ。
だから GraphicLayer クラスの restore メソッドにクリッカブルマップを復元するスクリプトを書いとけば、前景レイヤと背景レイヤの両方で使えるの。
そーすれば前景レイヤと背景レイヤ両方のクラスにクリッカブルマップを復元するスクリプトを書かなくてもよくなるってコト?
そういうこと。
で、今度は GraphicLayer クラスの restore メソッドから GraphicLayer クラスのスーパークラス、 AnimationLayer クラスの restore メソッドが呼び出されて、 セーブされた時にレイヤに読み込まれてた画像とかアニメーションとか部分追加読み込み画像が復元されるんだ。
AnimationLayer クラスの定義は system フォルダにある AnimationLayer.tjs の中に記述されています。
 また、これ以降「セーブされた時」というのは、厳密には「セーブされる直前にセーブ可能なラベルを通過した時」を意味します。
なんかだんだんややこしくなってきたね…
まぁ CharacterLayer クラス(前景レイヤ用のクラスね)のスーパークラスのスーパークラスのスーパークラスのスーパークラスまであるからね。
※ちなみに Layer クラス → KAGLayer クラス → AnimationLayer クラス → GraphicLayer クラス → CharacterLayer クラスの順に継承されています。
うわ、そんなにあるんだ…
あ、ところでさっき言ってた部分追加読み込み画像ってなんなの?
pimage タグで読み込んだ画像のことだよ。
あー、レイヤに表示されてる画像の中に別の画像を読み込んで表示できるタグのことだね。
そ。あと ptext タグでレイヤに書き込んだ文字もここで復元されるよ。
そーなんだ。
で、次に AnimationLayer クラスの restore メソッドから AnimationLayer クラスのスーパークラス、 KAGLayer クラスの restore メソッドが呼び出されて、 レイヤのサイズと表示位置とか、不透明度、表示状態(可視か不可視かってことね)、重ね合わせ順序が復元されて、 復元完了だよ。
KAGLayer クラスの定義は system フォルダにある KAGLayer.tjs の中に記述されています。
レイヤの状態を復元するのって結構タイヘンなんだねぇ…
セーブデータを読み込むと一瞬でセーブした時の状態が復元されるから一見カンタンそうに見えるけど、 実は目に見えないとこで KAG システムが色んなことをやってくれてるわけだね。
確かにこれ全部自分で作れって言われても作れないよねぇ。
だね。
それじゃここからが本題なんだけど、KAG システムが面倒を見てくれるのはここまでだから、 画像を拡大・縮小・回転した場合は、その分は自分で復元しないといけないワケ。
だよねぇ…でもどーすればいいの?
プラグイン(KAGPlugin クラスのことね)の onRestore メソッドって覚えてる?
KAGPlugin クラスについては §4.1onRestore メソッドについては §4.12 参照。
セーブデータが読み込まれる時に呼び出されるメソッドのことだよね?
そうそう。
onRestore メソッドは前景レイヤの復元処理が全部終わった後に呼び出されるから、 こんなふうに onRestore メソッドの中で拡大・縮小・回転の復元処理をやればセーブデータの読み込みに対応できるんだ。

<拡大・縮小・回転の復元処理を入れたロード時の前景レイヤ復元処理の流れ>

なるほどねぇ…
これってつまりセーブした時に画像が拡大・縮小・回転されてたら、 onRestore メソッドの中で画像を拡大・縮小・回転して復元するってことだよね?
そう。スクリプトにするとこんな感じね。

onRestore メソッド>

function onRestore(f, clear, elm)
{
    // セーブ時の前景レイヤの状態を復元します
    var foreFlags = f.foreCharacterLayers;
    var backFlags = f.backCharacterLayers;
    for(var i=0;i<foreFlags.count;i++)
    {
        // 表画面の i 番目の前景レイヤに画像が読み込まれている場合は
        // processImage メソッドを呼び出してセーブ時の状態を復元します
        if(foreFlags[i].loadParams !== void)
            processImage(foreFlags[i].loadParams, kag.fore.layers[i]);
        // 裏画面のレイヤも同様にセーブ時の状態を復元します
        if(backFlags[i].loadParams !== void)
            processImage(backFlags[i].loadParams, kag.back.layers[i]);
    }
}

最初に foreFlagsbackFlags ってゆー変数を作って f.foreCharacterLayersf.backCharacterLayers を代入してるみたいだけど…
これって何なの?
onRestore メソッドの第1引数の f は辞書配列になってて、セーブデータに保存されてるデータが入ってるってのは、 前にも onRestore メソッドを作ったことあるからわかるよね?
§4.12 参照。
えっと、この f ってゲーム変数の f
じゃないんだよね。前にプラグイン作った時にも言ったと思うけど。
あれっ、そーだったっけ?
引数名が f になってるからちょっと紛らわしいとは思うんだけど、 この f はゲーム変数とは別の辞書配列なの。
※ゲーム変数の f はゲーム変数用の辞書配列(kag.flags)への参照であり、 onRestore メソッドの引数の f は保存可能なラベルを通過した時点での様々な情報が記録されている辞書配列(kag.pcflags)への参照になっています。
そーなんだ…
onRestore メソッドの方の f には、 セーブする直前にセーブ可能なラベルを通った時の色んな情報が記録されてるんだ。
ちなみにこれが f に記録されてる情報だよ。

f に記録されている情報の一覧(KAG 3 version 3.30 Rev.2 現在)>

な、なんかすごいいっぱい情報が記録されてるんだね…
でしょ。
で、今注目して欲しいのは赤色で書いてる部分。
えっと、foreCharacterLayersbackCharacterLayers のとこが赤色になってて 「前景レイヤの設定」って書いてあるね。
foreCharacterLayersbackCharacterLayers は配列になってて、 それぞれ表画面の前景レイヤの情報と裏画面の前景レイヤの情報が記録されてるんだ。
配列になってるってことは、例えば foreCharacterLayers[0] には表画面の 0 番の前景レイヤの情報が入ってるってコト?
そうそう。
で、foreCharacterLayers[0] とかは辞書配列になってて、こういう情報が書き込まれてるの。

<前景レイヤの情報一覧(KAG 3 version 3.30 Rev.2 現在)>

またなんか色々書き込んであるねぇ…
レイヤの状態を復元するためには色々情報が必要だからね。
で、onRestore メソッドに戻ると、 最初に foreFlagsbackFlags にそれぞれ f.foreCharacterLayersf.backCharacterLayers を代入してるから…
foreFlagsbackFlags はそれぞれ表画面と裏画面の前景レイヤの情報が記録されてる配列への参照になってるってコト?
ん、そういうこと。
じゃ次は for ブロックの中を見てくね。
foreFlags[i].loadParams っていうのが void じゃないかどうかチェックしてるみたいだけど、 loadParams って?
loadParams ってのは、前景レイヤの情報一覧の最後の行に書いてある通り、 画像が読み込まれた時(基本的には image タグが実行された時だね)の設定が記録されてる辞書配列なんだ。
image タグが実行された時の設定って…属性の値とかのこと?
そうだよ。loadParams には image タグが実行された時にどういう属性が指定されてたかがこんな感じで記録されてるの。

loadParams の中身の例>

foreFlags[0].loadParams.tagnameeximage マクロ実行時の mp.tagname の値は一致しませんが、 tagname 要素の値は使用しないのでここでは考えないことにします。

例えば、eximage マクロで表画面の 0 番の前景レイヤに画像を読み込むと、 foreFlags[0].loadParams.page"fore" になって、 foreFlags[0].loadParams.layer"0" になるわけ。
他にも storage とか left とか指定されてる属性の値は全部記録されるんだ。
loadParams って image タグじゃなくて eximage マクロを実行した時にも記録されるの?
eximage マクロの中で image タグを実行してるでしょ。
その時に記録されるの。
あ、なるほどね。
で、loadParamseximage マクロに指定した属性の値が記録されるってことは、つまり loadParams の中身は eximage マクロを実行した時の mp の中身とおんなじになってるわけ。
mp については §4.8 参照。
mp もマクロに指定されてる属性の値が記録されてる辞書配列だから…確かにおんなじだね。
だから、foreFlags[0].loadParams を引数に指定して processImage メソッドを呼び出すことで、 画像が拡大・縮小・回転されてる場合でもちゃんと表画面の 0 番の前景レイヤの画像を復元できるってワケ。
え、そーなの?
eximage マクロmp を引数にして processImage メソッドを呼び出してたでしょ。
あ、そっか。
で、for ループを使って全部の前景レイヤに対して processImage メソッドを呼び出してるから、 結局セーブデータを読み込む時に全部の前景レイヤに対して eximage タグを実行してるようなもんだね。
う〜ん、何となくわかったよーな気はするけど…なんか難しいね。
まぁ考え方はちょっとややこしいかもね。
…えっと、今までのところで2つ質問があるんだけど、いいかな?
うん、何?
何でここforeFlags[i].loadParamsvoid じゃないかどうかチェックしてるの?
loadParams はレイヤに画像が読み込まれてない時とか、 freeimage タグで画像を解放した後には void になるんだ。
レイヤに画像が読み込まれてない時、つまり loadParamsvoid の時は processImage メソッドを呼び出す必要はないから、 loadParamsvoid じゃない時だけ processImage メソッドを呼び出すようにしてるの。
あ、そーなんだ。
じゃあもう一つの質問なんだけど、 processImage メソッドって引数1個だったよね?
なんでここprocessImage メソッドを呼び出す時は引数が2個になってるの?
う〜ん、それがちょっと面倒なとこなんだよね〜。
え、どーいうこと?
トランジションすると裏画面の画像が表画面に表示されるでしょ。
うん、そだね。
トランジションすると表画面のレイヤと裏画面のレイヤが入れ替わるから、 裏画面の内容が表画面に表示されるんだけど、 この時に kag.fore.layerskag.back.layers の中身も入れ替わるんだ。
中身が入れ替わるって…?
つまり、トランジションすると kag.fore.layers[0]kag.back.layers[0] が入れ替わって、 それまで表画面の 0 番の前景レイヤだったのが裏画面の 0 番の前景レイヤになるの。
もちろん 0 番の前景レイヤだけじゃなくて全部の表画面と裏画面の前景レイヤが入れ替わるよ。
へぇ、そーなんだ。
…でもそれと processImage メソッドの引数が2個になるのとどーゆー関係があるの?
image タグとか eximage マクロで裏画面の 0 番の前景レイヤに画像を読み込んだ後に onRestore メソッドが呼び出されたとすると、 backFlags[0].loadParams.page の値はどうなってると思う?
裏画面のレイヤなんだから "back" だよね?
ん、そだね。
じゃあ、それから1回トランジションした後に onRestore メソッドが呼び出された時には foreFlags[0].loadParams.page の値はどうなってると思う?
foreFlags ってことは表画面ってことだから、 "fore" でしょ?
それが実は foreFlags[0].loadParams.page"back" になってるんだ。
えっ、そーなの? なんで??
さっきも言った通り、トランジションすると表画面と裏画面のレイヤが入れ替わるから、 foreFlagsbackFlags の中身も入れ替わるんだ。
つまり、トランジションする前の backFlags[0].loadParams の中身は トランジションした後には foreFlags[0].loadParams の中身と入れ替わってるってワケ。
えと、トランジションする前は backFlags[0].loadParams.page"back" だったけど、トランジションしたから backFlags[0].loadParams.page の値と foreFlags[0].loadParams.page の値が入れ替わって、 foreFlags[0].loadParams.page の方が "back" になったってこと?
そういうこと。
だから、この状態で processImage(foreFlags[0].loadParams); を実行すると、foreFlags[0].loadParams.page の値が "back" になってるから、 裏画面のレイヤの画像が拡大・縮小・回転されちゃうわけ。
それはマズイよねぇ…
でしょ。
だから、2番目の引数に kag.fore.layers[0] を指定することで、 foreFlags[0].loadParams.page の値が "back" になってたとしても前景レイヤの画像の方を拡大・縮小・回転してね、って processImage メソッドに伝えてるんだ。
なるほどねぇ…
でも前に processImage メソッドを作った時には2番目の引数のことなんて何も書いてなかったよね?
ん、だから processImage メソッドはちょっと書き直さなきゃいけないの。
あ、書き直すんだ。
こんな感じにね。

processImage メソッド(改)>

function processImage(elm, layer = void// ←引数を2つに増やしました
{
    // 対象のレイヤ(第2引数)が指定されていない場合は対象のレイヤへの参照を layer に代入します
    if(layer === void)
        layer = kag.getLayerFromElm(elm);
    if(!isCharacterLayer(layer))
        return;

    if(elm.x1 !== void && elm.y1 !== void && elm.x2 !== void && elm.y2 !== void && elm.x3 !== void && elm.y3 !== void)
        transformImage(layer, elm);

    if(elm.angle !== void)
        rotateImage(layer, elm);
    else if(elm.scale !== void || elm.xscale !== void || elm.yscale !== void)
        scaleImage(layer, elm);

    if(elm.xblur !== void && elm.yblur !== void)
        boxBlur(layer, elm);
}

どこが変わったかわかりやすいように、書き換えたとこだけコメントつけてるよ。
えっと、layer っていう第2引数が増えたのと…
あと変わったのって最初の if のとこだけってこと?
そ。元々は第1引数の elmeximage マクロに指定されてる属性の値が記録されてる辞書配列ね)を kag.getLayerFromElm メソッドに渡してレイヤへの参照を取得してたわけだけど、 第2引数が指定されてればその必要はないから、第2引数が省略されてる時だけ kag.getLayerFromElm メソッドを呼び出すようにしたの。
※メソッドのデフォルト引数については §1.17getLayerFromElm メソッドについては §9.8 参照。
ってことは、第2引数って省略してもいいってコト?
ん。第2引数を省略すると前の processImage メソッドと全く同じになるから、 eximage マクロの eval タグの中身は書き変えなくても済むわけね。
なるほどねぇ。
さて、じゃこれで onRestore メソッドは一通りチェックできたね。
じゃあこれでセーブデータの読み込みに対応できたってこと?
ん〜…まぁとりあえずここまでのスクリプトを実行してみよっか。
…そーゆー言い方する時って大抵まだ何かあるんだよねぇ…
さすがにわかってきたみたいだね〜。
やっぱり…
んじゃ onRestore メソッドを追加して、 processImage メソッドを修正したら実行してみて。
first.ks の中身は変えなくていーの?
ん、とりあえずそのままで OK。
それじゃ実行するね。

<実行結果(セーブデータを読み込んだ後の状態)>

これってちゃんと画像が復元できてる…よね?
みたいだね。
え、じゃあもしかしてこれでおっけーってコト?
…だったらセーブデータの読み込みに対応するのは結構タイヘンって言わなくて済んだんだけどね。
う、やっぱりまだ何かあるんだ…
じゃ次は first.ks をこう書き変えて実行してみて。

<画像を 0.8 倍(80%)に縮小表示するように改変した first.ks>

; 画像読み込み機能拡張プラグインのスクリプトを読み込みます
[call storage="eximage.ks"]

; 画像を 0.8 倍に縮小して表示します
[eximage layer=0 page=fore storage="krkr" left=256 top=176 scale=80 visible=true]

*label|

データを保存します。[p][cm]

[save place=0]

データを読み込みます。[p][cm]

[load place=0]

scale 属性が 80 になってるから、 画像を 80% の大きさで表示するってことだよね。
ん、変えたのはそこだけだよ。
それじゃあとりあえず実行してみるね。

<実行結果(セーブデータを読み込んだ後の状態)>

あ、画像の右下の方が切れちゃってる…
onRestore メソッドを追加しただけじゃダメだったみたいだね。
まー何か問題があるってことは予想してたけどねぇ…
ってワケだから、ちゃんとセーブデータの読み込みに対応できるように、次回この問題を解決するね。
は〜い。
それじゃ、また次回ね。


前へ | TOP | 次へ