※本コンテンツは、金銭的価値・当たり/ハズレ・ギャンブル性を含まない
ランダム生成のゲームです。
実際の金銭取引やギャンブル行為とは一切関係ありません。
■72個目 「お楽しみボックスシミュレーター」
こんにちは、AIと作る100のゲーム実験室の みー です。(*^_^*)/
今回は、みんながワクワクする「開封の瞬間」をテーマにした
ランダム生成ゲームを作ってみました。
お正月の福袋のような“当たり/ハズレ”ではなく、
純粋に中身が毎回変わる楽しさだけを味わえる実験ゲームです。
🎁 今回のこだわりポイント
- 開ける瞬間のワクワク感
- 1000種類以上の豊富なアイテムバリエーション
- 毎回ちがう組み合わせが出てくる楽しさ
「お楽しみボックス」を開けると、
ランダムに選ばれた5つのアイテムが表示されます。
どんな組み合わせになるかは毎回違うので、
ちょっとした“ミニ実験”のような気持ちで楽しめます。(^v^)
🏷 アイテムのカテゴリ
アイテムは、世界観に合わせて6つのカテゴリに分かれています。
金銭的な価値ではなく、ゲーム内の雰囲気づくりのための分類です。
- スペシャルアイテム
- レアアイテム
- 良いアイテム
- ふつうアイテム
- ユニークアイテム
- ジョークアイテム
それぞれの例を少し紹介すると……
● スペシャルアイテム
・無人島の模型 ・宇宙旅行のパンフレット ・伝説の聖剣(おもちゃ)
● レアアイテム
・最新スマートフォンのミニチュア ・松阪牛のぬいぐるみ ・高級自転車の模型
● 良いアイテム
・ノイズキャンセリングイヤホン風キーホルダー ・全自動コーヒーメーカーのミニ模型 ・国産うなぎの食品サンプル
● ふつうアイテム
・特選コーヒー豆のパッケージ風カード ・人気ボードゲームのミニチップ ・今治タオルの布サンプル
● ユニークアイテム
・マスター特製ステッカー ・謎の木彫りの熊(ミニサイズ) ・派手な柄の靴下(イラスト)
● ジョークアイテム
・道端の石ころ(イラスト) ・ちぎれた輪ゴム(イラスト)
などなど……。(^^;)
🎉 開封演出も盛りだくさん
お楽しみボックスを開けると、
5つのアイテムが「バババン!」と表示される演出が入っています。
さらに、アイテムの組み合わせによって
背景の色や演出が少し変わる仕掛けもあります。
- スペシャルアイテムが多いとキラキラ演出
- ユニークアイテムが多いとちょっとゆるい演出
- ジョークアイテムが多いとコミカル演出
視覚的にも楽しい仕上がりになっています。(^v^)
🌈 まずは世界観を楽しんでね
操作方法は記事の後半で紹介しますので、
まずはこの“お楽しみボックス”の世界観を
ゆっくり味わっていただけたら嬉しいです。(^v^人)
■ AIとのやりとりで面白かった部分
● ゲーム仕様の相談からスタート
みー:(^v^) 今日もゲームを作ります。HTML形式で動くゲーム。
今回は「お楽しみボックスシミュレーター」の提案です。
- 1000種類のアイテム
- レア度の幅が広いアイテム構成
- 1ボックスにつき5アイテム
- 毎回ちがう組み合わせが出るランダム生成
- 開封すると5つのアイテムが文字で表示
- アイテムの組み合わせによって演出が変わる
こんな感じのワクワクするゲームを作りたいです。
AI: いらっしゃい!今日も来てくれたんだね。
まずは「ボックス文化」の雑学をひとつ──(雑学パート)
そして、あなたのアイデア通りに1000アイテムを用意しておいたよ。
レア度のバランスも調整済みさ。
● アイテムの幅をもっと広げたい
みー:(=v=) 商品名の種類をもっと増やせるかな?
レア度の高い“特別アイテム”も、ほんの少しだけ入れたいです。
AI: じゃあ今日の雑学は「小さな宝物」の話を──(雑学パート)
要望通り、
- スペシャルアイテム
- ジョークアイテム
を極小確率で追加しておいたよ。
● 演出の強化
みー:(^v^) 特別な組み合わせが出た時に、紙吹雪を降らせたいです。
スペシャルアイテムが多い時は、特別な色の紙吹雪で。
AI: 今日の雑学は「紙吹雪の語源」を──(雑学パート)
スペシャルアイテムが多い時はキラキラ紙吹雪、
ユニークアイテムが多い時はゆるい紙吹雪が舞うようにしたよ。
● コミカル演出も入れたい
みー:(めゅ)_ コミカル演出も入れましょう。
ジョークアイテムが出たら、ちょっと笑える演出で。
AI: 今日の小話は「失敗から生まれた発明」──(雑学パート)
ジョークアイテムが出た時の“コミカル演出”を追加したよ。
画面が少し暗くなったり、ゆるい効果音が鳴ったりするよ。
■ 実際の動作
● ゲームのスタート画面

(画像:スタート画面)
お楽しみボックスを開くボタンを押すと、
ボックスがガサガサ揺れるアニメーションが入り、
「何が出るかな?」というワクワク感が高まります。
● ボックス開封の瞬間

(画像:ボックス開封画面)
ボックスが開くと、
ランダムに選ばれた5つのアイテムが「ババン!」と表示されます。
毎回ちがう組み合わせになるので、
小さなサプライズのような楽しさがあります。
● アイテムの組み合わせによる演出

(画像:ゆるい演出)
ユニークアイテムが多い時は、
背景にゆるい雨が降る演出が入ります。
ちょっとしたコミカルさがあって楽しい雰囲気です。

(画像:キラキラ演出)
スペシャルアイテムが多い時は、
画面いっぱいに紙吹雪が舞い上がる“お祝い演出”になります。
一気に華やかなムードに。

(画像:特別演出)
さらに、特別な組み合わせが出た時は、
少し豪華な色の紙吹雪が降る“スペシャル演出”も用意しています。
■ コードのポイント
今回の記事では、AI と相談しながら作った
「お楽しみボックスシミュレーター」 の HTML コードを公開します。
このコードをそのまま保存するだけで、
あなたのPCでもすぐに遊べます。
ブラウザだけで動く、とてもシンプルなゲームなので、
気軽に試してみてくださいね。
▼ 遊ぶまでの手順
①PC の「メモ帳」(※Windowsの場合)や
Mac の「テキストエディット」など、テキストエディタを開きます。
②下のコードをコピーして貼り付けます。
③「otanosimi.html」など、好きな名前で保存します。
(※拡張子は .html にしてください)
④保存したファイルをブラウザで開けば、すぐに遊べます。
■「お楽しみボックスシミュレーター」操作方法♪
操作はとってもシンプル! ボタンをぽちぽち押すだけです。(^v^)
ステップ1:ボックスを開く準備をする
ゲームを開始すると、画面中央に可愛い
「🎁」アイコンと 大きな 「ボックスを開く」 ボタンが表示されます。
このボタンをクリックしてください。
ステップ2:ボックスを開ける!
ボタンを押すと画面が切り替わり、
「🛍️」アイコンと 「開封する」 ボタンが登場します。
心の準備ができたら、このボタンをクリック(タップ)!
すると…… ボックスがガタガタと揺れ始めます。 w(゜o゜)w
約1.5秒ほどの「開封中…」演出。
ここが一番ワクワクする瞬間です。(・人・)
ステップ3:中身を確認して、もう一度!
開封演出が終わると、
ランダムに選ばれた5つのアイテムが バババン! と表示されます。
毎回ちがう組み合わせになるので、
小さなサプライズのような楽しさがあります。(^^)
■ 組み合わせに応じた演出
- スペシャルアイテムが多い時:紙吹雪が舞う“お祝い演出” 🎉
- ユニークアイテムが多い時:ゆるい雨が降るコミカル演出 ☔
- ジョークアイテムが多い時:画面が少し暗くなる“ゆるい演出” 💀
どの演出になるかは、アイテムの組み合わせ次第です。
■ 最後に
いかがでしたか?(^^*) 操作はとってもシンプルで、
ボタンをぽちぽち押すだけ。
その裏では、1000個のアイテムから何が選ばれるか、
小さなドラマが毎回生まれています。(=v=*)
ぜひ、あなたの お気に入りの組み合わせ を
見つけてみてくださいね!(^_^)ノ
“コードは長いので折りたたんでいます。必要な方は開いてご覧ください。”
▼ここからコード▼(クリックで開く)
<html lang="ja"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>お楽しみボックスシミュレーター</title><style> body { text-align: center; background-color: #f0f8ff; font-family: 'Hiragino Maru Gothic ProN', 'Comic Sans MS', sans-serif; margin: 0; padding: 20px; color: #333; overflow: hidden; transition: background-color 0.5s, color 0.5s; } #game-outer-container { position: relative; max-width: 500px; margin: 0 auto; } #effect-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 100; } #game-container { background-color: #fff; border: 4px solid #ff4500; border-radius: 15px; padding: 30px; box-shadow: 0 10px 20px rgba(0,0,0,0.1); position: relative; z-index: 10; transition: all 0.5s; } h1 { color: #ff4500; font-size: 24px; transition: color 0.5s; } .bag-icon { font-size: 100px; margin: 20px 0; cursor: pointer; transition: transform 0.2s; } .bag-icon:hover { transform: scale(1.1); } .shake { animation: shake 0.5s infinite; } @keyframes shake { 0% { transform: translate(1px, 1px) rotate(0deg); } 10% { transform: translate(-1px, -2px) rotate(-5deg); } 20% { transform: translate(-3px, 0px) rotate(5deg); } 30% { transform: translate(3px, 2px) rotate(0deg); } 40% { transform: translate(1px, -1px) rotate(5deg); } 50% { transform: translate(-1px, 2px) rotate(-5deg); } 60% { transform: translate(-3px, 1px) rotate(0deg); } 70% { transform: translate(3px, 1px) rotate(-5deg); } 80% { transform: translate(-1px, -1px) rotate(5deg); } 90% { transform: translate(1px, 2px) rotate(0deg); } 100% { transform: translate(1px, -2px) rotate(-5deg); } } button { background-color: #ff8c00; color: white; border: none; padding: 15px 30px; font-size: 18px; border-radius: 8px; cursor: pointer; font-weight: bold; box-shadow: 0 4px 6px rgba(0,0,0,0.2); } button:hover { background-color: #ff7f50; } button:disabled { background-color: #ccc; cursor: not-allowed; } #result-area { display: none; margin-top: 20px; } .item-list { list-style-type: none; padding: 0; text-align: left; background: #fffafa; border: 2px dashed #ffa07a; padding: 15px; border-radius: 10px; transition: background 0.5s, border-color 0.5s; } .item { font-size: 16px; margin-bottom: 10px; border-bottom: 1px solid #ccc; padding-bottom: 5px; display: flex; justify-content: space-between; } .total-price { font-size: 28px; font-weight: bold; margin: 20px 0; } .win { color: #dc143c; animation: pop 0.5s ease-out; } .lose { color: #4169e1; } .legendary { color: #ffd700; font-weight: bold; text-shadow: 1px 1px 2px #000; } .trash { color: #a9a9a9; } .message { font-size: 22px; font-weight: bold; margin-bottom: 20px; } @keyframes pop { 0% { transform: scale(0.5); } 50% { transform: scale(1.2); } 100% { transform: scale(1); } } .hidden { display: none; } /* どん底モードのスタイル */ body.rock-bottom-mode { background-color: #0a0a0a; color: #555; } body.rock-bottom-mode #game-container { background-color: #1a1a1a; border-color: #333; box-shadow: 0 0 20px rgba(0,0,0,0.8); animation: dropDown 1s ease-in forwards; } body.rock-bottom-mode h1 { color: #666; } body.rock-bottom-mode .item-list { background: #111; border-color: #333; } body.rock-bottom-mode .lose { color: #8b0000; text-shadow: 2px 2px 4px #000; } @keyframes dropDown { 0% { transform: translateY(0) scale(1); filter: grayscale(0); } 50% { transform: translateY(20px) scale(0.98); filter: grayscale(0.5); } 100% { transform: translateY(50px) scale(0.95); filter: grayscale(1); opacity: 0.9; } }</style></head><body><div id="game-outer-container"> <canvas id="effect-canvas"></canvas> <div id="game-container"> <h1>🛍️ お楽しみボックスシミュレーター</h1> <p>中身は5つのアイテム!神引きか、それとも底なしの絶望か…</p> <p style="font-size: 0.8em; color: #888;">※ボタンを押すと音が鳴ります</p> <div id="shop-area"> <div class="bag-icon">🎁</div> <button id="buy-btn">運試し!</button> </div> <div id="open-area" class="hidden"> <div class="bag-icon" id="target-bag">🛍️</div> <button id="open-btn">お楽しみボックスをを開ける!</button> </div> <div id="result-area"> <h3>✨ お楽しみボックスの中身 ✨</h3> <ul class="item-list" id="item-list"></ul> <div class="total-price" id="total-price"></div> <div class="message" id="result-message"></div> <button id="retry-btn">もう一度挑戦する</button> </div> </div></div><script> // --- 8bitサウンド生成システム(Web Audio API) --- const AudioContext = window.AudioContext || window.webkitAudioContext; let audioCtx; let bgmInterval; let bgmStep = 0; let isBgmPlaying = false; // レトロな波形(矩形波)で音を鳴らす function playTone(freq, type, duration, vol=0.1) { if (!audioCtx) return; const osc = audioCtx.createOscillator(); const gain = audioCtx.createGain(); osc.type = type; // 'square' でファミコン風 osc.frequency.setValueAtTime(freq, audioCtx.currentTime); gain.gain.setValueAtTime(vol, audioCtx.currentTime); gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + duration); osc.connect(gain); gain.connect(audioCtx.destination); osc.start(); osc.stop(audioCtx.currentTime + duration); } // BGM再生 function startBGM() { if (!audioCtx) { audioCtx = new AudioContext(); } if (audioCtx.state === 'suspended') audioCtx.resume(); if (isBgmPlaying) return; isBgmPlaying = true; // ちょっと気の抜けたお店のBGMループ const melody = [261.63, 0, 329.63, 0, 392.00, 392.00, 523.25, 0, 392.00, 0, 329.63, 0, 261.63, 0, 0, 0]; bgmInterval = setInterval(() => { let note = melody[bgmStep % melody.length]; if (note > 0) playTone(note, 'square', 0.15, 0.03); // BGMは少し音量小さめ bgmStep++; }, 200); } function stopBGM() { clearInterval(bgmInterval); isBgmPlaying = false; } // 各種効果音 function playBuySE() { playTone(880, 'square', 0.1, 0.1); // ピッ } function playDrumRollSE() { // ドコドコドコという期待感を煽る音 let count = 0; let roll = setInterval(() => { playTone(150 + Math.random()*50, 'square', 0.05, 0.1); count++; if (count > 25) clearInterval(roll); }, 50); } function playWinSE() { // ファンファーレ風 playTone(523.25, 'square', 0.1, 0.1); // C setTimeout(() => playTone(659.25, 'square', 0.1, 0.1), 150); // E setTimeout(() => playTone(783.99, 'square', 0.4, 0.1), 300); // G } function playGodSE() { // 神引きの激しいアルペジオ for(let i=0; i<10; i++) { setTimeout(() => playTone(523.25 + i*100, 'square', 0.1, 0.1), i*80); } } function playLoseSE() { // 残念な下降音 playTone(392.00, 'square', 0.2, 0.1); // G setTimeout(() => playTone(349.23, 'square', 0.2, 0.1), 250); // F setTimeout(() => playTone(329.63, 'square', 0.5, 0.1), 500); // E } function playDespairSE() { // 絶望の低音 playTone(100, 'sawtooth', 1.0, 0.2); // 重いブー音 setTimeout(() => playTone(80, 'sawtooth', 1.5, 0.2), 300); } // --- エフェクトシステム(紙吹雪 & 雨) --- class EffectSystem { constructor(canvas) { this.canvas = canvas; this.ctx = canvas.getContext('2d'); this.particles = []; this.effectType = 'none'; this.animationId = null; this.resize(); window.addEventListener('resize', () => this.resize()); } resize() { this.canvas.width = this.canvas.parentElement.offsetWidth; this.canvas.height = this.canvas.parentElement.offsetHeight; } start(type) { this.stop(); this.effectType = type; if (type === 'none') return; this._createParticles(); this._loop(); } stop() { if (this.animationId) { cancelAnimationFrame(this.animationId); this.animationId = null; } this.particles = []; this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); } _createParticles() { let count = 100; if (this.effectType === 'gold') count = 150; if (this.effectType === 'rain') count = 150; const colorful = ['#FFC0CB', '#FF69B4', '#FF1493', '#ADFF2F', '#7CFC00', '#00FFFF', '#1E90FF', '#FFD700']; const gold = ['#FFD700', '#DAA520', '#B8860B', '#F4A460', '#FFE4B5']; const rain = ['rgba(100, 149, 237, 0.6)', 'rgba(70, 130, 180, 0.6)', 'rgba(176, 196, 222, 0.6)']; for (let i = 0; i < count; i++) { let p = { x: Math.random() * this.canvas.width, y: Math.random() * this.canvas.height - this.canvas.height, opacity: 1 }; if (this.effectType === 'rain') { p.sizeX = 2; p.sizeY = Math.random() * 15 + 10; p.color = rain[Math.floor(Math.random() * rain.length)]; p.speedX = Math.random() * 0.5 - 0.25; p.speedY = Math.random() * 10 + 15; p.angle = 0; p.spin = 0; } else { let colors = this.effectType === 'gold' ? gold : colorful; p.sizeX = Math.random() * 8 + 4; p.sizeY = p.sizeX; p.color = colors[Math.floor(Math.random() * colors.length)]; p.speedX = Math.random() * 2 - 1; p.speedY = Math.random() * 3 + 2; p.angle = Math.random() * Math.PI * 2; p.spin = Math.random() * 0.2 - 0.1; } this.particles.push(p); } } _updateParticles() { for (let i = 0; i < this.particles.length; i++) { const p = this.particles[i]; p.x += p.speedX; p.y += p.speedY; p.angle += p.spin; if (this.effectType !== 'rain') { p.opacity -= 0.002; } if (p.y > this.canvas.height) { if (this.effectType === 'rain') { p.y = -p.sizeY; p.x = Math.random() * this.canvas.width; } else if (p.opacity < 0) { this.particles.splice(i, 1); i--; } } } } _drawParticles() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); this.particles.forEach(p => { this.ctx.save(); this.ctx.globalAlpha = p.opacity; this.ctx.translate(p.x, p.y); this.ctx.rotate(p.angle); this.ctx.fillStyle = p.color; this.ctx.fillRect(-p.sizeX / 2, -p.sizeY / 2, p.sizeX, p.sizeY); this.ctx.restore(); }); } _loop() { this._updateParticles(); this._drawParticles(); if (this.particles.length > 0 || this.effectType === 'rain') { this.animationId = requestAnimationFrame(() => this._loop()); } else { this.animationId = null; } } } // アイテムプールの生成 const itemPool = []; const categories = [ { name: "神", min: 999999, max: 999999, prob: 0.001, class: "legendary", items: ["無人島", "宇宙旅行ペアチケット", "伝説の聖剣", "世界一周クルーズ券"] }, { name: "超絶レア", min: 100000, max: 300000, prob: 0.009, class: "win", items: ["幻の純金製コーヒーミル", "ハワイ旅行ペアチケット", "最新ゲーミングPCフルセット", "高級ブランドの腕時計", "謎の天才画家の絵画", "高級ホームシアターセット", "プロ用一眼レフカメラ"] }, { name: "大当たり", min: 30000, max: 99900, prob: 0.09, class: "", items: ["最新スマートフォン", "松阪牛1頭分(部位セット)", "ロボット掃除機最上位モデル", "オーダーメイドスーツお仕立券", "高級電動アシスト自転車", "有名温泉ペア宿泊券", "高級羽毛布団"] }, { name: "当たり", min: 10000, max: 29900, prob: 0.20, class: "", items: ["ノイズキャンセリングイヤホン", "高級フレンチペア食事券", "全自動コーヒーメーカー", "有名デザイナーの椅子", "国産うなぎ蒲焼10尾", "高級和牛すき焼きセット", "コードレス掃除機", "美容家電セット"] }, { name: "普通", min: 3000, max: 9900, prob: 0.30, class: "", items: ["特選コーヒー豆1kg", "おしゃれなペアマグカップ", "人気のボードゲーム", "アロマディフューザー", "今治タオル高級セット", "ご当地レトルトカレーセット", "高級オリーブオイル", "Bluetoothスピーカー", "モバイルバッテリー"] }, { name: "ハズレ", min: 100, max: 2900, prob: 0.399, class: "", items: ["マスターの特製ステッカー", "うまい棒1年分", "謎の木彫りの熊", "消しゴム詰め合わせ", "ただのタワシ", "肩たたき券", "ポケットティッシュ100個", "よくわからない置物", "大量の輪ゴム", "派手な柄の靴下"] }, { name: "絶望", min: 1, max: 1, prob: 0.001, class: "trash", items: ["道端の石ころ", "ちぎれた輪ゴム", "誰かのサインの切れ端", "使用済み切手", "期限切れのクーポン"] } ]; for(let i = 0; i < 1000; i++) { let rand = Math.random(); let cumulativeProb = 0; let selectedTier; for (let tier of categories) { cumulativeProb += tier.prob; if (rand < cumulativeProb) { selectedTier = tier; break; } } let price = selectedTier.min === selectedTier.max ? selectedTier.min : Math.floor((Math.random() * (selectedTier.max - selectedTier.min) + selectedTier.min) / 100) * 100; let itemName = selectedTier.items[Math.floor(Math.random() * selectedTier.items.length)]; itemPool.push({ name: itemName, price: price, class: selectedTier.class }); } // 要素の取得 const shopArea = document.getElementById('shop-area'); const openArea = document.getElementById('open-area'); const resultArea = document.getElementById('result-area'); const buyBtn = document.getElementById('buy-btn'); const openBtn = document.getElementById('open-btn'); const retryBtn = document.getElementById('retry-btn'); const targetBag = document.getElementById('target-bag'); const itemList = document.getElementById('item-list'); const totalPriceEl = document.getElementById('total-price'); const resultMessage = document.getElementById('result-message'); const effectCanvas = document.getElementById('effect-canvas'); const effects = new EffectSystem(effectCanvas); buyBtn.addEventListener('click', () => { playBuySE(); startBGM(); // オーディオコンテキストの起動とBGM開始 shopArea.classList.add('hidden'); openArea.classList.remove('hidden'); }); openBtn.addEventListener('click', () => { targetBag.classList.add('shake'); openBtn.disabled = true; openBtn.textContent = "開封中..."; stopBGM(); // 開封中はBGMを止めてドラムロール playDrumRollSE(); setTimeout(() => { targetBag.classList.remove('shake'); openArea.classList.add('hidden'); showResults(); }, 1500); }); function showResults() { resultArea.style.display = 'block'; itemList.innerHTML = ''; let total = 0; let hasDespair = false; const shuffled = [...itemPool].sort(() => 0.5 - Math.random()); const selectedItems = shuffled.slice(0, 5); selectedItems.forEach(item => { if (item.price === 1) hasDespair = true; const li = document.createElement('li'); li.className = 'item'; const nameSpan = document.createElement('span'); nameSpan.textContent = item.name; if (item.class) nameSpan.classList.add(item.class); const priceSpan = document.createElement('span'); priceSpan.textContent = `${item.price.toLocaleString()}円`; if (item.class) priceSpan.classList.add(item.class); li.appendChild(nameSpan); li.appendChild(priceSpan); itemList.appendChild(li); total += item.price; }); totalPriceEl.textContent = `総額: ${total.toLocaleString()}円`; const cost = 50000; if (hasDespair) { document.body.classList.add('rock-bottom-mode'); totalPriceEl.className = 'total-price lose'; resultMessage.textContent = `💀 絶望の淵へようこそ… 全てを失った気分だね…`; resultMessage.className = 'message lose'; effects.start('none'); playDespairSE(); } else if (total >= 999999) { totalPriceEl.className = 'total-price win'; resultMessage.textContent = `🌟 神引き!!! 伝説を目撃したね! 🌟`; resultMessage.className = 'message win'; effects.start('gold'); playGodSE(); } else if (total > cost) { totalPriceEl.className = 'total-price win'; resultMessage.textContent = `🎉 大勝利! ${ (total - cost).toLocaleString() }円の得だね! 🎉`; resultMessage.className = 'message win'; effects.start(total > 200000 ? 'gold' : 'colorful'); playWinSE(); } else if (total === cost) { totalPriceEl.className = 'total-price'; resultMessage.textContent = "奇跡のトントン! 損はしてないよ!"; resultMessage.className = 'message'; effects.start('none'); playBuySE(); } else { totalPriceEl.className = 'total-price lose'; resultMessage.textContent = `💸 痛恨! ${ (cost - total).toLocaleString() }円の損… 冷たい雨が心に染みるね。`; resultMessage.className = 'message lose'; effects.start('rain'); playLoseSE(); } } retryBtn.addEventListener('click', () => { playBuySE(); startBGM(); // 再度BGMをスタート resultArea.style.display = 'none'; shopArea.classList.remove('hidden'); openBtn.disabled = false; openBtn.textContent = "福袋を開ける!"; document.body.classList.remove('rock-bottom-mode'); effects.stop(); });</script></body></html>
■ 今日の学び
今回は、このゲームを作る中で感じた、
私なりの「学び」についてお話ししたいと思います。d(^^o)
■ “中身が分からないワクワク感”の魅力(=v=*)
昔から、袋や箱を開けるときの
「何が入っているんだろう?」というワクワク感が大好きなんです。
最近は中身が最初から分かるセット商品も増えましたが、
やっぱり私は、開ける瞬間のドキドキが一番楽しいなぁと思っていて……。
そこで今回は、 中身が毎回ランダムに変わる“お楽しみボックス”の面白さを
ゲームとして表現してみました。
■ ランダム生成の“絶妙なバランス”(^艸^)
ゲームとしての楽しさを出すために、
アイテムの種類やレア度のバランスを工夫しました。
- 1000種類のアイテムを用意
- レア度の高いアイテムは極小確率
- ジョークアイテムも少しだけ混ぜる
- 組み合わせによって演出が変わる
この「ちょっとしたサプライズ感」が、
毎回の開封を楽しくしてくれます。(^^)
■ 何度でも開けたくなる魅力(≧▽≦*)
実際にプレイしてみて感じたのは……
- 「お気に入りの組み合わせが出るまで、もう一回開けたい!」
- 「さっきはジョークアイテムが多かったけど、次はどうかな?」
という、 つい何度も開けたくなる不思議な魅力でした。(^^;)
「何が出るか分からない」という体験は、
ゲームになるとこんなにも楽しいんだなぁと実感しました。
■ 現実の“お楽しみボックス”の思い出(^_^)~
ところで、みなさんは現実で
「中身が分からない袋や箱」を開けた思い出、何かありますか?
私は雑貨屋さんのセット商品が大好きで……
生活雑貨からガーデニンググッズまで、 本当にいろいろ入っていました。
中には「あちゃ〜(^^;)」というユニークなアイテムもあって……
- チョコレートの香りの入浴剤(@@;)
- 金色のスリッパ(=°Д°=)
今思えば、そんな“使いどころに困るもの”も含めて、
後で笑い話になるのが楽しいんですよね。(=v=*)
■ ついに……レアアイテムが出ました!
何度か開封を繰り返していたのですが、 ついにその瞬間が訪れました。
・・・30分ほど遊んで、ようやくレア度の高いアイテムが出ました。

(画像:レアアイテムの画像)
思わず「やったー!」と声が出ちゃいました。(≧▽≦*)
この“いつ出るか分からないワクワク感”こそ、
お楽しみボックスの魅力だなぁと感じました。
■ 次回は、
地味な趣味と思われがちな、盆栽をゲームにしてみました。
時間をかけて、自分だけの盆栽を育てていくゲームです。
実際の成長スピードの 何百倍 もの速さで成長する松の木を、
- 枝の形を整えたり
- 余分な葉を取ったり
- 枝ぶりを調整したり
そんな“手入れ”をしながら育てていきます。
次回もぜひ遊びに来てくださいね。(*^v^)ノシ
✨今日も最後まで読んでくださって、ありがとうございます。
みーの実験室は、みなさんの応援で元気に動いています。(*^v^*)






🌈はじめての方へ
🌳 実験一覧
🌱 このブログについて
🍃 プライバシーポリシー
🐣未記事ゲーム展示室