集めた仲間によって評価が変わるRPGの作成
~タイトル画面/ゲーム内メニュー/会話選択肢/仲間加入まで~
こんにちは、東京経済大学3年のSTです。
前回は、NPCの配置と、会話用のコードを仕込みました。
今回の作業では、タイトル画面/ゲーム内メニュー/会話選択肢/仲間加入までのコードを仕込んでいきたいと思います。
今回のコードを拡張し、シナリオやフラグを次回からやっていこうと思います。
設計の核
画面モード:title / play / menu の3状態を gameMode で切り替え
進行管理
UIの軸
flags: 条件(王に会った?ボス撃破?初回会話?など)party: 仲間リスト(表示・評価に使用)items: あとで使う所持品UIの軸
showMsg(title, text): メッセージボックスshowChoice(title, text, options, onDecide): Yes/No 等の選択肢この構成にすると、ほぼ全てのイベントが「会話→選択→フラグ更新」で表せます。後からなにか足すときも、同じ枠組みで自然に拡張できます。
タイトル画面
最初の2択だけに絞る
タイトルは「ニューゲーム/コンテニュ」の2択に限定。ロード可否は
タイトルは「ニューゲーム/コンテニュ」の2択に限定。ロード可否は
localStorage の有無で自動案内。主要コード(抜粋)
function newGame(){
stage=0; px=10; py=5; onBoat=false; boatDir='down';
party=[]; items=[]; flags={};
setFlag("metKing", false); // 王様に会ったか
setFlag("bossCleared", false);
gameMode='play';
showMsg("ようこそ","仲間を集めて海を渡り、島のボスを倒そう。");
}
function backToTitle(){ gameMode='title'; }
function hasSave(){ return !!localStorage.getItem("buddyAssembleSave_v1"); }
ゲーム内メニューEscで開く 情報ハブ
ポーズではなくメニューとして扱い、内容は「仲間/アイテム/セーブ/タイトルへ戻る」。ここでは確認系(仲間一覧・所持品)と行動系(セーブ・タイトルへ)を同居させ、迷わない構造に
主要コード(抜粋)
let menuIndex = 0;
function openMenu(){ gameMode='menu'; menuIndex=0; }
function handleMenuSelect(label){
if(label==="仲間"){
party.length
? showMsg("仲間","現在の仲間:\n- " + party.join("\n- "))
: showMsg("仲間","まだ誰も仲間になっていません。");
}else if(label==="アイテム"){
items.length
? showMsg("アイテム","所持アイテム:\n- " + items.join("\n- "))
: showMsg("アイテム","何も持っていません。");
}else if(label==="セーブ"){
saveGame();
}else if(label==="タイトルへ戻る"){
showChoice("確認","タイトルへ戻りますか?(セーブ推奨)",["はい","いいえ"],(i)=>{
if(i===0) backToTitle();
});
}
}
会話はX/Enterで。NPCに文字列を持たせても良いし、関数を持たせても良いようにしました。
主要コード(抜粋)
let msgBox=null, choiceBox=null;
function showMsg(title, text){ msgBox={title,text}; }
function showChoice(title, text, options, onDecide){
choiceBox = { title, text, options: options||["はい","いいえ"], index:0, onDecide };
}
// NPCの例:説明係(guard4)
{ active:true, name:"親切な衛兵1", sprite:"guard4", stage:0, x:9, y:2, solid:true, talk:function(){
showMsg("親切な衛兵","【基本操作】\n矢印:移動 / X/Enter:会話 / Z:船 / Esc:メニュー");
}}
// NPCの例:会話がロジックを持つ(王様)
{ active:true, name:"王様", sprite:"king", stage:1, x:10, y:4, solid:true, talk:function(){
const first = !hasFlag('metKing');
setFlag('metKing', true);
showMsg("王様", first ? "よく来た。仲間を集め、船で島へ向かえ。"
: "進捗はどうだ? 仲間は集まっておるか。");
}}
設計意図
文字列→静的セリフ、関数→イベント。
会話UIは閉じる操作を統一(X/Enter)
仲間になる仕組みフラグ → 選択肢 → party.push
加入候補のNPCは、条件フラグを見て勧誘可否を変えます。今回は「王様に会っていること(metKing)」を最低条件に設定しました。
主要コード(抜粋)
let party = [], items = [], flags = {};
function setFlag(key, v=true){ flags[key]=v; }
function hasFlag(key){ return !!flags[key]; }
function addParty(name){ if(!party.includes(name)) party.push(name); }
function inParty(name){ return party.includes(name); }
// 加入候補:戦士A
{ active:true, name:"戦士A", sprite:"warrior1", stage:1, x:1, y:2, solid:true, talk:function(){
if(inParty("戦士A")){ showMsg("戦士A","もう一緒に戦っているさ。"); return; }
if(!hasFlag("metKing")){ showMsg("戦士A","王に会ってから来な。"); return; }
showChoice("戦士A","俺を仲間に入れるか?",["はい","いいえ"], function(i){
if(i===0){ addParty("戦士A"); showMsg("戦士A","よし、背中は任せろ。"); }
else { showMsg("戦士A","そうか。気が変わったらまた来い。"); }
});
}}
拡張のコツ
条件は増やしてOK(例:hasFlag('metKing') && items.includes('推薦状'))
同じ枠で裏シナリオ(ボス加入)も実現可能撃破後に「説得」選択肢を出すだけ
主要コード(抜粋)
function saveGame(){
const data = { stage, px, py, onBoat, boatDir, party, items, flags };
localStorage.setItem(SAVE_KEY, JSON.stringify(data));
showMsg("セーブ","冒険の記録を保存しました。");
}
function loadGame(){
const raw = localStorage.getItem(SAVE_KEY);
if(!raw){ showMsg("ロード","セーブデータが見つかりません。"); return; }
const d = JSON.parse(raw);
stage=d.stage; px=d.px; py=d.py; onBoat=d.onBoat; boatDir=d.boatDir;
party = Array.isArray(d.party)? d.party: [];
items = Array.isArray(d.items)? d.items: [];
flags = (d.flags && typeof d.flags==='object')? d.flags: {};
gameMode='play';
showMsg("ロード","再開しました。");
}
複数スロットは SAVE_KEY_1/2/3 で簡単に増やせる
操作まとめ(実装版)
移動:矢印キー
会話:X / Enter(プレイヤーの正面にNPCがいると発火)
メニュー:Esc(仲間/アイテム/セーブ/タイトルへ戻る)
船の乗降:Z(船タイル上で乗る、陸で降りる)
タイトル:Enter/X=ニューゲーム、C=コンテニュ
ここまででできること
完成したこと
タイトル2択(ニューゲーム/コンテニュ)
Escメニュー(仲間・アイテム・セーブ・タイトルへ)
会話UI(メッセージ/選択肢)
フラグ・仲間システム(加入・分岐)
セーブ/ロード(localStorage)
次の工程
イベント基盤の共通化:eventOnce(flag, fn) などのテンプレ化
キーアイテム導線:非加入NPC(おじいさん・メイド等)を使った入手フロー
島ダンジョン:簡易パズル+回転会話(同じNPCでセリフ変化)
ボス戦・説得:選択肢で撃破/勧誘→エンディングフラグ分岐
評価ルーチン:party と主要フラグで複数エンディング
セーブスロット・設定(音量/キーガイド)の追加