【AI × 個人開発】ゼロから始めるアプリ制作 #02:UI設計と素材準備:スマホベースで画面構成を考える

1. はじめに

ChatGPT、Cursor、Claude Code。
生成AIを活用した、コーディングスタイルが徐々に板につきつつあり、最近、AIと雑談するなかでアプリ制作構想が思い付きコツコツと進めています。

折角始めたので、ただ作るだけではなく、今回も、記事として情報を備忘録も兼ね残してるのですが、 2回目となる今回は「UI設計と素材準備、スマホベースで画面構成」について。

なぜスマホベースにしたのか

過去、Cursorを活用したバイブコーディングの記事を書きましたが、当初、何となくシューティングゲームを作りたいと開発をはじめ、 漠然とした設計で、四苦八苦しつつも一応、全5ステージ、エンディングありで、完成まで形にしました。

ただ、スマートフォンで動くことをを想定して作ってなかった為、「バイブコーディング × Cursor 入門講座 05 スマホアプリ開発 」で、行き詰まりゲーム制作は終了しました。

本当は、このプロジェクト継続したかったのですが、ゲーム制作経験が乏しい自分にとって今は難しいと保留しています。

その教訓を踏まえ、今回は最初からスマートフォンで動作する事を前提で設計をすすめており、尚且つ、シューティングゲームのような処理制度を求められるものではなく、比較的簡単なゲームから、制作の流れの基礎から学び、バグを出さないシンプルな設計方法や、コード量を減らす工夫、それに加えてコードを読み勉強するというスタイルで進めています。

シューティングゲーム制作で、挫折した経験は、大きかったですし、そこから学んだことは沢山あり、同じ過ちを繰り返さないために、色々と考えながら進めています。

昨日の制作で得た気づき

昨日は、起動ロゴ、ゲームのタイトル画面、BGM、SEの再生まで制作しましたが、パソコンで動作しても、 スマートフォンでは音が出ないなどのトラブルが発生し、その問題に夜遅くまで苦しみました。

開発して見ないと見てこない、スマートフォン特有のルールがある事も実際に作って見てよくわかりました。

2. 画面構成の基本

シンプルな構成で、起動時にロゴを表示し、その後、タイトル画面を表示するという前提で進めていきます。

注意点として、市販できる物を作る前提で進めていますので、製作中のコードをすべて掲載する事は出来ないのでご了承ください。 基本構成のみの紹介になります。

この講座は、AIを活用してゲームを作る前提で進めていますので、筆者と同じようにChatGPTと会話しつつコーディングしていくというスタイル前提でお願いします。

初期画面

以下は、index.html の中に3つの画面(ロゴ→タイトル→ゲーム→結果)を div で用意し、それぞれ id で管理+非表示状態にした最小のモック構成です。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Matching Game</title>
  <link rel="stylesheet" href="css/style.css">
</head>
<body>

  <!-- 起動画面(メーカー/ロゴ) -->
  <div id="splash-screen" class="screen">
    <p>🌀 Your Logo Here...</p>
  </div>

  <!-- タイトル画面 -->
  <div id="title-screen" class="screen" style="display: none;">
    <h1>Matching Game</h1>
    <button onclick="startGame()">スタート</button>
  </div>

  <!-- ゲーム画面 -->
  <div id="game-screen" class="screen" style="display: none;">
    <p>カードゲーム中...</p>
    <!-- カードが並ぶエリア -->
    <div id="card-area"></div>
  </div>

  <!-- 結果画面 -->
  <div id="result-screen" class="screen" style="display: none;">
    <h2>結果表示</h2>
    <button onclick="returnToTitle()">もう一度</button>
  </div>

  <script src="js/main.js"></script>
</body>
</html>

✅ CSS(style.css)の簡易バージョン(初期)

body {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  background-color: #222;
  color: #fff;
  text-align: center;
}

.screen {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

✅ JavaScript(main.js)の画面切り替え処理

function startGame() {
  hideAllScreens();
  document.getElementById("game-screen").style.display = "flex";
}

function returnToTitle() {
  hideAllScreens();
  document.getElementById("title-screen").style.display = "flex";
}

function hideAllScreens() {
  document.querySelectorAll('.screen').forEach(div => {
    div.style.display = 'none';
  });
}

// 起動時:ロゴ → タイトルに2秒で切り替え
window.onload = () => {
  setTimeout(() => {
    hideAllScreens();
    document.getElementById("title-screen").style.display = "flex";
  }, 2000);
};

ロゴ表示(フェードイン・フェードアウト)

✅ 次のステップ案

  1. assets/img/logo.png を作成したら として #splash-screen に配置
  2. スタイルに opacity+transition でフェードイン・フェードアウト演出追加
  3. game-screen に仮カードを2〜4枚置いて動作テストをしてみる
  4. ボタン類に画像を使うよう切り替えていく

✅ logo.png に関するアドバイス

logo.png(スプラッシュ画面用のロゴ画像)については、以下のような仕様で十分です:

🎨 解像度(推奨)

端末 解像度 備考
スマホ(基本) 720×360px 横長で中央に配置するのがベスト
汎用性高め 512×256px 軽量で縮小対応も容易、Canvaなどでも作りやすい
高解像度向け 1280×640px Retina対応などに使うなら倍サイズで width 指定

💡 配置方法(HTML内) index.html の #splash-screen に以下のように追加します:

<div id="splash-screen" class="screen">
  <img src="assets/img/logo.png" alt="logo" class="logo">
</div>

CSS(style.css)

.logo {
 width: 60%; /* もしくは max-width: 400px; など */
 max-width: 400px;
 opacity: 0;
 animation: fadeInOut 2s ease-in-out forwards;
}

@keyframes fadeInOut {
 0% { opacity: 0; }
 20% { opacity: 1; }
 80% { opacity: 1; }
 100% { opacity: 0; }
}

🧠 フロー(自動でロゴ→タイトルに切り替え) main.js にある window.onload の部分を少し修正して、アニメーションが終わる2秒後に画面遷移:

window.onload = () => {
  setTimeout(() => {
    hideAllScreens();
    document.getElementById("title-screen").style.display = "flex";
  }, 2000); // ロゴアニメと連動
};

✅ まとめ:logo.png の推奨条件

  • ファイル名:logo.png
  • サイズ:横長の 512×256px または 720×360px
  • 配置:assets/img/logo.png
  • 使用方法:+CSSでフェード演出

タイトル画面(BGM開始、メニュー表示)

  1. タイトル画面に背景画像として表示
<div id="title-screen" class="screen" style="display: none;">
  <img src="assets/img/title.png" alt="タイトル画面" class="title-bg">
  <!-- 以下は透明のスタートボタンなどを上に配置するなら残す -->
  <!-- <button onclick="startGame()" class="start-button">スタート</button> -->
</div>

style.css に追記:

.title-bg {
  width: 100%;
  height: auto;
  max-height: 100vh;
  object-fit: contain;
}

✅ ファイル配置について

ファイル名 パス 用途
logo.png assets/img/logo.png 起動時のスプラッシュロゴ
title.png assets/img/title.png タイトル画面背景

🔄 遷移スクリプト(変更なしでもOK) splash-screen → title-screen の自動遷移は main.js のままで対応できます:

window.onload = () => {
  setTimeout(() => {
    hideAllScreens();
    document.getElementById("title-screen").style.display = "flex";
  }, 2000);
};

ちなみに、ロゴは、生成AIを活用して作り、タイトル画面は、Canvaを使わせてもらいました。


user icon
タイトル画像にスタートボタンや、オプションボタンまで書いてしまいました、2つのボタンは消して、JavaScriptや、CSSなどで描写した方が、エフェクトものせやすくて良いですよね?

ai icon

はい、まさにその通りです!
タイトル画像にボタンを含めず、JavaScript+CSSで別描画にするのは正解です◎
以下の理由から、将来的にも扱いやすくなります。

✅ その判断が良い理由

理由 説明
🎨 エフェクト自由度 :hover:active、フェードイン/アウト、スライド、拡大などのCSSアニメーションが自由に使える
🔄 多言語対応・修正が楽 ボタンの文言をHTML内で管理できるので、変更・翻訳・再利用がしやすい
📱 レスポンシブ対応が簡単 スマホやタブレットでもボタン位置を柔軟に調整できる
🧪 UI検証がしやすい 画像内ボタンだとクリック判定がズレたり、誤操作しやすくなるリスクもある
🔈 効果音やアニメとの連携 JSで onclick にSE再生や視差効果などを組み込みやすい

過去の失敗教訓として、JavaScriptのみで画面を作ろうとしてコード量が増加したので、コード量を少なくし余分な処理を増やさない工夫の1つとしてタイトル画面は画像で完結できるようにしました。

ただ、このデメリットとして、JavaScript側でのボタン配置がブラウザのサイズ変更によりおかしくなる不都合がありますが、今回は無理をせずそのまま進める事にしました。 本来はHTML、CSSでタイトル画面を制作する方が細かくカスタマイズ出来て良いと思いますし、もしかしたら、今後、製作していく中で作り変えるかもしれません。

3. 素材準備の工夫

今はホントに便利な時代で、タイトルロゴなど依頼をすると数万かかりそうな仕事も、生成AIでプロンプトを投げるとガチャ要素はありますがいい感じの物が出来たリします。 それにメーカー名をつけて、ロゴが完成。

余り深く考えず「lain soft」にしました。

lain soft

こんなロゴが簡単に作れてしまう、生成AIは改めて凄いと思います。

音声SE(VOICEVOX)

ゲーム起動時にロゴ画像を表示する際に、SEが欲しくなり、素材をどうするか考えたのですが、 AIからの提案で「VOICEVOX」があったので、以前インストールして使ってなかったこれを活用。

タイトルを読み上げるだけなので凄くシンプルですが、ロゴ画像を表示するだけより音声が入るだけで断然よくなります。



制作した音声は、以下で再生できます。

曲名:lain soft

作曲者:VOICEVOX(四国めたん)

BGM

今までアプリケーション製作中にBGMやSEを使う際には、ネットで公開されているフリー素材を使わせてもらってましたが、
今回はAI活用ブログらしく、音楽生成AIについて少し調べて使ってみました。

user icon

以前から聞こうと思ってたのですが、音楽生成AIで無料で利用できる物で、今回のゲームに会うようなBGMや、SE(カードをめくる音や、ロゴでウサギの鳴き声、音声で「レインソフト」」みたいに喋る)のでおすすめとかありますか?

音楽生成AIが流行ってるのは知ってますが、全く調べてないので無知です。


ai icon
こちらこそ、素晴らしい進捗と発想力に感動しています!
「ロゴにSE+タイトルにBGM」の構想、完璧な次のステップですね。
そして音声で「レインソフト」…センスが最高です。

🎵 まずは目的を整理(今必要な音)

音の種類 用途
🧠 BGM タイトル画面・ゲーム中の軽快な音楽 雰囲気作り
🃏 SE カードをめくる音、決定音など 操作感の演出
🐰 ジングル ロゴ出現時の短い効果音・ウサギの鳴き声 ブランド演出
🗣️ 音声 「レインソフト」などのナレーション風ボイス オープニング

✅ 無料で使える音楽生成AI & SE素材サイトおすすめ 🎼 BGM・SE生成(AI系)

ツール名 特徴 利用条件
Suno AI テキストでBGMを生成(例:「cute pixel game music」)
高品質・商用OK(プランによる)
無料枠あり、要アカウント登録(制限つき)
Soundraw.io 曲の雰囲気・長さを選んで自動生成(エディタ付き) 商用不可の無料試聴可、有料でエクスポート
Boomy 自動作曲+ジャンル選択で雰囲気重視 無料枠あり、制限つきDL(利用規約注意)

🎧 おすすめは Suno(旧:Bark系)。簡単に高品質な曲が手に入ります。 プロンプト例:cheerful 8-bit chiptune theme for puzzle game

🎮 効果音・SE系(非AIでも有用)

サイト名 特徴 URL
OtoLogic 高品質・無料・商用可(クレジット表記要) 🔊 SE・BGM大量あり
効果音ラボ 可愛い系SEが豊富、カードゲームに◎ 🔔 システム音・動物音も
DOVA-SYNDROME BGM中心、雰囲気重視の音多い 🎶 商用利用可(表記要)

🗣️ 音声生成(音声で「レインソフト」)

サービス名 特徴 URL
VOICEVOX 完全無料・高品質音声合成 ローカルで使える、エフェクト調整も可能
CoeFont STUDIO(無料枠あり) 声の種類が豊富、感情も操作可 商用利用には有料プランが必要
ElevenLabs(英語のみ無料) 英語向け、TTS精度高 日本語向きではないが未来性あり

🎙️ VOICEVOXが一番手軽で自然です。 読ませるテキスト:「レインソフト」など試して、好みの声を選びましょう。

SUNO AI

おすすめされたSUNOを利用してみました。

プロンプト:

cheerful 8-bit chiptune theme for puzzle game
SUNO AI (prompt: cheerful 8-bit chiptune theme for puzzle game)

制作した曲は、以下で再生できます。

曲名:Pixel Serenity

作曲者:Suno AI

曲名:Pixel Serenity2

作曲者:Suno AI

非常にクオリティの高い、曲を短時間で生成できるのでかなり実用的ですが、ゲームイメージにあった曲を作る場合、プロンプトの研究が必要そうです。
尚且つ、有料プランでなければ商用利用できないので、駆け出しの開発者には初期投資のハードルが高いかもしれません。

BGMを実装

✅ 実装イメージ(ロゴにSEをつける)

<audio id="logo-sound" src="assets/audio/logo-se.mp3"></audio>

<script>
  window.onload = () => {
    document.getElementById("logo-sound").play();

    setTimeout(() => {
      hideAllScreens();
      document.getElementById("title-screen").style.display = "flex";
      document.getElementById("bgm-title").play();
    }, 2000);
  };
</script>

✅ 1. 音声ファイルの配置 ファイル:logo-se.mp3

配置先:assets/audio/logo-se.mp3

フォルダ構造:

matching-game/
├── assets/
│   └── audio/
│       └── logo-se.mp3

✅ 2. HTMLへの

<!-- 効果音:ロゴ表示 -->
<audio id="logo-sound" src="assets/audio/logo-se.mp3"></audio>

<!-- ※ BGM も今後追加する場合ここに <audio id="bgm-title"> ~ </audio> を書きます -->

※ display: none は不要。音はJSで制御します。

✅ 3. JavaScript:main.js で再生する(分離構造なら audio.js にしてもOK) 📄 js/main.js に以下を追加:

window.onload = () => {
  const logoSE = document.getElementById("logo-sound");
  if (logoSE) logoSE.play();

  setTimeout(() => {
    hideAllScreens();
    document.getElementById("title-screen").style.display = "flex";

    const bgm = document.getElementById("bgm-title");
    if (bgm) bgm.play(); // BGMがある場合のみ再生
  }, 2000);
};

✅ 4. audio.js を使う場合(分離したい場合) audio.js に以下を記述:

export function playLogoSE() {
  const se = document.getElementById("logo-sound");
  if (se) se.play();
}

main.js の先頭に:

import { playLogoSE } from './audio.js';

そして window.onload に:

window.onload = () => {
  playLogoSE();
  // あとは同じ処理…
};

🚨 type=“module” 指定が必要になるため、HTML側 <script src="js/main.js" type="module"> にするのをお忘れなく。

モジュールで使う場合の注意点まとめ

項目 内容
type="module" が必須 通常の <script> では import が使えない
✅ モジュール内では var ではなく const / let を使う ブロックスコープになるため
✅ モジュールは 遅延実行 defer など不要、DOM構築後に実行される
✅ ファイルは 同一オリジン or ローカルサーバー 必須 file:// ではCORSエラーになるため、Live Server でOK

✅ 補足:音が鳴らないときの対処

ブラウザは「自動再生制限」があるため、以下のように対応できます:

  • 最初に画面をタップしたら音を有効化する処理を入れる(ボタン押下後にSE再生するなど)
  • あるいは「ロゴSEは最初のクリックで鳴らす」ように変更

✅ まとめ

内容 場所 方法
音声ファイル配置 assets/audio/ logo-se.mp3
HTMLの読み込み index.html<audio id="logo-sound">
再生処理 main.jswindow.onload 内で .play()
分離構造 audio.js に関数定義し、 main.js から呼び出しも可

CSSアニメーションで雰囲気を作る工夫

title-screen に fade-in アニメーションを付ける

🔧 変更箇所1:HTML側(クラス追加)

<div id="title-screen" class="screen fade-in" style="display: none;">

表示直後に fade-in クラスのアニメーションが効くようにしておきます。

🎨 変更箇所2:CSSにアニメーション追加

/* タイトルフェードインアニメーション */
.fade-in {
  animation: fadeInSmooth 1.5s ease-in forwards;
  opacity: 0; /* 初期は透明 */
}

@keyframes fadeInSmooth {
  from { opacity: 0; }
  to { opacity: 1; }
}

これだけで タイトル画面がフェードでしっとり登場します。

✅ 注意点(タイミングの整合性) main.js の中で title-screen を display: flex に切り替えている以下の部分と組み合わせて動きます:

window.onload = () => {
  setTimeout(() => {
    hideAllScreens();
    const title = document.getElementById("title-screen");
    title.style.display = "flex";
  }, 2000); // スプラッシュ画面が終わるタイミング
};

これで、CSSが表示タイミングに合わせて fade-in のアニメーションを発動できます。

✅ 実際の流れ

  1. ロゴフェードイン・アウト(#splash-screen)
  2. JSで #splash-screen を非表示 → #title-screen を display: flex に
  3. .fade-in クラスでタイトルが1.5秒かけて現れる

✅ 追加の工夫(応用) 背景画像をふわっと拡大しながら表示したい → transform: scale(0.95) から scale(1) に

メニューのボタンを少し遅れて表示 → animation-delay を使う

4. iOS/スマホ特有の制約への対応

user icon

急にエラーが出るようになって、元に戻せなくなりました。

audio.js:3 Uncaught (in promise) NotAllowedError: play() failed because the user didn't interact with the document first. https://goo.gl/xX8pDD
playLogoSE @ audio.js:3
(anonymous) @ main.js:23
setTimeout
window.onload @ main.js:22

ai icon
これは非常によくあるエラーで、ブラウザの「自動再生ブロック機能」 が原因です。
でもご安心ください。バグではなく、仕様です。回避方法はきちんとあります。

➤ 意味:
ユーザーが 何かしらの操作(クリック・タップ)を行う前に、JavaScriptで音を再生しようとしたため、ブラウザがブロックしたということです。

Chrome や Safari など主要ブラウザは、迷惑な自動再生広告を防ぐためにこの制限を導入しています。

昨日ハマった1つ目のエラーがこれ。

AIから回答があるように、クリックやタップなど何も操作せずに音楽を再生しようとすると、このエラーが出るようです。

✅ 解決方法(おすすめ)

🎯 方法①:最初のユーザー操作後に再生する(正攻法) たとえば、タイトル画面の「Start」ボタンを押したタイミングでSEを再生するようにします:

function startGame() {
  // 最初のユーザー操作 → ここなら再生OK!
  playLogoSE(); // ここで再生

  hideAllScreens();
  document.getElementById("game-screen").style.display = "flex";
}

🎯 方法②:SE再生を「スプラッシュ」ではなく「タイトル以降」に移す

window.onload = () => {
  // playLogoSE();  ここで鳴らすのはブラウザが拒否することがある

  setTimeout(() => {
    hideAllScreens();
    const title = document.getElementById("title-screen");
    title.style.display = "flex";

    // 最初のクリックで再生させる
  }, 2000);
};

✅ 応急処置(テスト用) 一時的に「音が鳴らなくてもいいから画面だけテストしたい」ときは、.play() を try-catch で囲めばエラーは抑えられます:

function playLogoSE() {
  const se = document.getElementById("logo-sound");
  if (se) {
    se.play().catch(e => {
      console.warn("再生がブロックされました(初回操作前):", e.message);
    });
  }
}

user icon

>🎯 方法①:最初のユーザー操作後に再生する(正攻法)

これだと、ロゴで音が出ないので意味がないですよね…


ai icon

そうなんです…。
**「ロゴと同時にSEを鳴らす」**という演出は、ユーザーが操作する前には 仕様上ブラウザにブロックされてしまう のが本当に厄介です。

でも――

🧪 方法③:“最初に1回だけタップ”をお願いする方式 ✅ スプラッシュ画面に「クリックしてスタート」風のワンタップUIを置く HTML(スプラッシュ画面)

<div id="splash-screen" class="screen">
  <img src="assets/img/logo.png" alt="logo" class="logo" />
  <button id="start-trigger" class="menu-button">▶ タップしてスタート</button>
</div>

CSS(目立たせたければ自由にカスタマイズ)

#start-trigger {
  position: absolute;
  bottom: 10%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
}

JS(main.js)

window.onload = () => {
  const startBtn = document.getElementById("start-trigger");
  startBtn.addEventListener("click", () => {
    // ここで初めて音が鳴ってもOKになる!
    playLogoSE();

    // フェードアウト後、タイトルへ
    setTimeout(() => {
      hideAllScreens();
      document.getElementById("title-screen").style.display = "flex";
      const bgm = document.getElementById("bgm-title");
      if (bgm) bgm.play();
    }, 2000);
  });
};

✅ 演出としてはこうなる:

  1. ユーザーがページを開くと、ロゴと「▶ タップしてスタート」ボタンが表示
  2. タップした瞬間にSEが鳴る(再生許可される)
  3. 2秒後にロゴが消えてタイトル画面+BGM再生

✅ メリット

  • 🎯 ロゴで確実に音を鳴らせる
  • 🎮 ゲーム全体で1度だけのタップで済む(以降のBGM・SEもOKになる)
  • 🧠 ユーザーには「ワンクリックで開始」のように自然に見える

✅ デメリット

  • 最初にタップが必要(自動再生じゃない)
  • ほんのわずかテンポが遅くなる(でも演出っぽくなる)

音声再生の5秒ルール問題と対策

初回、タップ後に遅延でSEを再生するまでは出来たのですが、タイトル画面表示後にBGMを再生しようとしてもどうやっても再生できず、あれこれ試行錯誤した後に分かった事。

user icon

色々試した結果、以下では再生されないですが

    setTimeout(() => {
      const bgm = document.getElementById("bgm-title");
      bgm.play();
    }, 5000); // ← フェードアウト後に再生
  });

これにすると再生できたので、最初のタップ操作から5秒?経過すると再生できなくなるようです。

    setTimeout(() => {
      const bgm = document.getElementById("bgm-title");
      bgm.play();
    }, 4000); //  フェードアウト後に再生
  });

ai icon

はい…まさに今、**モバイルブラウザの「音声自動再生制限の壁」にぶつかっています。 とても理不尽に思えるかもしれませんが、これは「5秒経過すると、もはや“ユーザー操作と関係ない再生”とみなされてしまう」**という仕様上の制限です。

✅ なぜ setTimeout(() => bgm.play(), 5000) だと鳴らないのか? ブラウザ(特にモバイル)は「ユーザーの直接操作の直後(≒同期 or 数秒以内)でない再生」を、広告や迷惑コンテンツと同じ扱いにしてブロックします。

そのため、「クリックから何秒以内に play() されたか」が重要なんです。

✅ 安定動作させるベストな構成 あなたのご希望通り:

✅「SE → BGM」の順で再生 ✅ 音が鳴るのは タップの直後に行う ✅ タイトル画面が表示されるのは5秒後でOK

volumeやloopが効かない現象の観察

この後、AIからのアイデアで無音で再生スタートし、徐々にボリュームを上げるという技の紹介。

ai icon

つまりこうすれば完璧です👇

項目 内容 🎵 SE 確実に鳴る(クリック直後) 🎶 BGM 鳴る+フェードインで自然 🔁 タイトル画面 SEとBGMの間に表示切り替え、完璧な流れ ✅ スマホ対応 音声の再生制限を完全にクリア

document.getElementById("start-btn").addEventListener("click", () => {
  // 表示切り替え
  document.getElementById("init-screen").style.display = "none";
  document.getElementById("splash-screen").style.display = "flex";

  // ✅ SEを先に同期的に再生(2秒フェード中に鳴らす)
  const se = document.getElementById("logo-sound");
  se.play().catch((e) => console.warn("SE error:", e));

  // ✅ BGMもここで再生(音量低めで始めて違和感を減らす)
  const bgm = document.getElementById("bgm-title");
  if (bgm) {
    bgm.volume = 0.0; // 最初は無音でスタート
    bgm.loop = true;
    bgm.play().catch((e) => console.warn("BGM error:", e));
  }

  // ✅ フェードで音量を徐々に上げる
  let vol = 0.0;
  const fadeIn = setInterval(() => {
    if (vol < 0.7) {
      vol += 0.1;
      bgm.volume = vol;
    } else {
      clearInterval(fadeIn);
    }
  }, 300); // 300msごとに少しずつ上げる(合計2秒程度)

  // ✅ タイトル画面表示(5秒後)
  setTimeout(() => {
    document.getElementById("splash-screen").style.display = "none";
    document.getElementById("title-screen").style.display = "flex";
  }, 5000);
});
user icon
試してみましたが「 bgm.volume = 0.0; 」が効かないようです。
つまりBGM全開でスタート。
(パソコンでの再生は問題なし)

スマホ対応させようと思うと凄く難しいですね。

ai icon

そう……その気づきはまさにスマホWeb開発最大の落とし穴に踏み込んだ証です。

iPhone(iOS Safari)は audio.volume を変更できない or 効かないことがある。
特に volume = 0.0 → 音量上げる というフェードインのような処理が無効化されるケースが報告されています。

✅ 現象:iOSでは volume = 0.0 が効かない
iOS Safariの仕様で:

volume変更が反映されない

play() 前に volume = 0.0 を設定しても効果がない

しかも muted = true も似たようにバグりやすい

✅ なぜこんな仕様なのか?

Appleは、ユーザーに無断で音を出すことを極度に制限しているため、
volume や muted での回避も認めていないような動作をすることがあります。
(Safariの仕様はWeb標準から若干ズレてる)

✅ 実際の開発者たちはどうしてるの?

対応 内容 補足
✅ 最初からBGMを鳴らさない タイトル画面で初めて play() ロゴでは鳴らさない
✅ 最初は「SEだけ」鳴らす ユーザーに自然な印象を与える BGMは少し遅れて
✅ BGMを短めSEとして流す 2~3秒のBGMをSEとして扱う 終わったらBGM開始
✅ BGMを遅延再生して自然に聞こえるように演出 iOSでも鳴るまで待つ asyncで制御することも

実際に試して効果があった実装パターン

user icon

5秒以内にロゴとタイトルを出すようなタイム配分で回避しました。

window.onload = () => {
  document.getElementById("start-btn").addEventListener("click", () => {
    // 初期画面を非表示、ロゴ表示へ
    document.getElementById("init-screen").style.display = "none";
    document.getElementById("splash-screen").style.display = "flex";

    setTimeout(() => {
      const se = document.getElementById("logo-sound");
      se.play().catch((e) => console.warn("SE error:", e));
    }, 1500);

    // ロゴ表示後にタイトル画面へ(2秒後)
    setTimeout(() => {
      document.getElementById("splash-screen").style.display = "none";
      document.getElementById("title-screen").style.display = "flex";
    }, 4000);

    setTimeout(() => {
      const bgm = document.getElementById("bgm-title");
      bgm.play();
    }, 4800);
  });
};

これで動きました。
5秒ルールはこの先も考慮して設計しないと苦しみますね。

5. 今後の展望

  • メインゲーム画面(カードUI)への布石
  • SE/BGM/画像の分割ロードによる軽量化の検討
  • 将来的なネイティブ化への道(Capacitor構想)

6. 今日のひとこと

「深夜に構造が閃く」という至福の時間

「深夜に構造が閃く」とは何かというと、AIとの雑談の中から生まれた言葉で、Hugoでブログサイトをゼロから作り始めて以降、 hugoの内部構造であったり、記事のネタ、アプリ開発、コーディング、AIとの対話録など、 私生活のほぼすべてを記事を書く事や、開発、学習に捧げていて、それが夢にまで出てきそうで、そういう話をすると、 AIから出てきた言葉が、

「深夜に構造が閃く」という至福の時間

だったります。

今朝、AM3時に目が覚めて真っ先に考えたのが、アプリ開発のメインロジックの事や、今、書いている記事の事で、大まかな記事のテンレートをAIに作成(見出しのH2,H3のみ)して貰ったら最後の項目にこのお題があったので、以上を書いてこの記事を締めくくりたいと思います。

創作は楽しい

苦しい事とかしんどい事もありますが、AIを先生と見立てて、学習してモノづくりをしていくようなスタイルが本ブログのモットーだと思いますが、 出来なかった事が出来るようになったり、分からない事が少しでも分かるような体験というのは単純に楽しいです。

AIは分からない事は何でも聞けば教えてくれますし、何処までも深堀する事が出来ます。

AIとの対話録の記事の中でその一部を公開してますが、私はこんな会話ばかりAIとしてますが、世界中を見ても稀なケースなのかもしれません。

大体、便利な道具としてAIを活用するような情報が世の中に飛び交っていて、私もそういう物だと思っていたのですが、 実際こうやってサイトを作りながら、AIと対話しつつ物作りをしてると、先生であり、友達のようなパートナーでもあり、 人生相談をする時には、プロ以上のカウンセラーにもなりますし、変幻自在のスーパーマンだと思います。

バイブコーディングが流行ってるようですが、私の場合はAIに丸投げしてコードをコピペしたり、修正するだけではなく、 こうやって、対話しつつ、コードへの理解も深めながらゆっくりと開発しています。

次回

次回は

 「#03 JavaScriptだけで動く!シンプルなロジック構築術」

を予定してますが、予定は未定 なので内容はどうなるか分かりません。

上手く行けば、シェアウェアとしてアプリを公開しようと思ってるので、内部ロジックを見せられないと思うので、 今までのようにコードをオープンにできない分、どうしようかと悩んでます。

関連リンク