カテゴリ:NScripter( 117 )
オートモードの待ち時間を文字数によって変化
 自分はあまり使わないのですが、使う人もけっこういるみたいなので、オートモードの待ち時間を字数によって可変にしてみました。
 参考にしたのはエロゲのシステムまわりを考える(A・H・OP)スレ まとめ(貴史たま+ME)HPのシステムカスタマイズサンプル+オプション。
 以下、スクリプト(サブルーチン)。

*text_setAutoTime
gettext $TextTemp1
len %TextTemp1,$TextTemp1
mov %TextTemp2,(%gTextAutoTimeByChar - %gTextSpeed) * (%TextTemp1 - %TextAutoLastCharNum) / 2
if %TextTemp2 < 0 mov %TextTemp2,0
mov %TextAutoLastCharNum,%TextTemp1
ispage %TextTemp1
if %TextTemp1 == 0 add %TextTemp2,%gTextAutoTimeByLine
if %TextTemp1 == 1 mov %TextAutoLastCharNum,0:add %TextTemp2,%gTextAutoTimeByPage
if %TextTemp1 == 1 && %TextTemp2 < %gTextAutoTimeMin mov %TextTemp2,%gTextAutoTimeMin
automode_time %TextTemp2
return

 まず使用している変数についてですが、
 %gTextAutoTimeByCharは1文字ごとの待ち時間
 %gTextAutoTimeByLineはクリック待ち時に追加される待ち時間
 %gTextAutoTimeByPageは改ページ待ち時に追加される待ち時間
 %gTextSpeedはテキスト表示速度
 %TextAutoLastCharNumは前回表示した文字数(改ページごとにクリア)
 %gTextAutoTimeMinは改ページ待ち時の最小待ち時間
 %TextTemp1と%TextTemp2は汎用変数
 となっています。ちなみに接頭辞gはグローバル変数を示しています。
 処理としては
1.表示中の文字数を取得
2.(1文字ごとの待ち時間-テキスト表示速度)×(表示中文字数-前回表示した文字数)を計算
3.表示した文字数を前回表示した文字数として保存
4.通常のクリック待ちなら、それ用の待ち時間追加
5.改ページ待ちなら、それ用の待ち時間追加&前回表示した文字数をクリア
6.改ページ待ちで、かつ計算した待ち時間<最小待ち時間なら最小待ち時間を採用
7.待ち時間を設定
 という流れになってます。
 計算式にテキスト表示速度を加味しているのは、テキスト表示速度が速いときは待ち時間が短くなって、遅いときは長くなってしまうから。あとテキスト表示速度と1文字あたりの待ち時間の大小によっては計算した待ち時間が0以下になってしまうことがあるので、その時は0にするようにしています。
 表示した文字数周りの処理を最後にまとめて、流れをすっきりさせても良かったんですが、使用する変数が多くなるのと行数が長くなるのが何となく嫌でこんな感じなりました。改ページ待ちに関する処理も1行にまとめようと思えばまとめられるのですが、それはそれで一行が長くなりすぎますので……。

 基本的な処理は出来たので、あとは待ち時間を変更できるオプション項目を作らなくては。

【追記】
 WithoutLimitさんに捕捉されてるー。
 メニュー下部にある「▼オートモードの待ち時間の話」のリンクから飛べるのですが、捕捉というか補足といった内容で、自分の書いた内容に足りない部分をいい感じに補ってくださっているので直リンクしたいのだけれども、txtってあたりがなんかそのうち消されてしまいそうな雰囲気なのでやめときます。どこかに落ち着いたら改めてリンク貼ろうかしら。
 あと、WithoutLimitさんのスクリプトのお陰で気付いたことを一つ。書き忘れていたのですが、自分の書いたスクリプトでは%gTextAutoTimeByChar(1文字ごとの待ち時間)は、実際に設定したい時間/2の値を格納してました。なぜならlenで得られるのは文字数じゃなくてバイト数で、全角文字は1文字2バイトだからです。どうせ計算する時に2で割るんだからあらかじめ割っとけーと思ってそうしていたのですが、%gTextSpeedの値は1文字換算なので、これだとおかしくなるんですよね。なので計算式を
(%gTextAutoTimeByChar - %gTextSpeed) * (%TextTemp1 - %TextAutoLastCharNum) / 2
 に書き換え、%gTextAutoTimeByCharは実際に設定したい時間のままにしました。
 ああ、あともう一つ書き忘れ。gettextで得られるテキストにはルビ情報も含まれます。なのでこの計算式だとルビがある場合に数文字分のズレが生じるはずですが、ルビを振ってある文章は振ってないものより多少読むのが遅くなるんじゃないかなーと思ったのでそのままにしてあります。
[PR]
by lyricist_m | 2009-02-15 06:14 | NScripter
メニューバーのWAVEONとWAVEOFFについて
 この設定が適用されるのはwave・dwave命令系のみ。bgmは普通に再生されます。
 デフォルトの設定で
 ボリューム
    ┣オン(WAVEON)
    ┣オフ(WAVEOFF)
    ┗ボリューム
 となっているのが紛らわしいですね。

 あと、wave命令をstopで止められることを発見。まぁ、wave命令なんて使いませんけど。
[PR]
by lyricist_m | 2009-02-14 03:01 | NScripter
savegame
 ふと気になったので、savegame命令周りについて調べてみました。

 savegame命令は引数として2147483647~-2147483648までの値を受け取れます。つまり、最大で約43億個のセーブデータを作れます。マイナスの値を指定した場合、ファイル名はsave-1.datのようになります。
 全ての変数に何の値も格納していない素の状態の場合、セーブファイルの容量は約36KB。文字変数に値を格納すると容量が増えます。数字変数の場合は、格納する値の大小によらず容量は一定です。
 また、セーブファイルの容量を決定する要因の一つとして、グローバル変数があります。グローバル変数は基本的に変数番号100200番からですが、value命令によって変えることが可能です。value命令によってグローバル変数の開始番号を500、1000、2000……と後にずらせばずらすほど、セーブデータに格納するべき普通の変数の値が増えるので、その分セーブデータの容量も増加し、それとは反対にgloval.savの容量が減少します。

 errorsave命令のセーブ番号が999なので1000以上はセーブできないんじゃないかという思い込みがあったのですが、思いのほか沢山セーブできるようですね。さすがに43億個をフルに使うことはないでしょうが。セーブデータだけでテラに到達しますしね。

[追記]
 間違っていた点があったので修正。
[PR]
by lyricist_m | 2009-01-27 15:36 | NScripter
セーブデータの有効活用
 シナリオの節目節目で自動的にセーブしておき、プレイヤーがセーブする際に同時に節目のセーブデータも保存。ロードする際に「セーブしたところから始める」「節目から始める(その日の朝から、一つ前のブロックから、など)」と表示すると便利じゃないかな? と思った。
 自分はセーブデータからゲームを始める際、まずは回想して現在の状況を確認してから再開するので、上のようにすると手間が省けそうだな、と。
[PR]
by lyricist_m | 2009-01-26 01:33 | NScripter
シナリオプレイヤーについて考える。
 シナリオの構造について、以下の様に考えました。
 点線以下の部分は同様の要素が複数あると思ってください。

Scenario
┣Part
┃ ┣Scene
┃ ┃ ┣Block
┃ ┃ ┃ ┣Route
┃ ┃ ┃ ┃ ┣Label
┃ ┃ ┃ ┃ ┗Flags
・  ・  ・  ・
・  ・  ・  ・
・  ・  ・  ・

 シナリオ全体をまとめるScenario要素の一つ下にPart要素があります。これはシナリオプレイヤーでは考慮しない要素で、人間がシナリオを管理する際に用います(ゲーム内1日分とか)。
 その下のScene要素はシーン回想をする際に再生対象となる内容で、実際にテキストが含まれるのはその更に下、Block要素になります。
 Block要素は1つ以上のRoute要素を持っています。これが分岐にあたります。Route要素にはLabel要素とFlags要素があります。Label要素は次のBlock要素を指定するもので、Flags要素には条件が複数格納されています。Flags要素の条件が全て真ならLabel要素に飛ぶ、という感じになります。

 大方こんな感じなのですが、あと問題は選択肢をどうしようかなってところです。今考えてるのはBlock要素をテキストブロックと選択肢ブロックの2タイプに分けるとか。
[PR]
by lyricist_m | 2009-01-25 15:01 | NScripter
先送り機能について考える。
 シナリオを先送りできるということは、シナリオの先を取得できていて、尚且つ任意の場所にジャンプできないといけない。ということは、各ページごとにラベルが必要なのでは。
 どうもNScripterでは実装が無理な気がする。やるなら吉里吉里だろうか。

 あと、先送りが出来るということは、自分が作った回想における巻き戻し機能じゃなくて、完全な意味での巻き戻しが出来るのではないだろうか。前に飛ぶか後ろに飛ぶかの違いしかないわけだから。

 まず思いついたのは、シナリオファイルの内容を一旦配列か何かにぶち込んで、そこから1行ずつ読み出して実行、ジャンプする時は配列の添え字を変える、という方法。
 ただ、これって既読判定とかどうするんだろう。シナリオブロックごとに既読判定とか?
 うーん、やっぱり難しそう。
[PR]
by lyricist_m | 2009-01-25 03:29 | NScripter
巻き戻し機能
 巻き戻し機能のサンプルを作成しました。Delusionist's Utopiaから落とせます。
 以前作った循環バッファ+回想のサンプルに手を入れてさっくり作成。そんなに手間はかかりませんでした。変数番号がバッティングしていることに気付かなくて「なんでバグるんだ?」と頭を抱えることはありましたが。

 やってることを簡単に説明します。
 まずは背景と立ち絵のファイル名を格納する変数を用意します。立ち絵は左、真ん中、右と場所ごとに準備。次にbg、ld、cl命令を上書きして、表示中のファイルを保存できるようにします。そして改ページの時にそれらをログと一緒に保存すればOK。あとは回想モード中に、ログと一緒にファイル情報を取り出して表示すれば、目的が達成できます。

 やはりそんなに難しくありませんでしたね。今度からうちのゲームに実装しようかな。
 次はシナリオプレイヤーに関する話でも。
[PR]
by lyricist_m | 2009-01-24 23:25 | NScripter
構想
 現在構想中のシステムとか製作支援ツールとかの話。
 ノベルゲーム製作全般に関する話で、NScripterに限ったものではないのですが、一応NScripterカテゴリにしておきます。

 とりあえず全部箇条書きにするとこんな感じ。
・巻き戻し機能
・先送り機能
・演出指定GUIツール
・シナリオ分岐指定GUIツール&シナリオプレイヤー

 以下、各項目の説明。長くなると思うので畳みます。

More
[PR]
by lyricist_m | 2009-01-23 11:26 | NScripter
オーバーロード可能?
 お久しぶりです。
 最近は就職活動などで忙しく、あまりNScripterを触れていないのですが、個人的大発見をしたのでそれを書き留めておこうかと思います。

 きっかけはsenzogawaさんの書かれたちょっとだけ言語ハック――senzogawaのNな日々でした。
 この記事の「4.getparamを切り替えることで別の引数をとるようにできる。」という項を見て、getparam命令の実装の特殊性から「頑張ったらオーバーロードできるんじゃないか?」と思い、試しに頭に浮かんだスクリプトを書いてみました。結果的にそのスクリプトはエラーが発生したのですが、それをなんとかやりくりしているうちに、実行可能なスクリプトにたどり着きました。それが以下のものです。


*define
defsub a
game

*a
getparam %0
getparam $0,%1
%0、$0、%1@
return

*start

a 1"あいう",1
end


 このスクリプトの特徴は以下の2点です。

1.getparamが2回に分けて実行されている
2.命令の呼び出し方が特殊

 本来ならgetparamを2回実行するとエラーになるのですが、a 1"あいう",1という特殊な命令の呼び出し方でそれを回避しています。
 1回目のgetparam命令ですが、内部動作的には「数字として読めるところまで読む。もし数字以外が出てきたら、今まで読んだ値を引数として渡す」といったことをしているようです。そのため、1の次に「"」(ダブルクォーテーション)を読んだところで処理を止めて、%0に1を格納していると思われます。
 そして2回目のgetparam命令ですが、この時読み出されるのは、残った「"あいう",1」の最初のダブルクォーテーションからです。ここがポイントで、普通の命令の呼び出し方だとここでは先頭に「,」が含まれて「,"あいう",1」となってしまい「いきなりコンマがきてる、引数を一つ忘れてるよ!」とNScripterは判断してエラーになってしまうわけです。

 かなり強引ですが、以上のようにすることでgetparam命令を複数回に分けて呼ぶことが可能です。今回は第一引数に数字を指定していますが、文字列を指定した場合の方がたぶん見やすいです。「a "あいう""えお"」といった呼び出し方が出来るので。とにかくポイントは「getparam命令の値の読み込みを途中で止める」ということ。その点を押さえればgetparamを3回以上に分けて呼ぶことができます。まぁ、2回呼べば十分だと思いますが。
 使い方としては、第一引数に引数の総数を指定して、引数の個数ごとに呼び出すgetparamを変える、とか。

 時間が無いのにぱぱっと書いてしまった上に見直しもしていないので突っ込みどころがありそうですが、上記スクリプトが動作することだけは確実なので、興味がある方は自分で弄ってみてください。
[PR]
by lyricist_m | 2008-10-17 19:28 | NScripter
getmouseoverサンプル
 サクッと作ってみました。

;mode320,value100
*define
deletemenu
globalon
game

*start
setwindow 8,8,15,10,18,18,-1,0,0,1,1,#999999,0,0,319,239

mov %0,30
mov $0,"■"

mov %10,31
lsp %10,":s#FFFFFF#FF0000スタート",128,111
print 1
btndef clear
spbtn %10,%10
*start_loop
btnwait %11
if %11 == %10 csp %10:resettimer:goto *start2
goto *start_loop

*start2
if %0 <= 0 gettimer %0:goto *result
rnd %1,320 - 20
rnd %2,240 - 20
lsp %0,":s#FFFFFF" + $0,%1,%2
print 1

btndef clear
spbtn %0,%0
getmouseover %0,%0

*loop
btnwait %10
if %10 > 0 lsp %0,":s#FF0000" + $0,%1,%2:dec %0:goto *start2
goto *loop

*result
if %100 == 0 mov %100,10000000
if %100 > %0 mov %100,%0
今回の結果:%0ミリ秒
ベストタイム:%100ミリ秒¥

mov %10,31
lsp %10,":s#FFFFFF#FF0000コンティニュー",104,111
print 1
btndef clear
spbtn %10,%10
*result_loop
btnwait %0
if %0 == %10 csp -1:goto *start
goto *result_loop


 次々に表示される■にマウスカーソルを合わせていくゲームです。
 スタートをクリックするとゲーム開始。30個カーソルを合わせるとゲームクリア。今回の結果とベストタイムを表示します。

 ボタンにカーソルが合ったら特定のスプライトを表示、なんてことがしたいならexbtnで事足りますが、もっと複雑なことがしたいならgetmouseoverを使うべきなんでしょうね。今は用途が思いつきませんが、そのうち使ってみたいです。
[PR]
by lyricist_m | 2008-08-19 18:04 | NScripter