v
Skip to content

クエストトラッカー ​

クエストの記録を作ります。クエストごとに達成済みかどうかが表示され、プレイヤーの所持金も画面に出て、クエストを達成すると報酬が支払われて通知が出ます。クエストが達成されたかどうかは AI が判断し、報酬はビヘイビアがきっちり 1 回だけ支払い、記録はプレイヤー画面のページで見せます。コードは要りません。


完成するもの ​

  • クエスト記録のページ:「Find Herbs」(薬草探し)と「Defeat the Forest Wolf」(森の狼退治)。それぞれ達成済みか未達成かが表示されます
  • 所持金を画面に表示:報酬が入るたびに更新されます
  • クエストの達成は AI が記録:物語の中で達成されたら、プレイヤーがどんな言い方をしても AI が記録します
  • 報酬は 1 回だけ:薬草で +30 金貨、狼で +50 金貨。通知付き
  • 進み具合の表示:「1 / 2 達成」

仕組み ​

プレイヤーが村の長老に薬草を渡す
  → AI が、変数 Quests done のビヘイビアルールに従って「Find Herbs」をリストに追加
  → ビヘイビア「報酬:Find Herbs」が Quests done を見ている(変数が変化)
  → 条件(Quests done が「Find Herbs」を含む)を満たし、まだ一度も発動していない(最大発動回数 1)
  → Gold +30、通知「クエスト達成:Find Herbs(+30 金貨)」、AI に伝える
  → クエスト記録で Find Herbs が達成済みになる

物語の中で何かが起きたかどうかを判断するのは AI が得意で、きっちり 1 回だけ支払うのはエンジンが得意です。


作り方 ​

ステップ 1:変数を作る ​

カードの下の 追加できるもの から、それぞれ + 変数 を押します。

Quests done ​

項目値
名前Quests done
型リスト/表
初期値[]
ビヘイビアルール下を参照
プレイヤーが達成したクエストの名前のリスト。クエストは「Find Herbs」(村の長老に薬草を届ける)と「Defeat the Forest Wolf」(羊をさらっている狼を倒す)。物語の中でプレイヤーが本当に達成したら、その名前をそのままこのリストに追加する。達成していないクエストは追加しない。同じクエストを二度追加しない。

Gold ​

項目値
名前Gold
型数値
初期値0
範囲0 〜(空欄)
ビヘイビアルールプレイヤーの所持金。クエストの報酬は自動で加算される。戦利品、取引、盗難などで変えてもよい。

プレイヤー画面に表示 を押すと、チャット中も Gold がずっと見えます。

Quests completed(なくても OK) ​

項目値
名前Quests completed
型数値
計算式(自動で計算)len({Quests done})

リストの数を自分で数えてくれます。AI は読めますが、変えることはできません。


ステップ 2:クエストごとに報酬のビヘイビアを作る ​

追加できるもの から + ビヘイビア を押します。

報酬:Find Herbs ​

部分設定
発動するとき変数が変化:Quests done
次の場合のみQuests done 含む Find Herbs
効果変数を変更:Gold 加算 30
通知を表示:クエスト達成:Find Herbs(+30 金貨)、スタイル 成功
AIに伝える:プレイヤーはたったいま「Find Herbs」を達成し、30 金貨を受け取った。返信の中でそれに触れること。
最大発動回数1

報酬が二重に支払われないのは、最大発動回数 が 1 だからです。次のクエストが追加されるとリストはまた変わり、そのときも「Find Herbs を含む」は成り立ちますが、このビヘイビアはもう 1 回発動しています。

AIに伝える は、次の返信に向けて AI にひとこと渡します。これで AI は、長老が硬貨を数えて渡す場面を書けます。

報酬:Defeat the Forest Wolf ​

同じように作ります。条件は Defeat the Forest Wolf、Gold 加算 50、通知と AI へのひとこともそれに合わせます。

キーワードで発動させる場合

会話の中の言葉でクエストを達成させることもできます。発動するとき:AIがキーワードを発言 長老が薬草を受け取る。キーワードはカンマで区切り、どれか 1 つ が出ればビヘイビアが発動します。なので 倒す, 狼 のように書くと、「狼を見かけた」でも発動してしまいます。その場面ならではのフレーズを使い、クエストごとにスイッチの変数を用意して(効果で true にし、次の場合のみ で確認する)、二度発動しないようにします。プレイヤーの言い方はさまざまなので、上のように AI に記録させるほうがうまく対応できます。


ステップ 3:クエスト記録のページを作る ​

上部バー中央の プレイヤー画面 を押します。ページを追加 でページを足し(白紙のページ)、名前を「クエスト」にします。

  1. タイトルの 文字 の部品と、{Quests completed} / 2 達成 と書いた部品を置く(変数の値を挿入… を使います)
  2. リスト の部品を置く。中身の出どころ で 自分で書く を選び、行を 2 つ追加する。タイトル Find Herbs、本文 村の長老に薬草を届ける。報酬:30 金貨。タイトル Defeat the Forest Wolf、本文 羊をさらっている狼を倒す。報酬:50 金貨
  3. 各行をカードで表示 をオンにする
  4. ロック解除の条件 で 未解除の行をロック表示 をオンにし、見張る変数 を Quests done にして、照合する行の項目 は title のままにする。ロック中の表示 には まだ達成していません と書く。Quests done がその行のタイトルを含むと、行が明るくなります
  5. 会話のページへ戻る ページへ移動 のボタンを置き、会話のページには ページへ移動 → クエスト の「クエスト」ボタンを置く

編集 をオフにすると、マッシーを使わずにページを押して試せます。

画面全体のレイアウト

金貨やほかの数値をページではなく常に画面に出しておきたいなら、ステータス盤 テンプレート(画面全体 の中)を使うと、ゲージ 4 本、所持金、日数の付いたレイアウトがすぐに手に入ります。


ステップ 4:プレイテスト ​

上部バーの プレイ を押します。

  1. クエスト を開く:どちらも「まだ達成していません」、0 / 2 達成、Gold 0
  2. 薬草を探して持ち帰るまで遊ぶ。AI がクエストを記録すると通知が出て、Gold が 30 になる
  3. クエスト を開く:Find Herbs が達成済み、1 / 2
  4. 狼を倒す。Gold が 80 に、2 / 2

プレイテスト右側の このターン で、Quests done がいつ変わったか、報酬のビヘイビアがそれぞれ発動したかがわかります。

うまくいかないときは:

症状よくある原因直し方
物語では達成したのに記録されないAI がリストに追加しなかったビヘイビアルールで各クエストの名前を正確に書き、何をしたら達成になるかを書く
記録されたのに報酬が出ないAI が追加した名前が条件と一致しないリストの項目、条件、行のタイトルをすべて同じ文字にする
報酬が二重に出た最大発動回数 が 1 になっていない報酬のビヘイビアそれぞれに設定する
記録の行がロックされたまま照合する行の項目 か 見張る変数 が違うtitle と Quests done にする
AI が早すぎるタイミングで達成にするルールがゆるすぎる何が起きればいいのかを書く:「長老が薬草を手にしたときだけ」

カスタムコードの方法:チャットの下のクエストパネル ​

インターフェースを自分で書くなら、インターフェースのコードで同じ変数を読みます。ここまでの設定はそのままで大丈夫です。

パネル → フロントエンドコード で index.tsx を開き、デフォルトの return <Chat /> を置き換えます。

tsx
export default function MyWorld() {
  const api = useYumina();
  const msgs = api.messages || [];

  const done = Array.isArray(api.variables["Quests done"])
    ? api.variables["Quests done"].map(String)
    : [];
  const gold = Number(api.variables["Gold"] ?? 0);

  const quests = [
    { name: "Find Herbs", reward: 30 },
    { name: "Defeat the Forest Wolf", reward: 50 },
  ].map((q) => ({ ...q, done: done.includes(q.name) }));

  const completedCount = quests.filter((q) => q.done).length;

  return (
    <Chat renderBubble={(msg) => {
      const isLastMsg = msg.messageIndex === msgs.length - 1;
      return (
        <div>
          <div
            style={{ color: "#e2e8f0", lineHeight: 1.7 }}
            dangerouslySetInnerHTML={{ __html: msg.contentHtml }}
          />

          {isLastMsg && (
            <div style={{
              marginTop: "16px",
              padding: "16px",
              background: "linear-gradient(135deg, rgba(30,41,59,0.8), rgba(15,23,42,0.9))",
              borderRadius: "12px",
              border: "1px solid #334155",
            }}>
              {/* Header with gold */}
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "14px" }}>
                <div style={{ fontSize: "15px", fontWeight: "bold", color: "#e2e8f0" }}>Quest Tracker</div>
                <div style={{
                  display: "flex", alignItems: "center", gap: "6px", padding: "4px 12px",
                  background: "rgba(234,179,8,0.15)", border: "1px solid rgba(234,179,8,0.3)", borderRadius: "20px",
                }}>
                  <span style={{ fontSize: "14px" }}>💰</span>
                  <span style={{ fontSize: "14px", fontWeight: "bold", color: "#fbbf24" }}>{gold}</span>
                </div>
              </div>

              <div style={{ fontSize: "12px", color: "#64748b", marginBottom: "12px" }}>
                Completed {completedCount}/{quests.length}
              </div>

              {/* Quest list */}
              <div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
                {quests.map((quest) => (
                  <div key={quest.name} style={{
                    display: "flex", justifyContent: "space-between", alignItems: "center",
                    padding: "10px 14px", borderRadius: "8px",
                    background: quest.done ? "rgba(34,197,94,0.08)" : "rgba(30,41,59,0.5)",
                    border: quest.done ? "1px solid rgba(34,197,94,0.2)" : "1px solid #1e293b",
                  }}>
                    <span style={{
                      fontSize: "13px",
                      color: quest.done ? "#94a3b8" : "#e2e8f0",
                      textDecoration: quest.done ? "line-through" : "none",
                    }}>
                      {quest.name}
                    </span>
                    <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
                      <span style={{ fontSize: "12px", color: quest.done ? "#4ade80" : "#64748b" }}>
                        {quest.done ? `+${quest.reward} g` : `${quest.reward} g`}
                      </span>
                      <span style={{
                        display: "inline-flex", alignItems: "center", justifyContent: "center",
                        width: "24px", height: "24px", borderRadius: "6px", fontSize: "13px", fontWeight: "bold",
                        background: quest.done ? "rgba(34,197,94,0.2)" : "rgba(239,68,68,0.15)",
                        color: quest.done ? "#4ade80" : "#f87171",
                        border: quest.done ? "1px solid rgba(34,197,94,0.3)" : "1px solid rgba(239,68,68,0.25)",
                      }}>
                        {quest.done ? "✓" : "✗"}
                      </span>
                    </div>
                  </div>
                ))}
              </div>

              {completedCount === quests.length && (
                <div style={{
                  marginTop: "12px", padding: "10px", textAlign: "center", fontSize: "13px", fontWeight: "600",
                  background: "rgba(34,197,94,0.1)", border: "1px solid rgba(34,197,94,0.25)",
                  borderRadius: "8px", color: "#4ade80",
                }}>
                  All quests complete!
                </div>
              )}
            </div>
          )}
        </div>
      );
    }} />
  );
}

コードがしていること:

  • api.variables["Quests done"] は変数の名前でリストを読みます。名前がリストに入っていれば、そのクエストは達成済みです
  • api.variables["Gold"] で所持金を読みます
  • completedCount は達成したクエストを数え、進み具合の行と「すべて達成」の帯に使います
  • パネルは最後のメッセージの下にだけ出て、変数が変わればすぐに更新されます

パネル → フロントエンドコード で、ファイルの横の状態が緑の OK になっていれば大丈夫です。

自分でコードを書きたくないときは

キャンバス右上の AIアシスタント を押して、ほしいパネルを伝えます。


さらに発展させる ​

クエストを増やす ​

Quests done のビヘイビアルールにクエストの名前と達成の条件を書き足し、報酬のビヘイビアを追加し、クエスト記録に行を足します(コードなら quests の配列に 1 行)。計算式を使っているなら、記録のページの / 2 を新しい合計に変えます。

AI が出すクエスト ​

2 つ目のリスト Quests open を追加し、ビヘイビアルールで「キャラクターがプレイヤーに助けを求めたら、クエストを追加する」と AI に伝えます。記録のページには 2 つ目のリストの部品を置き、自分で書いた行の代わりに 変数を読む(Quests open)から表示します。

クエストの連鎖 ​

薬草のあとにだけ「Save the Village」を出したいときは、新しいクエストを待機ロアとして書きます(ビヘイビアにオンにされるまで AI からは見えません)。そのうえでビヘイビアを追加します。Quests done の 変数が変化、次の場合のみ Quests done 含む Find Herbs、効果はその設定の ロアエントリを有効化、最大発動回数 1。それ以降、AI は長老に新しい頼みごとがあることを知っています。次の場合のみ には条件をいくつも入れられ、すべて満たす か いずれか満たす を選べるので、もっと長い連鎖も作れます。待機ロアについてはオープニングと設定を参照してください。

金貨を使う ​

ショップも同じ Gold の変数を使います。クエストで増やし、ショップで減らす、という形です。


早見表 ​

やりたいことやり方
クエストの達成を AI に判断させる各クエストの名前を書いたビヘイビアルールを持つリスト/表の変数
報酬を 1 回だけ支払うビヘイビア:変数が変化、次の場合のみ リストがそのクエストを 含む、最大発動回数 1
プレイヤーに知らせる効果:通知を表示、スタイル 成功
AI に知らせる効果:AIに伝える(次の返信のみ)
達成したクエストを数える計算式 len({Quests done}) の数値の変数
クエスト記録を見せる自分で書く 行と、Quests done を見る ロック解除の条件 を設定したリストの部品
所持金を常に画面に出すGold の プレイヤー画面に表示
決まった言葉でクエストを達成させるプレイヤーがキーワードを発言 / AIがキーワードを発言(キーワードのどれか 1 つで発動)
オリジナルのパネル同じ変数を読むインターフェースのコード

これはレシピ #6 です

AI が何かが起きたことに気づき、ビヘイビアが 1 回だけ支払い、画面がそれを見せる。この流れは、物語の節目、サブクエストの分岐、実績にもそのまま使えます。