リクエスト詳細
✨ 既存アプリの改善
対応完了
対象アプリ: RPGストーリーフォージ AI風ドット絵冒険
戦闘開始時にシナリオ生成済みモンスター画像をCanvasへリアルタイム描画する機能の強化
## 1. 追加・改善する機能の目的
管理者指針「モンスターの画像生成を毎回行う」に対応し、戦闘画面でモンスタースプライトとして使う画像を、シナリオ生成時(create.php のPOST処理)に **その場でSVGベースのプロシージャル生成** で作成し、scenario_json 内に埋め込む。これにより、毎回異なるモンスタービジュアルが戦闘Canvasに表示される。
既存機能(モンスター図鑑、戦闘アニメーション、スプライトシート)を壊さず、monster_image_data フィールドとして拡張する。
---
## 2. 具体的な仕様
### 2-1. PHP側:シナリオ生成時にモンスター画像データを生成
`lib.php` の `rpgsf_create_scenario()` 内、敵配列を確定した直後に `rpgsf_generate_monster_image()` を呼び出し、各モンスターの `image_data` キーへSVGデータURI文字列を格納する。
```php
function rpgsf_generate_monster_image(array $enemy, string $field_type = 'outdoor'): string {
// モンスターの type / tags / field_type をシードに、
// SVG要素(頭・胴・腕・脚・目・色)をプロシージャル生成する
$seed = crc32(($enemy['id'] ?? '') . ($enemy['name'] ?? '') . $field_type);
// 色パレット: dungeon=暗色系, outdoor=緑・茶系, town=なし(出現しない)
$palettes = [
'dungeon' => ['#3a1a4a','#6b2d6b','#c0392b','#1a1a2e','#4a0e0e'],
'outdoor' => ['#2d5a1b','#8b4513','#4169e1','#708090','#556b2f'],
];
$palette = $palettes[$field_type] ?? $palettes['outdoor'];
$col = $palette[abs($seed) % count($palette)];
// bodyShape: 0=丸型, 1=縦長, 2=横長, 3=多角形
$shape = abs($seed >> 4) % 4;
// eye_count: 1〜3
$eyes = 1 + (abs($seed >> 8) % 3);
// 48x48 SVG を data URI で返す
// ... SVG文字列組み立て ...
$svg = '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48">';
// body
if ($shape === 0) $svg .= '<ellipse cx="24" cy="28" rx="16" ry="14" fill="'.$col.'" stroke="#000" stroke-width="2"/>';
elseif ($shape === 1) $svg .= '<rect x="14" y="12" width="20" height="28" rx="4" fill="'.$col.'" stroke="#000" stroke-width="2"/>';
elseif ($shape === 2) $svg .= '<rect x="8" y="18" width="32" height="18" rx="4" fill="'.$col.'" stroke="#000" stroke-width="2"/>';
else $svg .= '<polygon points="24,10 38,38 10,38" fill="'.$col.'" stroke="#000" stroke-width="2"/>';
// eyes
for ($e = 0; $e < $eyes; $e++) {
$ex = 16 + $e * 8;
$svg .= '<circle cx="'.$ex.'" cy="24" r="3" fill="#fff"/><circle cx="'.($ex+1).'" cy="24" r="1.5" fill="#000"/>';
}
$svg .= '</svg>';
return 'data:image/svg+xml;base64,' . base64_encode($svg);
}
```
各 `enemy` 配列に `image_data` が追加される。field_type は enemy の `area_type`('dungeon'/'outdoor')から決定。
### 2-2. scenario_json への格納
`rpgsf_create_scenario()` の enemies 組み立て後:
```php
foreach ($scenario['enemies'] as &$enemy) {
$ft = ($enemy['area_type'] ?? 'outdoor') === 'dungeon' ? 'dungeon' : 'outdoor';
$enemy['image_data'] = rpgsf_generate_monster_image($enemy, $ft);
}
unset($enemy);
```
### 2-3. JS側:戦闘Canvas描画への適用
`play.php` の戦闘Canvas描画ロジックにて、モンスタースプライト描画部分を以下に変更:
```javascript
// 既存: drawMonsterSprite(ctx, monster, x, y, pose)
function drawMonsterSprite(ctx, monster, x, y, pose) {
var imgData = monster.image_data || null;
if (imgData) {
var img = new Image();
img.onload = function() {
// ポーズ別オフセット: attack=+4px右, hit=+2px左, defeat=透明度0.4
var ox = 0, oy = 0, alpha = 1.0;
if (pose === 'attack') ox = 4;
if (pose === 'hit') { ox = -2; alpha = 0.7; }
if (pose === 'defeat') alpha = 0.3;
ctx.globalAlpha = alpha;
ctx.drawImage(img, x + ox, y + oy, 96, 96); // 48px→96pxで2倍描画
ctx.globalAlpha = 1.0;
};
img.src = imgData;
} else {
// フォールバック: 既存のドット絵描画
drawMonsterDotFallback(ctx, monster, x, y, pose);
}
}
```
戦闘開始時に `monster.image_data` を `window._battleMonsterImageCache` へキャッシュし、毎フレーム再生成しない:
```javascript
if (!window._battleMonsterImageCache) window._battleMonsterImageCache = {};
var cacheKey = monster.id + '_' + (monster.image_data || '').slice(-8);
if (!window._battleMonsterImageCache[cacheKey]) {
var img = new Image();
img.src = monster.image_data;
window._battleMonsterImageCache[cacheKey] = img;
}
```
### 2-4. モンスター図鑑への反映
図鑑画面でモンスター表示時、`image_data` が存在すれば `<img src="{image_data}">` を表示。既存のドット絵Canvas描画はフォールバックとして維持。
### 2-5. フィールド種別連動
町・村サブマップ(field_type='town')の敵は image_data を生成しない(そもそも出現しない)。dungeon/outdoor のみ生成対象とすることで処理軽量化。
---
## 3. 既存機能との整合(壊さない点)
- `image_data` は enemy 配列への **追加フィールド** のみ。既存の `type`, `hp`, `atk` 等は変更しない
- `image_data` が null/未定義の場合は既存フォールバック描画が動作するため、古いシナリオデータも壊れない
- シナリオ生成はPHP側で完結し、外部APIキー不要
- scenario_json サイズ増加は1モンスターあたり約200〜400バイト(SVG base64)で許容範囲
- 既存の戦闘アニメーション(待機・攻撃・被弾・撃破ポーズ)はオフセット+透明度で継続対応
管理者指針「モンスターの画像生成を毎回行う」に対応し、戦闘画面でモンスタースプライトとして使う画像を、シナリオ生成時(create.php のPOST処理)に **その場でSVGベースのプロシージャル生成** で作成し、scenario_json 内に埋め込む。これにより、毎回異なるモンスタービジュアルが戦闘Canvasに表示される。
既存機能(モンスター図鑑、戦闘アニメーション、スプライトシート)を壊さず、monster_image_data フィールドとして拡張する。
---
## 2. 具体的な仕様
### 2-1. PHP側:シナリオ生成時にモンスター画像データを生成
`lib.php` の `rpgsf_create_scenario()` 内、敵配列を確定した直後に `rpgsf_generate_monster_image()` を呼び出し、各モンスターの `image_data` キーへSVGデータURI文字列を格納する。
```php
function rpgsf_generate_monster_image(array $enemy, string $field_type = 'outdoor'): string {
// モンスターの type / tags / field_type をシードに、
// SVG要素(頭・胴・腕・脚・目・色)をプロシージャル生成する
$seed = crc32(($enemy['id'] ?? '') . ($enemy['name'] ?? '') . $field_type);
// 色パレット: dungeon=暗色系, outdoor=緑・茶系, town=なし(出現しない)
$palettes = [
'dungeon' => ['#3a1a4a','#6b2d6b','#c0392b','#1a1a2e','#4a0e0e'],
'outdoor' => ['#2d5a1b','#8b4513','#4169e1','#708090','#556b2f'],
];
$palette = $palettes[$field_type] ?? $palettes['outdoor'];
$col = $palette[abs($seed) % count($palette)];
// bodyShape: 0=丸型, 1=縦長, 2=横長, 3=多角形
$shape = abs($seed >> 4) % 4;
// eye_count: 1〜3
$eyes = 1 + (abs($seed >> 8) % 3);
// 48x48 SVG を data URI で返す
// ... SVG文字列組み立て ...
$svg = '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48">';
// body
if ($shape === 0) $svg .= '<ellipse cx="24" cy="28" rx="16" ry="14" fill="'.$col.'" stroke="#000" stroke-width="2"/>';
elseif ($shape === 1) $svg .= '<rect x="14" y="12" width="20" height="28" rx="4" fill="'.$col.'" stroke="#000" stroke-width="2"/>';
elseif ($shape === 2) $svg .= '<rect x="8" y="18" width="32" height="18" rx="4" fill="'.$col.'" stroke="#000" stroke-width="2"/>';
else $svg .= '<polygon points="24,10 38,38 10,38" fill="'.$col.'" stroke="#000" stroke-width="2"/>';
// eyes
for ($e = 0; $e < $eyes; $e++) {
$ex = 16 + $e * 8;
$svg .= '<circle cx="'.$ex.'" cy="24" r="3" fill="#fff"/><circle cx="'.($ex+1).'" cy="24" r="1.5" fill="#000"/>';
}
$svg .= '</svg>';
return 'data:image/svg+xml;base64,' . base64_encode($svg);
}
```
各 `enemy` 配列に `image_data` が追加される。field_type は enemy の `area_type`('dungeon'/'outdoor')から決定。
### 2-2. scenario_json への格納
`rpgsf_create_scenario()` の enemies 組み立て後:
```php
foreach ($scenario['enemies'] as &$enemy) {
$ft = ($enemy['area_type'] ?? 'outdoor') === 'dungeon' ? 'dungeon' : 'outdoor';
$enemy['image_data'] = rpgsf_generate_monster_image($enemy, $ft);
}
unset($enemy);
```
### 2-3. JS側:戦闘Canvas描画への適用
`play.php` の戦闘Canvas描画ロジックにて、モンスタースプライト描画部分を以下に変更:
```javascript
// 既存: drawMonsterSprite(ctx, monster, x, y, pose)
function drawMonsterSprite(ctx, monster, x, y, pose) {
var imgData = monster.image_data || null;
if (imgData) {
var img = new Image();
img.onload = function() {
// ポーズ別オフセット: attack=+4px右, hit=+2px左, defeat=透明度0.4
var ox = 0, oy = 0, alpha = 1.0;
if (pose === 'attack') ox = 4;
if (pose === 'hit') { ox = -2; alpha = 0.7; }
if (pose === 'defeat') alpha = 0.3;
ctx.globalAlpha = alpha;
ctx.drawImage(img, x + ox, y + oy, 96, 96); // 48px→96pxで2倍描画
ctx.globalAlpha = 1.0;
};
img.src = imgData;
} else {
// フォールバック: 既存のドット絵描画
drawMonsterDotFallback(ctx, monster, x, y, pose);
}
}
```
戦闘開始時に `monster.image_data` を `window._battleMonsterImageCache` へキャッシュし、毎フレーム再生成しない:
```javascript
if (!window._battleMonsterImageCache) window._battleMonsterImageCache = {};
var cacheKey = monster.id + '_' + (monster.image_data || '').slice(-8);
if (!window._battleMonsterImageCache[cacheKey]) {
var img = new Image();
img.src = monster.image_data;
window._battleMonsterImageCache[cacheKey] = img;
}
```
### 2-4. モンスター図鑑への反映
図鑑画面でモンスター表示時、`image_data` が存在すれば `<img src="{image_data}">` を表示。既存のドット絵Canvas描画はフォールバックとして維持。
### 2-5. フィールド種別連動
町・村サブマップ(field_type='town')の敵は image_data を生成しない(そもそも出現しない)。dungeon/outdoor のみ生成対象とすることで処理軽量化。
---
## 3. 既存機能との整合(壊さない点)
- `image_data` は enemy 配列への **追加フィールド** のみ。既存の `type`, `hp`, `atk` 等は変更しない
- `image_data` が null/未定義の場合は既存フォールバック描画が動作するため、古いシナリオデータも壊れない
- シナリオ生成はPHP側で完結し、外部APIキー不要
- scenario_json サイズ増加は1モンスターあたり約200〜400バイト(SVG base64)で許容範囲
- 既存の戦闘アニメーション(待機・攻撃・被弾・撃破ポーズ)はオフセット+透明度で継続対応
💬 返信 (3)
🛠 開発を開始しました (機能追加 (rpg-story-forge))
ご要望ありがとうございます。AI 開発ワーカーが実装を開始します。
通常 5〜30 分で Pull Request を作成し、レビュー後にリリースされます。
ご要望ありがとうございます。AI 開発ワーカーが実装を開始します。
通常 5〜30 分で Pull Request を作成し、レビュー後にリリースされます。
📝 開発が完了しました
ご要望いただいた内容の実装が完了し、最終チェック段階に入りました。
レビュー (自動) → リリース、の流れで進みます。
もう少々お待ちください。
ご要望いただいた内容の実装が完了し、最終チェック段階に入りました。
レビュー (自動) → リリース、の流れで進みます。
もう少々お待ちください。
✅ リリース完了のお知らせ
ご要望いただいた「RPGストーリーフォージ AI風ドット絵冒険」を実装し、リリースいたしました。
【ご利用方法】
ダッシュボード: https://www.aiapps.jp/?action=dashboard
アプリ詳細: https://www.aiapps.jp/apps/show.php?slug=rpg-story-forge
デモ環境は 1 時間以内に自動構築されます:
https://www.aiapps.jp/demo/rpg-story-forge/
ご利用ありがとうございます!
ご要望いただいた「RPGストーリーフォージ AI風ドット絵冒険」を実装し、リリースいたしました。
【ご利用方法】
ダッシュボード: https://www.aiapps.jp/?action=dashboard
アプリ詳細: https://www.aiapps.jp/apps/show.php?slug=rpg-story-forge
デモ環境は 1 時間以内に自動構築されます:
https://www.aiapps.jp/demo/rpg-story-forge/
ご利用ありがとうございます!
Echo
Iris