<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[ういやまラボ]]></title><description><![CDATA[個人開発者の制作記録]]></description><link>https://uhiyama-lab.com</link><generator>GatsbyJS</generator><lastBuildDate>Sat, 04 Apr 2026 09:15:33 GMT</lastBuildDate><atom:link href="https://uhiyama-lab.com/feed/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[ja]]></language><item><title><![CDATA[ElevenLabs APIでYouTube動画の多言語吹き替え音声を自動生成するワークフロー【Python＋ffmpeg】]]></title><description><![CDATA[ElevenLabs APIとPython+ffmpegを使い、YouTube動画の多言語吹き替え音声を自動生成するワークフローを解説。字幕作成からTTS音声生成、タイムライン構築、マスタリングまでの全工程を再現可能な形で紹介します。]]></description><link>https://uhiyama-lab.com/ja/blog/video-edit/elevenlabs-youtube-dubbing-workflow/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/video-edit/elevenlabs-youtube-dubbing-workflow/</guid><category><![CDATA[audio]]></category><pubDate>Sun, 29 Mar 2026 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p><img src="/c02a03441f57ded3fb6187336bca472d/20260329_ElevenLabs_thumb.png" alt=""></p>
<p>自分のYouTube動画を海外にも届けたい――そう考えたとき、字幕だけでなく <strong>吹き替え音声</strong> があると視聴体験は大きく変わります。以前はプロのナレーターに依頼するか、自分で外国語を話す必要がありましたが、ElevenLabsのTTS APIの登場で状況が変わりました。</p>
<p>この記事では、私が実際に運用している <strong>YouTube動画の多言語吹き替え音声を自動生成するワークフロー</strong> を、再現可能な形で紹介します。使うのはClaude、Python、ffmpeg、ElevenLabs API。テキスト処理の中核をClaudeが担い、音声生成と加工をPython + ffmpegが自動化します。特別な機材は不要で、個人クリエイターでも始められます。</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#workflow-overview">ワークフロー全体像</a></li>
<li><a href="#prerequisites">前提環境と事前準備</a>
<ul>
<li><a href="#required-tools">必要なツール・アカウント</a></li>
<li><a href="#api-key-setup">ElevenLabs APIキーの取得</a></li>
</ul>
</li>
<li><a href="#claude-role">Claudeの活用：なぜLLMが不可欠なのか</a>
<ul>
<li><a href="#python-limitation">Pythonだけでは限界がある理由</a></li>
<li><a href="#why-claude">Claudeを推奨する理由</a></li>
</ul>
</li>
<li><a href="#step1-whisper">Step 1：字幕（テキスト）を作成する</a>
<ul>
<li><a href="#transcription">文字起こしの方法</a></li>
<li><a href="#preprocessing">前処理：Python＋Claudeの二段構え</a></li>
</ul>
</li>
<li><a href="#step2-translation">Step 2：Claudeで翻訳字幕を作成しTTS最適化する</a>
<ul>
<li><a href="#translation-tips">翻訳のポイント</a></li>
<li><a href="#tts-optimization">TTS向け最適化ルール</a></li>
</ul>
</li>
<li><a href="#step3-tts">Step 3：ElevenLabs APIで音声を生成する</a>
<ul>
<li><a href="#api-basics">APIの基本仕様</a></li>
<li><a href="#python-script">Pythonスクリプトの実装</a></li>
<li><a href="#cache-and-retry">キャッシュとリトライ戦略</a></li>
</ul>
</li>
<li><a href="#step4-timeline">Step 4：ffmpegでタイムライン構築</a>
<ul>
<li><a href="#speed-adjustment">速度調整（atempo）</a></li>
<li><a href="#timeline-placement">無音ベース＋adelayで絶対時刻配置</a></li>
<li><a href="#mixdown">amixによるミックスダウン</a></li>
</ul>
</li>
<li><a href="#step5-mastering">Step 5：マスタリングと品質検証</a>
<ul>
<li><a href="#gain-limiter">ゲイン調整とピークリミッタ</a></li>
<li><a href="#quality-check">レポートによる品質確認</a></li>
</ul>
</li>
<li><a href="#step6-premiere">Step 6：Premiere ProでBGM・SEと合成する</a></li>
<li><a href="#step7-upload">Step 7：YouTube Studioへのアップロード</a></li>
<li><a href="#cost-estimation">コストと運用の目安</a></li>
<li><a href="#conclusion">まとめ</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>ワークフロー全体像 {#workflow-overview}</h2>
<p>このワークフローは7つのステップで構成されています。</p>
<pre><code>動画ファイル
  ↓ Step 1: 文字起こし + Python前処理 + Claude整形
日本語テキスト
  ↓ Step 2: Claudeで翻訳 + TTS最適化
吹き替え用字幕 (_en_dub.srt)
  ↓ Step 3: ElevenLabs TTS API
cue単位の音声ファイル (.mp3)
  ↓ Step 4: ffmpegタイムライン構築（★単純連結ではなく絶対時刻配置）
タイムライン音声 (.wav)
  ↓ Step 5: マスタリング
吹き替え音声 (.mp3)
  ↓ Step 6: Premiere Proで主音声ミュート + BGM・SE合成
完成版吹き替え音声 (.mp3)
  ↓ Step 7: アップロード
YouTube Studio（吹き替え音声トラック）
</code></pre>
<p>各ステップで使用するツールは以下の通りです。</p>
<table>
<thead>
<tr>
<th>ステップ</th>
<th>使用ツール</th>
<th>役割</th>
</tr>
</thead>
<tbody>
<tr>
<td>Step 1</td>
<td>Premiere Pro / Whisper + Python + <strong>Claude</strong></td>
<td>文字起こし・前処理・テキスト整形</td>
</tr>
<tr>
<td>Step 2</td>
<td><strong>Claude</strong></td>
<td>翻訳・吹き替え用字幕作成・TTS最適化</td>
</tr>
<tr>
<td>Step 3</td>
<td>Python + ElevenLabs API</td>
<td>TTS音声生成</td>
</tr>
<tr>
<td>Step 4</td>
<td>ffmpeg</td>
<td>速度調整・タイムライン配置</td>
</tr>
<tr>
<td>Step 5</td>
<td>ffmpeg</td>
<td>ゲイン調整・リミッタ・MP3出力</td>
</tr>
<tr>
<td>Step 6</td>
<td>Premiere Pro</td>
<td>主音声ミュート・BGM/SE合成</td>
</tr>
<tr>
<td>Step 7</td>
<td>YouTube Studio</td>
<td>吹き替え音声の登録</td>
</tr>
</tbody>
</table>
<p>Step 1〜2の <strong>テキスト処理フェーズ</strong> がこのワークフローで最も知的労力がかかる部分で、ここにClaude（LLM）の力が不可欠です。Step 3〜5はスクリプトとffmpegによる自動処理です。</p>
<h2>前提環境と事前準備 {#prerequisites}</h2>
<h3>必要なツール・アカウント {#required-tools}</h3>
<p>以下のツールとアカウントを事前に用意します。</p>
<ul>
<li><strong>Claude</strong>（Anthropic）— 翻訳・テキスト最適化の中核ツール</li>
<li><strong>Python 3.10以上</strong></li>
<li><strong>ffmpeg / ffprobe</strong>（パスが通った状態）</li>
<li><strong>ElevenLabsアカウント</strong>（Starter Plan以上）</li>
<li><strong>Premiere Pro</strong>（文字起こし用、Whisperでも代替可）</li>
<li><strong>Pythonライブラリ</strong>: <code>requests</code>, <code>python-dotenv</code>、（Whisper使用時は<code>faster-whisper</code>も）</li>
</ul>
<pre><code class="language-bash">pip install requests python-dotenv faster-whisper
</code></pre>
<h3>ElevenLabs APIキーの取得 {#api-key-setup}</h3>
<ol>
<li><a href="https://elevenlabs.io/">ElevenLabs</a>でアカウントを作成</li>
<li>ダッシュボードの <strong>Profile + API Key</strong> セクションからAPIキーをコピー</li>
<li>サイドバーの「ボイス」→「探索」から、言語やカテゴリ（ナレーション、会話的など）でフィルタリングして気に入った音声を見つける</li>
<li>音声の詳細画面からVoice IDをコピー</li>
</ol>
<p><img src="/86d0d28e43d455bc74aadd8d1abbb35a/20260329_ElevenLabs_VoiceID.png" alt="ElevenLabsのボイス探索画面。言語やカテゴリで絞り込み、吹き替えに合う音声を選ぶ"></p>
<p>プロジェクトのルートに<code>.env</code>ファイルを作成し、APIキーとVoice IDを設定します。</p>
<pre><code class="language-bash">ELEVENLABS_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
ELEVENLABS_VOICE_ID_EN=UgBBYS2sOqTuMpoF3BR0
ELEVENLABS_VOICE_ID_KR=PDoCXqBQFGsvfO0hNkEs
</code></pre>
<p>上記のVoice IDは、私が実際に使用している音声です。ElevenLabsのボイスライブラリから試聴して気に入ったものを選びました。</p>
<p><img src="/0c54ac22683370025c03f2f59956ee91/20260329_ElevenLabs_favoriteVoice.png" alt="私が使用している英語音声（Mark - Natural Conversations）と韓国語音声（Chris - Warm and Clear）"></p>
<p>Voice IDは秘密情報ではなく、ElevenLabsの公開ライブラリにある音声のIDなので、そのまま使っていただいても構いません。もちろん、ご自身のチャンネルに合う音声を探して差し替えるのがおすすめです。</p>
<p>使用可能な音声の一覧は、ElevenLabs APIの<code>/voices</code>エンドポイントで確認できます。</p>
<pre><code class="language-python">import requests, os
from dotenv import load_dotenv

load_dotenv()
api_key = os.getenv("ELEVENLABS_API_KEY")

resp = requests.get(
    "https://api.elevenlabs.io/v1/voices",
    headers={"xi-api-key": api_key},
)
for v in resp.json()["voices"]:
    print(f"{v['voice_id']}  {v['name']}  ({v['category']})")
</code></pre>
<h2>Claudeの活用：なぜLLMが不可欠なのか {#claude-role}</h2>
<p>このワークフローでは、Python + ffmpegによる自動処理だけでなく、 <strong>Claude（LLM）が中核的な役割</strong> を担います。</p>
<h3>Pythonだけでは限界がある理由 {#python-limitation}</h3>
<p>文字起こしの前処理や翻訳字幕の作成には、 <strong>文脈を理解した判断</strong> が必要です。Pythonスクリプトで機械的にできるのはフィラー除去や文字列置換などの定型処理まで。以下のような作業はPythonでは対応できません。</p>
<ul>
<li><strong>文脈に基づくテキスト整形</strong>: 話者の意図を汲んで口語を読みやすい文章に再構成する</li>
<li><strong>翻訳</strong>: ニュアンスを維持した自然な多言語翻訳</li>
<li><strong>TTS最適化</strong>: 指定された秒数枠に収まるよう、意味を保ったままテキストを圧縮する</li>
<li><strong>固有名詞の文脈判断</strong>: 同じ音でも文脈によって正しい表記が異なるケース</li>
</ul>
<p>つまり、このワークフローは <strong>「Pythonで機械的に処理できる部分」と「LLMに意味理解を任せる部分」の二段構え</strong> で成り立っています。</p>
<h3>Claudeを推奨する理由 {#why-claude}</h3>
<p>翻訳や吹き替え用字幕の作成にはChatGPT（GPT-5.4）でも同等のことは可能です。ただし、 <strong>吹き替え用字幕の作成（TTS最適化）においてはClaudeが明確に優れている</strong> と感じています。</p>
<p>吹き替え用字幕の作成では「この文を3.5秒枠に収まるよう、15語以内で意味を保って圧縮してください」といった制約付きの指示が大量に発生します。Claudeはこうした <strong>指定された時間枠に良い具合にテキストを圧縮する器用さ</strong> に長けています。単に文を短くするだけでなく、TTSが読み上げたときの自然さ（息継ぎの位置、contractionの使い方、列挙の圧縮）まで考慮した出力が安定して得られます。</p>
<p>このフロー構築時に実際にClaudeで吹き替え用字幕を作成したところ、181cueの字幕が146cueに最適化され（19.3%削減）ました。1cueずつの速度比（テキスト長 / 表示時間）がすべて1.3倍以内に収まるよう調整するという繊細な作業を、Claudeは高い精度でこなしてくれます。</p>
<h2>Step 1：字幕（テキスト）を作成する {#step1-whisper}</h2>
<h3>文字起こしの方法 {#transcription}</h3>
<p>吹き替えの起点となる日本語テキストを用意します。方法は大きく2つあります。</p>
<h4>方法1: Premiere Proの文字起こし機能（おすすめ）</h4>
<p>私が実際に使っているのはこちらです。Premiere Proには <strong>音声のテキスト変換</strong> 機能が組み込まれており、映像のコンテキストも考慮した高精度な文字起こしが得られます。手順は簡単で、Premiere Proのキャプションパネルから「文字起こし」を実行し、結果を <strong>テキスト(.txt)形式でエクスポート</strong> するだけです。</p>
<p>Premiere Proの文字起こしは動画の映像情報も解析に使うため、Whisper単体と比べて固有名詞や専門用語の認識精度が高く感じます。すでにPremiere Proで編集している場合は、追加のセットアップなしですぐに使えるのも大きなメリットです。</p>
<p>なお、Premiere Proにもフィラーワード（「えー」「あのー」等）を自動除去する機能がありますが、 <strong>これを適用せずにそのままエクスポートする</strong> ことを推奨します。フィラー除去を適用するとタイムコードが細分化されすぎてしまい、このあとClaude に渡して意味理解ベースの整形を行う際に、文脈の把握精度が落ちる可能性があります。フィラーの除去はClaude側で文脈を見ながら行うほうが、結果的に品質の高いテキストが得られます。</p>
<h4>方法2: Whisper（faster-whisper）を使う方法</h4>
<p>Premiere Proがない環境では、<a href="https://github.com/SYSTRAN/faster-whisper">faster-whisper</a>が強力な代替手段です。GPUがあれば<code>large-v3-turbo</code>モデルで高速かつ高精度な文字起こしが可能です。</p>
<pre><code class="language-bash">python transcribe.py "input_video.mp4" "output_dir/" \
    --model large-v3-turbo \
    --language ja \
    --beam-size 5
</code></pre>
<pre><code class="language-python">from faster_whisper import WhisperModel

model = WhisperModel("large-v3-turbo", device="cuda", compute_type="float16")
segments, info = model.transcribe(
    "input_video.mp4",
    language="ja",
    beam_size=5,
    vad_filter=True,
    vad_parameters={"min_silence_duration_ms": 500},
    word_timestamps=True,
    temperature=0.0,
)
</code></pre>
<p><code>vad_filter=True</code>で無音区間を自動検出し、セグメント分割が改善されます。出力はSRT形式で保存します。</p>
<p>どちらの方法でも、このあとの前処理と翻訳のステップは同じです。</p>
<h3>前処理：Python＋Claudeの二段構え {#preprocessing}</h3>
<p>文字起こしの生出力はそのままでは翻訳に使えません。前処理は <strong>Pythonで機械的に処理する段階</strong> と <strong>Claudeで意味理解して整形する段階</strong> の二段構えです。</p>
<h4>第1段階：Pythonによる機械的な前処理</h4>
<p>以下の3つはPythonスクリプトで自動化できます。</p>
<p><strong>フィラー除去</strong>: 「えー」「あのー」「うーん」など、テキスト全体がフィラーだけで構成されるセグメントを丸ごと除去します。</p>
<pre><code class="language-python">FILLER_ONLY = {
    "はい", "はい。", "えー", "えーと", "えっと",
    "うーん", "うん", "あの", "あー", "おー",
    "そう", "そうです", "そうですね", "ね", "で",
}

if segment["text"].strip() in FILLER_ONLY:
    continue  # このセグメントを丸ごとスキップ
</code></pre>
<p>また、文頭のフィラー（「えーと、では次に...」の「えーと、」部分）も正規表現で除去します。</p>
<p><strong>固有名詞の機械的な置換</strong>: Whisperは専門用語や固有名詞を高い確率で誤認識します。あらかじめ「誤認識パターン→正しい表記」の置換リストを用意しておきます。</p>
<pre><code class="language-python"># チャンネル固有の置換リストの例
REPLACEMENTS = [
    ("新学校", "進学校"),      # 同音異字
    ("元受け", "元請け"),      # 漢字誤り
    ("下受け", "下請け"),      # 漢字誤り
    ("チャンネル誤認識A", "正しいチャンネル名"),  # 固有名詞
]

for old, new in REPLACEMENTS:
    text = text.replace(old, new)
</code></pre>
<p>この置換リストは最初から完璧に作る必要はありません。字幕作成を繰り返す中で新しい誤認識パターンを見つけたら、Claudeに「この誤認識を対応表に追加しておいて」と指示しておけば、動画を作れば作るほど置換リストが充実し、前処理の精度が上がっていきます。</p>
<p><strong>セグメント結合</strong>: 短すぎるセグメントは隣接セグメントと結合します。</p>
<pre><code class="language-python">def should_merge(current, next_seg, max_gap=0.8, max_duration=16.0, max_chars=160):
    gap = next_seg["start"] - current["end"]
    if gap > max_gap:       # 間が0.8秒以上空いていたら別セグメント
        return False
    if next_seg["end"] - current["start"] > max_duration:  # 結合後16秒超
        return False
    if len(current["text"] + next_seg["text"]) > max_chars:  # 結合後160字超
        return False
    return True
</code></pre>
<h4>第2段階：Claudeによる意味理解ベースの整形</h4>
<p>Pythonの機械処理を通しても、文字起こしの生テキストはまだ「話し言葉の書き起こし」のままです。実際の例を見てみましょう。</p>
<p><strong>Whisper生出力（Python前処理後）</strong>:</p>
<pre><code>えーとですね今日はまあ設定周りの話をちょっとしたいなと思ってまして
まあ結構つまずくポイントっていうかですねそのへんをまとめていきます
</code></pre>
<p><strong>Claudeで整形した結果</strong>:</p>
<pre><code>今回は設定周りでつまずきやすいポイントをまとめて解説します。
</code></pre>
<p>ここで起きていることは単なる「誤字修正」ではありません。</p>
<ul>
<li>「えーとですね」「まあ」「ちょっと」「っていうかですね」→ 口語のフィラー的な表現を除去</li>
<li>2文にまたがっていた内容を1文に凝縮</li>
<li>話者の意図を汲み取り、翻訳しやすい明確な文に再構成</li>
</ul>
<p>もう1つ、配信動画の冒頭部分の例です。</p>
<p><strong>Whisper生出力</strong>:</p>
<pre><code>はいみなさんこんにちは
今日はちょっと天気が良くてですね散歩がてら来たんですけど
まあそれはさておきですね今日の本題なんですが
</code></pre>
<p><strong>Claudeで整形した結果</strong>:</p>
<pre><code>（冒頭の挨拶・雑談を削除し、テーマ部分から開始）
</code></pre>
<p>配信動画では冒頭に挨拶や雑談が数分入ることが多いですが、吹き替え音声ではテーマに直結した内容だけが必要です。「ここからがテーマの本題」という判断はPythonにはできず、 <strong>文脈を理解するClaudeだからこそ可能な処理</strong> です。</p>
<p>このように、前処理の第1段階（Python）でノイズを機械的に除去し、第2段階（Claude）で話し言葉を自然な文章に再構成するという流れが、翻訳精度を大きく左右します。</p>
<p>:::ad</p>
<h2>Step 2：Claudeで翻訳字幕を作成しTTS最適化する {#step2-translation}</h2>
<h3>翻訳のポイント {#translation-tips}</h3>
<p>前処理済みの日本語テキストを、Claudeに渡して対象言語に翻訳します。翻訳と同時に、TTS用の最適化も一括で指示するのが効率的です。</p>
<p>Claudeへの翻訳指示では、以下のポイントを明示します。</p>
<p><strong>全言語共通</strong>:</p>
<ul>
<li><strong>話し言葉として自然な翻訳</strong>にすること。字幕翻訳とは異なり、TTSで読み上げるため書き言葉的な表現は避ける</li>
<li>各cueは表示時間の枠内でTTSが読み切れるテキスト量に収めること</li>
</ul>
<p><strong>英語の場合</strong>:</p>
<ul>
<li>contractions を積極使用（"can not" → "can't"、"do not" → "don't"）</li>
<li>冗長な表現をカット（"Furthermore" → "Also"、"In order to" → "To"）</li>
</ul>
<p><strong>韓国語の場合</strong>:</p>
<ul>
<li><strong>해요体（ヘヨチェ）に統一</strong>する。韓国語には丁寧さのレベルが異なる複数の文体があります。해요体は日本語の「〜です・〜ます」に相当するカジュアルな丁寧語で、YouTube動画の語りかけ口調に最も自然です。一方、합니다体（ハムニダチェ）はニュースや公式文書で使われるフォーマルな敬語で、TTSで読み上げると堅く不自然に聞こえます</li>
<li>漢語系の長い複合語はTTSが一息で読み上げてしまうため、分解して自然なリズムにする（例: 「충분조건」→「충분한 조건」）</li>
</ul>
<p>実際の変換例を見てみましょう。</p>
<p><strong>日本語（Claude整形済み）</strong>:</p>
<pre><code>興味があるとか好きだなと思うこと以外、人ってそもそも長い時間はできないじゃないですか。
</code></pre>
<p><strong>英語の吹き替え用字幕（<code>_en_dub.srt</code>、Claude作成）</strong>:</p>
<pre><code>Other than things you're interested in or things you like,
you can't spend long hours on them, right?
</code></pre>
<p><strong>韓国語の吹き替え用字幕（<code>_kr_dub.srt</code>、Claude作成）</strong>:</p>
<pre><code>흥미가 있거나, 좋아하지 않으면
오랜 시간을 들일 수 없잖아요
</code></pre>
<p>Claudeは日本語の口語ニュアンス（「〜じゃないですか」という同意を求める語尾）を、英語では"right?"、韓国語では"〜잖아요"（해요体の同意表現）に適切に変換しています。こうした <strong>ニュアンスレベルの翻訳精度</strong> が、機械翻訳ではなくClaudeを使う理由です。</p>
<h3>TTS向け最適化ルール {#tts-optimization}</h3>
<p>翻訳後の字幕をTTS用に最適化した「吹き替え用字幕」を作成します。ファイル名は<code>動画名_en_dub.srt</code>のように言語コード + <code>_dub</code>を付けて、表示用字幕と区別します。</p>
<p><strong>核心ルール: 1cue = 1完結文</strong></p>
<p>通常の字幕では1つの文が複数cueにまたがることがありますが、TTS用SRTでは <strong>1つのcueに1つの完結した文</strong> を入れます。TTSエンジンはcue単位で読み上げるため、文の途中で切れると不自然なイントネーションになります。</p>
<p>具体例で見てみましょう。元の日本語が「基礎練習を毎日続けることが上達の近道ですが、ただ量をこなすだけでは効率が悪い」という文だった場合、以下のように変換します。</p>
<p><strong>通常の表示用字幕（_en.srt）</strong> — 画面に表示するための短い区切り:</p>
<pre><code>1
00:00:03,200 --> 00:00:05,800
Practicing the basics every day

2
00:00:05,800 --> 00:00:08,500
is the fastest way to improve,

3
00:00:08,500 --> 00:00:11,200
but just doing a lot of practice
isn't very efficient.
</code></pre>
<p><strong>TTS用の吹き替え字幕（_en_dub.srt）</strong> — 1cueに1完結文:</p>
<pre><code>1
00:00:03,200 --> 00:00:11,200
Practicing basics daily is the fastest way to improve, but just doing a lot isn't efficient.
</code></pre>
<p>表示用字幕では3cueに分かれていた文を、吹き替え用字幕では1cueにまとめています。TTSは<code>00:00:03,200</code>の地点でこの1文を一息で読み上げるため、自然なイントネーションになります。</p>
<p>もう1つ、テキスト短縮の例も見てみましょう。</p>
<table>
<thead>
<tr>
<th>変換前（翻訳そのまま）</th>
<th>変換後（TTS最適化）</th>
<th>短縮ポイント</th>
</tr>
</thead>
<tbody>
<tr>
<td>It is important to understand that...</td>
<td>You need to understand that...</td>
<td>冗長な書き出しをカット</td>
</tr>
<tr>
<td>In order to achieve this goal</td>
<td>To achieve this</td>
<td>前置詞句を圧縮</td>
</tr>
<tr>
<td>Furthermore, you should also consider</td>
<td>Also, consider</td>
<td>接続詞と冗長語を削減</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>ルール</th>
<th>詳細</th>
</tr>
</thead>
<tbody>
<tr>
<td>1cueの文字数</td>
<td>英語: 15-20語、韓国語: 25-35文字</td>
</tr>
<tr>
<td>超短cue</td>
<td>1秒未満のcueは隣接に統合</td>
</tr>
<tr>
<td>息継ぎ</td>
<td>20語以上の連続テキストにはカンマで区切りを入れる</td>
</tr>
<tr>
<td>数字</td>
<td>韓国語ではハングルで綴る（TTSの数字読みが不安定）</td>
</tr>
</tbody>
</table>
<p><strong>速度予測の目安（英語）</strong></p>
<pre><code>1語 ≈ 120-150ms
2秒枠 → 最大13-16語
3秒枠 → 最大20-25語
4秒枠 → 最大27-33語
</code></pre>
<p>cueの表示時間に対してテキストが長すぎると、速度調整の上限を超えて失敗します。上記の目安を参考にテキスト量を調整してください。</p>
<h2>Step 3：ElevenLabs APIで音声を生成する {#step3-tts}</h2>
<p>ここがこのワークフローの核心です。Dub SRTの各cueをElevenLabs APIに送り、音声ファイルを取得します。</p>
<h3>APIの基本仕様 {#api-basics}</h3>
<pre><code>エンドポイント: POST https://api.elevenlabs.io/v1/text-to-speech/{voice_id}
認証ヘッダー: xi-api-key: &#x3C;your-api-key>
レスポンス: audio/mpeg（MP3バイナリ）
</code></pre>
<p>リクエストボディ:</p>
<pre><code class="language-json">{
  "text": "The text to synthesize.",
  "model_id": "eleven_flash_v2_5",
  "voice_settings": {
    "stability": 0.5,
    "similarity_boost": 0.75
  }
}
</code></pre>
<p><code>stability</code>は声の一貫性（高いほど安定、低いほど表現力豊か）、<code>similarity_boost</code>は選択した音声への忠実度を制御します。吹き替え用途では上記のバランスがおすすめです。</p>
<h3>Pythonスクリプトの実装 {#python-script}</h3>
<p>処理の全体フローは以下の通りです。</p>
<pre><code class="language-python">import requests, hashlib, json, re, subprocess
from pathlib import Path
from dotenv import load_dotenv

API_BASE = "https://api.elevenlabs.io/v1"

def tts_generate(api_key, voice_id, text, output_path,
                 model_id="eleven_flash_v2_5", max_retries=3):
    """1つのテキストからMP3音声を生成する"""
    url = f"{API_BASE}/text-to-speech/{voice_id}"
    headers = {
        "xi-api-key": api_key,
        "Content-Type": "application/json",
    }
    body = {
        "text": text,
        "model_id": model_id,
        "voice_settings": {"stability": 0.5, "similarity_boost": 0.75},
    }

    for attempt in range(max_retries):
        resp = requests.post(url, headers=headers, json=body,
                             stream=True, timeout=60)
        if resp.status_code == 200:
            with open(output_path, "wb") as f:
                for chunk in resp.iter_content(chunk_size=4096):
                    f.write(chunk)
            return
        if resp.status_code in (429, 500, 502, 503):
            wait = 2 ** (attempt + 1)
            time.sleep(wait)
            continue
        resp.raise_for_status()
    raise RuntimeError(f"TTS failed after {max_retries} retries")
</code></pre>
<p>SRT解析も自前で実装します。正規表現でcue番号、タイムスタンプ、テキストを抽出します。</p>
<pre><code class="language-python">SRT_BLOCK_RE = re.compile(
    r"(?ms)^\s*(\d+)\s*\n"
    r"(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})\s*\n"
    r"(.*?)(?=\n{2,}|\Z)"
)

def parse_ts(value):
    """SRTタイムスタンプをミリ秒に変換"""
    hh, mm, rest = value.split(":")
    ss, ms = rest.split(",")
    return ((int(hh) * 60 + int(mm)) * 60 + int(ss)) * 1000 + int(ms)

def parse_srt(path):
    text = Path(path).read_text(encoding="utf-8-sig")
    cues = []
    for m in SRT_BLOCK_RE.finditer(text):
        cues.append({
            "index": int(m.group(1)),
            "start_ms": parse_ts(m.group(2)),
            "end_ms": parse_ts(m.group(3)),
            "text": m.group(4).strip(),
        })
    return cues
</code></pre>
<p>各cueに対して<code>tts_generate()</code>を呼び出し、個別のMP3ファイルを取得します。</p>
<h3>キャッシュとリトライ戦略 {#cache-and-retry}</h3>
<p>APIコストを抑えるため、生成済み音声はキャッシュします。テキスト・Voice ID・モデルIDからSHA1ハッシュを生成し、キャッシュキーとして使います。</p>
<pre><code class="language-python">def build_cache_key(voice_id, model_id, text):
    payload = f"{voice_id}\n{model_id}\n{text}".encode("utf-8")
    return hashlib.sha1(payload).hexdigest()

cache_dir = Path("_tts_cache")
cache_dir.mkdir(exist_ok=True)

key = build_cache_key(voice_id, model_id, cue["text"])
cached = cache_dir / f"{key}.mp3"

if cached.exists():
    # キャッシュヒット → API呼び出しをスキップ
    shutil.copy(cached, output_path)
else:
    tts_generate(api_key, voice_id, cue["text"], cached, model_id)
    shutil.copy(cached, output_path)
</code></pre>
<p>テキストを修正するとハッシュが変わるため、旧キャッシュは自動的に無効化されます。テスト実行（最初の5cueだけ生成）で使ったキャッシュは本番実行でもそのまま再利用されるので、クレジットの無駄がありません。</p>
<h2>Step 4：ffmpegでタイムライン構築 {#step4-timeline}</h2>
<p>cue単位のMP3が揃ったら、ffmpegで1本のタイムライン音声にまとめます。</p>
<h3>速度調整（atempo） {#speed-adjustment}</h3>
<p>TTS音声の長さがcueの表示時間より長い場合、<code>atempo</code>フィルタで再生速度を調整します。</p>
<pre><code class="language-python">def adjust_speed(input_path, output_path, tempo):
    """WAV音声の再生速度をtempo倍に調整"""
    subprocess.run([
        "ffmpeg", "-y", "-hide_banner", "-loglevel", "error",
        "-i", str(input_path),
        "-filter:a", f"atempo={tempo:.4f}",
        "-c:a", "pcm_s16le", "-ar", "48000", "-ac", "2",
        str(output_path),
    ], check=True)
</code></pre>
<p>速度比（ratio）の判定基準は以下の通りです。</p>
<table>
<thead>
<tr>
<th>ratio</th>
<th>判定</th>
</tr>
</thead>
<tbody>
<tr>
<td>1.0以下</td>
<td>調整不要（音声がcue枠より短い）</td>
</tr>
<tr>
<td>1.0〜1.3</td>
<td>atempoで加速して収める</td>
</tr>
<tr>
<td>1.3超</td>
<td>テキストが長すぎる→吹き替え用字幕側を修正</td>
</tr>
</tbody>
</table>
<p>英語の場合は1.5倍まで許容できますが、1.3倍を超えると早口感が出始めます。</p>
<h3>無音ベース＋adelayで絶対時刻配置 {#timeline-placement}</h3>
<p>ここが <strong>タイムライン構築の肝</strong> です。YouTubeの吹き替え音声は元動画と長さが完全一致していなければ登録できません。TTS音声を単純に前詰めで連結すると、cue間の無音区間が消えるため大抵の場合は元動画より短くなってしまい、アップロードが弾かれます。</p>
<p>そこで、まず動画全尺の無音WAVをベースとして作り、各cueの音声を <strong>SRTの開始時刻に基づいて絶対位置に配置</strong> する方式を取ります。</p>
<pre><code class="language-python"># 動画全尺の無音WAVを生成
subprocess.run([
    "ffmpeg", "-y", "-hide_banner", "-loglevel", "error",
    "-f", "lavfi", "-i", "anullsrc=r=48000:cl=stereo",
    "-t", f"{total_duration_sec:.3f}",
    "-c:a", "pcm_s16le", "silence.wav",
], check=True)
</code></pre>
<p>各cueの音声に<code>adelay</code>フィルタを適用して、SRTの開始時刻（ミリ秒）に配置します。</p>
<pre><code># ffmpegフィルタグラフの例
[1:a]adelay=3200|3200[d0]    # cue1を3.2秒地点に配置
[2:a]adelay=8500|8500[d1]    # cue2を8.5秒地点に配置
[3:a]adelay=15000|15000[d2]  # cue3を15.0秒地点に配置
</code></pre>
<p>この方式により、途中のcueが失敗しても後続のタイミングがずれません。</p>
<h3>amixによるミックスダウン {#mixdown}</h3>
<p>最後に<code>amix</code>フィルタで無音ベースと全cueを合成します。</p>
<pre><code>[0:a][d0][d1][d2]amix=inputs=4:duration=first:dropout_transition=0:normalize=0[out]
</code></pre>
<p><strong>重要</strong>: <code>normalize=0</code>を必ず指定してください。デフォルトの<code>normalize=1</code>だと各入力の音量が<code>1/N</code>に正規化され、cue数が多い（例: 150cue）とほぼ無音になります。</p>
<p>cue数が多い場合は30cueずつバッチ処理し、段階的にミックスします。フィルタグラフが長くなるため、<code>-filter_complex_script</code>でファイル経由で渡すのが安全です。</p>
<p>:::ad</p>
<h2>Step 5：マスタリングと品質検証 {#step5-mastering}</h2>
<h3>ゲイン調整とピークリミッタ {#gain-limiter}</h3>
<p>タイムライン音声にゲインとリミッタを適用し、最終MP3を出力します。</p>
<pre><code class="language-python">def apply_mastering(input_path, output_path, gain_db=7.0, limit_db=-1.0):
    limit_linear = 10 ** (limit_db / 20)  # dBFSを線形値に変換
    subprocess.run([
        "ffmpeg", "-y", "-hide_banner", "-loglevel", "warning",
        "-i", str(input_path),
        "-filter:a", f"volume={gain_db:.2f}dB,alimiter=limit={limit_linear:.4f}",
        "-ar", "48000", "-ac", "2",
        "-c:a", "libmp3lame", "-b:a", "192k",
        str(output_path),
    ], check=True)
</code></pre>
<ul>
<li><code>volume=7.0dB</code>: TTS音声は元動画より控えめな音量になりがちなので増幅します</li>
<li><code>alimiter</code>: ピークが-1.0dBFSを超えないようにクリッピングを防止します</li>
</ul>
<h3>レポートによる品質確認 {#quality-check}</h3>
<p>生成結果をJSONレポートで確認します。</p>
<pre><code class="language-json">{
  "input_cues": 171,
  "ok_cues": 170,
  "silenced_cues": 0,
  "failed_cues": 0,
  "skipped_empty_cues": 1,
  "cache_hits": 5,
  "duration_ok": true,
  "max_ratio": 1.28
}
</code></pre>
<p>確認すべきポイントは以下の3つです。</p>
<ul>
<li><strong>failed_cues = 0</strong>: すべてのcueが正常に生成されていること</li>
<li><strong>silenced_cues = 0</strong>: 無音で埋められたcueがないこと</li>
<li><strong>duration_ok = true</strong>: 出力MP3の長さが元動画と一致していること</li>
</ul>
<p>最終検証として、ffmpegでデコードエラーがないことも確認します。</p>
<pre><code class="language-bash">ffmpeg -v error -xerror -i output_dub.mp3 -f null NUL
# exit code 0 なら問題なし
</code></pre>
<h2>Step 6：Premiere ProでBGM・SEと合成する {#step6-premiere}</h2>
<p>Step 5で出力したMP3はTTS音声のみで、BGM（背景音楽）やSE（効果音）が含まれていません。ここでPremiere Proの編集済みタイムラインを活用します。</p>
<p>元動画の編集プロジェクトに吹き替え音声トラックを追加するだけで、通常の動画書き出しと吹き替え音声の書き出しを同じタイムラインから行えます。</p>
<p><img src="/2ecaaf0838542943780e6d9f08f30cc3/20260329_premierePro_dubAudio.png" alt="Premiere Proのタイムライン。A1（主音声・日本語）、A2（BGM）、A3（英語音声）、A4（韓国語音声）の4トラック構成"></p>
<p>書き出しの手順は以下の通りです。</p>
<ol>
<li><strong>日本語動画の書き出し</strong>: 主音声（A1）+ BGM（A2）を有効にして通常通り動画（.mp4）をエクスポート</li>
<li><strong>英語吹き替え音声の書き出し</strong>: 主音声（A1）をミュートし、英語音声（A3）+ BGM（A2）の状態でオーディオのみ（.mp3）をエクスポート</li>
<li><strong>韓国語吹き替え音声の書き出し</strong>: 同様に韓国語音声（A4）+ BGM（A2）でエクスポート</li>
</ol>
<p>最終的な出力は以下の3ファイルです。</p>
<ul>
<li><code>動画名.mp4</code> — 日本語音声付き動画（YouTube本体にアップロード）</li>
<li><code>動画名_en_dub.mp3</code> — 英語吹き替え音声（BGM+SE含む）</li>
<li><code>動画名_kr_dub.mp3</code> — 韓国語吹き替え音声（BGM+SE含む）</li>
</ul>
<p>この方式なら、トラックのミュート切り替えだけで各言語の音声を書き出せるので、言語が増えても手間はほとんど変わりません。視聴者が言語を切り替えたときも「音声だけが変わり、BGMやSEは同じ」という自然な体験になります。</p>
<p>さらに、元動画と各言語の吹き替え音声が同じタイムライン上にあるため、ここからショート動画を切り出す際にも吹き替え音声付きで書き出せます。ショート用に別途音声を用意する必要がなく、多言語ショート動画の量産にもそのまま対応できるのは大きなメリットです。</p>
<h2>Step 7：YouTube Studioへのアップロード {#step7-upload}</h2>
<!-- TODO: YouTube Studioのスクリーンショットを追加 -->
<ol>
<li>YouTube Studioで対象動画の編集画面を開く</li>
<li>「字幕」タブから対象言語を追加</li>
<li>「ダブ（吹き替え）を追加」を選択</li>
<li>Step 6で作成した完成版MP3ファイルをアップロード</li>
</ol>
<p>YouTubeの吹き替え音声は <strong>元動画と長さが完全一致</strong> していなければ登録できません。Step 4でタイムライン配置方式（単純連結ではなく、元動画のタイムコード位置にffmpegで各cueを配置）を採用したのはこのためです。この方式により、途中のcueが多少長くても短くても、全体の尺は常に元動画と一致します。</p>
<p>視聴者は動画の設定メニュー（歯車アイコン）から音声言語を切り替えて視聴できます。</p>
<h2>コストと運用の目安 {#cost-estimation}</h2>
<p>ElevenLabsの<code>eleven_flash_v2_5</code>モデルは <strong>1文字あたり0.5クレジット</strong> です。</p>
<h3>実測コスト（11分の動画）</h3>
<p>実際に11分の動画を英語と韓国語に吹き替えたときの消費クレジットは以下の通りです。</p>
<table>
<thead>
<tr>
<th>言語</th>
<th>消費クレジット</th>
</tr>
</thead>
<tbody>
<tr>
<td>英語</td>
<td>4,452</td>
</tr>
<tr>
<td>韓国語</td>
<td>2,199</td>
</tr>
</tbody>
</table>
<p>ElevenLabsは <strong>文字数課金</strong> なので、1文字あたりの情報密度が高い言語ほどコスパが良くなります。</p>
<table>
<thead>
<tr>
<th>言語</th>
<th>英語を100%とした場合のコスト比</th>
</tr>
</thead>
<tbody>
<tr>
<td>中国語</td>
<td>35〜40%</td>
</tr>
<tr>
<td>韓国語</td>
<td>約45%</td>
</tr>
<tr>
<td>日本語</td>
<td>50〜55%</td>
</tr>
<tr>
<td>英語</td>
<td>100%（基準）</td>
</tr>
<tr>
<td>ドイツ語</td>
<td>110〜120%</td>
</tr>
</tbody>
</table>
<p>ドイツ語は複合語が長くなる傾向があるため、英語より割高になります。</p>
<h3>日本円でのコスト感</h3>
<p>1USD=158円として、11分の動画を英語+韓国語に吹き替えた場合のコストは以下の通りです。</p>
<table>
<thead>
<tr>
<th>プラン</th>
<th>月額</th>
<th>クレジット</th>
<th>英語音声</th>
<th>韓国語音声</th>
<th>合計</th>
</tr>
</thead>
<tbody>
<tr>
<td>Starter</td>
<td>$5</td>
<td>30,000</td>
<td>117円</td>
<td>58円</td>
<td><strong>175円</strong></td>
</tr>
<tr>
<td>Creator</td>
<td>$22</td>
<td>100,000</td>
<td>155円</td>
<td>76円</td>
<td><strong>231円</strong></td>
</tr>
</tbody>
</table>
<p>Starter Planでも11分の動画1本あたり175円程度で2言語対応できます。月間30,000クレジットなので、同程度の動画なら月に4〜5本は処理できる計算です。</p>
<h3>コスト削減のポイント</h3>
<p>コスト削減の本質は <strong>テキスト文字数の削減</strong> です。contractionの活用（"do not" → "don't"）や冗長な表現のカット（"Furthermore" → "Also"）で5〜10%の削減が見込めます。モデルやオーディオフォーマットの変更ではクレジット消費は変わりません。</p>
<h2>まとめ {#conclusion}</h2>
<p>この記事では、ElevenLabs APIとPython + ffmpegを組み合わせた多言語吹き替え音声の自動生成ワークフローを紹介しました。</p>
<p>全体の流れをおさらいします。</p>
<ol>
<li><strong>Premiere ProやWhisper</strong> で日本語テキストを生成し、Python + <strong>Claude</strong> で前処理・整形する</li>
<li><strong>Claude</strong> で翻訳しTTS最適化された <strong>吹き替え用字幕</strong> を作成する</li>
<li><strong>ElevenLabs API</strong> で各cueからMP3音声を生成する</li>
<li><strong>ffmpeg</strong> の<code>adelay</code> + <code>amix</code>で絶対時刻ベースのタイムラインを構築する</li>
<li>ゲイン調整とリミッタで <strong>マスタリング</strong> し、吹き替え音声MP3を出力する</li>
<li><strong>Premiere Pro</strong> で主音声をミュートし、BGM・SEと合成して完成版MP3を作成する</li>
<li><strong>YouTube Studio</strong> に吹き替え音声としてアップロードする</li>
</ol>
<p>このワークフローの肝は2つあります。1つ目は <strong>Claudeによるテキスト処理</strong> （Step 1〜2）。口語の整形、ニュアンスを維持した翻訳、時間枠に合わせたテキスト圧縮――これらは意味理解が必要な作業であり、Pythonスクリプトだけでは実現できません。2つ目は <strong>絶対時刻配置</strong> （Step 4）。単純連結ではなくffmpegで元動画のタイムコード位置に各cueを配置する方式により、出力の尺が確実に元動画と一致します。YouTubeの吹き替え音声は元動画と長さが完全一致している必要があるため、この設計が不可欠です。</p>
<p>ElevenLabsは月額$5から、Claudeも無料プランから利用でき、個人チャンネルの多言語展開への敷居はかなり下がっています。海外視聴者へのリーチを広げたい方は、ぜひ試してみてください。</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/c02a03441f57ded3fb6187336bca472d/20260329_ElevenLabs_thumb.png" medium="image"/></item><item><title><![CDATA[VRChatのBooth商品を独自タグで探しやすくする「VRCFinder」を開発した話]]></title><description><![CDATA[VRChatアセット制作にあたり、Boothの商品情報を効率的にリサーチしたいと思い、独自タグで商品を整理・検索できるWebサービス「VRCFinder」を開発しました。開発の経緯と仕組みについて紹介します。]]></description><link>https://uhiyama-lab.com/ja/blog/webdev/vrcfinder-development-story/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/webdev/vrcfinder-development-story/</guid><category><![CDATA[vrchat]]></category><category><![CDATA[booth]]></category><pubDate>Sun, 22 Feb 2026 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>VRChatのアセット制作（衣装など）を始めるにあたって、まずBoothにどんな商品があるか、市場の全体像を把握したいと思いました。Boothには膨大なVRChat関連商品が出品されていますが、「テイスト」「カラー」「体型」のような切り口で横断的に商品を俯瞰する手段がなく、情報収集に時間がかかっていました。</p>
<p>そこで、Booth商品に独自のタグを付与して整理・検索できるWebサービス「VRCFinder」を開発しました。この記事では、開発の経緯と仕組みについて紹介します。</p>
<p><img src="/dcfdda89fb99f31c13b0db8865df347b/VRCFinder_Home.jpg" alt="VRCFinder ホーム画面"></p>
<p>:::post-link{url="<a href="https://vrcfinder.net/ja/">https://vrcfinder.net/ja/</a>" text="▶︎ VRCFinder - VRChat Booth検索補助サイト"}</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#background">開発のきっかけ</a></li>
<li><a href="#overview">VRCFinderの概要</a></li>
<li><a href="#how-it-works">データ収集と独自タグの仕組み</a></li>
<li><a href="#features">主な機能</a></li>
<li><a href="#technical">技術的な工夫</a></li>
<li><a href="#for-creators">制作者の情報収集にも</a></li>
<li><a href="#guidelines">ガイドライン準拠について</a></li>
<li><a href="#conclusion">まとめ</a></li>
</ul>
<p>:::</p>
<h2>開発のきっかけ {#background}</h2>
<p>VRChatは年々ユーザー数を伸ばしていて、メタバースプラットフォームとしての存在感が増しています。筆者もVRChat上で開催される技術・学術系の集会に積極的に参加していて、最新のAI技術や開発環境についての情報収集に活用しています。</p>
<p>VRChatの文化の中でも特に盛んなのが「改変」と呼ばれるアバターのカスタマイズです。ベースとなるアバターに衣装やアクセサリーを着せ替えたり、テクスチャを書き換えたりして、自分だけの見た目を作り上げる。この改変文化を支えているのが、クリエイターズマーケット<strong>Booth</strong>です。多くのクリエイターがVRChat向けのアバターや衣装、小物をBoothで販売していて、膨大な商品が集まっています。</p>
<p>ただ、BoothはVRChat専用のマーケットではありません。同人誌、音楽、ハンドメイド雑貨など、あらゆるジャンルの商品が出品されている総合マーケットプレイスです。VRChat関連はたくさんあるカテゴリの中の1つに過ぎません。</p>
<p>そのため、VRChatの衣装を探そうとしても、タグやカテゴリの検索結果にVRC以外の商品が混ざってきたり、VRChat特有の切り口（対応アバター、テイスト、体型など）で絞り込む手段がなかったりと、目的の商品にたどり着くまでに時間がかかる実情がありました。</p>
<p>Boothには日々たくさんのクリエイターが商品を出品していて、エコシステムとしてとても活発です。この商品データを、服装の種類やカラー、テイストといった切り口で整理して検索できたら便利だろうな、とはずっと思っていました。</p>
<p>そんな中、2025年10月にBoothがスクレイピングに関するガイドラインを更新しました。その中で、Boothのデータを活用した歓迎するアプリケーション例として「公開情報を収集し独自の検索やレコメンデーションに活用するアプリケーション」が挙げられていたんです。これを見て、自分が作りたかったものはまさにこれだと確信し、VRCFinderの開発に踏み切りました。（ガイドライン準拠の詳細は<a href="#guidelines">後述</a>します）</p>
<p>:::ad</p>
<h2>VRCFinderの概要 {#overview}</h2>
<p>VRCFinderは、Boothに出品されているVRChat関連商品に独自のタグとキーワードを付与し、多角的に検索・閲覧できる無料のWebサービスです。アカウント登録は不要で、PCでもスマートフォンでも利用できます。</p>
<p>注意点として、VRCFinderはリアルタイムの検索サイトではありません。収集対象は<strong>2022年以降に発売</strong>された商品のうち、<strong>好き数（いいね数）500以上</strong>の商品で、定期的にデータを収集・分析して掲載しています。商品1つ1つにタグを付与する分析処理に時間がかかるため、対象年数も絞っています。</p>
<p>新着商品をいち早くチェックしたいなら、Boothを直接見るのが一番早いです。VRCFinderが得意なのは、たとえば「夏だから水着衣装を探したい」「ゴシック系の改変をしたいけど、どんな衣装があるだろう」といったシーンで、テイストや衣装タイプなどの切り口から関連商品をまとめて見つけること。シーズンや改変テーマが決まったときの商品探しを、より効率的にするためのサービスです。</p>
<p>:::post-link{url="<a href="https://vrcfinder.net/ja/">https://vrcfinder.net/ja/</a>" text="▶︎ VRCFinder - VRChat Booth検索補助サイト"}</p>
<h2>データ収集と独自タグの仕組み {#how-it-works}</h2>
<h3>収集基準の背景</h3>
<p>「2022年以降・好き数500以上」という基準は、現実的な運用ラインとして設定しています。基準を厳しくしすぎると掲載数が少なくなりますし、逆に緩くしすぎると対象商品が増えすぎて分析に時間がかかり、データの更新頻度が落ちてしまいます。掲載範囲と更新スピードのバランスを取った結果、この水準に落ち着きました。</p>
<h3>タグ生成の流れ</h3>
<p>独自タグの生成は、複数の情報源を組み合わせて行っています。</p>
<ol>
<li><strong>テキスト分析</strong>: 商品のタイトル・登録タグ・概要欄（説明文）から、対応アバターや衣装種別、特徴的なキーワードを抽出</li>
<li><strong>画像分析</strong>: サムネイル画像からカラーやテイスト（かわいい系、クール系など）を判定</li>
<li><strong>手動キュレーション</strong>: 自動分析の結果を精査し、誤分類の修正や不足タグの補完を実施</li>
</ol>
<h3>テキスト分析から画像分析へ</h3>
<p>開発当初はテキスト分析のみでタグを生成していました。タイトルや概要欄から衣装種別や対応アバター名を抽出する仕組みです。これだけでもある程度は機能しましたが、テキストから得られる情報には限界がありました。</p>
<p>Boothの商品ページには、出品者が設定したタイトル・タグ・説明文が掲載されています。しかし、衣装のテイスト（かわいい系、クール系など）やメインカラー、装飾の特徴、体型の印象といった<strong>見た目から読み取れる情報</strong>は、商品ページに明記されていないことがほとんどです。出品者がわざわざ「この衣装はピンク系でかわいいテイストです」とは書かないですよね。</p>
<p>そこでサムネイル画像の分析を導入しました。画像を解析することで、衣装タイプ・テイスト・外見の特徴・カラー・装飾・体型といった、Boothの商品ページにも登録されていない客観的な情報をタグとして付与しています。これにより、「ゴシック系の衣装」「寒色系のロングコート」のような、抽象的なイメージからでも商品を探せるようになりました。</p>
<h2>主な機能 {#features}</h2>
<h3>独自タグ・キーワード検索</h3>
<p>Boothには存在しない独自のタグとキーワードで横断的に検索できます。「かわいい」「クール」「ゴシック」「和風」といったテイスト系のタグや、「ミニスカート」「ロングコート」「メイド服」といったアイテム種別のタグなど、直感的な言葉で商品を探せます。</p>
<h3>対応モデル一覧</h3>
<p>VRChatで人気のアバターモデルごとに、対応商品を一覧表示できます。自分が使っているアバターを選ぶだけで、そのアバター向けの衣装やアクセサリーを効率的に探せます。</p>
<p><img src="/7a4e04ea066484a12969793d062ccee3/VRCFinder_Search.jpg" alt="対応モデル一覧画面"></p>
<h3>カテゴリ・タグ一覧</h3>
<p>ジャンルごとに整理されたカテゴリとタグの一覧ページを用意しています。「どんなタグがあるか分からない」という方でも、一覧から興味のあるタグをタップするだけで関連商品を閲覧できます。</p>
<p><img src="/bc38fdf1145d90a0482451b5bf81b9ae/VRCFinder_Category.jpg" alt="タグ一覧画面"></p>
<h3>テイスト・体型・カラーフィルタ</h3>
<p>見た目の雰囲気（テイスト）、アバターの体型、メインカラーなどの条件で絞り込みが可能です。衣装だけでなくワールドの検索にも対応しています。改変のテーマが決まっているときに特に便利で、例えば「青基調で統一したい」ならカラーで青を選択するだけで、青色の衣装やワールドをまとめて見つけることができます。複数の条件を組み合わせれば、イメージにより近いアイテムに絞り込めます。</p>
<p><img src="/900958d58e347cdb533b090d8418fbff/VRCFinder_Search_Blue.jpg" alt="ワールドカテゴリでカラー「青」を選択した検索例"></p>
<p>:::ad</p>
<h2>技術的な工夫 {#technical}</h2>
<p>ここでは、開発の中で実際にぶつかった課題と、その対応について紹介します。</p>
<h3>ニュートラルな商品名の取得</h3>
<p>Boothではクリエイターが独自に期間限定セールを行うことがあり、セール中は商品名の頭に「【50%OFF】」「【セール中】」といった文言を付けるのがBoothの文化です。</p>
<p>これ自体は購入者にとってありがたい情報なのですが、VRCFinderの商品一覧にそのまま表示すると、セール文言の分だけ本来の商品名が押し出されてしまい、レイアウトが崩れることが多発しました。しかもセールが終わると元の商品名に戻るので、表示が安定しません。</p>
<p>そこで、セール関連の文言を検出してリスト表示上では非表示にする処理を入れています。</p>
<h3>タグの表記ゆれ統合</h3>
<p>クリエイターさんの中には、タグに細かく商品の特性を登録してくれている方がいます。ただし、同じ意味でも表記が異なるケースが少なくありません。</p>
<ul>
<li>「天使の輪」と「ヘイロー」</li>
<li>「手袋」と「グローブ」</li>
<li>「角」と「ホーン」</li>
<li>「Modular Avatar」と「ModularAvatar」</li>
<li>「魔法の杖」と「魔法杖」</li>
</ul>
<p>これらをそのまま別のタグとして扱うと、検索結果が細分化されすぎてしまいます。「ヘイロー」で検索した人が「天使の輪」タグの商品を見逃すのはもったいない。VRCFinderでは、同義のタグを統合する辞書を用意して対応しました。</p>
<h3>対応モデルの表記ゆれ対応</h3>
<p>表記ゆれの問題は、対応アバターモデルの記載でも発生していました。例えば人気モデルの「Lapwing」は、クリエイターさんによって「ラプウィング」「ラップウイング」「ラップウィング」など、カタカナ表記にかなりのバラつきがあります。</p>
<p>Boothで地味に検索がしづらい原因の1つがこれです。正式名称で検索しても、別の表記で登録されている商品がヒットしません。VRCFinderでは対応モデルごとに表記ゆれの辞書を作成し、どの表記で登録されていても正しくキャッチアップできるようにしています。</p>
<h2>制作者の情報収集にも {#for-creators}</h2>
<p>VRCFinderは、もともと筆者自身がアセット制作のリサーチ目的で開発を始めたツールです。</p>
<p>独自タグで市場の商品を横断的に閲覧できるため、制作者にとっても便利なリサーチツールとして機能します。</p>
<ul>
<li><strong>トレンドの把握</strong>: どんなテイストの衣装が人気なのか</li>
<li><strong>競合調査</strong>: 特定のカテゴリにどれくらいの商品があるか</li>
<li><strong>ニッチの発見</strong>: まだ商品数が少ないカテゴリを見つける</li>
<li><strong>対応アバターの選定</strong>: どのアバター向けの需要が大きいか</li>
</ul>
<p>好き数500以上の人気商品に絞って掲載しているため、ユーザーに支持されているデザインの傾向やトレンドを把握するのにも役立ちます。結果として、制作者だけでなく購入者にとっても便利な検索ツールになりました。</p>
<h2>ガイドライン準拠について {#guidelines}</h2>
<p>VRCFinderのデータ収集は、Boothのガイドラインおよびスクレイピングに関するガイドラインの見直し（2025/10/10）に基づき、公開情報の範囲内で行っています。</p>
<p>Booth側が歓迎するアプリケーション例として「公開情報を収集し独自の検索やレコメンデーションに活用するアプリケーション」が挙げられており、VRCFinderはこの方針に沿って運用しています。</p>
<p>参考: <a href="https://booth.pm/guidelines">Booth ガイドライン</a> / <a href="https://booth.pm/announcements/863">スクレイピングに関するガイドラインの見直しについて (2025/10/10)</a></p>
<p>:::ad</p>
<h2>まとめ {#conclusion}</h2>
<p>VRChatのアセット制作者として、Boothの商品情報をもっと効率的にリサーチしたいという動機から始めた開発が、VRCFinderという形になりました。</p>
<p>Boothには素晴らしい商品がたくさんあります。VRCFinderはそのエコシステムの上に、検索の切り口を追加するツールです。テイスト・カラー・体型・対応モデルなど、多角的な条件で商品を探せることで、理想のアイテムとの出会いをサポートします。</p>
<p>今後も掲載商品の拡充やタグ精度の向上に取り組んでいきます。なお、VRCFinderは定期的に収集したデータを掲載しているため、価格や在庫などの最新情報は必ずBoothの商品ページでご確認ください。</p>
<p>:::post-link{url="<a href="https://vrcfinder.net/ja/">https://vrcfinder.net/ja/</a>" text="▶︎ VRCFinder - VRChat Booth検索補助サイト"}</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/dcfdda89fb99f31c13b0db8865df347b/VRCFinder_Home.jpg" medium="image"/></item><item><title><![CDATA[【原神】星々の幻境チュートリアル全画像解説｜日本語UIノード一覧]]></title><description><![CDATA[原神の星々の幻境チュートリアルが英語UIで分からない方へ。全ノード設定を日本語UIで撮影し、画像付きで解説。「初めての幻境コンポーネント」「初めての幻境」の2シリーズを完全カバー。公式動画と併せて使える実用ガイド。]]></description><link>https://uhiyama-lab.com/ja/blog/gamedev/genshin-miliastra-tutorial-node-jp/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/gamedev/genshin-miliastra-tutorial-node-jp/</guid><category><![CDATA[ugc]]></category><category><![CDATA[learning]]></category><pubDate>Sun, 09 Nov 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p><img src="/67554480a79b2b8c006ce0a90be475fb/000_UGC%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB.webp" alt="原神 星々の幻境のメイン画面"></p>
<p>「星々の幻境のチュートリアル動画、英語UIで全然分からない...」</p>
<p>そんな悩みを抱えていませんか？原神の「星々の幻境」は素晴らしいUGC（ユーザー作成コンテンツ）機能ですが、公式チュートリアルが英語UIのため、日本語でノードを探すのに苦労する方が多いようです。</p>
<p>本記事では、<strong>全チュートリアル動画の各ノード設定を日本語UIで撮影し、画像付きで解説</strong>します。動画と併せて活用すれば、スムーズに学習を進められます。</p>
<p><strong>関連リンク：</strong></p>
<ul>
<li><a href="https://genshin.hoyoverse.com/miliastra_wonderland/?lang=ja-jp">星々の幻境 公式サイト</a></li>
<li><a href="https://x.com/GI_Miliastra">星々の幻境 公式X（旧Twitter）</a></li>
<li><a href="https://www.youtube.com/@MiliastraWonderland_JP">星々の幻境 公式YouTube</a></li>
</ul>
<p>:::toc</p>
<ul>
<li><a href="#about-realm">原神 - 星々の幻境とは</a></li>
<li><a href="#creator-level">クリエイターレベルについて</a>
<ul>
<li><a href="#creator-level-features">解放される8つの主要機能</a></li>
<li><a href="#creator-level-rewards">各レベルで解放・拡張される機能</a></li>
<li><a href="#level-up-method">レベルアップ方法</a></li>
</ul>
</li>
<li><a href="#tutorial-videos">チュートリアル動画について</a></li>
<li><a href="#first-component">初めての幻境コンポーネントを作ろう</a>
<ul>
<li><a href="#component-02">2. カスタム変数の設定と変化</a></li>
<li><a href="#component-04">4. プレイヤーノードグラフの構築</a></li>
</ul>
</li>
<li><a href="#first-realm">初めての幻境を作ろう</a>
<ul>
<li><a href="#realm-02">2. 基本的なモーションデバイス</a></li>
<li><a href="#realm-03">3. エフェクト</a></li>
<li><a href="#realm-04">4. スキルとジョブ</a></li>
<li><a href="#realm-05">5. モンスターのステータスプリセット</a></li>
<li><a href="#realm-06">6. コリジョンとインタラクション</a></li>
<li><a href="#realm-07">7. ロジック設計</a></li>
<li><a href="#realm-08">8. 幻境を構築</a></li>
<li><a href="#realm-09">9. インターフェイス</a></li>
</ul>
</li>
<li><a href="#summary">まとめ</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>原神 - 星々の幻境とは {#about-realm}</h2>
<p><img src="/7ae9b9070142d00de1bec41ff0423f66/001_%E6%93%8D%E4%BD%9C%E7%94%BB%E9%9D%A2.jpg" alt="原神 星々の幻境の操作画面"></p>
<p>「原神 - 星々の幻境」は、2025年10月22日に原神に実装されたUGC（ユーザー作成コンテンツ）機能です。原神のアセットを使い、ビジュアルスクリプティング方式でオリジナルゲームを制作できます。</p>
<h3>利用開始方法</h3>
<ol>
<li>原神本編のプロローグをクリア</li>
<li>「星々の幻境」モードがアンロック</li>
<li>「自分の幻境」を選択</li>
<li>エディタ画面でノード編集開始</li>
</ol>
<p>ノード編集は別ウィンドウで開き、ビジュアルノードを接続してゲームロジックを組み立てます。</p>
<p>:::ad</p>
<h2>クリエイターレベルについて {#creator-level}</h2>
<p><img src="/471255c77aeb29f923a825a3376de95d/002_%E3%82%AF%E3%83%AA%E3%82%A8%E3%82%A4%E3%82%BF%E3%83%BC%E3%83%AC%E3%83%99%E3%83%AB.png" alt="クリエイターレベルの画面"></p>
<p>原神の星々の幻境では、一部機能の利用に「クリエイターレベル」が必要です。レベルは1～5まであり、各レベルで様々な機能が解放・拡張されます。</p>
<h3>解放される8つの主要機能 {#creator-level-features}</h3>
<p>クリエイターレベルに応じて、以下の8つの機能が段階的に解放・拡張されます：</p>
<ol>
<li><strong>クラウド保存機能</strong> - 幻境データのクラウド保存</li>
<li><strong>ヒラメキ応援プログラム</strong> - 創作支援プログラム</li>
<li><strong>カスタムテキスト表示</strong> - 独自テキストの表示</li>
<li><strong>綺麗なパフォーマンスボックス</strong> - パフォーマンス最適化機能</li>
<li><strong>アチーブメント</strong> - 達成要素の実装</li>
<li><strong>ステージ内データ機能</strong> - ステージデータの管理</li>
<li><strong>ランキング機能</strong> - ランキングシステム</li>
<li><strong>ランクシステム</strong> - プレイヤーランク機能</li>
</ol>
<h3>各レベルで解放・拡張される機能 {#creator-level-rewards}</h3>
<p><img src="/299a690b696e6ba9678af09b374672c8/002_1-%E3%82%AF%E3%83%AA%E3%82%A8%E3%82%A4%E3%82%BF%E3%83%BC%E3%83%AC%E3%83%99%E3%83%AB1.png" alt="クリエイターレベル1の報酬"></p>
<p><img src="/505a25155de937febf27e8f28d3bdea5/002_2-%E3%82%AF%E3%83%AA%E3%82%A8%E3%82%A4%E3%82%BF%E3%83%BC%E3%83%AC%E3%83%99%E3%83%AB2.png" alt="クリエイターレベル2の報酬"></p>
<p><img src="/7550d28bf6ff6482ff8c4cfbf1a98765/002_3-%E3%82%AF%E3%83%AA%E3%82%A8%E3%82%A4%E3%82%BF%E3%83%BC%E3%83%AC%E3%83%99%E3%83%AB3.png" alt="クリエイターレベル3の報酬"></p>
<p><img src="/ed4017b709e43883b45953f5c54a35b9/002_4-%E3%82%AF%E3%83%AA%E3%82%A8%E3%82%A4%E3%82%BF%E3%83%BC%E3%83%AC%E3%83%99%E3%83%AB4.png" alt="クリエイターレベル4の報酬"></p>
<p><img src="/8573273bd71e8b37f7653a6de9d7ff05/002_5-%E3%82%AF%E3%83%AA%E3%82%A8%E3%82%A4%E3%82%BF%E3%83%BC%E3%83%AC%E3%83%99%E3%83%AB5.png" alt="クリエイターレベル5の報酬"></p>
<h3>レベルアップ方法 {#level-up-method}</h3>
<p>星々の幻境には、幻境を遊んで得られる「幻境レベル」がありますが、これは「クリエイターレベル」とは別物です。ゲームをプレイしてもクリエイターレベルは上がりません。クリエイターレベルを上げるには、<strong>成長タスク</strong>を完了する必要があります。</p>
<p><img src="/bb1224bc4bdfceef2c9a5620551713a8/004_%E3%82%AF%E3%83%AA%E3%82%A8%E3%82%A4%E3%82%BF%E3%83%BC%E3%83%AC%E3%83%99%E3%83%AB%E3%81%AE%E4%B8%8A%E3%81%92%E6%96%B9.png" alt="クリエイターレベルの上げ方"></p>
<p><strong>クリエイターレベルをLv.2に上げる</strong></p>
<p>クリエイターポータル内で「幻境コンポーネントクイズ挑戦」「幻境ステージクイズ挑戦」の2つのクイズに答えればOKです。チュートリアルを見れば簡単に答えられる内容なので、まずはレベル2を目標にしましょう。</p>
<p><img src="/51de3b45638cb92bfe43a07bf09bd85c/005_%E3%82%AF%E3%83%AA%E3%82%A8%E3%82%A4%E3%82%BF%E3%83%BC%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%82%AF%E3%82%A4%E3%82%BA.png" alt="クリエイターコンポーネントクイズ"></p>
<p><strong>Lv.3以降</strong></p>
<p>作成した幻境を投稿し、一定数のプレイヤーに遊んでもらう必要があります。公式Discordなどで他クリエイターと交流しながら創作アイデアを広げていくのがオススメです。</p>
<p><strong>報酬内容の変更について</strong></p>
<p>クリエイターレベルの報酬内容は今後も更新される可能性があります。実際、2025年10月31日の調整で、宝箱を開くときの「開く」を表示するタブオプションがLv.3の報酬からLv.2に変更されました。</p>
<p>参考：<a href="https://genshin.hoyoverse.com/miliastra_wonderland/news/details?id=160687">クリエイターレベルと特典の調整について</a></p>
<p>:::ad</p>
<h2>原神 - 星々の幻境 チュートリアル動画について {#tutorial-videos}</h2>
<p><img src="/5b7dc184912e0b716d071d0929dc5687/006_UGC%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB.png" alt="原神 星々の幻境 UGCチュートリアルの画面"></p>
<p>原神 - 星々の幻境のチュートリアル動画は、公式クリエイターポータル、YouTube、Xアカウントで公開されています。</p>
<p><strong>2025年10月時点で投稿されている2つの動画シリーズ：</strong></p>
<ul>
<li><strong>「初めての幻境コンポーネントを作ろう」</strong> - コインを作成して5枚拾ってステージクリアする基本システムの学習</li>
<li><strong>「初めての幻境を作ろう」</strong> - 敵の出現、レベルアップ、宝箱開閉など複雑なコンポーネントの作成と連携の学習</li>
</ul>
<p>どちらも理解しやすいチュートリアル動画ですが、英語UIで解説されているため、日本語で同じノードを探すのが意外と難しい場合があります。</p>
<p>本記事では、公式チュートリアルを補足する形で日本語UI版でのノード設定画像をまとめました。動画と併せてご覧いただければ、スムーズに学習を進められます。</p>
<p>:::ad</p>
<h2>初めての幻境コンポーネントを作ろう {#first-component}</h2>
<h3>2. カスタム変数の設定と変化 {#component-02}</h3>
<p><strong>公式チュートリアル動画</strong>
<a href="https://www.youtube.com/watch?v=tXXd-y4qWnE">https://www.youtube.com/watch?v=tXXd-y4qWnE</a></p>
<p><strong>ノード「プレハブ_取得時のスコア獲得」</strong></p>
<p><img src="/7693e09899d73750a4bb29bca3e54102/A2_%E3%82%B3%E3%82%A4%E3%83%B3%E5%8F%96%E5%BE%97.png" alt="プレハブ_取得時のスコア獲得のノード設定"></p>
<h3>4. プレイヤーノードグラフの構築 {#component-04}</h3>
<p><strong>公式チュートリアル動画</strong>
<a href="https://www.youtube.com/watch?v=9tzrJLzuDGI">https://www.youtube.com/watch?v=9tzrJLzuDGI</a></p>
<p><strong>ノード「プレイヤー_幻境終了」</strong></p>
<p><img src="/98be6b857225593ec504f8970859af05/A3_%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E7%B5%82%E4%BA%86.png" alt="プレイヤー_幻境終了のノード設定"></p>
<p>:::ad</p>
<h2>初めての幻境を作ろう {#first-realm}</h2>
<h3>2. 基本的なモーションデバイス {#realm-02}</h3>
<p><strong>公式チュートリアル動画</strong>
<a href="https://www.youtube.com/watch?v=1EswERt2m3c">https://www.youtube.com/watch?v=1EswERt2m3c</a></p>
<p><strong>ノード「石畳起動」</strong></p>
<p><img src="/5040679df7ed8fb7f843a79d81d06e18/B1_%E7%9F%B3%E7%95%B3%E8%B5%B7%E5%8B%95.png" alt="石畳起動のノード設定"></p>
<p><strong>ノード「石畳崩壊」</strong></p>
<p><img src="/dbd97a99ace911a380ddee638f0b24c5/B2_%E7%9F%B3%E7%95%B3%E5%B4%A9%E5%A3%8A.png" alt="石畳崩壊のノード設定"></p>
<h3>3. エフェクト {#realm-03}</h3>
<p><strong>公式チュートリアル動画</strong>
<a href="https://www.youtube.com/watch?v=I8F0bvl4jKY">https://www.youtube.com/watch?v=I8F0bvl4jKY</a></p>
<p><strong>ノード「松明を灯す」</strong></p>
<p><img src="/52242ca8a3c483bc11eea538e14bff99/B3_%E6%9D%BE%E6%98%8E%E3%82%92%E7%81%AF%E3%81%99.png" alt="松明を灯すのノード設定"></p>
<h3>4. スキルとジョブ {#realm-04}</h3>
<p><strong>公式チュートリアル動画</strong>
<a href="https://www.youtube.com/watch?v=yQoQn5IzJqc">https://www.youtube.com/watch?v=yQoQn5IzJqc</a></p>
<p><strong>ノード「戦闘能力を獲得」</strong></p>
<p><img src="/37ac53a7020c78c23021fcbe1e73a08b/B4_%E6%88%A6%E9%97%98%E8%83%BD%E5%8A%9B%E3%81%AE%E7%8D%B2%E5%BE%97.png" alt="戦闘能力を獲得のノード設定"></p>
<h3>5. モンスターのステータスプリセット {#realm-05}</h3>
<p><strong>公式チュートリアル動画</strong>
<a href="https://www.youtube.com/watch?v=It5UWLvC9fg">https://www.youtube.com/watch?v=It5UWLvC9fg</a></p>
<p><strong>ノード「Lv40になる」</strong></p>
<p><img src="/6ab59d3c5837adbebbbeacfbc372d1ac/B5_Lv40%E3%81%AB%E3%81%AA%E3%82%8B.png" alt="Lv40になるのノード設定"></p>
<p><strong>ノード「Lv90になる」</strong></p>
<p><img src="/e3501a61b1ae70332e16597e83607fa4/B6_Lv90%E3%81%AB%E3%81%AA%E3%82%8B.png" alt="Lv90になるのノード設定"></p>
<p><strong>ノード「宝箱のアンロック」</strong></p>
<p><img src="/5d000853469483050cbe554e69c04474/B7_%E5%AE%9D%E7%AE%B1%E3%81%AE%E3%82%A2%E3%83%B3%E3%83%AD%E3%83%83%E3%82%AF.png" alt="宝箱のアンロックのノード設定"></p>
<p><strong>ノード「宝箱を開く」</strong></p>
<p><img src="/54cebd9f4f2ecd2089f056af4362f7c2/B8_%E5%AE%9D%E7%AE%B1%E3%82%92%E9%96%8B%E3%81%8F.png" alt="宝箱を開くのノード設定"></p>
<p>:::ad</p>
<h3>6. コリジョンとインタラクション {#realm-06}</h3>
<p><strong>公式チュートリアル動画</strong>
<a href="https://www.youtube.com/watch?v=ZrlBqOkgLmE">https://www.youtube.com/watch?v=ZrlBqOkgLmE</a></p>
<p><strong>ノード「松明を灯す」（変数追加版）</strong></p>
<p><img src="/fc40aadb87e503f8b214a5b105e32948/B9_%E6%9D%BE%E6%98%8E%E3%82%92%E7%81%AF%E3%81%99_%E4%BF%AE%E6%AD%A3.png" alt="松明を灯す（変数追加版）のノード設定"></p>
<p><strong>ノード「武器のロック解除」</strong></p>
<p><img src="/c81a3b06a81104d0feb43d04a84aed11/B10_%E6%AD%A6%E5%99%A8%E3%81%AE%E3%83%AD%E3%83%83%E3%82%AF%E8%A7%A3%E9%99%A4.png" alt="武器のロック解除のノード設定"></p>
<p><strong>ノード「鍵を拾ってゲートを開ける」</strong></p>
<p><img src="/b54fc5dbf2d76cb780316c9d01c5c33d/B11_%E9%8D%B5%E3%82%92%E6%8B%BE%E3%81%A3%E3%81%A6%E3%82%B2%E3%83%BC%E3%83%88%E3%82%92%E9%96%8B%E3%81%91%E3%82%8B.png" alt="鍵を拾ってゲートを開けるのノード設定"></p>
<p><strong>ノード「木のゲートを開ける」</strong></p>
<p><img src="/1bd9e04852294eb8779b1829368d7bcb/B12_%E6%9C%A8%E3%81%AE%E3%82%B2%E3%83%BC%E3%83%88%E3%82%92%E9%96%8B%E3%81%91%E3%82%8B.png" alt="木のゲートを開けるのノード設定"></p>
<p><strong>ノード「幻境から退出」</strong></p>
<p><img src="/35b6b942b22b353c4d5473ecd5d24519/B13_%E5%B9%BB%E5%A2%83%E3%81%8B%E3%82%89%E9%80%80%E5%87%BA.png" alt="幻境から退出のノード設定"></p>
<h3>7. ロジック設計 {#realm-07}</h3>
<p><strong>公式チュートリアル動画</strong>
<a href="https://www.youtube.com/watch?v=cL1VYOA5zDg">https://www.youtube.com/watch?v=cL1VYOA5zDg</a></p>
<p><strong>ノード「戦闘能力の獲得」（ヒルチャール出現版）</strong></p>
<p><img src="/20ec4a40d95c5af4a650c1c3f6dd30f5/B14_%E6%88%A6%E9%97%98%E8%83%BD%E5%8A%9B%E3%81%AE%E7%8D%B2%E5%BE%97_2.png" alt="戦闘能力の獲得（ヒルチャール出現版）のノード設定"></p>
<p><strong>ノード「Lv40になるとき」（ヒルチャール対応版）</strong></p>
<p><img src="/bc123b74eb3d16f283b16a18eff7eaaa/B15_%E3%83%AC%E3%83%99%E3%83%AB40%E3%81%AB%E3%81%AA%E3%82%8B%E3%81%A8%E3%81%8D(%E3%83%92%E3%83%AB%E3%83%81%E3%83%A3%E3%83%BC%E3%83%AB).png" alt="Lv40になるとき（ヒルチャール対応版）のノード設定"></p>
<p><strong>ノード「宝箱を開く」（鍵の出現版）</strong></p>
<p><img src="/995b56a143bf5590e916330e993f7239/B16_%E5%AE%9D%E7%AE%B1%E3%82%92%E9%96%8B%E3%81%8F(%E9%8D%B5%E3%81%AE%E5%87%BA%E7%8F%BE).png" alt="宝箱を開く（鍵の出現版）のノード設定"></p>
<p><strong>ノード「プレイヤー幻境終了」（エフェクト+終了装置有効化版）</strong></p>
<p><img src="/08edd1acedbcd6b185e3ff6a9c1ca69a/B17_%E3%83%97%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC%E5%B9%BB%E5%A2%83%E7%B5%82%E4%BA%86.png" alt="プレイヤー幻境終了のノード設定"></p>
<p>:::ad</p>
<h3>8. 幻境を構築 {#realm-08}</h3>
<p><strong>公式チュートリアル動画</strong>
<a href="https://www.youtube.com/watch?v=vHCWOKq8kTI">https://www.youtube.com/watch?v=vHCWOKq8kTI</a></p>
<p><strong>ノード「石畳起動」（5つの石畳を同時に作動版）</strong></p>
<p><img src="/70e533cf3c82b243b3872845da8dc2f7/B18_%E7%9F%B3%E7%95%B3%E8%B5%B7%E5%8B%95(5%E3%81%A4).png" alt="石畳起動（5つの石畳を同時に作動）のノード設定"></p>
<p><strong>ノード「プレイヤー_幻境終了」（エフェクト追加版）</strong></p>
<p><img src="/6b3ef3ac777319430f3f22f7b8ab9775/B19_%E3%83%97%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC_%E5%B9%BB%E5%A2%83%E7%B5%82%E4%BA%86(%E3%82%A8%E3%83%95%E3%82%A7%E3%82%AF%E3%83%88%E8%BF%BD%E5%8A%A0).png" alt="プレイヤー_幻境終了（エフェクト追加版）のノード設定"></p>
<h3>9. インターフェイス {#realm-09}</h3>
<p><strong>公式チュートリアル動画</strong>
<a href="https://www.youtube.com/watch?v=Z-f88zYNrlo">https://www.youtube.com/watch?v=Z-f88zYNrlo</a></p>
<p><strong>ノード「ポップアップ_幻境説明」「テキストボックス_武器を探す」「テキストボックス_鍵を探す」</strong></p>
<p>※同じ構成で3つ作成し、プレハブインデックスだけ書き換える</p>
<p><img src="/d64a7097e2fd822c0fc03154fe371bc6/B20_%E3%83%9D%E3%83%83%E3%83%97%E3%82%A2%E3%83%83%E3%83%97_%E5%B9%BB%E5%A2%83%E8%AA%AC%E6%98%8E.png" alt="ポップアップとテキストボックスのノード設定"></p>
<p>:::ad</p>
<h2>まとめ {#summary}</h2>
<p>原神の「星々の幻境」は、大手ゲームタイトルである原神のアセットを使ってオリジナルゲームを制作できる画期的なUGC機能です。公式チュートリアル動画は非常に充実していますが、英語UIで解説されているため、日本語UIで同じノードを探すのに苦労することがあります。</p>
<p>本記事では、「初めての幻境コンポーネントを作ろう」「初めての幻境を作ろう」の2つのチュートリアルシリーズについて、日本語UIでの全ノード設定画面を画像付きで紹介しました。</p>
<h3>これから星々の幻境を始める方へ</h3>
<ol>
<li><strong>まずはチュートリアルから</strong>：公式の「初めての幻境コンポーネントを作ろう」シリーズで基本を学びましょう</li>
<li><strong>クリエイターレベル2を目指す</strong>：2つのマーク試験に合格してLv.2に到達すれば、多くの機能が使えるようになります</li>
<li><strong>本記事を参考資料に</strong>：動画を見ながら本記事の画像を参照すれば、スムーズに学習を進められます</li>
</ol>
<h3>創作のヒント</h3>
<p>星々の幻境では、原神の美麗なアセットを使って自由にゲームを制作できます。公式Discordでは他のクリエイターと交流したり、作品を共有したりできるので、積極的に参加してみるのもおすすめです。</p>
<p>クリエイターレベルを上げながら、様々な機能を使いこなして魅力的な幻境を制作してみてください。本記事が、原神 - 星々の幻境でのコンテンツ制作の助けになれば幸いです。</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/67554480a79b2b8c006ce0a90be475fb/000_UGC%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB.webp" medium="image"/></item><item><title><![CDATA[配色パターンを作るのが面倒だったので『キャラクター配色パターンメーカー』を作った]]></title><description><![CDATA[キャラの配色パターンを作るのに、レイヤー複製して色を置き換えて…が面倒だったので『キャラクター配色パターンメーカー』を開発。肌や主線を固定したまま、服や髪の色だけをワンクリックで変更できます。]]></description><link>https://uhiyama-lab.com/ja/blog/tooldev/character-color-pattern-maker/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/tooldev/character-color-pattern-maker/</guid><pubDate>Fri, 29 Aug 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>キャラクターの配色パターンを作るのって、意外と面倒なんですよね。「このキャラ、髪の色変えたらどうなるかな？」と思って試すには、レイヤー複製して、パーツごとに色を置き換えて…って作業を繰り返す必要があります。</p>
<p>イラスト全体に色調補正をかける方法もあるんですが、肌の色や主線の色まで一緒に変わってしまうので、純粋な配色パターンの比較にはなりません。「肌と主線はそのままで、服と髪だけ変えたい」っていう、ちょっと細かい要望に応えるのが難しいんです。</p>
<p>そんなわけで、ブラウザで画像を読み込んで、残したい色を固定するだけで、ワンクリックで配色パターンを作れるツールを開発しました。</p>
<p><img src="/96d56f65d1e893900e1582078cd3648d/001_character-color-pattern-maker.webp" alt="">
※サンプルイラストは管理人が描いたファンアートです</p>
<p>:::post-link{url="/ja/tools/character-color-pattern-maker/" text="▶︎ キャラクター配色パターンメーカー"}</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#introduction">配色パターン作りが面倒だった話</a></li>
<li><a href="#what-is-ccpm">どんなツール？</a></li>
<li><a href="#features">できること</a></li>
<li><a href="#how-to-use">使い方</a></li>
<li><a href="#palette-types">配色生成モードの種類</a></li>
<li><a href="#advanced-tips">配色シートの活用方法</a></li>
<li><a href="#tech-info">安心のブラウザ完結</a></li>
</ul>
<p>:::</p>
<h2>配色パターン作りが面倒だった話 {#introduction}</h2>
<p><img src="/d03349435523e5d1afad37f0eed2898f/color-pattern-sheet-pastel-1756088056388.png" alt=""></p>
<p>イラストを描いた後で「他の色だったらどうかな？」と試したくなることって、よくあるんですよね。でもそのためにレイヤーを複製してパーツごとに色を置き換えていく作業は、正直面倒です。</p>
<p>全体に色調補正をかける方法もありますが、肌の色や主線の色まで変わってしまって、純粋な配色の比較になりません。「肌と主線はそのままで、服と髪だけ変えたい」って思っても、手作業だと時間がかかります。</p>
<p>そんな悩みを解決するために、このツールを開発しました。ワンクリックで配色パターンを生成できて、偶然面白い色の組み合わせが見つかることもあります。</p>
<h2>どんなツール？ {#what-is-ccpm}</h2>
<p><img src="/f4ff950fdb504bf0f8b6daf3d6620525/002_character-color-pattern-maker_sample1.webp" alt=""></p>
<p>イラストをアップロードして、ワンクリックで配色パターンを生成できるブラウザツールです。最大の特徴は、変更したくない色（肌、主線など）を固定して、不要な色（背景など）を除外できる点です。</p>
<p>これで、キャラの肌や髪の色はそのままに、服や小物の配色だけを変更できます。色彩理論に基づいたパターンや、ランダムで思いもよらない組み合わせも試せます。</p>
<p>:::post-link{url="/ja/tools/character-color-pattern-maker/" text="▶︎ キャラクター配色パターンメーカー"}</p>
<p>:::ad</p>
<h2>できること {#features}</h2>
<ul>
<li>人間の知覚に近い色空間（CIELAB）でイラストを分析して、主要な色を抽出</li>
<li>主線の自動検出機能で、アウトラインの色を簡単に固定</li>
<li>画像をクリックして、変更したくない色を固定、不要な色を除外</li>
<li>ランダム探索、色彩理論、パステル調など、多彩な配色生成モード</li>
<li>生成された配色をリアルタイムでプレビュー</li>
<li>変更前後のイラストと主要色をまとめた配色パターンシートをPNG画像でダウンロード</li>
<li>すべての処理がブラウザ内で完結、イラストデータは外部に送信されない</li>
<li>ドラッグ＆ドロップ対応で、シンプルな操作性</li>
</ul>
<h2>使い方 {#how-to-use}</h2>
<p><img src="/0b225dc116b27626a6141ad76cb9fdad/003_character-color-pattern-maker_sample2.webp" alt=""></p>
<p>基本的な流れは3ステップです。</p>
<h3>1. 画像をアップロードする</h3>
<p><img src="/9546d42b461e388aa285f4d73bd55d48/004-howto-import.png" alt=""></p>
<p>配色を変更したいイラストを、画面左側にドラッグ＆ドロップするか、クリックしてファイルを選択します。背景が透過されたキャラ単体のイラストが最も効果的です。</p>
<h3>2. 色を固定・除外する</h3>
<p><img src="/abe4e900e83d48b6dbd73f8a83fbc27d/005-howto-color-settings.png" alt=""></p>
<p>画像が分析されたら、変更したくない色を設定します。元画像をクリックして、その部分の色を直接「固定」するのが直感的でおすすめ。</p>
<p>まず肌色や髪色をプレビュー上でクリックして固定。次に背景色などを除外。最後に自動検出された主線色も固定すれば精度が上がります。</p>
<h3>3. 配色パターンを生成＆ダウンロード</h3>
<p><img src="/50832b5118731c8aa63b5bf819fdc0b0/006-howto-pattern-generate.png" alt=""></p>
<p>「ランダム探索」や各種配色パターンボタンをクリックすると、固定・除外した色以外が新しい配色に変わります。同じボタンを連続でクリックすると、どんどん違うパターンが出てきます。気に入った配色が見つかったら、「配色シートをダウンロード」ボタンで保存しましょう。</p>
<h2>配色生成モードの種類 {#palette-types}</h2>
<p>いろんな配色パターンを試せるように、複数のモードを用意しています。</p>
<ul>
<li><strong>ランダム探索</strong>: クリックするたびに、まったく新しい色の組み合わせを提案します。思わぬ発見があるかも。</li>
<li><strong>色彩理論モード</strong>: アナロガス（類似色）やコンプリメンタリー（補色）など、色彩理論をベースにした配色を生成。まとまりがありつつ新鮮な印象になります。</li>
<li><strong>パステル調</strong>: 全体的に淡く、優しい色合いに変換。ファンシーな雰囲気を出したい時に。</li>
<li><strong>ビブラント</strong>: 彩度と明度を引き上げて、鮮やかで活き活きとした配色に。ポップで元気なキャラに合います。</li>
<li><strong>その他</strong>: 色を混ぜ合わせる「ブレンド」や、独創的な「クリエイティブ」モードなど、いろんなパターンを試せます。</li>
</ul>
<h2>配色シートの活用方法 {#advanced-tips}</h2>
<p><img src="/d03349435523e5d1afad37f0eed2898f/color-pattern-sheet-pastel-1756088056388.png" alt=""></p>
<p><img src="/4edb3e00e85bffe7a2f0b3e4f2d62b41/color-pattern-sheet-colorTheory-1756094742949.png" alt=""></p>
<p><img src="/b03f018c3a109c4b1e9d997eff776579/color-pattern-sheet-random-1756094726295.png" alt=""></p>
<p>好みの配色が見つかったら、「配色シートのダウンロード」ボタンを押してください。</p>
<p>ダウンロードできる配色パターンシートは、変更前後のイラストが一目で比較できて、主要なカラーコードも記載されています。</p>
<ul>
<li><strong>デザイン資料として</strong>: 新しい配色案をチームやクライアントに提案する時の比較資料に。</li>
<li><strong>SNSでの共有</strong>: 「どの色が好き？」とフォロワーに聞くのにも使えます。</li>
<li><strong>セルフレビュー</strong>: いろんなパターンを見比べることで、自分の配色の好みや新しい可能性を発見できます。</li>
</ul>
<h2>安心のブラウザ完結 {#tech-info}</h2>
<p>このツールは、プライバシーを最優先に設計しています。画像のアップロードから色彩分析、配色の生成、画像のダウンロードまで、すべての処理がブラウザ内で完結します。</p>
<p>イラストデータが外部のサーバーに送信・保存されることは一切ありません。オフライン環境でも、一度ページを読み込んでいれば基本的な機能は動作します。未公開の作品でも、安心して使えます。</p>
<p>:::post-link{url="/ja/tools/character-color-pattern-maker/" text="▶︎ キャラクター配色パターンメーカー"}</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/d03349435523e5d1afad37f0eed2898f/color-pattern-sheet-pastel-1756088056388.png" medium="image"/></item><item><title><![CDATA[手の資料写真を撮るのが難しくて『ハンドトレスカメラ』を作った]]></title><description><![CDATA[手を描く時、自分の手を資料として撮影しようとしても、スマホを持ちながら理想的な角度で撮るのは難しいですよね。ラフ画をカメラに重ねて撮影できる『ハンドトレスカメラ』を開発しました。]]></description><link>https://uhiyama-lab.com/ja/blog/tooldev/hand-trace-camera/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/tooldev/hand-trace-camera/</guid><pubDate>Sun, 27 Jul 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>イラストを描く時、「手」を描くのって難しいですよね。複雑な関節の動き、指の曲がり具合、手のひらの向き...。資料として自分の手を撮影しようとしても、スマホを持ちながら理想的な角度で撮るのは至難の業です。</p>
<p>そんなわけで、描きかけの手のラフ画像を半透明でカメラ映像に重ねて、描きたい手のポーズと角度で撮影できる「ハンドトレスカメラ」を作りました。</p>
<p><img src="/1d31b00af6119af1ad6ace206050046f/howto-hand-trace-camera.webp" alt=""></p>
<p>すべての処理はブラウザ内で完結するので、画像や写真が外部に送信されることはありません。</p>
<p>:::post-link{url="/ja/tools/hand-trace-camera/" text="ハンドトレスカメラ"}</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#introduction">資料撮影の難しさ</a></li>
<li><a href="#what-is-htc">どんなツール？</a></li>
<li><a href="#features">できること</a></li>
<li><a href="#how-to-use">使い方</a></li>
<li><a href="#privacy">ブラウザで完結</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>資料撮影の難しさ {#introduction}</h2>
<p>手は人体の中でも特に複雑な構造を持つパーツです。5本の指それぞれが3つの関節を持ち、手首の角度も含めると無限に近いポーズのバリエーションが存在します。</p>
<p>資料撮影でよくある悩み：</p>
<ul>
<li>描きたい角度で自分の手を撮影するのは物理的に困難</li>
<li>シャッターを押す動作でポーズが崩れてしまう</li>
<li>ラフと実際の手の位置関係を確認しながら撮影するのが難しい</li>
<li>利き手と逆の手を撮影する際の不自然さ</li>
</ul>
<p>「ハンドトレスカメラ」は、これらの問題を「ラフ画像のオーバーレイ表示」で解決します。</p>
<h2>どんなツール？ {#what-is-htc}</h2>
<p><img src="/1d31b00af6119af1ad6ace206050046f/howto-hand-trace-camera.webp" alt=""></p>
<p>描きかけの手のラフ画像を半透明でカメラ映像に重ね合わせることで、理想的な手の資料写真を撮影できるツールです。</p>
<p>「描きたい手のポーズが決まっているけど、正確な形が掴めない」「微妙な指の角度を確認したい」「手と物の持ち方を正確に描きたい」そんな時に使えます。</p>
<p>ラフ画像の透明度を自在に調整できるため、実際の手とラフを重ねて比較しながら、最適な角度とポーズを見つけられます。</p>
<p>:::post-link{url="/ja/tools/hand-trace-camera/" text="ハンドトレスカメラ"}</p>
<p>:::ad</p>
<h2>できること {#features}</h2>
<p><img src="/cac6742c157cc209d4a6c69736f83510/hand-trace-camera.png" alt=""></p>
<ul>
<li>アップロードした画像を半透明でカメラ映像に重ね合わせ、リアルタイムで確認しながら撮影可能</li>
<li>透明度を0〜100%の範囲で自由に調整。ラフと実写のバランスを細かくコントロール</li>
<li>サイズ（10〜500%）、位置（X/Y軸）、回転（-180〜180度）を細かく調整して、ラフと実際の手をピッタリ合わせられる</li>
<li>モバイルデバイスでは、1本指で移動、2本指でピンチ拡大・回転が可能</li>
<li>フロント/バックカメラの切り替えに対応（複数カメラ搭載デバイス）</li>
<li>3:4の縦長アスペクト比で、900×1200ピクセルの高解像度PNG画像として保存</li>
<li>すべての処理がブラウザ内で完結。画像や写真が外部サーバーに送信されない</li>
<li>画像の調整がすぐにプレビューに反映され、ストレスフリーな操作感</li>
</ul>
<h2>使い方 {#how-to-use}</h2>
<p>基本的な流れは3ステップです。</p>
<ol>
<li><strong>ラフ画像をアップロード</strong></li>
</ol>
<p>描きかけの手のラフ画像をドラッグ＆ドロップ、またはクリックしてアップロードします。PNG、JPG、WEBP形式に対応。</p>
<ol start="2">
<li><strong>画像を調整してカメラを開始</strong></li>
</ol>
<p>透明度、サイズ、位置、回転を調整して、ラフと実際の手が重なるようにします。準備ができたら「カメラを開始」ボタンをクリック。</p>
<ol start="3">
<li><strong>ポーズを合わせて撮影</strong></li>
</ol>
<p>実際の手をラフに合わせてポーズを取り、「撮影」ボタンをクリック。撮影した画像はプレビューで確認後、ダウンロードできます。</p>
<p>モバイルデバイスでは、画面をタッチして直感的に画像を調整できます。</p>
<p>:::ad</p>
<h2>ブラウザで完結 {#privacy}</h2>
<p>このツールは、ブラウザだけで全ての処理が完結するように作られています。特別なソフトウェアのインストールは必要ありません。</p>
<p>画像のアップロード、カメラ映像の処理、画像の合成まで、すべての処理がブラウザ内で行われます。アップロードしたラフ画像や撮影した写真が、外部のサーバーに送信・保存されることはありません。</p>
<p>:::post-link{url="/ja/tools/hand-trace-camera/" text="ハンドトレスカメラ"}</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/1d31b00af6119af1ad6ace206050046f/howto-hand-trace-camera.webp" medium="image"/></item><item><title><![CDATA[【Steam】審査で何度か不合格になったので、必要な画像14枚のチェックリストを作った]]></title><description><![CDATA[Steam販売に必要な画像14枚のサイズ一覧と審査基準。実際に不合格になった経験から、NGデザインのポイントもまとめました。]]></description><link>https://uhiyama-lab.com/ja/blog/gamedev/steam-release-images-checklist/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/gamedev/steam-release-images-checklist/</guid><category><![CDATA[marketing]]></category><pubDate>Fri, 18 Jul 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p><img src="/6cccbd788c576fcc91d23d99032cab41/20180718-Steam%E3%82%B9%E3%83%88%E3%82%A2%E3%83%9A%E3%83%BC%E3%82%B8.png" alt=""></p>
<p>先日、Boothとitch.ioとSteamにて、<a href="/ja/product/animsprite-pixelizer/">AnimSprite Pixelizer</a>という2Dゲーム開発効率化ツールをリリースしました。CLIP Studioなどで作画したキャラ歩行アニメーション等を、共通のピクセルサイズで一括変換してスプライトシート書き出しができるアプリです。</p>
<p><img src="/1fdb2e9a28c745394d10e0023911acc8/movie_convert.webp" alt=""></p>
<p>Boothとitch.ioは商品情報と画像を登録すれば、すぐに販売できます。しかしSteamは、事前にサポートチームの審査に合格する必要があります。</p>
<p>AnimSprite Pixelizerの場合、アプリ本体の審査は一発で合格しましたが、商品画像について何度か訂正を求められました。今回は、Steam販売に必要な14枚の画像とデザインの注意点についてまとめます。</p>
<p>:::post-link{url="/ja/product/animsprite-pixelizer/" text="AnimSprite Pixelizer"}</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#why-so-many-images">なぜ14枚も必要なのか</a></li>
<li><a href="#image-checklist">必要な画像チェックリスト</a></li>
<li><a href="#design-cautions">デザインの注意点</a></li>
<li><a href="#conclusion">効率的な制作のコツ</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>なぜ14枚も必要なのか {#why-so-many-images}</h2>
<p><strong>「ストアの画像を用意するのメンドクサ！！」</strong></p>
<p>Steam販売経験のあるクリエイターさんは100%同じ言葉を口にするでしょう。それもそのはず。Steamの審査には最低14枚ほどの画像を用意する必要があるんです。</p>
<p>「なんでそんなに必要なの！？」と思う人はSteamをよく見てみてください。</p>
<ul>
<li>検索時のリストに表示される小さな画像（小型カプセル）</li>
<li>フロントページ上部の特集画像（メインカプセル）</li>
<li>季節セールに表示される縦長の画像（垂直カプセル）</li>
<li>ライブラリでゲームを選択したときに、プレイ時間などと一緒に表示される上部のヒーロー画像</li>
</ul>
<p>これらすべてを準備しなければなりません。</p>
<p>しかも、これらの画像は厳格にサイズが決められています。例えば、ヘッダーカプセルは920×430px、小型カプセルは462×174pxです。完璧に同じサイズでなければなりません。SteamWorksでは、ドロップされた画像のサイズに基づいて各画像が設定されるため、少しでもサイズが違うと拒絶されます。</p>
<p><img src="/0ac5b37194d5ea2f8ad4af44ed414122/20250718-Steam%E3%83%89%E3%83%AD%E3%83%83%E3%83%97%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9.png" alt=""></p>
<p>:::ad</p>
<h2>必要な画像チェックリスト {#image-checklist}</h2>
<p>登録に必要な画像のチェックリストを作成しました。これを見ながら、Photoshopなど画像作成ツールでキャンバスを作成してデザインしましょう。</p>
<p><img src="/0f9e02fb6cbaa7f576082b6b39ea8b0c/20250718-Steam%E3%82%B9%E3%83%88%E3%82%A2%E7%99%BB%E9%8C%B2%E3%81%AB%E5%BF%85%E8%A6%81%E3%81%AA%E7%94%BB%E5%83%8F%E4%B8%80%E8%A6%A7.png" alt=""></p>
<p>Steamのストア情報に必要なグラフィックアセットは、「ストアアセット」「スクリーンショットアセット」「ライブラリアセット」の3種類です。下記のチェックリストを参考に制作しましょう。</p>
<p><em>※記事執筆時点（2025年7月）の指定サイズです。</em></p>
<h3>ストアアセット</h3>
<p><img src="/c77bdaeb7a069dd27c406bf18bdd4c42/20250718-Steam%E5%90%88%E6%A0%BC%E7%94%BB%E5%83%8F_1_%E3%82%B9%E3%83%88%E3%82%A2%E3%82%A2%E3%82%BB%E3%83%83%E3%83%88.jpg" alt=""></p>
<table>
<thead>
<tr>
<th>画像タイプ</th>
<th>サイズ（px）</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>ヘッダーカプセル</strong></td>
<td>920×430</td>
<td>ストアページ上部、検索結果など</td>
</tr>
<tr>
<td><strong>小型カプセル</strong></td>
<td>462×174</td>
<td>検索結果のリスト表示</td>
</tr>
<tr>
<td><strong>メインカプセル</strong></td>
<td>1232×706</td>
<td>フロントページの特集枠</td>
</tr>
<tr>
<td><strong>垂直カプセル</strong></td>
<td>748×896</td>
<td>セール時の縦長表示</td>
</tr>
</tbody>
</table>
<h3>スクリーンショットアセット</h3>
<p><img src="/e4e63a16f728e8b6cf60d50c4517b174/20250718-Steam%E5%90%88%E6%A0%BC%E7%94%BB%E5%83%8F_2_%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%E3%82%A2%E3%82%BB%E3%83%83%E3%83%88.jpg" alt=""></p>
<ul>
<li><strong>スクリーンショット</strong>: 1920×1080（推奨）× <strong>最低5枚</strong></li>
</ul>
<h3>ライブラリアセット</h3>
<p><img src="/cd11aadea1ccfadf5aaf5e72e966634f/20250718-Steam%E5%90%88%E6%A0%BC%E7%94%BB%E5%83%8F_3_%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%A2%E3%82%BB%E3%83%83%E3%83%88.jpg" alt=""></p>
<table>
<thead>
<tr>
<th>画像タイプ</th>
<th>サイズ（px）</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>ライブラリカプセル</strong></td>
<td>600×900</td>
<td>ライブラリのグリッド表示</td>
</tr>
<tr>
<td><strong>ライブラリヘッダー</strong></td>
<td>920×430</td>
<td>ライブラリ詳細ページ上部</td>
</tr>
<tr>
<td><strong>ライブラリヒーロー画像</strong></td>
<td>3840×1240</td>
<td>ライブラリの大型バナー</td>
</tr>
<tr>
<td><strong>ライブラリのロゴ</strong></td>
<td>1280×720</td>
<td>ゲームのロゴ表示</td>
</tr>
</tbody>
</table>
<h3>その他</h3>
<ul>
<li><strong>コミュニティアイコン</strong>: 184×184</li>
</ul>
<p>:::ad</p>
<h2>デザインの注意点 {#design-cautions}</h2>
<p>画像サイズと同じく、デザインについても拒絶理由が存在するので注意しましょう。</p>
<h3>⚠ 注意点A：「ロゴ以外の文字を乗せるな！」</h3>
<p><img src="/4fe719516132cf757962b532d8665a57/20250718-Steam%E5%90%88%E6%A0%BC%E7%94%BB%E5%83%8F_4_%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E6%B3%A8%E6%84%8F%E7%82%B9A.jpg" alt=""></p>
<p>実際にデザインをしていると、良かれと思ってテキストを追加したくなりますが、ロゴ以外の文字があると不合格になるので注意してください。下記のような小さな文字でもNGです。</p>
<p>マーケティング用コピーや引用など、ロゴ以外の文字情報を追加すると不合格になります。</p>
<h3>⚠ 注意点B：「とにかく視認性を良く！」</h3>
<p><img src="/e37d938e0f85f36d17e235e64d55676e/20250718-Steam%E3%82%B9%E3%83%88%E3%82%A2%E7%99%BB%E9%8C%B2_5_%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E6%B3%A8%E6%84%8F%E7%82%B9B.jpg" alt=""></p>
<p>上記の場合は「アプリの操作イメージを伝えたい」という思いからUIのスクショなどを組み込んでデザインしましたが不合格になりました。UI画像を削除して要素を大きく表示したら合格しました。</p>
<p>確かに客観的に見ても修正後のほうが視認性が良いですよね。サムネイルであることを意識しながらデザインするのが大切です。</p>
<h2>効率的な制作のコツ {#conclusion}</h2>
<p>今回の注意点を参考にしてもらえれば、Steamのストア情報もスムーズに登録できるはずです。</p>
<p>Steamサポートチームはかなり具体的に修正内容を通知してくれるので助かりました。画像サイズについて説明する記事はいくつもありますが、NGデザインについてはほぼ情報がなかったので、参考にしていただけると幸いです。</p>
<p>最初はヘッダーカプセル（920×430）あたりからデザインを始めて、完成したら別名保存をして、キャンバスサイズを変更して、要素の位置だけ整えて別名保存して、を繰り返せば、位置を変えるだけで複数のパターンを効率的に制作できます。</p>
<p>Steamストア申請で面倒なのは、SteamWorksアカウント取得時の税務情報の登録と、ストア画像の作成だと思うので、まずはここを乗り越えてゲームをリリースしましょう。</p>
<p>また、Steam販売に際してローカライズしたほうがよい言語については、<a href="/ja/blog/gamedev/steam-game-localization-language/">以前の記事</a>で解説しているので、そちらも御覧ください。</p>
<p>それと<a href="https://store.steampowered.com/app/3849460/AnimSprite_Pixelizer__Convert_Handdrawn_Animations_to_Pixel_Art/">AnimSprite Pixelizer</a>もヨロシクネ。自分で絵も描く2Dゲーム開発者の人にオススメです。</p>
<p>:::post-link{url="/ja/product/animsprite-pixelizer/" text="AnimSprite Pixelizer"}</p>
<p>:::ad</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/cd11aadea1ccfadf5aaf5e72e966634f/20250718-Steam%E5%90%88%E6%A0%BC%E7%94%BB%E5%83%8F_3_%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%A2%E3%82%BB%E3%83%83%E3%83%88.jpg" medium="image"/></item><item><title><![CDATA[【Steam】多言語化するならどの言語？ローカライズ優先度を統計から考えた【2025年版】]]></title><description><![CDATA[Steam公式の統計データから、個人ゲーム開発者が優先すべきローカライズ言語を分析。英語と簡体中国語で63%のユーザーをカバーできます。]]></description><link>https://uhiyama-lab.com/ja/blog/gamedev/steam-game-localization-language/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/gamedev/steam-game-localization-language/</guid><category><![CDATA[marketing]]></category><pubDate>Fri, 04 Jul 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p><img src="./images/movie_convert.webp" alt=""></p>
<p>先日、Boothとitch.ioとSteamにて、<a href="/ja/product/animsprite-pixelizer/">AnimSprite Pixelizer</a>という2Dゲーム開発効率化ツールをリリースしました。CLIP Studioなどで作画したキャラ歩行アニメーション等を、共通のピクセルサイズで一括変換してスプライトシート書き出しができるアプリです。</p>
<p><img src="/1c3e4c916282fad3c7d881d149e604b0/thumb-animsprite-pixelizer.jpg" alt=""></p>
<p>アプリが完成したあと、せっかくなので多言語化対応にも挑戦してみました。</p>
<p>ここで問題になるのが「どの言語にローカライズすべきか？」という点です。この記事では、個人・小規模でゲームを開発している方に向けて、どの言語を優先的に対応すべきかを、Steamが毎月公開している「<a href="https://store.steampowered.com/hwsurvey/">Steam ハードウェア&#x26;ソフトウェア調査</a>」のデータから分析していきます。</p>
<p>:::post-link{url="/ja/product/animsprite-pixelizer/" text="AnimSprite Pixelizer"}</p>
<p>:::post-link{url="<a href="https://store.steampowered.com/hwsurvey/">https://store.steampowered.com/hwsurvey/</a>" text="Steam ハードウェア&#x26;ソフトウェア調査"}</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#steam-language-data">Steam言語別データの最新状況</a></li>
<li><a href="#top-languages">トップ10言語と市場動向</a></li>
<li><a href="#localization-strategy">ローカライズの優先順位</a></li>
<li><a href="#practical-tips">実装で気をつけるポイント</a></li>
<li><a href="#conclusion">多言語化を前提とした設計</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>Steam言語別データの最新状況 {#steam-language-data}</h2>
<p>Steam公式の「Hardware &#x26; Software Survey」から得られた最新データによると、現在のSteam利用者の言語分布は以下のようになっています。任意参加の調査ですが、Steamにおける最も信頼性の高い公式統計です。</p>
<p><img src="/d798e490248249208ce115800b86d8e4/20250704-1-Steam202506%E8%A8%80%E8%AA%9E%E5%88%A5%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E6%95%B0.png" alt=""></p>
<p><em>Steamハードウェア＆ソフトウェア 調査: June 2025</em></p>
<h2>トップ10言語と市場動向 {#top-languages}</h2>
<p>現在のSteam言語別利用者ランキングは以下のとおりです：</p>
<table>
<thead>
<tr>
<th>順位</th>
<th>言語</th>
<th>利用者割合</th>
<th>前回比</th>
<th>傾向</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>1位</strong></td>
<td><strong>英語</strong></td>
<td><strong>36.31%</strong></td>
<td>-1.93%</td>
<td>📉 減少</td>
</tr>
<tr>
<td><strong>2位</strong></td>
<td><strong>簡体中国語</strong></td>
<td><strong>26.73%</strong></td>
<td>+2.61%</td>
<td>📈 大幅増加</td>
</tr>
<tr>
<td><strong>3位</strong></td>
<td><strong>ロシア語</strong></td>
<td>9.46%</td>
<td>+0.43%</td>
<td>📈 増加</td>
</tr>
<tr>
<td>4位</td>
<td>スペイン語（スペイン）</td>
<td>4.34%</td>
<td>-0.40%</td>
<td>📉 減少</td>
</tr>
<tr>
<td>5位</td>
<td>ポルトガル語（ブラジル）</td>
<td>3.87%</td>
<td>-0.35%</td>
<td>📉 減少</td>
</tr>
<tr>
<td>6位</td>
<td>ドイツ語</td>
<td>2.86%</td>
<td>-0.19%</td>
<td>📉 減少</td>
</tr>
<tr>
<td>7位</td>
<td>日本語</td>
<td>2.59%</td>
<td>-0.10%</td>
<td>📉 微減</td>
</tr>
<tr>
<td>8位</td>
<td>フランス語</td>
<td>2.33%</td>
<td>-0.13%</td>
<td>📉 減少</td>
</tr>
<tr>
<td>9位</td>
<td>ポーランド語</td>
<td>1.68%</td>
<td>-0.09%</td>
<td>📉 微減</td>
</tr>
<tr>
<td>10位</td>
<td>韓国語</td>
<td>1.48%</td>
<td>+0.27%</td>
<td>📈 増加</td>
</tr>
</tbody>
</table>
<h3>注目すべき市場トレンド</h3>
<p><strong>📈 成長している言語市場：</strong></p>
<ul>
<li><strong>簡体中国語</strong>：+2.61%（最大の成長率）。中国国内のPCゲーム市場の成熟が背景にあると考えられます。</li>
<li><strong>ロシア語</strong>：+0.43%。インディーゲームとの親和性が高いとされる市場です。</li>
<li><strong>韓国語</strong>：+0.27%。熱心なゲームコミュニティを持つ市場です。</li>
<li><strong>繁体中国語</strong>：+0.07%</li>
<li><strong>タイ語</strong>：+0.07%</li>
</ul>
<p><strong>📉 減少している言語市場：</strong></p>
<ul>
<li><strong>英語</strong>：-1.93%（最大の減少率）</li>
<li><strong>スペイン語（スペイン）</strong>：-0.40%</li>
<li><strong>ポルトガル語（ブラジル）</strong>：-0.35%</li>
<li><strong>ドイツ語</strong>：-0.19%</li>
</ul>
<p>日本語ユーザー比率はわずか2.59%。世界の広さを感じますね。</p>
<p>最も注目すべきは、英語と簡体中国語で全体の63%以上のユーザーをカバーできるという点です。ローカライズをするなら、この2言語への対応は必須級と言えるでしょう。</p>
<p>特に簡体中国語ユーザーは近年急増しており、「黒神話: 悟空」が大きな話題となった2024年8月の調査では、簡体中国語が英語を抜いて1位になるという逆転現象も発生しました。</p>
<div class="post-link">
<a href="https://www.gamespark.jp/article/2024/09/03/144711.html" target="_blank">参考記事:Steam言語別ユーザー数で「中国語」がトップに。中国発の高評価ACT『黒神話：悟空』が影響か (2024/09/03) – GameSpark</a>
</div>
<p>:::ad</p>
<h2>ローカライズの優先順位 {#localization-strategy}</h2>
<p>限られた開発リソースで、どの言語を優先すべきかを考えてみましょう。</p>
<p><img src="/782663fde6bd4660106dc5177a9ea33e/20250704-2-Steam202506%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%A9%E3%82%A4%E3%82%BA%E5%84%AA%E5%85%88%E5%BA%A6.png" alt=""></p>
<h3>Tier別優先度の考え方</h3>
<h4>🥇 Tier 1（必須レベル）</h4>
<ul>
<li><strong>英語</strong>（36.31%）- 依然として最大のユーザーベース、グローバル標準言語</li>
<li><strong>簡体中国語</strong>（26.73%）- 急成長中、将来性が極めて高い</li>
</ul>
<p>この2言語だけで既に63%以上のユーザーをカバーできます。小規模チームや個人開発者は、まずこの2つに集中するのが最も効率的です。</p>
<h4>🥈 Tier 2（高優先）</h4>
<ul>
<li><strong>ロシア語</strong>（9.46%）- 安定した成長傾向、大きなユーザーベース</li>
<li><strong>スペイン語（スペイン）</strong>（4.34%）- 減少傾向ですが依然として大きな市場。また、ラテンアメリカの広大なスペイン語圏ユーザーにもリーチできる可能性があります。</li>
<li><strong>ポルトガル語（ブラジル）</strong>（3.87%）- 南米の重要市場</li>
</ul>
<h4>🥉 Tier 3（中優先）</h4>
<ul>
<li><strong>ドイツ語</strong>（2.86%）- 欧州の主要市場</li>
<li><strong>日本語</strong>（2.59%）- 高い購買力、品質を重視する市場</li>
<li><strong>フランス語</strong>（2.33%）- 欧州・カナダ市場</li>
</ul>
<h4>🌟 Tier 4（新興市場・将来性）</h4>
<ul>
<li><strong>韓国語</strong>（1.48%）- 成長中（+0.27%）、ゲーム文化が発達</li>
<li><strong>繁体中国語</strong>（1.39%）- 台湾・香港市場</li>
<li><strong>タイ語</strong>（0.88%）- 東南アジアの成長市場</li>
</ul>
<h3>段階的なローカライズ戦略</h3>
<p>日本の個人開発者がローカライズを進める場合、下記のような段階的アプローチが現実的でしょう。</p>
<ol>
<li><strong>Phase 1</strong>：日本語・英語対応（開発者の母語＋グローバル標準）</li>
<li><strong>Phase 2</strong>：簡体中国語を追加（最大の成長市場を狙う）</li>
<li><strong>Phase 3</strong>：ロシア語、韓国語を追加（成長中の主要市場を押さえる）</li>
<li><strong>Phase 4</strong>：その他のTier 2-3言語（スペイン語、ポルトガル語など）を順次追加</li>
</ol>
<h2>実装で気をつけるポイント {#practical-tips}</h2>
<h3>フォント対応</h3>
<p>特に中国語、韓国語、日本語、タイ語などは、それぞれ専用のフォントが必要です。Webフォントやシステムフォントで対応できるか、あるいはフォントファイルを同梱する必要があるかを事前に確認しましょう。</p>
<h3>文字数の変動</h3>
<p>言語によってテキストの長さは大きく変わります。UIデザインはこれらの変動に対応できるよう、柔軟性を持たせることが重要です。</p>
<ul>
<li><strong>短くなる傾向</strong>：日本語、中国語、韓国語（表意文字）</li>
<li><strong>長くなる傾向</strong>：ドイツ語、ロシア語（複合語や格変化）</li>
</ul>
<p>ボタンやテキストボックスがはみ出さないよう、あらかじめ長めのテキストでテストしておくと安心です。</p>
<h3>ドキュメントの整備（FAQ）</h3>
<p>多言語化するということは、様々な言語でお問い合わせが届く可能性があるということです。すべての質問に個別対応するのは大変な労力です。あらかじめ想定される質問とその回答（Q&#x26;A）をドキュメントとしてまとめておき、対応言語に翻訳して公開しておきましょう。これにより、ユーザーは自己解決でき、開発者のサポート負担を大幅に軽減できます。</p>
<h3>右から左へ記述する言語（RTL）</h3>
<p>アラビア語やヘブライ語など、右から左へ記述する言語（RTL: Right-to-Left）に対応する場合、UI全体のレイアウト反転が必要になることもあります。Tier上位ではありませんが、将来的に対応する可能性があれば念頭に置いておくと良いでしょう。</p>
<h2>多言語化を前提とした設計 {#conclusion}</h2>
<p>今回の調査で、理想的にはランキング上位の10言語に対応できれば、大半のSteamユーザーにリーチできることがわかりました。</p>
<p>昨今はChatGPT、Claude、Geminiといった生成AIの活用により、非常に精度の高い翻訳が手軽に可能になっています。UIやシステムテキストであれば、AI翻訳だけでもかなりの品質が期待できるでしょう。もちろん、ストーリーやキャラクターの会話など、ゲームの没入感を左右する重要なテキストは、プロの翻訳家やネイティブスピーカーによるチェックが望ましいです。</p>
<p>重要なのは、開発の初期段階から多言語化を前提とした設計を心掛けることです。</p>
<p>テキストをプログラム内に直接書き込む（ハードコードする）のではなく、言語ごとのCSVファイルやJSONファイルから読み込む形式にしておけば、後から対応言語を増やすのが非常に容易になります。</p>
<p>フォントの準備や、文字数増減によるレイアウト崩れを防ぐUI設計など、事前に考慮すべき点はありますが、実装面のハードルはAIの進化によって着実に下がってきています。グローバル市場に挑戦するために、ぜひ多言語化を検討してみてください。</p>
<p>:::ad</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/d798e490248249208ce115800b86d8e4/20250704-1-Steam202506%E8%A8%80%E8%AA%9E%E5%88%A5%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E6%95%B0.png" medium="image"/></item><item><title><![CDATA[【Godot】これからGodotを始める2Dゲーム開発者が知っておくべき13の重要機能とオススメ教材]]></title><description><![CDATA[Godotで2Dゲームを作る初心者が知るべき重要機能を解説。ポーズ制御やノックバック、オートタイルや組み込み関数など重要ポイントを抜粋。]]></description><link>https://uhiyama-lab.com/ja/blog/gamedev/godot-2d-game-practical-techniques/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/gamedev/godot-2d-game-practical-techniques/</guid><category><![CDATA[godot]]></category><category><![CDATA[learning]]></category><pubDate>Fri, 20 Jun 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p><img src="./images/20250613_02_Godot-Udemy%E3%82%B3%E3%83%BC%E3%82%B902.webp" alt=""></p>
<p><a href="/ja/blog/gamedev/godot-for-unity-developer/">前回</a>の記事ではGodotの基礎学習についてまとめました。今回はその続編として、Udemyの「<a href="https://trk.udemy.com/Oex4on">Godot4: Build a 2D Action-Adventure Game</a>」を完走し、実践的な2Dアクションアドベンチャーゲームの開発に挑戦しました。</p>
<p><img src="/48132ef7a853ca7593bf0e7303d67155/20250620-1-Godot4-Build-a-2D-Action-Adventure-Game-%E4%BF%AE%E4%BA%86%E8%A8%BC%E6%9B%B8.png" alt=""></p>
<p>基本的なプレイヤー操作から、NPCとの対話、パズル、戦闘システムまで体系的に網羅されたコースです。この記事では、コースを通して学んだGodotの重要機能や概念を整理して共有します。</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#course-overview">コース概要</a></li>
<li><a href="#godot-deep-dive">Godotの重要機能・概念ノート</a>
<ul>
<li><a href="#process-modes">Process Mode：ポーズ機能の制御</a></li>
<li><a href="#motion-modes">CharacterBody2DのMotion Mode</a></li>
<li><a href="#input-map">InputMap：キーバインド管理</a></li>
<li><a href="#input-processing">入力処理の使い分け</a></li>
<li><a href="#movement-functions">move_and_slideとmove_toward</a></li>
<li><a href="#groups">グループ：柔軟なオブジェクト判定</a></li>
<li><a href="#collision-system">Collision Layers/Masks</a></li>
<li><a href="#terrains">Terrains：オートタイル作成</a></li>
<li><a href="#marker2d">Marker2D：管理ノードの定番</a></li>
<li><a href="#scene-inheritance">Editable ChildrenとScene継承</a></li>
<li><a href="#autoload">Autoload：シーンをまたぐデータ管理</a></li>
<li><a href="#visual-effects">modulateによるホワイトフラッシュ</a></li>
<li><a href="#animation-systems">AnimatedSprite2D vs AnimationPlayer</a></li>
</ul>
</li>
<li><a href="#conclusion">まとめ</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>コース概要 {#course-overview}</h2>
<p><img src="/6c7c7b963cc6743257b455a2face5022/20250620-2-Godot4-Build-a-2D-Action-Adventure-Game.png" alt=""></p>
<p>「<a href="https://trk.udemy.com/Oex4on">Godot4: Build a 2D Action-Adventure Game</a>」は、2Dアクションアドベンチャーゲームをゼロから作り上げる英語コースです。開発を進めるなかで、こんな疑問が自然に解消されていきます。</p>
<ul>
<li>エリア間の移動はどう実装する？</li>
<li>オブジェクトを押す仕組みは？</li>
<li>開封済み宝箱の状態をどう保持する？</li>
<li>NPCとの会話をどう管理する？</li>
<li>複数スイッチで開く扉の実装方法は？</li>
<li>被ダメ時のホワイトフラッシュはどうやる？</li>
</ul>
<p>具体的なカリキュラムとしては、プレイヤーの8方向移動、Terrainsによるオートタイル環境構築、Y-Sortによる重なり順制御、RigidBody2Dの物理パズル、ダイアログシステム、Autoloadによるデータ永続化、敵AI＋ノックバック付き戦闘システムなどを一通りカバーしています。</p>
<p>前回紹介した「<a href="https://trk.udemy.com/nXQoM9">Godot Engineで気軽に2Dゲームを作ろう</a>」より難易度は上がりますが、「ゲーム開発で本当に必要な知識」が詰まった良コースです。Udemyは頻繁にセールがあるので、お気に入り登録して狙うのがおすすめです。</p>
<p>:::post-link{url="<a href="https://trk.udemy.com/Oex4on">https://trk.udemy.com/Oex4on</a>" text="Godot4: Build a 2D Action-Adventure Game (Udemy)"}</p>
<p>:::ad</p>
<h2>Godotの重要機能・概念ノート {#godot-deep-dive}</h2>
<p>ここからは、コースで特に重要だと感じた機能を深堀りします。</p>
<h3>Process Mode：ポーズ機能の制御 {#process-modes}</h3>
<p><img src="/1ec4a7ad68f1d39a7e2065226dd31075/20250620-3-Godot-Node-Process.png" alt=""></p>
<p>「NPCと会話中は背景の敵やプレイヤーは止めたいが、ダイアログの操作は続けたい」――ゲーム開発でよくあるこの要件を、Godotでは各ノードのProcess Modeで解決します。</p>
<ul>
<li><strong>Pausable（デフォルト）</strong>: <code>get_tree().paused = true</code>で停止する。プレイヤーや敵など、ゲーム世界のオブジェクト向け。</li>
<li><strong>Always</strong>: ポーズを無視して常に動作。会話中のNPCやUI、BGMに使う。</li>
<li><strong>When Paused</strong>: ポーズ中だけ動作。ポーズメニュー専用UIに最適。</li>
</ul>
<p>Unityでは<code>Time.timeScale = 0</code>でゲーム全体を止めつつ、個別のスクリプトで<code>Time.unscaledDeltaTime</code>を使い分ける必要がありましたが、Godotではノード単位でProcess Modeを設定するだけ。非常にスマートです。</p>
<p>実装例：ダイアログ表示中にゲームをポーズする</p>
<p><img src="/a52d52be285993f1653fe23fcc4ccd51/20250620-4-Godot-Node-Process-DIalogue_1.webp" alt=""></p>
<p>※会話中はシーンが停止し、会話終了と同時に解除される</p>
<pre><code class="language-gdscript"># NPC.gd
# このNPCノードのProcess Modeをインスペクターで "Always" に設定しておく

func _process(delta):
    if Input.is_action_just_pressed("interact") and can_talk:
        if is_dialog_active():
            close_dialog()
            get_tree().paused = false
        else:
            open_dialog()
            get_tree().paused = true
</code></pre>
<p>NPC自身は止めずにゲーム全体をポーズすることで、ダイアログの開閉を安全に処理できます。</p>
<p>:::ad</p>
<h3>CharacterBody2DのMotion Mode {#motion-modes}</h3>
<p><img src="/89348029df3ef2a43cd3e8d383636730/20250620-5-CharacterBody2D-MotionMode.png" alt=""></p>
<p><code>CharacterBody2D</code>にはMotion Modeという設定があり、ゲームのジャンルに応じた物理挙動を切り替えられます。プロジェクト開始時に必ず確認すべき項目です。</p>
<ul>
<li><strong>Grounded（デフォルト）</strong>: 重力が適用され、<code>is_on_floor()</code>が機能する。プラットフォーマーや横スクロールアクション向け。</li>
<li><strong>Floating</strong>: 重力なし、床の概念もなし。トップダウンのアクションゲームやシューティング向け。</li>
</ul>
<pre><code class="language-gdscript"># Player.gd (Motion Modeを "Floating" に設定)
extends CharacterBody2D

@export var speed: float = 200.0

func _physics_process(delta):
    var direction = Input.get_vector("move_left", "move_right", "move_up", "move_down")
    velocity = direction * speed
    move_and_slide()
</code></pre>
<p>設定を間違えると意図しない重力が発生したり、床判定がおかしくなるので注意。</p>
<p>:::ad</p>
<h3>InputMap：キーバインド管理 {#input-map}</h3>
<p><img src="/7a26659119f2bcd4e9c57ef57d2607e0/20250620-6-InputMap.png" alt="Godot InputMap"></p>
<p>Unityでは<code>Input.GetKey(KeyCode.A)</code>のようにキーを直接指定しがちですが、GodotのInput Map（プロジェクト設定 → Input Map）では「アクション名」を定義してキーを紐づけます。コードでは<code>"move_left"</code>のような抽象名で入力を扱うため、可読性が高く、キーコンフィグの実装も容易です。</p>
<pre><code class="language-gdscript">func _process(delta):
    # 単発の入力（押した瞬間）
    if Input.is_action_just_pressed("interact"):
        open_chest()

    # 継続的な入力（押し続けている間）
    if Input.is_action_pressed("dash"):
        speed = DASH_SPEED
    else:
        speed = NORMAL_SPEED

    # 4方向を正規化されたVector2で取得（非常に便利）
    var direction = Input.get_vector("move_left", "move_right", "move_up", "move_down")
    velocity = direction * speed
    move_and_slide()
</code></pre>
<p><code>Input.get_vector()</code>は4アクションから正規化された<code>Vector2</code>を返してくれるため、トップダウンの移動処理を1行で書けます。</p>
<p>:::ad</p>
<h3>入力処理の使い分け {#input-processing}</h3>
<p>InputMapでアクションを定義したら、入力の「状態」に応じて適切なメソッドを使い分けます。</p>
<p>:::post-link{url="<a href="https://docs.godotengine.org/ja/4.x/classes/class_input.html">https://docs.godotengine.org/ja/4.x/classes/class_input.html</a>" text="Inputについて (Godot公式ドキュメント)"}</p>
<table>
<thead>
<tr>
<th>用途</th>
<th>メソッド</th>
<th>具体例</th>
</tr>
</thead>
<tbody>
<tr>
<td>押した瞬間の1回限り</td>
<td><code>is_action_just_pressed()</code></td>
<td>ジャンプ、攻撃、メニュー開閉</td>
</tr>
<tr>
<td>押している間ずっと</td>
<td><code>is_action_pressed()</code></td>
<td>移動、ダッシュ、チャージ</td>
</tr>
<tr>
<td>離した瞬間</td>
<td><code>is_action_just_released()</code></td>
<td>チャージ攻撃の発動</td>
</tr>
<tr>
<td>アナログ入力（0.0〜1.0）</td>
<td><code>get_action_strength()</code></td>
<td>ゲームパッドのトリガー</td>
</tr>
</tbody>
</table>
<pre><code class="language-gdscript">func _process(delta):
    # 1回限りのアクション
    if Input.is_action_just_pressed("jump"):
        if is_on_floor():
            velocity.y = JUMP_VELOCITY

    if Input.is_action_just_pressed("attack"):
        perform_attack()

    # 継続的なアクション
    if Input.is_action_pressed("dash"):
        current_speed = dash_speed
    else:
        current_speed = normal_speed

    # チャージ系：押している間ためて、離したら発動
    if Input.is_action_pressed("charge"):
        charge_power += charge_rate * delta
        charge_power = min(charge_power, max_charge)

    if Input.is_action_just_released("charge"):
        fire_charged_shot(charge_power)
        charge_power = 0.0
</code></pre>
<p>よくある間違いとして、<code>is_action_pressed()</code>で射撃を処理すると毎フレーム弾が発射されてしまいます。単発アクションには必ず<code>is_action_just_pressed()</code>を使いましょう。</p>
<p>:::ad</p>
<h3>move_and_slideとmove_toward {#movement-functions}</h3>
<p><img src="/1ed07252c30fcaa6b53d8159476479d7/20250620-7-Godot-Knockback-Move_toward_1.webp" alt="ノックバックにはmove_toward"></p>
<p>Godotの移動処理で中心となる2つの関数です。</p>
<ul>
<li><strong><code>move_and_slide()</code></strong>: <code>CharacterBody2D</code>の主力。現在の<code>velocity</code>に基づいて移動し、壁や床との衝突を自動処理してくれる。</li>
<li><strong><code>move_toward(target, delta)</code></strong>: 現在の値を目標値に向かって少しずつ変化させる。滑らかな加速・減速に使う。</li>
</ul>
<p>この2つの使い分けが重要になるのが、ノックバック実装の場面です。コースで実際にぶつかった問題を紹介します。</p>
<p><code>velocity</code>を直接代入する方式だと、ノックバック中にプレイヤーが移動入力をした瞬間、ノックバック効果が一瞬で消えてしまいます。<code>move_toward</code>で段階的に速度を変化させることで、ノックバックが自然に減衰しながら通常移動に戻る挙動を実現できます。</p>
<pre><code class="language-gdscript"># 直接代入だとノックバックが即消える
func move_player():
    var move_vector = Input.get_vector("move_left", "move_right", "move_up", "move_down")
    velocity = move_vector * move_speed  # ノックバックの速度が即上書きされる

# move_towardで段階的に変化させる
@export var acceleration: float = 500.0

func move_player():
    var move_vector = Input.get_vector("move_left", "move_right", "move_up", "move_down")
    var target_velocity = move_vector * move_speed
    velocity = velocity.move_toward(target_velocity, acceleration * delta)

# ノックバック処理
func apply_knockback(direction: Vector2, strength: float):
    velocity += direction * strength
</code></pre>
<p><code>move_toward</code>の第2引数は「1フレームで目標にどれだけ近づくか」を指定します。<code>acceleration * delta</code>の形で使うことで、フレームレートに依存しない滑らかな遷移が得られます。</p>
<p>:::ad</p>
<h3>グループ：柔軟なオブジェクト判定 {#groups}</h3>
<p><img src="/0cda0501e8b92a2c6b1bbb08f1aff61c/20250620-8-Node-Groups.webp" alt=""></p>
<p>「攻撃が当たったオブジェクトが敵かどうか判定したい」――この要件に対して、Godotではグループを使います。</p>
<p>UnityのTagシステムに近い概念ですが、重要な違いがあります。Unityでは1つのGameObjectに1つのTagしか設定できませんでした。Godotのグループは複数設定可能なので、「このオブジェクトは "enemies" であり "damageable" でもある」といった柔軟な分類ができます。</p>
<p>設定方法はシンプルで、ノードのインスペクター → Nodeタブ → Groupsからグループ名を追加するだけ。コードでは<code>is_in_group()</code>で判定します。</p>
<pre><code class="language-gdscript"># Playerの攻撃判定用Area2Dに接続
func _on_sword_area_body_entered(body: Node2D):
    if body.is_in_group("enemies"):
        body.take_damage(attack_power)
    elif body.is_in_group("pushable"):
        pass  # 押せるオブジェクトの処理
</code></pre>
<p>:::ad</p>
<h3>Collision Layers/Masks {#collision-system}</h3>
<p><img src="/570771fe74938039b9d63decbef52c05/20250620-9-CollisionLayer.png" alt=""></p>
<p>衝突判定をきちんと整理しないと、「プレイヤーの剣が味方にも当たる」「敵同士が引っかかる」といった問題が起きます。GodotのCollision Layers/Masksは、これを防ぐ仕組みです。</p>
<ul>
<li><strong>Layer</strong>: そのオブジェクトが「どの層に存在するか」</li>
<li><strong>Mask</strong>: そのオブジェクトが「どの層と衝突判定を行うか」</li>
</ul>
<p>たとえば、Player（Layer 1）、Enemies（Layer 2）、Weapons（Layer 3）と分けた場合：</p>
<table>
<thead>
<tr>
<th>オブジェクト</th>
<th>Layer</th>
<th>Mask</th>
<th>理由</th>
</tr>
</thead>
<tbody>
<tr>
<td>プレイヤー</td>
<td>1</td>
<td>2</td>
<td>敵に触れてダメージを受ける</td>
</tr>
<tr>
<td>敵</td>
<td>2</td>
<td>1</td>
<td>プレイヤーのみと接触（敵同士はすり抜け）</td>
</tr>
<tr>
<td>プレイヤーの武器</td>
<td>3</td>
<td>2</td>
<td>敵のみを攻撃（プレイヤーとは衝突しない）</td>
</tr>
</tbody>
</table>
<pre><code class="language-gdscript"># 武器のMaskで敵のみ検出するため、この関数には敵だけが入ってくる
func _on_weapon_area_body_entered(body):
    if body.is_in_group("enemies"):
        body.take_damage(attack_power)
</code></pre>
<p><img src="/12990fb13dc09ab98291a9dadccd21d6/20250620-10-CollisionLayerName.png" alt=""></p>
<p>「プロジェクト設定」→「Layer Names」で各レイヤーに名前を付けておくと、インスペクターでの設定が格段に分かりやすくなります。</p>
<p>:::ad</p>
<h3>Terrains：オートタイル作成 {#terrains}</h3>
<p><img src="/0b310fe96d946acd6caf5e2aa8a84265/20250620-11-Terrains%E6%A9%9F%E8%83%BD.webp" alt=""></p>
<p>GodotのTerrains機能は、いわゆるオートタイルを驚くほど簡単に作れるシステムです。UnityでRule Tileを使ったことがある人なら、その手軽さに感動するはずです。</p>
<p><img src="/c66f8a6a0460fe10e05f2dc1fc60b779/20250620-12-Terrains-Paint.png" alt=""></p>
<p><code>TileSet</code>リソース内の「Terrains」タブで、タイルの各辺がどの地形タイプに接するかを視覚的にペイントするだけ。<code>TileMap</code>エディタでブラシツールを使えば、境界線を自動判定して適切なタイルを配置してくれます。</p>
<p>便利機能として、ランダムブラシ（ダイスアイコン）で複数タイルからランダム選択、確率制御で特定タイルの出現頻度調整、「F」キーで全タイルに衝突判定を一括適用などがあります。</p>
<p>:::ad</p>
<h3>Marker2D：管理ノードの定番 {#marker2d}</h3>
<p>Unityでは空のGameObjectにスクリプトをアタッチして「マネージャー」を作るのが定番でしたが、Godotでは<strong>Marker2D</strong>がその役割を担います。</p>
<p>Marker2Dは位置情報（Transform）だけを持つ最も軽量な2Dノードです。レンダリングも物理演算もないため、GameManagerやPuzzleManagerのようなシーン管理スクリプトの置き場所として最適です。Unityの空GameObjectと違い、エディタ上で十字マーカーが表示されるので視認性も良好です。</p>
<p>:::ad</p>
<h3>Editable ChildrenとScene継承 {#scene-inheritance}</h3>
<p>ベースシーンからバリエーションを作成する方法は2つあります。用途に応じた使い分けが重要です。</p>
<ul>
<li><strong>Editable Children</strong>: シーンに配置したインスタンスを右クリック→「Editable Children」で中身を直接編集。変更はその配置先にのみ反映される。見た目やセリフだけ違うモブNPCの量産に便利。</li>
<li><strong>Scene Inheritance</strong>: ベースシーン（BaseNPC.tscn）を継承して新しいシーン（Shopkeeper.tscn）を作成。親の機能を引き継ぎつつ独自機能を追加できる。「話す」＋「売買する」機能を持つ商人NPCなど、機能的に異なる派生種に最適。</li>
</ul>
<p><img src="/ed829aa20a87c40a9ab62aa2176b093f/20250620-13-EditableChildren.png" alt="EditableChildrenはノード表記が黄色になる"></p>
<table>
<thead>
<tr>
<th>特徴</th>
<th>Editable Children</th>
<th>Scene 継承</th>
</tr>
</thead>
<tbody>
<tr>
<td>向いているNPC</td>
<td>村人A、村人Bなど（見た目・セリフ違い）</td>
<td>商人、鍛冶屋など（独自機能持ち）</td>
</tr>
<tr>
<td>再利用性</td>
<td>低い（その場限り）</td>
<td>高い（継承シーンを各所に配置可能）</td>
</tr>
<tr>
<td>管理</td>
<td>シンプル（ベースシーンのみ）</td>
<td>体系的（機能ごとにファイルが分離）</td>
</tr>
</tbody>
</table>
<p>:::ad</p>
<h3>Autoload：シーンをまたぐデータ管理 {#autoload}</h3>
<p>「宝箱を開けてエリア移動して戻ってきたら、また閉じている」――シーン切替でデータが失われる問題は、Godotでは<strong>Autoload</strong>で解決します。Unityの<code>DontDestroyOnLoad</code>＋シングルトンに相当する機能です。</p>
<p>プロジェクト設定でスクリプトをAutoloadに登録すると、ゲーム起動時に自動読み込みされ、どのシーンからでもグローバルにアクセスできます。</p>
<p><img src="/aaef1d7db4fa7da8977180c4213f3d54/20250620-14-Autoload.png" alt=""></p>
<pre><code class="language-gdscript"># GameManager.gd (AutoLoadに登録)
extends Node

var opened_chests: Array[String] = []
var player_hp: int = 3
var player_spawn_position: Vector2
</code></pre>
<pre><code class="language-gdscript"># TreasureChest.gd
extends StaticBody2D

@export var chest_id: String  # "forest_chest_01"などユニークIDを設定

func _ready():
    if GameManager.opened_chests.has(chest_id):
        play_open_animation(false)

func open_chest():
    GameManager.opened_chests.append(chest_id)
    play_open_animation(true)
</code></pre>
<p>プレイヤーのHP、スコア、インベントリ、クエスト進捗など、シーンをまたいで維持したいデータはすべてAutoloadで管理できます。</p>
<p>:::ad</p>
<h3>modulateによるホワイトフラッシュ {#visual-effects}</h3>
<p><img src="/1ed07252c30fcaa6b53d8159476479d7/20250620-7-Godot-Knockback-Move_toward_1.webp" alt="ノックバックにはmove_toward"></p>
<p>ダメージを受けた瞬間に一瞬白く光る「ホワイトフラッシュ」は、プレイヤーへのフィードバックとして非常に効果的です。Godotではmodulateプロパティで簡単に実装できます。</p>
<p><code>modulate</code>はノードとその子孫の色に乗算されるカラー値で、デフォルトは白(1, 1, 1)。値を大きくすると明るく、小さくすると暗くなります。CharacterBody2Dのmodulateを変更すれば、子のAnimatedSprite2Dも自動で色が変わるため、個別操作は不要です。</p>
<pre><code class="language-gdscript"># Player.gd
func take_damage(amount):
    # ...ダメージ計算...
    flash_effect()

func flash_effect():
    modulate = Color(2, 2, 2)  # 白く光らせる
    await get_tree().create_timer(0.1).timeout  # 0.1秒待機
    modulate = Color(1, 1, 1)  # 元に戻す
</code></pre>
<p><code>await get_tree().create_timer(0.1).timeout</code>は、タイマーノードを追加せずに一時的な待機処理を1行で書ける便利な記法です。</p>
<p>:::ad</p>
<h3>AnimatedSprite2D vs AnimationPlayer {#animation-systems}</h3>
<p>Godotには主要な2Dアニメーションシステムが2つあり、用途で使い分けます。</p>
<h4>AnimatedSprite2D</h4>
<p><img src="/ba1b7e59a402de97b2d64a029c514cc9/20250620-15-AnimatedSprite2D.webp" alt=""></p>
<p>スプライトフレームアニメーション専用。歩行、攻撃、アイドルなど、スプライトシートの切り替えだけで完結するアニメーションに最適です。</p>
<pre><code class="language-gdscript">if velocity.x > 0:
    $AnimatedSprite2D.play("move_right")
elif velocity.x &#x3C; 0:
    $AnimatedSprite2D.play("move_left")
else:
    $AnimatedSprite2D.stop()
</code></pre>
<h4>AnimationPlayer</h4>
<p><img src="/aed1f930237a4bc64fe862e8674a7055/20250620-16-AnimationPlayer.webp" alt=""></p>
<p>位置、回転、スケール、任意のプロパティを同時制御できる汎用アニメーションシステム。UnityのAnimatorに近い存在で、剣を振る動作やUI演出、カメラワークなど複合的な制御に使います。</p>
<pre><code class="language-gdscript">func attack():
    var player_animation: String = $AnimatedSprite2D.animation
    if player_animation == "move_right":
        $AnimatedSprite2D.play("attack_right")
        $AnimationPlayer.play("attack_right")  # 剣の位置・角度を制御
</code></pre>
<h4>使い分けの指針</h4>
<table>
<thead>
<tr>
<th>アニメーション内容</th>
<th>推奨システム</th>
</tr>
</thead>
<tbody>
<tr>
<td>スプライトフレーム切り替えのみ</td>
<td>AnimatedSprite2D</td>
</tr>
<tr>
<td>位置・回転・スケール変更</td>
<td>AnimationPlayer</td>
</tr>
<tr>
<td>複数オブジェクトの同期</td>
<td>AnimationPlayer</td>
</tr>
<tr>
<td>複雑な状態遷移</td>
<td>AnimationTree</td>
</tr>
</tbody>
</table>
<p>実際の開発では、キャラクターの基本動作にAnimatedSprite2D、武器やエフェクトの動作にAnimationPlayerを組み合わせて使うのが一般的です。</p>
<p>:::ad</p>
<h2>まとめ {#conclusion}</h2>
<p>今回のコースを通して、Godotの設計思想の一貫性と2D開発における強さを改めて実感しました。Process Mode、Motion Mode、Collision Layersといった概念が統一的に設計されており、「やりたいこと」に対する解決策がストレートに用意されています。</p>
<p>特に、Terrains、Y-Sort、<code>move_and_slide()</code>など、2Dゲーム開発で「こういう機能が欲しかった」という部分が標準搭載されている点は大きな魅力です。Unity経験者にとって、Godotは学習コストの低さと開発の快適さを両立した有力な選択肢だと感じました。</p>
<p>:::post-link{url="<a href="https://trk.udemy.com/Oex4on">https://trk.udemy.com/Oex4on</a>" text="Godot4: Build a 2D Action-Adventure Game (Udemy)"}</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/59200aedc8d0c070a26cf5f9f88d6743/20250620-%E3%80%90Godot%E3%80%91%E3%81%93%E3%82%8C%E3%81%8B%E3%82%89Godot-Engine%E3%82%92%E5%A7%8B%E3%82%81%E3%82%8B2D%E3%82%B2%E3%83%BC%E3%83%A0%E9%96%8B%E7%99%BA%E8%80%85%E3%81%8C%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%8A%E3%81%8F%E3%81%B9%E3%81%8D13%E3%81%AE%E9%87%8D%E8%A6%81%E6%A9%9F%E8%83%BD%E3%81%A8%E3%82%AA%E3%82%B9%E3%82%B9%E3%83%A1%E6%95%99%E6%9D%90.png" medium="image"/></item><item><title><![CDATA[【Godot】これからGodotを学ぶUnity開発者のための対応表とオススメ教材]]></title><description><![CDATA[Unity開発者がGodot Engineに触れた学習記録。Unityとの対応表とおすすめ教材やエンジンの注目ポイントなどを紹介。]]></description><link>https://uhiyama-lab.com/ja/blog/gamedev/godot-for-unity-developer/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/gamedev/godot-for-unity-developer/</guid><category><![CDATA[godot]]></category><category><![CDATA[learning]]></category><pubDate>Fri, 13 Jun 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p><img src="/b98a8feef9781d72c3c987f8c64dd24d/20250613_01_Godot-%E3%83%87%E3%83%A2%E3%82%B2%E3%83%BC%E3%83%A0.webp" alt="">
※Godot公式が配布しているデモプロジェクト (<a href="https://docs.godotengine.org/ja/4.x/getting_started/first_2d_game/">最初の2Dゲーム</a>)</p>
<p>Godot Engineは、2014年にオープンソース化されたMITライセンスのゲーム開発エンジンです。2023年のUnity Runtime Fee騒動をきっかけに移行先として注目が集まり、知名度が一気に上がりました。</p>
<p><img src="/0d81a8243d740411ea7d495c318b51f0/20250613_02_Godot-Udemy%E3%82%B3%E3%83%BC%E3%82%B902.webp" alt="">
※学習中のUdemyコース「<a href="https://trk.udemy.com/55NY9j">Godot4: Build a 2D Action-Adventure Game</a>」</p>
<p>この記事では、Unity開発者がGodotを学ぶ際に役立つ情報をまとめています。Udemyコースの受講感想、Godotに触れて感じたこと、UnityとGodotの概念対応表、コードの書き方の違いなどを紹介します。</p>
<p>:::post-link{url="<a href="https://godotengine.org/ja/">https://godotengine.org/ja/</a>" text="Godot Engine 公式サイト"}</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#godot-overview">Godot Engineに対するQ&#x26;A</a></li>
<li><a href="#udemy-godot-course">Udemy講座を受講した感想</a></li>
<li><a href="#godot-impressions">Godotに触れて感動したこと</a></li>
<li><a href="#unity-developers-api">Unity開発者向け: Godotとの対応表</a></li>
<li><a href="#unity-developers-code">Unity開発者向け: コードで見る違い</a></li>
<li><a href="#conclusion">まとめ</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>Godot Engineに対するQ&#x26;A {#godot-overview}</h2>
<p><img src="/603933730c8f88496e1991167f729df8/20250613_03_Godot%E3%81%A3%E3%81%A6%E3%81%AA%E3%81%AB%EF%BC%9F.png" alt=""></p>
<p>Godotを学び始める前、私にはいくつもの疑問がありました。「そもそもGodotって何がすごいの？」「ノードベースって難しくない？」「GDScriptって覚える価値あるの？」――同じ疑問を持つ方も多いはず。ここでは、学習前の疑問に対して実際に触れた今の自分が答えます。</p>
<p><strong>Q. Godot Engineって？</strong>
2014年にオープンソース化されたゲーム開発エンジン。MITライセンスで完全無料、ロイヤリティもなし。エンジン本体はC++製。</p>
<p><strong>Q. UnityやUnreal Engineと比べた第一印象は？</strong>
とにかく軽い。v4.4.1時点でエンジン全体が約149MB。Unityのインストーラーを起動する間にGodotは開発を始められる。ただし日本語情報の少なさは覚悟が必要。</p>
<p><strong>Q. Godotで作られた有名ゲームって？</strong>
「Backpack Battles」「Backshot Roulette」「Unrailed 2: Back on Track」など。意外と知られたタイトルが並んでいる。<a href="https://godotengine.org/showcase/">公式サイトのShowcase</a>で一覧を確認でき、各年度のまとめ動画もおすすめ。</p>
<p><strong>Q. ノードベースってなに？</strong>
機能をノードの階層構造で構築する仕組み。たとえば物理判定のあるキャラクターなら、CharacterBody2Dノードの下にAnimatedSprite2D（アニメーション）とCollisionShape2D（衝突判定）を配置する。Unityでいうコンポーネントのアタッチに近い感覚なので、Unity経験者なら難しくない。</p>
<p><strong>Q. ノードベースって自由度低くない？</strong>
ノードを継承して独自クラスを作れるので、コーディングの自由度はUnityやUnreal Engineと変わらない。</p>
<p><strong>Q. GDScriptって何？</strong>
PythonベースのGodot独自言語。C#もサポートされているが、基礎学習ではGDScriptから始めるのが圧倒的に効率がいい。構文がシンプルで、エンジンとの統合も深い。</p>
<p>:::ad</p>
<h2>Udemy講座を受講した感想 {#udemy-godot-course}</h2>
<p>新しいエンジンを学ぶとき、私は公式ドキュメントに加えて動画チュートリアルを複数こなすようにしています。今回はUdemyの「<a href="https://trk.udemy.com/nXQoM9">Godot Engineで気軽に2Dゲームを作ろう</a>」を受講し、6.5時間ほどで修了しました。</p>
<p><img src="/4eaa4758da3b260a279877ac55228e14/20250613_04_Udemy%E4%BF%AE%E4%BA%86%E8%A8%BC%E6%9B%B8.jpg" alt=""></p>
<p>コースは基礎（プロジェクト設定、ノード・シーン理解、Windows/Web向けビルド）から始まり、後半ではオリジナル2Dゲームの開発に入ります。1動画1〜5分で要点を押さえてくれるため、テンポよく進められました。</p>
<p><img src="/fccd6ce2ee50d024dc4ccecbae4d9ca4/20250613_05_Godot-Engine%E3%81%A7%E6%B0%97%E8%BB%BD%E3%81%AB2D%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%9C%E3%82%8D%E3%81%86.webp" alt="">
※コースの手順で作成できるゲームの完成形</p>
<p>特に面白かったのは、穴掘り法アルゴリズムによる迷路のプロシージャル生成です。UI作成、データ保存、射撃システム、敵AIまで一通り実装し、タイトル→エリア選択→迷路→タイトルというゲームサイクルを完成させます。</p>
<p><img src="/5092f5b98ceb3bc1295b287484c5e3c3/20250613_06_%E7%A9%B4%E6%8E%98%E3%82%8A%E6%B3%95%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0.png" alt="">
※穴掘り法アルゴリズムの実行ログ</p>
<p>Godotの日本語コースはまだ少なく、ここまで整備されたカリキュラムは貴重です。まずこのコースで全体像をつかんでから、英語圏のより高度なコースに進むのがおすすめです。Udemyは頻繁にセールがあるので、お気に入りに登録しておくとよいでしょう。</p>
<p>学習中のTipsとして、Godotの独自仕様はChatGPTやClaudeに「Unity/Unreal Engineで例えると何に相当するか？」と聞きながら進めると理解が格段に早くなります。<code>@export</code> → <code>SerializeField</code>、<code>get_tree().change_scene_to_file()</code> → <code>SceneManager.LoadScene</code> のように既存の知識と紐づけるだけで、学習効率がまるで変わります。</p>
<p>:::post-link{url="<a href="https://trk.udemy.com/nXQoM9">https://trk.udemy.com/nXQoM9</a>" text="Godot Engineで気軽に2Dゲームを作ろう (Udemy)"}</p>
<p>:::ad</p>
<h2>Godotに触れて感動したこと {#godot-impressions}</h2>
<p><img src="/6ffdffa973bd1a65261aa229a54e4402/20250613_07_AnimatedSprte2D.png" alt=""></p>
<p>コースを通じてGodotの設計思想に触れ、率直に感動しました。Unity/Unreal Engineとは明確に異なるアプローチで、特に2D開発では「こういうのが欲しかった」と思う場面が何度もありました。</p>
<ul>
<li><strong>ノードとシグナルの直感性</strong>: Godotの核となる2つの概念。ノードで構造を作り、シグナルで通信する。この組み合わせに慣れると、UnityのGetComponent + UnityEventよりも流れるように実装できる感覚がある。</li>
<li><strong>軽量さが生む快適さ</strong>: Unityはプロジェクトを開くだけで数十秒〜数分かかることがあるが、Godotは.exeをクリックして数秒で開発再開。コンパイルもほぼ一瞬なので、「ちょっと試す」のハードルが劇的に低い。</li>
<li><strong>シーンの再利用性</strong>: 最初は「シーン = Prefab + Scene」という概念に戸惑ったが、慣れるとUnityと同じ感覚で使える。GameManager的なものはMarker2Dにスクリプトをアタッチしてシーン化し、AutoLoadでシングルトン化すればOK。</li>
<li><strong>2D専用機能の充実</strong>: TileMapやAnimatedSprite2Dなど、2D開発に特化したノードが最初から揃っている。Unityの2Dが「3Dエンジンの上に載せた2D」なのに対し、Godotの2Dはネイティブ。</li>
<li><strong>スプライトシート制作</strong>: Unityではスプライトのスライス→個別設定が必要だったが、Godotはグリッドアイコンから画像を選ぶだけでタイムラインに並ぶ。この手軽さは2Dゲーム開発者にとって大きい。</li>
<li><strong>タイルセット管理</strong>: 衝突判定を視覚的に設定でき、Physics Layerでタイルごとの当たり判定を効率的に管理できる。Unityのタイルマップ設定と比べて手順が少ない。</li>
</ul>
<p>:::ad</p>
<h2>Unity開発者向け: Godotとの対応表 {#unity-developers-api}</h2>
<p><img src="/0c97dc71e5e0f5c153e6c381222653b7/20250613_08-Godot-Unity%E5%AF%BE%E5%BF%9C%E8%A1%A8.png" alt=""></p>
<p>Unity開発者がGodotを学ぶとき、まず知りたいのは「Unityのあれは、Godotでは何にあたるのか？」でしょう。以下の対応表をざっと眺めてから学習を始めると、理解がスムーズになります。</p>
<table>
<thead>
<tr>
<th>Godot</th>
<th>Unity（近い概念）</th>
</tr>
</thead>
<tbody>
<tr>
<td>シーン (.tscn)</td>
<td>Prefab + Scene</td>
</tr>
<tr>
<td><code>get_tree().change_scene_to_file()</code></td>
<td><code>SceneManager.LoadScene()</code></td>
</tr>
<tr>
<td><code>$記法</code> / <code>get_node()</code></td>
<td><code>GameObject.Find()</code> / <code>FindObjectOfType()</code></td>
</tr>
<tr>
<td><code>get_parent()</code></td>
<td><code>transform.parent</code></td>
</tr>
<tr>
<td><code>_ready()</code></td>
<td><code>Start()</code></td>
</tr>
<tr>
<td><code>_process(delta)</code></td>
<td><code>Update()</code></td>
</tr>
<tr>
<td><code>_physics_process(delta)</code></td>
<td><code>FixedUpdate()</code></td>
</tr>
<tr>
<td><code>CanvasLayer</code></td>
<td>Canvas (Sorting Order)</td>
</tr>
<tr>
<td><code>Control</code></td>
<td>UI GameObject</td>
</tr>
<tr>
<td><code>Label</code></td>
<td>Text / TextMeshPro</td>
</tr>
<tr>
<td>シグナル</td>
<td>UnityEvent / C# Event</td>
</tr>
<tr>
<td><code>@export</code></td>
<td><code>[SerializeField]</code></td>
</tr>
<tr>
<td><code>@onready</code></td>
<td><code>Awake()</code>での初期化</td>
</tr>
<tr>
<td>AutoLoad</td>
<td><code>DontDestroyOnLoad</code></td>
</tr>
<tr>
<td><code>load()</code> / <code>preload()</code></td>
<td><code>Resources.Load()</code></td>
</tr>
</tbody>
</table>
<p>あくまで「おおよその対応」です。細かい挙動は異なるので、実際に手を動かしながら違いを体感してみてください。</p>
<p>:::ad</p>
<h2>Unity開発者向け: コードで見る違い {#unity-developers-code}</h2>
<p>対応表で概念を押さえたら、次はコードの書き方を見比べてみましょう。GDScriptはPythonに似たインデントベースの構文で、C#と比べてコード量が少ないのが特徴です。Unity開発者が最初に戸惑いやすいのは、型宣言のスタイル、<code>$</code>記法によるノードアクセス、そしてシグナルの仕組みあたりでしょう。</p>
<h3>例1：変数の公開と初期化 (<code>@export</code> vs <code>[SerializeField]</code>)</h3>
<p>ゲーム開発では「インスペクターで値を調整したい」場面が頻繁にあります。移動速度やジャンプ力をコード内にハードコーディングするのではなく、エディタ上でいつでも変更できるようにしたい――Unityでは<code>[SerializeField]</code>を使いますが、Godotでは<code>@export</code>アノテーションが同じ役割を果たします。</p>
<p>また、Unityで<code>Start()</code>に書いていた初期化処理は、Godotでは<code>_ready()</code>に記述します。</p>
<p><strong>Godot (GDScript)</strong></p>
<pre><code class="language-gdscript"># Player.gd
extends Node2D

@export var player_name: String = "Hero"
@export var speed: int = 100

# UnityのStart()に相当
func _ready():
    print("Player Name: ", player_name)
    print("Initial Speed: ", speed)
</code></pre>
<p><strong>Unity (C#)</strong></p>
<pre><code class="language-csharp">// Player.cs
using UnityEngine;

public class Player : MonoBehaviour
{
    [SerializeField] private string playerName = "Hero";
    [SerializeField] private int speed = 100;

    // Godotの_ready()に相当
    void Start()
    {
        Debug.Log("Player Name: " + playerName);
        Debug.Log("Initial Speed: " + speed);
    }
}
</code></pre>
<p>なお、毎フレーム処理は<code>_process(delta)</code>（Unityの<code>Update()</code>に相当）で記述します。引数の<code>delta</code>はUnityの<code>Time.deltaTime</code>と同じくフレーム間の経過秒数で、フレームレートに依存しない動作を実現するために使います。</p>
<h3>例2：物理演算の処理 (<code>_physics_process</code> vs <code>FixedUpdate</code>)</h3>
<p>キャラクターを左右キーで移動させる、ゲーム開発の基本中の基本です。Unityでは<code>Rigidbody2D</code>をアタッチし、<code>Start()</code>で<code>GetComponent</code>して変数に保持し、<code>FixedUpdate()</code>で<code>velocity</code>を設定する――という3ステップが必要でした。</p>
<p>Godotの<code>CharacterBody2D</code>は<code>velocity</code>プロパティと<code>move_and_slide()</code>を内蔵しているため、コンポーネント取得のボイラープレートが不要です。コードを見比べると、Godot側のシンプルさが際立ちます。</p>
<p><strong>Godot (GDScript)</strong></p>
<pre><code class="language-gdscript"># Character.gd
extends CharacterBody2D

const SPEED = 300.0

# UnityのFixedUpdate()に相当
func _physics_process(delta):
    var direction = Input.get_axis("ui_left", "ui_right")
    velocity.x = direction * SPEED
    move_and_slide()  # Godotの便利な組み込み関数
</code></pre>
<p><strong>Unity (C#)</strong></p>
<pre><code class="language-csharp">// Character.cs
using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
public class Character : MonoBehaviour
{
    [SerializeField] private float speed = 300.0f;
    private Rigidbody2D rb;

    void Start()
    {
        rb = GetComponent&#x3C;Rigidbody2D>();
    }

    // Godotの_physics_process()に相当
    void FixedUpdate()
    {
        float moveInput = Input.GetAxis("Horizontal");
        rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
    }
}
</code></pre>
<h3>例3：ノードの取得 (<code>$</code> vs <code>GetComponent</code>)</h3>
<p>「子オブジェクトのアニメーションを再生したい」「コリジョンを無効化したい」――こうした操作はゲーム開発で頻出します。Unityでは<code>GetComponentInChildren&#x3C;T>()</code>や<code>transform.Find()</code>で参照を取得し、nullチェックを挟んでからアクセスするのが定番パターンです。</p>
<p>Godotの<code>$</code>記法（<code>get_node()</code>のシンタックスシュガー）はノードパスを直接書けるため、驚くほど簡潔です。ツリー構造が見えているぶん、どのノードにアクセスしているかも一目瞭然です。</p>
<p><strong>Godot (GDScript)</strong></p>
<pre><code class="language-gdscript"># Player.gd
extends Node2D

func _ready():
    # $記法で子ノードに直接アクセス
    $AnimatedSprite2D.play("run")

    # get_node() を使う方法もある
    var collision_shape = get_node("CollisionShape2D")
    collision_shape.disabled = true
</code></pre>
<p><strong>Unity (C#)</strong></p>
<pre><code class="language-csharp">// Player.cs
using UnityEngine;

public class Player : MonoBehaviour
{
    void Start()
    {
        // 子オブジェクトからコンポーネントを取得
        Animator animator = GetComponentInChildren&#x3C;Animator>();
        if (animator != null)
        {
            animator.Play("run");
        }

        // 名前で子オブジェクトを探す場合
        Transform collisionShape = transform.Find("CollisionShape");
        if (collisionShape != null)
        {
            collisionShape.gameObject.SetActive(false);
        }
    }
}
</code></pre>
<h3>例4：シグナル (<code>signal</code> vs <code>UnityEvent</code>)</h3>
<p>「プレイヤーがダメージを受けたらUIのHP表示を更新したい」――オブジェクト間の通知はゲームのあらゆる場面で必要になります。Unityでは<code>UnityEvent</code>やC#の<code>event</code>で実装しますが、発信側と受信側の参照管理がやや煩雑になりがちです。</p>
<p>Godotのシグナルはこの問題をエレガントに解決しています。大きな特徴は2つ。<strong>エディタ上でノード同士の接続をGUIで設定できること</strong>と、<strong>発信側が受信側の存在を一切知らなくてよい疎結合な設計</strong>です。コードで接続する場合も<code>.connect()</code>一行で済みます。</p>
<p><strong>Godot (GDScript)</strong></p>
<pre><code class="language-gdscript"># Player.gd
extends Node2D

signal health_changed(new_health)

var health: int = 100

func take_damage(damage: int):
    health -= damage
    health_changed.emit(health)  # シグナル発信
</code></pre>
<pre><code class="language-gdscript"># UI.gd
extends Control

func _ready():
    var player = get_node("../Player")
    player.health_changed.connect(_on_health_changed)

func _on_health_changed(new_health: int):
    print("体力が ", new_health, " になりました")
</code></pre>
<p><strong>Unity (C#)</strong></p>
<pre><code class="language-csharp">// Player.cs
using UnityEngine;
using UnityEngine.Events;

public class Player : MonoBehaviour
{
    [SerializeField] private UnityEvent&#x3C;int> onHealthChanged;

    private int health = 100;

    public void TakeDamage(int damage)
    {
        health -= damage;
        onHealthChanged?.Invoke(health);  // イベント発信
    }
}
</code></pre>
<pre><code class="language-csharp">// UI.cs
using UnityEngine;

public class UI : MonoBehaviour
{
    [SerializeField] private Player player;

    void Start()
    {
        player.onHealthChanged.AddListener(OnHealthChanged);
    }

    private void OnHealthChanged(int newHealth)
    {
        Debug.Log("体力が " + newHealth + " になりました");
    }
}
</code></pre>
<h3>例5：シーン切り替え (<code>change_scene_to_file</code> vs <code>SceneManager.LoadScene</code>)</h3>
<p>レベルクリア時に次のステージへ遷移する処理です。Unityでは<code>SceneManager.LoadScene()</code>にシーン名を渡しますが、Build Settingsへのシーン登録が事前に必要でした。</p>
<p>Godotでは<code>.tscn</code>ファイルへのパスを直接指定するだけでシーンを切り替えられます。Build Settingsのような登録ステップがないぶん、プロトタイピング時の取り回しが軽快です。</p>
<p><strong>Godot (GDScript)</strong></p>
<pre><code class="language-gdscript"># GameManager.gd
extends Node

func level_complete():
    print("レベルクリア！")
    get_tree().change_scene_to_file("res://scenes/Level2.tscn")
</code></pre>
<p><strong>Unity (C#)</strong></p>
<pre><code class="language-csharp">// GameManager.cs
using UnityEngine;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour
{
    public void LevelComplete()
    {
        Debug.Log("レベルクリア！");
        SceneManager.LoadScene("Level2");
    }
}
</code></pre>
<p>:::ad</p>
<h2>まとめ {#conclusion}</h2>
<p>Godotは、特に2D開発者におすすめできるゲームエンジンです。スプライトやタイルセット管理ではUnity/Unreal Engineに対して明確な優位性があり、純粋な2DゲームはGodot、ライティング演出にこだわるならUnityという使い分けが良いと感じました。</p>
<p>エンジンとしての成熟度では、情報量・ストア環境・商業実績いずれもUnityやUnreal Engineが圧倒的です。しかし「Backpack Battles」や「Backshot Roulette」などGodot製タイトルは着実に増えており、Runtime Fee騒動で移行した開発者の作品が出揃えばシェアは伸びるでしょう。</p>
<p>オープンソースという点もユニークです。MayaとBlenderの関係を見ると、Godotにも化ける可能性はあります。UnityやUnreal Engineが無料で使える以上、Blenderほどの急速な浸透は難しいかもしれませんが、業界の受け皿として成長する余地は十分です。</p>
<p>私自身はUnityやUnreal Engineにも引き続き触れますが、Godotが第三の選択肢になり得ることを確認できたのは収穫でした。興味がある方はぜひ触れてみてください。</p>
<div class="post-references">
<ul>
<li>
<a href="https://godotengine.org/ja/" target="_blank" rel="noopener noreferrer">Godot Engine 公式サイト</a>
</li>
<li>
<a href="https://trk.udemy.com/nXQoM9" target="_blank" rel="sponsored noopener noreferrer">Godot Engineで気軽に2Dゲームを作ろう (Udemy)</a>
</li>
<li><a href="https://trk.udemy.com/55NY9j" target="_blank" rel="sponsored noopener noreferrer">Godot4: Build a 2D Action-Adventure Game (Udemy)</a></li>
</ul>
</div>
<p>:::ad</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/9ce861a3079316212a5aaab591197063/20250613-Godot-Thumb.png" medium="image"/></item><item><title><![CDATA[吹き出し素材を探すのが面倒すぎたので『マンガ吹き出しメーカー』を作った]]></title><description><![CDATA[ブログ用の吹き出しを素材サイトで探すのが面倒だったので『マンガ吹き出しメーカー』を開発。フチをドラッグするだけでしっぽが生えてきて、背景透過PNGですぐダウンロードできます。]]></description><link>https://uhiyama-lab.com/ja/blog/tooldev/speech-bubble-studio/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/tooldev/speech-bubble-studio/</guid><pubDate>Thu, 12 Jun 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>ブログ記事用に吹き出し画像が欲しくて、素材サイトをいろいろ探してみたんですが、これが意外と面倒で。しっぽの向きが逆だったり、線の太さが微妙に違ったり、「あとちょっと」で理想の形が見つからないんですよね。</p>
<p>かといって、Photoshopで自作するのもパスの操作が面倒だし…。「ただの吹き出し一つにこんなに時間かけるのもなぁ」と思って、結局ブラウザでサクッと作れるツールを作ってみました。</p>
<p><img src="/c8da9eec1d6a672241dbd152ea7ea25d/howto-speech-bubble-studio.webp" alt=""></p>
<p>図形のフチをドラッグするだけでしっぽが生えてくる仕組みなので、数秒で好きな形の吹き出しが作れます。</p>
<p>:::post-link{url="/ja/tools/speech-bubble-studio/" text="▶︎ Speech Bubble Studio"}</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#introduction">吹き出し素材を探すのが面倒だった話</a></li>
<li><a href="#what-is-sbs">どんなツール？</a></li>
<li><a href="#features">できること</a></li>
<li><a href="#how-to-use">使い方</a></li>
<li><a href="#teq">どんな時に使える？</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>吹き出し素材を探すのが面倒だった話 {#introduction}</h2>
<p>素材サイトで吹き出しを探すと、いくつか問題があって。まず、しっぽの向きが逆だったり、線の太さが自分のイラストと合わなかったり。モコモコした形が欲しいのに見つからなかったりします。</p>
<p>自分で作ろうとしても、Photoshopで楕円と三角形を組み合わせて、パスを結合して…ってやってると、「たかが吹き出し一つにこんなに時間かけるのか」って気持ちになるんですよね。</p>
<p>そんな経緯で、もっと手軽に吹き出しを作れるツールがあればいいなと思って開発しました。</p>
<h2>どんなツール？ {#what-is-sbs}</h2>
<p><img src="/db8bd1dbd440c89edefc2f06f548c537/01-speech-bubble-studio.webp" alt=""></p>
<p>ブラウザで吹き出しを作れるツールです。ベースの形や線のスタイルを選んだら、プレビュー画面のフチをドラッグするだけ。ドラッグした場所からしっぽが伸びてきて、先端の位置も自由に調整できます。</p>
<p>作った吹き出しは背景透過のPNG画像でダウンロードできます。</p>
<p>:::post-link{url="/ja/tools/speech-bubble-studio/" text="▶︎ Speech Bubble Studio"}</p>
<p>:::ad</p>
<h2>できること {#features}</h2>
<ul>
<li>プレビュー画面のフチをドラッグして、好きな場所にしっぽを生成・調整</li>
<li>楕円、四角、モコモコ、ギザギザなど、形状や線の太さ・色を自由に設定</li>
<li>「漫画風」「思考風」などのプリセットで、ワンクリックで設定を呼び出し</li>
<li>標準(1x)と高解像度(2x)を選択して、背景透過PNGで書き出し</li>
<li>作成した吹き出しは個人・商用問わず自由に利用OK（クレジット不要）</li>
<li>スマホ・タブレットのタッチ操作にも対応</li>
</ul>
<h2>使い方 {#how-to-use}</h2>
<p>基本的な流れは3ステップです。</p>
<ol>
<li><strong>ベースの形を調整する</strong></li>
</ol>
<p>右側の設定パネルで形や線のスタイルを決めます。プリセットから選ぶのが簡単ですが、幅や高さ、線の太さなども細かく調整できます。</p>
<p><img src="/4820a6d204bfff22157c66e5f0ffb483/02-speech-bubble-studio.png" alt=""></p>
<ol start="2">
<li><strong>しっぽを生やして調整する</strong></li>
</ol>
<p>左側のプレビュー画面で、図形のフチをクリック＆ドラッグします。その場所からしっぽが伸びてくるので、青いハンドルで先端の位置を調整します。</p>
<p><img src="/dcfae9f04b7ba9555ddaa122016a41e3/03-speech-bubble-studio.png" alt=""></p>
<ol start="3">
<li><strong>ダウンロードする</strong></li>
</ol>
<p>「書き出し」セクションで解像度（1x/2x）を選んで、「PNGでダウンロード」ボタンを押せば完成です。</p>
<h2>どんな時に使える？ {#teq}</h2>
<p>こんな場面で使えます。</p>
<ul>
<li><strong>ブログやWebサイトの解説画像に:</strong> イラストや写真にセリフを添えたい時に便利です。</li>
<li><strong>YouTubeのサムネイルや動画内のテロップに:</strong> ゲーム実況や解説動画でキャラクターにセリフを言わせる演出に。</li>
<li><strong>SNS投稿の画像に:</strong> イラストや写真に吹き出しを追加するだけで、投稿が面白くなります。</li>
</ul>
<p>:::ad</p>
<h2>まとめ {#conclusion}</h2>
<p>吹き出しを探したり自作したりする手間が面倒だったので、ドラッグするだけで作れるツールを開発しました。作成した吹き出しは個人・商用問わず自由に使えます。ブログ、動画、SNSなどで活用してもらえると嬉しいです。</p>
<p>:::post-link{url="/ja/tools/speech-bubble-studio/" text="▶︎ Speech Bubble Studio"}</p>
<p>:::ad</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/c8da9eec1d6a672241dbd152ea7ea25d/howto-speech-bubble-studio.webp" medium="image"/></item><item><title><![CDATA[自分のイラストの配色バランスが気になって『キャラ配色アナライザー』を作った]]></title><description><![CDATA[自分が描いたイラストの配色って、客観的に見るとどうなってるんだろう？色の構成比率を円グラフで可視化して、配色バランスを数値で確認できる『キャラ配色アナライザー』を開発しました。]]></description><link>https://uhiyama-lab.com/ja/blog/tooldev/character-color-analyzer/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/tooldev/character-color-analyzer/</guid><pubDate>Mon, 09 Jun 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>自分でイラストを描いた後、「この配色って、客観的に見るとどうなってるんだろう？」って気になることありませんか？制作中は感覚で色を選んでいるので、完成後に振り返ると「意外とこの色が多かったんだ」とか「アクセントカラーが弱かったかも」って発見があります。</p>
<p>スポイトで色を拾うだけだと、全体の配色バランスは分かりません。「どの色が、どのくらいの面積を占めているのか？」を数値で把握できたら、自分の配色の癖や改善点が見えてくるはずです。</p>
<p>そんなわけで、イラストをアップロードするだけで、色の構成比率を円グラフで可視化できる『キャラ配色アナライザー』を開発しました。すべての処理がブラウザ内で完結するので、未公開の作品でも安心して分析できます。</p>
<p><img src="/27bd3a968b25c341cbf578fe6ccc643d/thumb-character-color-analyzer.png" alt="">
※参考画像は管理人が描いたファンアートです。</p>
<p>:::post-link{url="/ja/tools/character-color-analyzer/" text="▶︎ キャラ配色アナライザー"}</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#introduction">配色バランスを知りたかった話</a></li>
<li><a href="#what-is-cca">どんなツール？</a></li>
<li><a href="#features">できること</a></li>
<li><a href="#how-to-use">使い方</a></li>
<li><a href="#usage-tips">分析結果の活用方法</a></li>
<li><a href="#advanced-features">分析の仕組み</a></li>
<li><a href="#tech-info">安心のブラウザ完結</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>配色バランスを知りたかった話 {#introduction}</h2>
<p>イラストを描いていると、感覚で色を選んでいることが多いんですよね。「この色、いい感じかも」って塗っていって、完成した後に「でも客観的に見ると、どんなバランスなんだろう？」って気になります。</p>
<p>スポイトで色を拾っても、それは「その色が使われている」ことしか分かりません。「どの色が、どのくらいの面積を占めているのか？」という構成比率を知ることで、自分の配色の癖や、改善点が見えてくるはずです。</p>
<p>そんなわけで、色の構成比率を円グラフで可視化するツールを作りました。</p>
<h2>どんなツール？ {#what-is-cca}</h2>
<p><img src="/7596a643dd0f9ad3bd1b097c5d4c5010/howto-character-color-analyzer.webp" alt=""></p>
<p>イラスト画像をアップロードすると、使われている色の種類とその構成比率を円グラフとリストで表示するツールです。</p>
<p>自分のイラストの配色バランスを数値で確認したい時に使えます。背景色など不要な色を分析から除外する機能もあるので、キャラクター本体の配色に絞って分析できます。</p>
<p>:::post-link{url="/ja/tools/character-color-analyzer/" text="▶︎ キャラ配色アナライザー"}</p>
<p>:::ad</p>
<h2>できること {#features}</h2>
<ul>
<li>画像をドラッグ＆ドロップするだけで分析開始、サーバーに送信されない</li>
<li>分析結果を円グラフとパーセンテージで可視化</li>
<li>人間の知覚に近い「CIELAB色空間」とk-meansクラスタリングで色を抽出</li>
<li>プレビュー画像上の不要な部分（背景など）をクリックして除外</li>
<li>除外した色をリストで管理、いつでも復帰可能</li>
<li>分析された色のHEXコードをワンクリックでコピー</li>
<li>すべての処理がブラウザ内で完結、未公開作品でも安心</li>
<li>PCでもスマートフォンでも利用可能</li>
</ul>
<h2>使い方 {#how-to-use}</h2>
<p>基本的な流れは3ステップです。</p>
<ol>
<li><strong>画像をアップロードする</strong></li>
</ol>
<p><img src="/ae5d10aca716197c98a1c45264488333/character-color-analyzer-import.png" alt=""></p>
<p>分析したいイラスト画像を左側のエリアにドラッグ＆ドロップするか、クリックしてファイルを選択します。画像はサーバーに送信されず、ブラウザの中だけで分析されます。</p>
<ol start="2">
<li><strong>分析結果を確認＆設定を調整する</strong></li>
</ol>
<p><img src="/27bd3a968b25c341cbf578fe6ccc643d/thumb-character-color-analyzer.png" alt=""></p>
<p>右側に色の構成比を示した円グラフとカラーリストが表示されます。左側のパネルで「抽出する色の数」をスライダーで調整できます。</p>
<ol start="3">
<li><strong>不要な色を除外する（任意）</strong></li>
</ol>
<p><img src="/9e4557ce386ecfb78f9188ed851bbeef/character-color-analyzer-%E9%99%A4%E5%A4%96%E3%83%84%E3%83%BC%E3%83%AB.webp" alt=""></p>
<p>背景など、キャラ本体と関係ない色を除外したい場合は、左側のプレビュー画像上の該当箇所をクリックします。クリックした領域の色が除外リストに追加され、分析結果がリアルタイムで更新されます。</p>
<h2>分析結果の活用方法 {#usage-tips}</h2>
<p>分析結果を使って、いろんな気づきが得られます。</p>
<ul>
<li><strong>自分の作品を客観視する:</strong>
完成したイラストを分析してみると、「思っていたより特定の色に偏ってるな」「アクセントカラーが弱かったかも」など、自分の配色の癖や改善点を発見できます。</li>
<li><strong>配色の引き出しを増やす:</strong>
自分の過去の作品を分析して、「なぜこの配色は心地良いのか」を考えてみましょう。メインカラーとサブカラーの比率、アクセント色の使い方などを数値で把握することで、感覚的だった「センス」を具体的な「テクニック」として学べます。</li>
<li><strong>配色リファレンス集を作る:</strong>
いろんなイラストを分析して、その結果をストックしていくと、自分だけの配色リファレンス集が完成します。「元気なキャラは暖色系の比率が高い」「クールなキャラは無彩色をうまく使っている」など、実践的な配色ルールを学べます。</li>
</ul>
<p>※配色見本として他人の作品を分析する場合は、私的利用の範囲に留めるなど、著作物の利用規約にご配慮ください。</p>
<p>:::ad</p>
<h2>分析の仕組み {#advanced-features}</h2>
<p>シンプルな見た目ですが、精度の高い分析のために技術を工夫しています。</p>
<p><strong>人間の感覚に近い「CIELAB色空間」:</strong>
一般的なRGB値ではなく、人間の色の知覚モデルに近い「CIELAB」という色空間で色の計算を行っています。これで、「機械的に色が近い」ではなく「人間が見て色が似ている」と感じるピクセル同士を、より正確にグループ化できます。</p>
<p><strong>平均色を賢く見つける「k-meansクラスタリング」:</strong>
イラストには無数の色やグラデーションが存在します。その中から「イラストを代表する色」を抽出するために、k-meansというクラスタリング技術を利用しています。画像内のピクセルを自動的に指定された数の色のグループに分類し、それぞれのグループの平均的な色を抽出しています。</p>
<h2>安心のブラウザ完結 {#tech-info}</h2>
<p>このツールは、プライバシーを最優先に設計しています。画像の選択から分析、結果の表示まで、すべての処理がブラウザ内部で完結します。</p>
<p>画像データが外部のサーバーに送信・保存されることは一切ありません。オフラインの状態でも動作するので、未公開作品や個人的なイラストも、情報漏洩の心配なく安心して使えます。</p>
<p>:::post-link{url="/ja/tools/character-color-analyzer/" text="▶︎ キャラ配色アナライザー"}</p>
<p>:::ad</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/7596a643dd0f9ad3bd1b097c5d4c5010/howto-character-color-analyzer.webp" medium="image"/></item><item><title><![CDATA[配色のアイデアが欲しくて『キャラ配色ナビゲーター』を作った]]></title><description><![CDATA[キャラクターの配色を考えるのって、意外と難しいんですよね。いつも似たような配色になってしまうので、ベースカラーを選ぶだけで色彩理論に基づいた配色パターンを提案してくれる『キャラ配色ナビゲーター』を開発しました。]]></description><link>https://uhiyama-lab.com/ja/blog/tooldev/character-color-navigator/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/tooldev/character-color-navigator/</guid><pubDate>Sat, 31 May 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>キャラクターの配色を考えるのって、意外と難しいんですよね。「どんな色を組み合わせればいいんだろう？」って悩んだり、気づいたら「いつも似たような配色になってるな」ってことありませんか？</p>
<p>基本的な配色ルールは知っていても、そこから新しいアイデアを出すのは簡単じゃないです。「この髪色に合う服の色は？」「アクセントカラーはどうしよう？」って考え始めると、時間がかかります。</p>
<p>そんなわけで、ベースカラーを1色選ぶだけで、色彩理論に基づいた配色パターンを提案してくれる『キャラ配色ナビゲーター』を開発しました。</p>
<p><img src="/a94af2609a52db763e2778093fe4ca3a/howto-character-color-palette.webp" alt=""></p>
<p>:::post-link{url="/ja/tools/character-color-navigator/" text="▶︎ キャラ配色ナビゲーター"}</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#introduction">配色のアイデア探しに困った話</a></li>
<li><a href="#what-is-ccn">どんなツール？</a></li>
<li><a href="#features">できること</a></li>
<li><a href="#how-to-use">使い方</a></li>
<li><a href="#palette-types">配色パターンの種類</a></li>
<li><a href="#advanced-tips">パレット画像の活用方法</a></li>
<li><a href="#tech-info">ブラウザで完結</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>配色のアイデア探しに困った話 {#introduction}</h2>
<p>キャラクターデザインで配色を考える時、いつも悩むんですよね。色の組み合わせって無限にあるから、「どれが一番いいんだろう？」って迷います。</p>
<p>暖色系の明るい色は「活発」な印象、寒色系の暗い色は「落ち着き」や「神秘性」を出せるのは知ってるんですが、実際に組み合わせを考え始めると、気づいたらいつも同じようなパターンになってしまいます。</p>
<p>新しい配色のアイデアが欲しいな、と思って、このツールを作りました。</p>
<h2>どんなツール？ {#what-is-ccn}</h2>
<p><img src="/93d2bc811fd88e41f9e65ce607a9922b/character-color-navigator.png" alt=""></p>
<p>ベースカラーを1色選ぶだけで、色彩理論に基づいた配色パターンを自動で提案してくれるツールです。</p>
<p>「髪の色は決まったけど、服の色はどうしよう…」「もっと目立つ色の組み合わせを知りたい！」「いつもと違う配色を試したい」そんな時に使えます。</p>
<p>提案されるパレットは、色の役割（ベース、アソート、アクセント）も考慮されているので、実際のデザイン作業にそのまま使えます。</p>
<p>:::post-link{url="/ja/tools/character-color-navigator/" text="▶︎ キャラ配色ナビゲーター"}</p>
<p>:::ad</p>
<h2>できること {#features}</h2>
<ul>
<li>好きな色を1色選ぶだけで、複数の配色パターンを瞬時に生成</li>
<li>モノクロマティック、アナロガス、コンプリメンタリー、トライアドなど、様々な配色理論を網羅</li>
<li>「穏やか・調和」「メリハリ・インパクト」など、テーマ別に配色を絞り込み</li>
<li>各パレット内の色が「ベースカラー」「アソートカラー」「アクセントカラー」としてどの程度の比率で使われるかの目安を表示</li>
<li>表示された色のHEXコードをワンクリックでコピー</li>
<li>気に入ったテーマの配色パターンをまとめて1枚のPNG画像として保存</li>
<li>各配色パターンの特徴や与える印象についての簡単な解説を表示</li>
<li>定番の配色理論だけでなく、思いがけない色の組み合わせも提案</li>
<li>ブラウザだけで完結、ソフトのインストール不要</li>
<li>シンプルなインターフェースで、誰でも簡単に使える</li>
</ul>
<h2>使い方 {#how-to-use}</h2>
<p>基本的な流れは3ステップです。</p>
<ol>
<li><strong>ベースカラーを選ぶ</strong></li>
</ol>
<p><img src="/12c34e8f9bf73bf30fdf85bf4de4bee6/02-character-color-navigator.webp" alt=""></p>
<p>左側のコントロールパネルで、キャラクターのイメージを代表する「ベースカラー」を選択します。カラーピッカーで直感的に選ぶか、HEXコードを直接入力してください。</p>
<ol start="2">
<li><strong>配色テーマを選択</strong></li>
</ol>
<p><img src="/fcd322d53e9e6d160ccee35586a19d8d/03-character-color-navigator.webp" alt=""></p>
<p>「配色テーマを選ぶ」セクションから、作りたい雰囲気に合わせてテーマを選択します。「穏やか・調和」「メリハリ・インパクト」「バランスの取れたカラフル」など、テーマを選ぶと、右側にそのテーマに合った配色パターンが自動的に表示されます。</p>
<ol start="3">
<li><strong>パレットを確認・保存</strong></li>
</ol>
<p>生成されたカラーパレットを確認しましょう。各色の上にマウスカーソルを合わせると、HEXコードが表示され、クリックするとコピーできます。気に入った配色が見つかったら、「パレット画像を保存」ボタンで、現在表示されているテーマの配色パレット（最大8種類）をまとめて1枚の画像としてダウンロードできます。</p>
<h2>配色パターンの種類 {#palette-types}</h2>
<p>いろんな配色理論に基づいたパターンが提案されます。</p>
<ul>
<li><strong>モノクロマティック配色 (Monochromatic):</strong></li>
</ul>
<p>単一の色相をベースに、明るさや鮮やかさだけを変化させて組み合わせる配色です。全体にまとまりが生まれ、落ち着いた印象になります。</p>
<ul>
<li><strong>アナロガス配色 (Analogous):</strong></li>
</ul>
<p>色相環上で隣り合う、似た色同士で構成される配色です。自然で目に優しく、穏やかで親しみやすい印象になります。</p>
<ul>
<li><strong>コンプリメンタリー配色 (Complementary):</strong></li>
</ul>
<p>色相環上で正反対に位置する補色同士を組み合わせる配色です。強い色の対比が生まれ、力強くダイナミックな印象を与えます。</p>
<ul>
<li><strong>スプリットコンプリメンタリー配色 (Split Complementary):</strong></li>
</ul>
<p>ベースカラーに対して、その補色の両隣にある2色を組み合わせる配色です。補色ほど強くない、和らげた華やかさと安定感があります。</p>
<ul>
<li><strong>トライアド配色 (Triadic):</strong></li>
</ul>
<p>色相環を三等分する位置にある3色を組み合わせる配色です。多様な色を使いながらも、均等なバランスで安定感があり、活気のある明るい印象になります。</p>
<ul>
<li><strong>テトラディック配色 (Tetradic):</strong></li>
</ul>
<p>色相環上で長方形または正方形を形成する4色で構成する配色です。豊かで多様性に富んだ表現が可能ですが、色の配分や面積バランスの調整が重要になります。</p>
<p>これらの他にも、様々なバリエーションが提案されます。いろいろ試してみてください。</p>
<p>:::ad</p>
<h2>パレット画像の活用方法 {#advanced-tips}</h2>
<p>ダウンロードしたパレット画像は、配色見本として使うだけでなく、イラスト制作のワークフローに直接組み込むこともできます。</p>
<p>保存したパレット画像を、CLIP STUDIO PAINT、Photoshop、Procreate、SAIといったイラスト制作ソフトに、新規レイヤーとして読み込んでください。あとは、そのレイヤーからスポイトツールで色を拾うだけです。HEXコードを一つ一つ入力する手間が省けて、スピーディーに作画に反映できます。</p>
<h2>ブラウザで完結 {#tech-info}</h2>
<p>このツールは、ブラウザだけで全ての処理が完結するように設計されています。特別なアプリケーションのインストールは不要です。</p>
<p>色の選択、配色パターンの計算、パレット画像の生成といった処理は、すべてブラウザ内部で行われます（主にJavaScriptとHTML5 Canvas APIを利用）。入力されたベースカラーの情報や生成されたパレットデータが、外部のサーバーに送信されることはありません。</p>
<p>:::post-link{url="/ja/tools/character-color-navigator/" text="▶︎ キャラ配色ナビゲーター"}</p>
<p>:::ad</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/a94af2609a52db763e2778093fe4ca3a/howto-character-color-palette.webp" medium="image"/></item><item><title><![CDATA[ちょっとした効果音が欲しくて『シンプル効果音メーカー』を作った]]></title><description><![CDATA[ゲーム開発や動画編集で、ちょっとした効果音が欲しい時ってありますよね。フリー素材を探すのも面倒だったので、ブラウザでXYパッドを操作するだけでオリジナルの効果音を作れる『シンプル効果音メーカー』を開発しました。]]></description><link>https://uhiyama-lab.com/ja/blog/tooldev/simple-sound-fx-generator/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/tooldev/simple-sound-fx-generator/</guid><category><![CDATA[audio]]></category><pubDate>Fri, 30 May 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>ゲーム開発や動画編集をしていると、「ちょっとした効果音が欲しいな」って思う場面ありますよね。フリー素材を探すのもいいんですが、イメージにピッタリ合うものを見つけるのは意外と時間がかかります。</p>
<p>「もっと手軽に、自分で効果音を作れたらいいのに」と思って、ブラウザでサクッと効果音を生成できるツールを開発しました。</p>
<p><img src="/4cdc9ed4f0065e17a9d8d4df0594341b/01_simpleSoundFXGenerator.png" alt=""></p>
<p>XYパッドをマウスで操作するだけで、直感的に音の高さや変化を調整できます。作成したサウンドはWAVファイルでダウンロードできて、商用利用もOKです。</p>
<p>:::post-link{url="/ja/tools/simple-sound-fx-generator/" text="シンプル効果音メーカー"}</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#overview">効果音が欲しかった話</a></li>
<li><a href="#what-is-tool">どんなツール？</a></li>
<li><a href="#features">できること</a></li>
<li><a href="#usage">使い方</a></li>
<li><a href="#sound-tips">サウンド調整のコツ</a></li>
<li><a href="#implementation">ブラウザで完結</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>効果音が欲しかった話 {#overview}</h2>
<p>ゲーム開発のプロトタイプを作ってる時、「ジャンプ音」とか「アイテム取得音」みたいな、ちょっとした効果音が欲しくなりますよね。フリー素材サイトで探すのもいいんですが、イメージに合うものを見つけるのに時間がかかることがあります。</p>
<p>「パッと自分で作れたら便利なのに」と思って、ブラウザで効果音を生成できるツールを開発しました。</p>
<h2>どんなツール？ {#what-is-tool}</h2>
<p><img src="/4a0d1b20bea25a77db98e60bf283d485/02_simpleSoundFXGenerator.webp" alt=""></p>
<p>ブラウザで動作する効果音作成ツールです。XYパッドをマウスで操作するだけで、直感的に音の高さや変化を調整できます。</p>
<p>音の「波形」「持続時間」「音量」に加えて、「エンベロープ（音量の時間変化）」や「ピッチスライド（音程の変化）」も細かく調整できるので、多彩なサウンドを生み出せます。</p>
<p>レトロな2Dゲームでよく使われるような効果音のプリセットも用意しているので、サウンドデザインの知識がなくてもすぐに音を作り始められます。</p>
<h2>できること {#features}</h2>
<ul>
<li>XYパッドでマウスやタッチ操作、音の高さとピッチ変化をリアルタイムに調整</li>
<li>波形選択（サイン波、矩形波など）、持続時間、音量、ADSRエンベロープを自由にカスタマイズ</li>
<li>「ジャンプ」「シュート」「爆発」など、2Dゲーム用の効果音プリセットを搭載</li>
<li>作成したサウンドをWAVファイルで保存</li>
<li>個人・商用問わず、作成した効果音を自由に利用OK</li>
<li>ブラウザだけで完結、ソフトのインストール不要</li>
</ul>
<h2>使い方 {#usage}</h2>
<p>基本的な流れは3ステップです。</p>
<ol>
<li><strong>基本の音づくり</strong></li>
</ol>
<p><img src="/7a888f92857d529315f19a57eec70ac4/03_simpleSoundFXGenerator.png" alt=""></p>
<p>左側の「サウンド設定」と「エンベロープ」で音の基本を決めます。「波形」を選んで、「持続時間」「音量」を設定。次に、「アタック（音の立ち上がり）」「ディケイ（最大音量から持続音への移行）」「サスティン（持続音の音量）」「リリース（音が消えるまでの余韻）」を調整します。</p>
<p>ポイント：エンベロープの各時間は、サウンド全体の持続時間より短く設定すると意図した音になりやすいです。</p>
<ol start="2">
<li><strong>サウンドパッドで音を調整</strong></li>
</ol>
<p><img src="/569c435c5c4b019b1d795031fed11de4/04_simpleSoundFXGenerator.webp" alt=""></p>
<p>右側の「サウンドパッド」で音の高さや変化を調整します。パッド上をマウスや指でドラッグすると、カーソルの位置に応じて音の高さ（Y軸）やピッチの変化（X軸）が変わります。いろいろ試して、面白い音を見つけてみましょう。</p>
<ol start="3">
<li><strong>サウンドを再生＆ダウンロード</strong></li>
</ol>
<p><img src="/bd186dee12581828c338082023780dee/05_simpleSoundFXGenerator.png" alt=""></p>
<p>「再生」ボタンで作成した音を確認できます。納得のいくサウンドができたら「ダウンロード」ボタンで、WAVファイルとして保存できます。</p>
<p>プリセットを選んでから各パラメータを微調整するのもオススメです。</p>
<p>:::ad</p>
<h2>サウンド調整のコツ {#sound-tips}</h2>
<p>イメージに近い効果音を作るためのヒントです。</p>
<ul>
<li><strong>XYパッドの使い方:</strong>
<ul>
<li><strong>Y軸（上下）:</strong> 音の基本の高さ。上にいくほど高く、下にいくほど低くなります。</li>
<li><strong>X軸（左右）:</strong> 音が鳴っている間のピッチの変化。左に行くほど開始音から下がり、右に行くほど上がります。中央付近ではピッチの変化は少なくなります。</li>
</ul>
</li>
<li><strong>エンベロープ (ADSR) で音の印象を変える:</strong>
<ul>
<li><strong>アタック (Attack):</strong> 短いと「タッ！」と歯切れの良い音、長いと「フワ～」と柔らかい立ち上がりに。</li>
<li><strong>ディケイ (Decay) &#x26; サスティン (Sustain):</strong> ディケイが短くサスティンレベルが低いと「タンッ」と減衰する音、ディケイが長くサスティンレベルが高いと「パーン」と伸びる音になります。</li>
<li><strong>リリース (Release):</strong> 短いと「プツッ」と音が切れ、長いと「ゥゥゥン…」と余韻が残ります。</li>
</ul>
</li>
<li><strong>波形の種類:</strong>
<ul>
<li><strong>サイン波 (Sine):</strong> 丸く柔らかい音。笛や「ピュン」といった音に。</li>
<li><strong>矩形波 (Square):</strong> ファミコンのようなピコピコした音。レトロゲームの効果音に。</li>
<li><strong>ノコギリ波 (Sawtooth):</strong> 「ジー」っというような倍音を多く含む鋭い音。爆発音の芯などに。</li>
<li><strong>三角波 (Triangle):</strong> サイン波より少し硬いが、矩形波よりは柔らかい中間的な音。</li>
</ul>
</li>
</ul>
<p>いろんなパラメータを組み合わせて、オリジナルサウンドを探求してみてください。</p>
<h2>ブラウザで完結 {#implementation}</h2>
<p>このツールは、ブラウザだけで全ての処理が完結するように作られています。特別なソフトウェアのインストールは必要ありません。</p>
<p>サウンドの生成には、ブラウザに搭載されている音声技術「Web Audio API」を利用しています。リアルタイムでの音の変化や、WAVファイルへの書き出しも、すべてブラウザ上で行われます。</p>
<p>:::post-link{url="/ja/tools/simple-sound-fx-generator/" text="シンプル効果音メーカー"}</p>
<p>:::ad</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/569c435c5c4b019b1d795031fed11de4/04_simpleSoundFXGenerator.webp" medium="image"/></item><item><title><![CDATA[【VRChat】2Dイラストが動く『ぷにぷにアバター』を自作絵に簡単改変する手順]]></title><description><![CDATA[自分の描いた絵をVRChatで動かそう！人気の「ぷにぷにアバター」改変方法をやさしく解説。]]></description><link>https://uhiyama-lab.com/ja/blog/dialy/vrchat-punipuni-install/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/dialy/vrchat-punipuni-install/</guid><category><![CDATA[vrchat]]></category><pubDate>Wed, 02 Apr 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p><img src="/1ed3534ac1987f6eda0d2a8c03b36229/20250402-%E3%81%B7%E3%81%AB%E3%81%B7%E3%81%AB-010-%E3%81%B7%E3%81%AB%E3%81%B7%E3%81%AB%E3%82%A2%E3%83%90%E3%82%BF%E3%83%BC.webp" alt="VRChat内で動く改変されたぷにぷにアバター"></p>
<p>VRChatでは、2025年2月頃から「ぷにぷにアバター」と呼ばれる、2Dイラストがそのまま動き出したようなアバターをよく見かけるようになりました。</p>
<p>この流れのきっかけとなったのが、rio3dさんがBoothで公開した「<a href="https://rio3d.booth.pm/items/6556549">[3Dモデル] #ぷにぷにあばたー ﾅｷﾞﾁｬﾝ</a>」です。このアバターをベースにすることで、自分の描いたイラストをVRChatの3D空間で動かすことができます。</p>
<p>ペーパーマリオのように、平面的なキャラクターが立体空間を歩き回る姿はとてもユニークですよね。</p>
<p>今回は、この「#ぷにぷにあばたー ﾅｷﾞﾁｬﾝ」を例に、自分のイラストに差し替えてオリジナルのぷにぷにアバターを作成する手順を解説します。</p>
<p>改変に必要なもの：</p>
<ul>
<li><a href="https://rio3d.booth.pm/items/6556549">[3Dモデル] #ぷにぷにあばたー ﾅｷﾞﾁｬﾝ</a> (Boothで購入)</li>
<li>Unity + VRChat Creator Companion (VCC) がセットアップされた環境</li>
<li>アバターにしたい自分のイラスト (後述する指定の形式で4種類)</li>
</ul>
<p>手順はシンプルです。</p>
<ol>
<li>「#ぷにぷにあばたー ﾅｷﾞﾁｬﾝ」をUnityプロジェクトにインポートする</li>
<li>アバターに使われているイラスト（テクスチャ）を、自分の用意したイラストに差し替える</li>
<li>VRChatにアップロードする</li>
</ol>
<p>たったこれだけで、あなたの描いたキャラクターがVRChatで動き出します。それでは、具体的な手順を見ていきましょう。</p>
<p>:::post-link{url="<a href="https://rio3d.booth.pm/items/6556549">https://rio3d.booth.pm/items/6556549</a>" text="[3Dモデル] #ぷにぷにあばたー ﾅｷﾞﾁｬﾝ (BOOTH)"}</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#import-avatar">Unityに「ぷにぷにアバター」をインポートする</a></li>
<li><a href="#modify-avatar">簡単！ぷにぷにアバターのイラスト差し替え手順</a>
<ul>
<li><a href="#prepare-images">差し替え用イラストの準備</a></li>
<li><a href="#overwrite-files">Unity上でイラストファイルを上書き</a></li>
<li><a href="#confirm-upload">Unityでの確認とVRChatへのアップロード</a></li>
</ul>
</li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>Step 1: Unityに「ぷにぷにアバター」をインポートする {#import-avatar}</h2>
<p>まずは、Boothで購入した「#ぷにぷにあばたー ﾅｷﾞﾁｬﾝ」のフォルダに含まれている<code>.unitypackage</code>ファイルを、あなたのVRChatプロジェクトのAssetsウィンドウにドラッグ＆ドロップしてインポートします。</p>
<p><img src="/c8fd43247555a74f10d668ca59936e74/001-%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E5%B0%8E%E5%85%A5.png" alt="Unityにぷにぷにアバターのunitypackageをドラッグ&#x26;ドロップする"></p>
<p>インポートが完了したら、Projectウィンドウの<code>Assets/PUNIPUNI_AVATAR</code>フォルダ内にある「<strong>PUNI</strong>」という名前のPrefab（プレハブ）を、Hierarchyウィンドウ（シーン）にドラッグ＆ドロップして配置します。</p>
<p><img src="/cb85ad7c52e50506a92d0851bf9c5feb/20250402-%E3%81%B7%E3%81%AB%E3%81%B7%E3%81%AB-002-%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88%E5%AE%8C%E4%BA%86.png" alt="インポート完了後、PUNI Prefabをヒエラルキーに配置"></p>
<p>この状態でVRChat Creator Companion (VCC) の手順に従ってアバターをアップロードすれば、オリジナルの「ﾅｷﾞﾁｬﾝ」ぷにぷにアバターとして使用できます。</p>
<hr>
<h2>Step 2: 簡単！ぷにぷにアバターのイラスト差し替え手順 {#modify-avatar}</h2>
<p>ここからが本番の「改変」作業です。ぷにぷにアバターの見た目と動きは、主に以下の4枚のPNG画像（テクスチャ）によって制御されています。これらの画像を自分のイラストに差し替えるだけで、簡単にオリジナルアバターが作れます。</p>
<p><img src="/dbf65a17f05d993b8fd0c282d6b94869/20250402-%E3%81%B7%E3%81%AB%E3%81%B7%E3%81%AB-003-%E8%A1%A8%E7%A4%BA%E7%B4%A0%E6%9D%90.png" alt="ぷにぷにアバターを構成する4枚のPNG画像"></p>
<p>これらの画像は、Unityプロジェクトの<code>Assets/PUNIPUNI_AVATAR/Materials</code>フォルダ内にあります。</p>
<ul>
<li><code>tex_nagi_default.png</code>: 通常時（立っている時やアイドル状態）のイラスト</li>
<li><code>tex_nagi_default_talk.png</code>: 口パク時（話している時）のイラスト</li>
<li><code>tex_nagi_walk_1.png</code>: 歩行時アニメーション1枚目のイラスト</li>
<li><code>tex_nagi_walk_2.png</code>: 歩行時アニメーション2枚目のイラスト</li>
</ul>
<p>VRChat内で実際に動かすと、通常時は<code>default</code>、話すと<code>talk</code>、歩くと<code>walk_1</code>と<code>walk_2</code>が交互に表示されることで、アニメーションしているように見えます。</p>
<p>通常の3Dアバター改変のように複雑な位置調整などは不要です。最も簡単な改変方法は、この4つのファイルと全く同じファイル名で自分のイラストを用意し、元のファイルを上書きしてしまうことです。これにより、アバター内部の参照設定を維持したまま、見た目だけを自分のイラストに入れ替えることができます。</p>
<p>:::ad</p>
<h3>2-1. 差し替え用イラストの準備 {#prepare-images}</h3>
<p>まずは、上記4つの状態に対応する自分のイラストを用意します。ポイントは以下の通りです。</p>
<ul>
<li><strong>画像サイズ:</strong> 元のﾅｷﾞﾁｬﾝ画像は3035×3035ピクセルで作成されています。可能であれば、これと同じサイズでイラストを用意し、中央にキャラクターを配置するのがおすすめです。（異なるサイズでも動作しますが、表示がずれる可能性があります）</li>
<li><strong>ファイル名:</strong> 元のファイル名（<code>tex_nagi_default.png</code> など）と完全に同じ名前を付けます。</li>
<li><strong>ファイル形式:</strong> PNG形式（透過背景も利用可能）で保存します。</li>
</ul>
<p>私は、以前作成したマヌカちゃんアバターの改変モデルを元に、以下のようなイラストを描いてみました。（当ブログのWebツール「<a href="/ja/tools/image-to-pixelization/">ドット絵変換ツール</a>」で少しドット絵風にしています）</p>
<p>:::post-link{url="/ja/tools/image-to-pixelization/" text="ドット絵変換ツール"}</p>
<p><img src="/1cdc2a2522dc30637fb9aa236f2f58c7/20250402-%E3%81%B7%E3%81%AB%E3%81%B7%E3%81%AB-004-%E7%BD%AE%E3%81%8D%E6%8F%9B%E3%81%88%E7%B4%A0%E6%9D%90.png" alt="差し替え用に作成した4種類のイラスト"></p>
<p>（左から順に、通常、口パク、歩行1、歩行2）</p>
<p><img src="/04e252ad57f1cd36e48091df825367e8/20241121-0-VRChat%E3%81%AF%E3%81%98%E3%82%81%E3%81%A6%E3%81%AE%E3%83%A2%E3%83%87%E3%83%AB%E5%B0%8E%E5%85%A51.webp" alt="元になったマヌカちゃんアバター"></p>
<p>描いた（または用意した）イラスト4枚に、それぞれ対応する元のファイル名を付けます。</p>
<p><img src="/e66cff87572ee241bc89da681f4d5965/20250402-%E3%81%B7%E3%81%AB%E3%81%B7%E3%81%AB-005-%E7%B4%A0%E6%9D%90%E3%83%AA%E3%83%8D%E3%83%BC%E3%83%A0.png" alt="用意したイラストに元のファイル名をつける"></p>
<h3>2-2. Unity上でイラストファイルを上書き {#overwrite-files}</h3>
<p>次に、Unityエディタ上で元のイラストファイルを特定し、用意した自分のイラストで上書きします。</p>
<ol>
<li>
<p>UnityのProjectウィンドウで、<code>Assets/PUNIPUNI_AVATAR/Materials</code>フォルダを開きます。</p>
</li>
<li>
<p>中にある元のﾅｷﾞﾁｬﾝのPNGファイル（例: <code>tex_nagi_default.png</code>）のどれか一つを右クリックし、「Show in Explorer」（Macの場合は「Reveal in Finder」）を選択します。これにより、これらのファイルが保存されている実際のフォルダが開きます。</p>
</li>
</ol>
<p><img src="/0d81074ccd01036dec4209884d248fa3/20250402-%E3%81%B7%E3%81%AB%E3%81%B7%E3%81%AB-006-%EF%BE%85%EF%BD%B7%EF%BE%9E%EF%BE%81%EF%BD%AC%EF%BE%9D%E7%94%BB%E5%83%8F.png" alt="UnityのMaterialsフォルダでPNGファイルを右クリックしShow in Explorerを選択"></p>
<ol start="3">
<li>開いたフォルダに、先ほど準備した自分のイラストPNGファイル4枚すべてをコピー＆ペースト（またはドラッグ＆ドロップ）します。</li>
</ol>
<p><img src="/209eb0768a6d747370345d8166a94575/20250402-%E3%81%B7%E3%81%AB%E3%81%B7%E3%81%AB-007-%E6%94%B9%E5%A4%89%E7%B4%A0%E6%9D%90%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88.png" alt="開いたフォルダに自作イラストをコピー&#x26;ペースト"></p>
<ol start="4">
<li>「ファイル名は既に存在します」という警告が表示されるので、「ファイルを置き換える」を選択して、4つのファイルをすべて上書きします。</li>
</ol>
<p><img src="/127c133661da57a9542ee4372c900396/20250402-%E3%81%B7%E3%81%AB%E3%81%B7%E3%81%AB-008-%E7%94%BB%E5%83%8F%E3%81%AE%E4%B8%8A%E6%9B%B8%E3%81%8D.png" alt="ファイルの上書き確認ダイアログ"></p>
<h3>2-3. Unityでの確認とVRChatへのアップロード {#confirm-upload}</h3>
<p>ファイルの上書きが完了したら、Unityエディタの画面に戻ります。Unityがファイルの変更を自動的に検知し、インポート処理を行います。少し待つと、ProjectウィンドウやSceneビューに表示されているアバターの見た目が、自分のイラストに変わっているはずです。</p>
<p><img src="/94fb1e62fe51c58506b207988f91fbc9/20250402-%E3%81%B7%E3%81%AB%E3%81%B7%E3%81%AB-009-Unity%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88.webp" alt="Unityエディタ上でイラストが差し替わったことを確認"></p>
<p>（↑Unity上で自分のイラストに置き換わりました）</p>
<p>ここまで確認できたら、あとは通常のVRChatアバターアップロード手順と同じです。VRChat SDK Control Panelを開き、「Build &#x26; Publish for Windows」ボタンを押して、アバターをアップロードしましょう。</p>
<p>これで、あなたのオリジナルイラストがVRChatで動く「ぷにぷにアバター」の完成です。</p>
<p><img src="/1ed3534ac1987f6eda0d2a8c03b36229/20250402-%E3%81%B7%E3%81%AB%E3%81%B7%E3%81%AB-010-%E3%81%B7%E3%81%AB%E3%81%B7%E3%81%AB%E3%82%A2%E3%83%90%E3%82%BF%E3%83%BC.webp" alt="VRChat内で動く完成したオリジナルぷにぷにアバター"></p>
<p>さあ、あなたの分身をVRChatの世界で歩き回らせてみましょう。</p>
<p>:::post-link{url="<a href="https://rio3d.booth.pm/items/6556549">https://rio3d.booth.pm/items/6556549</a>" text="[3Dモデル] #ぷにぷにあばたー ﾅｷﾞﾁｬﾝ (BOOTH)"}</p>
<p>:::ad</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/1ed3534ac1987f6eda0d2a8c03b36229/20250402-%E3%81%B7%E3%81%AB%E3%81%B7%E3%81%AB-010-%E3%81%B7%E3%81%AB%E3%81%B7%E3%81%AB%E3%82%A2%E3%83%90%E3%82%BF%E3%83%BC.webp" medium="image"/></item><item><title><![CDATA[CursorでClaude APIを連携する方法：プレミアムモデル制限後の設定手順]]></title><description><![CDATA[プレミアムモデル500回上限に達した際のClaude API連携 設定手順ガイド]]></description><link>https://uhiyama-lab.com/ja/blog/dialy/cursor-claude-api-setup/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/dialy/cursor-claude-api-setup/</guid><category><![CDATA[cursor]]></category><pubDate>Thu, 20 Mar 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>最近、多くの開発者の間で話題のAIコードエディタ「<a href="https://www.cursor.com/ja">Cursor</a>」。その便利さから、私も遅ればせながら利用を開始しました。</p>
<p><img src="/b69dec85557d8b0ea98f5ba92dc3fa31/20250320-Cursor-Claude-API_00.png" alt="Cursorエディタのスクリーンショット"></p>
<p>これまでChatGPT ProやClaude 3などを活用し、実装速度が既に10倍向上したと感じていましたが、Cursorを導入したことで、<strong>さらに10倍、つまり従来の100倍近いスピードアップ</strong>を実感しています。すっかり「Cursor最高！」という状態です。</p>
<p>Cursorの最大の魅力は、エディタ内で直接AIと対話できる点です。コーディング中に「このコンポーネント群をリファクタリングして」とか、コンソールのエラーを貼り付けて「このエラーの原因と解決策を教えて」といった質問がその場で可能です。AIによるコード変更は、Gitのように差分が色分け表示されるため、意図しない修正を簡単に拒否できます。不足パッケージのインストールやファイルの削除も、ユーザーが「Accept」ボタンを押すことで初めて実行されるので安心です。</p>
<p>これらの機能はWeb版のChatGPTやClaudeでも可能ですが、Cursorはプロジェクト全体のファイル構造や依存関係をより深く理解した上で提案してくれるため、修正の精度が高く、開発体験が非常に快適です。</p>
<p>また、Web版のAIツールでは長い対話や大量のコード生成で応答が不安定になったり途切れたりすることがありますが、Cursorでは膨大な量の修正も一気に実行してくれます。</p>
<p>稀に（特に高性能モデルで）意図しない過度な修正が行われることもありますが、「Restore」機能で直前の指示時点まで簡単にファイル状態を戻せるため、安心して試行錯誤できます。（ただし、削除されたフォルダの復元は難しい場合があるので、Accept前のコードレビューは重要です）。</p>
<p>さて、ここからが本題です。</p>
<p><img src="/a800d81d00acb4119996a5428fa3b2aa/20250320-Cursor-Claude-API_01.png" alt="Cursorのプレミアムモデル使用回数制限の表示"></p>
<p>CursorのProプランには、高性能な<strong>プレミアムモデル</strong>を高速（Fast）で利用できる回数に、<strong>月500回</strong>という制限があります。</p>
<p>これは、AIへの指示（リクエスト）を月に500回まで高速処理できるチケットのようなものです。通常の使い方であれば十分な回数ですが、私のようにCursorの便利さに夢中になり、ツール開発や大規模リファクタリングに没頭すると、数日で上限に達してしまうこともあります。</p>
<p>上限を超えると、低速（Slow）モードでの利用は可能ですが、一度高速モードの快適さを知ってしまうと、なかなか耐えられません。</p>
<p>Cursorでは「プレミアムモデルの利用回数を増やす」という直接的なプランは提供されていません。上限を超えて高速モードを維持したい場合は、ユーザー自身がOpenAIやAnthropicなどの<strong>APIキーを設定し、従量課金で利用する</strong>必要があります。</p>
<p>この記事では、「CursorでClaude APIを連携したい」と考えている方に向けて、<strong>Anthropic ConsoleでClaude APIキーを取得し、クレジットをチャージして、Cursorに設定するまでの具体的な手順</strong>を分かりやすく解説します。</p>
<p>:::ad</p>
<hr>
<p><strong>この記事の内容</strong></p>
<ol>
<li><a href="#cursor-premium">Cursorのプレミアムモデルとは？制限と高速利用の仕組み</a></li>
<li><a href="#api-setup">Anthropic APIキーの設定手順（アカウント作成からクレジット購入まで）</a></li>
<li><a href="#cursor-connect">CursorとClaude APIを連携させる具体的な方法</a></li>
<li><a href="#api-combination">API連携（従量課金）の仕組みと注意点</a></li>
<li><a href="#cost-performance">Claude API連携後の実際のコスト感と代替案</a></li>
<li><a href="#summary">まとめ：CursorとClaude API連携で開発効率を維持</a></li>
<li><a href="#slowmode">追記：意外と使える？SlowModeという選択肢</a></li>
<li><a href="#gemini-option">追記2：SlowModeの実情とGemini 2.5 Proという強力な選択肢 (2025年4月)</a></li>
</ol>
<hr>
<h2>Cursorのプレミアムモデルとは？制限と高速利用の仕組み</h2>
<p>Cursorには複数のAIモデルが組み込まれていますが、その中でも特に高性能なモデル群が「<a href="https://docs.cursor.com/settings/models">プレミアムモデル</a>」と呼ばれています。代表的なものとしては以下が挙げられます。</p>
<ul>
<li><strong>Claude 3.5 Sonnet</strong>（Anthropic社製）</li>
<li><strong>GPT-4 / GPT-4o</strong>（OpenAI社製）</li>
<li>その他、最新の高性能モデル</li>
</ul>
<p>これらのプレミアムモデルは、複雑なコード生成、高度なデバッグ支援、的確な質疑応答に優れており、標準モデルと比較して高い精度を発揮します。</p>
<p>Cursorの有料プラン「Proプラン」（月額$20、日本円で約3,000円 ※為替レートにより変動）では、これらのプレミアムモデルを**「fast（高速）」モードで月500回まで**利用できます。fastモードでは、AIへのリクエストが優先的に処理され、待ち時間なくスムーズに開発作業を進められます。</p>
<p>この月500回の制限を超過すると、自動的に**「slow（低速）」モード**へと切り替わります。slowモードでは、AIの応答に時間がかかるようになり、特にサーバー混雑時には数秒から数分の待機時間が発生することもあります。</p>
<p>しかし、Cursorの設定で**「Enable usage-based pricing（従量課金を有効にする）」**オプションをオンにすることで、500回を超えてもfastモードを維持できます。この場合、利用したAPIのトークン量（処理したテキストの量）に応じて、連携したAPIサービス（今回はAnthropic）に追加料金が発生する仕組みです。</p>
<p><img src="/6f5f9bf38446587643cf6bdf49f79f73/20250320-Cursor-Claude-API_02.png" alt="Cursorの従量課金設定画面"></p>
<p>「Enable usage-based pricing」を有効にすると、上記のような設定項目が表示されます。</p>
<p>ここで重要なのが**「Monthly Spending limit」**（月間支出上限）の設定です。これは、従量課金で発生するAPI利用料金が設定した上限額に達した場合、Cursor側で自動的にAPI利用を停止してくれる安全機能です。あくまでAPI利用料金の上限設定であり、CursorのProプラン料金（$20）とは別に、API連携先（AnthropicやOpenAI）へ直接API利用料金を支払う必要がある点に注意しましょう。</p>
<p><img src="/ea9ec90ec7050803c9d25be5e1aa1b6d/20250320-Cursor-Claude-API_03.png" alt="CursorでのAIモデル利用履歴"></p>
<p>なお、Cursorの設定画面下部では、どのモデルを何回利用したかの詳細な履歴を確認できます。</p>
<p>「Included in Pro」と表示されているのが、Proプランに含まれる月500回のプレミアムモデル利用分です。一方、「User API Key」と表示されているのが、今回設定するような<strong>ユーザー自身のAPIキー（Claude APIなど）を利用した分</strong>、つまり従量課金が発生する利用分です。</p>
<p>履歴を見ると、「Aborted, Not Charged」（中断、課金なし）や「Errored, Not Charged」（エラー、課金なし）といった項目があることがわかります。これは、処理が途中でキャンセルされたり、エラーで完了しなかったりした場合には、API利用料金が発生しないことを示しており、利用者にとっては嬉しいポイントです。</p>
<p>:::ad</p>
<h2>Anthropic APIキーの設定手順（アカウント作成からクレジット購入まで）</h2>
<p>それでは、CursorでClaude 3.5 SonnetなどのAnthropic社製モデルを従量課金で利用するために必要な、<strong>Anthropic APIキーの設定手順</strong>をステップごとに解説します。</p>
<h3>ステップ1：Anthropic Consoleでアカウントを作成する</h3>
<ul>
<li>まず、Anthropicの公式サイト（<a href="https://console.anthropic.com">console.anthropic.com</a>）にアクセスします。</li>
<li>「Sign Up」ボタンをクリックし、メールアドレスとパスワードを設定してアカウントを作成します。その後、登録したメールアドレスに届く認証メールを確認し、認証を完了させます。</li>
<li>ログイン後、必要に応じて氏名や組織名などの基本情報を入力します。</li>
</ul>
<h3>ステップ2：APIキーを取得する</h3>
<ul>
<li>ログイン後、画面左側のメニューから「API Keys」を選択します。</li>
<li>「Create Key」ボタンをクリックします。</li>
<li>APIキーを識別するための名前を入力します（例：「Cursor-Integration」など、分かりやすい名前を推奨）。</li>
<li>APIキーが生成され、画面に表示されます。このキーは<strong>一度しか表示されない</strong>ため、必ずコピーしてパスワードマネージャーなど安全な場所に保管してください。</li>
</ul>
<p><img src="/039505ae0c9587a8b2dafdd492c6fa29/20250320-Cursor-Claude-API_04.png" alt="Anthropic ConsoleでのAPIキー作成画面"></p>
<h3>ステップ3：支払い情報を設定し、クレジットを購入する</h3>
<ul>
<li>左側メニューの「Plans &#x26; Billing」に移動します。</li>
<li>APIを利用するには、あらかじめクレジットを購入しておく必要があります。「Add Payment Method」をクリックし、クレジットカード情報を登録します。</li>
<li>次に、「Purchase credits」セクションでクレジットを購入します。お試しであれば、最低購入額の**$5**（日本円で約750円程度 ※レートにより変動）からで十分でしょう。</li>
<li>クレジットが一定額を下回った際に自動で追加購入（チャージ）する「Auto-reload」機能もありますが、意図しない課金を防ぐため、<strong>最初は手動（OFF）にしておく</strong>ことをお勧めします。</li>
</ul>
<p><img src="/474b5f35a1c79d143093f786694ca52c/20250320-Cursor-Claude-API_05.png" alt="Anthropic Consoleのクレジット購入画面"></p>
<p><img src="/d5b00c876975b0608ffe1cb59c51da39/20250320-Cursor-Claude-API_06.png" alt="Anthropicのクレジット購入ボタン"></p>
<p>無事にクレジットの購入が完了すると、ダッシュボードに残高（例：$5.00）が表示されます。今後、Cursor経由でClaude APIを利用するたびに、ここから利用料金が差し引かれていきます。</p>
<p><img src="/bb5a02d6095a6f572ee108b396a67af9/20250320-Cursor-Claude-API_07.png" alt="Anthropicの自動リチャージ設定画面"></p>
<p>「Auto Reload」設定では、「残高が$5を下回ったら、自動的に$10をチャージする」といった設定が可能です。頻繁にAPIを利用し、手動でのチャージが面倒な場合はONにすると便利ですが、まずは利用状況を見てから判断するのが良いでしょう。</p>
<p>:::ad</p>
<h2>CursorとClaude APIを連携させる具体的な方法</h2>
<p>Anthropic Consoleでの準備が整ったら、いよいよCursorとClaude APIを連携させます。手順は非常に簡単です。</p>
<h3>ステップ1：CursorにAnthropic APIキーを登録する</h3>
<ul>
<li>Cursorエディタを開き、メニューバー（または設定画面）から「Settings」>「Configure models」へと進みます。</li>
<li>設定項目の中から「Anthropic API Key」という欄を見つけます。</li>
<li>先ほどAnthropic Consoleで取得し、安全な場所に保存しておいた<strong>APIキーをこの欄に貼り付け</strong>ます。</li>
<li>設定を保存するために、「Save」や「Apply」といったボタンをクリックします（UIはバージョンによって若干異なる場合があります）。</li>
</ul>
<p><img src="/1e1e93bc17de2c6da7a31bc7994ee76a/20250320-Cursor-Claude-API_08.png" alt="Cursorの設定画面でのAnthropic APIキー入力欄"></p>
<h3>ステップ2：連携を確認し、利用を開始する</h3>
<ul>
<li>設定が完了したら、実際にCursorでAI機能（コード生成、チャットなど）を使ってみましょう。特に、プレミアムモデル（Claude 3.5 Sonnetなど）を選択して試してみてください。</li>
<li>もし既にProプランの月500回制限を超えている場合は、このAPI連携によって再び「fast」モードで応答が返ってくるはずです。</li>
<li>念のため、Anthropic Consoleの「Usage」や「Billing」ページで、トークン使用量とそれに応じたコストが発生しているかを確認すると確実です。</li>
</ul>
<p>これで、CursorでClaude APIを利用するための連携設定は完了です。先述のCursorの利用履歴画面で「User API Key」としての利用が記録されていれば、正しく連携されています。</p>
<h2>API連携（従量課金）の仕組みと注意点</h2>
<p>初めてClaude APIのような外部APIと連携して従量課金サービスを利用する際に、知っておくべき基本的な仕組みと注意点をまとめました。</p>
<ul>
<li><strong>従量課金の仕組み</strong>：Cursor経由でClaude APIを利用するたびに、処理したテキスト量（トークン数）に応じて料金が発生し、Anthropic Consoleで購入したクレジット残高から自動的に差し引かれます。</li>
<li><strong>残高不足に注意</strong>：クレジット残高がなくなると、CursorからAPIを利用できなくなる可能性があります（fastモードが使えなくなる、またはエラーになる）。定期的にAnthropic Consoleで残高を確認しましょう。</li>
<li><strong>自動リチャージ（Auto Reload）</strong>：前述の通り、この機能はデフォルトでOFFになっています。ONにすると、残高が指定額以下になった際に自動でクレジットカードから追加チャージされるため便利ですが、予期せぬ高額請求を防ぐため、利用状況を把握するまではOFFのままか、慎重に設定額を決めることをお勧めします。</li>
<li><strong>コスト意識を持つ</strong>：API連携は技術的に簡単ですが、「使った分だけお金がかかる」という従量課金の性質を常に意識することが重要です。特に、大規模なコード生成や複数ファイルにまたがるリファクタリングを頻繁に行うと、想像以上にクレジットを消費する可能性があります。Anthropic Consoleで使用状況を定期的にモニタリングし、コストを把握しましょう。</li>
</ul>
<p>これらの点を理解しておけば、安心してCursorとClaude APIの連携を活用できるはずです。</p>
<p>:::ad</p>
<h2>Claude API連携後の実際のコスト感と代替案</h2>
<h3>API連携後の実際のコスト感：クレジットはどれくらい持つの？</h3>
<p>実際にCursorとClaude APIを連携させて使ってみた感想として、気になるのは「$5のクレジットでどれくらい使えるのか？」という点だと思います。</p>
<p>私の使い方（複数ファイルにまたがるリファクタリングや、やや複雑なコード生成を頻繁に依頼する）では、$5のクレジットは思ったよりも早く消費される印象でした。感覚的には、<strong>集中的に使うと50回程度のリクエストで$5を使い切ってしまう</strong>こともありました。</p>
<p>これはかなりヘビーな使い方の場合です。もしProプランの月500回を使い切ったのと同じペースでAPI連携（従量課金）を500回利用した場合、API利用料金だけで$50（日本円で約7,500円程度）かかってしまう計算になります。もちろん、簡単な質問や短いコードの修正程度であれば、もっと少ないコスト（数百円程度）で済むでしょう。（そもそも、そのようなライトな使い方であれば、Proプランの月500回制限を超えることは稀かもしれません）。</p>
<h3>コストを抑えるための代替案</h3>
<p>Claude API連携のコストが思ったより高いと感じる場合、以下の代替案も検討に値します。</p>
<ul>
<li><strong>複数Cursorアカウント戦略</strong>：やや裏技的ですが、もう一つ別のアカウントでCursor Proプランを契約すれば、合計で月1,000回のプレミアムモデル高速利用枠を確保できます。API利用料金によっては、こちらの方が安上がりになる可能性があります（月額$20×2 = $40）。</li>
<li><strong>他のAIコードエディタやAPIの活用</strong>：例えば、最近注目されている「<a href="https://www.cline.ai/">Cline</a>」のようなエディタでは、Claude APIよりも利用料金が格段に安いとされる「Deepseek」モデルなどを利用できる場合があります。定型的なコード生成やリファクタリングであれば、これらのモデルでも十分なケースがあり、コスト効率が良い可能性があります。</li>
<li><strong>ハイブリッド戦略（モデルの使い分け）</strong>：もし別途ChatGPT PlusやClaude Proなどを契約している場合、複雑な仕様の検討や高度な相談はそちらで行い、具体的なコーディング作業をCursor（や他のエディタ）の低コストなAPIで行う、という使い分けも有効です。例えば、Claudeで処理の方向性を固め、その指示をDeepseek API連携したエディタに渡して実装を進める、といった方法で、精度とコストのバランスを取れるかもしれません。</li>
</ul>
<p>これらの代替案も視野に入れ、ご自身の開発スタイルや予算に合った最適な方法を見つけることが重要です。</p>
<h2>まとめ：CursorとClaude API連携で開発効率を維持</h2>
<p>この記事では、AIコードエディタCursorのProプランにおけるプレミアムモデルの月500回制限に達した後も、<strong>Claude APIと連携することで高速な応答性能（fastモード）を維持し、開発効率を落とさずに作業を続ける方法</strong>について解説しました。</p>
<p>Anthropic Consoleでのアカウント作成、APIキー取得、クレジット購入、そしてCursorへの設定手順を具体的に紹介しました。また、API連携（従量課金）の仕組みや注意点、実際のコスト感、そしてコストを抑えるための代替案についても触れました。</p>
<p>プログラミング作業の効率をさらに高めたい方、特にAIアシスタントをコーディングに積極的に活用したい開発者にとって、CursorとAPIの連携は試してみる価値のある選択肢です。ご自身の利用頻度や予算に応じて、今回紹介した方法や代替案を組み合わせ、最適な開発環境を構築してみてください。</p>
<p>:::ad</p>
<h2>追記：意外と使える？SlowModeという選択肢</h2>
<p>この記事を公開した後もCursorを継続して利用していますが、一つ新たな発見がありました。試しにAPI連携（従量課金）をOFFにし、プレミアムモデルの制限を超えた状態、つまり**「SlowMode」**でしばらく使ってみたのです。</p>
<p>その結果、「<strong>あれ？SlowModeでも意外と実用的なのでは？</strong>」と感じました。</p>
<p>多くのAIツールにおける「SlowMode」は、応答が非常に遅く、実用に耐えないことが多い印象ですが、CursorのSlowModeは、少なくとも私が試した限りでは、<strong>FastModeとの体感差がそれほど大きくありませんでした</strong>。</p>
<p>もちろん、SlowModeでも生成されるコードの品質や精度はFastModeと基本的に同等です。そのため、API連携による従量課金に切り替える前に、<strong>一度SlowModeを試してみて、その速度が本当にストレスに感じるかどうかを確認する</strong>価値は大いにありそうです。</p>
<p>ただし、時間帯やCursorサーバー側の負荷状況によっては、SlowModeの応答速度が低下する可能性は否定できません。</p>
<p>そこから考えられるのは、「<strong>普段はSlowModeで利用し、応答が遅いと感じた時だけ一時的に従量課金をONにする</strong>」という運用です。これにより、API利用料金を大幅に節約できる可能性があります。</p>
<p>結論として、Cursor Proプランの月500回制限を超えたからといって、必ずしもすぐにAPI連携（従量課金）に移行する必要はなく、「SlowModeで様子を見る」という選択肢も有効である、というのが追記情報です。</p>
<hr>
<h2>追記2：SlowModeの実情とGemini 2.5 Proという強力な選択肢 (2025年4月)</h2>
<p>前回の追記で「SlowModeでも意外と使える」とお伝えしましたが、その後の利用で状況が変化しました。ある日、<strong>SlowModeでの応答に30秒以上かかる</strong>というケースに遭遇したのです。やはり、時間帯やサーバーの混雑状況によって、SlowModeの応答速度は大きく変動するようです。</p>
<p>この時は、前回の追記に書いた通り、一時的にClaude APIの従量課金をONにすることで、快適な高速応答に戻すことができました。SlowModeが遅いと感じた際の回避策として、従量課金オプションは有効です。</p>
<p>しかし、Cursor ProのFastモード（月500回）を使い切った後の選択肢は、SlowModeの我慢やClaude API連携（従量課金）だけではありません。<strong>最近、非常に強力な新しい選択肢が登場しました！</strong></p>
<p>それが、Googleが2025年3月25日に発表した最新の大規模言語モデル「<strong>Gemini 2.5 Pro Experimental</strong>」です。この発表直後から大きな注目を集めている高性能モデルが、なんと<strong>Cursorのエディタ内で利用可能</strong>になりました！</p>
<p>実際にいくつかのプロジェクトで試してみたところ、その性能は驚くほど高く、<strong>Claude 3.5 SonnetやGPT-4oといった既存の高性能モデルと比較しても遜色ない、あるいは場面によってはそれ以上</strong>と感じています。特に、Gemini 2.5 Proの最大の特徴である<strong>広大なコンテキストウィンドウ</strong>（一度に処理・記憶できる情報量）のおかげか、複数ファイルにまたがるような大規模なリファクタリング作業も、非常にスムーズかつ的確にこなしてくれます。</p>
<p>そして、現時点（2025年4月3日）で最も注目すべき点は、この**「Gemini 2.5 Pro Experimental」モデルが、Cursor上で無料で利用できる**ということです！</p>
<p>もしあなたがCursor ProプランのFastモード500回を使い切ってしまい、応答速度に不満を感じているなら、<strong>まずはClaude APIの従量課金や他の有料オプションを検討する前に、Cursorのモデル設定で「Gemini 2.5 Pro Experimental」を選択し、試してみることを強く推奨します。</strong></p>
<p>無料で利用できるにも関わらず、非常に高い性能を持っているため、多くの場合、このモデルで十分に満足できる可能性があります。ぜひ、この新しい選択肢を活用してみてください。</p>
<p>:::ad</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/a800d81d00acb4119996a5428fa3b2aa/20250320-Cursor-Claude-API_01.png" medium="image"/></item><item><title><![CDATA[UI設計用のダミー画像が欲しくて『Sample Image Generator』を作った]]></title><description><![CDATA[Webやゲーム開発でUI設計する時、仮置き用のダミー画像が欲しいことってありますよね。サイズ・背景色・テキストを指定してサンプル画像を即座に作成できる『Sample Image Generator』を開発しました。]]></description><link>https://uhiyama-lab.com/ja/blog/tooldev/sample-image-generator/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/tooldev/sample-image-generator/</guid><pubDate>Wed, 12 Mar 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Webやゲーム開発でUI設計する時、「とりあえず仮置きの画像が欲しい」ってことありますよね。ワイヤーフレームやプロトタイプを作ってる段階で、適当なサイズのダミー画像が必要になります。</p>
<p>でも、わざわざ画像編集ソフトを開いて作るのも面倒だし、フリー素材を探すのも時間がかかります。「サイズと色を指定するだけで、パッと作れたらいいのに」と思って、ブラウザでサンプル画像を生成できるツールを開発しました。</p>
<p><img src="/0783a449011b4a00f9fcf65c5c970680/20250312_sampleImageGenerator-thumb.png" alt=""></p>
<p>サイズ・背景色・テキストを指定するだけで、即座にPNG画像をダウンロードできます。</p>
<p>:::post-link{url="/ja/tools/sample-image-generator/" text="▶︎ Sample Image Generator"}</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#overview">ダミー画像が必要だった話</a></li>
<li><a href="#features">できること</a></li>
<li><a href="#usage">使い方</a></li>
<li><a href="#format">対応フォーマット</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>ダミー画像が必要だった話 {#overview}</h2>
<p>UI設計してる時、画像の仮置きが必要になる場面って結構ありますよね。ワイヤーフレームやプロトタイプを作ってる段階では、実際の画像はまだないことが多いです。</p>
<p>でも、わざわざ画像編集ソフトを開いて作るのも面倒。「800x600の灰色の画像」みたいな、シンプルなダミー画像をパッと作りたいだけなんです。</p>
<p>そんなわけで、ブラウザでサイズ・背景色・テキストを指定するだけで、PNG画像を即座に生成できるツールを作りました。</p>
<p>例えばこんな画像を作れます。</p>
<p><img src="/b0d958d7572ce15cc26dff0ff3071795/sample-image-800x600-1.png" alt=""></p>
<p><img src="/d6649b91e0787b52f6cadcbfcbfc2f42/sample-image-768x1500-1.png" alt=""></p>
<p><img src="/d1b0683cdd7ec48269ec72d7cbbdfc47/sample-image-2000x768-1.png" alt=""></p>
<h2>できること {#features}</h2>
<ul>
<li>サイズ・背景色・テキスト色・文言を細かく指定</li>
<li>テキスト非表示オプション</li>
<li>テキストサイズを自動調整して、中央に配置</li>
<li>生成画像を即座にプレビュー・ダウンロード</li>
<li>プリセットサイズやプリセットカラーを多数収録</li>
</ul>
<h2>使い方 {#usage}</h2>
<p><img src="/6b2922ba10087408e98753059d93ad41/sample-image-generator-steps.png" alt=""></p>
<ol>
<li>
<p><strong>サイズ設定</strong>
プリセットから選ぶか、幅・高さを数値で直接入力します。</p>
</li>
<li>
<p><strong>背景色設定</strong>
プリセットから選ぶか、カラーパレットから自由な色を指定します。</p>
</li>
<li>
<p><strong>テキスト設定</strong>
表示のオン/オフ、文字列を指定できます。必要に応じてテキスト色を変更しましょう。</p>
</li>
<li>
<p><strong>プレビュー &#x26; ダウンロード</strong>
設定を変えるとプレビューが自動更新されます。ダウンロードボタンでPNGファイルを取得できます。</p>
</li>
</ol>
<h2>対応フォーマット {#format}</h2>
<p>現在はPNG形式の書き出しに対応しています。ブラウザがcanvas機能をサポートしていれば動作するため、デスクトップ/モバイルを問わず利用できます。</p>
<p>:::post-link{url="/ja/tools/sample-image-generator/" text="▶︎ Sample Image Generator"}</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/0783a449011b4a00f9fcf65c5c970680/20250312_sampleImageGenerator-thumb.png" medium="image"/></item><item><title><![CDATA[出来るだけ公平なゲームマップを自動生成する「カタン・ボードジェネレーター」を作ってみた]]></title><description><![CDATA[カタンにハマって、ボード配置の偏りが気になったので『カタン・ボードジェネレーター』を開発。資源と数字の偏りを解消して、公平なゲームマップを瞬時に生成できます。]]></description><link>https://uhiyama-lab.com/ja/blog/tooldev/catan-board-generator/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/tooldev/catan-board-generator/</guid><pubDate>Sat, 08 Mar 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>世界的に人気なボードゲーム「カタン(Catan)」をご存知ですか？</p>
<p>普段ボードゲームを遊ばない私ですが、年始に旧友らとプレイして、気がついたらドハマリしていました。</p>
<p><img src="/0b248b47e634e1e019687f9fc6dcc7ec/catan-board-8.png" alt=""></p>
<p>カタンは19枚の資源タイルを組み合わせたボード上で、プレイヤーが拠点を配置し、毎ターンダイスを2個振って、その合計値に対応したタイルに隣接する拠点を持つプレイヤーが資源を獲得するシンプルなルールです。</p>
<p>資源を使って「街道」を建て、その街道沿いに新しい拠点を建てることで、より多くの資源を獲得できるようになります。</p>
<p>ここで重要になってくるのが、タイルの配置方法と数字の配分です。同じ種類の資源が隣接していたり、期待値が高い数字(6と8)が隣接していると、特定プレイヤーが極端に資源を獲得してゲームバランスが崩れます。</p>
<p>より公平なゲームにするためには、確率が極端に偏らないように、慎重にタイルを配置する必要があります。</p>
<p>そこで今回は、数学的に「公平なタイル配置」を瞬時に生成できる「カタン・ボードジェネレーター」を作成してみました。</p>
<p><strong>【2025年6月更新】</strong> v2.1.0にて<strong>資材バランスの視覚化</strong>と<strong>生成精度の向上</strong>を実現！v2.0.0で実装した拡張版（5-6人用）対応とUI改善に加え、より高品質なボード生成が可能になっています。</p>
<p>:::post-link{url="/ja/tools/catan-board-generator" text="▶︎ Catan Board Generator"}</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#overview">カタン・ボードジェネレーターとは</a></li>
<li><a href="#features">特徴・主な機能</a></li>
<li><a href="#usage">使用方法</a></li>
<li><a href="#rules">配置ルールの詳細</a></li>
<li><a href="#v2-0-0">v2.0.0 拡張版対応(2025/06/07)</a></li>
<li><a href="#v2-1-0">v2.1.0 公平精度向上+資材バランス視覚化(2025/06/12)</a></li>
<li><a href="#v2-2-0">v2.2.0 バランス崩壊オプション(2025/06/14)</a></li>
<li><a href="#conclusion">まとめ</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>カタン・ボードジェネレーターとは {#overview}</h2>
<p><img src="/d6b7d77209c27bff6db53a497495a21d/20250614-catan-board-generator2.1.0.webp" alt=""></p>
<p>このツールは、カタン（Catan）のボードを自動で生成するWebアプリです。
一度に「資源タイル」と「数字チップ」をランダム配置できるほか、数字だけを再シャッフルすることも可能です。
公式ルールで推奨される「6と8が隣接しないようにする」「同じ資源が連続しすぎない」などを考慮しつつ、ある程度バランスを取った配置を目指しています。
自分好みの盤面ができたら、画像として保存しておくこともできます。</p>
<p><strong>v2.0.0では通常版（3-4人用）に加えて拡張版（5-6人用）にも対応し、UIも大幅に改善しました。</strong></p>
<p>:::post-link{url="/ja/tools/catan-board-generator" text="▶︎ Catan Board Generator"}</p>
<h2>特徴・主な機能 {#features}</h2>
<ul>
<li>通常版・拡張版の両対応 – 3-4人用（19タイル）と5-6人用（30タイル）を切り替え可能</li>
<li>資源タイルと数字チップの自動シャッフル</li>
<li>「数字だけシャッフル」で資源配置はそのまま保持</li>
<li>6と8の隣接を厳格に回避</li>
<li>出現確率の偏りを抑えた高度なバランス調整</li>
<li>2カラムレイアウトで操作性が大幅向上</li>
<li>レスポンシブ対応 – PC、タブレット、スマホで最適表示</li>
<li>高解像度画像として保存が可能（2000x2000px）</li>
</ul>
<h2>使用方法 {#usage}</h2>
<ol>
<li><a href="/ja/tools/catan-board-generator" target="_blank">カタン・ボードジェネレーター</a> へアクセス</li>
<li><strong>ボードタイプを選択</strong> – 「通常版」または「拡張版（5-6人用）」を選択</li>
<li>「<strong>シャッフル</strong>」ボタンを押して、資源タイル＋数字チップをランダム配置</li>
<li>資源配置は気に入ったまま、数字だけ変えたい場合は「<strong>数字だけシャッフル</strong>」ボタン</li>
<li>画像として保存したい場合は「<strong>画像を保存</strong>」ボタン</li>
</ol>
<p><img src="/52f9866d131de98a7e1637d11b4f3fdc/20250607-catan-board-generator-settings.png" alt=""></p>
<h2>配置ルールの詳細 {#rules}</h2>
<p>このツールは公式ルールを基盤としつつ、より公平で戦略的なゲーム展開を目指すための独自のバランス調整を加えています。</p>
<h3>【基本的な配置ルール】</h3>
<ul>
<li><strong>資源の隣接禁止：</strong> 同じ種類の資源タイル（森、牧草地、丘陵、山地、畑）は隣接しません</li>
<li><strong>特定数字の隣接禁止：</strong> 高確率の「6」と「8」、および低確率の「2」と「12」は隣接しないように配置されます</li>
<li><strong>同じ数字の重複禁止：</strong> 同じ資源タイプに同じ数字が複数割り当てられることはありません</li>
</ul>
<h3>【高度なバランス調整】</h3>
<ul>
<li><strong>高確率数字の分散：</strong> 高確率の「6」「8」が特定の資源に集中しないよう、各資源タイプにバランス良く割り当てられます</li>
<li><strong>交差点の期待値均一化：</strong> 3つのタイルが接する「交差点」の価値（3タイルの数字の出やすさの合計）が極端に低い場所が固まらないように調整します</li>
<li><strong>資源ごとの数字品質：</strong> 特定の資源に悪い数字（2, 3, 11, 12など）ばかりが集まらないようにし、各資源に最低限の生産期待値を保証します</li>
<li><strong>確率分散の最適化：</strong> 各資源タイプの生産確率が理想的な比率に近づくよう微調整されます</li>
</ul>
<h3>【表示について】</h3>
<ul>
<li><strong>数字の確率：</strong> 数字チップ下の「●」の数は、その数字の出やすさ（36通り中の確率）を表しています</li>
<li><strong>高確率の強調：</strong> 特に確率の高い「6」と「8」は、赤色で表示されます</li>
<li><strong>視認性：</strong> 各タイルは資源に応じた色分けで、一目でボード構成が把握できます</li>
</ul>
<h2>v2.0.0 拡張版対応(2025/06/07) {#v2-0-0}</h2>
<p><img src="/0a86d5f390d8162a8d4a686d1df547d6/20250607-catan-board-generator.webp" alt=""></p>
<p>2025年6月のアップデートで、<strong>v2.0.0</strong>をリリースしました。主な改善点は以下の通りです：</p>
<h3>拡張版の特徴</h3>
<ul>
<li><strong>タイル数：</strong> 19枚 → 30枚に増加</li>
<li><strong>資源構成：</strong> 各資源が追加され、より多様な戦略が可能</li>
<li><strong>数字チップ：</strong> 各数字が追加され、より多くの選択肢</li>
<li><strong>砂漠タイル：</strong> 1枚 → 2枚に増加</li>
</ul>
<h3>その他の改善点</h3>
<ul>
<li><strong>2カラムレイアウト：</strong> PC画面ではコントロールパネルとボード表示を分離し、操作性を大幅向上</li>
<li><strong>レスポンシブデザイン：</strong> デバイスサイズに応じてレイアウトを最適化</li>
<li><strong>動的パラメータ調整：</strong> ボードサイズに応じて生成アルゴリズムのパラメータを自動調整</li>
<li><strong>探索空間の最適化：</strong> 拡張版では探索ステップ数を2.3倍に増加し、安定した生成を実現</li>
<li><strong>パフォーマンス改善：</strong> 座標計算のキャッシュ化とアルゴリズムの最適化</li>
<li><strong>エラーハンドリング：</strong> 生成失敗時の適切なフィードバックとリトライ機能</li>
</ul>
<p>これにより、<strong>通常版と同等の品質</strong>で拡張版のボードを生成できるようになりました。</p>
<h2>v2.1.0 公平精度向上+資材バランス視覚化(2025/06/12) {#v2-1-0}</h2>
<p><img src="/d6b7d77209c27bff6db53a497495a21d/20250614-catan-board-generator2.1.0.webp" alt=""></p>
<p>2025年6月12日のアップデートで、<strong>v2.1.0</strong>をリリースしました。主な改善点は以下の通りです：</p>
<ul>
<li><strong>資材バランスの視覚化：</strong> 「資源の出やすさ」を示す統計情報セクションを追加し、各資源の生産期待値をスコア化して表示</li>
<li><strong>公平性の向上：</strong> 資源ごとの合計生産期待値に下限値を設定（通常版:11, 拡張版:16）し、極端に不利な資源が生まれないよう調整</li>
<li><strong>処理の高速化：</strong> Web Workerを導入し、ボード生成中の計算処理を高速化・安定化</li>
</ul>
<p>これらの改善により、生成されたボードの品質をより正確に評価できるようになり、より公平で戦略的なゲーム展開が可能になりました。</p>
<h2>v2.2.0 バランス崩壊オプション(2025/06/14) {#v2-2-0}</h2>
<p><img src="/9a1140a676469f5bc45f3b9376036c89/20250614-v2.2.0-%E3%83%90%E3%83%A9%E3%83%B3%E3%82%B9%E5%B4%A9%E5%A3%8A%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3.webp" alt=""></p>
<p>2025年6月14日のアップデートで、v2.2.0をリリースしました。主な改善点は以下の通りです：</p>
<ul>
<li><strong>バランス崩壊オプション:</strong> あえて資源や数字の偏りを許容する「バランス崩壊オプション」を新設。デフォルトでは最も公平なボードが生成されますが、このオプションを有効にすることで、特定資源や数字が極端に偏る"カオスなボード"も作成可能になりました。</li>
<li><strong>資源期待値のバランス:</strong> 各資源の生産期待値の下限を調整し、偏りの度合いを選択可能に。</li>
<li><strong>資源の隣接制限:</strong> 同じ資源が隣接しないよう制限するか選択可能に。</li>
<li><strong>数字の隣接制限:</strong> 高確率数字や低確率数字が隣接しないよう制限するか選択可能に。</li>
</ul>
<p>これにより、通常のバランス重視プレイはもちろん、ネタプレイや上級者向けの変則ルールにも対応できるようになりました。</p>
<p>「バランス崩壊オプション」を使って、あえて理不尽なボードや戦略性の高い盤面を楽しむこともできます。</p>
<h2>まとめ {#conclusion}</h2>
<p>この「カタン・ボードジェネレーター」を使えば、6と8が固まる問題や資源タイルが偏る問題を回避しつつ、短時間でボードレイアウトを決めることができます。</p>
<p>v2.0.0では拡張版対応により5-6人でのプレイにも対応し、UI改善によってより使いやすくなりました。v2.1.0では資材バランスの視覚化と生成精度の向上により、より公平で戦略的なボード生成が可能になっています。</p>
<p>ゲーム開始までの準備時間を大幅に減らして、いち早くプレイに集中したい時におすすめです。</p>
<h3>ご利用上の注意点</h3>
<p>当ツールは公平なゲーム体験を目指して設計されていますが、ボードの「最適さ」はプレイヤーの戦略や好みによっても異なります。生成ロジックは確率的な要素を含むため、最終的な盤面のバランスについては、ご自身の目で確認の上、ご活用ください。</p>
<p>:::post-link{url="/ja/tools/catan-board-generator" text="▶︎ Catan Board Generator"}</p>
<p>皆さんのカタンライフが、よりスムーズで楽しいものになりますように！</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/d6b7d77209c27bff6db53a497495a21d/20250614-catan-board-generator2.1.0.webp" medium="image"/></item><item><title><![CDATA[Synology NAS DS218playの故障したファンを交換してみた]]></title><description><![CDATA[SynologyNAS 218playの故障したファンを交換する手順]]></description><link>https://uhiyama-lab.com/ja/blog/dialy/synology-ds218play-fan-replacement/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/dialy/synology-ds218play-fan-replacement/</guid><category><![CDATA[hardware]]></category><pubDate>Tue, 11 Feb 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>映像制作やゲーム制作の膨大なデータを管理するため、いくつかの <strong>NAS(Network Atacched Storage)</strong> を運用しています。複数PC間で制作した素材を、ネットワーク経由で即時受け渡しすることができるので非常に便利です。</p>
<p>今回、その中でも古くから使用しているSynologyNAS DS218playのファンが定期的に止まるようになってしまったので、スペアパーツを購入して交換した手順を記録します。</p>
<hr>
<h2>NAS本体+スペアパーツ</h2>
<ul>
<li>NAS本体: <a href="https://amzn.to/419jDve">Synology NASキット 2ベイ DS218play クアッドコアCPU 1GBメモリ搭載 - Amazon</a></li>
<li>スペアファン: <a href="https://amzn.to/4jM6tvH">【Synologyスペアパーツ】SynologyNAS用交換用ファン FAN_92x92x25_1 [92mmファン 25mm厚] 国内正規代理店品</a></li>
</ul>
<hr>
<h2>発生した問題: ファンが停止&#x26;再起動を繰り返す</h2>
<p>今回発生したのは、SynologyNASの電源をいれてしばらくするとファンが停止し、数秒で復活して、再び停止するを繰り返す問題です。数分おきに下記のような警告が通知されました。</p>
<p><img src="/2e3fab709e8221962309d2a1e01e700c/001.png" alt=""></p>
<p>ファンの掃除などをしても問題が解決しなかったため、スペアパーツを購入。</p>
<hr>
<h2>スペアパーツへの交換</h2>
<p><img src="/df0d4f8ea21cefbeb5bbe117feb42f0b/002.jpeg" alt=""></p>
<p>届いたスペアファン。AmazonのSynology販売から購入しました。</p>
<p><img src="/6b17177cf67c9e94f6885f405b5bc9e7/003.jpeg" alt=""></p>
<p>SynologyNASのネジを外します。</p>
<p><img src="/1ddcb5e01c0345e2a66578218a4d5198/004.jpeg" alt=""></p>
<p>ファン周りの構造は非常にシンプルです。電源ケーブルを抜いて、銀ラベルを剥がします。</p>
<p><img src="/d5c7491c315bd564aea33ce07b9be973/005.jpeg" alt=""></p>
<p>SynologyNASのファンはモデルによって固定方法が異なります。</p>
<p>例えばDS218は背面からネジで止められていますが、DS218playはこのように内側のゴムリングで固定されています。</p>
<p>DS218playのシステムメンテナンスページは見つかりませんでしたが、下記からDS223jの交換手順が確認できます。DS218playとほぼほぼ同じ手順で交換できます。</p>
<p><a href="https://kb.synology.com/ja-jp/HIGs/DS223j_HIG/4">DS223j 製品マニュアル 4.1 故障したファンの交換 - Synologyナレッジセンター</a></p>
<p><img src="/3b1b0b5c4fdb543e3d8e3ca75080dc6b/006.jpg" alt=""></p>
<p>NASの底面の4箇所のネジを取り外して、指が届かない下部のゴムリングも外してスペアファンと交換します。</p>
<p><img src="/a3835931b8915b988faff8d9dd3254dc/007.jpg" alt=""></p>
<p>取り付け完了。これで正常に動作するようになりました。</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/083023f3bd887a9f79d91b64550c867e/20250211-Thumb_Synology%E3%83%95%E3%82%A1%E3%83%B3%E4%BA%A4%E6%8F%9B_%E3%82%B5%E3%83%A0%E3%83%8D%E3%82%A4%E3%83%AB.jpg" medium="image"/></item><item><title><![CDATA[学習のモチベーションが続かなくて『進捗チェックシートメーカー』を作った]]></title><description><![CDATA[分厚い参考書や長いオンラインコース、進捗が見えないとモチベーション保つの難しいですよね。紙に印刷してチェックを付けられる『進捗チェックシートメーカー』を開発しました。]]></description><link>https://uhiyama-lab.com/ja/blog/tooldev/tools-progress-checksheet/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/tooldev/tools-progress-checksheet/</guid><pubDate>Sun, 02 Feb 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>分厚い参考書や長いオンラインコース、学習を始めたはいいものの、なかなか最後までやり遂げられないことってありますよね。日々の進捗が見えにくいと、モチベーションを保つのが難しいです。</p>
<p>そんな時、アナログな「進捗チェックシート」が意外と効果を発揮します。紙に印刷して、自分の手でチェックを付けていくことで、「今日も頑張った！」「あと少しだ！」って実感できるんです。</p>
<p>そんなわけで、ブラウザで簡単にチェックシートを作成して、印刷できる『進捗チェックシートメーカー』を開発しました。</p>
<p><img src="/8945c1e078474d4cde312740b7cb1e23/progress-checksheet.png" alt=""></p>
<p>資格試験の勉強、読書記録、オンラインコースの受講管理など、いろんな目標達成に使えます。</p>
<p>:::post-link{url="/ja/tools/progress-checksheet/" text="▶︎ 進捗チェックシートメーカー（印刷用）"}</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#overview">モチベーション維持が難しかった話</a></li>
<li><a href="#features">できること</a></li>
<li><a href="#usage">使い方</a></li>
<li><a href="#benefits">活用アイデア</a></li>
<li><a href="#tech">ブラウザで完結</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>モチベーション維持が難しかった話 {#overview}</h2>
<p><img src="/dc1fd055a8b1513f4e6201eebd6d9b9e/%E9%80%B2%E6%8D%97%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%82%B7%E3%83%BC%E3%83%88%E3%83%A1%E3%83%BC%E3%82%AB%E3%83%BC.webp" alt=""></p>
<p>参考書や問題集、オンラインコースなど、長い学習を続けるのって、進捗が見えにくいとモチベーション維持が難しいですよね。</p>
<p>デジタルツールで進捗管理する方法もありますが、「紙に印刷して、ペンでチェックを入れる」というアナログな行為が、意外と達成感や学習継続のモチベーションに繋がるんです。</p>
<p>そんなわけで、表紙画像と開始ページ・終了ページを入力するだけで、A4横サイズのチェックシートを自動生成できるツールを作りました。</p>
<p>:::post-link{url="/ja/tools/progress-checksheet/" text="▶︎ 進捗チェックシートメーカー（印刷用）"}</p>
<h2>できること {#features}</h2>
<ul>
<li>画像選択とページ数入力だけで、簡単にチェックシートを作成</li>
<li>参考書の表紙やコースのサムネイルなど、好きな画像を表紙に設定可能（画像なしでもOK）</li>
<li>開始ページと終了ページを指定して、必要な範囲だけのチェックシートを作成</li>
<li>A4横サイズ（3508×2480ピクセル）で、印刷して使いやすいレイアウト</li>
<li>表紙画像はドラッグ&#x26;ドロップでも簡単に設定できる</li>
<li>画像処理はすべてブラウザ内で行われ、画像データがサーバーに送信されない</li>
<li>指定された総ページ数に応じて、チェックボックスのマス目の大きさと数が自動で最適化</li>
<li>生成されたチェックシートはすぐにPNG画像としてダウンロード可能</li>
<li>手書きでチェックすることで進捗を実感しやすく、学習継続をサポート</li>
</ul>
<h2>使い方 {#usage}</h2>
<p>基本的な流れは3ステップです。</p>
<ol>
<li><strong>表紙画像を選択（任意）</strong></li>
</ol>
<p>参考書の表紙など、好きな画像ファイルをドラッグ&#x26;ドロップするか、エリアをクリックして選択します。画像なしでもOKです。</p>
<ol start="2">
<li><strong>ページ数を入力</strong></li>
</ol>
<p>「開始ページ」と「終了ページ」を入力します。例えば、10ページから150ページまで管理したい場合は、開始に「10」、終了に「150」と入力。プレビューが自動で更新されます。</p>
<ol start="3">
<li><strong>画像をダウンロード</strong></li>
</ol>
<p>「画像をダウンロード」ボタンをクリックするだけ。PNG画像がダウンロードされるので、プリンターで印刷して使えます。</p>
<h2>活用アイデア {#benefits}</h2>
<p>紙に印刷してチェックを付けていくことで、こんなメリットがあります。</p>
<ul>
<li>一つ一つチェックマークを付けていくことで、目に見える形で進捗を実感でき、達成感が得られる</li>
<li>「あとこれだけ進んだ」「目標まであと少し」といった具体的な感覚が、学習を続ける上でのモチベーションになる</li>
<li>シートを壁に貼ったり、手帳に挟んだりして常に目に入る場所に置くことで、学習を意識しやすくなり、習慣化を助ける</li>
<li>ページ数の代わりにタスクの項目数を設定すれば、30日間チャレンジなどのチェックシートとしても使える</li>
<li>紙とペンで記録することで、集中力を高めたり、デジタル疲れを軽減したりする効果も期待できる</li>
</ul>
<p>お子さんのドリル学習の管理、趣味の編み物や手芸の工程管理、長編ドラマの視聴記録など、いろんな使い方ができます。</p>
<h2>ブラウザで完結 {#tech}</h2>
<p>このツールは、ブラウザだけで全ての処理が完結するように作られています。特別なソフトウェアのインストールは必要ありません。</p>
<p>表紙画像の処理やチェックシートの描画は、すべてブラウザ内部で行われます。選択した画像データが外部のサーバーに送信されることはないので、安心して使えます。</p>
<p>:::post-link{url="/ja/tools/progress-checksheet/" text="▶︎ 進捗チェックシートメーカー（印刷用）"}</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/dc1fd055a8b1513f4e6201eebd6d9b9e/%E9%80%B2%E6%8D%97%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%82%B7%E3%83%BC%E3%83%88%E3%83%A1%E3%83%BC%E3%82%AB%E3%83%BC.webp" medium="image"/></item><item><title><![CDATA[Next.jsサイトをCloudflare Pagesにデプロイ！独自ドメイン設定までの完全ガイド]]></title><description><![CDATA[Next.jsサイトをCloudflare Pagesにデプロイして、お名前ドットコムで契約した独自ドメインを設定する手順]]></description><link>https://uhiyama-lab.com/ja/blog/webdev/nextjs-cloudflare-custom-domain/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/webdev/nextjs-cloudflare-custom-domain/</guid><category><![CDATA[cloudflare]]></category><category><![CDATA[nextjs]]></category><pubDate>Wed, 29 Jan 2025 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>この記事では、人気フレームワーク「<strong>Next.js</strong>」で作成したWebサイトを、高機能なホスティングサービス「<strong>Cloudflare Pages</strong>」へ<strong>デプロイ</strong>し、さらにオリジナルの<strong>独自ドメイン</strong>（例: <code>your-domain.com</code>）を設定するまでの全手順を、初心者の方にも分かりやすく、ステップバイステップで解説する完全ガイドです。</p>
<p>Next.jsのデプロイ先としては開発元のVercelが有名ですが、<strong>Cloudflare Pages</strong>も非常に有力な選択肢です。Cloudflareが提供する<strong>高速なCDN</strong>、強力な<strong>セキュリティ機能</strong>、そして<strong>余裕のある無料枠</strong>といったメリットがあり、個人開発から小～中規模のWebサイトまで幅広く活用できます。今回は、筆者がよく利用するCloudflare Pagesへのデプロイ方法をご紹介します。</p>
<p>※この手順は、作成したNext.jsプロジェクトのコードが、GitHub（またはGitLab）のリポジトリ（プライベートでも可）にプッシュ（保存）されていることを前提としています。</p>
<hr>
<p><strong>この記事で解説する手順</strong></p>
<ol>
<li><a href="#cloudflare-setup">Cloudflare PagesにNext.jsプロジェクトを追加する</a></li>
<li><a href="#private-repo">GitHubプライベートリポジトリへのアクセス許可を設定する</a></li>
<li><a href="#build-config">Next.js用のビルド設定と注意点 (Node.jsバージョン, 互換フラグ)</a></li>
<li><a href="#custom-domain">Cloudflare Pagesに独自ドメインを設定する (ネームサーバー変更)</a></li>
<li><a href="#summary">まとめ：Next.js + Cloudflare Pages + 独自ドメイン設定の流れ</a></li>
</ol>
<hr>
<p>:::ad</p>
<h2>Cloudflare PagesにNext.jsプロジェクトを追加する</h2>
<p>まず最初のステップとして、Cloudflareの管理画面（ダッシュボード）で新しいPagesプロジェクトを作成し、デプロイしたいNext.jsプロジェクトのGitHubリポジトリと連携させます。</p>
<ol>
<li>Cloudflareダッシュボードにログインし、左側のメニューから「<strong>Workers &#x26; Pages</strong>」セクションを選択します。（※CloudflareのUIは頻繁に更新されるため、メニュー名が若干異なる場合があります。類似の項目を探してください。）</li>
</ol>
<p><img src="/dfa43011604702c7a72dafa125429d10/001-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="2">
<li>表示された画面の「概要」タブなどで、「<strong>アプリケーションを作成</strong>」ボタンを見つけてクリックします。</li>
</ol>
<p><img src="/4535771dc6d1e2b39e594487e1edc031/002-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="3">
<li>「アプリケーションを作成」画面が開いたら、「<strong>Pages</strong>」タブが選択されていることを確認し、「<strong>Gitに接続</strong>」ボタンをクリックします。Cloudflare PagesはGitリポジトリと連携して自動デプロイを行うのが基本です。</li>
</ol>
<p><img src="/d95690e92ea9a31768e4788a9132f368/003-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="4">
<li>
<p>GitHubまたはGitLabアカウントとの連携画面に進みます。初めて連携する場合は、「アカウントを追加」ボタンをクリックし、画面の指示に従ってGitHubアカウントを認証し、Cloudflareからのアクセスを許可します。</p>
</li>
<li>
<p>アカウント連携が完了すると、「リポジトリを選択」セクションに、連携したアカウントのGitリポジトリ一覧が表示されます。この中から、デプロイしたいNext.jsプロジェクトのリポジトリを選択してください。</p>
</li>
</ol>
<p><img src="/30346b498642a2b32802bd0b8c0629a9/004-20250129-NextjsCloudflare.png" alt=""></p>
<p>:::ad</p>
<h2>GitHubプライベートリポジトリへのアクセス許可を設定する</h2>
<p><strong>【ポイント】</strong> もしデプロイしたいリポジトリがプライベート（非公開）設定の場合、上記のリポジトリリストに表示されないことがあります。これは、Cloudflare Pagesアプリケーションがそのプライベートリポジトリにアクセスする権限を持っていないためです。その場合は、以下の手順でGitHub側でアクセス許可を与える必要があります。</p>
<ol>
<li>GitHubにログインし、右上のアイコンから「Settings」（設定）を開き、左側メニューの「<strong>Applications</strong>」（アプリケーション）を選択します。</li>
</ol>
<p><img src="/8ce05f133841e1c988705f4cdd919e98/005-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="2">
<li>「Installed GitHub Apps」タブを選択し、リストの中から「<strong>Cloudflare Pages</strong>」（または類似の名前）を見つけてクリックします。</li>
</ol>
<p><img src="/062e3f0f72b0df23edb2c6072ea27dfb/006-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="3">
<li>「Repository access」（リポジトリへのアクセス）セクションまでスクロールします。ここで、「<strong>Only select repositories</strong>」（選択したリポジトリのみ）オプションを選択し、表示されるリポジトリリストの中から、Cloudflare PagesにデプロイしたいNext.jsプロジェクトのリポジトリにチェックを入れます。最後に「Save」ボタンを押して変更を保存します。</li>
</ol>
<p>**【セキュリティ推奨】**もしここが「All repositories」（すべてのリポジトリ）になっていた場合でも、セキュリティの観点から、必要なリポジトリのみにアクセスを限定する「Only select repositories」に変更することをお勧めします。</p>
<p><img src="/e59fb5fa7a6636270d106b9d6a2095c7/007-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="4">
<li>このGitHub側の設定が完了したら、Cloudflare Pagesの画面に戻り、リポジトリ選択のリストを更新（またはページを再読み込み）すると、先ほどアクセスを許可したプライベートリポジトリが表示されるはずです。目的のリポジトリを選択し、「<strong>セットアップを開始</strong>」ボタンをクリックして次のステップに進みます。</li>
</ol>
<p><img src="/d4c3a78a7601d2caf4e773694ea7f977/008-20250129-NextjsCloudflare.png" alt=""></p>
<p>:::ad</p>
<h2>Next.js用のビルド設定と注意点 (Node.jsバージョン, 互換フラグ)</h2>
<p>リポジトリを選択すると、次にビルド設定とデプロイに関する設定画面が表示されます。Cloudflare PagesがGitHubリポジトリからコードを取得し、どのようにビルドしてデプロイするかをここで指定します。</p>
<ol>
<li>「ビルド設定」セクションにある「<strong>フレームワーク プリセット</strong>」のドロップダウンメニューを開き、「<strong>Next.js</strong>」を選択します。Cloudflare Pagesは多くのフレームワークに対応しており、プリセットを選択すると、そのフレームワークに最適なビルドコマンドや設定が自動的に入力されるため便利です。</li>
</ol>
<p><img src="/e0bd3eb315bf52c825dd4de95d1700de/009-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="2">
<li>プリセットを選択すると、「ビルドコマンド」（例: <code>npm run build</code> や <code>next build</code>）や「ビルド出力ディレクトリ」（例: <code>.next</code> や <code>.vercel/output/static</code>。Next.jsのバージョンや設定により異なる場合があります）が自動的に入力されます。通常はこのままで問題ありません。</li>
</ol>
<p><img src="/19e595450239f29de19068d3773fa45e/010-20250129-NextjsCloudflare.png" alt=""></p>
<p><strong>【注意点1】Node.jsバージョンの指定が必要な場合</strong></p>
<p>基本的な設定は以上ですが、このまま「保存してデプロイする」をクリックすると、ビルド時にエラーが発生することがあります。よくある原因の一つが、<strong>Cloudflare Pagesのビルド環境で使用されるNode.jsのバージョンが、あなたのNext.jsプロジェクトが必要とするバージョンよりも古い</strong>場合です。</p>
<p>ビルドログを確認し、以下のようなエラーメッセージが表示されていたら、Node.jsのバージョン指定が必要です。</p>
<pre><code>Initializing build environment. Failed: error finding node version '>=18.0.0'
You are using Node.js 18.17.1.
For Next.js, Node.js version "^18.18.0 || ^19.8.0 || >= 20.0.0" is required.
</code></pre>
<p>（上記はエラーメッセージの例です。バージョン番号は異なる場合があります）</p>
<p>このエラーを解決するには、Cloudflare Pagesのプロジェクト設定で、使用するNode.jsのバージョンを明示的に指定します。</p>
<p>プロジェクトの「設定」>「環境変数」を開き、「ビルド環境変数」セクション（または「実稼働/プレビュー環境変数」の両方）で「<strong>変数を追加</strong>」をクリックし、以下のように設定します。</p>
<ul>
<li><strong>変数名:</strong> <code>NODE_VERSION</code></li>
<li><strong>値:</strong> あなたのNext.jsプロジェクトが必要とするNode.jsのバージョン番号を指定します（例: <code>18.18.0</code> や <code>20.0.0</code> など。エラーメッセージやNext.jsのドキュメント、ローカル開発環境に合わせてください）。<code>package.json</code> の <code>engines</code> フィールドで指定している場合は、それに合わせるのが良いでしょう。</li>
</ul>
<p><img src="/cc174ca0b081ed64e92e5181dc8f8e02/011-20250129-NextjsCloudflare.png" alt=""></p>
<p>環境変数を設定した後、再度デプロイ（ビルドログ画面などから「デプロイを再試行」）を行うと、指定したNode.jsバージョンが使用され、ビルドが正常に完了するはずです。</p>
<p><strong>【注意点2】Node.js互換フラグの設定が必要な場合</strong></p>
<p>ビルドは成功したものの、デプロイされたサイト（<code>xxx.pages.dev</code>）にアクセスすると、以下のようなエラー画面が表示される場合があります。</p>
<p><img src="/be406fbe2f5ac591d6aba15a59891ce2/012-20250129-NextjsCloudflare.png" alt=""></p>
<p>これは「<strong>Node.JS Compatibility Error</strong>」と呼ばれるもので、Cloudflare Pagesの実行環境（Cloudflare Workers）でNext.jsアプリケーション（特にサーバーサイドの機能やAPIルートなど）を正しく動作させるために、Node.jsのAPI互換モードを有効にする必要があることを示しています。</p>
<p>このエラーを解決するには、以下の設定を行います。</p>
<p>プロジェクトの「設定」>「関数」>「<strong>互換性フラグ</strong>」セクション（または「互換性設定」など類似の項目）を見つけます。「フラグを設定」（または「追加」）ボタンをクリックし、「<strong><code>nodejs_compat</code></strong>」というフラグを追加して保存します。（通常、実稼働環境とプレビュー環境の両方に設定します）</p>
<p><img src="/8d17cf2c3f6e6a3b595816dda0ea6bb1/013-20250129-NextjsCloudflare.png" alt=""></p>
<p>このフラグを設定し、再度デプロイが完了した後、サイトにアクセスすると、Next.jsアプリケーションが正常に表示されるはずです。</p>
<p>:::ad</p>
<h2>Cloudflare Pagesに独自ドメインを設定する (ネームサーバー変更)</h2>
<p>ビルドとデプロイが成功し、<code>プロジェクト名.pages.dev</code> のようなURLでサイトが表示されるようになったら、最後のステップとして、あなたが所有する<strong>独自ドメイン</strong>（例: <code>your-cool-site.com</code>）をこのCloudflare Pagesプロジェクトに割り当てます。</p>
<p>独自ドメインを設定する大まかな流れは以下の通りです。</p>
<ol>
<li><strong>Cloudflare Pages側での設定:</strong> プロジェクトの設定画面で、使用したい独自ドメイン名を追加します。</li>
<li><strong>ネームサーバー情報の確認:</strong> Cloudflareから、あなたのドメインに設定すべきネームサーバーのアドレスが指示されます。</li>
<li><strong>ドメインレジストラ側での設定:</strong> あなたがドメインを取得したサービス（例: お名前ドットコム, Google Domains, Route 53など）の管理画面で、ドメインのネームサーバー設定を、Cloudflareから指示されたものに変更します。</li>
<li><strong>DNS反映待ち:</strong> ネームサーバー変更の情報がインターネット全体に行き渡る（DNSが伝播する）のを待ちます。通常は数分から数時間ですが、最大で48時間程度かかることもあります。</li>
<li><strong>Cloudflare Pages側での有効化:</strong> Cloudflareがネームサーバーの変更を確認できたら、ドメインが「アクティブ」状態になり、Pagesプロジェクトと独自ドメインが接続されます。</li>
</ol>
<p>以下では、ドメインレジストラとして「お名前ドットコム」を使用した場合の具体的な手順例を示します。（基本的な流れは他のレジストラでも同様です）</p>
<h3>ステップ1：Cloudflare Pagesにカスタムドメインを追加</h3>
<ol>
<li>Cloudflare Pagesのプロジェクト画面に戻り、「<strong>カスタムドメイン</strong>」タブを選択し、「<strong>カスタムドメインを設定</strong>」ボタンをクリックします。</li>
</ol>
<p><img src="/b9795d9a343feb37b13ec70da802e18d/014-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="2">
<li>テキストボックスに、設定したいあなたの独自ドメイン名（例: <code>your-cool-site.com</code>）を入力し、「続行」をクリックします。</li>
</ol>
<p><img src="/b9795d9a343feb37b13ec70da802e18d/015-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="3">
<li>もしまだそのドメインがCloudflareで管理されていない場合は、CloudflareにドメインのDNS管理を移管（または部分的に委任）するプロセスが開始されます。画面の指示に従い、「<strong>DNS転送を開始</strong>」や「サイトを追加」のようなボタンをクリックします。（※これはドメイン自体をCloudflareに移管するのではなく、あくまでDNSの管理権限をCloudflareに向ける設定です）</li>
</ol>
<p><img src="/ac3508f4e68657ddf8f8ecdc62b8a7e4/016-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="4">
<li>ドメイン名を入力し、Cloudflareが既存のDNSレコードをスキャンするプロセスに進みます。「Continue」をクリックします。</li>
</ol>
<p><img src="/8c78667cf2cb92208782b0618de6a29f/017-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="5">
<li>Cloudflareのプラン選択画面が表示されます。独自ドメインの利用だけであれば、通常は画面下部にある「<strong>Freeプラン</strong>」（無料プラン）を選択して「Continue」をクリックすれば十分です。</li>
</ol>
<p><img src="/33b4685caa79f0a97ee18cbee96c605c/018-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="6">
<li>Cloudflareがスキャンした現在のDNSレコードが表示されます（もしあれば）。内容を確認し（分からなければそのままでもOK）、「Continue」をクリックします。</li>
</ol>
<p><img src="/388192ac00a0cc2bc9ce43b915e4743e/019-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="7">
<li>確認のポップアップが出たら「Confirm」をクリックします。</li>
</ol>
<p><img src="/616b184bafe178e4d6dc95de1aa6299f/020-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="8">
<li><strong>【重要】</strong> この画面で、あなたのドメインに設定すべき**Cloudflareのネームサーバーアドレス（通常は2つ）**が表示されます。これらのアドレス（例: <code>ada.ns.cloudflare.com</code>, <code>ben.ns.cloudflare.com</code> など）を正確にコピーまたはメモしておきます。これが次のステップで必要になります。</li>
</ol>
<p><img src="/946c58750fc180221415b5f54c182516/021-20250129-NextjsCloudflare.png" alt=""></p>
<h3>ステップ2：ドメインレジストラでネームサーバーを変更</h3>
<p>次に、あなたのドメインを取得・管理しているサービス（ドメインレジストラ）の管理画面にログインし、ネームサーバーの設定を変更します。</p>
<ol>
<li>
<p>ドメインレジストラ（この例では「お名前ドットコム」）のウェブサイトにアクセスし、ログインします。</p>
</li>
<li>
<p>ドメイン管理メニューの中から、対象ドメインの「ネームサーバー設定」や「DNS設定」といった項目を探し、編集画面を開きます。</p>
</li>
<li>
<p>レジストラの現在のネームサーバー設定（通常はレジストラ自身のネームサーバーが設定されています）を削除または変更し、先ほどCloudflareから指示された<strong>2つのネームサーバーアドレス</strong>を入力・保存します。（入力欄が複数ある場合は、指示された2つを入力し、残りは空欄にするか、レジストラの指示に従います）</p>
</li>
</ol>
<p><img src="/e307fad0e4a297adc6aecb0f0b2ff974/022-20250129-NextjsCloudflare.png" alt=""></p>
<p><strong>【注意】DNSの伝播には時間がかかります！</strong></p>
<p>ネームサーバーの変更設定がインターネット全体に行き渡る（DNSが伝播する）までには、時間がかかります。早ければ数分で反映されることもありますが、通常は<strong>数時間</strong>、場合によっては<strong>最大で48時間程度</strong>かかることもあります。この間は、独自ドメインでサイトにアクセスできなかったり、古い情報が表示されたりすることがありますので、焦らず気長に待ちましょう。Cloudflareのダッシュボードで定期的にステータスを確認するか、Cloudflareから届く確認メールを待ちます。</p>
<p><img src="/b88980c78926d23a5926b41010c93998/023-20250129-NextjsCloudflare.png" alt=""></p>
<h3>ステップ3：Cloudflare Pagesでドメインを有効化</h3>
<p>ネームサーバーの変更がCloudflare側で無事に認識されると、Cloudflareダッシュボードのドメイン管理画面で、該当ドメインのステータスが「保留中」から「<strong>アクティブ</strong>」に変わります。（今回は比較的早く、5分程度で反映されました）。</p>
<p><img src="/29322942e66e1233f7cc1b7f0533c45d/024-20250129-NextjsCloudflare.png" alt=""></p>
<p>ドメインがアクティブになったら、いよいよCloudflare Pagesプロジェクトと独自ドメインを正式に接続します。</p>
<ol>
<li>
<p>再度、Cloudflare Pagesのプロジェクト設定画面に戻り、「<strong>カスタムドメイン</strong>」タブを開きます。</p>
</li>
<li>
<p>「カスタムドメインを設定」ボタンをクリックし、もう一度あなたの独自ドメイン名を入力して「続行」します。</p>
</li>
<li>
<p>今度はCloudflareがドメインを認識しているため、「ドメインをアクティベート」や「DNSレコードを確認」といった画面が表示されるはずです。Cloudflare Pagesが必要とするDNSレコード（通常はCNAMEレコード）が自動で設定（または提案）されます。内容を確認し、「<strong>ドメインをアクティベート</strong>」ボタンをクリックします。</p>
</li>
</ol>
<p><img src="/173482d29608e1eeabb5c882ed88174b/025-20250129-NextjsCloudflare.png" alt=""></p>
<ol start="4">
<li>ステータスが「初期化中」や「認証中」などと表示され、しばらく待つと最終的に「<strong>アクティブ</strong>」に変わります。これで設定は完了です！</li>
</ol>
<p>設定が完了すると、あなたの独自ドメイン（例: <code>https://your-cool-site.com</code>）にアクセスすると、Cloudflare PagesにデプロイされたNext.jsサイトが表示されるようになります。（SSL証明書もCloudflareが自動で発行・管理してくれます）</p>
<p><img src="/1289bdf01e20fc6fd9cc9d4ca2418514/026-20250129-NextjsCloudflare.png" alt=""></p>
<p>:::ad</p>
<h2>まとめ：Next.js + Cloudflare Pages + 独自ドメイン設定の流れ</h2>
<p>以上が、<strong>Next.js</strong>サイトを<strong>Cloudflare Pages</strong>にデプロイし、<strong>独自ドメイン</strong>を設定するまでの一連の手順でした。全体の流れをもう一度おさらいしましょう。</p>
<ol>
<li><strong>GitHub準備:</strong> Next.jsプロジェクトをGitHubリポジトリにプッシュする。</li>
<li><strong>Cloudflare Pages連携:</strong> Cloudflare Pagesでプロジェクトを作成し、GitHubリポジトリと連携する。（プライベートリポジトリの場合はGitHub側でアクセス許可設定を行う）</li>
<li><strong>ビルド設定とエラー対応:</strong>
<ul>
<li>フレームワークプリセットで「Next.js」を選択。</li>
<li>必要に応じて、環境変数 <code>NODE_VERSION</code> でNode.jsバージョンを指定する。</li>
<li>必要に応じて、互換性フラグ <code>nodejs_compat</code> を設定する。</li>
</ul>
</li>
<li><strong>独自ドメイン設定:</strong>
<ul>
<li>Cloudflare Pagesにカスタムドメインを追加し、指示されたCloudflareのネームサーバーを確認する。</li>
<li>ドメインレジストラの管理画面で、ネームサーバーをCloudflareのものに変更する。</li>
<li>DNSの伝播を待ち、Cloudflareでドメインがアクティブになったら、Pagesプロジェクトでドメインを有効化する。</li>
</ul>
</li>
</ol>
<p>ビルド時のNode.js関連のエラーや、DNS設定の反映待ち時間など、いくつか注意すべき点はありますが、手順自体は比較的シンプルに進められます。Cloudflare Pagesは、高速な配信、強力なセキュリティ、そして手厚い無料枠を提供しており、Next.jsアプリケーションのホスティング先として非常に魅力的な選択肢です。ぜひこのガイドを参考に、デプロイに挑戦してみてください。</p>
<p>（※この記事では基本的なデプロイ手順を解説しました。実際のアプリケーションの要件によっては、環境変数の設定、カスタムビルドコマンド、リダイレクトルールなど、さらに詳細な設定が必要になる場合があります。その際はCloudflare Pagesの公式ドキュメント等も合わせて参照してください。）</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/dfa43011604702c7a72dafa125429d10/001-20250129-NextjsCloudflare.png" medium="image"/></item><item><title><![CDATA[【VRChat】190,000ポリゴンのVeryPoorモデルを無理矢理70,000ポリゴンに削減した記録]]></title><description><![CDATA[MantisLODEditorとMeshDeleterでVRChatアバターのポリゴンを188,784から69,995に65%削減した備忘録]]></description><link>https://uhiyama-lab.com/ja/blog/dialy/vrchat-avatar-optimization/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/dialy/vrchat-avatar-optimization/</guid><category><![CDATA[vrchat]]></category><pubDate>Sun, 22 Dec 2024 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>VRChatで使用しているアバターのパフォーマンスランクが「Very Poor」になってしまうことがあります。Very Poorランクは、ワールドによっては表示が制限されたり、他ユーザーへの負荷となる可能性もあるため、可能な範囲で最適化が推奨されています。</p>
<p>今回は、Unityのツール「Mantis LOD Editor」と「MeshDeleterWithTexture」を用いて、アバターのポリゴン数を削減し、パフォーマンスランクを「Very Poor」から「Poor」へ改善できたため、その手順と方法を記録としてまとめます。</p>
<p>最適化結果：ビフォーアフター</p>
<p>最適化前：188,784ポリゴン / パフォーマンスランク: Very Poor</p>
<p><img src="/04e252ad57f1cd36e48091df825367e8/20241216-1-VRC%E6%9C%80%E9%81%A9%E5%8C%96-%E4%BD%9C%E6%A5%AD%E5%89%8D.webp" alt="最適化前のアバター（Very Poorランク）"></p>
<p>最適化後：69,995ポリゴン / パフォーマンスランク: Poor</p>
<p><img src="/bddc141e1028e8f54846e32d8ea8abed/20241216-2-VRC%E6%9C%80%E9%81%A9%E5%8C%96-%E4%BD%9C%E6%A5%AD%E5%BE%8C.webp" alt="最適化後のアバター（Poorランク）"></p>
<p>上記のように、見た目への影響を最小限に抑えつつ、ポリゴン数を63%削減することに成功しました。</p>
<p><strong>【はじめに：この記事を読む前に】</strong></p>
<p>この記事は、約19万ポリゴンのハイエンドモデルを無理やり7万ポリゴン以下に削減しようとした記録です。正直なところ、実際にやってみて「ここまで削減する必要があったかな？」と感じる部分もありました。</p>
<p>実用的には、パフォーマンスが重要な場面では別途軽量なアバターを用意する方が現実的です。ただ、「こういう削減方法もあるんだ」という知識として、あるいは軽度の最適化（10万→7万ポリゴン程度）を目指す場合の参考として、気軽に読んでいただければと思います。</p>
<p>具体的には、以下の方法でポリゴン数を効果的に削減しました。これらのテクニックは、アバター改変や自作モデル制作においても重要になると思われます。</p>
<ul>
<li>隠れたパーツの削除：服の下で見えない体の一部（肘など）のメッシュを削除</li>
<li>Mantis LOD Editorによる削減：各パーツ（服、髪、小物など）のポリゴン数を、見た目が崩れない範囲で削減</li>
<li>MeshDeleterWithTextureによる部分削除：服の不要な装飾や、さらに隠れた部分のメッシュをテクスチャ指定で削除</li>
<li>（応用）MeshDeleterによるパーツ分離：複合パーツ（例：インナーとアウターが一体化した服）を分離し、それぞれ個別に最適化</li>
</ul>
<p>この記事では、これらの手順を順番に解説していきます。</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#tools">必要なツールと準備</a></li>
<li><a href="#terminology">知っておくと便利な用語</a></li>
<li><a href="#mantis-basics">Mantisでポリゴン削減</a></li>
<li><a href="#mantis-limitations">Mantisだけでは難しいケース</a></li>
<li><a href="#meshdeleter">MeshDeleterで部分削除</a></li>
<li><a href="#advanced">応用：服パーツを分離</a></li>
<li><a href="#summary">最適化の成果とポイント</a></li>
<li><a href="#references">参考情報</a></li>
<li><a href="#appendix">追記：ハイエンドモデルの注意点</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>必要なツールと準備 {#tools}</h2>
<p>今回のVRChatアバター最適化（ポリゴン削減）で使用した主なツールは以下の通りです。</p>
<ul>
<li>
<p><strong><a href="https://assetstore.unity.com/packages/tools/modeling/mantis-lod-editor-professional-edition-37086?locale=ja-JP">Mantis LOD Editor - Professional Edition</a></strong> (Unity Asset Store / 有料: 約$55)</p>
<ul>
<li>高品質なポリゴン削減（リダクション）機能を持つ定番のUnityアセット。アバターの各パーツのポリゴン数を効率的に減らせます。購入後、UnityのPackageManager > My Assetsからインポートします。</li>
</ul>
</li>
<li>
<p><strong><a href="https://booth.pm/ja/items/5409262">【非破壊でポリゴン削減】Mantis LOD EditorのNDMF化ツール</a></strong> (Booth: ひつぶさん作 / 無料 ※Mantis LOD Editor本体が必要)</p>
<ul>
<li>上記のMantis LOD Editorを、VRChatアバター改変で広く使われているフレームワーク「NDMF（なでもふ）」上で、より安全かつ簡単に（非破壊的に）利用できるようにするツール。Boothからダウンロードし、unitypackageをインポートします。実際にアバターに適用するのはこちらです。</li>
</ul>
</li>
<li>
<p><strong><a href="https://booth.pm/ja/items/1501527">MeshDeleterWithTexture beta</a></strong> (Booth: がとーしょこらさん作 / 無料)</p>
<ul>
<li>テクスチャ画像上で範囲を指定することで、対応するメッシュ部分を削除（実際には新しいメッシュを生成）できる非常に便利なUnityエディタ拡張。Mantisでは難しい、服の細かい装飾の削除や、隠れた部分のメッシュ除去に役立ちます。Boothからダウンロードし、unitypackageをインポートします。</li>
</ul>
</li>
<li>
<p><strong>anatawa12's VRC Avatar Tools (旧: gists pack)</strong> (VCC経由で導入可能)</p>
<ul>
<li>アバターのパフォーマンス統計情報を詳細に表示する機能などが含まれるツール群。特に「<strong>Actual Performance</strong>」タブは、最適化作業中の現在のポリゴン数やパフォーマンスランクをリアルタイムで確認するために<strong>必須</strong>です。VCC (VRChat Creator Companion) の「Manage Project」から簡単に追加できます。</li>
</ul>
</li>
</ul>
<p><strong>【重要】Actual Performanceタブでの確認方法：</strong></p>
<p>anatawa12's VRC Avatar Toolsを導入すると、VRChat SDK Control Panelに「Avatars」タブが追加され、その中に「Actual Performance」という項目が表示されます。MantisやMeshDeleterで最適化を行った後、<strong>Unityを再生モード（Play Mode）にする</strong>と、このタブの情報が更新され、最新のポリゴン数やパフォーマンスランクを確認できます。目標は、ここの「Polygons」の数値を<strong>70,000以下</strong>にすることです（Poorランクの上限）。</p>
<p><img src="/4ee413c875f7ee17a7c7e58982d8d11e/20241216-3-anatawa.png" alt="VCCでanatawa12&#x27;s VRC Avatar Toolsを追加する画面"></p>
<p><img src="/d3b4e47773d1c5ab82e8a7fe7db48b41/20241216-4-ActualPerformance.png" alt="Actual Performanceタブでポリゴン数とランクを確認"></p>
<p>（↑この例では188,784ポリゴンでVery Poor。目標は70,000ポリゴン以下！）</p>
<h2>知っておくと便利な用語 {#terminology}</h2>
<p>アバター最適化を進める上で、いくつか知っておくと理解が深まる用語があります。</p>
<ul>
<li>
<p><strong>非破壊的 (Non-Destructive)</strong>
元のデータを直接書き換えずに行う編集方法のこと。今回使うNDMF版MantisやMeshDeleterは、元のメッシュデータを保持したまま処理を行うため、「非破壊的」です。つまり、設定を間違えたり、結果が気に入らなかったりした場合でも、ツール（コンポーネント）を削除したり設定を戻したりするだけで、簡単に<strong>元の状態に戻すことができます</strong>。初心者でも安心して試せる大きなメリットです。</p>
</li>
<li>
<p><strong>NDMF (Nade Nadenadeshiko Mod Fwk / なでもふ)</strong>
VRChatアバター改変のためのフレームワーク（仕組み）の一つ。アバターの色々な設定や改変を「コンポーネント」として管理し、それらをビルド時に自動で適用してくれます。非破壊的な改変と相性が良く、多くの便利ツールがNDMFに対応しています。今回使う「Mantis LOD EditorのNDMF化ツール」もその一つです。</p>
</li>
</ul>
<p>:::ad</p>
<h2>Mantisでポリゴン削減 {#mantis-basics}</h2>
<p><img src="/4b45f136e3deef181d7ec2a26f8799c9/20241216-5-%E3%83%9D%E3%83%AA%E3%82%B4%E3%83%B3%E5%AF%BE%E8%B1%A1.png" alt="VRChat SDKのValidation画面でポリゴン超過パーツを特定"></p>
<p>まずは、アバターの中でどのパーツがポリゴン数を増やしている主犯なのかを特定しましょう。</p>
<ol>
<li>
<p>UnityでVRChat SDK Control Panelを開き、「Builder」タブを選択します。</p>
</li>
<li>
<p>アバターを選択した状態でビルドを実行しようとすると、パフォーマンスに関する警告（Validation Results）が表示されます。「Polygons」に関する警告メッセージの横にある「<strong>Select</strong>」ボタンをクリックすると、ポリゴン数が特に多いメッシュ（衣装パーツなど）がHierarchyウィンドウでハイライトされます。</p>
</li>
<li>
<p>ハイライトされたオブジェクト（衣装パーツなど）に、「<strong>NDMF Mantis LOD Editor</strong>」コンポーネントを追加します。（Inspectorウィンドウで「Add Component」し、"Mantis"で検索すると見つかります）</p>
</li>
<li>
<p>追加されたコンポーネントの「<strong>Quality</strong>」スライダーを調整します。この数値を下げるほどポリゴン数が削減されますが、下げすぎるとメッシュの形が崩れてしまいます。</p>
</li>
</ol>
<p><img src="/3dfcb689a443b88d00b4267fedd9325b/20241216-6-MantisLOD-%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%80%E3%83%BC.webp" alt="NDMF Mantis LOD EditorコンポーネントのQualityスライダー"></p>
<p><strong>【ポイント】Shaded Wireframe表示を活用しよう</strong></p>
<p>Qualityスライダーを調整する際は、Unityのシーンビュー表示を「<strong>Shaded Wireframe</strong>」に切り替えると、ポリゴンの削減具合やメッシュの崩れ具合が視覚的に分かりやすくなります。「Shaded」表示と適宜切り替えながら、見た目を損なわないギリギリのラインを探っていきましょう。</p>
<p><img src="/97c3930af34380a375ab4fba109852cf/20241216-7-ShadedWireframe.png" alt="Shaded Wireframe表示でポリゴンを確認"></p>
<p>この「パーツ特定 → コンポーネント追加 → Quality調整」を、ポリゴン数が多いパーツに対して繰り返していくのが基本的な流れです。</p>
<h3>【重要】使わないパーツは完全に削除しよう</h3>
<p>アバター改変をしていると、「元の服や髪型も、いつか使うかもしれないから一応残しておこう」と、非表示にしただけでHierarchyに残してしまうことがあります。しかし、<strong>非表示にしただけのメッシュもポリゴン数としてカウントされてしまう</strong>場合があります！</p>
<p>今回の私のアバターも、元のマヌカちゃんのエプロンパーツなどを非表示で残していましたが、これらをHierarchyから完全に削除したところ、<strong>約16,000ポリゴンも削減</strong>できました。</p>
<p>使わないパーツは思い切って削除しましょう。もし後で必要になった場合は、元のアバターや衣装のunitypackageを再インポートすれば簡単に戻せます。</p>
<p><img src="/fe49145f606f644bed17c449179cf053/20241216-8-%E4%B8%8D%E8%A6%81%E3%83%91%E3%83%BC%E3%83%84%E3%81%AE%E5%89%8A%E9%99%A4.png" alt="Hierarchyから不要なパーツを削除する"></p>
<p>（参考： <a href="https://note.com/kohadachan/n/n68e1f8f15606">UnityだけでVRChatアバターのVeryPoorを脱出する方法｜こはだ 様</a>）</p>
<h3>【注意】顔と素体のポリゴン削減は慎重に！</h3>
<p><img src="/9e3fc02e22c9124162041785f1c775d7/20241216-9-%E9%A1%94%E3%83%91%E3%83%BC%E3%83%84%E3%81%AF%E3%83%8E%E3%83%BC%E3%82%BF%E3%83%83%E3%83%81.png" alt="顔周りの細かいメッシュ構造"></p>
<p>Mantisでのポリゴン削減は効果的ですが、<strong>アバターの「顔」と「素体（Body）」のメッシュは、原則として削減しない</strong>方が無難です。</p>
<p>特に顔周りは、豊かな表情を作るために非常に多くの細かいポリゴン（メッシュ）で構成されています。口周りのポリゴンはリップシンク（口パク）の様々な形に対応するため、目周りのポリゴンはまばたきや感情表現のために必要です。</p>
<p>これらの部分をMantisで安易に削減してしまうと、<strong>喋るたびに口が破綻したり、表情が崩壊したりする</strong>可能性が非常に高くなります。同様に、素体の関節部分なども、削減するとポーズを取ったときに不自然な見た目になりやすいです。</p>
<p>ポリゴン削減は、主に<strong>服、髪、アクセサリーなどのパーツ</strong>で行い、顔と素体はできるだけ元の状態を保つようにしましょう。</p>
<h3>【判断】時にはデザイン的な妥協も必要</h3>
<p><img src="/7bdb2a6a77a323041479748e02564585/20241216-10-%E5%B0%BB%E5%B0%BE%E6%9C%80%E9%81%A9%E5%8C%96.png" alt="尻尾パーツの削減前後の比較"></p>
<p>どうしても目標のポリゴン数（Poorランクの70,000ポリゴン）に収まらない場合、デザインの一部を諦めるという判断が必要になることもあります。</p>
<p>今回のアバターでは、尻尾パーツのポリゴン数をMantisである程度削減しましたが、それでも目標達成が難しかったため、最終的に<strong>尻尾パーツ自体を削除</strong>する決断をしました。もちろん残念ですが、パフォーマンスランク改善のためには、こうしたトレードオフが必要になる場合もあります。</p>
<p>:::ad</p>
<h2>Mantisだけでは難しいケース {#mantis-limitations}</h2>
<p>Mantis LOD Editorは非常に優秀なツールですが、最適化を進める中で、Mantisだけでは対応が難しい、あるいは効率が悪いと感じる場面が出てきました。</p>
<ul>
<li><strong>複合パーツの削減限界：</strong> インナーとアウターが一体化している服など、一つのオブジェクトに複数の部位が含まれている場合、Mantisで削減すると、先にポリゴン数が少ない部分（例: インナー）が破綻してしまい、ポリゴン数が多い部分（例: アウター）を十分に削減できないことがある。パーツを分離して個別に削減したい。</li>
<li><strong>隠れたメッシュの残り：</strong> 服の下に隠れている素体の一部（例: お腹周り）を非表示にしてもメッシュが残り、ポリゴン数に含まれてしまう。この隠れた部分だけを削除したい。</li>
<li><strong>部分的な装飾の削除：</strong> 服の特定の部分（ポケット、ベルト、フリルなど）だけを削除してポリゴン数を稼ぎたいが、Mantisでは全体的な削減しかできない。</li>
</ul>
<p>これらの課題を解決するために役立つのが、「<strong>MeshDeleterWithTexture beta</strong>」です。</p>
<p><img src="/5d6ae22b8211e9636242fe78b9e32320/20241216-11-MeshDeleter-%E4%BD%9C%E6%A5%AD%E5%89%8D.png" alt="最適化前の体メッシュ（服で隠れている部分も表示）"></p>
<p>（↑例えば、服の下に隠れているこのお腹部分のメッシュを削除したい）</p>
<p><img src="/f7d88f2640997ff1bc7f0e4655126501/20241216-12-MeshDeleter-%E8%85%B9%E9%83%A8%E5%89%8A%E9%99%A4%E5%89%8D.png" alt="服を非表示にした状態。腹部のメッシュが残っている"></p>
<h2>MeshDeleterで部分削除 {#meshdeleter}</h2>
<p>「<strong>MeshDeleterWithTexture beta</strong>」は、その名の通り、<strong>テクスチャ画像上で指定した範囲に対応するメッシュ部分を削除</strong>（正確には、削除した新しいメッシュを生成）してくれる画期的なツールです。</p>
<p>使い方は非常に直感的です。</p>
<ol>
<li>
<p>がとーしょこらさんの<a href="https://booth.pm/ja/items/1501527">Booth</a>からunitypackageをダウンロードし、プロジェクトにインポートします。</p>
</li>
<li>
<p>Unityメニューバーに「GotoTools」という項目が追加されるので、「MeshDeleter with Texture」を選択して専用ウィンドウを開きます。</p>
</li>
</ol>
<p><img src="/c91687d2699cbfe3039d8f9efd1788d2/20241216-13-MeshDeleter%E3%83%A1%E3%83%8B%E3%83%A5%E3%83%BC.png" alt="MeshDeleter with Textureのメニュー"></p>
<ol start="3">
<li>
<p>ウィンドウ上部の「Renderer」という欄に、メッシュを削除したいオブジェクト（例: 体のメッシュを持つオブジェクト）をHierarchyウィンドウからドラッグ&#x26;ドロップします。</p>
</li>
<li>
<p>オブジェクトに設定されているテクスチャ画像がウィンドウ内に表示されます。</p>
</li>
<li>
<p>画面右側の「Draw Type」から「PEN」などを選択し、テクスチャ上で<strong>削除したい部分（例: 服で隠れるお腹の部分）を黒く塗りつぶします</strong>。塗りつぶした部分は、シーンビュー上のモデルにもリアルタイムで反映され、削除される範囲を確認できます。</p>
</li>
</ol>
<p><img src="/ecac34eec7ca8be6d8cd7afea555889b/20241216-14-MeshDeleter.png" alt="MeshDeleterでテクスチャのお腹部分を黒く塗りつぶす"></p>
<ol start="6">
<li>削除範囲を確認したら、「<strong>DeleteMesh</strong>」ボタンをクリックします。これで、指定した部分が削除された新しいメッシュが生成され、オブジェクトに自動で適用されます。</li>
</ol>
<p><img src="/f24a68730f3db3fe817fc751e8003ffc/20241216-15-MeshDeleter-%E5%89%8A%E9%99%A4%E5%AE%8C%E4%BA%86.png" alt="MeshDeleter実行後、お腹部分のメッシュが削除された状態"></p>
<p><strong>【ポイント】MeshDeleterも非破壊的！</strong></p>
<p>このツールも「非破壊的」で、元のメッシュデータはプロジェクト内に残っています。もし削除範囲を間違えたり、元に戻したくなったりした場合は、オブジェクトのMesh Renderer（またはSkinned Mesh Renderer）コンポーネントで、Meshの指定を元のファイルに戻すだけでOKです。安心して試せますね。</p>
<p><img src="/f32170709c0d0b07995f8f5f35754761/20241216-16-MeshDeleter%E6%96%B0%E8%A6%8F%E3%83%A1%E3%83%83%E3%82%B7%E3%83%A5%E9%81%A9%E7%94%A8.png" alt="生成された新しいメッシュが適用されているInspector画面"></p>
<p>この方法で、服の下に隠れていた腹部メッシュを削除したところ、<strong>約1,000ポリゴン削減</strong>できました。さらに、残った部分にMantis LOD Editorを適用して、より効率的にポリゴンを削減することも可能です。</p>
<p>:::ad</p>
<h2>応用：服パーツを分離 {#advanced}</h2>
<p><img src="/8bc0b47e1d2bd8cae35cb665adfa2fb6/20241216-17-Techware.png" alt="最適化対象のテックウェア（インナーとアウターが一体）"></p>
<p>MeshDeleterの「テクスチャ範囲指定でメッシュを削除する」機能は、応用することで<strong>一体化している服パーツを分離する</strong>のにも使えます。</p>
<p>今回使用したテックウェア（約60,000ポリゴン）は、コート部分（アウター）と上半身のインナーが同じ一つのオブジェクト（メッシュ）として作られていました。このままだと、Mantisで削減しようとしても、ポリゴン数の少ないインナー部分が先に破綻してしまい、ポリゴン数の多いコート部分を十分に削減できませんでした。</p>
<p>そこで、MeshDeleterを使って「インナーだけのメッシュ」と「アウターだけのメッシュ」を擬似的に作り出すことにしました。</p>
<p><strong>【パーツ分離手順】</strong></p>
<ol>
<li>
<p>元のテックウェアオブジェクトをHierarchy上で複製（Ctrl+D または Cmd+D）し、それぞれ「インナー用」「アウター用」など分かりやすい名前に変更します。</p>
</li>
<li>
<p>「インナー用」オブジェクトを選択し、MeshDeleterウィンドウを開きます。</p>
</li>
<li>
<p>テクスチャ上で、<strong>アウター（コート）に対応する部分をすべて黒く塗りつぶし</strong>、「DeleteMesh」を実行します。これにより、インナー部分だけが残ったメッシュが生成されます。</p>
</li>
</ol>
<p><img src="/f420d73579e2136640b57f5731b8cc2a/20241216-19-%E3%82%A4%E3%83%B3%E3%83%8A%E3%83%BC%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88.png" alt="インナー用オブジェクトでコート部分を塗りつぶす"></p>
<p><img src="/088d05f551a8baac55d771021965dc0b/20241216-18-%E3%82%A4%E3%83%B3%E3%83%8A%E3%83%BC%E7%94%A8.png" alt="インナー部分だけのメッシュが完成"></p>
<ol start="4">
<li>同様に、「アウター用」オブジェクトを選択し、MeshDeleterウィンドウで今度は<strong>インナーに対応する部分をすべて黒く塗りつぶし</strong>、「DeleteMesh」を実行します。これにより、アウター（コート）部分だけが残ったメッシュが生成されます。</li>
</ol>
<p><img src="/343b351f01ea87174a939a722592b768/20241216-20-%E3%82%A2%E3%82%A6%E3%82%BF%E3%83%BC%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88.png" alt="アウター用オブジェクトでインナー部分を塗りつぶす"></p>
<p>これで、もともと一体だった服を「インナー」と「アウター」の2つのメッシュに分離できました。Blenderなどの外部モデリングソフトを使わずに、<strong>Unity上だけでパーツを分離できる</strong>のは非常に便利です。</p>
<p>分離後は、それぞれのパーツ（インナー、アウター）に個別にNDMF Mantis LOD Editorを適用し、より効果的にポリゴン数を削減していくことが可能になります。</p>
<h2>最適化の成果とポイント {#summary}</h2>
<p>以上の方法を用いた結果、最初に18万ポリゴンあったVeryPoorランクのアバターを、69,995ポリゴンのPoorランクに改善することができました。</p>
<p><img src="/d6454183b26357a114e6c94e21e45caa/20241216-21-%E6%9C%80%E7%B5%82%E7%B5%90%E6%9E%9C.png" alt="最終的なポリゴン数（69995）とPoorランクの表示"></p>
<p>最終的に目標の70,000ポリゴン以下に収めるために、服の装飾削除やコートのポリゴン削減など、デザイン面での調整も行いました。見た目を維持しながらポリゴン数を削減する作業は、試行錯誤が必要ですが、非常に良い練習になったと感じています。</p>
<p><img src="/30d8a5fff84b009bb73259b9e48801a6/20241216-22-%E6%9C%80%E7%B5%82%E7%B5%90%E6%9E%9C.png" alt="最適化後のアバターの外観（見た目の劣化は最小限）"></p>
<p>今回使用した「Mantis LOD Editor (NDMF版)」と「MeshDeleterWithTexture beta」は、どちらも非破壊的なツールであるため、初心者でも比較的安心して試すことができます。「いつでも元に戻せる」という点は、最適化作業を進める上での大きな心理的な支えになります。</p>
<p>Mantisの強力なポリゴン削減能力と、MeshDeleterの柔軟な部分編集・応用力を組み合わせることで、Very Poorランクのアバターも効果的に最適化できる可能性が高まります。</p>
<p>VRChatアバターのポリゴン数でお悩みの方は、ぜひこの記事で紹介したツールと手順を参考に、最適化に挑戦してみてください。（なお、パフォーマンスランクにはポリゴン数以外にもマテリアル数などが影響するため、総合的な最適化を目指す場合はそちらも考慮が必要です）</p>
<h2>参考情報 {#references}</h2>
<p>今回のアバター最適化（ポリゴン削減）にあたり、以下の記事や動画を大変参考にさせていただきました。素晴らしい情報をありがとうございます。</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=VIDEO_ID">【第5回】lil NDMF Mesh Simplifier VS Mantis LOD Editor ポリゴン削減比較＆解説【VeryPoor→Medium】 - INSTチャンネル YouTube</a> （※元のリンクがYouTube検索結果になっていたためダミーリンクにしています。実際の動画を検索してください）</li>
<li><a href="https://metacul-frontier.com/?p=9367">非破壊でポリゴン削減！　Mantis LOD EditorのNDMF化ツールを紹介！ | メタカル最前線</a></li>
<li><a href="https://metacul-frontier.com/?p=7292#toc_id9">【2024年11月最新版】VCCにこれは入れておけ！　アバター改変の便利ツール紹介 | メタカル最前線</a></li>
<li><a href="https://note.com/suhamahzk/n/nc8ede0789a23">Mantis LOD Editorを使ってアバターを2万ポリ以下にする【VRChat】｜すゞは</a></li>
<li><a href="https://note.com/kohadachan/n/n68e1f8f15606">UnityだけでVRChatアバターのVeryPoorを脱出する方法｜こはだ</a></li>
</ul>
<p>:::ad</p>
<hr>
<h2>追記：ハイエンドモデルの注意点 {#appendix}</h2>
<p>この記事ではVery PoorランクのアバターをPoorランクに改善する手順を紹介しましたが、すべてのケースで同様の最適化が最善とは限らない点について追記します。特に、元のポリゴン数が非常に多い、いわゆる「ハイエンドモデル」の最適化には注意が必要です。</p>
<p>これらのモデルは、ディテールに富んだ装飾や複雑な衣装、多くのギミックなどが魅力ですが、その分ポリゴン数が膨大（10万ポリゴンをはるかに超えることも珍しくありません）です。このようなモデルに対してMantis LOD Editorなどでポリゴン削減を行うと、わずかな削減率でも見た目のディテールが失われ、モデル本来の魅力が大きく損なわれてしまう可能性があります。</p>
<p>Poorランクの規定（7万ポリゴン）を少しオーバーしている程度のアバター（例えば7万～10万ポリゴン程度）や、もともとシンプルなデザインのデフォルメモデルなどであれば、この記事で紹介した方法は有効でしょう。しかし、規定の倍以上、例えば15万、20万ポリゴンを超えるようなハイエンドモデルを、見た目を維持したままPoorランクまで無理に削減しようとすることは、多くの場合困難であり、あまり推奨できません。</p>
<p>では、どうすれば良いかというと、無理に一つのモデルを極端に最適化するのではなく、用途に応じた使い分けを検討するのが現実的です。</p>
<ul>
<li>通常利用：メインのアバターは、最適化せず元のクオリティのまま使用する</li>
<li>軽量化が必要な場面：大人数が集まるイベントや、パフォーマンスが重視される特定のワールドに参加する際には、別途軽量化されたバージョン（Quest対応版などが用意されている場合もあります）を用意するか、あるいは最初から軽量な別のアバターを使用する</li>
</ul>
<h3>筆者の体験談と反省</h3>
<p>実を言うと、今回Poorランク（69,995ポリゴン）まで最適化したマヌカ改変モデルですが、しばらく使ってみて、少し考えが変わりました。確かに軽量化には成功したのですが、その過程で服の細かい装飾などを一部削除してしまったことに対し、「そこまでして最適化する必要があったかな？」という気持ちが後から出てきました。</p>
<p>また、私自身のVRChatのプレイスタイルとして、パフォーマンスが非常にシビアなワールドに頻繁に参加するわけではなかったため、「このモデルに関しては、Very Poorのままでも大きな支障はなかったかもしれない」というのが正直なところです。</p>
<p>アバターの最適化は、VRChatを快適に楽しむ上で重要な要素の一つですが、それが常に必須というわけではありません。ご自身のプレイスタイルや参加するコミュニティ、そして「どの程度の見た目の変化なら許容できるか」をよく考慮した上で、最適化を行うかどうか、どのレベルまで行うかを判断することが大切だと思います。</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/07e8f6201d7b8c8e4a6258f032d8b995/20241216-VRChat-MantisLOD_thumb.jpg" medium="image"/></item><item><title><![CDATA[Google Analytics Data APIで静的ブログサイトに『人気記事一覧』を実装する方法【Next.js / Gatsby.js】]]></title><description><![CDATA[Google Analytics Data APIで人気記事をjsonとして取得すれば、Next.jsやGatsby.jsで構築した静的ブログサイトにも人気記事一覧を実装することができます。]]></description><link>https://uhiyama-lab.com/ja/blog/webdev/static-popular-posts/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/webdev/static-popular-posts/</guid><category><![CDATA[gatsby]]></category><category><![CDATA[nextjs]]></category><pubDate>Sun, 15 Dec 2024 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p><strong>JAMstack</strong>構成（例: <strong>Next.js</strong>や<strong>Gatsby.js</strong> + ヘッドレスCMS）は、フロントエンドとバックエンドを分離することで、使用する技術選択の自由度が高く、パフォーマンスやセキュリティ面でのメリットも大きいモダンなWebサイト構築のアプローチです。</p>
<p>しかし、その一方で、従来のWordPressサイトなどではプラグインで簡単に実現できた機能、例えば「<strong>人気記事一覧（ランキング）</strong>」などを実装しようとすると、少し工夫が必要になります。</p>
<p>WordPressで人気記事一覧が容易なのは、記事データとページ閲覧数（PV数）が同じデータベース内に記録されており、動的にデータを取得・集計してランキング表示できるためです。しかし、JAMstack構成の多くは、ビルド時に静的なHTMLファイルを生成するため、リアルタイムで変動する閲覧数を基にした動的なランキング表示は得意ではありません。また、フロントエンドとバックエンドが分離しているため、アクセス数を記録・参照する共通の仕組みが標準では備わっていません。</p>
<p>そこで活用したいのが、多くのWebサイトで導入されているアクセス解析ツール「<strong>Google Analytics</strong>」です。Google Analyticsに蓄積されたページごとの閲覧数データを、そのAPI（<strong>Google Analytics Data API (GA4 Data API)</strong>）経由で取得し、ビルドプロセスに組み込むことで、静的サイトでありながら「人気記事一覧」を表示することが可能になります。</p>
<p>この記事では、<strong>Google Analytics Data API (GA4)</strong> を利用して人気記事データを取得し、<strong>Next.js</strong>や<strong>Gatsby.js</strong>といった静的サイトジェネレーター環境で「<strong>人気記事一覧</strong>」を実装するための具体的な手順と考え方を紹介します。</p>
<p>:::ad</p>
<hr>
<p><strong>この記事の内容</strong></p>
<ol>
<li>静的サイト（ヘッドレスCMS）で人気記事一覧を実装する難しさ</li>
<li>Google Analytics Data API (GA4) の有効化と基本準備</li>
<li>サービスアカウントの利用とセキュリティ上の注意点</li>
<li>Node.jsによる人気記事データ取得スクリプトの実装例</li>
<li>ビルド時にJSONデータを活用し、静的サイトに組み込む方法</li>
<li>まとめ：静的サイトでもAPI連携で人気記事一覧は実現できる！</li>
</ol>
<hr>
<h2>静的サイト（ヘッドレスCMS）で人気記事一覧を実装する難しさ</h2>
<p><img src="/2e8bca8b3ad8539c7f515b77622be091/001-JAMstack-GoogleAnalytics.png" alt="JAMstack構成とGoogle Analytics連携のイメージ図"></p>
<p>前述の通り、従来の動的なCMS（WordPressなど）では、データベースへのアクセスを通じてリアルタイムにPV数を集計し、人気記事ランキングを表示することが一般的でした。</p>
<p>しかし、<strong>JAMstack</strong>構成の<strong>静的サイト</strong>では、サイトのコンテンツはビルド時に生成され、サーバー側での動的なデータ処理は基本的には行いません。そのため、サイトが表示されるたびにPV数をリアルタイムで集計してランキングを変動させる、といった実装は困難です。</p>
<p>そこで、外部サービスである<strong>Google Analytics</strong>のデータを活用するアプローチが有効になります。基本的な流れは以下のようになります。</p>
<ol>
<li><strong>ビルド前にデータを取得:</strong> スクリプトを実行し、<strong>Google Analytics Data API</strong>を通じて、指定期間のページ別閲覧数を取得します。（例: 直近30日間のPV数トップ10記事）</li>
<li><strong>データを整形・保存:</strong> 取得したランキングデータを扱いやすい形式（例: <strong>JSONファイル</strong>）に整形して、プロジェクト内に保存します。</li>
<li><strong>ビルド時にデータを参照:</strong> Next.jsやGatsby.jsなどの静的サイトジェネレーターがサイトをビルドする際に、保存されたJSONファイルを読み込みます。</li>
<li><strong>静的ページに埋め込み:</strong> 読み込んだランキングデータ（記事タイトル、URL、PV数など）を使って、「人気記事一覧」のコンポーネントやリストを生成し、静的なHTMLページ内に埋め込みます。</li>
</ol>
<p>この方法であれば、サイトを<strong>デプロイ（ビルド）するたびに最新のランキング情報が反映</strong>され、かつサイト自体は高速な静的ファイルとして提供できるため、パフォーマンスを損なうこともありません。</p>
<hr>
<p>:::ad</p>
<h2>Google Analytics Data API (GA4) の有効化と基本準備</h2>
<p>まず、Google Analyticsのデータをプログラムから取得するために、APIの有効化と必要な情報を準備します。</p>
<p><img src="/9f99caf966076427147763643005c4a5/002-GoogleAnalyticsDataApi.png" alt="Google Cloud PlatformでGoogle Analytics Data APIを有効化する画面"></p>
<ol>
<li>
<p><strong>Google Cloud Platform (GCP) でAPIを有効化:</strong></p>
<ul>
<li>Google Cloud Console (<a href="https://console.cloud.google.com/">https://console.cloud.google.com/</a>) にアクセスし、プロジェクトを選択または新規作成します。</li>
<li>「APIとサービス」>「ライブラリ」で「<strong>Google Analytics Data API</strong>」を検索し、有効にします。</li>
</ul>
</li>
<li>
<p><strong>サービスアカウントを作成し、キーをダウンロード:</strong></p>
<ul>
<li>GCPの「APIとサービス」>「認証情報」で、「認証情報を作成」>「サービスアカウント」を選択し、新しいサービスアカウントを作成します。（名前は任意、ロールは不要な場合が多い）</li>
<li>作成したサービスアカウントを選択し、「キー」タブ>「鍵を追加」>「新しい鍵を作成」で「<strong>JSON</strong>」形式のキーを作成・ダウンロードします。<strong>このJSONファイルは後でスクリプトから使用します。大切に保管してください。</strong></li>
</ul>
</li>
<li>
<p><strong>サービスアカウントにGA4プロパティへのアクセス権を付与:</strong></p>
<ul>
<li>Google Analytics (<a href="https://analytics.google.com/">https://analytics.google.com/</a>) にアクセスし、対象のGA4プロパティの「管理」（歯車アイコン）を開きます。</li>
<li>「プロパティ設定」>「プロパティのアクセス管理」で、「+」ボタン >「ユーザーを追加」を選択します。</li>
<li>先ほど作成したサービスアカウントのメールアドレス（GCPの認証情報画面で確認できます）を入力し、「役割」として少なくとも「<strong>閲覧者</strong>」の権限を付与して追加します。</li>
</ul>
</li>
<li>
<p><strong>GA4プロパティIDを控える:</strong></p>
<ul>
<li>
<p>Google Analyticsの「管理」>「プロパティ設定」で、「<strong>プロパティ ID</strong>」（数字のみのID）を確認し、控えておきます。これもスクリプトで使用します。</p>
<p><img src="/4e1a18b07e990b5e6cdca55e2b6c61cb/004-AnalyticsPropertyID.png" alt="Google Analytics 4のプロパティID確認画面"></p>
</li>
</ul>
</li>
</ol>
<p>これらの準備（特にGCPやGA4の操作）については、Googleの公式ドキュメントや他の解説記事も参考に、ご自身の環境に合わせて進めてください。</p>
<hr>
<p>:::ad</p>
<h2>サービスアカウントの利用とセキュリティ上の注意点</h2>
<p><img src="/323d845aa6d5c9756b1aaa3e6277c5b0/003-GoogleServiceAccount.png" alt="Google Cloud Platformでのサービスアカウントキー管理画面"></p>
<p>Google Analytics Data APIのように、Googleのサービスにプログラムからアクセスする際には、通常「<strong>サービスアカウント</strong>」という特殊なアカウントを使用します。これは、個人のGoogleアカウントではなく、アプリケーションやスクリプトが自身を認証するためのものです。</p>
<p>前述のステップで作成しダウンロードした<strong>サービスアカウントキー（JSONファイル）<strong>には、そのサービスアカウントとしてAPIにアクセスするための秘密の情報が含まれています。そのため、このキーファイルの取り扱いには</strong>細心の注意が必要</strong>です。</p>
<ul>
<li><strong>絶対に公開しない:</strong> JSONキーファイルは、<strong>Gitリポジトリ（特にPublicリポジトリ）に絶対にコミットしないでください</strong>。誤って公開してしまうと、第三者に不正利用される危険性があります。<code>.gitignore</code>ファイルにキーファイル名を追加し、Gitの管理対象から除外しましょう。</li>
<li><strong>安全な場所に保管:</strong> ローカル開発環境ではプロジェクトルートなどに置くこともありますが、本番環境（デプロイ先）では、環境変数やシークレット管理機能を使って安全にキー情報を渡すのが一般的です。（後述のスクリプト例参照）</li>
<li><strong>権限は最小限に:</strong> GA4プロパティでサービスアカウントに付与する権限は、データ取得に必要な最低限の「閲覧者」権限にしておきましょう。</li>
</ul>
<hr>
<p>:::ad</p>
<h2>Node.jsによる人気記事データ取得スクリプトの実装例</h2>
<p>ここでは、準備したサービスアカウントキーとGA4プロパティIDを使って、実際に人気記事データを取得し、JSONファイルとして保存するNode.jsスクリプトの例を示します。</p>
<p><strong>【準備】</strong></p>
<ol>
<li>プロジェクトのルートディレクトリに<code>scripts</code>フォルダを作成し、その中に以下のスクリプトを<code>fetch-popular-posts.js</code>などの名前で保存します。</li>
<li>プロジェクトのルートディレクトリに、GCPからダウンロードしたサービスアカウントキーのJSONファイルを<code>service-account.json</code>（または任意の名前）として配置します。</li>
<li>プロジェクトのルートディレクトリに<code>.env</code>ファイルを作成し、以下の形式でGA4プロパティIDを記述します（<code>?????????</code>の部分は実際のIDに置き換えてください）。</li>
</ol>
<pre><code>GA4_PROPERTY_ID=?????????
</code></pre>
<ol start="4">
<li><strong>【重要】</strong><code>service-account.json</code>と<code>.env</code>ファイルは機密情報を含むため、必ず<code>.gitignore</code>ファイルに追加して、Gitリポジトリに含まれないようにします。</li>
</ol>
<pre><code># .gitignore ファイルの例
service-account.json
.env
</code></pre>
<ol start="5">
<li>必要なnpmパッケージをインストールします。ターミナルで以下のコマンドを実行してください。</li>
</ol>
<pre><code class="language-bash">npm install @google-analytics/data dotenv
</code></pre>
<pre><code>（または `yarn add @google-analytics/data dotenv`）
</code></pre>
<p><strong>【スクリプト例: <code>scripts/fetch-popular-posts.js</code>】</strong></p>
<pre><code class="language-javascript">// .envファイルから環境変数を読み込む
require("dotenv").config();
// Google Analytics Data APIクライアントライブラリ
const { BetaAnalyticsDataClient } = require("@google-analytics/data");
// Node.jsのファイルシステムとパス操作モジュール
const fs = require("fs");
const path = require("path");

// 非同期関数として人気記事取得処理を定義
async function fetchPopularPosts() {
  let credentials;

  // --- 認証情報の設定 ---
  // 本番環境用に環境変数 GA_CREDENTIALS_JSON があればそれを使う (推奨)
  if (process.env.GA_CREDENTIALS_JSON) {
    try {
      credentials = JSON.parse(process.env.GA_CREDENTIALS_JSON);
    } catch (e) {
      console.error("Failed to parse GA_CREDENTIALS_JSON environment variable.", e);
      process.exit(1);
    }
  }
  // 環境変数がなければ、ローカルの service-account.json を読み込む
  else {
    const keyFilePath = path.resolve(__dirname, "../service-account.json");
    if (fs.existsSync(keyFilePath)) {
      credentials = JSON.parse(fs.readFileSync(keyFilePath, "utf8"));
    } else {
      console.error(`Service account key file not found at ${keyFilePath}. Or set GA_CREDENTIALS_JSON env var.`);
      process.exit(1);
    }
  }

  // --- GA4 Data API クライアントの初期化 ---
  const analyticsDataClient = new BetaAnalyticsDataClient({
    credentials: {
      client_email: credentials.client_email,
      private_key: credentials.private_key,
    },
  });

  // --- GA4 プロパティIDの取得 ---
  const propertyId = process.env.GA4_PROPERTY_ID;
  if (!propertyId) {
    throw new Error("GA4_PROPERTY_ID is not set in environment variables.");
  }

  // --- APIリクエストの実行 ---
  try {
    const [response] = await analyticsDataClient.runReport({
      // プロパティIDを指定
      property: `properties/${propertyId}`,
      // データ取得期間 (過去30日間)
      dateRanges: [{ startDate: "30daysAgo", endDate: "today" }],
      // 取得するディメンション (ページのパス, ページのタイトル)
      dimensions: [{ name: "pagePath" }, { name: "pageTitle" }],
      // 取得するメトリクス (表示回数)
      metrics: [{ name: "screenPageViews" }], // GA4では "ga:pageviews" ではなく "screenPageViews"
      // 並び順 (表示回数の降順 = 多い順)
      orderBys: [{ metric: { metricName: "screenPageViews" }, desc: true }],
      // 取得件数 (上位10件)
      limit: 10,
      // ディメンションフィルタ (ブログ記事パス '/blog/' で始まるページのみ対象とする例)
      dimensionFilter: {
        filter: {
          fieldName: "pagePath",
          stringFilter: {
            matchType: "PARTIAL_REGEXP", // 正規表現に部分一致
            value: "^/blog/", // /blog/ で始まるパス
          },
        },
      },
    });

    // --- 結果の整形 ---
    const popularPosts = response.rows.map((row) => ({
      pagePath: row.dimensionValues[0].value,
      pageTitle: row.dimensionValues[1].value,
      pageViews: parseInt(row.metricValues[0].value, 10), // 閲覧数を整数に変換
    }));

    // --- JSONファイルへの書き出し ---
    // プロジェクトルートに data フォルダがなければ作成
    const dataDir = path.resolve(__dirname, "../data");
    if (!fs.existsSync(dataDir)) {
      fs.mkdirSync(dataDir);
    }
    // dataフォルダ内に popular-posts.json として保存
    fs.writeFileSync(
      path.join(dataDir, "popular-posts.json"),
      JSON.stringify(popularPosts, null, 2) // 読みやすいように整形して出力
    );
    console.log("Successfully fetched popular posts and saved to data/popular-posts.json");

  } catch (error) {
      console.error("Error fetching Google Analytics data:", error);
      process.exit(1); // エラー発生時はプロセスを終了
  }
}

// 関数を実行
fetchPopularPosts();
</code></pre>
<p><strong>スクリプトのポイント：</strong></p>
<ul>
<li><strong>認証情報:</strong> 環境変数 <code>GA_CREDENTIALS_JSON</code> があればそれを優先的に使用し、なければローカルの <code>service-account.json</code> を読み込みます。これにより、ローカル開発と本番環境（Cloudflare Pagesなど）で認証情報を安全に扱うことができます。（本番環境では、環境変数にJSONキーの内容全体を設定するのが一般的です）</li>
<li><strong>APIリクエスト:</strong> <code>runReport</code> メソッドでGA4にリクエストを送ります。
<ul>
<li><code>dateRanges</code>: データ取得期間を指定します（例: "30daysAgo"～"today"）。</li>
<li><code>dimensions</code>: 取得したい情報の種類（ページのパス、タイトルなど）を指定します。</li>
<li><code>metrics</code>: 集計したい指標（表示回数 <code>screenPageViews</code> など）を指定します。</li>
<li><code>orderBys</code>: 並び順を指定します（例: 表示回数の多い順）。</li>
<li><code>limit</code>: 取得する最大件数を指定します。</li>
<li><code>dimensionFilter</code>: <strong>【重要】</strong> 取得するページを絞り込むためのフィルターです。この例では「ページのパス (<code>pagePath</code>) が正規表現 <code>^/blog/</code> に一致するもの」だけを取得しています。これにより、ブログ記事以外のページ（トップページ <code>/</code> など）がランキングに含まれるのを防ぎます。<strong>この <code>value</code> は、あなたのブログ記事のURL構造に合わせて必ず変更してください。</strong></li>
</ul>
</li>
<li><strong>結果の整形と保存:</strong> APIからの応答データを扱いやすいJSON形式（パス、タイトル、閲覧数のオブジェクト配列）に変換し、<code>data/popular-posts.json</code> ファイルに書き出します。</li>
</ul>
<p>このスクリプトをターミナルで <code>node scripts/fetch-popular-posts.js</code> と実行すると、<code>data</code> フォルダ（なければ作成される）に人気記事のデータがJSONファイルとして保存されます。</p>
<hr>
<p>:::ad</p>
<h2>ビルド時にJSONデータを活用し、静的サイトに組み込む方法</h2>
<p>人気記事データを含むJSONファイルが用意できたら、あとは静的サイトジェネレーター（Next.jsやGatsby.js）のビルドプロセスでこのJSONファイルを読み込み、ページやコンポーネントにデータを渡して表示するだけです。</p>
<p>しかし、ここで重要なのは「<strong>サイトをビルド（デプロイ）するたびに、このJSONファイルが最新の情報に更新されるようにする</strong>」ことです。そうしないと、ランキングが古いままになってしまいます。</p>
<p>これを実現するには、通常、プロジェクトの <code>package.json</code> ファイル内の <code>scripts</code> セクションを編集し、ビルドコマンドの実行前にデータ取得スクリプトが実行されるように設定します。</p>
<p><strong>【<code>package.json</code> の設定例 (Gatsby.jsの場合)】</strong></p>
<pre><code class="language-json">{
  "scripts": {
    // データ取得スクリプトを実行するコマンドを定義
    "fetch-data": "node scripts/fetch-popular-posts.js",
    // 開発サーバー起動時にもデータを取得する場合 (任意)
    "develop": "npm run fetch-data &#x26;&#x26; gatsby develop",
    // 本番ビルド時に必ずデータ取得を実行するように設定
    "build": "npm run fetch-data &#x26;&#x26; gatsby build",
    // startコマンドも同様に（開発用サーバー起動など）
    "start": "npm run develop",
    // 他にも test や serve など...
  }
}
</code></pre>
<p>この例では、</p>
<ol>
<li><code>fetch-data</code> という名前でデータ取得スクリプトを実行するコマンドを定義しています。</li>
<li><code>build</code> コマンド（本番用ビルド）の実行時に、まず <code>npm run fetch-data</code> を実行し、その後に <code>gatsby build</code> が実行されるように設定しています。（<code>&#x26;&#x26;</code> はコマンドを順番に実行する指定子）</li>
<li>同様に、開発サーバー起動時 (<code>develop</code>, <code>start</code>) にもデータを取得するようにしています（開発中も最新データで確認したい場合）。Next.jsの場合は <code>next dev</code> や <code>next build</code> の前に <code>npm run fetch-data &#x26;&#x26;</code> を追加します。</li>
</ol>
<p>このように設定しておくことで、<code>npm run build</code> コマンドを実行（またはVercelやCloudflare Pagesなどのホスティングサービスで自動ビルドが実行される際）に、</p>
<ol>
<li>まず <code>fetch-popular-posts.js</code> が実行され、最新の人気記事データが <code>data/popular-posts.json</code> に保存されます。</li>
<li>次に、Gatsby (またはNext.js) のビルドプロセスが開始され、その中で <code>data/popular-posts.json</code> ファイルを読み込み、人気記事一覧を含む静的HTMLページが生成されます。</li>
</ol>
<p>この「<strong>ビルド時データフェッチ</strong>」のアプローチにより、サイト訪問者は常に（ビルド時点での）最新の人気記事一覧を高速な静的ページとして閲覧でき、サーバーへの負荷やAPIの実行時呼び出しを気にする必要がなくなります。これはパフォーマンスとセキュリティの観点からもJAMstack構成の大きなメリットです。</p>
<p>あとは、各フレームワークの方法に従って、ビルド時にJSONデータを読み込み、Reactコンポーネントなどに渡して表示する部分を実装してください。（例: Gatsbyでは<code>gatsby-node.js</code>でJSONを読み込みGraphQLデータレイヤーに追加、Next.jsでは<code>getStaticProps</code>内で<code>fs.readFile</code>を使ってJSONを読み込むなど）</p>
<hr>
<p>:::ad</p>
<h2>まとめ：静的サイトでもAPI連携で人気記事一覧は実現できる！</h2>
<p><strong>JAMstack</strong>や<strong>ヘッドレスCMS</strong>を採用した<strong>静的サイト</strong>環境では、WordPressのような従来の動的CMSとは異なり、「人気記事一覧」をリアルタイムで表示することは困難です。</p>
<p>しかし、この記事で解説したように、<strong>Google Analytics Data API (GA4)</strong> を活用し、<strong>サイトのビルドプロセス前に</strong>アクセスデータを取得・整形してJSONファイルなどの形で保存し、それをビルド時に静的ページに埋め込むというアプローチを取ることで、静的サイトのメリット（高速表示、高セキュリティ、スケーラビリティ）を維持しつつ、<strong>最新の人気記事ランキングを表示することが可能</strong>になります。</p>
<p>この方法は、特定のCMSやフロントエンドフレームワークに強く依存しないため、<strong>Next.js</strong>、<strong>Gatsby.js</strong>だけでなく、様々なJAMstack構成に応用できます。サービスアカウントキーの安全な管理や、APIリクエストのフィルター設定など、いくつか注意点はありますが、一度仕組みを構築すれば、自動でランキングが更新される便利な機能を実現できます。</p>
<p>ぜひ、ご自身の静的ブログサイトやWebサイトの構成に合わせて、この手法を参考に「人気記事一覧」の実装に挑戦してみてください。</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/6ee80f0db975ccec9e791d933a7d498a/20241215-Google-Analytics-Data-API%E3%81%A7%E9%9D%99%E7%9A%84%E3%83%96%E3%83%AD%E3%82%B0%E3%82%B5%E3%82%A4%E3%83%88%E3%81%AB%E3%80%8E%E4%BA%BA%E6%B0%97%E8%A8%98%E4%BA%8B%E4%B8%80%E8%A6%A7%E3%80%8F%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B.jpg" medium="image"/></item><item><title><![CDATA[【VRChat】服の裏地が透ける/透明になる問題を修正！MeshFlipperの使い方]]></title><description><![CDATA[VRChatでコートやマントの裏地が透けて見える問題を、MeshFlipperで解決する方法を解説]]></description><link>https://uhiyama-lab.com/ja/blog/dialy/vrchat-meshflipper/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/dialy/vrchat-meshflipper/</guid><category><![CDATA[vrchat]]></category><pubDate>Sat, 14 Dec 2024 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>VRChatでマントやロングコートなど、丈の長い衣装を着ているアバターを使ったとき、「あれ？ フレンド視点だと服の裏地が透明になって透けて見える…」と指摘された経験はありませんか？</p>
<p>自分視点では特に問題ないのに、他人から見るとコートの裏側などが描画されず、体が透けて見えてしまう…。これはVRChatでアバターを使っていると比較的よく遭遇する現象です。</p>
<p>この「衣装の裏地が透ける問題」、実は簡単なUnityツールで解決できる場合があります。この記事では、その原因と、解決策となるツール「MeshFlipper」の使い方について、備忘録も兼ねて解説します。</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#cause">裏地が透ける原因</a></li>
<li><a href="#meshflipper-usage">MeshFlipperの導入と使い方</a></li>
<li><a href="#technical-details">技術的な仕組み</a></li>
<li><a href="#summary">まとめ</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>裏地が透ける原因 {#cause}</h2>
<p><img src="/ba051a855353fe211521d4a44ecb2269/02_20241214_VRC-%E6%9C%8D%E3%81%AE%E8%A3%8F%E5%9C%B0.jpg" alt="VRChat内でコートの裏地が透けて見えている状態の例"></p>
<p>この現象の主な原因は、多くの3Dモデルで採用されている「片面レンダリング（または片面描画）」という仕組みにあります。</p>
<p>簡単に言うと、3Dモデルを構成するポリゴン（板のようなもの）は、基本的に「表面」だけを描画し、「裏面」は描画しないことで、コンピューターの描画負荷を軽くしています。体にぴったりフィットした服などは、外側（表面）だけが見えれば十分なので、この仕組みで問題ありません。</p>
<p>しかし、コート、マント、スカートのようにヒラヒラしたり、裾が長かったりする衣装の場合、キャラクターが動いたり、特定の角度から見たりすると、衣装の「裏面」が見えてしまうことがあります。</p>
<p>片面レンダリングでは、この「裏面」は描画対象外なので、結果として何も描画されず、背景や体が透けて見えてしまうのです。これが、他人視点や三人称視点のカメラで見たときに「裏地が透明になっている」「バグで描画されていない」ように見える正体です。</p>
<p>この問題を解決するには、単純に「衣装の裏面もちゃんと描画されるようにする」必要があります。そのための処理を簡単に行ってくれるのが、次にご紹介する「MeshFlipper」というツールです。</p>
<p>:::ad</p>
<h2>MeshFlipperの導入と使い方 {#meshflipper-usage}</h2>
<p>衣装の裏地が透ける問題の解決方法を探していたところ、以下の記事で「MeshFlipper」というUnityエディタ拡張ツールの存在を知りました。（情報感謝です！）</p>
<p>:::post-link{url="<a href="https://note.com/moegitsubasa/n/nd73471257f31#fcc0f527-bb59-4f40-9edf-f6ba3ca05c92">https://note.com/moegitsubasa/n/nd73471257f31#fcc0f527-bb59-4f40-9edf-f6ba3ca05c92</a>" text="Android(Quest)対応方法マニュアルAdvanced!! - もえぎつばさ 様"}</p>
<p>MeshFlipperは、Unity上で3Dモデルのメッシュデータ（形状データ）を加工し、裏面も描画されるように両面化してくれる非常に便利なツールです。fum1さんがBoothで無料配布してくれています。</p>
<p>:::post-link{url="<a href="https://booth.pm/ja/items/5645609">https://booth.pm/ja/items/5645609</a>" text="【無料】メッシュの裏面を生成するツール / Mesh Flipper (BOOTH)"}</p>
<p>導入と基本的な使い方は以下の通り、とても簡単です。</p>
<p><img src="/30a151be27cea699863e52e5dca7a102/03_20241214_VRC-%E6%9C%8D%E3%81%AE%E8%A3%8F%E5%9C%B0.jpg" alt="MeshFlipperのウィンドウと設定オプション"></p>
<ol>
<li>
<p>MeshFlipperをUnityプロジェクトにインポートする
まず、上記BoothからMeshFlipperをダウンロードします。ダウンロードしたファイルに含まれる<code>MeshFlipper.cs</code>というスクリプトファイルを、Unityプロジェクトの<code>Assets</code>フォルダ内の好きな場所（例えば<code>Editor</code>フォルダなど）にドラッグ&#x26;ドロップしてインポートします。</p>
</li>
<li>
<p>裏地が透ける衣装のメッシュを選択する
UnityのHierarchyウィンドウで、修正したいアバターを選択し、その中から裏地が透けてしまう衣装のパーツ（オブジェクト）を探します。そのオブジェクトにアタッチされている<code>Skinned Mesh Renderer</code>または<code>Mesh Renderer</code>コンポーネントを見つけてください。</p>
</li>
<li>
<p>MeshFlipperウィンドウを開く
Unityのメニューバーから <code>fum1</code> > <code>Mesh Flipper</code> を選択し、MeshFlipperの専用ウィンドウを開きます。</p>
</li>
<li>
<p>オプションを設定する（重要：TwoSidesを選択）
MeshFlipperウィンドウが開いたら、対象の衣装パーツ（Skinned Mesh Rendererなどが付いたオブジェクト）を選択した状態で、以下のオプションを設定します。</p>
<ul>
<li>✅ TwoSides: このオプションにチェックを入れるのが基本です。これがメッシュを両面化する機能で、チェックを入れると裏面も描画されるようになります。</li>
<li><code>Flip</code>: こちらはポリゴンの面の向き（法線）を反転させる機能です。通常、裏地が透ける問題の解決には<code>TwoSides</code>だけで十分です。</li>
</ul>
<p>ほとんどの場合、<code>TwoSides</code>にチェックを入れるだけで裏地が透ける問題は解決します。</p>
</li>
<li>
<p>「Create Mesh」ボタンを押して実行する
オプションを設定したら、「Create Mesh」ボタンをクリックします。すると、MeshFlipperが選択したメッシュを両面化処理し、新しいメッシュデータを作成して自動的に元のメッシュと差し替えてくれます。（元のメッシュデータもバックアップとして残る場合があります）</p>
</li>
</ol>
<p>処理が完了したら、Unityのシーンビューで衣装の裏側から見てみたり、VRChatにアップロードしてフレンドに確認してもらったりしましょう。裏地がちゃんと描画され、透けなくなっていれば成功です！</p>
<h2>技術的な仕組み {#technical-details}</h2>
<p>少しだけ技術的な話をすると、MeshFlipperは内部で以下のような処理を行っています。</p>
<ul>
<li>頂点データの複製と法線の反転: 元のメッシュの頂点データをコピーし、コピーした側のポリゴンの向き（法線）を反転させます。</li>
<li>メッシュの結合: 元のメッシュ（表面）と、法線を反転させたコピー（裏面用）を一つに結合します。</li>
</ul>
<p>これにより、実質的に「表面用のポリゴン」と「裏面用のポリゴン」の両方を持つメッシュデータが作成されます。結果として、どちらの方向から見てもポリゴンが描画されるようになり、裏地が透ける問題が解決するという仕組みです。</p>
<p>要するに、MeshFlipperは「本来は存在しなかった（描画されなかった）服の裏側」を擬似的に作ってくれるツール、と理解しておけば大丈夫です。</p>
<p>この複雑な処理をボタン一つで実行してくれるので、特にコートやスカートなど、裏側が見えやすい衣装を扱う際には非常に重宝します。</p>
<p>:::ad</p>
<h2>まとめ {#summary}</h2>
<p>VRChatでアバターの衣装（特にコートやマント、スカートなど）の裏地が透けてしまう、透明になってしまうという問題に遭遇したら、まずはUnityエディタ拡張ツール「MeshFlipper」の利用を検討してみてください。</p>
<p>簡単な手順でメッシュを両面化でき、多くの場合、これだけで裏地が正常に描画されるようになります。</p>
<p>MeshFlipperは、開発者のfum1さんがBoothで無料配布してくれています。同じ悩みを持つVRChatユーザーやアバター改変を行う方は、ぜひ導入して試してみることをお勧めします！</p>
<p>:::post-link{url="<a href="https://booth.pm/ja/items/5645609">https://booth.pm/ja/items/5645609</a>" text="【無料】メッシュの裏面を生成するツール / Mesh Flipper (BOOTH)"}</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/d3ad67638072a83f635c60294eedd529/Thumb_20241214_VRC-%E6%9C%8D%E3%81%AE%E8%A3%8F%E5%9C%B0.jpg" medium="image"/></item><item><title><![CDATA[【解決】Noto Sans JPフォントを50%軽量化！OTF情報を保持するFontToolsサブセット化]]></title><description><![CDATA[FontToolsでNoto Sans JPフォントのサブセット化を行い、OTF情報を保持したままファイルサイズを50%削減してページ速度を改善する方法]]></description><link>https://uhiyama-lab.com/ja/blog/webdev/optimize-subset-fonttools/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/webdev/optimize-subset-fonttools/</guid><category><![CDATA[font]]></category><pubDate>Thu, 05 Dec 2024 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Webサイトの表示速度が遅い…その原因の一つに、<strong>Webフォントのファイルサイズが大きい</strong>ことが挙げられます。特に日本語フォントは多くの文字を含むため、データ量が膨大になりがちです。この記事では、人気のある日本語フォント「<strong>Noto Sans JP</strong>」を例に、必要な文字だけを抽出して軽量化する「<strong>サブセット化</strong>」を行い、<strong>ページ読み込み速度を改善</strong>する方法を解説します。</p>
<p>様々なツールがある中で、当初「サブセットフォントメーカー」を試しましたが、私の環境では表示崩れが発生しました。最終的にPythonライブラリ「<strong>FontTools</strong>」を用いることで、<strong>OTF情報（文字詰めなどの見た目を整える情報）を維持</strong>したまま、Noto Sans JPフォントを<strong>WOFF2形式</strong>にサブセット化し、ファイルサイズを<strong>50%以上削減</strong>、読み込み速度を向上させることに成功しました。その具体的な手順と注意点を備忘録としてまとめます。</p>
<p>:::ad</p>
<p><strong>この記事の内容</strong></p>
<ol>
<li>フォントのサブセット化とは？なぜページ速度改善に必要なのか</li>
<li>【失敗談】サブセットフォントメーカーでOTF情報が欠落し表示が崩れた件</li>
<li>解決策！FontToolsを使った高精度なサブセット作成手順</li>
<li>まとめ：FontToolsによるサブセット化の効果とページ速度への貢献</li>
</ol>
<hr>
<h2>1. フォントのサブセット化とは？なぜページ速度改善に必要なのか</h2>
<p><strong>フォントのサブセット化</strong>とは、簡単に言うと、フォントファイルの中から<strong>Webサイトで実際に使用する文字だけを抜き出し、不要な文字データを削除してファイルサイズを小さくする</strong>技術のことです。「普段使わない難しい漢字や特殊記号まで全部読み込むのは無駄だよね？使う文字だけに絞って軽くすれば、サイトがもっと速く表示されるはず！」という考え方に基づいています。</p>
<p>このブログでも使用している定番の日本語フォント「<strong>Noto Sans JP</strong>」は、Google Fontsからも入手できる高品質なフォントですが、非常に多くの文字（ひらがな、カタカナ、常用漢字、人名漢字、記号など）を網羅しています。</p>
<p><a href="https://fonts.google.com/noto/specimen/Noto+Sans+JP">Noto Sans Japanese (Google Fonts)</a></p>
<p>そのため、ダウンロードしてそのままWebフォント（例えばWOFF形式）としてサイトに組み込むと、標準の太さ（Regular）や太字（Bold）だけで、<strong>それぞれ約4MBものファイルサイズ</strong>になってしまいます。これでは、ユーザーがページを開くたびに大きなデータをダウンロードする必要があり、表示速度のボトルネックになりかねません。</p>
<p><img src="/43e98bd6513397d536aa6416e509273c/01_%E9%9D%9E%E3%82%B5%E3%83%96%E3%82%BB%E3%83%83%E3%83%88%E7%8A%B6%E6%85%8B%E3%81%AE%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88%E3%82%B5%E3%82%A4%E3%82%BA.png" alt="サブセット化前のNoto Sans JPフォントファイルサイズ（各約4MB）"></p>
<p>（↑サブセット化前のフォントファイル。サイズが大きいことがわかります。）</p>
<p>そこで重要になるのが、この巨大なフォントファイルを軽量化する「サブセット化」なのです。</p>
<hr>
<p>:::ad</p>
<h2>2. 【失敗談】サブセットフォントメーカーでOTF情報が欠落し表示が崩れた件</h2>
<p>フォントのサブセット化について調べると、「サブセットフォントメーカー」というGUIツールがよく紹介されています。私もいくつかの解説サイトを参考に、このツールと「WOFFコンバータ」を使ってNoto Sans JP（TTF形式）からサブセットWOFFフォントを作成してみました。</p>
<p><img src="/3161406842fc07175719871426a3e6dc/02_%E3%82%B5%E3%83%96%E3%82%BB%E3%83%83%E3%83%88%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88%E3%83%A1%E3%83%BC%E3%82%AB%E3%83%BC%E3%81%AB%E3%82%88%E3%82%8B%E5%A4%89%E6%8F%9B.jpg" alt="サブセットフォントメーカーの画面"></p>
<p>しかし、生成されたフォントをサイトに適用したところ、<strong>予期せぬ表示崩れ</strong>が発生してしまいました。</p>
<p><img src="/680a82a264eda5c5afd6600b0cc79928/03_OTF%E6%83%85%E5%A0%B1%E6%AC%A0%E6%90%8D%E3%81%AB%E3%82%88%E3%82%8B%E8%A1%A8%E7%A4%BA%E3%82%BA%E3%83%AC.png" alt="サブセットフォントメーカーで変換後の表示崩れ（文字間隔が広がる）"></p>
<p>（↑左が元の表示、右がサブセットフォントメーカーで変換後の表示。記事タイトル部分の文字間隔が不自然に広がっています。）</p>
<p>原因を調査したところ、どうやら「サブセットフォントメーカー」がフォント変換を行う過程で、Noto Sans JPに含まれる重要な<strong>OTF（OpenType Font）情報</strong>の一部、特に文字間隔の調整（カーニング、ペアカーニング）を行うための情報（GPOSテーブルなど）を削除してしまったようです。</p>
<p>OTF情報は、文字の形だけでなく、文字と文字の間のアキを自動調整したり、特定の文字の組み合わせで合字（リガチャ）に置き換えたり（GSUBテーブル）といった、<strong>フォントの見た目を美しく整えるための重要なデータ</strong>です。これが失われたために、文字間隔がデフォルトのままになり、レイアウトが崩れてしまったと考えられます。</p>
<p>軽量化はできても、見た目が損なわれては意味がありません。そこで、<strong>OTF情報を維持したまま、高精度なサブセット化ができるツール</strong>を探し、Python製のライブラリ「<strong>FontTools</strong>」にたどり着きました。</p>
<hr>
<p>:::ad</p>
<h2>3. 解決策！FontToolsを使った高精度なサブセット作成手順</h2>
<p><strong>FontTools</strong>は、フォントファイルを操作するための強力なPythonライブラリ群です。オープンソースで開発されており、TTFやOTF形式のフォントに対して、サブセット化はもちろん、情報の編集、形式変換など、様々な高度な処理を行うことができます。コマンドラインから手軽に利用できるのも魅力です。</p>
<p><a href="https://github.com/fonttools/fonttools">FontTools (GitHubリポジトリ)</a></p>
<p>FontToolsを使ってNoto Sans JPのサブセットを作成する手順は以下の通りです。</p>
<h3>3-1. FontToolsのインストール</h3>
<p>FontToolsはPythonライブラリなので、まずお使いの環境にPythonがインストールされていることを確認してください。Pythonが利用できる環境であれば、ターミナル（コマンドプロンプト）で以下の<code>pip</code>コマンドを実行してFontToolsをインストールします。</p>
<pre><code class="language-bash">pip install fonttools brotli zopfli
</code></pre>
<p>※ WOFF2形式の扱いや、より高い圧縮率（<code>--with-zopfli</code>オプション）を実現するために、<code>brotli</code> と <code>zopfli</code> も一緒にインストールしておくと良いでしょう。</p>
<h3>3-2. サブセット化コマンドの実行</h3>
<p>次に、ターミナルで<code>pyftsubset</code>コマンドを使ってサブセット化を実行します。Noto Sans JPの元フォントファイル（例: <code>NotoSansJP-Regular.ttf</code>）があるディレクトリで、以下のコマンドを実行してください。（ファイル名やパスはご自身の環境に合わせて変更してください。）</p>
<pre><code class="language-bash"># NotoSansJP-Regular.ttf からサブセット NotoSansJP-Regular.woff2 を作成する例
pyftsubset NotoSansJP-Regular.ttf \
  --output-file=NotoSansJP-Regular.woff2 \
  --flavor=woff2 \
  --layout-features='*' \
  --unicodes='U+0020-007E,U+3000-30FF,U+4E00-9FAF,U+FF01-FF60,U+FF65-FF9F' \
  --with-zopfli \
  --verbose
</code></pre>
<p>同様に、Bold（太字）など他のウェイトのフォントもサブセット化する場合は、元ファイル名と出力ファイル名を変更してコマンドを実行します。</p>
<p><strong>コマンドオプションの解説:</strong></p>
<ul>
<li>
<p><strong><code>NotoSansJP-Regular.ttf</code></strong></p>
<p>サブセット化の元となるフォントファイル（TTFまたはOTF）。</p>
</li>
<li>
<p><strong><code>--output-file=NotoSansJP-Regular.woff2</code></strong></p>
<p>出力するサブセットフォントのファイル名とパスを指定。</p>
</li>
<li>
<p><strong><code>--flavor=woff2</code></strong></p>
<p>出力形式を<strong>WOFF2</strong>に指定。WOFF2はWebフォント用に最適化された形式で、圧縮率が高くファイルサイズを小さくできます。</p>
</li>
<li>
<p><strong><code>--layout-features='*'</code></strong></p>
<p><strong>【重要】</strong> これがOTF情報を保持するためのオプションです。<code>'*'</code>を指定することで、カーニング（文字詰め）やリガチャ（合字）などのOpenTypeレイアウト機能に関する情報をすべて保持します。これにより、サブセットフォントメーカーで発生したような表示崩れを防ぎます。</p>
</li>
<li>
<p><strong><code>--unicodes='...'</code></strong></p>
<p><strong>【重要】</strong> サブセットに含める文字のUnicode範囲を指定します。ここで指定した範囲外の文字はフォントに含まれなくなり、表示できなくなる（文字化けする）ので慎重に設定します。上記コマンド例の範囲は以下をカバーしています。</p>
<ul>
<li><code>U+0020-007E</code>: 基本的な半角英数字と記号 (ASCII)</li>
<li><code>U+3000-30FF</code>: 全角スペース、句読点、ひらがな、カタカナなど</li>
<li><code>U+4E00-9FAF</code>: CJK統合漢字（一般的に使われる漢字の多くをカバー）</li>
<li><code>U+FF01-FF60</code>: 全角の英数字や記号の一部</li>
<li><code>U+FF65-FF9F</code>: 半角カタカナ</li>
</ul>
<p>※ サイトで使用する文字に合わせて、この範囲を調整する必要があります。例えば、特定の記号や第二水準漢字なども使う場合は、対応するUnicode範囲を追加します。</p>
</li>
<li>
<p><strong><code>--with-zopfli</code></strong></p>
<p>Googleが開発したZopfli圧縮アルゴリズムを使用し、WOFF2ファイルをさらに高圧縮します。ファイルサイズ削減に効果的です。（<code>zopfli</code>ライブラリのインストールが必要）</p>
</li>
<li>
<p><strong><code>--verbose</code></strong></p>
<p>サブセット化の処理中に詳細なログを出力します。エラーの原因調査や、どの情報が含まれたか/除外されたかを確認するのに役立ちます。</p>
</li>
</ul>
<h3>3-3. 生成されたWOFF2ファイルをCSSで読み込む</h3>
<p>コマンドが正常に完了すると、指定した場所に軽量化されたWOFF2形式のサブセットフォントファイルが生成されます。あとは、このファイルをWebサーバーにアップロードし、CSSの<code>@font-face</code>規則を使って読み込むように設定を変更します。</p>
<pre><code class="language-css">/* Regular (標準) */
@font-face {
  font-family: 'Noto Sans JP'; /* フォント名を指定 */
  /* ↓ 生成したWOFF2ファイルのパスを指定 */
  src: url('/path/to/your/fonts/NotoSansJP-Regular.woff2') format('woff2');
  font-weight: 400; /* または normal */
  font-style: normal;
  /* font-display: swap; はフォント読み込み中の挙動を指定するプロパティ。設定推奨 */
  font-display: swap;
}

/* Bold (太字) */
@font-face {
  font-family: 'Noto Sans JP';
  /* ↓ 太字用WOFF2ファイルのパスを指定 */
  src: url('/path/to/your/fonts/NotoSansJP-Bold.woff2') format('woff2');
  font-weight: 700; /* または bold */
  font-style: normal;
  font-display: swap;
}

/* 必要に応じて他のウェイトも同様に設定 */

/* body や 各要素で font-family を指定 */
body {
  font-family: 'Noto Sans JP', sans-serif;
}
</code></pre>
<p>これで、軽量化されたサブセットフォントがWebサイトで利用されるようになります。</p>
<hr>
<p>:::ad</p>
<h2>4. まとめ：FontToolsによるサブセット化の効果とページ速度への貢献</h2>
<p>FontToolsを使ってNoto Sans JPのサブセット化を行った結果、以下のような効果が得られました。</p>
<p><img src="/90946f0b1716b602c5462dafbcffd828/04_%E3%82%B5%E3%83%96%E3%82%BB%E3%83%83%E3%83%88%E5%8C%96%E3%81%AB%E3%82%88%E3%82%8B%E5%AE%B9%E9%87%8F%E5%89%8A%E6%B8%9B.png" alt="FontToolsでサブセット化した後のフォントファイルサイズ（各約1.5MB）"></p>
<p>（↑FontToolsでサブセット化後。ファイルサイズが大幅に削減されています！）</p>
<p>これにより、ブラウザがダウンロードするデータ量が減り、ページの読み込み時間も改善されました。以下のネットワーク分析結果は、変更前と変更後のフォント読み込み時間を示しています。</p>
<p><strong>変更前（サブセット化前）：</strong></p>
<p><img src="/730fffd840b22ada70f842d6eee15147/05_%E5%A4%89%E6%9B%B4%E5%89%8D%E3%81%AE%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E5%88%86%E6%9E%90.png" alt="サブセット化前のフォント読み込み時間"></p>
<p><strong>変更後（FontToolsでサブセット化後）：</strong></p>
<p><img src="/635e4a74eb8fe92487999793128e351c/06_%E5%A4%89%E6%9B%B4%E5%BE%8C%E3%81%AE%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E5%88%86%E6%9E%90.png" alt="FontToolsでサブセット化後のフォント読み込み時間（改善）"></p>
<p>ファイルサイズ削減に伴い、読み込み時間も短縮されていることが確認できます。これは、Core Web Vitalsなどのページ速度指標の改善にも繋がります。</p>
<p>FontToolsはコマンドラインツールですが、一度コマンドを理解すれば、OTF情報を保持したまま高精度なサブセット化が可能です。Webサイトの表示速度を改善したい、特に日本語フォントの重さにお悩みの方は、ぜひFontToolsによるサブセット化を試してみてはいかがでしょうか。含める文字範囲（<code>--unicodes</code>）をさらに絞り込むなど、工夫次第でさらなる軽量化も可能です。</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/38ceaa1c1ec73ee22af828c487833ce2/20241205-NotoSansJP-%E3%82%B5%E3%83%96%E3%82%BB%E3%83%83%E3%83%88.jpg" medium="image"/></item><item><title><![CDATA[SSDをクローン換装してCドライブの容量を500GBから2TBに拡張した備忘録]]></title><description><![CDATA[Cドライブの容量不足を解消するため、SSDをクローン換装して500GBから2TBに拡張。起動エラー0xc000000eの対処法も含めた手順を詳しく解説]]></description><link>https://uhiyama-lab.com/ja/blog/dialy/ssd-clone-and-replacement-guide/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/dialy/ssd-clone-and-replacement-guide/</guid><category><![CDATA[hardware]]></category><pubDate>Sun, 01 Dec 2024 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>4年ほど使っている自作PCで、最近以下のような気になる動作が増えてきました。</p>
<ul>
<li>エクスプローラでフォルダを開いたり、ファイルを表示したりするのに妙に時間がかかる。</li>
<li>ファイル名の変更中など、ファイル操作をしていると表示が一瞬リセットされる（入力が確定されてイライラ…）。</li>
<li>ごく稀にPC全体がプチフリーズし、Windowsのタスクバーなどが再読み込みされるような挙動をする。</li>
</ul>
<p>メモリ診断やGPUドライバ更新、不要ファイルの削除などを試しても改善せず、「そろそろPCの寿命かな…」と考え始めていました。しかし、その前にふとPCの心臓部とも言えるCドライブの状態を確認してみると…</p>
<p><img src="/ca77ce0905962a9228dc90ae10d142ff/20241201-1_C%E3%83%89%E3%83%A9%E3%82%A4%E3%83%96%E6%8F%9B%E8%A3%85%E5%89%8D.png" alt="容量が限界に近いCドライブの状態（赤色表示）"></p>
<p>真っ赤！残りの空き容量は5%未満という危険水域でした。</p>
<p>一般的に、WindowsがインストールされているCドライブは、常に10%～15%程度の空き容量を確保しておくことが推奨されています。空き容量が極端に少ないと、Windowsの一時ファイル作成、ページングファイル（仮想メモリ）の確保、システムアップデートなどに支障をきたし、結果としてシステムの不安定化、パフォーマンス低下、読み書き速度の低下（特にSSDの場合）など、様々な不具合を引き起こす原因となります。</p>
<p>そこで今回、この容量不足を解消すべく、現在のCドライブ（約500GBのM.2 SSD）の内容を丸ごと新しい大容量SSD（2TBの2.5インチSSD）にコピー（クローン）して交換（換装）することに挑戦しました。結果的に容量拡張は成功したものの、途中でWindowsが起動しなくなるトラブルにも遭遇し、かなり苦戦しました…。</p>
<p>この記事では、そのSSDクローン換装の具体的な手順、使用したツール、そして遭遇した起動エラー「0xc000000e」の対処法などを、備忘録として詳しくまとめていきます。同じようにCドライブの容量不足に悩んでいる方や、SSD換装を検討している方の参考になれば幸いです。</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#preparation">事前準備</a></li>
<li><a href="#step1-clone">Step 1: SSDクローン作成</a></li>
<li><a href="#step2-media">Step 2: インストールメディア作成</a></li>
<li><a href="#step3-install">Step 3: SSD取り付けと起動エラー対処</a></li>
<li><a href="#step4-partition">Step 4: パーティション拡張</a></li>
<li><a href="#result">換装完了と結果</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>事前準備 {#preparation}</h2>
<p>今回のSSDクローン換装で使用した主な物品とソフトウェアは以下の通りです。</p>
<ul>
<li>
<p>新しいSSD: シリコンパワー 2TB SSD 3D NAND A58 (2.5インチ SATA)</p>
<ul>
<li>
<p>換装先のSSDです。容量や規格（M.2 NVMe, 2.5インチ SATA等）、メーカーはお好みで。今回はセールで安価だったこちらを選びました。元のM.2から2.5インチへの変更ですが、マザーボードに空きSATAポートがあれば問題ありません。</p>
<p>:::post-link{url="<a href="https://amzn.to/3VicRQJ">https://amzn.to/3VicRQJ</a>" text="シリコンパワー 2TB SSD 3D NAND A58 (Amazon)"}</p>
</li>
</ul>
</li>
<li>
<p>SSDクローンソフト: Macrium Reflect Free</p>
<ul>
<li>
<p>元のSSDの内容を新しいSSDに丸ごとコピー（クローン）するためのソフトウェア。様々なソフトがありますが、今回は無料で評価も高かったこちらを使用しました。</p>
<p>:::post-link{url="<a href="https://www.gigafree.net/system/SystemBackup/macriumreflectfreeedition.html">https://www.gigafree.net/system/SystemBackup/macriumreflectfreeedition.html</a>" text="Macrium Reflect Free (窓の杜)"}</p>
<p>:::post-link{url="<a href="https://note.com/combat_travor/n/n931acb354e2d">https://note.com/combat_travor/n/n931acb354e2d</a>" text="Macrium Reflect Freeを使ってSSDをクローンする方法 (combat-travor 様)"}</p>
</li>
</ul>
</li>
<li>
<p>USBメモリ: 16GB以上のもの</p>
<ul>
<li>
<p>換装後にWindowsが起動しなくなった場合の修復作業で使用します。Windowsのインストールメディアを作成するために必要です。【重要】この作業は事前にやっておくことを強く推奨します。</p>
<p>:::post-link{url="<a href="https://www.microsoft.com/ja-jp/software-download/windows10">https://www.microsoft.com/ja-jp/software-download/windows10</a>" text="Windows 10メディア作成ツール (Microsoft)"}</p>
<p>:::post-link{url="<a href="https://www.microsoft.com/ja-jp/software-download/windows11">https://www.microsoft.com/ja-jp/software-download/windows11</a>" text="Windows 11メディア作成ツール (Microsoft)"}</p>
</li>
</ul>
</li>
<li>
<p>パーティション管理ソフト: Paragon Hard Disk Manager 15 (または同等の機能を持つソフト)</p>
<ul>
<li>換装後、新しいSSDの全容量をCドライブとして使えるように、パーティションサイズを調整（拡張）するために使用しました。Windows標準の「ディスクの管理」でも可能ですが、より柔軟な操作ができる専用ソフトが便利です。今回は手持ちのソフトを使いましたが、無料のパーティション管理ソフトもあります。</li>
</ul>
</li>
<li>
<p>SATA-USB変換ケーブル/ケース (任意):</p>
<ul>
<li>新しいSSDをPC内部に取り付ける前に、USB接続でクローン作業を行う場合に必要です。PC内部に直接接続できる場合は不要。</li>
</ul>
</li>
</ul>
<p>SSDやソフトウェアは上記以外のものでも構いません。ご自身の環境や予算に合わせて選択してください。ただし、起動トラブルに備えて、Windowsインストールメディアだけは事前に作成しておくことをお勧めします。</p>
<p>※BCD (Boot Configuration Data) とは、Windowsの起動に必要な構成情報（どのディスクからOSを起動するかなど）を格納したファイルです。SSD換装後に起動エラーが出る場合、このBCD情報が新しい環境と合わなくなっていることが原因の一つとして考えられます。</p>
<hr>
<p>:::ad</p>
<h2>Step 1: SSDクローン作成 {#step1-clone}</h2>
<p>まず、現在のCドライブの内容を、新しいSSDに丸ごとコピー（クローン）します。</p>
<ol>
<li>
<p>新しいSSDをPCに接続します。（SATA-USB変換ケーブルを使うか、PC内部の空きポートに接続）</p>
</li>
<li>
<p>クローンソフト（今回はMacrium Reflect Free）を起動します。</p>
</li>
<li>
<p>ソフトの指示に従い、コピー元ドライブ（現在のCドライブ）とコピー先ドライブ（新しいSSD）を選択し、クローン処理を開始します。</p>
</li>
</ol>
<p><img src="/d1cfce82599748e4f398d586e01f74c0/20241201-2_SSD%E3%82%AF%E3%83%AD%E3%83%BC%E3%83%B3.png" alt="Macrium Reflect Freeでのクローン設定画面例"></p>
<p>（具体的な操作手順は、前述のcombat-travor様の参考記事が非常に分かりやすいです）</p>
<p><img src="/2fbd03b60ab624fca7b40561c18e3016/20241201-3_SSD%E3%82%AF%E3%83%AD%E3%83%BC%E3%83%B3%E5%AE%8C%E4%BA%86.png" alt="Macrium Reflect Freeでのクローン完了画面"></p>
<p>私の環境では、約450GB使用していたCドライブのクローンに約3時間40分かかりました。時間は環境やデータ量によって大きく変動します。</p>
<p>【ポイント】クローン時のパーティションサイズ</p>
<p>クローンソフトの設定で、コピー先のパーティションサイズを指定できる場合があります。基本的には、コピー元と同じサイズでクローンするのが安全です。新しいSSDの残りの容量は、クローン完了時点では「未割り当て」領域として残しておきます。この未割り当て領域は、後ほどStep 4でCドライブと結合します。</p>
<p>💡 クローン時のパーティションサイズは、元のドライブと同じサイズに設定するのが推奨されます。大容量SSDの残りは一旦「未割り当て」のままにします。</p>
<p>クローン完了後、Windowsの「ディスクの管理」（<code>diskmgmt.msc</code>）やパーティション管理ソフトで、新しいSSDに元のCドライブと同じ構成のパーティションが作成され、残りが未割り当て領域になっていることを確認しましょう。</p>
<p><img src="/e86f44d8d9a5680fac1feb2ffeffccfb/20241201-4_SSD%E3%82%AF%E3%83%AD%E3%83%BC%E3%83%B3%E5%AE%8C%E4%BA%86%E7%A2%BA%E8%AA%8D.png" alt="パーティション管理ソフトでクローン後のSSDを確認"></p>
<p>（↑元のCドライブの内容がコピーされ、残りが未割り当てになっている状態）</p>
<h2>Step 2: インストールメディア作成 {#step2-media}</h2>
<p>SSDの換装後、すんなりWindowsが起動すれば良いのですが、環境によっては起動エラーが発生することがあります。私の場合は、以下のブルースクリーンエラーが表示されました。</p>
<p><img src="/df609ed8b112b468c813a1f5d2784298/20241201-5_%E3%83%96%E3%83%AB%E3%83%BC%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3.png" alt="エラーコード 0xc000000e のブルースクリーン画面"></p>
<p>エラーコード「0xc000000e」は、Windowsが起動に必要なデバイス（この場合は新しいSSD）にアクセスできない、または起動構成データ（BCD）に問題がある場合に発生することが多いエラーです。</p>
<p>このエラーを修復するには、Windowsの回復環境からコマンドプロンプトを起動する必要があります。そのために、事前にWindowsインストールメディア（USBメモリ）を作成しておきます。</p>
<ol>
<li>
<p>Microsoftの公式サイトから「メディア作成ツール」をダウンロードします。</p>
<p>:::post-link{url="<a href="https://www.microsoft.com/ja-jp/software-download/windows10">https://www.microsoft.com/ja-jp/software-download/windows10</a>" text="Windows 10メディア作成ツール (Microsoft)"}</p>
<p>:::post-link{url="<a href="https://www.microsoft.com/ja-jp/software-download/windows11">https://www.microsoft.com/ja-jp/software-download/windows11</a>" text="Windows 11メディア作成ツール (Microsoft)"}</p>
</li>
<li>
<p>16GB以上の空のUSBメモリをPCに接続します。</p>
</li>
<li>
<p>ダウンロードしたメディア作成ツールを実行し、画面の指示に従って「別の PC のインストール メディアを作成する (USB フラッシュ ドライブ、DVD、または ISO ファイル)」を選択し、USBメモリにインストールメディアを作成します。</p>
</li>
</ol>
<p><img src="/e1eace0b83cd5ecb0f2b799fa37b53ea/20241201-6_%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2.png" alt="Windowsメディア作成ツールの画面"></p>
<p>このUSBメモリがあれば、万が一Windowsが起動しなくなっても、修復作業を行うことができます。</p>
<p>:::post-link{url="<a href="https://jp.minitool.com/data-recovery/fix-error-0xc000000e.html">https://jp.minitool.com/data-recovery/fix-error-0xc000000e.html</a>" text="Windows 10でエラーコード0xc000000eを修正する方法 (MiniTool)"}</p>
<p>:::post-link{url="<a href="https://drmpls.com/2023/07/21/replaced-the-ssd-cannot-boot/">https://drmpls.com/2023/07/21/replaced-the-ssd-cannot-boot/</a>" text="クローンしたSSDから起動できない時の原因と対処法 (ドクター・エムプレイス)"}</p>
<h2>Step 3: SSD取り付けと起動エラー対処 {#step3-install}</h2>
<p>クローン作業とインストールメディア作成が終わったら、いよいよSSDの交換です。</p>
<ol>
<li>
<p>PCの電源を完全に切り、電源ケーブルを抜きます。</p>
</li>
<li>
<p>PCケースを開け、古いSSD（元のCドライブ）を取り外し、新しいSSD（クローン済みのもの）を取り付けます。（※静電気対策を忘れずに！）</p>
</li>
<li>
<p>PCケースを閉じ、電源ケーブルを接続し、PCの電源を入れます。</p>
</li>
<li>
<p>電源を入れた直後（メーカーロゴが表示されている間）に、指定のキー（多くはDELキーかF2キー）を押して、BIOS（UEFI）設定画面に入ります。</p>
</li>
<li>
<p>BIOS設定画面で、「Boot」や「起動」といったメニューを探し、起動順位（Boot Options / Boot Priority）を確認します。ここで、新しく取り付けたSSDが「Windows Boot Manager」として認識され、起動順位の1番目に設定されていることを確認します。（もし認識されていない、または順位が低い場合は設定を変更します）</p>
</li>
<li>
<p>設定を保存してBIOSを終了し、PCを再起動します。</p>
</li>
</ol>
<p>【トラブル発生】BIOSでは認識されるのに 0xc000000e エラーで起動しない！</p>
<p>私の環境では、ここが最大の難関でした。BIOS上では新しいSSDが起動ドライブとして正しく認識されているにも関わらず、Windowsを起動しようとすると前述のブルースクリーンエラー「0xc000000e」が発生してしまうのです。</p>
<p>最初はクローン失敗を疑い、別のソフト（Paragon）でクローンし直したりしましたが、結果は同じでした。</p>
<p>【解決策】Windowsインストールメディアから起動し、BCDを再構築する</p>
<p>最終的に、Step 2で作成したWindowsインストールメディア（USBメモリ）を使って以下の手順を行うことで解決しました。これは、Windowsの起動構成データ（BCD）を修復・再構築する作業です。</p>
<ol>
<li>
<p>PCの電源を切った状態で、Windowsインストールメディア（USBメモリ）を接続します。</p>
</li>
<li>
<p>PCの電源を入れ、すぐにBIOS設定画面に入ります。</p>
</li>
<li>
<p>起動順位を変更し、USBメモリを最優先（1番目）にして設定を保存・再起動します。</p>
</li>
<li>
<p>USBメモリからWindowsセットアップ画面が起動したら、「コンピューターを修復する」>「トラブルシューティング」>「詳細オプション」>「コマンド プロンプト」を選択します。</p>
</li>
<li>
<p>コマンドプロンプトで、以下のコマンドを順番に実行します。これは、ブートセクタの修復とBCDの再構築を行うためのコマンドです。（詳細な手順は下記の参考記事を参照してください）</p>
<ul>
<li><code>bootrec /fixmbr</code></li>
<li><code>bootrec /fixboot</code> （※後述の注意点あり）</li>
<li><code>bootrec /scanos</code></li>
<li><code>bootrec /rebuildbcd</code></li>
</ul>
<p>（場合によっては、<code>diskpart</code>コマンドでEFIパーティションにドライブ文字を割り当てる作業が必要になることもあります）</p>
</li>
</ol>
<p>:::post-link{url="<a href="https://freesoft.tvbok.com/tips/efi_installation/uefi_bootrec.html">https://freesoft.tvbok.com/tips/efi_installation/uefi_bootrec.html</a>" text="コマンドプロンプトからUEFIを修復する方法 (ぼくんちのTV 別館)"}</p>
<p>【注意】<code>bootrec /fixboot</code> で「アクセスが拒否されました」エラーが出る場合</p>
<p><img src="/63c80fbeba3531f3f128d2ff38e9e55e/20241201-7_fixboot.png" alt="bootrec /fixboot でアクセス拒否のエラー"></p>
<p>上記のコマンド実行中、<code>bootrec /fixboot</code> で「アクセスが拒否されました (Access is denied.)」というエラーが表示されることがあります。これは、最近のWindows（特にUEFIモードでインストールされている場合）でよく見られる現象のようです。</p>
<p>私の場合は、このエラーを無視して、次の <code>bootrec /scanos</code> と <code>bootrec /rebuildbcd</code> を実行したところ、結果的に問題なくWindowsが起動するようになりました。<code>fixboot</code>コマンドは主に古いMBR形式のディスク修復に使われるもので、UEFI環境では必ずしも必要ではない、あるいは別の手順（<code>diskpart</code>を使ったEFIパーティション操作など）が必要な場合があるようです。</p>
<p>もしこのエラーに遭遇し、<code>rebuildbcd</code>まで実行しても起動しない場合は、以下の参考記事にあるような追加の対処法を試す必要があるかもしれません。</p>
<p>:::post-link{url="<a href="https://itojisan.xyz/trouble/17752/">https://itojisan.xyz/trouble/17752/</a>" text="「bootrec /fixboot」でアクセスが拒否される時の対処法 (IT HOOK)"}</p>
<p>BCDの再構築が成功し、コマンドプロンプトを終了してPCを再起動すると、無事に新しいSSDからWindowsが起動するはずです！</p>
<h2>Step 4: パーティション拡張 {#step4-partition}</h2>
<p>無事にWindowsが起動したら、最後の大仕事です。現在、新しいSSDには元のCドライブと同じサイズのパーティションしかなく、残りの大容量スペースは「未割り当て」領域として使えない状態になっています。この未割り当て領域をCドライブと結合し、SSDの全容量を使えるようにします。</p>
<p><img src="/6d48deef56eeee2a3c2f7a40c064b79c/20241201-8_C%E3%83%89%E3%83%A9%E3%82%A4%E3%83%96%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3%E7%B5%B1%E5%90%88.png" alt="パーティション管理ソフトでCドライブと未割り当て領域を表示"></p>
<p>この作業は、Windows標準の「ディスクの管理」でも可能ですが、回復パーティションなどが間に挟まっていると拡張できない場合があるため、専用のパーティション管理ソフトを使うのが確実です。</p>
<p>今回は手持ちの「Paragon Hard Disk Manager 15」を使用しました。「パーティションサイズの変更/移動」のような機能を選択し、Cドライブのパーティションを選択して、後方の未割り当て領域を取り込むようにサイズを最大まで広げます。</p>
<p><img src="/71715adb0af97fed3af5d433c055ee75/20241201-9_C%E3%83%89%E3%83%A9%E3%82%A4%E3%83%96%E5%AE%B9%E9%87%8F%E6%8B%A1%E5%BC%B5.png" alt="Paragon Hard Disk Managerでパーティションサイズを拡張する設定"></p>
<p>設定を適用すると、通常はPCの再起動が求められ、Windows起動前の画面でパーティションの変更処理が実行されます。処理が完了し、再度Windowsが起動すれば、Cドライブの容量が新しいSSDの最大容量（今回は約2TB）まで拡張されているはずです。</p>
<p>:::ad</p>
<h2>換装完了と結果 {#result}</h2>
<p><img src="/38a6b84edea0c7f964cb9f58bc1d3b33/20241201-10_C%E3%83%89%E3%83%A9%E3%82%A4%E3%83%96%E6%8F%9B%E8%A3%85%E5%BE%8C.png" alt="容量が拡張されたCドライブの状態（空き容量十分）"></p>
<p>以上の手順を経て、無事にCドライブのSSDを500GBから2TBに換装し、容量を大幅に拡張することができました！</p>
<p>Cドライブの空き容量が十分に確保された（15%以上）ことで、以前発生していたエクスプローラの遅延や表示の再描画、プチフリーズといったPCの不調も嘘のように解消されました。やはり、Cドライブの空き容量不足はシステムの安定性に大きな影響を与えるようです。ドライブが赤色の状態は放置せず、早めに対処（不要ファイルの削除、または今回のような容量拡張）することが重要ですね。</p>
<p>SSDのクローン換装は、手順自体はそれほど複雑ではありませんが、環境によっては起動トラブルが発生する可能性もあります。特にUEFI環境での換装は、BCD周りの問題が起きやすいようです。この記事で紹介したトラブルシューティングが、同じ問題に直面した方の助けになれば幸いです。（※作業は自己責任でお願いします）</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/481ebec3151d6e206b01a0511c1c7ace/20241201-SSD%E3%82%92%E3%82%AF%E3%83%AD%E3%83%BC%E3%83%B3%E6%8F%9B%E8%A3%85%E3%81%97%E3%81%A6C%E3%83%89%E3%83%A9%E3%82%A4%E3%83%96%E3%81%AE%E5%AE%B9%E9%87%8F%E3%82%92500GB%E3%81%8B%E3%82%892TB%E3%81%AB%E6%8B%A1%E5%BC%B5%E3%81%99%E3%82%8B.jpg" medium="image"/></item><item><title><![CDATA[【RPGツクールMV/MZ】簡単なカットシーン作成術＆カメラ演出で魅力UP！(Galv_CamControl編)]]></title><description><![CDATA[RPGツクールでドラマティックなカットシーンを作成する方法と、Galv_CamControlプラグインを使ったカメラ演出の追加方法を解説]]></description><link>https://uhiyama-lab.com/ja/blog/gamedev/rpgmaker-cutscene-camera/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/gamedev/rpgmaker-cutscene-camera/</guid><category><![CDATA[rpgmaker]]></category><pubDate>Thu, 28 Nov 2024 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p><img src="/8ee0d58ba150d836da5f6bae53388e76/07_%E3%83%AA%E3%83%83%E3%83%81%E3%82%AB%E3%83%83%E3%83%88%E3%82%B7%E3%83%BC%E3%83%B3.webp" alt="カメラ演出を加えたRPGツクールのカットシーン例"></p>
<p>RPGツクールでゲームにドラマティックな<strong>カットシーン</strong>（イベントシーン）を盛り込みたい！と思ったとき、実はUnityやUnreal Engineといった他のゲームエンジンと比較して、<strong>RPGツクール</strong>では驚くほど<strong>簡単</strong>に基本的なカットシーンを作成できます。</p>
<p>この記事では、RPGツクールの標準機能を使った<strong>基本的なカットシーンの作り方</strong>から、さらに表現力を豊かにするための<strong>カメラ演出</strong>の追加方法までを解説します。カメラ演出には、定番のプラグイン「<strong>Galv_CamControl.js</strong>」を使用します。</p>
<p>これらのテクニックを使えば、あなたのゲームの物語をより魅力的に、そしてプレイヤーの記憶に残るものにできるはずです。</p>
<p><strong>この記事の内容</strong></p>
<ol>
<li>基本的なカットシーンの作り方（RPGツクール標準機能）</li>
<li>カメラ演出の追加方法（Galv_CamControlプラグイン）</li>
<li>リッチなカットシーンの完成へ</li>
</ol>
<hr>
<p>:::ad</p>
<h2>基本的なカットシーンの作り方（RPGツクール標準機能）</h2>
<p>まずは、プラグインを使わずにRPGツクールの標準機能だけでカットシーンを作成する方法です。</p>
<p><img src="/33abb1aa2c7206be5e0e3fc5ffbbe143/01_%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%AA%E3%82%AB%E3%83%83%E3%83%88%E3%82%B7%E3%83%BC%E3%83%B3.webp" alt="RPGツクールの標準機能だけで作成したシンプルなカットシーン"></p>
<p>（↑標準機能だけでも、このようなシーンは作成可能です）</p>
<p><img src="/405233ee3063576dadf2003d565fa565/02_%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%AA%E3%82%AB%E3%83%83%E3%83%88%E3%82%B7%E3%83%BC%E3%83%B3.png" alt="RPGツクールのイベントコマンド設定例（基本的なカットシーン）"></p>
<p>RPGツクールのイベントコマンドにある、以下の基本機能を組み合わせるだけで、キャラクター同士の会話や動きを含むカットシーンを簡単に作成できます。</p>
<ul>
<li><strong>文章の表示：</strong> キャラクターのセリフやナレーションを表示します。顔グラフィックと組み合わせることで、誰が話しているかを明確にできます。</li>
<li><strong>フキダシアイコンの表示：</strong> キャラクターの頭上に「！」や「？」などのアイコンを表示し、感情や状態を視覚的に表現します。</li>
<li><strong>移動ルートの設定：</strong> イベントキャラクターやプレイヤーを指定した経路で移動させます。向きの変更、ウェイト（待ち時間）、スイッチ操作なども組み込めます。対象キャラを「このイベント」や名前で指定できるのが非常に直感的です。</li>
<li><strong>透明状態の変更：</strong> プレイヤーキャラクターなどを一時的に画面から見えなくします。イベントシーンへの導入時や終了時に便利です。</li>
</ul>
<p><strong>【Tips】イベントには分かりやすい名前を付けよう！</strong></p>
<p><img src="/bd2bfa8b12be53b3705d680235da1348/03_%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AB%E3%81%AF%E5%90%8D%E5%89%8D%E3%82%92%E3%81%A4%E3%81%91%E3%82%88%E3%81%86.png" alt="イベント設定画面でイベント名を設定する"></p>
<p><img src="/4175f05307ffed7ba493b095bf6dbe61/04_%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AB%E3%81%AF%E5%90%8D%E5%89%8D%E3%82%92%E3%81%A4%E3%81%91%E3%82%88%E3%81%86_2.png" alt="移動ルートの設定でイベント名を指定する場面"></p>
<p>他のゲームエンジン経験者から見ると、RPGツクールのイベント作成機能、特にキャラクター指定（「プレイヤー」「このイベント」「イベントID:1」など）や移動ルート設定の手軽さは驚くほど洗練されています。</p>
<p>ここで一つ重要なポイントは、マップ上に配置するキャラクターやオブジェクトなどの<strong>イベントには、必ず分かりやすい名前（例：「村人A」「宝箱_洞窟」など）を付けておく</strong>ことです。イベントコマンドで対象を指定する際に名前で選択できるため、管理が非常に楽になります。これは個人開発であっても、後々の修正やデバッグ時に「あのイベントどれだっけ？」と迷う時間を減らし、将来の自分を助けることにつながります。ゲーム制作が進むほどデータは複雑になるので、こうした小さな心がけが大切です。</p>
<p>さて、標準機能だけでもカットシーンは作れますが、ずっと同じ画角だと少し単調に見えてしまうかもしれません。そこで次に、カメラワークを追加してみましょう。</p>
<p>:::ad</p>
<h2>カメラ演出の追加方法（Galv_CamControlプラグイン）</h2>
<p>基本的なカットシーンに<strong>カメラワーク</strong>を加えることで、シーンの臨場感を高めたり、プレイヤーの視線を誘導したりと、より印象的な演出が可能になります。RPGツクールMV/MZでカメラ制御を実現するには、プラグインを利用するのが一般的です。</p>
<p>今回は、多くのツクールユーザーに利用されている定番のカメラ制御プラグイン「<strong>Galv_CamControl.js</strong>」を使用します。</p>
<p>プラグイン配布元：<a href="https://galvs-scripts.com/2015/11/27/mv-cam-control/">Galv's Scripts – MV Cam Control</a> (※MZで利用する場合は、有志による改変版などが必要になる場合があります。別途検索・ご確認ください)</p>
<p><strong>【プラグインの導入】</strong></p>
<ol>
<li>上記サイト（または対応版を探して）からプラグインファイル（<code>Galv_CamControl.js</code>）をダウンロードします。</li>
<li>RPGツクールのプロジェクトフォルダ内にある <code>js/plugins</code> フォルダに、ダウンロードしたファイルをコピーします。</li>
<li>RPGツクールエディタを開き、「ツール」メニューから「プラグイン管理」を選択します。</li>
<li>プラグイン管理ウィンドウで、空いている行をダブルクリックし、「名前」のドロップダウンリストから「Galv_CamControl」を選択して「OK」をクリックします。</li>
<li>状態が「ON」になっていることを確認し、「OK」をクリックしてプラグイン管理ウィンドウを閉じます。</li>
</ol>
<p>これでプラグインが有効になり、イベントコマンドの「プラグインコマンド」からカメラ制御の命令を実行できるようになります。</p>
<p><img src="/b971ff55f19c0b732b2bf6059d2d1d17/06_Galv_CamControl.png" alt="Galv_CamControlのプラグインコマンド入力例"></p>
<h3>Galv_CamControlの基本的な使い方 (プラグインコマンド)</h3>
<p>イベントコマンドの「プラグインコマンド」を使って、以下のような命令を実行することでカメラを制御できます。（これはMVベースの書式例です。MZや特定の改変版では書式が若干異なる場合がありますので、プラグインのヘルプ等をご確認ください）</p>
<ul>
<li><strong>特定のイベントにカメラを向ける（追従させる）:</strong>
例: <code>CAM EVENT 1</code> （イベントIDが1のキャラクターにカメラを向ける）
例: <code>CAM EVENT 2</code> （イベントIDが2のキャラクターにカメラを向ける）</li>
<li><strong>プレイヤーにカメラを戻す（追従させる）:</strong>
<code>CAM PLAYER</code></li>
<li><strong>指定したマップ座標にカメラを移動させる:</strong>
<code>CAM MAP X Y</code> （例: <code>CAM MAP 10 15</code> で座標(10, 15)にカメラを移動）</li>
<li><strong>カメラの追従を解除（現在の位置で固定）する:</strong>
<code>CAM TARGET 0</code></li>
</ul>
<p><strong>【補足：カメラの移動時間（スクロール）】</strong>
上記のコマンドの後に、**半角スペースを空けて数字（フレーム数）**を指定すると、カメラがその時間をかけてスムーズに移動します。例えば、<code>CAM EVENT 1 60</code> と入力すると、60フレーム（1秒）かけてイベント1の位置までカメラがスクロールします。時間を指定しない場合は、カメラは瞬時に移動します。</p>
<p>これらのコマンドを、イベントコマンドの「文章の表示」や「移動ルートの設定」、「ウェイト」などと組み合わせることで、会話の話し手にカメラをフォーカスさせたり、重要なアイテムや場所にカメラを向けたりといった演出が可能になります。</p>
<p><strong>【カメラ演出の応用：見せて伝えるゲームデザイン】</strong></p>
<p><img src="/b567a12e9cebceb0065275543d267c0f/06-1-%E5%AE%9D%E7%AE%B1%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88.webp" alt="カメラを宝箱に向ける演出例"></p>
<p>優れたゲームデザインでは、プレイヤーに「どこへ行け」「何を取れ」と指示するだけでなく、<strong>目的地や対象物をカメラで一度映し出す</strong>ことで、プレイヤーが直感的に理解できるように誘導します。</p>
<p>Galv_CamControlのようなカメラ制御プラグインは、カットシーンの演出強化だけでなく、このような「<strong>見せて伝える</strong>」ゲームデザインを実現するためにも非常に有効です。例えば、NPCからクエストを受けたら、その目的地や関連するオブジェクトに一瞬カメラをパンさせる、といった使い方です。これにより、プレイヤーは次に何をすべきか分かりやすくなり、ゲーム体験が向上します。</p>
<p>💡 <strong>応用テクニック：対峙シーンの演出</strong>
キャラクター同士が対峙するような緊迫したシーンでは、二人の間に透明な空のイベントを配置し、そのイベントにカメラを固定（例: <strong>CAM EVENT [空イベントID] [時間]</strong> → <strong>CAM TARGET 0</strong>）することで、キャラクターが画面の両端に配置され、中央に空間ができるような、映画的な構図を作り出すことも可能です。</p>
<p>:::ad</p>
<h2>リッチなカットシーンの完成へ</h2>
<p><img src="/8ee0d58ba150d836da5f6bae53388e76/07_%E3%83%AA%E3%83%83%E3%83%81%E3%82%AB%E3%83%83%E3%83%88%E3%82%B7%E3%83%BC%E3%83%B3.webp" alt="カメラ演出を加えたRPGツクールのカットシーン完成例"></p>
<p>基本的なイベントコマンド（会話、移動、フキダシなど）に、<strong>Galv_CamControl</strong>による<strong>カメラワーク</strong>を加えるだけで、最初のシンプルなカットシーンがよりダイナミックで、見ごたえのあるものになったのがお分かりいただけるかと思います。</p>
<p>特にRPGツクールのような2Dベースのゲームでは、画面に動きがないとイベントシーンが単調に見えがちです。カメラを効果的に動かすことで、プレイヤーの視点を誘導し、シーンの重要性を高め、感情的なインパクトを与え、プレイヤーを飽きさせない工夫を凝らしましょう。</p>
<p>RPGツクールの手軽なイベント作成機能と、プラグインによるカメラ制御を組み合わせることで、あなたのゲームはさらに魅力的なものになるはずです。</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/8ee0d58ba150d836da5f6bae53388e76/07_%E3%83%AA%E3%83%83%E3%83%81%E3%82%AB%E3%83%83%E3%83%88%E3%82%B7%E3%83%BC%E3%83%B3.webp" medium="image"/></item><item><title><![CDATA[【VRChat】初めてのアバター購入＆改変！マヌカちゃんで実践したモデル・服・髪の導入手順と注意点]]></title><description><![CDATA[VRChat初心者が人気アバター「マヌカ」で初めてのアバター改変に挑戦した記録。VCCの使い方、服装の着せ替え、瞳と髪型の変更方法を詳しく解説]]></description><link>https://uhiyama-lab.com/ja/blog/dialy/vrchat-first-model-import/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/dialy/vrchat-first-model-import/</guid><category><![CDATA[vrchat]]></category><pubDate>Thu, 21 Nov 2024 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p><img src="/04e252ad57f1cd36e48091df825367e8/20241121-0-VRChat%E3%81%AF%E3%81%98%E3%82%81%E3%81%A6%E3%81%AE%E3%83%A2%E3%83%87%E3%83%AB%E5%B0%8E%E5%85%A51.webp" alt="VRChat内で改変したマヌカアバター"></p>
<p>VRChatに興味があるけど、「アバターってどうやって用意するの？」「改変って難しそう…」と感じている方も多いのではないでしょうか。実は最近、VRChatのアバター導入や改変プロセスは、以前に比べてかなり分かりやすく、始めやすくなっています。</p>
<p>この記事では、VRChat初心者だった筆者が、人気アバター「マヌカ」ちゃんをBoothで購入し、初めてのアバター改変（着せ替え、テクスチャの色や瞳の変更、髪型変更など）に挑戦した際の記録をまとめました。</p>
<p>特に、公式ツール「VCC (VRChat Creator Companion)」のおかげで、Unityを使ったアバターのアップロード作業が驚くほど簡単になっています。この記事が、これからVRChatでアバターを使ってみたい、アバター改変を始めてみたいという方の参考になれば幸いです。</p>
<p>（筆者は3Dモデル制作の学習の一環としてVRChatアバター改変を始めました。学習ステップは以下の通り考えています）</p>
<ol>
<li>既存販売アバターの基本的な改変（着せ替え、色変更、簡単なギミック等） ※本記事の内容</li>
<li>VRoid Studioで作成したモデルをVRChatへ導入（テクスチャ作成等の練習）</li>
<li>Blender等によるフルスクラッチモデル制作（最終目標）</li>
</ol>
<p>今回は、ステップ1の体験談と手順、つまずいた点などを詳しくご紹介します。</p>
<p>:::ad</p>
<p>:::toc</p>
<ul>
<li><a href="#manuka-import">マヌカ購入と導入</a></li>
<li><a href="#clothing">服装の着せ替え</a></li>
<li><a href="#color-change">瞳と服の色変更</a></li>
<li><a href="#hairstyle">髪型の変更</a></li>
<li><a href="#summary">まとめ</a></li>
<li><a href="#bonus">おまけ：ワールド制作失敗談</a></li>
</ul>
<p>:::</p>
<p>:::ad</p>
<h2>マヌカ購入と導入 {#manuka-import}</h2>
<p><img src="/3e56556e7a8f6ffd12a5ac77698c77f6/20241121-1-%E3%83%9E%E3%83%8C%E3%82%AB%E3%83%A2%E3%83%87%E3%83%AB%E5%B0%8E%E5%85%A5.webp" alt="購入したマヌカモデルをUnityに導入した様子"></p>
<p>今回、初めてのアバター改変のベースとして選んだのは、Studio DINGOさんが制作・販売されている大人気3Dモデル「マヌカ」ちゃんです。</p>
<p>:::post-link{url="<a href="https://jingo1016.booth.pm/items/5058077">https://jingo1016.booth.pm/items/5058077</a>" text="オリジナル3Dモデル「マヌカ」ver1.02 (BOOTH)"}</p>
<p>ちょうどアバターを探していた時期に、作者様がX(旧Twitter)でブラックフライデーセール（50%OFF！）を実施されていたのが購入の決め手でした。さすが人気モデル、クオリティが高くて可愛いです！</p>
<p>【Tips】Boothでのセール情報：VRChatアバターや衣装が多く販売されているBoothでは、クリエイターさんが個別にセールを行うことがあります。気になるモデルを見つけたら、作者さんのXアカウントなどをフォローしておくと、お得な情報を見逃さずに済むかもしれません。</p>
<p>アバターの導入手順（UnityへのインポートとVRChatへのアップロード）については、「メタカル最前線」さんの以下の記事が非常に分かりやすかったです。画像付きで丁寧に解説されているので、初心者の方はまずこちらを読むのがおすすめです。</p>
<p>:::post-link{url="<a href="https://metacul-frontier.com/?p=15582">https://metacul-frontier.com/?p=15582</a>" text="【徹底解説】VRChatのアバターアップロード・導入方法 (メタカル最前線)"}</p>
<h3>VCC (VRChat Creator Companion) がとにかく便利！</h3>
<p><img src="/17816428c87b0cd955e9cf4fef02d2e1/20241121-2-VCC%E3%83%A1%E3%83%8B%E3%83%A5%E3%83%BC%E7%94%BB%E9%9D%A2.png" alt="VRChat Creator Companion (VCC) のメニュー画面"></p>
<p>今回のアバター導入で特に感動したのが、VRChat公式ツール「VCC (VRChat Creator Companion)」の存在です。以前（2018年頃）はUnityのセットアップやVRChat用コンポーネントの導入がもっと複雑だった記憶がありますが、VCCを使えば驚くほど簡単でした。</p>
<p>VCC上で作りたいもの（アバターかワールドか）を選んでプロジェクトを新規作成するだけで、必要なものが揃ったUnityプロジェクトが自動でセットアップされます。Unity Hubから手動で設定する手間が省け、非常にストレスフリーです。プロジェクト管理画面も見やすく、初心者にとって大きな助けになるツールだと感じました。</p>
<p><img src="/e63282bb7d5d80559db01dd7c3b219d5/20241121-3-VCC%E3%83%A1%E3%83%8B%E3%83%A5%E3%83%BC%E7%94%BB%E9%9D%A22.png" alt="VCCでのプロジェクト管理画面"></p>
<p>:::ad</p>
<h2>服装の着せ替え {#clothing}</h2>
<p><img src="/d244c0e175c1b4828105191a654a03a2/20241121-4-%E6%9C%8D%E3%81%AE%E5%B0%8E%E5%85%A5.webp" alt="購入した衣装をマヌカちゃんに着せた様子"></p>
<p>アバターの導入ができたら、次はいよいよ改変の第一歩、服装の着せ替えです。通常、別売りの衣装アセットはアバターの体型に合わせてUnity上で微調整が必要ですが、「マヌカ」ちゃんのような人気アバターの場合、多くのクリエイターさんが「マヌカ対応」として、マヌカちゃんに最適化された衣装をBoothで販売してくれています。これは初心者にとって非常にありがたい点です。</p>
<p>今回は、デフォルトの元気な雰囲気から、少し落ち着いたミステリアスな雰囲気に変えたかったので、オーバーサイズのテックウェアを探していました。そこで見つけたのが、hajimata雑貨店さんの「マヌカ対応衣装【Tech Wear】」です。</p>
<p>:::post-link{url="<a href="https://booth.pm/ja/items/6288909">https://booth.pm/ja/items/6288909</a>" text="マヌカ対応衣装【Tech Wear】(BOOTH)"}</p>
<p>探していた日にちょうど発売されたという偶然もあり、即購入。デザインもクオリティも素晴らしい衣装です！</p>
<p><img src="/a691699b1288e3b51a0f614e015aa9a5/20241121-5-%E6%9C%8D%E8%A3%85%E5%B0%8E%E5%85%A5Unity.png" alt="Unity上でマヌカちゃんにTech Wearを着せている様子"></p>
<p>「マヌカ対応」衣装なので、導入は非常に簡単。基本的には、購入した衣装のPrefab（プレハブ：設定済みの部品セット）を、UnityのHierarchyウィンドウにあるマヌカちゃんアバターのオブジェクトに入れるだけでOKです。（元の服や不要なパーツは非表示にします。今回はケモミミも非表示にしました）</p>
<p>衣装の着せ替え方法についても、先ほど紹介した「メタカル最前線」さんの記事が大変参考になりました。</p>
<p>:::post-link{url="<a href="https://metacul-frontier.com/?p=13689">https://metacul-frontier.com/?p=13689</a>" text="BOOTHで買った衣装をUnityで簡単に改変する方法 (メタカル最前線)"}</p>
<h3>【つまずきポイント】衣装小物の追従設定ミス</h3>
<p>Tech Wearに付属していた防弾メガネ（ゴーグル）の配置で少しミスをしました。</p>
<p>最初、衣装本体と同じ階層にメガネのPrefabを配置してしまったため、メガネが体の動きには追従するものの、頭の動きには追従せず、結果的に頭の中に埋まってしまいました。</p>
<p><img src="/4a2ca6e7dd9c8ca13996c7be50e020ee/20241121-6-%E6%9C%8D%E3%83%90%E3%82%B0.webp" alt="メガネが頭に埋まってしまう失敗例"></p>
<p>【解決策】メガネのような頭部に追従させたい小物は、アバターの骨格構造（Armature）の中にある「Head」（頭ボーン）の子オブジェクトとして配置する必要があります。具体的には、<code>Armature/Hips/Spine/Chest/Neck/Head</code> の下にメガネのPrefabを移動させます。これで頭の動きに合わせてメガネも動くようになります。</p>
<p><img src="/2bd90af32027d5f82ed6807d839ae130/20241121-7-%E3%83%A1%E3%82%AC%E3%83%8D%E8%A8%AD%E7%BD%AE%E4%BD%8D%E7%BD%AE.png" alt="正しいメガネの配置場所（Headボーンの下）"></p>
<p>アバターの親子関係（ヒエラルキー構造）を理解することが、小物を正しく配置する鍵になります。</p>
<p>:::ad</p>
<h2>瞳と服の色変更 {#color-change}</h2>
<p>服装の次は、アバターの印象を大きく左右する瞳と服の色を変更してみました。</p>
<p>瞳の改変には、Boothで販売されている瞳テクスチャを購入して差し替えるのが最も簡単な方法です。しかし今回は、今後の3Dモデル制作の練習も兼ねて、マヌカちゃんの元の顔テクスチャファイルに直接加筆修正する方法を試しました。</p>
<p>【手順】</p>
<ol>
<li>Unityプロジェクト内にあるマヌカちゃんの顔テクスチャファイル（.pngや.psdなど）を見つけ、作業用フォルダにコピーする。</li>
<li>PhotoshopやCLIP STUDIO PAINTなどの画像編集ソフトで、コピーしたテクスチャファイルを開き、瞳の部分を描き変える。（今回は瞳孔の形を変え、色を緑色に変更）</li>
<li>編集したテクスチャをPNG形式で出力する。</li>
<li>出力したPNGファイルを、Unityプロジェクト内の元のテクスチャファイルと同じフォルダに、<strong>同じファイル名で上書き保存</strong>する。</li>
</ol>
<p>ファイルを上書きすると、Unityが自動的に変更を検知し、アバターの見た目に反映してくれます。非常に便利です。</p>
<p><img src="/000588ef4588e51af30d3668a3be9981/20241121-8-%E7%9E%B3%E3%81%AE%E6%8F%8F%E3%81%8D%E5%A4%89%E3%81%88.png" alt="編集前後の瞳テクスチャ比較"></p>
<h3>【つまずきポイント】消したはずのハイライトが表示される？ → シェイプキーの罠</h3>
<p>瞳テクスチャを更新した際、「あれ？テクスチャ編集で消したはずの瞳のハイライトやクローバー柄が表示されている…？」という現象に遭遇しました。</p>
<p>【原因と解決策】これは、テクスチャではなく「シェイプキー」で制御されている要素でした。シェイプキーとは、アバターの特定の部位の形状をスライダーで変化させられる機能のことで、表情の変化（目パチ、口パク）や、こうした細かい装飾の表示/非表示に使われることがあります。</p>
<p>UnityのInspectorウィンドウで、アバターの顔や体のメッシュ（Skinned Mesh Renderer）を選択し、「BlendShapes」（シェイプキーのこと）の項目を確認します。そこにハイライトや柄に対応する名前のスライダーがあれば、その値を調整することで表示を消すことができました。（つまり、テクスチャから消す必要はなかった…！）</p>
<p><img src="/ad726e29508c49d004596d6969c080a6/20241121-9-%E3%82%B7%E3%82%A7%E3%82%A4%E3%83%97%E3%82%AD%E3%83%BC.png" alt="UnityのInspectorでシェイプキー（BlendShapes）を調整する様子"></p>
<p>ついでに、落ち着いた雰囲気にするために瞼を少し下げるシェイプキーも調整。シェイプキーはアバターの個性を出す上で非常に強力な機能ですね。</p>
<p>服の色の変更については、瞳の色に合わせて、元の黒色ベースの服テクスチャに紺色のレイヤーを重ねて色調を変更したものを、同様に上書きする方法で対応しました。もっと凝るなら、模様を描き足したりするのも面白そうです。</p>
<p><img src="/4163ea9a3ffc75c1a1668e62e23d9a51/20241121-10-%E7%9E%B3%E3%81%AE%E4%BF%AE%E6%AD%A3.webp" alt="瞳と服の色を改変した後のマヌカちゃん"></p>
<p>これで、だいぶ目標としていた「落ち着いてミステリアス」なイメージに近づいてきました。</p>
<p>:::ad</p>
<h2>髪型の変更 {#hairstyle}</h2>
<p><img src="/1499a56468bf4a472749366717539cfb/20241121-11-%E9%AB%AA%E5%9E%8B%E3%81%AE%E5%A4%89%E6%9B%B4.png" alt="新しい髪型に変更したマヌカちゃん"></p>
<p>仕上げに髪型も変更します。ここでも「マヌカ対応」の髪型アセットがBoothに豊富にあり、イメージに合うものをすぐに見つけることができました。人気アバターならではの利点ですね。</p>
<p>今回は、かやすとあさんの「みみだしボブ」を購入しました。</p>
<p>:::post-link{url="<a href="https://kayastore.booth.pm/items/5061681">https://kayastore.booth.pm/items/5061681</a>" text="みみだしボブ【Manuka Hairstyle】(BOOTH)"}</p>
<p>マヌカちゃんデフォルトのお団子ヘアも可愛いですが、今回のコンセプトにはボブの落ち着いたシルエットが合うと考えました。（付属のリボンはアクセントとして残し、サイズを最大に変更しました）</p>
<p><img src="/04e252ad57f1cd36e48091df825367e8/20241121-0-VRChat%E3%81%AF%E3%81%98%E3%82%81%E3%81%A6%E3%81%AE%E3%83%A2%E3%83%87%E3%83%AB%E5%B0%8E%E5%85%A51.webp" alt="髪型とリボンを変更した完成形のマヌカちゃん"></p>
<h3>【つまずきポイント】髪型セットアップ時のエラー</h3>
<p>衣装と同様に、購入した髪型のPrefabをアバターのオブジェクトに入れてセットアップしようとした際に、エラーが発生することがありました。（特にModular Avatarなどのツールを使っている場合）</p>
<p><img src="/4b3a6db490d7d24fd68d569d8f3073b5/20241121-12-%E9%AB%AA%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97%E3%82%A8%E3%83%A9%E3%83%BC.png" alt="髪型セットアップ時のエラーメッセージ例"></p>
<p>【解決策】このような場合、髪型オブジェクトに「MA Bone Proxy」（Modular Avatarに含まれるコンポーネント）を追加し、そのコンポーネントの「Bone Reference」にアバターの「Head」ボーンを指定することで解決できる場合があります。これにより、髪が頭に正しく追従するようになります。（この設定を行えば、再度セットアップツールを適用する必要はありません）</p>
<p><img src="/87aecd88713f00e7041817b0c5090116/20241121-13-%E9%AB%AA%E8%A8%AD%E5%AE%9A.png" alt="MA Bone Proxyコンポーネントの設定例"></p>
<p>アセットによっては導入方法が異なる場合があるので、付属のReadmeなどをよく確認することが大切です。</p>
<p>:::ad</p>
<h2>まとめ {#summary}</h2>
<p><img src="/fe7d380b3ff9e3c805dbfdfaf6cc6f54/20241121-15-%E3%81%BE%E3%81%A8%E3%82%81.jpg" alt="改変したアバターの集合写真"></p>
<p>今回は「はじめてのVRChatモデル導入と改変」として、人気アバター「マヌカ」ちゃんをベースに、衣装や髪型の購入・導入、瞳や服のテクスチャ編集・色変更に挑戦しました。</p>
<p>VRChat公式ツール「VCC」の登場や、Boothでの豊富な対応アセット、そして多くの先人たちが残してくれた分かりやすい解説記事のおかげで、初心者でも比較的スムーズに、そして楽しくアバター改変を進めることができました。関係者の皆様に感謝です。</p>
<p>目標としていた「落ち着いてミステリアス」な雰囲気のマヌカちゃんも、なんとか形になったのではないかと思います。</p>
<p>最終目標であるBlenderでのフルスクラッチモデル制作への道はまだ遠いですが、今回のような改変作業を通して、テクスチャ編集やUnityの操作、アバターの構造（Armatureやシェイプキー）についての理解を少しずつ深めることができました。今後もVRoidモデルの導入などを試しつつ、学習を続けていけば、いつか目標に到達できるかもしれない、と感じています。</p>
<p>何よりも、自分の手でアバターが変化していく過程は非常に面白かったです。この記事が、これからVRChatアバターの世界に足を踏み入れようとしている方、初めてのアバター改変に挑戦しようとしている方の、何かしらの参考になれば幸いです。次回は「ギミック組み込み」など、もう少し複雑な改変にも挑戦してみたいと思います！</p>
<h2>おまけ：ワールド制作失敗談 {#bonus}</h2>
<p><img src="/b8889cc59dd932994971a3cc3042648b/20241121-14-%E3%83%86%E3%82%B9%E3%83%88%E3%83%AF%E3%83%BC%E3%83%AB%E3%83%89%E4%BD%9C%E6%88%90.webp" alt="自作ワールドでの失敗シーン"></p>
<p>アバター改変と並行して、VRChatの「ワールド制作」にも少しだけ挑戦してみました。「ワールド作るならアスレチックでしょ！」と安易に考えた結果、見事に壁にぶつかりました。</p>
<p>特に難しかったのが「移動する床にプレイヤーを乗せる」という処理です。通常のゲーム開発の感覚で動く床を配置しても、VRChatではプレイヤーが床の動きに追従せず、そのまま下に落ちてしまいます。これは、VRChatがオンラインゲームであり、複数プレイヤーの位置情報を常に同期させる必要があるため、単純な物理演算だけではうまくいかないようです。</p>
<p>椅子に座らせるなどのギミックを使えば回避できるそうですが、移動床を実現するにはかなりテクニカルな実装が必要になるとのこと。まずは基本的なワールド構築から学んでいこうと思います。</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/7e0f2208b67d10ff09e10114cab72ef8/Thumb_20241121-VRChat%E3%81%AF%E3%81%98%E3%82%81%E3%81%A6%E3%81%AE%E3%83%A2%E3%83%87%E3%83%AB%E5%B0%8E%E5%85%A5.jpg" medium="image"/></item><item><title><![CDATA[【RPGツクールMV/MZ】マップ単位でキャラクターを自動切り替えするプラグイン UM_MapActorSetting.js]]></title><description><![CDATA[RPGツクールでマップごとに操作キャラクターを自動切り替えするプラグインUM_MapActorSetting.jsの使い方と実装コードを解説]]></description><link>https://uhiyama-lab.com/ja/blog/gamedev/rpgmaker-plugin-mapactorsetting/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/gamedev/rpgmaker-plugin-mapactorsetting/</guid><category><![CDATA[rpgmaker]]></category><pubDate>Mon, 28 Oct 2024 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p><strong>RPGツクールMV</strong>や<strong>RPGツクールMZ</strong>で、複数の主人公が登場したり、特定のマップでは特定のキャラクターを操作したりするようなゲームを作りたいと思ったことはありませんか？</p>
<p>通常、マップごとに操作キャラクターを切り替えるには、マップ遷移イベントや場所移動イベントの中に「パーティリーダーの変更」コマンドを配置する必要があります。しかし、対象となるマップが多い場合、この方法は以下のような課題があります。</p>
<ul>
<li>マップごとにイベントを設定・コピーする<strong>手間がかかる</strong>。</li>
<li>設定漏れやキャラクターの指定ミスなど、<strong>バグの原因</strong>になりやすい。</li>
<li>どのマップでどのキャラクターがリーダーになるのか、後から<strong>確認・管理するのが大変</strong>。</li>
</ul>
<p>これらの課題を解決するために、<strong>マップのメモ欄に簡単なタグを記述するだけで、そのマップに入った際に自動的に指定したキャラクターに操作を切り替える</strong>プラグイン「<strong>UM_MapActorSetting.js</strong>」を作成しました。</p>
<p>この記事では、この自作プラグインの機能、導入方法、設定手順、そして具体的な使用例について解説します。イベントコマンドを使わずに、もっと手軽にキャラクター切り替えを実装したい方におすすめです。</p>
<p><img src="/2709b732d5d65a079ea64dff8cd593c1/20241028-%E3%83%9E%E3%83%83%E3%83%97%E5%8D%98%E4%BD%8D%E3%81%A7%E6%93%8D%E4%BD%9C%E3%82%AD%E3%83%A3%E3%83%A9%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B.webp" alt="マップ移動時に自動で操作キャラクターが切り替わる様子"></p>
<p>:::ad</p>
<hr>
<p><strong>この記事の内容</strong></p>
<ol>
<li>プラグインの概要とメリット</li>
<li>プラグインのインストール方法</li>
<li>基本的な設定手順 (パラメータとマップメモ)</li>
<li>具体的な使用例 (勇者と姫の切り替え)</li>
<li>使用上の注意点</li>
<li>プラグインコード (UM_MapActorSetting.js)</li>
</ol>
<hr>
<h2>プラグインの概要とメリット</h2>
<p>前述の通り、RPGツクールMV/MZでマップごとに操作キャラクターを切り替える標準的な方法は、イベントコマンド「パーティリーダーの変更」を使用することです。しかし、この方法はマップ数が多くなるほど設定が煩雑になり、管理も大変です。</p>
<p><strong>UM_MapActorSetting.js</strong> プラグインは、この問題を解決するために開発されました。</p>
<p><strong>主な機能とメリット：</strong></p>
<ul>
<li><strong>マップのメモ欄で指定:</strong> どのマップでどのキャラクターを操作させるかを、各マップの「メモ」欄に簡単なタグ（例: <code>&#x3C;Player:hero></code>）で指定します。</li>
<li><strong>自動切り替え:</strong> プレイヤーがメモタグを設定したマップに移動すると、プラグインが自動的にパーティの先頭キャラクター（操作キャラクター）を指定されたアクターに入れ替えます。</li>
<li><strong>イベント不要:</strong> マップごとに切り替え用のイベントを作成・配置する必要がなくなります。</li>
<li><strong>設定がシンプル:</strong> プラグインパラメータで「識別子」と「アクターID」を紐付け、マップメモに識別子を書くだけなので、設定が簡単で、後からの確認や変更も容易です。</li>
</ul>
<p>これにより、複数キャラクターを切り替えるゲームの開発がよりスムーズになります。</p>
<p>:::ad</p>
<h2>プラグインのインストール方法</h2>
<p>プラグインの導入は簡単です。</p>
<ol>
<li>この記事の最後にあるプラグインコードをコピーし、テキストエディタ（メモ帳など）に貼り付けて、ファイル名を <code>UM_MapActorSetting.js</code> として保存します。</li>
<li>作成した <code>UM_MapActorSetting.js</code> ファイルを、あなたのRPGツクールプロジェクトフォルダ内の <code>js/plugins</code> フォルダに配置します。</li>
<li>RPGツクールエディタを開き、「ツール」メニューから「プラグイン管理」を開きます。</li>
<li>プラグインリストの空いている行をダブルクリックし、「名前」のドロップダウンから <code>UM_MapActorSetting</code> を選択し、「状態」を「ON」にして「OK」をクリックします。</li>
</ol>
<p><img src="/7f951310894d95dfc0405fc1e21bb0e1/20241028-01-%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E5%B0%8E%E5%85%A5.png" alt="RPGツクールMV/MZのプラグイン管理画面でUM_MapActorSettingを有効化"></p>
<p>これでプラグインが有効になりました。</p>
<h2>基本的な設定手順 (パラメータとマップメモ)</h2>
<p>次に、プラグインが正しく動作するように設定を行います。</p>
<h3>プラグインパラメータの設定</h3>
<p>まず、マップのメモ欄で使う「識別子（任意の文字列）」と、実際に切り替える「アクター（キャラクター）」を紐付けます。</p>
<ol>
<li>プラグイン管理画面で <code>UM_MapActorSetting</code> をダブルクリック（または選択してEnter）し、右側の「パラメータ」欄を開きます。</li>
<li>「ActorSettings」（アクター設定リスト）という項目をダブルクリックします。</li>
<li>リストの空いている行をダブルクリック（または下の「+」ボタン的なものを操作）して、新しい設定を追加します。</li>
<li>追加した行で、以下の2つの項目を設定します。
<ul>
<li><strong>Character ID (キャラクターID):</strong> マップのメモ欄で使用する任意の識別子（半角英数字推奨、例: <code>hero</code>, <code>princess</code>, <code>actor1</code> など）を入力します。</li>
<li><strong>Actor (アクター):</strong> 右側のプルダウンメニューから、この識別子に対応させたいアクターをデータベースから選択します。</li>
</ul>
</li>
<li>切り替えたいキャラクターの数だけ、この設定を追加します。</li>
<li>設定が終わったら「OK」をクリックして閉じます。</li>
</ol>
<p><img src="/b93ad0b91e252cbd4bd915a57e296858/20241028-02-%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E5%B0%8E%E5%85%A5_1.png" alt="プラグインパラメータActorSettingsの設定画面を開く"></p>
<p><img src="/c6ba3b7766bc9cbebd5fcac101362d5b/20241028-03-%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E5%B0%8E%E5%85%A5_1.png" alt="Character IDとActorを設定する"></p>
<h3>マップメモの設定</h3>
<p>次に、操作キャラクターを切り替えたいマップ側で設定を行います。</p>
<ol>
<li>RPGツクールエディタで、操作キャラクターを自動で切り替えたいマップを開きます。</li>
<li>マップ編集画面で、マップ名を右クリック（または編集モードでマップを選択）し、「マップ設定」（または「マップのプロパティ」）を開きます。</li>
<li>右下にある「<strong>メモ</strong>」欄に、以下の形式でタグを記述します。</li>
</ol>
<pre><code>&#x3C;Player:識別子>
</code></pre>
<p><code>識別子</code> の部分には、プラグインパラメータで設定した「<strong>Character ID</strong>」を正確に入力します。（例: <code>&#x3C;Player:hero></code>）</p>
<ol start="4">
<li>設定したら「OK」をクリックして閉じます。</li>
</ol>
<p><img src="/bc19bad503e966263002a688567dde38/20241028-04-%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E5%B0%8E%E5%85%A5_1.png" alt="マップ設定のメモ欄にタグを記述する"></p>
<p>これで、プレイヤーがこのマップに移動してきた際に、<a href="Player:%E8%AD%98%E5%88%A5%E5%AD%90">Player:識別子</a> タグで指定された識別子に対応するアクターが、自動的にパーティの先頭（操作キャラクター）になります。</p>
<p>:::ad</p>
<h2>具体的な使用例 (勇者と姫の切り替え)</h2>
<p>例として、「アクターID: 1」が「勇者」、「アクターID: 2」が「姫」としてデータベースに登録されているとします。</p>
<h3>1. プラグインパラメータの設定：</h3>
<p>プラグイン管理画面で以下のように設定します。</p>
<p><img src="/db09b103f06d9e74f3c4ed8966bd44e1/20241028-05-%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E5%B0%8E%E5%85%A5_1.png" alt="勇者(hero)と姫(princess)のパラメータ設定例"></p>
<ul>
<li><strong>設定1:</strong>
<ul>
<li>Character ID: <code>hero</code></li>
<li>Actor: <code>0001: 勇者</code> (データベースのアクターID 1)</li>
</ul>
</li>
<li><strong>設定2:</strong>
<ul>
<li>Character ID: <code>princess</code></li>
<li>Actor: <code>0002: 姫</code> (データベースのアクターID 2)</li>
</ul>
</li>
</ul>
<p><img src="/65550e395a3c4efc7a3cc8c3e5dc8061/20241028-06-%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E5%B0%8E%E5%85%A5_1.png" alt="プラグインパラメータ設定後のリスト表示"></p>
<h3>2. マップメモの設定：</h3>
<p>例えば、2つのマップに対して以下のようにメモを設定します。</p>
<ul>
<li><strong>マップ1（勇者の街）のメモ欄:</strong></li>
</ul>
<pre><code>&#x3C;Player:hero>
</code></pre>
<ul>
<li><strong>マップ2（お城）のメモ欄:</strong></li>
</ul>
<pre><code>&#x3C;Player:princess>
</code></pre>
<p><strong>結果：</strong></p>
<ul>
<li>プレイヤーが「勇者の街」マップに入ると、操作キャラクターが自動的に「勇者」になります。</li>
<li>プレイヤーが「お城」マップに入ると、操作キャラクターが自動的に「姫」になります。</li>
</ul>
<p><img src="/2709b732d5d65a079ea64dff8cd593c1/20241028-%E3%83%9E%E3%83%83%E3%83%97%E5%8D%98%E4%BD%8D%E3%81%A7%E6%93%8D%E4%BD%9C%E3%82%AD%E3%83%A3%E3%83%A9%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B.webp" alt="マップ移動でキャラクターが切り替わるデモ"></p>
<p>このように、イベントコマンドを一切使わずに、マップごとの操作キャラクター切り替えを実現できます。</p>
<h2>使用上の注意点</h2>
<ul>
<li>キャラクターの切り替えは、マップ遷移時（場所移動コマンド実行後など）とゲームロード時に自動的に行われます。</li>
<li>マップのメモ欄に記述するタグ <code>&#x3C;Player:識別子></code> の「識別子」部分は、プラグインパラメータで設定した「Character ID」と<strong>完全に一致</strong>させてください。大文字・小文字も区別されます。記述を間違えると切り替えは機能しません。</li>
<li>プラグインは、指定されたアクターをパーティの<strong>先頭</strong>に入れ替える（元々いた場合は先頭にし、いなければ追加して先頭にする）シンプルな動作をします。パーティメンバー全体の入れ替えや並び順の制御は行いません。もし元々パーティにいないアクターを指定した場合、そのアクターがパーティに追加されてリーダーになります。（元のリーダーはパーティから外れます）</li>
<li>データベースで新しいアクターを追加・変更した場合や、プラグインパラメータの「Character ID」を変更した場合は、必ずプラグインパラメータの設定も更新してください。</li>
<li>他のパーティメンバー変更系プラグインと競合する可能性はあります。併用する場合はご注意ください。</li>
</ul>
<p>:::ad</p>
<h2>プラグインコード (UM_MapActorSetting.js)</h2>
<p>このプラグインコードはご自由にお使いください。改変なども問題ありません。</p>
<pre><code class="language-javascript">//=============================================================================
// UM_MapActorSetting
//=============================================================================

/*:
 * @plugindesc マップのメモタグに基づいて、自動的にプレイヤーキャラクターを切り替えます。
 * @author UHIMA
 *
 * @param MapActorSettings
 * @text アクター設定
 * @type struct&#x3C;ActorConfig>[]
 * @desc 各アクターの設定
 *
 * @help
 * ============================================================================
 * ■ 概要
 * ============================================================================
 * このプラグインは、マップのメモタグに基づいて自動的にプレイヤーキャラクターを
 * 切り替えることを可能にします。
 *
 * ============================================================================
 * ■ 使用方法
 * ============================================================================
 * プレイヤーキャラクターを切り替えたいマップには、以下のメモタグを追加してください：
 *
 * &#x3C;Player:characterId>
 *
 * プラグインパラメータで「データベース」のアクターIDとキャラクター名を紐づけることができます。
 * これにより&#x3C;Player:harold>のような簡易的な記述でアクターを切り替えることができます。
 * 新規キャラクターを追加した場合やアクターIDを変更した場合は、このプラグインの紐づけも対応させてください。
 *
 * 例：
 * &#x3C;Player:harold>
 */

/*~struct~ActorConfig:
 * @param Character ID
 * @text キャラクターID
 * @type string
 * @desc マップメモタグで使用する識別子（例：harold）
 *
 * @param Actor
 * @text アクター
 * @type actor
 * @desc 切り替えるアクター
 */

(function () {
  var parameters = PluginManager.parameters("UM_MapActorSetting");
  var MapActorSettings = JSON.parse(parameters["MapActorSettings"] || "[]").map(
    (setting) => JSON.parse(setting)
  );

  var _Game_Player_performTransfer = Game_Player.prototype.performTransfer;
  Game_Player.prototype.performTransfer = function () {
    _Game_Player_performTransfer.call(this);
    switchActorByMap();
  };

  function switchActorByMap() {
    if ($dataMap &#x26;&#x26; $dataMap.meta.Player) {
      var characterId = $dataMap.meta.Player;
      var actorConfig = MapActorSettings.find(
        (config) => config["Character ID"] === characterId
      );

      if (actorConfig) {
        var newActorId = Number(actorConfig.Actor);
        var currentLeader = $gameParty.leader();
        var currentLeaderId = currentLeader ? currentLeader.actorId() : null;

        if (currentLeaderId !== newActorId) {
          if (currentLeaderId) {
            $gameParty.removeActor(currentLeaderId);
          }
          $gameParty.addActor(newActorId);
          $gamePlayer.refresh();
        }
      }
    }
  }

  var _Scene_Map_start = Scene_Map.prototype.start;
  Scene_Map.prototype.start = function () {
    _Scene_Map_start.call(this);
    switchActorByMap();
  };
})();
</code></pre>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/2709b732d5d65a079ea64dff8cd593c1/20241028-%E3%83%9E%E3%83%83%E3%83%97%E5%8D%98%E4%BD%8D%E3%81%A7%E6%93%8D%E4%BD%9C%E3%82%AD%E3%83%A3%E3%83%A9%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B.webp" medium="image"/></item><item><title><![CDATA[【RPGツクールMV/MZ】スイッチで床が出現！イベントを使ったギミックの作り方]]></title><description><![CDATA[RPGツクールで特定のスイッチをONにすると床が出現して通行可能になるギミックの作り方を詳しく解説]]></description><link>https://uhiyama-lab.com/ja/blog/gamedev/rpgmaker-spawn-maptile/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/gamedev/rpgmaker-spawn-maptile/</guid><category><![CDATA[rpgmaker]]></category><pubDate>Wed, 23 Oct 2024 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p><img src="/7e5c0c95e30d10fefe73b0bee9422a3f/20241023-1-RPG%E3%83%84%E3%82%AF%E3%83%BC%E3%83%AB%E9%80%9A%E8%A1%8C%E5%BA%8A%E8%A1%A8%E7%A4%BA.webp" alt="RPGツクールでスイッチON時に床が出現する様子"></p>
<p><strong>RPGツクールMV</strong>や<strong>RPGツクールMZ</strong>でゲームを作成する際、特定の条件を満たすと道が開ける、といったギミックを実装したくなることがありますよね。例えば、ボスを倒したり、仕掛けを解いたりした後に、それまで通れなかった場所に<strong>床が出現</strong>して進めるようになる、といった演出です。</p>
<p>このような「<strong>スイッチ</strong>をONにすると<strong>出現する床</strong>」は、プレイヤーに達成感を与え、探索の幅を広げる効果的なギミックとなります。</p>
<p>この記事では、RPGツクールMV/MZで、特定のスイッチがONになった時に<strong>イベント</strong>機能を使って床（足場）を出現させ、通行可能にする具体的な<strong>作り方</strong>を解説します。</p>
<hr>
<p>:::ad</p>
<p><strong>この記事の内容</strong></p>
<ol>
<li>なぜ「イベント」で床を作るのか？（概要説明）</li>
<li>イベントで床タイル画像を使うための「タイルセット」編集</li>
<li>スイッチで出現・通行可能になる床イベントの設定手順</li>
<li>まとめ：イベント活用で動的なマップを作ろう</li>
</ol>
<hr>
<h2>なぜ「イベント」で床を作るのか？（概要説明）</h2>
<p>RPGツクールでは、マップの基本的な地形や壁などは「<strong>タイル</strong>」を配置して作成します。しかし、マップに配置した<strong>タイルそのものは、基本的に静的なもの</strong>であり、ゲーム中のスイッチや変数の状態によって表示を切り替えたり、消したりすることはできません。</p>
<p>そのため、今回のような「特定の条件（スイッチON）で出現する床」のような動的な仕掛けは、「<strong>イベント</strong>」機能を使って実装する必要があります。イベントであれば、出現条件を設定したり、表示する画像を指定したり、通行設定を制御したりすることが可能です。</p>
<p>この記事では例として、ゲーム内の**スイッチ「#0001_通行床表示」**がONになった時に、指定した場所に床タイルが表示され、通行可能になるように設定していきます。</p>
<p>:::ad</p>
<h2>イベントで床タイル画像を使うための「タイルセット」編集</h2>
<p>まず、出現させたい床のグラフィックをイベントの画像として設定する準備が必要です。</p>
<p>マップ上で新しいイベントを作成し、画像設定を開いてみてください。おそらく、[タイルセットB]（壁やオブジェクトなど）や[タイルセットC]（建物の屋根など）の画像は選択できても、肝心の<strong>地面や床に使われるタイル画像がリストに表示されない</strong>ことに気づくはずです。</p>
<p><img src="/b596604e63ddeffca91d13df26168b85/20241023-2-%E3%82%BF%E3%82%A4%E3%83%AB%E3%82%BB%E3%83%83%E3%83%88%E5%88%9D%E6%9C%9F%E8%A8%AD%E5%AE%9A.png" alt="イベント画像設定の初期状態（床タイルがない）"></p>
<p>これは、イベント画像として使用できるタイルセットがデフォルトでは限定されているためです。これを解決するには、「<strong>データベース</strong>」（ツールバーの歯車アイコンなど）を開き、「<strong>タイルセット</strong>」タブで設定を変更する必要があります。</p>
<ol>
<li>
<p>データベースを開き、「タイルセット」タブを選択します。</p>
</li>
<li>
<p>現在のマップで使用しているタイルセット（例: 001: フィールド）を選択します。</p>
</li>
<li>
<p>右側の設定項目で、<strong>[A]タブの[A2]（地面タイル）に設定されている画像シートと同じ画像ファイルを、[D]タブ（または空いている他のタブ、例: [E]）にも設定</strong>します。</p>
</li>
</ol>
<p><img src="/e1e330aa6f231d72b7ce68679b8cfcbc/20241023-3-%E3%82%BF%E3%82%A4%E3%83%AB%E3%82%BB%E3%83%83%E3%83%88%E6%9B%B4%E6%96%B0.png" alt="タイルセットのA2と同じ画像をDに設定する"></p>
<ol start="4">
<li>これで設定は完了です。「適用」または「OK」を押してデータベースを閉じます。</li>
</ol>
<p>この操作により、イベントの画像設定リストに[タイルセットD]（または設定したタブ）が追加され、そこから床タイルを選択できるようになります。</p>
<p><strong>【重要】通行設定の確認！</strong></p>
<p>タイルセット編集画面で、追加設定した床タイルの画像を選択し、左側の「<strong>通行</strong>」設定がどのようになっているか必ず確認してください。</p>
<ul>
<li><strong>◯</strong>: 通行可能</li>
<li><strong>×</strong>: 通行不可</li>
<li><strong>★</strong>: 通行可能（プレイヤーより上に表示）</li>
</ul>
<p>出現させたい床タイルが「<strong>×</strong>」（通行不可）になっていると、イベントで床が表示されてもプレイヤーはその上を歩くことができません。必ず「<strong>◯</strong>」（または状況に応じて★）に設定されているか確認し、もし×だったらクリックして◯に変更しておきましょう。</p>
<p>:::ad</p>
<h2>スイッチで出現・通行可能になる床イベントの設定手順</h2>
<p>タイルセットの準備ができたら、床を出現させるイベントを作成します。設定は非常にシンプルです。</p>
<ol>
<li>
<p>床を出現させたいマップ上のマスを右クリックし、「イベントの作成」を選択します。</p>
</li>
<li>
<p>イベントエディターが開いたら、まず<strong>1ページ目</strong>（初期状態のページ）は、<strong>画像などを何も設定せず、基本的にそのままの状態</strong>にしておきます。ただし、以下の2点は確認・設定してください。</p>
</li>
</ol>
<ul>
<li><strong>オプション:</strong> 「すり抜け」のチェックが<strong>オフ</strong>になっていることを確認します。（チェックが入っていると、床がない状態でもプレイヤーが通過できてしまいます）</li>
<li><strong>プライオリティ:</strong> 「<strong>通常キャラの下</strong>」を選択します。（これにより、後で床が出現したときにプレイヤーがその上を歩けます）</li>
</ul>
<p>（※トリガーはデフォルトの「決定ボタン」のままで問題ありません。実行内容も空のままでOKです。）</p>
<p><img src="/f9f9340287b3519ec516995094ad1e88/20241023-5-%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E8%A8%AD%E5%AE%9A.png" alt="出現床イベントの設定画面（1ページ目：ほぼデフォルト）"></p>
<ol start="3">
<li>
<p>次に、イベントエディター上部の「<strong>EVページ作成</strong>」ボタンをクリックし、<strong>2ページ目</strong>を作成します。</p>
</li>
<li>
<p><strong>2ページ目</strong>で、床が出現した状態を設定します。</p>
</li>
</ol>
<ul>
<li><strong>出現条件:</strong> 左側の「出現条件」欄で「<strong>スイッチ</strong>」にチェックを入れ、対象のスイッチ（例: <strong>#0001_通行床表示</strong>）が「<strong>ON</strong>」であることを指定します。</li>
<li><strong>画像:</strong> 左下の「画像」欄をダブルクリックし、タイルセット選択画面を開きます。先ほどタイルセット編集で選択可能になった床タイル（例: [タイルセットD]から）を選び、「OK」をクリックします。</li>
<li><strong>オプション:</strong> 1ページ目と同様に、「すり抜け」は<strong>オフ</strong>のままにします。</li>
<li><strong>プライオリティ:</strong> 1ページ目と同じく「<strong>通常キャラの下</strong>」を選択します。</li>
</ul>
<p>（※トリガーや実行内容は、このページも基本的にそのままでOKです。）</p>
<p><img src="/0bfca8b8b293e46b1f106fb7a4e4527e/20241023-6-%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E8%A8%AD%E5%AE%9A-2.png" alt="出現床イベントの設定画面（2ページ目：スイッチONで床画像表示）"></p>
<ol start="5">
<li>
<p>これでイベントの設定は完了です。「OK」を押してイベントエディターを閉じます。</p>
</li>
<li>
<p>作成した「出現床」イベントをコピー（Ctrl+C または Cmd+C）し、床を出現させたい他のマスにペースト（Ctrl+V または Cmd+V）して配置していきます。</p>
</li>
</ol>
<p><img src="/791cd291d26108d233d22fa5c6681e99/20241023-4-%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E9%85%8D%E7%BD%AE.png" alt="マップ上に作成した出現床イベントをコピーして配置する"></p>
<p>ゲームをテストプレイし、指定したスイッチ（例: #0001_通行床表示）をONにするイベントを実行すると、イベントを配置した場所に床が出現し、その上を歩けるようになっているはずです。</p>
<p><img src="/7e5c0c95e30d10fefe73b0bee9422a3f/20241023-1-RPG%E3%83%84%E3%82%AF%E3%83%BC%E3%83%AB%E9%80%9A%E8%A1%8C%E5%BA%8A%E8%A1%A8%E7%A4%BA.webp" alt="スイッチON後に床が出現し通行可能になったゲーム画面"></p>
<p>以上で、スイッチ操作によって出現する通行可能な床の実装は完了です。</p>
<hr>
<p>:::ad</p>
<h2>まとめ：イベント活用で動的なマップを作ろう</h2>
<p>この記事では、<strong>RPGツクールMV/MZ</strong>で「<strong>スイッチ</strong>がONになると<strong>出現する床</strong>」を<strong>イベント</strong>機能を使って実装する基本的な方法を解説しました。</p>
<p>ポイントは以下の通りです。</p>
<ul>
<li>タイル自体は動的に変更できないため、「イベント」として床を作成する。</li>
<li>イベント画像で床タイルを使うために、事前に「データベース」で「タイルセット」設定を変更しておく必要がある。</li>
<li>タイルセット編集時には、床タイルの「通行設定」が「◯（通行可能）」になっているか確認する。</li>
<li>イベントは2ページ構成にし、1ページ目は空白（スイッチOFF時）、2ページ目は出現条件（スイッチON）と床画像、オプション（<strong>すり抜けOFF</strong>、<strong>プライオリティ通常キャラの下</strong>）を設定する。</li>
</ul>
<p>このテクニックを使えば、ボス撃破後に新たな道が開けたり、仕掛けを解くことで隠し通路が出現したりと、プレイヤーの行動によってマップが変化する、よりダイナミックなゲームを作ることができます。ぜひ、あなたの作品作りに活用してみてください！</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/7e5c0c95e30d10fefe73b0bee9422a3f/20241023-1-RPG%E3%83%84%E3%82%AF%E3%83%BC%E3%83%AB%E9%80%9A%E8%A1%8C%E5%BA%8A%E8%A1%A8%E7%A4%BA.webp" medium="image"/></item><item><title><![CDATA[【Unity】InputSystemを使って「溜め攻撃」を超シンプルに実装する方法]]></title><description><![CDATA[UnityのInput Systemを活用して溜め攻撃を実装する方法を解説。startedとcanceledイベントを使ったシンプルな実装例を紹介]]></description><link>https://uhiyama-lab.com/ja/blog/gamedev/unity-input-system-charge-attack/</link><guid isPermaLink="false">https://uhiyama-lab.com/ja/blog/gamedev/unity-input-system-charge-attack/</guid><category><![CDATA[unity]]></category><pubDate>Fri, 05 Apr 2024 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>この記事では、Unityの新しい入力システム「<strong>Input System</strong>」を活用して、ゼルダの伝説の回転斬りのような「<strong>溜め攻撃</strong>」アクションを実装する方法を解説します。</p>
<p>Input Systemには長押しを検知する「Hold Interaction」もありますが、今回はあえてそれを使わず、ボタン入力の基本的なイベントである「<strong>押した瞬間 (<code>started</code>)</strong>」と「<strong>離した瞬間 (<code>canceled</code>)</strong>」の時刻差を利用するアプローチを紹介します。</p>
<p>従来の<code>Update</code>関数内で毎フレーム入力を監視する方法と比較して、Input Systemのイベント駆動型アプローチは、コードがシンプルになり、特に機能追加や変更に対する保守性が向上するメリットが期待できます。</p>
<hr>
<p><strong>この記事の内容</strong></p>
<ol>
<li>溜め攻撃の実装：Input System (Press) vs Update vs Hold Interaction</li>
<li>Input SystemのインストールとInput Actionsアセットの作成</li>
<li>Input Actions：左クリック（攻撃ボタン）アクションの定義</li>
<li>スクリプト連携：PlayerInputとイベント処理スクリプト</li>
<li>溜め時間の計算ロジック：startedとcanceledの活用</li>
<li>中間演出の実装：コルーチンでInput Systemの弱点を補う</li>
<li>まとめ：Pressイベント方式のメリット・デメリットと使い分け</li>
</ol>
<hr>
<p>:::ad</p>
<h2>溜め攻撃の実装：Input System (Press) vs Update vs Hold Interaction</h2>
<p>溜め攻撃を実装する方法はいくつか考えられます。それぞれの特徴を見てみましょう。</p>
<ul>
<li><strong>従来の<code>Update</code>関数による実装：</strong>
<ul>
<li><code>Update()</code>内で毎フレーム入力をチェックし、ボタンが押されている時間を計測します。</li>
<li>仕組みは直感的ですが、入力の種類や条件分岐が増えるとコードが複雑化しがちです。</li>
</ul>
</li>
<li><strong>Input System の <code>Hold Interaction</code> を使う実装：</strong>
<ul>
<li>Input ActionsアセットでInteractionを「Hold」に設定し、指定時間（Hold Time）長押しされると<code>performed</code>イベントが発生します。</li>
<li>設定は簡単ですが、後述するように「短押し（通常攻撃）」と「長押し（溜め攻撃）」を同じボタンで使い分けたい場合に、実装がやや複雑になることがあります。</li>
</ul>
</li>
<li><strong>Input System の <code>Press</code> イベント (<code>started</code>/<code>canceled</code>) を使う実装（本記事のアプローチ）：</strong>
<ul>
<li>ボタンが押された瞬間（<code>started</code>）と離された瞬間（<code>canceled</code>）のイベントを利用し、その間の時間を計測します。</li>
<li>イベント駆動でコードが整理されやすく、短押し・長押しの判定や溜め中の処理を柔軟に実装できます。</li>
</ul>
</li>
</ul>
<h3>Q. なぜHold InteractionではなくPressイベント (started/canceled) を使うのか？</h3>
<p><code>Hold Interaction</code>は指定時間長押しされたことを検知するのに便利ですが、「同じボタンで短押し（通常攻撃）と長押し（溜め攻撃）を使い分けたい」場合や、「溜めている最中に演出を入れたい」場合には、少し扱いにくい側面があります。</p>
<ul>
<li><strong>短押しと長押しの両立：</strong> Hold Interactionでは、長押しが成立して<code>performed</code>が呼ばれる前に、短押し時の処理（通常攻撃など）をボタンを押した直後に実行させたい場合、工夫が必要です。例えば、「押した瞬間（<code>started</code>）に通常攻撃を出し、Holdが成立したらキャンセルする」といった制御や、別途短押し用のアクションを用意するなど、実装が複雑になりがちです。</li>
<li><strong>押下開始タイミングの活用：</strong> ボタンを押した瞬間にキャラクターが構えモーションに入ったり、溜めエフェクトを開始したりといった処理を即座に行いたい場合、Hold Interactionだけではタイミングを計りにくいことがあります。</li>
<li><strong>溜め中の制御：</strong> 溜めゲージを表示したり、一定時間ごとに溜めレベルが上がる演出を入れたりする場合、最終的に<code>performed</code>イベントが発生するまで待つ必要があるHold Interactionでは、溜めている途中の細かな制御がしづらいです。</li>
</ul>
<p>一方、本記事で紹介する**<code>Press</code>イベント (<code>started</code>/<code>canceled</code>) を利用する方法**では、</p>
<ul>
<li>ボタンを押した瞬間の<code>started</code>イベントと、離した瞬間の<code>canceled</code>イベントの時刻差を計測することで、押下時間を正確に把握できます。</li>
<li>これにより、「一定時間未満なら通常攻撃、一定時間以上なら溜め攻撃」といった判定を<code>canceled</code>イベント内で簡単に行えます。</li>
<li><code>started</code>イベント発生時に溜め動作やエフェクトを開始し、<code>canceled</code>イベントで攻撃を発動、という流れを自然に実装できます。</li>
<li>（後述するコルーチンなどを組み合わせれば）溜めている最中の演出や状態変化も柔軟に組み込めます。</li>
</ul>
<p>このように、<strong>短押し/長押しの判定や、溜め中の演出・状態管理を柔軟に行いたい場合</strong>には、<code>started</code>/<code>canceled</code>イベントを利用するアプローチが適していると言えます。</p>
<hr>
<p>:::ad</p>
<h2>Input SystemのインストールとInput Actionsアセットの作成</h2>
<p>まず、プロジェクトにInput Systemパッケージを導入し、入力を定義するInput Actionsアセットを作成します。</p>
<h3>Input Systemパッケージのインストール</h3>
<p>Unityエディタのメニューから操作します。</p>
<p><img src="/01f9ea2434798b9f69941dfc524c1870/20240405-001-InputSystem%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%96%B9%E6%B3%95.png" alt="Package ManagerからInput Systemをインストールする手順"></p>
<ol>
<li><strong>Window > Package Manager</strong> を開きます。</li>
<li>左上のドロップダウンメニューで「<strong>Packages: Unity Registry</strong>」を選択します。</li>
<li>リストから「<strong>Input System</strong>」を探し、「Install」ボタンをクリックします。</li>
<li>インストール中にプロジェクト設定の変更を促すダイアログが出たら、「Yes」を選択してエディタを再起動します。</li>
</ol>
<h3>Input Actionsアセットファイルの作成</h3>
<p>次に、入力アクションを定義するためのアセットファイルを作成します。</p>
<p><img src="/de1f989dd6e51c777f5fd5040a1e1dce/20240405-002-InputActions%E3%81%AE%E9%81%B8%E6%8A%9E.png" alt="ProjectウィンドウでInput Actionsアセットを作成"></p>
<ol>
<li>Projectウィンドウで右クリックし、<strong>Create > Input Actions</strong> を選択します。</li>
<li>作成されたアセットファイル（例: <code>PlayerInputActions.inputactions</code>）に分かりやすい名前を付けます。</li>
<li>作成したアセットファイルを選択し、Inspectorウィンドウで「<strong>Generate C# Class</strong>」にチェックを入れ、「Apply」ボタンを押します。</li>
</ol>
<p><img src="/ef48f4829b7a3d51e834c1b07195bce1/20240405-003-InputActions%E3%81%A7%E4%BD%9C%E6%88%90%E3%81%95%E3%82%8C%E3%81%9F%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.png" alt="Generate C# Classにチェックを入れるとC#スクリプトが生成される"></p>
<p>「Generate C# Class」にチェックを入れることで、このInput Actionsアセットに対応するC#クラスが自動生成され、スクリプトから入力イベントを扱いやすくなります。</p>
<hr>
<h2>Input Actions：左クリック（攻撃ボタン）アクションの定義</h2>
<p>作成したInput Actionsアセットファイル（例: <code>PlayerInputActions.inputactions</code>）をダブルクリックして編集ウィンドウを開き、溜め攻撃に使用するアクションを定義します。</p>
<p><img src="/5b55d7d9bcc6411a98f16180c6e40494/20240405-004-InputAction%E3%81%AE%E7%99%BB%E9%8C%B2.png" alt="Input Actionsエディタで攻撃アクションを設定する"></p>
<ol>
<li><strong>Action Maps</strong> 列の「+」ボタンをクリックし、新しいAction Mapを作成します（例: <code>Gameplay</code>）。Action Mapは関連するアクションをまとめるグループです。</li>
<li><strong>Actions</strong> 列の「+」ボタンをクリックし、新しいActionを作成します（例: <code>AttackLeft</code>）。これが具体的な入力操作に対応します。</li>
<li>作成した<code>AttackLeft</code> Actionを選択し、右側のPropertiesパネルで以下を設定します。
<ul>
<li><strong>Action Type:</strong> 「<strong>Button</strong>」を選択します。これは、押す/離すの単純な入力に適しています。</li>
</ul>
</li>
<li><code>AttackLeft</code> Actionの下にある<code>&#x3C;No Binding></code>を選択し、Propertiesパネルで以下を設定します。これが具体的な入力デバイスのボタンとの紐付け（Binding）です。
<ul>
<li><strong>Path:</strong> プルダウンメニューから「Mouse」>「<strong>Left Button</strong>」を選択します。（ゲームパッドのボタンなどもここで設定可能）</li>
</ul>
</li>
<li>編集が終わったら、ウィンドウ上部の「<strong>Save Asset</strong>」ボタンをクリックして変更を保存します。</li>
</ol>
<p>これで、「マウスの左クリック」が<code>AttackLeft</code>という名前のアクションとして、スクリプトからイベントとして受け取れるようになりました。</p>
<hr>
<h2>スクリプト連携：PlayerInputとイベント処理スクリプト</h2>
<p>定義したInput Actionsを実際にゲーム内で機能させるために、スクリプトとの連携を設定します。</p>
<ol>
<li>
<p>シーン内に空のGameObjectを作成し、分かりやすい名前（例: <code>InputManager</code>）を付けます。</p>
</li>
<li>
<p>作成した<code>InputManager</code> GameObjectに「<strong>Player Input</strong>」コンポーネントを追加します。</p>
</li>
<li>
<p>Player Inputコンポーネントの「<strong>Actions</strong>」フィールドに、先ほど作成したInput Actionsアセット（例: <code>PlayerInputActions.inputactions</code>）をドラッグ＆ドロップで設定します。</p>
</li>
<li>
<p>以下のC#スクリプト（例: <code>InputManager.cs</code>）を作成し、<code>InputManager</code> GameObjectにアタッチします。</p>
</li>
</ol>
<pre><code class="language-csharp">using UnityEngine;
using UnityEngine.InputSystem;

// PlayerInputコンポーネントが必須であることを示す
[RequireComponent(typeof(PlayerInput))]
public class InputManager : MonoBehaviour
{
    // ボタンが押され始めた時刻を記録する変数
    private float buttonPressStartTime;
    // 溜め攻撃と判定する時間のしきい値（例: 1秒）
    private const float specialAttackThreshold = 1.0f;

    // PlayerInputコンポーネントから呼び出されるメソッド
    // メソッド名はInput Actionsで定義したアクション名（例: AttackLeft）に
    // "On" を付けたものにするか、後述のようにInspectorで手動設定する
    public void OnAttackLeft(InputAction.CallbackContext context)
    {
        // ボタンが押された瞬間 (started) の処理
        if (context.started)
        {
            Debug.Log("Attack button pressed (started)");
            // 押下開始時刻を記録
            buttonPressStartTime = Time.time;
            // ここで構えモーションや溜めエフェクト開始などの処理を入れることも可能
        }
        // ボタンが離された瞬間 (canceled) の処理
        else if (context.canceled)
        {
            Debug.Log("Attack button released (canceled)");
            // 押されていた時間を計算
            float pressDuration = Time.time - buttonPressStartTime;
            Debug.Log($"Press duration: {pressDuration} seconds");

            // 押下時間がしきい値を超えていたら溜め攻撃
            if (pressDuration > specialAttackThreshold)
            {
                PerformSpecialAttack(); // 溜め攻撃実行メソッド呼び出し
            }
            // しきい値未満なら通常攻撃
            else
            {
                PerformNormalAttack(); // 通常攻撃実行メソッド呼び出し
            }
        }
        // context.performed は Button タイプでは started とほぼ同じタイミングで呼ばれることが多い
        // Hold Interaction を使わない場合、主に started と canceled を使う
    }

    // 通常攻撃を実行する処理（中身は仮）
    private void PerformNormalAttack()
    {
        Debug.Log("Perform Normal Attack!");
        // ここに実際の通常攻撃ロジックを記述
    }

    // 溜め攻撃を実行する処理（中身は仮）
    private void PerformSpecialAttack()
    {
        Debug.Log("Perform Special Attack!");
        // ここに実際の溜め攻撃ロジックを記述
    }
}
</code></pre>
<ol start="5">
<li>
<p><code>InputManager</code> GameObjectを選択し、InspectorウィンドウでPlayer Inputコンポーネントの「<strong>Behavior</strong>」を「<strong>Invoke Unity Events</strong>」に設定します。</p>
</li>
<li>
<p>「Events」セクションが展開されるので、設定したAction Map名（例: <code>Gameplay</code>）を開き、その中のアクション名（例: <code>Attack Left</code>）に対応するイベント欄の「+」ボタンをクリックします。</p>
</li>
<li>
<p>イベント欄に<code>InputManager</code> GameObject自体をドラッグ＆ドロップし、右側のドロップダウンメニューから「InputManager」>「<strong>OnAttackLeft (InputAction.CallbackContext)</strong>」を選択します。（スクリプトのメソッド名が <code>On[アクション名]</code> であれば自動で認識されることもあります）</p>
</li>
</ol>
<p>これで、マウスの左ボタンがクリックされる（押される、または離される）たびに、<code>InputManager.cs</code>スクリプト内の<code>OnAttackLeft</code>メソッドが呼び出されるようになります。このイベント駆動の仕組みにより、<code>Update</code>関数を使うことなく入力処理を実現できます。</p>
<hr>
<p>:::ad</p>
<h2>溜め時間の計算ロジック：startedとcanceledの活用</h2>
<p>前述のスクリプト (<code>InputManager.cs</code>) 内の <code>OnAttackLeft</code> メソッドで行っている溜め時間の計算ロジックを詳しく見てみましょう。</p>
<ol>
<li><strong>押下開始 (<code>context.started</code>):</strong>
<ul>
<li>マウスの左ボタンが押された瞬間にこのブロックが実行されます。</li>
<li>現在の時刻 (<code>Time.time</code>) を <code>buttonPressStartTime</code> 変数に記録します。これが溜め時間の計測開始点となります。</li>
</ul>
</li>
<li><strong>押下終了 (<code>context.canceled</code>):</strong>
<ul>
<li>押されていた左ボタンが離された瞬間にこのブロックが実行されます。</li>
<li>現在の時刻 (<code>Time.time</code>) から、記録しておいた押下開始時刻 (<code>buttonPressStartTime</code>) を引くことで、ボタンが押されていた時間 (<code>pressDuration</code>) を計算します。</li>
<li>計算した <code>pressDuration</code> と、あらかじめ定義しておいた溜め攻撃のしきい値 (<code>specialAttackThreshold</code>) を比較します。</li>
<li><code>pressDuration</code> がしきい値より長ければ <code>PerformSpecialAttack()</code> メソッドを、短ければ <code>PerformNormalAttack()</code> メソッドを呼び出します。</li>
</ul>
</li>
</ol>
<p>このように、Input Systemの <code>started</code> と <code>canceled</code> イベントを利用することで、ボタンが押されていた時間を正確に計測し、それに基づいて通常攻撃と溜め攻撃を振り分けることができます。<code>Update()</code> 関数内で毎フレーム時間を加算していく必要がないため、コードがシンプルになり、処理負荷も軽減される可能性があります。</p>
<hr>
<h2>中間演出の実装：コルーチンでInput Systemの弱点を補う</h2>
<p>Input Systemのイベント駆動モデルは、「押した」「離した」といった瞬間のイベントを捉えるのは得意ですが、「<strong>ボタンを押し続けている間の特定のタイミング</strong>（例: 溜め攻撃が可能になる瞬間）」で何か処理を行いたい場合には、少し工夫が必要です。</p>
<p>例えば、「溜め時間がしきい値に達したらキャラクターを光らせる」「溜め完了のSEを鳴らす」といった演出を入れたい場合、<code>started</code> と <code>canceled</code> イベントだけでは、その「中間点」を直接検知できません。</p>
<p>この問題を解決する一般的な方法の一つが、Unityの「<strong>コルーチン (Coroutine)</strong>」を利用することです。ボタンが押された (<code>started</code>) 時点でコルーチンを開始し、一定時間（溜め攻撃のしきい値）が経過したら、溜め完了の合図（シグナル）を送る、という仕組みを作ります。</p>
<p>以下は、先ほどの <code>InputManager.cs</code> にコルーチンを追加し、溜め完了のシグナル（ここではDebugログ出力）を実装した例です。</p>
<pre><code class="language-csharp">using UnityEngine;
using UnityEngine.InputSystem;
using System.Collections; // コルーチンのために必要

[RequireComponent(typeof(PlayerInput))]
public class InputManager : MonoBehaviour
{
    private float buttonPressStartTime;
    private const float specialAttackThreshold = 1.0f;
    // 実行中のコルーチンを保持する変数
    private Coroutine chargeCheckCoroutine;
    // 溜め完了シグナルが送られたかどうかのフラグ
    private bool isChargeComplete = false;

    public void OnAttackLeft(InputAction.CallbackContext context)
    {
        if (context.started)
        {
            Debug.Log("Attack button pressed (started)");
            buttonPressStartTime = Time.time;
            isChargeComplete = false; // 溜め開始時にフラグをリセット

            // もし既にコルーチンが動いていたら停止する（連打対策）
            if (chargeCheckCoroutine != null)
            {
                StopCoroutine(chargeCheckCoroutine);
            }
            // 溜め時間監視コルーチンを開始
            chargeCheckCoroutine = StartCoroutine(ChargeTimerCoroutine());
        }
        else if (context.canceled)
        {
            Debug.Log("Attack button released (canceled)");
            // ボタンが離されたら、溜め時間監視コルーチンを停止
            if (chargeCheckCoroutine != null)
            {
                StopCoroutine(chargeCheckCoroutine);
                chargeCheckCoroutine = null; // 保持しているコルーチン参照をクリア
            }

            float pressDuration = Time.time - buttonPressStartTime;
            Debug.Log($"Press duration: {pressDuration} seconds");

            // 溜め完了フラグが立っていれば（＝しきい値を超えていれば）溜め攻撃
            if (isChargeComplete) // または pressDuration > specialAttackThreshold でも判定可能
            {
                PerformSpecialAttack();
            }
            else
            {
                PerformNormalAttack();
            }

            // 攻撃実行後にフラグをリセット
            isChargeComplete = false;
        }
    }

    // 溜め時間を監視するコルーチン
    private IEnumerator ChargeTimerCoroutine()
    {
        // しきい値の時間だけ待機
        yield return new WaitForSeconds(specialAttackThreshold);

        // しきい値に到達したら（かつボタンがまだ押されている場合）
        // isChargeComplete フラグを立て、溜め完了の合図を送る
        // ※ context.ReadValue&#x3C;float>() > 0 などでボタンが押され続けているか確認する方がより厳密
        Debug.Log("Charge Complete threshold reached!");
        isChargeComplete = true;

        // ここで溜め完了エフェクト（光る、SE鳴らすなど）をトリガーする
        TriggerChargeCompleteEffect();

        chargeCheckCoroutine = null; // コルーチン終了
    }

    private void PerformNormalAttack()
    {
        Debug.Log("Perform Normal Attack!");
        // 通常攻撃ロジック
    }

    private void PerformSpecialAttack()
    {
        Debug.Log("Perform Special Attack!");
        // 溜め攻撃ロジック
    }

    private void TriggerChargeCompleteEffect()
    {
         Debug.Log("Play Charge Complete Effect!");
        // 溜め完了時の演出処理（エフェクト表示、SE再生など）
    }
}
</code></pre>
<p>このコードでは、ボタンが押されたら <code>ChargeTimerCoroutine</code> を開始し、<code>specialAttackThreshold</code> 秒後に <code>isChargeComplete</code> フラグを <code>true</code> にして、溜め完了演出メソッド <code>TriggerChargeCompleteEffect()</code> を呼び出します。ボタンが離された (<code>canceled</code>) 時点で、このフラグが <code>true</code> になっているかどうかで溜め攻撃か通常攻撃かを判断します。</p>
<p>このようにコルーチンを組み合わせることで、Input Systemのイベント駆動のメリットを活かしつつ、溜め攻撃における中間的なタイミングでの処理も実現できます。一見すると <code>Update</code> で実装するより複雑に感じるかもしれませんが、入力の種類が増えたり、他のアクションとの連携が必要になったりした場合、Input SystemのAction Mapやイベントによる責務分離がコード全体の整理に役立ちます。</p>
<hr>
<p>:::ad</p>
<h2>まとめ：Pressイベント方式のメリット・デメリットと使い分け</h2>
<p>Unityの<strong>Input System</strong>における<strong>Pressイベント (<code>started</code> / <code>canceled</code>)</strong> を利用することで、イベント駆動に基づいた溜め攻撃の実装が可能になることを見てきました。</p>
<p><strong>メリット：</strong></p>
<ul>
<li><code>Update</code>関数を使わずに済み、コードがイベント単位で整理されやすい。</li>
<li>押下開始・終了のタイミングを正確に捉えられる。</li>
<li>短押し/長押しの判定や、それに応じた処理の分岐が比較的容易。</li>
<li>Input Actionsアセットによる入力マッピング管理が直感的で、キーコンフィグなどへの拡張性が高い。</li>
</ul>
<p><strong>デメリット（考慮点）：</strong></p>
<ul>
<li>「押され続けている間の特定のタイミング」での処理（溜め完了演出など）には、コルーチンなどの補完的な仕組みが必要になる。</li>
<li>単純な溜め時間計測だけなら、<code>Update</code>関数で実装する方がシンプルに感じる場合もある。</li>
</ul>
<p><strong>どちらを選ぶべきか？</strong></p>
<p>最終的な実装方法は、プロジェクトの規模や要件、開発チームの好みによって異なります。</p>
<ul>
<li><strong>小規模なプロジェクトやプロトタイプ</strong>で、溜め攻撃以外の入力が少ない場合は、<code>Update</code>関数によるシンプルな実装でも十分かもしれません。</li>
<li><strong>中規模以上のプロジェクト</strong>で、多様な入力（ゲームパッド対応、キーコンフィグなど）や、他のアクションとの連携、将来的な拡張性・保守性を重視する場合は、<strong>Input SystemのPressイベント + コルーチン等による補完</strong>という組み合わせが有力な選択肢となるでしょう。Hold Interactionも選択肢ですが、本記事で解説したような柔軟性を求める場合はPressイベント方式が有利な場面があります。</li>
</ul>
<p>Input Systemは学習コストが少しありますが、慣れれば強力な武器になります。ぜひ、ご自身のプロジェクトに合った方法で溜め攻撃の実装に挑戦してみてください。</p>]]></content:encoded><media:content url="https://uhiyama-lab.com/static/01f9ea2434798b9f69941dfc524c1870/20240405-001-InputSystem%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%96%B9%E6%B3%95.png" medium="image"/></item></channel></rss>