戻る:アニメ編(その九) |
予期しない一時描画の抑制
今回は JPY ファイルの文法の話ではなく、シナリオ内での使い方に関する話題を扱います。
特に背景継承を利用したカットイン型の JPY ファイルで起きることですが、画面を再描画した際に、既に済んでいるはずの一時描画が繰り返し行われてしまうことがあります。
これを防ぐにはどうするか、CardWirth の背景描画の仕様を確認しながらその対策を考えます。
サンプル
今回は単に JPY ファイル単体で生ずる問題でないので、実際のシナリオの形態に寄せてサンプルを作成しました。
具体的には、こんなエリアです。
相変わらず冒険者の宿ですが・・・右側にメニューカードが二枚設置されています。
まず、下側の「画面再構築」メニューは、文字通り「画面の再構築(背景・セルを読み込み)」コンテントを呼び出すだけです。ふつう、このコンテントをユーザーが手動で呼び出すことはありませんが、ここでは検証の便宜のために特に設けました。
次に、上側の「周囲の様子」メニューは、「生命感知」キーコードを受け取ると JPY ファイルを用いて一時描画を行います。どういう動作をするかは、実際に見ていただくのが早いでしょう。
いささか茶番じみてはいますが「宿にひそむ何者かの気配を察知した」という表現だとご理解ください(宿に隠れひそんでいるキャラといえば彼女に決まっています)。そのコンテントツリーは以下のようになっています。
JPY ファイルを実行する背景変更コンテントの設定内容はこんな感じです。
背景には「背景継承」を選択し、セルとして JPY ファイルを配置しています。一時描画の実行後に元の画面を描画する必要がありますので(カットイン型)、このセルにはマスク有を設定します(アニメ編(その二)参照のこと)。
JPYソースを表示
1: [init] 2: 3: [Mask] 4: width=632 5: height=420 6: color=$000000 7: visible=0 8: transparent=0 9: paintmode=3 10: alpha=63 11: animation=1 12: wait=100 13: 14: [frame000] 15: fileName=EFV_NewType.bmp 16: savecache=1 17: visible=0 18: 19: [frame001] 20: width=158 21: height=420 22: loadcache=1 23: visible=0 24: clip=0,0,158,420 25: paintmode=3 26: alpha=191 27: animeclip=0,0,158,420 28: animation=3 29: animeposition=0,0 30: wait=50 31: 32: [frame002] 33: width=158 34: height=420 35: loadcache=1 36: visible=0 37: clip=158,0,158,420 38: paintmode=3 39: alpha=191 40: animeclip=158,0,158,420 41: animation=3 42: animeposition=158,0 43: wait=50 44: 45: [frame003] 46: width=158 47: height=420 48: loadcache=1 49: visible=0 50: clip=316,0,158,420 51: paintmode=3 52: alpha=191 53: animeclip=316,0,158,420 54: animation=3 55: animeposition=316,0 56: wait=50 57: 58: [frame004] 59: width=158 60: height=420 61: loadcache=1 62: visible=0 63: clip=474,0,158,420 64: paintmode=3 65: alpha=191 66: animeclip=474,0,158,420 67: animation=3 68: animeposition=474,0 69: wait=50 70: 71: [Luthia] 72: fileName=..\card\Cast_ルティア.bmp 73: savecache=2 74: visible=0 75: colormap=1 76: mask=1 77: 78: [Luthia_00] 79: loadcache=2 80: visible=0 81: paintmode=3 82: alpha=19 83: animation=3 84: animeposition=111,94 85: wait=100 86: 87: [Luthia_01] 88: loadcache=2 89: visible=0 90: paintmode=3 91: alpha=38 92: animation=3 93: animeposition=111,94 94: wait=100 95: 96: [Luthia_02] 97: loadcache=2 98: visible=0 99: paintmode=3 100: alpha=57 101: animation=3 102: animeposition=111,94 103: wait=100 104: 105: [Luthia_03] 106: loadcache=2 107: visible=0 108: paintmode=3 109: alpha=76 110: animation=3 111: animeposition=111,94 112: wait=100 113: 114: [Luthia_04] 115: loadcache=2 116: visible=0 117: paintmode=3 118: alpha=95 119: animation=3 120: animeposition=111,94 121: wait=500 122: 123: [Luthia_05] 124: loadcache=2 125: visible=0 126: paintmode=3 127: alpha=76 128: animation=3 129: animeposition=111,94 130: wait=100 131: 132: [Luthia_06] 133: loadcache=2 134: visible=0 135: paintmode=3 136: alpha=57 137: animation=3 138: animeposition=111,94 139: wait=100 140: 141: [Luthia_07] 142: loadcache=2 143: visible=0 144: paintmode=3 145: alpha=38 146: animation=3 147: animeposition=111,94 148: wait=100 149: 150: [Luthia_08] 151: loadcache=2 152: visible=0 153: paintmode=3 154: alpha=19 155: animation=3 156: animeposition=111,94 157: wait=100 158: 159: [Luthia_09] 160: loadcache=2 161: visible=0 162: paintmode=3 163: alpha=0 164: animation=3 165: animeposition=111,94 166: wait=100
さ、これで準備完了です。問題はここからです。
セーブ&ロード
CardWirth にはシナリオの途中でデータをセーブする機能がありますが、一時描画を含む JPY を背景に含んだ状態でセーブしてから再開すると、再開時にまた一時描画を実行してしまう、という問題があります。
先ほどのサンプルで確認してみましょう。
この動画で起きたことを順番に列記しますと
- 操作:生命感知を使う
- キーコードを捕捉して JPY ファイルによる一時描画のエフェクトが実行される
- 操作:セーブする
- 操作:再開する
- 一時描画がまた実行される
という具合ですが、再開時に一時描画が行われてしまっては困るのです。この時点では「生命感知」を使っているわけではないのですからね。
これはカットイン型の一時描画を行う限り、常に付きまとう問題といえるでしょう。
解説:背景継承と画面の再構築
なぜこのような現象が起きるのかというと、それは CardWirth の背景描画の仕組みと関係があります。
ヘルプファイル editor128.chm の〈画像描画に関する仕様〉のページにはその仕組みの説明がありますが、例によって一度読んだくらいでは分からないような書き方なので、少し噛み砕いて説明することにします。
CardWirth の背景画面は、ベースとなる背景画像と、それに重なるセル画像の組み合わせで成り立っていることは既にご存知の通りですが、CW エンジンはこの情報をファイルリストという形で保存しています。
そしてこれとは別に、画面に表示するためファイルリストを元に裏画面 A というものを作成します*1。これは上記のファイルリストから作成された一枚絵、実際に CardWirth のプレイ画面に表示される背景画像と考えてよいでしょう。
模式的に図示すると、こんな感じです。
図の左がファイルリストで、内部的に CardWirth は全ての画像の情報を保持しています。
一方、右側の裏画面 A はプレイ画面に表示するための一枚絵(ビットマップ)で、ファイルリストを全て合成した形の画像としてメモリ上に保持しているわけです。
この状態で、「背景継承」を指定して背景変更コンテントを行うと、既存のファイルリストはそのままに、新しく指定したセルが追加されます。このサンプルの場合は生命感知エフェクトの JPY ファイルが追加されるわけですね。
ただし、エンジンの動作としてはファイルリストから画像を再生成するのではなく、裏画面 A に付け足す形で描画を行います。模式図で示します。
この右側の状態が「背景変更コンテントによる」一時描画の状態なんです。
そして、一時描画は裏画面 A に影響を与えないので(だから「一時」描画なのです)、コンテント終了後の状態はこうなります。
我々が見ているのは裏画面 A を元に作成されるプレイ画面なので、一時描画が終わった後は JPY ファイルのことなんか忘れてしまうのですが、エンジン内部ではまだファイルリストに残っているんですね。
そして画面の再構築を行うと・・・それは画面の再構築コンテントだけでなく、セーブ&再開でも行われるのですが、CW エンジンはこの時点でのファイルリストを合成して背景画像を作り出す作業を行いますので、中に含まれる一時描画が再実行されてしまうのです。これこそがまさに不具合の原因です。
さらにマズいことに、同じことを繰り返すとその都度ファイルリストに JPY ファイルが蓄積されてしまいます。つまり「生命感知」を二回やってから画面の再構築を行うと、ファイルリストに JPY ファイルが二回配置されてしまうため、エフェクトが二回表示されるのです。
動画で確認してみましょう。
先の説明どおり、画面再構築時にエフェクトが二回表示されましたね。
今は同じ内容の一時描画を二回繰り返しましたが、同じカットイン形式の JPY を何種類か実行した場合でも、やはりそれぞれが実行した回数だけファイルリストに蓄積されることになります。
とはいえ、原因が分かれば手の打ちようはあります。
解説:再描画の抑制
たぶん多くの方が思いつかれる解決策はフラグ参照を用いてセルの表示を制御することでしょう。
背景変更コンテントなどで設定するセルは、フラグの値によって表示させたりさせなかったりする機能があります。値が TRUE/表示/ON/有/可/済みなどの真値を意味する値であれば当該のセルを表示させ、反対に FALSE/非表示/OFF/無/不可/まだなどの偽値を意味する値なら表示させないように出来るわけです。
このサンプルの場合ですと、JPY を実行する直前だけフラグを真値に設定し、JPY 実行直後は偽値に変更するようにします。すると画面の再構築時には当該セルが表示されないため、不要な一時描画を避けることが出来る、というわけです。
まず、エフェクト制御用のフラグを作ります。
そして、これをコンテントツリーに組み込みます。
背景変更コンテントの前後でフラグの値を変更、一時描画の間だけ値が真値になるように設定します。
そして、背景変更コンテントで当該のセルにフラグ参照を設定します。
これで問題のセルは画面再構築時に表示されないようになるはずです。
動画で確認してみましょう。
画面再構築時に不要な一時描画が行われるのは防ぐことができました、はずなのですが・・・
予期せぬ現象
前項の対策は一見うまく行ったように見えるのですが・・・そう簡単にいかないのがエフェクトブースターの厄介さ、この程度でどうにかなるくらいなら、わざわざこんなページは書きません。
論より証拠、このサンプルで「生命感知」→「画面の再構築」→「生命感知」と行うと、どうなるでしょう。
二回目の「生命感知」の際に、エフェクトが二回表示されてしまいました。
ほとんどの方がこんな結果を予測できなかったと思います。
煩瑣になりますので詳細な検証プロセスの再現は避けますが(興味がお有りの方は同じようなサンプルを作って試して下さい)、どうもこういう事態が起きているようです。
- フラグ:真
- 「生命感知」実行1
- 一時描画実行
- (ファイルリストに JPY ファイル追加)
- フラグ:偽
- 画面の再構築
- フラグが偽なのでファイルリストにある JPY ファイルは一時描画しない(※)
- フラグ:真
- 「生命感知」実行2
- フラグが真になったので※分の一時描画実行
- 「生命感知」2の分の一時描画実行
- (ファイルリストに JPY ファイルをさらに追加)
- フラグ:偽
ここで奇妙なのは※印をつけた、ファイルリスト内にある JPY ファイルの挙動です。
参照フラグが偽値であった際、描画そのものがなされないというのではなく、「存在するけどフラグによって見えないようにしているだけ」とでもいう状態になっているようなのです*2。
その表示待機状態とでもいうべき JPY ファイルは、次の表示可能なタイミング(この場合は「生命感知」実行2)において一時描画を行ってしまい、そのために余計なエフェクトが発生してしまう、こんな状況だろうと思われます。
※なぜそんな奇妙な挙動をするのかは聞かないで下さい。私にもよく分かりません。
いずれにせよ、この問題を解決するにはフラグ制御だけでは不足で、同時に同じ JPY ファイルを二度以上読み込まないようにすることがあわせて必要なようです。
解決策
その解決方法は、たとえばこのようになるでしょう。
- 読み込んだかどうかを追加のフラグで管理し、二度目以降は「背景変更」コンテントではなく、ただ「画面の再構築」だけを行うようにする。
- 当該の JPY ファイルを都度「背景変更」で追加するのではなく、あらかじめセルの背景に読み込んでおき、ただフラグの ON(+画面の再構築)/OFF だけによって表示を制御する。
一番目の解決法は、たとえばこんな感じです。
新たに「エフェクト読み込み」というフラグを導入し(初期値は「まだ」)、これの値によって動作を変更するようにします。
- フラグの値が「まだ」の場合は背景変更コンテント(背景継承)によって JPY ファイルをファイルリストに追加する→処理の最後に値を「済み」にする
- フラグの値が「済み」の場合は画面の再構築コンテントを用いて一時描画を行い、それ以上 JPY ファイルを読み込まないようにする。
ただしこの場合、他の理由で背景変更コンテントが行われたり、あるいはエリアを移動した際には JPY ファイルの読み込みが行われていない状況になりますので、適切にフラグの値をリセットする必要があります。
一方、二番目の解決法ではキーコードイベントの発火時にではなく、シーンビューであらかじめエリアの背景に対して JPY ファイルを設定しておきます。
そして「生命感知」のキーコードイベントで(背景変更ではなく)画面の再構築を行います。
それでは動画で確認してみましょう。どちらでもかまいませんが、ここでは二番目の対策を施したものを実行してみます。
画面の再構築時にも、そして二度目の「生命感知」実行時にも余計なエフェクトが表示されませんでしたね。この対策は有効だといえるでしょう。
補足
今回は少し厄介な、そして根本的な対策を見つけることの難しい(しかしよく起きがちな)問題を取り上げました。
この件が厄介なのは、ただ JPY ファイル単体で完結する不具合ではなく、周囲のコンテントとの兼ね合いで起きているということにあります。
このサンプルでは極めて単純化した形で示していますが・・・つまり、背景変更や画面の再構築が「生命感知」のキーコードイベントでしか起きない、そういうエリア構造になっているわけですけれども、実際のシナリオではもっと他の要素が関係して画面構成が変化することがありえるわけです。
つまり、他のキーコードイベントによるエフェクトが関係するかもしれませんし、ストーリーの進行によって背景画像を描き換えるということも起きるでしょう。
現実のシナリオ制作においてはそうした複合的要因を考慮し、対策を施さなければいけないからです。
ですから、この種のカットイン的エフェクトを多用するシナリオでは場当たり的に問題を解決することが難しいといえます。
どのタイミングで背景変更するか、あるいは画面を再構築するか、ある程度の見通しを持った上で(できれば適切な設計を行ってから)シナリオを制作する必要があるでしょう。
まとめとおさらい
今回のポイントは下記の通りです。
- 背景変更コンテント等で「背景継承」を用いると、背景画像のファイルリストが増えていく
- 「背景変更」と「画面の再構築」では内部的に挙動が異なる
- 「背景変更(+背景継承)」は既にある裏画面 A を元に描画する
- 「画面の再構築」はファイルリストから裏画面 A を再生成し、描画する
- 「画面の再構築」時の不具合を避けるには以下の二つに留意する
- 一時描画を含む JPY ファイルはフラグを用いて動作させないようにする
- 同一の JPY ファイルを同一の画面に二度以上読み込まないようにする
以上を持ちましてアニメ編は終了といたします。
次回は JPY ファイル開発の話題として、Microsoft Excel を用いて JPY ファイルを生成する方法を説明します。
戻る:アニメ編(その九) |
※サンプルに用いた効果音(フレクサトーンによる閃き音)は効果音ラボ様の素材を使用いたしました。
*1 実際には裏画面 A に各種カードの画像が合成されて裏画面 B が作成され、プレイ画面として表示されます
*2 おそらくは裏画面 A 上に
コメント
最新を表示する
NG表示方式
NGID一覧