はじめに
こちらの記事は、Axmol Engine なるものを使ってバイブコーディングでどこまでできるかを、検証と勉学を兼ねた試験的な目論見となります!
- 第1回【Axmol Engine】 バイブコーディングで脱出ゲームを作る[ 1 ]:スプラッシュ画面シーンを実装する
- 第2回【Axmol Engine】 バイブコーディングで脱出ゲームを作る[ 2 ]:タイトルシーンを作って画像を表示する(iPhone/Android対応)
- 第3回【Axmol Engine】 バイブコーディングで脱出ゲームを作る[ 3 ]:タップした領域への移動
- 第4回【Axmol Engine】 バイブコーディングで脱出ゲームを作る[ 4 ]:ノードを使用してポップアップを実装する
- 第5回【Axmol Engine】 バイブコーディングで脱出ゲームを作る[ 5 ]:テクスチャアトラスでダイヤル錠UIを実装する
- 第6回【Axmol Engine】 バイブコーディングで脱出ゲームを作る[ 6 ]:リファクタリング(メモリ消費対応、ステージをJSON管理、処理修正)+戻るボタン設置
- 第7回【Axmol Engine】 バイブコーディングで脱出ゲームを作る[ 7 ]:ローカルストレージでステージ進捗管理・ギミックをJSONで管理
- 第8回【Axmol Engine】 バイブコーディングで脱出ゲームを作る[ 8 ]:アイテムシステムの実装1 - アイテムの取得 - (今回)
前回の記事ではステージ・ギミックのJSON設定化を行いました。
今回はアイテムの定義・ステージへの配置・取得・所持パネル表示までを実装します。
やったこと
- アイテム関連画像作成
- アイテム情報定義
- ステージ定義にアイテム取得情報追加
- アイテム未所持の場合にステージにアイテムが置いてある画像を表示
- アイテム所持後はステージにアイテム画像を表示しない
- アイテム所持後は右側のアイテムスロットゾーンにアイテムアイコンを表示
まずは画像をChatGPTに描いてもらいましたので、Contentに配置してclaudeにメモリしてもらいます
プロンプト:
Content/item_002.png ... 郵便受けに置いてある鍵画像
Content/items/item_01.png ... アイテム:鍵
を配置しましたので覚えておいてください
郵便受けを開けた状態の背景画像上に重ねることで鍵が置いてある状況を演出

Step 1. ステージにアイテムを配置する(stages.json)
プロンプト:
stages.jsonの"openpost"に 配列オブジェクト"items" を追加してください
"items"には
背景上に重ねて表示するアイテム画像:"item_002.png"、
アイテム番号:1
を設定
アイテム画像ファイル名はステージと連動している名前が分かりやすいですです
各ステージ定義に items 配列を追加します。itemNo は後述のローカルストレージキー生成と統一するため、文字列型にします。
"openpost": [
{
"bg": "bg_stage_002_2.png",
"touches": [],
"back": "infront",
"items": [
{ "image": "item_002.png", "itemNo": "1" }
]
}
]
Step 2. アイテム定義ファイルを作成する(items.json)
プロンプト:
items.jsonを作成してください
オブジェクトを定義して、文字列のアイテム番号をキーとしてください
アイテム番号:1
アイテム名称:金の鍵
アイテム画像:items/item_01_1.png
アイテム番号(文字列)をキーにしたオブジェクトです。
{
"1": {
"name": "金の鍵",
"image": "items/item_01_1.png"
}
}
Step 3. items.json を C++ に読み込む
プロンプト:
メインシーンの初期処理で、items.jsonをマップで保持しておいてください
構造体と static 変数
struct ItemDef {
std::string name;
std::string image;
};
static std::unordered_map<std::string, ItemDef> s_itemDefs;
loadItemDefs()
static void loadItemDefs()
{
auto json = ax::FileUtils::getInstance()->getStringFromFile("items.json");
rapidjson::Document doc;
doc.Parse(json.c_str());
if (doc.HasParseError() || !doc.IsObject())
return;
s_itemDefs.clear();
for (auto it = doc.MemberBegin(); it != doc.MemberEnd(); ++it)
{
std::string key = it->name.GetString();
const auto& entry = it->value;
s_itemDefs.emplace(key, ItemDef{
.name = entry["name"].GetString(),
.image = entry["image"].GetString(),
});
}
}
init() で呼び出します。
loadStageDefs();
loadGimmickDefs();
loadItemDefs();
Step 4. ステージ表示時にアイテム画像を重ねる
プロンプト:
各ステージ表示時に、stages.jsonの"items" > "itemNo" を参照して、
設定されていれば、ローカルストレージの"item_"を参照、その値をアイテムのステータスとして扱い、まだ保存されていなければ"0"として、
"0"であれば"image"の画像を表示
まず StageDef に items フィールドを追加します。
struct StageItem {
std::string image;
std::string itemNo;
ax::Rect rect;
bool hasRect = false;
};
struct StageDef {
std::string bg;
std::vector<TouchZone> touches;
std::string back;
std::vector<StageItem> items; // 追加
};
loadStageDefs() でパースします。
if (entry.HasMember("items"))
for (const auto& item : entry["items"].GetArray())
{
StageItem si;
si.image = item["image"].GetString();
si.itemNo = item["itemNo"].GetString();
if (item.HasMember("rect"))
{
const auto& r = item["rect"].GetArray();
si.rect = ax::Rect(r[0].GetFloat(), r[1].GetFloat(),
r[2].GetFloat(), r[3].GetFloat());
si.hasRect = true;
}
def.items.push_back(std::move(si));
}
showStageBg() の loadAndFadeIn 内、FadeIn 実行直前でアイテム画像を背景スプライトの子として追加します。背景スプライト(_bg)の子にすることで、ステージ遷移時のフェードアウト・削除と同時に消えます。
auto* ud = ax::UserDefault::getInstance();
for (const auto& item : def.items)
{
std::string status{ud->getStringForKey(("item_" + item.itemNo).c_str(), "0")};
if (status != "0")
continue;
auto itemSp = Sprite::create(item.image);
if (!itemSp)
continue;
auto bgSize = sp->getContentSize();
itemSp->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
itemSp->setPosition(Vec2(bgSize.width / 2, bgSize.height / 2));
sp->addChild(itemSp, 1);
}
sp->runAction(FadeIn::create(duration));
Step 5. アイテムをタップして取得する
プロンプト:
"items"のオブジェクトに"rect" を追加してください
"post" の "touches" 領域と同じで良いです
アイテムのステータスが "0" で、"rect"の領域をタップした場合、
- アイテムのステータスを"1" に更新
- 同じステージを再描画
stages.json の items に rect を追加します。
"items": [
{ "image": "item_002.png", "itemNo": "1", "rect": [550, 51, 570, 760] }
]
onTouchesBegan() の touches ループの後にアイテムタップ判定を追加します。
for (const auto& item : it->second[_stageStatus].items)
{
if (!item.hasRect)
continue;
std::string status{ax::UserDefault::getInstance()
->getStringForKey(("item_" + item.itemNo).c_str(), "0")};
if (status != "0")
continue;
if (!item.rect.containsPoint(local))
continue;
auto* ud = ax::UserDefault::getInstance();
ud->setStringForKey(("item_" + item.itemNo).c_str(), "1");
ud->flush();
refreshItemPanel();
showStageBg();
return;
}
タップで "item_1" = "1" がローカルストレージに保存され、再描画時にアイテム画像が消えます。
Step 6. 所持アイテム表示パネルを作成する
プロンプト:
画面右側に、z軸上、背景画像より上、ポップアップより下に、幅 200px、縦画面高さいっぱいに、黒+透過50%のノードを作成
init() 内で LayerColor を生成します。z=5(背景 z=0 より上、ポップアップ z=10 より下)。タグ 500 で後から参照できるようにします。
constexpr float panelWidth = 200.0f;
auto rightPanel = LayerColor::create(
Color32(0, 0, 0, 128), panelWidth, visibleSize.height);
rightPanel->setPosition(Vec2(origin.x + visibleSize.width - panelWidth, origin.y));
rightPanel->setTag(500);
this->addChild(rightPanel, 5);
Step 7. refreshItemPanel() を実装する
プロンプト:
このノードにアイテムを表示するメソッドを作成
items.json の内容を参照し、ローカルストレージをチェックして、ステータス"1"のアイテムの"image"を上から順に表示
アイテム番号(数値)順にソートして上から積み下げます。画像はスケールせず実サイズで表示します。
void MainScene::refreshItemPanel()
{
auto* panel = this->getChildByTag(500);
if (!panel)
return;
panel->removeAllChildren();
// アイテム番号順にソート
std::vector<std::string> keys;
keys.reserve(s_itemDefs.size());
for (const auto& kv : s_itemDefs)
keys.push_back(kv.first);
std::sort(keys.begin(), keys.end(),
[](const std::string& a, const std::string& b) {
return std::stoi(a) < std::stoi(b);
});
auto* ud = ax::UserDefault::getInstance();
float panelWidth = panel->getContentSize().width;
float panelHeight = panel->getContentSize().height;
constexpr float margin = 10.0f;
float y = panelHeight - margin;
for (const auto& key : keys)
{
std::string status{ud->getStringForKey(("item_" + key).c_str(), "0")};
if (status != "1")
continue;
const auto& item = s_itemDefs.at(key);
auto sp = Sprite::create(item.image);
if (!sp || sp->getContentSize().width == 0)
continue;
float h = sp->getContentSize().height;
sp->setAnchorPoint(Vec2::ANCHOR_MIDDLE_TOP);
sp->setPosition(Vec2(panelWidth / 2, y));
panel->addChild(sp);
y -= h + margin;
}
}
呼び出しタイミング
// init() — 再起動後の復元
showStageBg();
refreshItemPanel();
// アイテムタップ取得後
ud->setStringForKey(("item_" + item.itemNo).c_str(), "1");
ud->flush();
refreshItemPanel();
showStageBg();
成果物
郵便受けを開けると鍵が・・・
bg_stage_002_2.pngの上にitem_002.pngを描画して鍵が置いてある演出をしています
タップすると鍵を取得、右の所持アイテムスロットゾーンに表示されました!
まとめ
| ファイル | 役割 |
|---|---|
items.json |
アイテム番号をキーとした名称・画像の定義 |
stages.json の items
|
ステージ上の演出画像・タップ取得エリア |
UserDefault["item_<no>"] |
アイテム取得状態("0" 未取得 / "1" 取得済) |
refreshItemPanel() |
所持アイテムを右パネルに反映 |
アイテムを追加する際は items.json にエントリを追加し、配置したいステージの items に画像とタップ矩形を設定するだけで対応できます。



