v
Skip to content

キャラクター作成フォーム ​

物語が始まる前に、プレイヤーがキャラクターシートを埋めます。名前、クラス、短いバックストーリー。「Start Adventure」を押すと物語が始まり、AI は最初からプレイヤーが何者かを知っています。プレイヤー画面にはちょうどこのためのテンプレートがあるので、フォームにコードは要りません。


これから作るもの ​

最初のメッセージの前に、プレイヤーが順に通る数枚のページです。

  • 名前の入力欄
  • クラスの選択:Warrior、Mage、Rogue。それぞれに一行の説明が付きます
  • バックストーリーの入力欄(任意)
  • 確認ページ:選んだ内容をまとめ、物語を始めるボタンがあります

プレイヤーが入力したものは、すべて普通の変数に入ります。AI は最初の返信からそれを読めます。

しくみ ​

1. プレイヤーが新しくプレイを始める → チャットの代わりに最初の開始ページが出る
2. 名前とバックストーリーを入力 → 変数「プレイヤー名」と「バックストーリー」に保存
3. クラスを選ぶ → 変数「クラス」に保存
4. 確認する → あなたのオープニングでチャットが始まる
5. プレイヤーが最初のメッセージを送る
   → AI のプロンプトには、変数とそのビヘイビアルール、
     選んだクラスのときだけ送られる設定が入る
   → AI はプレイヤーを名前で呼び、クラスに合わせて書く

手順 ​

ステップ 1:テンプレートから開始ページを追加する ​

上部中央の プレイヤー画面 を押し、テンプレート を開きます。オープニング · 物語が始まる前 から次の 3 つを追加します。

テンプレートできるもの
キャラクターシート名前・性別・年齢・外見・秘密ひとつ。入力するとカードができていきます。必須は名前だけです
出自を選ぶ選択肢が 3 つあるカード選択。これをクラス選択に作り変えます
開始前の確認ここまでの選択をまとめます。いつも最後に並びます

プレイヤーは物語が始まる前に、これらを順番に通ります。どのテンプレートにも、カードに追加される変数が書いてあります(「このカードに次の変数を追加します」)。これらは普通の変数で、AI に意味を伝えるビヘイビアルールもすでに入っています。たとえば「プレイヤーの名前。物語の人物は皆この名前でプレイヤーを呼ぶ。別の名前を作らない。」のように。

名前だけでいいなら

名前を入力 テンプレートは、名前と呼ばれ方だけを聞きます。

ステップ 2:出自の選択をクラスの選択に変える ​

左のページの列で 出自を選ぶ のページを選び、次のように変えます。

  1. 見出しをクリックして「クラスを選ぶ」に書き換える
  2. カード選択を選び、3 つの選択肢の名前を Warrior(「近接戦闘の専門家。HP が高い」)、Mage(「遠距離魔法。MP が高い」)、Rogue(「身軽で隠密が得意。クリティカルが高い」)に変える
  3. ページの列でそのページをダブルクリックし、名前を「クラス」に変える

カード選択は、選ばれた選択肢を変数 出自 に保存します。キャンバスに戻り、変数ブロックで出自を見つけて名前を クラス に変え、ビヘイビアルール を書き直します。

プレイヤーが最初に選んだクラス:Warrior、Mage、Rogue のどれか。戦闘で何ができるか、周りからどう見られるかを決める。変えないこと。

続けて AI での扱い を開き、AI アクセス を AI は読み取り専用 にします。これで AI がプレイヤーのクラスを書き換えられなくなります。

ステップ 3:キャラクターシートを整える ​

キャラクターシート のページでは、名前は残し、欄のひとつをバックストーリーに変えます。

  • いらない部品は削除します(部品を選ぶか レイヤー から探して Delete キー)。その変数は、あとでキャンバスから消せます
  • 「ひとつの秘密」の欄のラベルを「バックストーリー(任意)」に、プレースホルダーを「あなたの過去を数行で」に変えます。キャンバスでその変数の名前を プレイヤーの秘密 から バックストーリー に変え、ビヘイビアルールを プレイヤー自身が書いたバックストーリー。物語に活かし、矛盾させないこと。 にします(テンプレートのもとのルールは、物語の誰も知らない秘密として扱うもので、バックストーリーには合いません)

プレイヤー名 はテンプレートのルールのままで大丈夫です。名前を飛ばすプレイヤーに備えたいなら、開始ボタンを選んで これが埋まるまで押せない でプレイヤー名にチェックを入れます。名前が入力されるまで、ボタンはグレーのままになります。

ステップ 4(任意):クラスごとに設定を用意する ​

変数は、プレイヤーが何を選んだかを AI に伝えます。クラスごとにもっと材料を渡したいなら、そのクラスのときだけ AI に届く設定をクラスごとに追加します。

  1. 設定「Mage の設定」を追加し、この世界で Mage が何を知っていて何ができるかを書く
  2. 変数 クラス をその設定の上にドラッグし、できた線をクリックする。条件を クラス = Mage にする
  3. Warrior と Rogue でも同じようにする

これで Mage を選んだプレイヤーの AI には Mage の設定が届き、ほかのクラスの設定は届きません。条件ロアについてはオープニングと設定を参照してください。

ステップ 5:オープニングを書く ​

オープニング(キャンバスの オープニング)は、フォームのあとに出る最初のメッセージです。いつもどおりに書きます。

*運命の門が開き、エルダーランドに夜明けの光があふれる。*

*遠くに街の影がゆらめき、石畳の道がそこへ続いている。風は草の匂いと、遠くの炉の煙の匂いを運んでくる。*

目の前には三つの道がある。町へ続く広い道、森を抜ける細い小道、川へ下る坂。どちらへ進む?

プレイヤーの選択をオープニングの本文に入れない

オープニングは、プレイヤーがフォームを埋める前、プレイが作られた時点で書き込まれます。オープニングに {{Player name}} のマクロを書くと、プレイヤーが入力した名前ではなく、変数の初期値が表示されます。名前やクラスは AI に任せましょう。AI の最初の返信は、もうそれを知っています。

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

プレイヤー画面で 編集 をオフにすると、マッシーを使わずにページを順に押して確かめられます。そのあと上部バーの プレイ を押します。

  1. キャラクターシートが出る。「Elara」と、バックストーリーを一行入力する
  2. Mage を選ぶ
  3. 確認ページに選んだ内容が並ぶ。開始ボタンを押す
  4. オープニングが出る。「町のほうへ向かう」と送る
  5. AI があなたを Elara と呼び、Mage として描く(杖、呪文、ローブに目を留める人々)

プレイテストの右側の このターン で、AI に届いた変数と設定を確認できます。

うまくいかないときは:

症状考えられる原因直し方
フォームが出ないページが開始ページの流れに入っていないオープニング · 物語が始まる前 のテンプレートから追加し、確認ページを最後にする
ページに 出口なし の注意が付くそのページに、どこかへ進むボタンがないページへ移動 のボタンを付ける
AI がクラスを無視するクラスの変数にビヘイビアルールがない、または AI が変数を見ていないビヘイビアルールを書く。カードの Context にある AI に見せる変数 が なし になっていないか確認する
クラスの設定が届かない条件の値が、選んだ選択肢と完全には一致していない条件は選択肢の名前どおり Mage にする
AI がプレイヤーの名前を変えてしまうプレイヤー名を AI が書き換えられるAI アクセス を AI は読み取り専用 にする

カスタムコードの方法:最初のメッセージの中にフォームを出す ​

フォームを自分の画面のコードで描きたい場合、たとえば最初のメッセージのすぐ下に出したい場合は、こちらのコード版を使います。文字の変数を 3 つ(player_name、player_class、player_backstory。どれも AI は読み取り専用、player_name の初期値は Traveler)と、オープニングを 2 つ使います。1 つ目は「あなたは誰?」と聞く短い場面、2 つ目が本当の物語のオープニングです。

画面のコードは、変数を名前で読み書きします。パネル → フロントエンドコード で index.tsx を開き、デフォルトの return <Chat /> を置き換えます。

tsx
export default function MyWorld() {
  const api = useYumina();

  const [name, setName] = React.useState("");
  const [selectedClass, setSelectedClass] = React.useState("");
  const [backstory, setBackstory] = React.useState("");

  // Once a class is saved, the form is done
  const hasCreated = String(api.variables.player_class || "") !== "";

  const classes = [
    { id: "Warrior", label: "Warrior", icon: "⚔️", desc: "Melee specialist, high HP" },
    { id: "Mage", label: "Mage", icon: "🔮", desc: "Ranged magic, high MP" },
    { id: "Rogue", label: "Rogue", icon: "🗡️", desc: "Agile and stealthy, high crit" },
  ];

  const handleStart = async () => {
    if (!selectedClass) return;
    // Switch to the second opening FIRST: switching resets variables
    // to that opening's starting values
    await api.switchGreeting(1);
    api.setVariable("player_name", name.trim() || "Traveler");
    api.setVariable("player_class", selectedClass);
    api.setVariable("player_backstory", backstory.trim());
  };

  const label = { fontSize: "13px", color: "#a5b4fc", marginBottom: "6px", fontWeight: "600" };
  const field = {
    width: "100%", padding: "10px 14px", background: "#0f172a",
    border: "1px solid #334155", borderRadius: "8px", color: "#e2e8f0",
    fontSize: "14px", outline: "none", boxSizing: "border-box", fontFamily: "inherit",
  };

  return (
    <Chat renderBubble={(msg) => (
      <div>
        <div
          style={{ color: "#e2e8f0", lineHeight: 1.7 }}
          dangerouslySetInnerHTML={{ __html: msg.contentHtml }}
        />

        {msg.messageIndex === 0 && !hasCreated && (
          <div style={{
            marginTop: "20px",
            padding: "24px",
            background: "linear-gradient(135deg, #1e1b4b 0%, #1a1a2e 100%)",
            borderRadius: "16px",
            border: "1px solid #312e81",
          }}>
            <div style={{ fontSize: "18px", fontWeight: "bold", color: "#c4b5fd", marginBottom: "20px", textAlign: "center" }}>
              Create Your Character
            </div>

            <div style={{ marginBottom: "16px" }}>
              <div style={label}>Character Name</div>
              <input
                type="text"
                value={name}
                onChange={(e) => setName(e.target.value)}
                placeholder="Leave blank for 'Traveler'"
                style={field}
              />
            </div>

            <div style={{ marginBottom: "16px" }}>
              <div style={{ ...label, marginBottom: "8px" }}>Choose a Class</div>
              <div style={{ display: "flex", gap: "10px" }}>
                {classes.map((cls) => {
                  const picked = selectedClass === cls.id;
                  return (
                    <button
                      key={cls.id}
                      onClick={() => setSelectedClass(cls.id)}
                      style={{
                        flex: 1,
                        padding: "14px 10px",
                        background: picked ? "linear-gradient(135deg, #4338ca, #6366f1)" : "#1e293b",
                        border: picked ? "2px solid #818cf8" : "1px solid #334155",
                        borderRadius: "10px",
                        color: picked ? "#e0e7ff" : "#94a3b8",
                        cursor: "pointer",
                        textAlign: "center",
                      }}
                    >
                      <div style={{ fontSize: "24px", marginBottom: "4px" }}>{cls.icon}</div>
                      <div style={{ fontSize: "14px", fontWeight: "bold", marginBottom: "2px" }}>{cls.label}</div>
                      <div style={{ fontSize: "11px", opacity: 0.7 }}>{cls.desc}</div>
                    </button>
                  );
                })}
              </div>
            </div>

            <div style={{ marginBottom: "20px" }}>
              <div style={label}>Backstory (optional)</div>
              <textarea
                value={backstory}
                onChange={(e) => setBackstory(e.target.value)}
                placeholder="A few sentences about your character's past..."
                rows={3}
                style={{ ...field, resize: "vertical" }}
              />
            </div>

            <button
              onClick={handleStart}
              disabled={!selectedClass}
              style={{
                width: "100%",
                padding: "14px",
                background: selectedClass ? "linear-gradient(135deg, #7c3aed, #a855f7)" : "#374151",
                border: "none",
                borderRadius: "10px",
                color: selectedClass ? "#f5f3ff" : "#6b7280",
                fontSize: "16px",
                fontWeight: "bold",
                cursor: selectedClass ? "pointer" : "not-allowed",
              }}
            >
              {selectedClass ? "Start Adventure" : "Pick a class first"}
            </button>
          </div>
        )}
      </div>
    )} />
  );
}

コードがしていること:

  • msg.messageIndex === 0 && !hasCreated で、フォームを最初のメッセージにだけ、しかもクラスが保存されるまでのあいだだけ出します
  • 3 つの React.useState は、プレイヤーが Start Adventure を押すまで、入力中の内容を持っておきます
  • api.switchGreeting(1) は、最初のメッセージを 2 つ目のオープニングに差し替えます(オープニングは 0 から数えます)。切り替えると、変数はそのオープニングの初期値に戻ります。 そのためコードは切り替えが終わるのを待ってから変数を書き込みます。順番が逆だと、切り替えで消えてしまいます
  • api.setVariable("player_class", ...) は変数の名前で書き込みます

AI は次のプロンプトでこれらの変数を読みます。プロフィールを設定の文章として書き出したいなら、毎ターン送る設定にマクロを書くこともできます。

[Player character]
Name: {{player_name}}
Class: {{player_class}}
Backstory: {{player_backstory}}

設定の中のマクロは、AI のプロンプトが組み立てられるたびに埋められるので、いつも現在の値になります。オープニングの中のマクロはそうではありません。オープニングはプレイが作られた時点で書き込まれるので、そこに書いた {{player_name}} は初期値(「Traveler」)を表示します。

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

以降のメッセージにキャラクターを表示する ​

renderBubble の中、メッセージ本文の上に入れます。

tsx
{hasCreated && (
  <div style={{ display: "flex", gap: "8px", marginBottom: "8px", fontSize: "12px", color: "#a5b4fc" }}>
    <span>{String(api.variables.player_name)}</span>
    <span style={{ opacity: 0.5 }}>|</span>
    <span>{String(api.variables.player_class)}</span>
  </div>
)}

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

キャンバス右上の AIアシスタント を押して、ほしいフォームを説明してください。


さらに発展させる ​

クラスを増やす ​

テンプレートの方法なら、カード選択に選択肢を足し、対応する設定を追加します。コードの方法なら、classes の配列に項目を足します。

能力値 ​

能力値を振る テンプレートは、6 ポイントを筋力・敏捷・魅力・知力に振り分けます。クラスのページと確認ページのあいだに置きましょう。

クラスごとに違う始まり方 ​

クラスごとにまったく別の場所から始めたいなら、クラスごとにオープニングを書き、オープニングを選ぶ テンプレートを追加します(オープニングが 2 つ以上必要です)。オープニングに結びつけたシチュエーションは、そのオープニングから始めたプレイの中にだけ存在します。シチュエーションを参照してください。


早見表 ​

やりたいことやり方
物語の前にフォームを出すプレイヤー画面 → テンプレート → オープニング · 物語が始まる前
名前だけ聞く名前を入力 テンプレート
いくつもの欄を埋めてもらうキャラクターシート テンプレート
いくつかの選択肢から 1 つ選ぶ出自を選ぶ テンプレート(カード選択)を名前を変えて使う
始める前にまとめを見せる開始前の確認 テンプレート。いつも最後
入力を必須にする開始ボタン → これが埋まるまで押せない
AI に選択を変えさせないAI アクセス:AI は読み取り専用
選択ごとに設定を変える変数に条件を付けた設定
選択を設定の本文に入れる設定に {{player_name}} のようなマクロを書く(オープニングには書かない)
コード:オープニングを切り替えてから選択を保存await api.switchGreeting(1) のあとに api.setVariable(...)

これはレシピ #4 です

ボタンからオープニングを切り替える方法と、設定の中でマクロを使う方法は、UI によるシーンジャンプと設定の切り替えで詳しく扱っています。キャラクター作成は、ほかのレシピの前段階としてよく使われます。クラスによってショップの所持金の初期値や、持ち物の最初のアイテムを決めることもできます。