@pasta_sakura_script
句点でひと呼吸、読点で小さな間――台詞の「間」を自動で刻んでくれるのが @pasta_sakura_script ですわ。長い台詞を読みやすい位置で折り返す力まで、備えておりますのよ。
では私も、……読点ごとに、……間を置いて、……お話しいたしましょうか。
まあ、それでは間の取りすぎですわ! 仕組みを知って、ほどよい話し方に整えてくださいまし。
@pasta_sakura_script は、台詞のテキストにウェイトタグ(\_w[ミリ秒])を挿入してさくらスクリプトへ変換するモジュールである。budoux による日本語の分かち書きを使った自動改行(\n または \n[N] の挿入)も提供する。
local SAKURA_SCRIPT = require "@pasta_sakura_script"
モジュールのメタデータ: _VERSION = "1.0.0"、_DESCRIPTION = "Sakura Script wait insertion module for natural conversation tempo"
act:talk との関係
アクション行の台詞や、シーン関数で act:talk(...) に渡したテキストは、さくらスクリプトを組み立てるときに、発言したアクターの表を引数にしてこのモジュールの talk_to_script で変換される。アクターの表は pasta.toml の [actor."名前"] に書いた値を持つ(CONFIG.actor["名前"] と同じ表)。アクション行に登録していないアクター名を書いた行は、名前だけを持つ表で変換されるため、[talk] の値と実装の既定値を使い、自動改行はしない(登録していないアクター名)。
ふつうのゴーストでは、このモジュールを直接 require する必要はない。ウェイトの長さや自動改行は、pasta.toml の [talk] セクションと [actor."名前"] のキーで調整する(pasta.toml での設定・pasta.toml での budoux 設定)。
talk_to_script(actor, talk, column)
テキストにウェイトタグを挿入して返す。アクターの表に budoux があれば、続けて自動改行も行う。
SAKURA_SCRIPT.talk_to_script(actor, talk) -> string
SAKURA_SCRIPT.talk_to_script(actor, talk, column) -> string
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
actor | table | nil | ✅ | アクターの表。ウェイトの設定(アクター表のウェイト設定)と budoux・budoux_newlines・budoux_jump をこの表の直下から読む。表でない値(nil など)を渡すと、[talk] の値と実装の既定値を使い、自動改行はしない |
talk | string | nil | ✅ | 変換する台詞のテキスト |
column | table | nil | — | 自動改行の行の状態の表(行の状態の表)。渡すと、始めの状態として読み、変換の後に終わりの状態へ書き換える。渡さない(nil)ときは、最初の行の頭から数える。表でも nil でもない値を渡すと Lua のエラーになる |
戻り値: 変換後の文字列(column を渡しても、戻り値は文字列 1 つである)。
talkがnilまたは""のときは""を返す。columnは書き換えない。talkに数値を渡すと文字列にしてから変換する。表など文字列にできない値を渡すと Lua のエラーになる。
行の状態の表
column に渡す表は、次のフィールドを持つ。無いフィールドは、表の「無いときの値」として読む(空の表 {} は、最初の行の頭である)。縦書きのバルーンでは、行が列に当たる。
| フィールド | 型 | 無いときの値 | 説明 |
|---|---|---|---|
line | integer | 1 | 行の番号。最初の行が 1 |
width | integer | 0 | その行に積んだ字数(半角 1 文字を 1、全角 1 文字を 2 と数える。1 文字は 文字の分類 の見た目の 1 文字。タグは数えない) |
jump | boolean | false | budoux_jump の配列を使っているか(行を動かすタグでの数え直し) |
- 変換が成功した後にだけ、
line・width・jumpを書き換える。変換がエラーになったときは、表は渡したときのままである。 - アクター表に
budouxが無い(空の配列、表でないactorを含む)ときも、改行は入れずに、テキストの字数と行を動かすタグで状態を進める。 - 同じ表を続けて渡すと、前の呼び出しの終わりの行と字数を引き継いで数える。引き継いだ字数のままでは最初の語が行に収まらないときは、その語の前に改行を入れる。字数が 0 なら、最初の語の前に改行を入れない。
- 応答を組み立てるとき、pasta は 1 回の出力の中で、スポットごとにこの表を持って台詞のたびに渡す(1 回の出力の中の持ち越し)。
local col = { line = 2, width = 10 }
SAKURA_SCRIPT.talk_to_script({ budoux = {40, 12} }, "今日は", col)
-- → \n今日は(2 行目の上限 12 に、字数 10 から「今日は」の 6 を足すと超えるため、前で改行する)
-- col.line → 3、col.width → 6、col.jump → false
アクター表のウェイト設定
ウェイトの長さ(ミリ秒)は、アクター表の直下にある次のキーで決まる。キーごとに「アクター表の値 → pasta.toml の [talk] の値 → 実装の既定値」の順で最初に見つかった値を使う。
| キー | 対象の文字 | 既定値 |
|---|---|---|
script_wait_normal | 一般の文字(下の分類に当たらない文字) | 50 |
script_wait_period | 句点 | 1000 |
script_wait_comma | 読点 | 500 |
script_wait_strong | 感嘆符・疑問符 | 500 |
script_wait_leader | リーダー(… など) | 200 |
- 既定値は実装の既定値である。
- アクター表の値が数値(数値に変換できる文字列を含む)でないキーは、無いものとして扱う。小数は整数に切り捨てる。
- 実際に挿入されるウェイトは、設定値から 50 を引いた値(
値 - 50)である。計算した値が 0 以下のときは何も挿入しない。既定のscript_wait_normal = 50では、一般の文字の後ろにウェイトは入らない。 - どの文字を句点・読点などとみなすかは、アクター表では変えられない。pasta.toml の
[talk]で変える(pasta.toml での設定)。
動作仕様
文字の分類
このページの「1 文字」は、見た目の 1 文字(拡張書記素クラスタ)を指す。組み合わせの絵文字(🙆♀️・👍🏽・🇯🇵)や結合文字の付いた文字(e に結合用のアクセントを付けた é)は、いくつかのコードポイントでできていても 1 文字である。ウェイトの挿入・文字の分類・budoux の幅は、この 1 文字を単位にする。1 文字の途中には、ウェイトも改行も入らない。
半角カナと半角の濁点・半濁点の並び(ガ・パ など)も 1 文字になる。以前の版では カ と ゙ の 2 文字として数えていたが、今はウェイトが並びの後ろにだけ入り、カ と ゙ の間では改行しない。
テキストを先頭から読み、次の順で分類する。
- さくらスクリプトの単位: 次のタグ・エスケープ・囲みを、それぞれ 1 つの単位として読む。そのまま出力し、ウェイトは入れない。読み方は文法の章の コマンドの字句構造 と同じである。
- タグ:
\に、_を 0〜2 個付けた名前の 1 文字(ASCII 英数字・!・+・*・?・&・-)が続く形(\h・\n・\_w・\-)。名前がs・p・bのときは直後の数字 1 桁(0〜9)を、wのときは直後の数字 1 桁(1〜9)を名前に含め、この形は引数を取らない(\s12は\s1と2、\w10は\w1と0)。名前(数字付きの形を除く)の直後に[があれば、引数を閉じる]までをタグに含める(\s[5]・\_w[100]・\![open,inputbox])。引数の中の\と続く 1 文字は組で、\]では引数は閉じない。引数の先頭の"から次の"までは引用で(""は中の"1 文字)、中の]・,では引数は閉じない(角括弧内のエスケープと引用)。引数が閉じないときは名前までがタグになり、[を 2 の文字として読み、その後ろは続けて読む。 - エスケープ:
\\と\%。2 文字で 1 つの単位として読み、後ろの文字とつなげない(\\nは\\とnで、改行タグ\nにはならない)。アクション行の\\・\%はこの形で出力される(インライン要素)。 - 囲み:
\_?から次の\_?まで、\_!から次の\_!までを、全体で 1 つの単位として読む(タグをそのまま表示する囲み)。囲みは 1 回の呼び出しに渡したテキスト(talk_to_scriptのtalk、break_linesのtext)の中でだけ読まれる。同じテキストの中に閉じが無い\_?(\_!)は、ただのタグになる。アクション行の台詞とact:talkの台詞は、同じアクターの続けての台詞が 1 つにつながったテキストで 1 回の呼び出しになる。act:sakura_scriptで積んだテキストは他とつながらない(sakura_script(actor, text))。
- タグ:
- それ以外の文字は 1 文字ずつ、次の文字の集合に含まれるかを上から順に調べる(複数の集合に含まれる文字は、先に当たった分類になる)。タグの名前の直後に続く文字もここで分類する(
\nHelloのHelloは一般の文字、\n!?の!?は感嘆符・疑問符)。\の後ろがタグにもエスケープにも囲みにもならないとき(\あ、テキスト末尾の\など)は、その\も 1 文字として分類する。ただし、その\の直後にはウェイトを入れず、続く字の後ろに入れる(直後に入れると\\になり、エスケープとして読まれるため)。いくつかのコードポイントでできた文字は、まず 1 文字の全体で集合を調べ、どれにも当たらなければ先頭のコードポイント(基底の文字)で調べる。2 つ目以降のコードポイントでは調べない(ガは先頭のカで一般の文字になる)。集合に書いた文字の当たり方は pasta.toml の chars_* を参照する。
| 分類 | 文字の集合([talk] のキー) | 既定の文字 |
|---|---|---|
| 句点 | chars_period | 。。.. |
| 読点 | chars_comma | 、,, |
| 感嘆符・疑問符 | chars_strong | ?!!? |
| リーダー | chars_leader | ・・‥… |
| 行頭禁則文字 | chars_line_start_prohibited | ゛゜ヽヾゝゞ々ー)]}」』):;]}」、・ー゙゚ |
| 行末禁則文字 | chars_line_end_prohibited | ([{「『([{「 |
| 一般の文字 | (上のどれにも当たらない文字) | — |
ウェイトの挿入
- 一般の文字: 1 文字ごとに、その後ろへ
script_wait_normal - 50のウェイトを入れる。組み合わせの絵文字(🙆♀️など)も 1 文字で、ウェイトは後ろに 1 つだけ入る。 - リーダー: 1 文字ごとに、その後ろへ
script_wait_leader - 50のウェイトを入れる。 - 句点・読点・感嘆符・疑問符・行頭禁則文字: 連続する並び(
」!?。など)をひとまとめにし、並びの最後に 1 回だけウェイトを入れる。値は並びの中の文字の設定値の最大値から 50 を引いた値である。行頭禁則文字は並びを延ばすだけで、自分の値を持たない(行頭禁則文字だけの並びにはウェイトが入らない)。 - 行末禁則文字: ウェイトを入れない。直前の並びはそこで終わる。
- さくらスクリプトの単位(タグ・エスケープ・囲み): ウェイトを入れない。単位の途中(エスケープの 2 文字の間、囲みの中)にも何も入れない。直前の並びはそこで終わる。
使用例
local SAKURA_SCRIPT = require "@pasta_sakura_script"
-- 既定値(一般の文字 50 → ウェイトなし、句点 1000 → 950)
SAKURA_SCRIPT.talk_to_script(nil, "こんにちは。")
-- → こんにちは。\_w[950]
-- アクター表の直下でウェイトを変える
local actor = { script_wait_normal = 100 }
SAKURA_SCRIPT.talk_to_script(actor, "こんにちは。")
-- → こ\_w[50]ん\_w[50]に\_w[50]ち\_w[50]は\_w[50]。\_w[950]
local actor2 = { script_wait_normal = 80, script_wait_period = 200 }
SAKURA_SCRIPT.talk_to_script(actor2, "やあ。")
-- → や\_w[30]あ\_w[30]。\_w[150]
-- さくらスクリプトタグはそのまま残る
SAKURA_SCRIPT.talk_to_script(actor, "こんにちは\\s[5]元気?")
-- → こ\_w[50]ん\_w[50]に\_w[50]ち\_w[50]は\_w[50]\s[5]元\_w[50]気\_w[50]?\_w[450]
-- 囲みの中にはウェイトが入らない
SAKURA_SCRIPT.talk_to_script(actor, "あ\\_?\\s[1]abc\\_?い")
-- → あ\_w[50]\_?\s[1]abc\_?い\_w[50]
-- タグの名前の直後の字は通常の文字として扱う
SAKURA_SCRIPT.talk_to_script(nil, "\\n!?")
-- → \n!?\_w[450]
-- 組み合わせの絵文字は 1 文字(途中にウェイトは入らない)
SAKURA_SCRIPT.talk_to_script(actor, "🙆♀️OK")
-- → 🙆♀️\_w[50]O\_w[50]K\_w[50]
-- 連続する句読点は最大値(感嘆符・疑問符 500 → 450)
SAKURA_SCRIPT.talk_to_script(nil, "え!?")
-- → え!?\_w[450]
-- 読点・句点・行頭禁則文字の並び(句点 1000 が最大 → 950)
SAKURA_SCRIPT.talk_to_script(nil, "あ、」。")
-- → あ、」。\_w[950]
-- リーダーは 1 文字ごと(200 → 150)
SAKURA_SCRIPT.talk_to_script(nil, "そう…なの?」")
-- → そう…\_w[150]なの?」\_w[450]
pasta.toml での設定
[talk] セクションで、全アクター共通のウェイトの長さと文字の集合を設定する。書かなかったキーは実装の既定値になる。アクター表([actor."名前"])に同じ名前のウェイトのキーを書くと、そのアクターではアクター表の値が優先される。
[talk]
# ウェイト(ミリ秒)
script_wait_normal = 50
script_wait_period = 1000
script_wait_comma = 500
script_wait_strong = 500
script_wait_leader = 200
# 文字の集合
chars_period = "。。.."
chars_comma = "、,,"
chars_strong = "?!!?"
chars_leader = "・・‥…"
chars_line_start_prohibited = "゛゜ヽヾゝゞ々ー)]}」』):;]}」、・ー゙゚"
chars_line_end_prohibited = "([{「『([{「"
上の値はすべて実装の既定値である。アクターごとに句点の間だけ変える例:
[talk]
script_wait_normal = 100
[actor."さくら"]
spot = 0
script_wait_period = 300
[actor."うにゅう"]
spot = 1
この設定で、さくらの「やあ。」は や\_w[50]あ\_w[50]。\_w[250]、うにゅうの「やあ。」は や\_w[50]あ\_w[50]。\_w[950] になる。
break_lines(text, widths)
budoux の日本語分割モデルで求めた語の区切り位置に、さくらスクリプトの改行タグ \n を挿入する。
SAKURA_SCRIPT.break_lines(text, widths) -> string
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
text | string | nil | ✅ | 改行を入れるテキスト。nil または "" のときは "" を返す |
widths | table | nil | ✅ | 行ごとの幅の上限の配列。nil・表でない値・空の表のときは text をそのまま返す |
widths の仕様:
widths[1]が 1 行目、widths[2]が 2 行目の幅の上限である。配列の最後の値が、それ以降の行すべてに使われる({10, 12}なら 1 行目は 10 まで、2 行目以降は 12 まで)。- 幅は半角 1 文字を 1、全角 1 文字を 2 と数える(東アジアの文字幅で幅が曖昧な文字も 2)。
10は全角 5 文字分である。1 文字は 文字の分類 の見た目の 1 文字で、いくつかのコードポイントでできた文字は文字全体の幅で数える(🙆♀️は 2)。 - 改行は 1 文字の途中には入らない。
- 要素は数値で書く。小数は整数に切り捨てる。数値でない要素があると Lua のエラーになる。
改行の入れ方:
- 語を先頭から順に行へ積み、次の語を足すと幅の上限を超えるときに、その語の前へ
\nを入れて次の行へ移る。 - 1 語だけで上限を超える場合は、語の途中で分割しない(その行は上限を超える)。
- さくらスクリプトの単位(タグ・エスケープ・囲み。
\_w[50]・\\・\%・\_?…\_?など)は幅に数えず、途中に\nを入れず、元の文字との位置関係を保ったまま出力する。文字の直後にある単位はその文字の側に残り、\nは単位の後ろに入る。単位の読み方は 文字の分類 と同じである(タグの名前の直後の字は通常の文字として幅に数える)。 - テキストの中に行を動かすタグ(
\n・\n[…]・引数の無い\c・\_l[…])があると、そのタグの後ろの字を数え直す(行を動かすタグでの数え直し)。 break_linesは、毎回最初の行の頭から数え、入れる改行は\nである。アクター表のbudoux_newlines・budoux_jumpに当たる指定は無いため、\_l[…]の所では字数だけを 0 に戻す。
通常は talk_to_script と組み合わせて呼ぶ必要はない。アクター表に budoux があれば、talk_to_script がウェイトの挿入後に自動で break_lines を適用する。break_lines を直接呼ぶのは、独自の変換手順を組む場合である。
行を動かすタグでの数え直し
自動改行(talk_to_script と break_lines)は、テキストの中の次の単位の所で、その後ろの字を数え直す。作者が台詞に書いたタグも、単語や関数が返して台詞につながったタグも、act:sakura_script で積んだテキストの中のタグも同じに扱う。縦書きのバルーンでは、行が列に当たる。
| 単位 | 後ろの字の数え方 |
|---|---|
\n・\n[…](\n[half] を含む) | 次の行の頭から字数 0 で数え、配列の次の値を使う |
引数の無い \c | 最初の行の頭から字数 0 で数え、budoux の配列の先頭の値を使う(budoux_jump の配列を使っていたら budoux へ戻る) |
\_l[…](相対の \_l[@…] を含む) | 字数 0 から数える。アクター表に budoux_jump があれば、その配列の先頭の値から使い(もう一度 \_l[…] があれば先頭から使い直す)、続く \n・\n[…] のたびに次の値を使う。budoux_jump が無いか空の配列なら、行の番号は変えず、同じ値を使い続ける |
- 上の表に無い単位では数え直さない。
\x・\x[noclear]・\_q・引数つきの\c[…]・ウェイト(\_w[…]など)・表情の切り替え(\s[…])・スコープの切り替え(\0・\1・\p[…]など)がこれに当たる。 - 単位の読み方は 文字の分類 と同じである。エスケープ
\\の後ろのn(\\n)と、囲み(\_?…\_?・\_!…\_!)の中の\n・\c・\_lは、行を動かすタグとして読まない。 - 行を動かすタグの直後に続く語の前には、改行を入れない(行の頭に空の行を作らない)。
- 行を動かすタグが budoux の 1 語の途中にあると、タグの前と後ろを別の行として数え、タグの後ろを新しい行の頭として扱う。
- 改行を入れる位置の直前が単位にならない
\(文字の分類)のときは、その\の前で改行する(\の直後に入れると\\のエスケープとして読まれるため。\が行の頭にあるときは改行しない)。 - 相対の
\_l[@…]でも字数は 0 に戻る。横書きで\_l[@…]を使って表示の位置を少しずらしているときも、そこから数え直す。
以前の版では、テキストに元からある \n などを数え直さず、前の字数を引き継いで数えていた。この直しで、budoux を使うゴーストでは改行の位置が変わることがある。
SAKURA_SCRIPT.break_lines("今日は\\n天気", {8})
-- → 今日は\n天気(\n の後ろは字数 0 から数えるので、「天気」の前に改行は入らない)
pasta.toml での budoux 設定
アクター表に budoux を書くと、そのアクターの台詞に自動改行が入る。値は break_lines の widths と同じ配列である。
[actor."女の子"]
spot = 0
budoux = [10, 12]
# 1 行目は幅 10(全角 5 文字)まで、2 行目以降は幅 12(全角 6 文字)まで
budoux を書かないアクター、または空の配列を書いたアクターには自動改行は入らない。
同じアクター表に、次の 2 つのキーも書ける。どちらも budoux が空でない配列のときだけ読む(budoux が無いアクターでは、書き間違いがあってもエラーにならない)。
| キー | 型 | 説明 |
|---|---|---|
budoux_newlines | 数値 | 自動改行で入れる改行の幅の倍率。100 倍して小数点以下を切り捨てた N が 100 なら \n、それ以外は \n[N] を入れる。書かなければ \n。数値に変換できる文字列も数値として読み、数値にできない値を書くとその台詞の変換がエラーになる(budoux_newlines) |
budoux_jump | 整数の配列 | \_l[…] の後ろの行に使う幅の配列(行を動かすタグでの数え直し)。書き方と、整数にできない要素の扱いは budoux と同じ。表でない値は無視する(budoux_jump) |
[actor."ぱすた"]
spot = 0
budoux = [40] # 20 字ずつ
budoux_jump = [40, 28] # \_l[…] の後: 1 行目は 20 字、2 行目以降は 14 字
budoux_newlines = 1.8 # 自動改行を \n[180] にする
budoux_newlines が効くのは自動改行で入れる改行だけで、台詞に書いた改行や pasta が出す区切りの幅は変わらない。break_lines は、この 2 つのキーを使わない。
使用例(直接呼び出し)
local SAKURA_SCRIPT = require "@pasta_sakura_script"
SAKURA_SCRIPT.break_lines("今日はいい天気ですね", {6})
-- → 今日は\nいい\n天気ですね
SAKURA_SCRIPT.break_lines("今日はいい天気ですね", {10})
-- → 今日はいい\n天気ですね
-- さくらスクリプトタグは幅に数えず、出力に残る
SAKURA_SCRIPT.break_lines("こ\\_w[50]れ\\_w[50]は\\_w[50]テ\\_w[50]ス\\_w[50]ト", {6})
-- → こ\_w[50]れ\_w[50]は\_w[50]\nテ\_w[50]ス\_w[50]ト
-- nil・空の表は安全
SAKURA_SCRIPT.break_lines(nil, {10}) -- → ""
SAKURA_SCRIPT.break_lines("テスト", {}) -- → "テスト"(変更なし)
SAKURA_SCRIPT.break_lines("テスト", nil) -- → "テスト"(変更なし)
-- talk_to_script 経由の自動適用(アクター表に budoux がある場合)
local ok, CONFIG = pcall(require, "@pasta_config")
local actor = ok and CONFIG.actor and CONFIG.actor["女の子"] -- budoux = [10, 12] を書いたアクター
SAKURA_SCRIPT.talk_to_script(actor, "今日はいい天気ですね。")
-- → 今日はいい\n天気ですね。\_w[950](ウェイトを挿入した後で改行が入る)
改行の位置は budoux の分割結果に依存する。上の出力は現行の分割モデルでの結果である。
これで台詞の「間」も折り返しも、あなたの思うままですわね。早口すぎるゴーストになっていたら、まず script_wait_normal を疑いなさいまし。
既定の一般文字は 50 ミリ秒、差し引き 0 でウェイトは入らない、でございましたね。
おほほほ! さきほどのあなたの話しぶりとは、大違いですわね!