
AIとの対話でコンテンツを追加してみた③~AI使って知識0(ゼロ)から始めるWEBサービス・アプリ制作21~
今回はリリース中のAndroidアプリ「イチオシ!(1押し)」に今後アップデートで追加予定の「カスタマイズ機能」を先行してWEBアプリ側に追加してみることにしました。
今回追加したカスタマイズ機能は

紛らわしいものから正解を探すゲームなので、ユーザーが自分で好きな画像をアップロードして遊べるようにしてみました。

画像はサーバーに保管されませんので、安心してアップロードしてみてください。紛らわしければ紛らわしいほど面白いと思います。
ゲームはここからアクセスできます

本当は家紋ではなく、
・ザたっちさん
・上白石萌歌さんと萌音さん
みたいな双子画像とか、
・正岡子規と小峠さん
みたいによく似ているお二人とかで画像を載せたかったのですが、何も許可取ってないので断念しました。
本当の本音は志村けんさんのバカ殿でやりたかった・・・(笑)
サーバーには画像が保管されませんので、個人利用の範囲で遊んでみてください!
実装の手順としては
・元のコードにhtmlタグを追加
<!-- game-containerの中に追加 -->
<div id="customize-section">
<h2>ゲームカスタマイズ</h2>
<p>ブラウザを再読み込みするとリセットします。画像はサーバーに保存されません。最大3MBまでの画像をアップロードできます。</P>
<div class="custom-inputs">
<div>
<label>ターゲット画像:</label>
<input type="file" id="target-image" accept="image/*">
<img id="target-preview" style="max-width: 50px; display: none;">
</div>
<div>
<label>その他の画像1:</label>
<input type="file" id="other-image1" accept="image/*">
<img id="other1-preview" style="max-width: 50px; display: none;">
</div>
<div>
<label>その他の画像2:</label>
<input type="file" id="other-image2" accept="image/*">
<img id="other2-preview" style="max-width: 50px; display: none;">
</div>
</div>
<button id="apply-custom">カスタム設定を適用</button>
</div>
cssは省略して、JavaScriptで画像の読み込みなどを制御
function handleImageUpload(input, previewId, imageType) {
const file = input.files[0];
if (file) {
if (file.size > 3 * 1024 * 1024) {
alert('画像サイズは3MB以下にしてください');
input.value = '';
return;
}
const reader = new FileReader();
reader.onload = function(e) {
const preview = document.getElementById(previewId);
preview.src = e.target.result;
preview.style.display = 'block';
customImages[imageType] = e.target.result;
};
reader.readAsDataURL(file);
}
}
そしてカスタム時のゲームボード設定
function createBoard(stage) {
if (isCustomMode) {
createCustomBoard(stage);
} else {
gameBoard.innerHTML = '';
gameBoard.style.gridTemplateColumns = `repeat(${stage.columns}, 1fr)`;
const targetIndex = Math.floor(Math.random() * stage.cells);
for (let i = 0; i < stage.cells; i++) {
const cell = document.createElement('div');
cell.classList.add('cell');
if (i === targetIndex) {
cell.textContent = stage.targetContent;
cell.addEventListener('click', () => handleCellClick(true));
} else {
cell.textContent = typeof stage.otherContent === 'function' ? stage.otherContent(i) : stage.otherContent;
cell.addEventListener('click', () => handleCellClick(false));
}
gameBoard.appendChild(cell);
}
}
}
function createCustomBoard(stage) {
gameBoard.innerHTML = '';
gameBoard.style.gridTemplateColumns = `repeat(${stage.columns}, 1fr)`;
const targetIndex = Math.floor(Math.random() * stage.cells);
for (let i = 0; i < stage.cells; i++) {
const cell = document.createElement('div');
cell.classList.add('cell');
const img = document.createElement('img');
img.style.width = '100%';
img.style.height = '100%';
img.style.objectFit = 'contain';
if (i === targetIndex) {
img.src = customImages.target;
cell.addEventListener('click', () => handleCellClick(true));
} else {
img.src = Math.random() < 0.5 ? customImages.other1 : customImages.other2;
cell.addEventListener('click', () => handleCellClick(false));
}
cell.appendChild(img);
gameBoard.appendChild(cell);
}
}
近日中にiOS版もリリースしようと、コードを改定中です。
※cordova環境なので、APPLEが準備してくれているゲームセンターのリーダーボード機能実装に苦戦中です・・・
ぜひカスタマイズして遊んでみてくださいね!