novelsphere.js Document

Chapter 8 タイトル画面を作ろう

Section 1 外観を作る

タイトル画面に必要な要素

短いノベルならわざわざタイトル画面を作ろうとも思わないかもしれませんが、セーブされたデータをロードしたり、本の表紙のような雰囲気を出すためにはやはりタイトル画面が必要です。そういうわけで、この章ではタイトル画面を作ることにします。

タイトル画面といってもいろいろなタイプがありますが、今回は必要最小限の要素で組み立ててみましょう。具体的には、以下の要素を実装することにします。

背景と効果音で下地を作る

ここまでの章で、「first.ks」「rclick.ks」「main.ks」という3つのファイルに分けてシナリオを書いてきました。

ここではもうひとつ「title.ks」というシナリオファイルを作り、タイトル画面のためのシナリオはそこに書いていくことにします。

「first.ks」を開くと、最後の行は「main.ks」へジャンプするための[jump]タグが書かれているはずですが、この行を以下のように変更してください。

[jump storage=title.ks]

そして新しいファイルを作り、中には以下のシナリオを書きます。そして他のシナリオファイルと同じく「data」フォルダ内の「scenario」フォルダに、「title.ks」という名前で保存してください。

**title.ks

; ========================================
; タイトル画面
; ========================================
[fadeinse storage=se_kamome loop=true time=3000]
[ボワッと表示 file=bg_title time=3000]
[s]

シナリオの中身は、基本的に今まで学んできたことの応用です。

次に、タイトル画面で使う素材を準備します。素材集の中の「bg_title.png」を「bgimage」フォルダに、「se_kamome.mp4」「se_kamome.ogg」の2つのファイルを「sound」フォルダに、それぞれコピーしておいてください。

準備が終わったら、ビルド・再生してみます。カモメの鳴き声をバックに、浜辺の背景が表示されましたか?

Section 2 ボタンを配置する

ボタンとリンクの関係

今回はタイトル画面に「最初からはじめる」と「続きからはじめる」というボタンを配置します。

前章では[link]タグを使ってリンクを作りましたが、クリックされるとどこか別の場所へジャンプするという意味では、リンクはボタンと似ています。なので、「最初からはじめる」「続きからはじめる」のようなメニューがテキストとして表示される形でもよいのなら、タイトル画面はこれまで学んだタグだけで作ることができます。

とはいえ、タイトル画面に表示されるのがただのリンクではやはり味気ないので、今回は画像を使ったボタンを配置してみることにしましょう。

ボタンの取り扱い

novelsphere.jsでボタンを配置するときは、リンクと同じように、メッセージレイヤに配置する必要があります。これまで、メッセージレイヤはテキストを表示するためのレイヤとして使ってきたので、あれ?と思うかもしれませんが、実はボタンを置くときにも使うことを覚えておいてください。

初期状態では、novelsphere.jsには2つのメッセージレイヤ(message0とmessage1)が存在します。今までテキストを表示するために使ってきたのはmessage0レイヤです。もちろんこれを使ってもいいのですが、今回はmessage1レイヤをタイトル画面のボタン配置専用レイヤとして使ってみることにしましょう。

ボタンを配置する ── [button][current][locate]

早速、「title.ks」を以下のように書き換えて、ボタンを配置してみましょう。

**title.ks

; ========================================
; タイトル画面
; ========================================
[position layer=message0 page=fore visible=false]

[fadeinse storage=se_kamome loop=true time=3000]
[ボワッと表示 file=bg_title transtime=3000 x=0 y=0]
[backlay]
[current layer=message1 page=back]
[position layer=message1 page=back opacity=0 top=0 left=0 width=1136 height=640 visible=true]
[locate x=577 y=447][button graphic=btn_newgame target=*newgame]
[locate x=778 y=560][button graphic=btn_continue target=*continue]
[trans method=crossfade time=1000]
[wt]
[s]

*newgame
[cm]
[ボワッと表示 file=white transtime=3000 x=0 y=0]
[fadeoutse time=3000]
[wf]
[jump storage=main.ks]

*continue
[cm]
[ボワッと表示 file=white transtime=3000 x=0 y=0]
[fadeoutse time=3000]
[wf]
[eval o2_exp="alert('ここでロードします')"]

このシナリオを動かすためには新しい画像が必要ですので、以下の画像を素材集から「data」フォルダ内の「image」フォルダにコピーしてから、ビルドしてください。

「最初からはじめる」「続きからはじめる」の2つのボタンが表示されるようになっているはずです。「最初からはじめる」ボタンはもう動きますが、「続きからはじめる」ボタンはまだちゃんとは動きません(「ここでロードします」というダイアログが表示されるだけです)。「続きからはじめる」ボタンを動かすにはロード機能の実装が必要ですが、これについては後述します。

今回はこれまでに学んだことをフル活用していますので、おさらいも兼ねて「title.ks」の構造を順に見ていきましょう。

1. [fadeinse]タグと[ボワッと表示]マクロで下地(背景と効果音)を作る

この部分については前節で説明したので省略します。

2. [backlay]タグでbackページの内容をforeページにコピーする

今回は、ボタンの表示にもトランジションを使うことにします。トランジションの前には必ず[backlay]タグが必要であることは、Chapter 4で学んだとおりです。

3. [current]タグでカレントメッセージレイヤを変更する

current]タグを書くのは今回が初めてです。しかし、テンプレートを使ってシナリオを書きはじめた瞬間から、あなたはこのタグを目にしてきているはずです。このタグにはいったいどのような意味があるのでしょうか?

このタグは「テキストやボタンを描画するとき、どのメッセージレイヤに描画するか?」を指定するために使います。前章までに書いてきたキャラのセリフや地の文のテキストは、message0レイヤのforeページに描画されていますが、これは「main.ks」の最初のほうに[current layer=message0 page=fore]というタグがあるからです。このタグによって、「これからテキストを描画するときはmessage0レイヤのforeページに描画してね」と指示しているわけです。このように、テキストの描画先となるレイヤのことをカレントメッセージレイヤといいます。

この後に学ぶ[button]タグを使ってボタンを描画するときにも、そのボタンはカレントメッセージレイヤに描画されます。ここでは、カレントメッセージレイヤをmessage1レイヤのbackページとすることで、これからのボタンの描画に備えています。

4. [position]タグでmessage1レイヤのbackページの見え方を整える

今回ボタンを置くのはmessage1レイヤですが、このレイヤはまだ一度も使ったことがないので、事前に見え方を整えておく必要があります。メッセージレイヤの見え方を整えるには、41ページで学んだように[position]タグを使います。

ここでは、message1レイヤを「メッセージ枠の不透明度は0(完全に透明)、画面の上端から0ピクセル、左端から0ピクセルの位置に置き、大きさは左右1136ピクセル、上下640ピクセルとし、可視状態とする」という指示を出しています。要するに、画面全体を覆う透明な膜のような状態にしているわけです。

5. [locate]タグで位置を指定する

[button]タグでボタンを描画する前に、どこに描画するのかを指定しなければなりません。そのためには[locate]タグを使います。

[locate]タグにはx属性とy属性を使うことができ、それぞれ、そのメッセージレイヤの左端からのピクセル数と上端からのピクセル数を指定します。

たとえば1つ目の[locate]タグは、これからボタンを描画する時には、このメッセージレイヤの左端から577ピクセル、上端から447ピクセルの位置に描画しろ、という意味になります。

6. [button]タグでボタンを描画する

ようやくボタンを描画する時がやってきました。ボタンを描画するには[button]タグを使います。

graphic属性には、そのボタンのボタン画像のファイル名を指定します。target属性には、このボタンがクリックされたときにジャンプする先のラベル名を指定します。今回は必要が無かったので使いませんでしたが、storage属性も指定することができます。このあたりは[link]タグでリンクを作るときと全く一緒です。

7. [trans]タグと[wt]タグでトランジションを行う

これまでbackページに準備してきた内容を、トランジションを使って目に見える場所(foreページ)に持ってきます。やり方はChapter 4と同じです。

8. [s]タグでノベルの進行を止める

ボタンを描画し終わった後は、そのボタンをクリックしない限りノベルが先に進むことがないよう、[s]タグで進行を止めます。これも[link]タグの使い方と同じです。

9. ボタンがクリックされたときのジャンプ先を用意する

今回描画した2つのボタンは、クリックされるとそれぞれnewgameラベルとcontinueラベルにジャンプするようになっていますので、当然、これらの飛び先を準備しておく必要があります。

ボタンのクリック時、いきなり本編が始まったりロードが行われると無粋ですので、ここではクリック後、効果音をフェードアウトさせながら画面を徐々に白くし、少し間を空けてから本編やロードが始まるようにしました。

ボタン画像の作り方

ここで、今使ったボタン画像を見てみることにしましょう。

先ほど「image」フォルダに入れた「btn_newgame.png」を開いてみます。

「btn_newgame.png」の中身

このように、ボタン画像は3つの画像を横に連結したファイルになっていなければなりません。

これら3つの画像は、左から順に「通常の時の画像」「クリックされた時の画像」「マウスカーソルが上にあるときの画像」を意味します。

Section 3 ロード機能を実装する

情報をロードする ── [load]

最後の仕上げに、タイトル画面の「続きからはじめる」をクリックすると、最後にセーブした状態がロードできるようにしましょう。

「title.ks」の*continueラベル以下の箇所を、以下のように書き換えてください。

*continue
[cm]
[ボワッと表示 file=white transtime=3000 x=0 y=0]
[fadeoutse time=3000]
[wf]
[load place=0]

上記の通り、[load]タグを追加しました。

実は、[load]タグを置くだけの書き方には、ちょっとだけ問題があります。

1回でもセーブをした後なら正常にロードが行われますが、まだ何もセーブしていない状態で「続きからはじめる」をクリックすると、エラーとなってしまうためです。

そこで、先ほどの箇所のシナリオを、さらに以下のように書き換えます。

f*continue
[cm]
[ボワッと表示 file=white transtime=3000 x=0 y=0]
[fadeoutse time=3000]
[wf]
[if o2_exp="ev.save.date[0]"]
	[load place=0]
[else]
	[cm]
	[current layer=message1 page=fore]
	[locate x=577 y=447][button graphic=btn_newgame target=*newgame]
	[locate x=778 y=560][button graphic=btn_continue target=*continue]
[endif]
[s]

[load]タグを[if]〜[endif]で囲いました。[if]タグのo2_exp属性の中身はev.save.date[0]となっています。前章で、o2_exp属性では==を使って変数の中身と値を比べるという話をしましたが、ここには==が使われていません。

種明かしをすると、ここに書かれているev.save.date[0]というのは、ある特殊な変数の名前です。このように==を使わずに変数名だけを[if]タグのo2_exp属性に書いた場合、「そもそもその変数は存在するか?」という疑問文の意味となります。したがって今回書き足した[if]〜[endif]部分は、「もしev.save.date[0]という名前の変数が存在すれば[load]タグを実行し、もし存在しなければ[cm]タグ以下を実行する」という意味となります。

ev.save.date[0]という変数には、セーブスロット0番のセーブされた日時が入っているというルールとなっています。もしセーブスロット0番にセーブデータが入っていなければ、この変数自体が存在しないため、このような[if]タグの書き方によって、セーブスロット0番にセーブを行ったことがあるかどうかを判別できます。


ちなみに、セーブスロット0番が空だった場合に実行される[cm]タグ以下の部分では、ボタンの再描画を行っています。ボタンは一度クリックされるともうクリックを受け付けなくなってしまうため、もう一度置き直す必要があるのです。

変数の種類について

最後に、変数の種類について知っておきましょう。

ノベルをビルド・再生して、以下のことを試してみてください。

……どうでしょうか。最後の[if]タグの箇所で「次は部屋の中でお会いしましょう!」と表示されるはずが、「次は森の中でお会いしましょう!」と表示されてしまいませんでしたか?

実は、セーブ機能を使ってセーブを行うときにはtf.basho変数の中身はセーブされません。ノベルをセーブして一回終了し、起動し直してロードしたとき、画面の状態(背景や立ち絵がどのような状況か)やBGMの状態(どのBGMが鳴っているか)はセーブ時の状態に復元されるのに、肝心のtf.basho変数の中身は復元されず、この変数は存在しないことになってしまうのです。これによって本編最後の[if]タグの動きがおかしくなってしまいました。

3種類の変数を使い分けよう

言うまでもなく、「選択肢で何を選択したか」を記録できないようでは困りますが、この問題は3つある変数の種類を適切に使い分けることで解決できます。

種類 変数名の規則 説明
セーブ変数 f.で始まる セーブの際、セーブデータと一緒に保存されます。 ロードの際、セーブデータから復元されます。
システム変数 sf.で始まる セーブを行わなくても、随時保存されます。 ノベルの起動時、最新の状態が自動的に復元されます。
一時変数 tf.で始まる セーブの際に保存されず、ロードの際に復元されません。 ノベルを終了すると消えてしまいます。

各変数は、名前の頭に付ける文字列によって区別されています。今まで使ってきたtf.bashoは、tf.から始まる名前なので、実は一時変数であったことがわかります。上の表のとおり、一時変数はセーブもロードもされず、ノベルを終了すると消えてしまうので、実は選択肢の選択結果を保存するためには不適切な変数だったことになります。

では、選択肢の選択結果はどの種類の変数に保存するべきでしょうか? この情報はセーブと共に保存され、ロードの際に復元されてほしい情報なので、セーブ変数を使うのが適切です。

ちなみにシステム変数は、本編とは関係のない、ノベル全体に関わる情報、例えば「1回でもエンディングを見たか?」という情報(いわゆるクリアフラグ)の保存に使われます。

そういうわけで、「main.ks」内のtf.basho変数を、全てf.basho変数に置き換えてみてください。その上で、本節の冒頭の実験を繰り返してみてください。今度は最後の[if]タグ部分が正常に動作するはずです。

特殊な変数の種類について

ロード機能の話からはそれてしまいますが、上記3つの他にも、novelsphere.jsには特殊な種類の変数が存在します。

種類 変数名の規則 説明
環境変数 ev.で始まる そのノベルや、ノベルをプレイしている環境の情報が書き込まれています。
マクロ変数 mp.で始まる マクロ内でのみ使える変数です。 そのマクロに渡された属性の値が書き込まれています。

これらの変数はいずれも、値を書き込んではいけない、読み出し専用の変数で、[if]タグなどを使って中身を比較するときにのみ使います。

環境変数は、ev.から始まる名前を持っており、novelsphere.jsによって様々な変数があらかじめ用意されています。

たとえばev.ua変数には以下のような文字列が書き込まれています。これは、このノベルをプレイしているブラウザの情報です。

ev.ua変数に書き込まれている値の一例:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_1) AppleWebKit/536.25 (KHTML, like Gecko) Version/6.0 Safari/536.25

また、先ほど使ったev.save.date[0]も環境変数の1つです。この他の環境変数については、ドキュメントに詳細が記されています。

マクロ変数は、マクロ内([macro]〜[endmacro]の間)でのみ読み出しが行える変数で、そのマクロに渡された属性の値の情報が、文字列として書き込まれています。

これを使って、たとえば以下のようなシナリオを書くことができます。

@macro name=test
	[if o2_exp="mp.zokusei=='atai'"]
		zokuseiという属性にataiという値が指定されました。
	[else]
		zokuseiという属性には、atai以外の値が指定されました。
	[endif]
@endmacro

[test zokusei=atai]

このシナリオを実行すると、「zokuseiという属性にataiという値が指定されました。」というテキストが表示されます。これは、マクロ変数のルールにしたがって、mp.zokuseiという変数に、ataiという文字列が書き込まれているためです。