6.10 セーブ画面を作る 〜サムネイル編〜

今回はサムネイルをクリックした時に実行するスクリプトを書いていくことにするね。
うん。
サムネイルをクリックした時にやらなきゃいけないことは結構色々あるんだけど、 スクリプトは大体こんな感じだね。

<サムネイルがクリックされた時に実行されるスクリプト>

*thumbnail
; すでにセーブデータがある場合は上書き確認します
; セーブデータが存在しないかまたは上書き確認ダイアログボックスで「はい」が選択された場合はセーブします
[if exp="kag.getBookMarkDate((sf.saveinfo.page - 1) * 10 + tf.saveload.thumbnail) == '' || askYesNo('セーブデータを上書きしますか?')"]
; このセーブデータの番号を sf.saveinfo.latest(最新のセーブデータの番号を記録しておく変数)にセットします
[eval exp="sf.saveinfo.latest = (sf.saveinfo.page - 1) * 10 + tf.saveload.thumbnail"]
; データをセーブします
[save place="&sf.saveinfo.latest"]
; サムネイルの表示を更新します
[call target=*update_thumbnail]
; サムネイルの下に表示する日付を更新します
[call target=*update_date]
; 最新セーブデータ指示画像を更新します
[call target=*update_new]
; 画面下部のセーブデータ情報の表示を更新します
[eval exp="update_saveinfo(tf.saveload.thumbnail)"]
[endif]
[s]

確かになんか色々やってるねぇ。
とりあえず始めから順番に見ていこっか。
うん、そうだね。
まず、最初の if タグのとこでセーブデータがあるかどうかチェックして、 もしあったら上書きするかどうか確認するダイアログボックスを表示してるんだ。
で、セーブデータが無かったり、確認のダイアログボックスで「はい」が選択されたら endif タグまでのスクリプトを実行するの。
な、なんか最初っから複雑だね…
じゃあ、セーブデータがあるかどうかチェックしてる部分と、 確認のダイアログボックスを表示する部分に分けて見てみるね。
うん。
まず、セーブデータがあるかどうかは getBookMarkDate メソッドでチェックできるんだ。
kag.getBookMarkDate になってるから、 これって kag オブジェクトのメソッドだよね?
セーブデータは kag オブジェクトが管理してるからね。
で、このメソッドには引数が1つあって、この引数にセーブデータの番号を指定するの。
セーブデータの番号って、save タグとか load タグの place 属性と同じ値でいいの?
うん、それでいいよ。
えっと、(sf.saveinfo.page - 1) * 10 + tf.saveload.thumbnail って書いてあるけど、これってどうなってるの?
sf.saveinfo.pagetf.saveload.thumbnail はわかるよね?
sf.saveinfo.page が表示されてるページの番号で、 tf.saveload.thumbnail はクリックされたサムネイルの番号なんだよね。
§6.8 参照。
ん、そう。
1ページに表示するセーブデータの数は10個だから、1ページ目には1番〜10番、 2ページ目には11番〜20番、3ページ目には21番〜30番、っていうふうにセーブデータが表示されるよね。
うん、そだね。
じゃあ、p ページ目には何番〜何番のセーブデータが表示される?
えっ、p ページ目?
p1 なら1ページ目だから、 1番〜10番ってことね。
あ、そーいうことね。
えっと…それぞれのページの最初のサムネイルのセーブデータは、1番、11番、21番…ってなってるから、 p1 増えると 10 ずつ増えていくよね。
そうだね。
あと、p × 1010, 20, 30…ってなるから、 (p × 10 - 9)番目〜(p × 10)番目、かな?
ん、そうなるよね。
ちなみに ((p - 1) × 10 + 1)番目〜((p - 1) × 10 + 10)番目って言い換えることもできるよね。
(p - 1) × 10 + 1p × 10 - 10 + 1 だし、 ((p - 1) × 10 + 10)p × 10 - 10 + 10 だから…
うん、確かにそうだね。
じゃあ、p ページ目の t 番目のサムネイルは何番目のセーブデータを表してることになる?
えっとね… t1〜10 だから、 (p - 1) × 10 + t でいいのかな?
そうそう。
あとは、psf.saveinfo.page に置き換えて、 ttf.saveload.thumbnail に置き換えれば、 (sf.saveinfo.page - 1) * 10 + tf.saveload.thumbnail になるってワケ。
あ、ホントだ。
getBookMarkDate メソッドの引数と同じ値になってる!
じゃこれで引数は OK だね。
getBookMarkDate メソッドは、 引数で指定した番号のセーブデータがあれば、そのセーブデータの日時を返すんだけど、 セーブデータが無かったら空文字列を返すんだ。
ってことは、もしセーブデータが無かったら、 kag.getBookMarkDate((sf.saveinfo.page - 1) * 10 + tf.saveload.thumbnail) == '' が真になるんだね。
ん、そのとーり。
だから、セーブデータが無かったら if タグの exp 属性の値が真になって、endif タグまでのスクリプトが実行されるの。
で、セーブデータがあったら、次の askYesNo メソッドが呼び出されるんだ。
(注) askYesNo は特定のクラスに属していないので関数と呼んだ方が妥当かもしれませんが、 簡単のため以後このような関数もメソッドと呼ぶことにします。
えっ? セーブデータが無かったら askYesNo メソッドって実行されないの?
method1() || method2() っていう式があったとすると、 まず method1 っていうメソッドが呼び出されるよね。
|| 演算子(論理 OR 演算子)については §1.9 参照。
うん、そだね。
もし method1true を返したとすると、 method2 っていうメソッドがどんな値を返しても、 この式全体としては真になるでしょ?
確かにそうなるね。
そういう時は method2 は呼び出されないようになってるんだ。
へぇ、そうなんだ。
だから、getBookMarkDate メソッドが空文字列以外の文字列を返した時、 つまりセーブデータがあった時だけ askYesNo メソッドが呼び出されるの。
なるほどね〜。
で、askYesNo メソッドは、こーいうダイアログボックスを表示するメソッドなんだ。
askYesNo メソッドは system フォルダ内にある YesNoDialog.tjs 内で定義されています。

askYesNo メソッドを呼び出すと表示されるダイアログボックス>

askYesNo メソッドの引数が 'セーブデータを上書きしますか?' になってるってことは、 引数に指定したメッセージがダイアログボックスに表示されるってこと?
ん、そうだよ。
あと、askYesNo メソッドには第2引数もあって、 ダイアログボックスのタイトルに表示されるメッセージが指定できるようになってるよ。
ダイアログボックスのタイトルって、『確認』って表示されてるののこと?
そ。第2引数のデフォルト値は "確認" っていう文字列になってるから、 第2引数を省略するとタイトルに『確認』って表示されるんだ。
あ、そうなんだ。
で、askYesNo メソッドの戻り値は、 「はい」を選択した時は true で、「いいえ」を選択した時は false になるの。
えっと、じゃあ if タグの exp 属性の || の右側は askYesNo('セーブデータを上書きしますか?') になってるから、 「はい」が選択されたら exp 属性の値が真になって endif タグまでのスクリプトが実行されるんだね。
ん、そういうこと。
逆に言うと、セーブデータがあって、さらに確認ダイアログボックスで「いいえ」を選択したら ifendif の間のスクリプトは実行されないから、セーブされないわけね。
そういうことになるね。
それじゃ、次はデータをセーブする時に実行するスクリプトを見ていくね。
まずは eval タグから。
これって sf.saveinfo.latest っていうシステム変数に、 セーブデータの番号を代入してるんだよね?
sf.saveinfo.latest は最新のセーブデータの番号を記録しとくための変数なんだ。
ここでセーブするデータが最新のデータになるから、ここでセーブデータの番号を代入しとくの。
この変数って、この画像を最新のセーブデータの所に表示するために使うんだよね?

<最新のセーブデータを表す画像>

ん、そうだよ。
じゃ次は save タグでデータをセーブするとこね。
さっき eval タグで sf.saveinfo.latest にセーブデータの番号を代入したから、 place 属性を &sf.saveinfo.latest にしてるんだよね?
そ。まぁこれは別に特別なコトもしてないからわかるよね。
うん。
んじゃ次の call タグね。
これってサブルーチンを呼び出してるみたいだけど、 *update_thumbnail ってどんなサブルーチンなの?
サムネイルの表示を更新するサブルーチンだよ。
スクリプトはこんな感じね。

<サムネイルの表示を更新するサブルーチン>

*update_thumbnail
[if exp="kag.getBookMarkDate((sf.saveinfo.page - 1) * 10 + tf.saveload.thumbnail) != ''"]
; セーブデータがある → サムネイル画像を読み込みます
[image layer="&tf.saveload.thumbnail - 1" page=fore storage="&kag.getBookMarkFileNameAtNum((sf.saveinfo.page - 1) * 10 + tf.saveload.thumbnail)" visible=true]
[else]
; セーブデータが無い → (No Data が見えるように)レイヤを非表示にします
[layopt layer="&tf.saveload.thumbnail - 1" visible=false]
[endif]
[return]

このサブルーチンで、セーブデータがあればサムネイルを読み込んで表示して、 無ければサムネイル用の前景レイヤを非表示にして「No Data」が見えるようにするんだ。
えっと、if タグの exp 属性で getBookMarkDate メソッドを呼び出してるから、ここでセーブデータがあるかどうかをチェックしてるんだね。
ん、そう。
あ、でもセーブデータの番号って sf.saveinfo.latest に記録してるんだから、 引数は sf.saveinfo.latest にすればいいんじゃないの?
それに、セーブした後にこのサブルーチンを呼び出すんだから、 getBookMarkDate メソッドでわざわざセーブデータがあるかどうか調べる必要はないんじゃない?
このサブルーチンがデータをセーブした時にだけ呼び出されるんならそうなんだけどね。
そうじゃないの?
他にもセーブ画面を初期化したりページを切り替えたりする時にも呼び出すの。
その時には最新のセーブデータ以外のサムネイルも更新するから、セーブデータがあるかどうかは判らないし、 sf.saveinfo.latest は使えないんだ。だからこうしてるの。
ふぅん、そうなんだ。
で、セーブデータがあったら image タグでサムネイルを読み込んで表示するわけね。
storage 属性の kag.getBookMarkFileNameAtNum っていうのがサムネイルのファイル名を取得できるメソッドなの?
getBookMarkFileNameAtNum メソッドは正確にはセーブデータのファイル名を取得できるメソッドだよ。
サムネイルを保存する設定にしてる時はセーブデータがサムネイル画像ファイルに埋め込まれるから、 このメソッドの戻り値を storage 属性に指定すればサムネイル画像が読み込めるんだ。
セーブデータが画像になってるってなんか不思議だよね。
余談だけど、サムネイル付きのセーブデータはこうなってるんだ。

<サムネイル付きのセーブデータファイルの構造>

セーブデータファイルの先頭の方にサムネイルの画像データが書き込んであって、 その後にセーブデータが書き込んであるの。
image タグでサムネイル付きセーブデータを読み込むと、 画像データの部分だけが読み込まれるから、画像が表示できるってワケ。
へ〜、こんなふうになってるんだ。
さて、話がちょっとそれちゃったけど、image タグの話に戻るね。
layer 属性の方はわかる?
えっと、1番目〜10番目のサムネイルはそれぞれ0番〜9番の前景レイヤを使ってるから、 tf.saveload.thumbnail - 1 になってるんだよね?
ん、そう。
あと、セーブデータが無かったら、layopt タグでサムネイル表示用のレイヤを非表示にしてるんだけど、これはわかるよね?
うん、おっけーだよ。
それじゃ、サムネイルをクリックした時に実行するスクリプトの方に戻るね。
次はサムネイルの下に表示する日付を更新する部分だね。
*update_date っていうサブルーチンを呼び出してるね。
*update_date はサムネイルの下に表示する日付を更新するサブルーチンで、 スクリプトはこうなってるよ。

<サムネイルの下に表示する日付を更新するサブルーチン>

*update_date
[current layer="&'message' + (kag.numMessageLayers - 14 + tf.saveload.thumbnail)"]
[er]
; メッセージは中央揃えで表示します
[style align=center autoreturn=false]
[nowait]
; セーブデータの日付(getBookMarkDate メソッドの戻り値)を表示します
[emb exp="kag.getBookMarkDate((sf.saveinfo.page - 1) * 10 + tf.saveload.thumbnail)"]
[endnowait]
[return]

最初に current タグで操作対象のメッセージレイヤを設定してるんだけど、 layer 属性がどう指定されてるかわかる?
kag.numMessageLayers - 14 + tf.saveload.thumbnail がレイヤ番号だよね?
そうだよ。
前回メッセージレイヤの番号を表にまとめたから、それを見るとわかりやすいんじゃないかな。
あ、そうだね。
えーっと、1番目のサムネイル用のメッセージレイヤの番号が kag.numMessageLayers - 13 で、 2番目が kag.numMessageLayers - 12 になるから…
x 番目のサムネイル用のメッセージレイヤの番号は?
kag.numMessageLayers - 14 + x かな?
あとは xtf.saveload.thumbnail に置き換えて…
kag.numMessageLayers - 14 + tf.saveload.thumbnail だね!
それから、er タグでメッセージレイヤの文字を消して、 style タグで文字をセンタリングしてるんだけど、これはわかる?
うん。
で、後は文字表示速度をノーウェイトにして、emb タグでセーブデータの日付を表示するわけね。
ここでも getBookMarkDate メソッドを使うんだね。
まぁ getBookMarkDate メソッドはセーブデータの日付を取得するためのメソッドだからね。
あ、そうだよね。
えっと、getBookMarkDate メソッドの戻り値ってどんな感じになってるの?
"年/月/日 時:分" っていう形式の文字列だよ。
例えば 2008 年 1 月 23 日午後 12 時 34 分だったら "2008/01/23 12:34" っていう文字列になるね。
これで十分セーブデータの日時がわかるから、getBookMarkDate メソッドの戻り値をそのまま表示してるんだ。
確かにそれならそのまま表示してもわかりやすいね。
じゃこれで日付を表示するサブルーチンはチェックできたから、 サムネイルがクリックされた時に実行されるスクリプトの方に戻るね。
次は最新のセーブデータを表す画像の表示を更新する部分だね。
これも *update_new っていうサブルーチンを呼び出してるね。
うん。
*update_new はこういうサブルーチンね。

<最新のセーブデータを表す画像の表示を更新するサブルーチン>

*update_new
[if exp="sf.saveinfo.latest !== void && sf.saveinfo.latest > (sf.saveinfo.page - 1) * 10 && sf.saveinfo.latest <= sf.saveinfo.page * 10"]
; 現在のページ内に最新のセーブデータがあればそのデータのサムネイルの所に New を表示します
[layopt layer=11 page=fore visible=true left="&8 + (sf.saveinfo.latest - 1) % 5 * 126" top="&93 + (sf.saveinfo.latest - 1) % 10 \ 5 * 140"]
[else]
; 現在のページ内に最新のセーブデータが無ければレイヤを非表示にします
[layopt layer=11 page=fore visible=false]
[endif]
[return]

なんか最初の if タグの条件がすごい複雑なんだけど…?
これは今表示されてるページに最新のセーブデータがあるかどうかをチェックしてるの。
3つの条件が && でつながってるね。
じゃ1つずつ見ていくね。
まず sf.saveinfo.latest !== void から。
sf.saveinfo.latest って最新のセーブデータの番号だよね?
これって void になったりするの?
sf.saveinfo.latest はセーブデータが1つも無い時に void にしとくんだ。
セーブデータが1つも無い時?
セーブデータが1つも無かったら最新のセーブデータも無いわけでしょ?
だから void にしとくの。
あ〜、なるほど。
じゃあ、sf.saveinfo.latest !== void はセーブデータがある時に真になるってことだよね?
そう。
じゃ次は2つ目の sf.saveinfo.latest > (sf.saveinfo.page - 1) * 10 ね。
これは3つ目の sf.saveinfo.latest <= sf.saveinfo.page * 10 と一緒に考えた方がわかりやすいかな。
えっと、これってつまり sf.saveinfo.latest(sf.saveinfo.page - 1) * 10 より大きくて sf.saveinfo.page * 10 以下かどうか、ってことだよね?
そうそう。
ちなみに今表示されてるページと (sf.saveinfo.page - 1) * 10sf.saveinfo.page * 10 の関係はこうなってるよ。

現在表示されているページ
sf.saveinfo.page の値
(sf.saveinfo.page - 1) * 10sf.saveinfo.page * 10
1010
21020
32030
43040
54050
65060
76070
87080
98090
1090100

え〜っと、例えば1ページ目が表示されてる時だと、sf.saveinfo.latest1〜10 の範囲内かどうかで、 2ページ目が表示されてる時だと、11〜20 の範囲内かどうかってことだよね?
そ。1ページ目と2ページ目に表示するセーブデータの番号って何番〜何番だった?
ん〜…1ページ目が1番〜10番で、2ページ目が11番〜20番…
あっ、sf.saveinfo.latest の値の範囲と同じになってるね。
つまり、sf.saveinfo.latest > (sf.saveinfo.page - 1) * 10 && sf.saveinfo.latest <= sf.saveinfo.page * 10 は、今表示されてるセーブデータの中に最新のセーブデータがあれば真になるってこと。
そっか。それじゃあ、if タグの exp 属性は「セーブデータが少なくとも1個はあって、 さらに今表示してるセーブデータの中に最新のセーブデータがある」時に真になるんだね。
ん。で、条件が真になったら、layopt タグで最新のセーブデータのサムネイルのとこに 「New」 を表示するわけね。
left 属性と top 属性の値がちょっとややこしいよね。
それじゃまず 「New」 を表示する位置から見てみるね。

「New」 を表示する位置>

1ページ目に表示するセーブデータは1番〜10番、2ページ目は11番〜20番だから、 「New」 を表示する位置は最新のセーブデータの番号(sf.saveinfo.latest)の下1ケタの値で決まるよね。
それって sf.saveinfo.latest の下1ケタが 1 だったら (8, 93) の位置に表示して、2 だったら (134, 93) の位置に表示するってこと?
そう。まとめるとこんな感じになるね。

sf.saveinfo.latest の下1ケタの値と 「New」 の表示位置の関係>

sf.saveinfo.latest
下1ケタの値
  left    top  
1  8 93
2134 93
3260 93
4386 93
5512 93
6  8233
7134233
8260233
9386233
0512233

このままだと計算しにくいから、(sf.saveinfo.latest - 1) の下1ケタの値を考えることにすると、こんな感じになるよ。
あと、ついでに (sf.saveinfo.latest - 1) % 5 の値と (sf.saveinfo.latest - 1) % 10 \ 5 の値も書いとくね。

(sf.saveinfo.latest - 1) の下1ケタの値と 「New」 の表示位置の関係>

sf.saveinfo.latest
下1ケタの値
(sf.saveinfo.latest - 1)
下1ケタの値
left(sf.saveinfo.latest - 1) % 5top(sf.saveinfo.latest - 1) % 10 \ 5
10 8 + 0 * 126 = 8   0 93 + 0 * 140 = 93  0
21 8 + 1 * 126 = 134 1 93 + 0 * 140 = 93  0
32 8 + 2 * 126 = 260 2 93 + 0 * 140 = 93  0
43 8 + 3 * 126 = 386 3 93 + 0 * 140 = 93  0
54 8 + 4 * 126 = 512 4 93 + 0 * 140 = 93  0
65 8 + 0 * 126 = 8   0 93 + 1 * 140 = 233 1
76 8 + 1 * 126 = 134 1 93 + 1 * 140 = 233 1
87 8 + 2 * 126 = 260 2 93 + 1 * 140 = 233 1
98 8 + 3 * 126 = 386 3 93 + 1 * 140 = 233 1
09 8 + 4 * 126 = 512 4 93 + 1 * 140 = 233 1
% 演算子と \ 演算子については §1.6 参照。

確かに left8 + (sf.saveinfo.latest - 1) % 5 * 126 で、 top93 + (sf.saveinfo.latest - 1) % 10 \ 5 * 140 になってるね。
じゃあとはexp 属性が偽だった時の処理だけど、こっちはわかるよね?
こっちはレイヤを非表示にしてるだけだね。
そ。exp 属性が偽ってことは今表示してるページに最新のセーブデータが無いってことだから、 「New」 を表示する必要はないよね。
そうだね。
それじゃサムネイルがクリックされた時に実行されるサブルーチンに戻るね。
あと残ってるのは、画面の下の方に表示されるセーブデータ情報の表示を更新する処理だけなんだけど…
ん?
今回は結構長くなっちゃったし、 セーブデータの情報の表示を更新するスクリプトはわりと複雑だから、次回にまわすことにしよっか。
えっ、そんなに複雑なの?
ま、今回見てきたサブルーチンよりはね。
う〜ん、だったら次回にした方がいいかも。
ってワケで、続きは次回ね。


前へ | TOP | 次へ