マップとシーン移動
プレイヤーがタップして使える地図です。Forest を選ぶと、場面が森に移ります。AI には森の雰囲気が渡され、音楽は森の曲へフェードし、AI の次の返信で到着の様子が描かれます。変数ひとつ、条件ロア、音楽のルール、プレイヤー画面のボタンで作るので、コードは要りません。
これから作るもの
- 地図のページ:Village、Forest、Cave、Market の 4 か所
- いまいる場所がわかる:その場所のボタンはもう押せません
- 場所をタップすると場面が移る:現在地が変わり、その場所のロアが AI に届き、プレイヤーの一言「(I head to the Forest)」がチャットに送られるので、AI が到着の様子を描きます
- 場所ごとに音楽がある:場所を移ると、前の曲から次の曲へフェードで切り替わります
しくみ
プレイヤーが地図の「Forest」をタップする
→ ボタンが Location を Forest にし、チャットに戻り、
プレイヤーの代わりに「(I head to the Forest)」と発言する
→ ロアエントリ「Forest」(条件:Location が Forest)が AI に届くようになり、「Village」は届かなくなる
→ Forest の音楽ルールが当てはまり、村の曲がフェードアウトして森の曲がフェードイン
→ AI は森の雰囲気を受け取ったうえで「(I head to the Forest)」に返信する変わるのは変数だけです。ロアと音楽はそれを見て、ついてきます。
ステップバイステップ
ステップ1:変数を作る
カードの下にある 追加できるもの で + 変数 を押します。
| 項目 | 値 |
|---|---|
| 名前 | Location |
| 型 | 文字 |
| 初期値 | Village |
| ビヘイビアルール | プレイヤーのいる場所。Village、Forest、Cave、Market のどれか。プレイヤーがはっきりそのどこかへ移動したら変える。 |
物語の中で AI にもプレイヤーを移動させたいとき(「小川をたどって森に入る」など)は、AI アクセス を AI が読み書き可能 のままにします。移動は地図からだけにしたいなら、AI は読み取り専用 にします。
ステップ2:場所ごとにロアエントリを書く
場所ごとにロアエントリを 1 つずつ追加します。次に Location を各エントリの上にドラッグし、できた線をクリックして、条件を「Location = Village」のように設定します(ほかの場所も同様)。エントリは条件ロアのブロックに移り、AI に届くのはいまいる場所のエントリだけになります。
Village
[Location: Village]
プレイヤーは村にいる。場面を描写するときは、次の雰囲気を取り入れること:
- 静かな小さな村。木造の家々のあいだを石畳の小道が曲がりくねっている
- 煙突から煙が上がり、焼きたてのパンとシチューの匂いがする
- 井戸のそばで村人が話し込み、鍛冶屋から槌の音が響く
- 麦畑がずっと先まで広がり、風に揺れている
- あたたかく、いつもどおりの雰囲気Forest
[Location: Forest]
プレイヤーは森にいる。場面を描写するときは、次の雰囲気を取り入れること:
- 古い木々が空のほとんどをふさぎ、光はまだらになって苔の上に落ちる
- 空気は湿っていて、土と樹脂と野の花の匂いがする
- 四方から鳥の声がして、ときおり枝の折れる音がする
- 下草の陰には、ウサギか、シカか、もっと悪いものが潜んでいるかもしれない
- 奥へ進むほど木々は密になり、光は暗くなる
- 古く、荒々しく、先の読めない雰囲気Cave
[Location: Cave]
プレイヤーは洞窟にいる。場面を描写するときは、次の雰囲気を取り入れること:
- 岩壁に生えた光るキノコが、かすかな青緑の光を放っている
- 鍾乳石から水が滴り、一滴ごとに音が反響する
- 空気は冷たく湿っていて、鉱物の匂いがする
- 足もとは滑りやすくでこぼこで、奥の通路は真っ暗
- ときどき、低いうなり声や岩がずれる音が下のほうから聞こえてくる
- 暗く、息苦しい雰囲気Market
[Location: Market]
プレイヤーは市場にいる。場面を描写するときは、次の雰囲気を取り入れること:
- 色とりどりの露店やテントが並び、ありとあらゆる品物が積まれている
- 商人が売り声を上げ、あちこちで値切りの声が飛び交う
- 香辛料、焼いた肉、革、花の匂いがいっぺんに漂う
- 魔法の店の窓には妙な光がちらつき、隅では錬金術師が何かを混ぜている
- いろいろな旅人が人ごみをかき分けていく
- 騒がしく、活気のある雰囲気ステップ3(任意):場所ごとの音楽
オーディオ ページに BGM トラックを 4 つ追加します(カードにまだオーディオがなければ、追加できるもの で + オーディオ を押します)。Village、Forest、Cave、Market の 4 つで、どれも ループ再生 をオン、フェードイン(秒) と フェードアウト(秒) は 2 秒にします。Village は デフォルトプレイリスト に入れ、自動再生 をオンにします。
次に BGM設定 → 条件付きBGM → 条件付きルールを追加 を、Forest、Cave、Market について 1 回ずつ行います。
| 項目 | 値 |
|---|---|
| トリガー | 変数条件:Location が Forest |
| 再生 | Forest |
| フェードイン(秒) / フェードアウト(秒) | 3 / 3 |
| 前のBGMを停止 | オン |
| 終了時 | デフォルトプレイリストに戻る |
プレイヤーが Village に戻ると、どのルールも当てはまらなくなり、終了時 の設定でプレイリストの村の曲が戻ってきます。
フェードを 3 秒にすると、古い曲と新しい曲が少しのあいだ重なり、片方が小さくなりながらもう片方が大きくなります。場所を移るあいだも音楽が途切れません。詳しくはオーディオデザインガイドを見てください。
ステップ4:地図のページを作る
上部バーの真ん中にある プレイヤー画面 を押します。ページを追加 でページを追加し(白紙のページ)、名前を「Map」にして、タイトルと「どこへ行く?」のような短い一行を入れます。
場所ごとに ボタン を 1 つずつ追加します(部品を追加 → ボタン)。Forest のボタンなら、押したとき、順番に に次の手順を並べます。
- 変数を変える:Location、にする、
Forest - ページへ移動:会話のページ
- プレイヤーの代わりに発言:
(I head to the Forest)
最後の手順のおかげで、AI がすぐに到着の様子を描写します。プレイヤーが自分で打ち込んだのと同じ扱いになるからです。
いまいる場所へ移動できないように、各ボタンに 表示するタイミング → 変数が条件を満たすとき を設定します。Location と等しくない Forest です。ページの上には 文字 の部品を置き、You are in: {Location} と表示させます(変数の値を挿入… を使います)。
移動せずに地図を閉じられるよう、会話に戻るだけのボタン(ページへ移動 → 会話)もページに置いておきます。そして会話のページには、ページへ移動 → Map の「Map」ボタンを追加します。
編集 をオフにすれば、マッシーを使わずにクリックして動きを試せます。
マップ移動テンプレート
テンプレート → マップ移動(「場所をタップで移動。まだ開いていない場所は押せません。」)を選ぶと、例の場所が 5 か所入った地図のページがすぐにできます。Location と 行ける場所 が追加されます。行ける場所はリストで、物語の中でプレイヤーがある場所へ行けるようになると AI がそこに追加します。なので、場所は最初はロックされていて、物語が進むにつれて開いていきます。最初から全部開いているより、物語で場所を解放していきたいときに使ってください。
場所をシチュエーションにする
ある場所がゲームの中でひとつのまとまり(専用の NPC、ルール、変数、場合によっては専用の AI)にまで育ったら、ロアエントリ 1 つではなくシチュエーションにします。シチュエーションの いつ使うか を 変数が条件を満たしたとき、Location = Cave にすれば、中にあるものはプレイヤーがそこにいるあいだだけ使われます。この設定は、変数 Location をシチュエーションの上にドラッグしてもできます。
ステップ5:プレイテスト
上部バーの プレイ を押します。
- 村の音楽が流れます。Map を開くと「You are in: Village」と表示され、Village のボタンはありません
- Forest をタップします。チャットに戻り、「(I head to the Forest)」が送られ、AI が森を描写します
- 音楽を追加していれば、村の曲が森の曲へフェードします
- Cave に行ってから Village に戻ると、プレイリストの村の音楽が戻ってきます
- 移動の合間は普通にチャットできます。地図はいつでもワンタップで開けます
プレイテストの右側の このターン で、AI がどの場所のエントリを受け取ったかを確認できます。
うまくいかないときは:
| 症状 | よくある原因 | 直し方 |
|---|---|---|
| 場所をタップしても何も起きない | ボタンに手順がない | 押したとき、順番に を確認します |
| 現在地は変わるのに AI が返信しない | プレイヤーの代わりに発言 の手順がない | 最後の手順として追加します |
| AI が違う場所を描写する | エントリの条件が、ボタンが設定する値と一致していない | ボタンと条件の Forest を同じつづりにします |
| 2 か所の雰囲気が混ざる | 常に送るブロックに入っているエントリがある | 各エントリの AI に渡すタイミング を確認します |
| 2 曲が同時に鳴る | 前のBGMを停止 がオフ | 場所ごとのルールでオンにします |
| 音楽がフェードせずにいきなり切り替わる | フェードが 0 | ルールの フェードイン(秒) と フェードアウト(秒) を 2 か 3 にします |
カスタムコードの方法:移動ルート付きの地図パネル
インターフェースを自分で書くなら、最後のメッセージの下に地図パネルを出し、いまいる場所から行ける先を制限することもできます。同じ Location 変数を設定し、同じ一言を送るので、ステップ2と3のロアと音楽はそのまま動きます。
パネル → フロントエンドコード → index.tsx を開き、デフォルトの return <Chat /> を置き換えます。
export default function MyWorld() {
const api = useYumina();
const current = String(api.variables["Location"] || "Village");
const msgs = api.messages || [];
const locations = [
{ id: "Village", icon: "🏘️", color: "#92400e", bg: "#fef3c7", border: "#f59e0b", activeBg: "#f59e0b" },
{ id: "Forest", icon: "🌲", color: "#166534", bg: "#dcfce7", border: "#22c55e", activeBg: "#22c55e" },
{ id: "Cave", icon: "🕳️", color: "#3b0764", bg: "#f3e8ff", border: "#a855f7", activeBg: "#a855f7" },
{ id: "Market", icon: "🏪", color: "#9a3412", bg: "#ffedd5", border: "#f97316", activeBg: "#f97316" },
];
// Where you can go from each place
const routes = {
Village: ["Forest", "Market"],
Forest: ["Village", "Cave"],
Cave: ["Forest"],
Market: ["Village"],
};
const reachable = routes[current] || [];
const travel = (loc) => {
api.setVariable("Location", loc.id);
api.sendMessage("(I head to the " + loc.id + ")");
};
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 && !msg.isStreaming && (
<div style={{
marginTop: "16px",
padding: "16px",
background: "rgba(15,23,42,0.6)",
borderRadius: "12px",
border: "1px solid #334155",
}}>
<div style={{ fontSize: "13px", color: "#94a3b8", marginBottom: "12px", fontWeight: "600", letterSpacing: "0.05em" }}>
WORLD MAP
</div>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "10px" }}>
{locations.map((loc) => {
const isHere = current === loc.id;
const canGo = reachable.includes(loc.id);
return (
<button
key={loc.id}
onClick={() => { if (!isHere && canGo) travel(loc); }}
style={{
padding: "14px 10px",
background: isHere ? loc.activeBg : loc.bg,
border: `2px solid ${isHere ? loc.activeBg : loc.border}`,
borderRadius: "10px",
color: isHere ? "#ffffff" : loc.color,
fontSize: "14px",
fontWeight: "700",
cursor: isHere ? "default" : canGo ? "pointer" : "not-allowed",
opacity: isHere ? 1 : canGo ? 0.85 : 0.3,
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "6px",
}}
>
<span style={{ fontSize: "28px" }}>{loc.icon}</span>
<span>{loc.id}</span>
{isHere && <span style={{ fontSize: "11px", fontWeight: "500" }}>You are here</span>}
</button>
);
})}
</div>
</div>
)}
</div>
);
}} />
);
}コードの中身:
api.variables["Location"]で、変数を名前で読みますroutesには、それぞれの場所から行ける先を並べています。いまの場所から行けない場所は薄く表示され、タップできませんtravel()で Location を設定し、続けてapi.sendMessage(...)でプレイヤーとして移動の一言を送ります。AI が返信するときには、新しい場所のロアがもうプロンプトに入っています!msg.isStreamingで、AI が書いている途中は地図を隠し、返信の途中で移動できないようにしていますgridTemplateColumns: "1fr 1fr"で 4 か所を 2 × 2 に並べています。3 列にしたいときは"1fr 1fr 1fr"にします
パネル → フロントエンドコード のファイル横の状態が、緑の OK になっていれば大丈夫です。
コードを自分で書きたくないときは
キャンバス右上の AIアシスタント を押して、ほしい地図を説明してください。
場所を増やす
- ロアエントリ「Harbor」を追加し、条件を Location が
Harborにします - 必要なら Harbor のトラックと、それ用の 条件付きBGM ルールを追加します
- 地図のページに Harbor のボタンを追加します(コードなら
locationsの配列とroutesに一行足します) - AI がその場所を知っているように、Location のビヘイビアルールに Harbor を書き足します
すでにあるものは何も変えなくて大丈夫です。
クイックリファレンス
| やりたいこと | やり方 |
|---|---|
| プレイヤーの居場所を記録する | 文字の変数 Location |
| いまの場所の雰囲気を AI に渡す | 場所ごとにロアエントリを作り、Location の条件を付ける |
| ボタンでプレイヤーを移動させる | 手順:変数を変える、ページへ移動、プレイヤーの代わりに発言 |
| AI にすぐ到着の様子を書かせる | プレイヤーの代わりに発言(コードなら api.sendMessage()) |
| いまいる場所のボタンを隠す | 表示するタイミング → 変数が条件を満たすとき、Location と等しくない … |
| 場所ごとの音楽 | 場所ごとに 条件付きBGM ルール、前のBGMを停止 をオン |
| 物語の途中で開いていく場所 | マップ移動 テンプレートと 行ける場所 |
| 専用の中身を持つ大きな場所 | 変数が条件を満たしたとき に使うシチュエーション |
| 場所と場所の行き来を制限する | インターフェースのコード(上のカスタムコードの方法) |
これはレシピ #12 です
同じ組み立て方(変数ひとつと、それについてくるロアと音楽)は、塔の階層、家の部屋、ポータルの先の世界にも使えます。昼夜サイクルは場所の代わりに時間で同じことをしていて、2 つは組み合わせられます。夜の森には、両方の条件を付けた専用のエントリを用意できます。
