【VRChat】アバター最適化でVery Poor脱出!Mantis & MeshDeleterでポリゴン削減してPoorにする方法

VRChatで使用しているアバターのパフォーマンスランクが「Very Poor」になってしまうことがあります。Very Poorランクは、ワールドによっては表示が制限されたり、他ユーザーへの負荷となる可能性も指摘されており、可能な範囲での最適化が推奨されています。

今回は、Unityのツール「Mantis LOD Editor」と「MeshDeleterWithTexture」を用いて、アバターのポリゴン数を削減し、パフォーマンスランクを「Very Poor」から「Poor」へ改善することができたため、その手順と方法を記録としてまとめます。

【最適化結果:ビフォーアフター】

最適化前: 188,784ポリゴン / パフォーマンスランク: Very Poor

最適化前のアバター(Very Poorランク)

最適化後: 69,995ポリゴン / パフォーマンスランク: Poor

最適化後のアバター(Poorランク)

上記のように、見た目への影響を最小限に抑えつつ、ポリゴン数を63%削減することに成功しました!

具体的には、以下の方法でポリゴン数を効果的に削減しました。これらのテクニックは、アバター改変や自作モデル制作においても重要になると思われます。

  • 隠れたパーツの削除: 服の下で見えない体の一部(肘など)のメッシュを削除。
  • Mantis LOD Editorによる削減: 各パーツ(服、髪、小物など)のポリゴン数を、見た目が崩れない範囲で削減。
  • MeshDeleterWithTextureによる部分削除: 服の不要な装飾や、さらに隠れた部分のメッシュをテクスチャ指定で削除。
  • (応用)MeshDeleterによるパーツ分離: 複合パーツ(例:インナーとアウターが一体化した服)を分離し、それぞれ個別に最適化。

この記事では、これらの手順を順番に解説していきます。


この記事の内容

  1. 最適化に必要なツールと準備
  2. 知っておくと便利な専門用語(非破壊的, NDMF)
  3. Mantis LOD Editorを使ったポリゴン削減の基本
  4. Mantisだけでは難しいケースと課題
  5. MeshDeleterWithTextureを使った部分的なメッシュ削除
  6. 応用テクニック:MeshDeleterで服パーツを分離する
  7. まとめ:Very Poor脱出!最適化の成果とポイント
  8. 参考にさせていただいた情報
  9. 追記:ハイエンドモデル最適化の限界と注意点 (2025年4月)

最適化に必要なツールと準備

今回のVRChatアバター最適化(ポリゴン削減)で使用した主なツールは以下の通りです。

  • Mantis LOD Editor – Professional Edition (Unity Asset Store / 有料: 約$55)
    • 高品質なポリゴン削減(リダクション)機能を持つ定番のUnityアセット。アバターの各パーツのポリゴン数を効率的に減らせます。購入後、UnityのPackageManager > My Assetsからインポートします。
  • 【非破壊でポリゴン削減】Mantis LOD EditorのNDMF化ツール (Booth: ひつぶさん作 / 無料 ※Mantis LOD Editor本体が必要)
    • 上記のMantis LOD Editorを、VRChatアバター改変で広く使われているフレームワーク「NDMF(なでもふ)」上で、より安全かつ簡単に(非破壊的に)利用できるようにするツール。Boothからダウンロードし、unitypackageをインポートします。実際にアバターに適用するのはこちらです。
  • MeshDeleterWithTexture beta (Booth: がとーしょこらさん作 / 無料)
    • テクスチャ画像上で範囲を指定することで、対応するメッシュ部分を削除(実際には新しいメッシュを生成)できる非常に便利なUnityエディタ拡張。Mantisでは難しい、服の細かい装飾の削除や、隠れた部分のメッシュ除去に役立ちます。Boothからダウンロードし、unitypackageをインポートします。
  • anatawa12’s VRC Avatar Tools (旧: gists pack) (VCC経由で導入可能)
    • アバターのパフォーマンス統計情報を詳細に表示する機能などが含まれるツール群。特に「Actual Performance」タブは、最適化作業中の現在のポリゴン数やパフォーマンスランクをリアルタイムで確認するために必須です。VCC (VRChat Creator Companion) の「Manage Project」から簡単に追加できます。

【重要】Actual Performanceタブでの確認方法:

anatawa12’s VRC Avatar Toolsを導入すると、VRChat SDK Control Panelに「Avatars」タブが追加され、その中に「Actual Performance」という項目が表示されます。MantisやMeshDeleterで最適化を行った後、Unityを再生モード(Play Mode)にすると、このタブの情報が更新され、最新のポリゴン数やパフォーマンスランクを確認できます。目標は、ここの「Polygons」の数値を70,000以下にすることです(Poorランクの上限)。

VCCでanatawa12's VRC Avatar Toolsを追加する画面

Actual Performanceタブでポリゴン数とランクを確認

(↑この例では188,784ポリゴンでVery Poor。目標は70,000ポリゴン以下!)

知っておくと便利な専門用語(非破壊的, NDMF)

アバター最適化を進める上で、いくつか知っておくと理解が深まる用語があります。

  • 非破壊的 (Non-Destructive)
    元のデータを直接書き換えずに行う編集方法のこと。今回使うNDMF版MantisやMeshDeleterは、元のメッシュデータを保持したまま処理を行うため、「非破壊的」です。つまり、設定を間違えたり、結果が気に入らなかったりした場合でも、ツール(コンポーネント)を削除したり設定を戻したりするだけで、簡単に元の状態に戻すことができます。初心者でも安心して試せる大きなメリットです。
  • NDMF (Nade Nadenadeshiko Mod Fwk / なでもふ)
    VRChatアバター改変のためのフレームワーク(仕組み)の一つ。アバターの色々な設定や改変を「コンポーネント」として管理し、それらをビルド時に自動で適用してくれます。非破壊的な改変と相性が良く、多くの便利ツールがNDMFに対応しています。今回使う「Mantis LOD EditorのNDMF化ツール」もその一つです。

Mantis LOD Editorを使ったポリゴン削減の基本

VRChat SDKのValidation画面でポリゴン超過パーツを特定

まずは、アバターの中でどのパーツがポリゴン数を増やしている主犯なのかを特定しましょう。

1. UnityでVRChat SDK Control Panelを開き、「Builder」タブを選択します。

2. アバターを選択した状態でビルドを実行しようとすると、パフォーマンスに関する警告(Validation Results)が表示されます。「Polygons」に関する警告メッセージの横にある「Select」ボタンをクリックすると、ポリゴン数が特に多いメッシュ(衣装パーツなど)がHierarchyウィンドウでハイライトされます。

3. ハイライトされたオブジェクト(衣装パーツなど)に、「NDMF Mantis LOD Editor」コンポーネントを追加します。(Inspectorウィンドウで「Add Component」し、”Mantis”で検索すると見つかります)

4. 追加されたコンポーネントの「Quality」スライダーを調整します。この数値を下げるほどポリゴン数が削減されますが、下げすぎるとメッシュの形が崩れてしまいます。

NDMF Mantis LOD EditorコンポーネントのQualityスライダー

【ポイント】Shaded Wireframe表示を活用しよう

Qualityスライダーを調整する際は、Unityのシーンビュー表示を「Shaded Wireframe」に切り替えると、ポリゴンの削減具合やメッシュの崩れ具合が視覚的に分かりやすくなります。「Shaded」表示と適宜切り替えながら、見た目を損なわないギリギリのラインを探っていきましょう。

Shaded Wireframe表示でポリゴンを確認

この「パーツ特定 → コンポーネント追加 → Quality調整」を、ポリゴン数が多いパーツに対して繰り返していくのが基本的な流れです。

【重要】使わないパーツは完全に削除しよう

アバター改変をしていると、「元の服や髪型も、いつか使うかもしれないから一応残しておこう」と、非表示にしただけでHierarchyに残してしまうことがあります。しかし、非表示にしただけのメッシュもポリゴン数としてカウントされてしまう場合があります!

今回の私のアバターも、元のマヌカちゃんのエプロンパーツなどを非表示で残していましたが、これらをHierarchyから完全に削除したところ、約16,000ポリゴンも削減できました。

使わないパーツは思い切って削除しましょう。もし後で必要になった場合は、元のアバターや衣装のunitypackageを再インポートすれば簡単に戻せます。

Hierarchyから不要なパーツを削除する

(参考: UnityだけでVRChatアバターのVeryPoorを脱出する方法|こはだ 様

【注意】顔と素体のポリゴン削減は慎重に!

顔周りの細かいメッシュ構造

Mantisでのポリゴン削減は効果的ですが、アバターの「顔」と「素体(Body)」のメッシュは、原則として削減しない方が無難です。

特に顔周りは、豊かな表情を作るために非常に多くの細かいポリゴン(メッシュ)で構成されています。口周りのポリゴンはリップシンク(口パク)の様々な形に対応するため、目周りのポリゴンはまばたきや感情表現のために必要です。

これらの部分をMantisで安易に削減してしまうと、喋るたびに口が破綻したり、表情が崩壊したりする可能性が非常に高くなります。同様に、素体の関節部分なども、削減するとポーズを取ったときに不自然な見た目になりやすいです。

ポリゴン削減は、主に服、髪、アクセサリーなどのパーツで行い、顔と素体はできるだけ元の状態を保つようにしましょう。

【判断】時にはデザイン的な妥協も必要

尻尾パーツの削減前後の比較

どうしても目標のポリゴン数(Poorランクの70,000ポリゴン)に収まらない場合、デザインの一部を諦めるという判断が必要になることもあります。

今回のアバターでは、尻尾パーツのポリゴン数をMantisである程度削減しましたが、それでも目標達成が難しかったため、最終的に尻尾パーツ自体を削除する決断をしました。もちろん残念ですが、パフォーマンスランク改善のためには、こうしたトレードオフが必要になる場合もあります。

Mantisだけでは難しいケースと課題

Mantis LOD Editorは非常に優秀なツールですが、最適化を進める中で、Mantisだけでは対応が難しい、あるいは効率が悪いと感じる場面が出てきました。

  • 複合パーツの削減限界: インナーとアウターが一体化している服など、一つのオブジェクトに複数の部位が含まれている場合、Mantisで削減すると、先にポリゴン数が少ない部分(例: インナー)が破綻してしまい、ポリゴン数が多い部分(例: アウター)を十分に削減できないことがある。パーツを分離して個別に削減したい。
  • 隠れたメッシュの残り: 服の下に隠れている素体の一部(例: お腹周り)を非表示にしてもメッシュが残り、ポリゴン数に含まれてしまう。この隠れた部分だけを削除したい。
  • 部分的な装飾の削除: 服の特定の部分(ポケット、ベルト、フリルなど)だけを削除してポリゴン数を稼ぎたいが、Mantisでは全体的な削減しかできない。

これらの課題を解決するために役立つのが、「MeshDeleterWithTexture beta」です。

最適化前の体メッシュ(服で隠れている部分も表示)

(↑例えば、服の下に隠れているこのお腹部分のメッシュを削除したい)

服を非表示にした状態。腹部のメッシュが残っている

MeshDeleterWithTexture betaを使った部分的なメッシュ削除

MeshDeleterWithTexture beta」は、その名の通り、テクスチャ画像上で指定した範囲に対応するメッシュ部分を削除(正確には、削除した新しいメッシュを生成)してくれる画期的なツールです。

使い方は非常に直感的です。

1. がとーしょこらさんのBoothからunitypackageをダウンロードし、プロジェクトにインポートします。

2. Unityメニューバーに「GotoTools」という項目が追加されるので、「MeshDeleter with Texture」を選択して専用ウィンドウを開きます。

MeshDeleter with Textureのメニュー

3. ウィンドウ上部の「Renderer」という欄に、メッシュを削除したいオブジェクト(例: 体のメッシュを持つオブジェクト)をHierarchyウィンドウからドラッグ&ドロップします。

4. オブジェクトに設定されているテクスチャ画像がウィンドウ内に表示されます。

5. 画面右側の「Draw Type」から「PEN」などを選択し、テクスチャ上で削除したい部分(例: 服で隠れるお腹の部分)を黒く塗りつぶします。塗りつぶした部分は、シーンビュー上のモデルにもリアルタイムで反映され、削除される範囲を確認できます。

MeshDeleterでテクスチャのお腹部分を黒く塗りつぶす

6. 削除範囲を確認したら、「DeleteMesh」ボタンをクリックします。これで、指定した部分が削除された新しいメッシュが生成され、オブジェクトに自動で適用されます。

MeshDeleter実行後、お腹部分のメッシュが削除された状態

【ポイント】MeshDeleterも非破壊的!

このツールも「非破壊的」で、元のメッシュデータはプロジェクト内に残っています。もし削除範囲を間違えたり、元に戻したくなったりした場合は、オブジェクトのMesh Renderer(またはSkinned Mesh Renderer)コンポーネントで、Meshの指定を元のファイルに戻すだけでOKです。安心して試せますね。

生成された新しいメッシュが適用されているInspector画面

この方法で、服の下に隠れていた腹部メッシュを削除したところ、約1,000ポリゴン削減できました。さらに、残った部分にMantis LOD Editorを適用して、より効率的にポリゴンを削減することも可能です。

応用テクニック:MeshDeleterで服パーツを分離する

最適化対象のテックウェア(インナーとアウターが一体)

MeshDeleterの「テクスチャ範囲指定でメッシュを削除する」機能は、応用することで一体化している服パーツを分離するのにも使えます。

今回使用したテックウェア(約60,000ポリゴン)は、コート部分(アウター)と上半身のインナーが同じ一つのオブジェクト(メッシュ)として作られていました。このままだと、Mantisで削減しようとしても、ポリゴン数の少ないインナー部分が先に破綻してしまい、ポリゴン数の多いコート部分を十分に削減できませんでした。

そこで、MeshDeleterを使って「インナーだけのメッシュ」と「アウターだけのメッシュ」を擬似的に作り出すことにしました。

【パーツ分離手順】

1. 元のテックウェアオブジェクトをHierarchy上で複製(Ctrl+D または Cmd+D)し、それぞれ「インナー用」「アウター用」など分かりやすい名前に変更します。

2. 「インナー用」オブジェクトを選択し、MeshDeleterウィンドウを開きます。

3. テクスチャ上で、アウター(コート)に対応する部分をすべて黒く塗りつぶし、「DeleteMesh」を実行します。これにより、インナー部分だけが残ったメッシュが生成されます。

インナー用オブジェクトでコート部分を塗りつぶす

インナー部分だけのメッシュが完成

4. 同様に、「アウター用」オブジェクトを選択し、MeshDeleterウィンドウで今度はインナーに対応する部分をすべて黒く塗りつぶし、「DeleteMesh」を実行します。これにより、アウター(コート)部分だけが残ったメッシュが生成されます。

アウター用オブジェクトでインナー部分を塗りつぶす

これで、もともと一体だった服を「インナー」と「アウター」の2つのメッシュに分離できました。Blenderなどの外部モデリングソフトを使わずに、Unity上だけでパーツを分離できるのは非常に便利です。

分離後は、それぞれのパーツ(インナー、アウター)に個別にNDMF Mantis LOD Editorを適用し、より効果的にポリゴン数を削減していくことが可能になります。

まとめ:Very Poor脱出!最適化の成果とポイント

以上の方法を用いた結果、最初に18万ポリゴンあったVeryPoorランクのアバターを、69,995ポリゴンのPoorランクに改善することができました。

最終的なポリゴン数(69995)とPoorランクの表示

最終的に目標の70,000ポリゴン以下に収めるために、服の装飾削除やコートのポリゴン削減など、デザイン面での調整も行いました。見た目を維持しながらポリゴン数を削減する作業は、試行錯誤が必要ですが、非常に良い練習になったと感じています。

最適化後のアバターの外観(見た目の劣化は最小限)

今回使用した「Mantis LOD Editor (NDMF版)」と「MeshDeleterWithTexture beta」は、どちらも非破壊的なツールであるため、初心者でも比較的安心して試すことができます。「いつでも元に戻せる」という点は、最適化作業を進める上での大きな心理的な支えになります。

Mantisの強力なポリゴン削減能力と、MeshDeleterの柔軟な部分編集・応用力を組み合わせることで、Very Poorランクのアバターも効果的に最適化できる可能性が高まります。

VRChatアバターのポリゴン数でお悩みの方は、ぜひこの記事で紹介したツールと手順を参考に、最適化に挑戦してみてください。(なお、パフォーマンスランクにはポリゴン数以外にもマテリアル数などが影響するため、総合的な最適化を目指す場合はそちらも考慮が必要です)

参考にさせていただいた情報

今回のアバター最適化(ポリゴン削減)にあたり、以下の記事や動画を大変参考にさせていただきました。素晴らしい情報をありがとうございます。


追記:ハイエンドモデル最適化の限界と注意点 (2025年4月)

この記事ではVery PoorランクのアバターをPoorランクに改善する手順を紹介しましたが、すべてのケースで同様の最適化が最善とは限らない点について追記します。特に、元のポリゴン数が非常に多い、いわゆる「ハイエンドモデル」の最適化には注意が必要です。

これらのモデルは、ディテールに富んだ装飾や複雑な衣装、多くのギミックなどが魅力ですが、その分ポリゴン数が膨大(10万ポリゴンをはるかに超えることも珍しくありません)です。このようなモデルに対してMantis LOD Editorなどでポリゴン削減を行うと、わずかな削減率でも見た目のディテールが失われ、モデル本来の魅力が大きく損なわれてしまう可能性があります。

Poorランクの規定(7万ポリゴン)を少しオーバーしている程度のアバター(例えば7万~10万ポリゴン程度)や、もともとシンプルなデザインのデフォルメモデルなどであれば、この記事で紹介した方法は有効でしょう。しかし、規定の倍以上、例えば15万、20万ポリゴンを超えるようなハイエンドモデルを、見た目を維持したままPoorランクまで無理に削減しようとすることは、多くの場合困難であり、あまり推奨できません。

では、どうすれば良いかというと、無理に一つのモデルを極端に最適化するのではなく、用途に応じた使い分けを検討するのが現実的です。

  • 通常利用: メインのアバターは、最適化せず元のクオリティのまま使用する。
  • 軽量化が必要な場面: 大人数が集まるイベントや、パフォーマンスが重視される特定のワールドに参加する際には、別途軽量化されたバージョン(Quest対応版などが用意されている場合もあります)を用意するか、あるいは最初から軽量な別のアバターを使用する

【筆者の体験談と反省】

実を言うと、今回Poorランク(69,995ポリゴン)まで最適化したマヌカ改変モデルですが、しばらく使ってみて、少し考えが変わりました。確かに軽量化には成功したのですが、その過程で服の細かい装飾などを一部削除してしまったことに対し、「そこまでして最適化する必要があったかな?」という気持ちが後から出てきました。

また、私自身のVRChatのプレイスタイルとして、パフォーマンスが非常にシビアなワールドに頻繁に参加するわけではなかったため、「このモデルに関しては、Very Poorのままでも大きな支障はなかったかもしれない」というのが正直なところです。

アバターの最適化は、VRChatを快適に楽しむ上で重要な要素の一つですが、それが常に必須というわけではありません。ご自身のプレイスタイルや参加するコミュニティ、そして「どの程度の見た目の変化なら許容できるか」をよく考慮した上で、最適化を行うかどうか、どのレベルまで行うかを判断することが大切だと思います。

Google Analytics Data APIで静的ブログサイトに『人気記事一覧』を実装する方法【Next.js / Gatsby.js】

JAMstack構成(例: Next.jsGatsby.js + ヘッドレスCMS)は、フロントエンドとバックエンドを分離することで、使用する技術選択の自由度が高く、パフォーマンスやセキュリティ面でのメリットも大きいモダンなWebサイト構築のアプローチです。

しかし、その一方で、従来のWordPressサイトなどではプラグインで簡単に実現できた機能、例えば「人気記事一覧(ランキング)」などを実装しようとすると、少し工夫が必要になります。

WordPressで人気記事一覧が容易なのは、記事データとページ閲覧数(PV数)が同じデータベース内に記録されており、動的にデータを取得・集計してランキング表示できるためです。しかし、JAMstack構成の多くは、ビルド時に静的なHTMLファイルを生成するため、リアルタイムで変動する閲覧数を基にした動的なランキング表示は得意ではありません。また、フロントエンドとバックエンドが分離しているため、アクセス数を記録・参照する共通の仕組みが標準では備わっていません。

そこで活用したいのが、多くのWebサイトで導入されているアクセス解析ツール「Google Analytics」です。Google Analyticsに蓄積されたページごとの閲覧数データを、そのAPI(Google Analytics Data API (GA4 Data API))経由で取得し、ビルドプロセスに組み込むことで、静的サイトでありながら「人気記事一覧」を表示することが可能になります。

この記事では、Google Analytics Data API (GA4) を利用して人気記事データを取得し、Next.jsGatsby.jsといった静的サイトジェネレーター環境で「人気記事一覧」を実装するための具体的な手順と考え方を紹介します。


この記事の内容

  1. 静的サイト(ヘッドレスCMS)で人気記事一覧を実装する難しさ
  2. Google Analytics Data API (GA4) の有効化と基本準備
  3. サービスアカウントの利用とセキュリティ上の注意点
  4. Node.jsによる人気記事データ取得スクリプトの実装例
  5. ビルド時にJSONデータを活用し、静的サイトに組み込む方法
  6. まとめ:静的サイトでもAPI連携で人気記事一覧は実現できる!

静的サイト(ヘッドレスCMS)で人気記事一覧を実装する難しさ

JAMstack構成とGoogle Analytics連携のイメージ図

前述の通り、従来の動的なCMS(WordPressなど)では、データベースへのアクセスを通じてリアルタイムにPV数を集計し、人気記事ランキングを表示することが一般的でした。

しかし、JAMstack構成の静的サイトでは、サイトのコンテンツはビルド時に生成され、サーバー側での動的なデータ処理は基本的には行いません。そのため、サイトが表示されるたびにPV数をリアルタイムで集計してランキングを変動させる、といった実装は困難です。

そこで、外部サービスであるGoogle Analyticsのデータを活用するアプローチが有効になります。基本的な流れは以下のようになります。

  1. ビルド前にデータを取得: スクリプトを実行し、Google Analytics Data APIを通じて、指定期間のページ別閲覧数を取得します。(例: 直近30日間のPV数トップ10記事)
  2. データを整形・保存: 取得したランキングデータを扱いやすい形式(例: JSONファイル)に整形して、プロジェクト内に保存します。
  3. ビルド時にデータを参照: Next.jsやGatsby.jsなどの静的サイトジェネレーターがサイトをビルドする際に、保存されたJSONファイルを読み込みます。
  4. 静的ページに埋め込み: 読み込んだランキングデータ(記事タイトル、URL、PV数など)を使って、「人気記事一覧」のコンポーネントやリストを生成し、静的なHTMLページ内に埋め込みます。

この方法であれば、サイトをデプロイ(ビルド)するたびに最新のランキング情報が反映され、かつサイト自体は高速な静的ファイルとして提供できるため、パフォーマンスを損なうこともありません。


Google Analytics Data API (GA4) の有効化と基本準備

まず、Google Analyticsのデータをプログラムから取得するために、APIの有効化と必要な情報を準備します。

Google Cloud PlatformでGoogle Analytics Data APIを有効化する画面

  1. Google Cloud Platform (GCP) でAPIを有効化:
    • Google Cloud Console (https://console.cloud.google.com/) にアクセスし、プロジェクトを選択または新規作成します。
    • 「APIとサービス」>「ライブラリ」で「Google Analytics Data API」を検索し、有効にします。
  2. サービスアカウントを作成し、キーをダウンロード:
    • GCPの「APIとサービス」>「認証情報」で、「認証情報を作成」>「サービスアカウント」を選択し、新しいサービスアカウントを作成します。(名前は任意、ロールは不要な場合が多い)
    • 作成したサービスアカウントを選択し、「キー」タブ>「鍵を追加」>「新しい鍵を作成」で「JSON」形式のキーを作成・ダウンロードします。このJSONファイルは後でスクリプトから使用します。大切に保管してください。
  3. サービスアカウントにGA4プロパティへのアクセス権を付与:
    • Google Analytics (https://analytics.google.com/) にアクセスし、対象のGA4プロパティの「管理」(歯車アイコン)を開きます。
    • 「プロパティ設定」>「プロパティのアクセス管理」で、「+」ボタン >「ユーザーを追加」を選択します。
    • 先ほど作成したサービスアカウントのメールアドレス(GCPの認証情報画面で確認できます)を入力し、「役割」として少なくとも「閲覧者」の権限を付与して追加します。
  4. GA4プロパティIDを控える:
    • Google Analyticsの「管理」>「プロパティ設定」で、「プロパティ ID」(数字のみのID)を確認し、控えておきます。これもスクリプトで使用します。

    Google Analytics 4のプロパティID確認画面

これらの準備(特にGCPやGA4の操作)については、Googleの公式ドキュメントや他の解説記事も参考に、ご自身の環境に合わせて進めてください。


サービスアカウントの利用とセキュリティ上の注意点

Google Cloud Platformでのサービスアカウントキー管理画面

Google Analytics Data APIのように、Googleのサービスにプログラムからアクセスする際には、通常「サービスアカウント」という特殊なアカウントを使用します。これは、個人のGoogleアカウントではなく、アプリケーションやスクリプトが自身を認証するためのものです。

前述のステップで作成しダウンロードしたサービスアカウントキー(JSONファイル)には、そのサービスアカウントとしてAPIにアクセスするための秘密の情報が含まれています。そのため、このキーファイルの取り扱いには細心の注意が必要です。

  • 絶対に公開しない: JSONキーファイルは、Gitリポジトリ(特にPublicリポジトリ)に絶対にコミットしないでください。誤って公開してしまうと、第三者に不正利用される危険性があります。.gitignoreファイルにキーファイル名を追加し、Gitの管理対象から除外しましょう。
  • 安全な場所に保管: ローカル開発環境ではプロジェクトルートなどに置くこともありますが、本番環境(デプロイ先)では、環境変数やシークレット管理機能を使って安全にキー情報を渡すのが一般的です。(後述のスクリプト例参照)
  • 権限は最小限に: GA4プロパティでサービスアカウントに付与する権限は、データ取得に必要な最低限の「閲覧者」権限にしておきましょう。

Node.jsによる人気記事データ取得スクリプトの実装例

ここでは、準備したサービスアカウントキーとGA4プロパティIDを使って、実際に人気記事データを取得し、JSONファイルとして保存するNode.jsスクリプトの例を示します。

【準備】

  1. プロジェクトのルートディレクトリにscriptsフォルダを作成し、その中に以下のスクリプトをfetch-popular-posts.jsなどの名前で保存します。
  2. プロジェクトのルートディレクトリに、GCPからダウンロードしたサービスアカウントキーのJSONファイルをservice-account.json(または任意の名前)として配置します。
  3. プロジェクトのルートディレクトリに.envファイルを作成し、以下の形式でGA4プロパティIDを記述します(?????????の部分は実際のIDに置き換えてください)。
    GA4_PROPERTY_ID=?????????
  4. 【重要】service-account.json.envファイルは機密情報を含むため、必ず.gitignoreファイルに追加して、Gitリポジトリに含まれないようにします。
    # .gitignore ファイルの例
    service-account.json
    .env
        
  5. 必要なnpmパッケージをインストールします。ターミナルで以下のコマンドを実行してください。
    npm install @google-analytics/data dotenv

    (または yarn add @google-analytics/data dotenv

【スクリプト例: scripts/fetch-popular-posts.js

// .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();

スクリプトのポイント:

  • 認証情報: 環境変数 `GA_CREDENTIALS_JSON` があればそれを優先的に使用し、なければローカルの `service-account.json` を読み込みます。これにより、ローカル開発と本番環境(Cloudflare Pagesなど)で認証情報を安全に扱うことができます。(本番環境では、環境変数にJSONキーの内容全体を設定するのが一般的です)
  • APIリクエスト: `runReport` メソッドでGA4にリクエストを送ります。
    • dateRanges: データ取得期間を指定します(例: “30daysAgo”~”today”)。
    • dimensions: 取得したい情報の種類(ページのパス、タイトルなど)を指定します。
    • metrics: 集計したい指標(表示回数 `screenPageViews` など)を指定します。
    • orderBys: 並び順を指定します(例: 表示回数の多い順)。
    • limit: 取得する最大件数を指定します。
    • dimensionFilter: 【重要】 取得するページを絞り込むためのフィルターです。この例では「ページのパス (`pagePath`) が正規表現 ^/blog/ に一致するもの」だけを取得しています。これにより、ブログ記事以外のページ(トップページ `/` など)がランキングに含まれるのを防ぎます。この `value` は、あなたのブログ記事のURL構造に合わせて必ず変更してください。
  • 結果の整形と保存: APIからの応答データを扱いやすいJSON形式(パス、タイトル、閲覧数のオブジェクト配列)に変換し、data/popular-posts.json ファイルに書き出します。

このスクリプトをターミナルで node scripts/fetch-popular-posts.js と実行すると、`data` フォルダ(なければ作成される)に人気記事のデータがJSONファイルとして保存されます。


ビルド時にJSONデータを活用し、静的サイトに組み込む方法

人気記事データを含むJSONファイルが用意できたら、あとは静的サイトジェネレーター(Next.jsやGatsby.js)のビルドプロセスでこのJSONファイルを読み込み、ページやコンポーネントにデータを渡して表示するだけです。

しかし、ここで重要なのは「サイトをビルド(デプロイ)するたびに、このJSONファイルが最新の情報に更新されるようにする」ことです。そうしないと、ランキングが古いままになってしまいます。

これを実現するには、通常、プロジェクトの package.json ファイル内の scripts セクションを編集し、ビルドコマンドの実行前にデータ取得スクリプトが実行されるように設定します。

package.json の設定例 (Gatsby.jsの場合)】

{
  "scripts": {
    // データ取得スクリプトを実行するコマンドを定義
    "fetch-data": "node scripts/fetch-popular-posts.js",
    // 開発サーバー起動時にもデータを取得する場合 (任意)
    "develop": "npm run fetch-data && gatsby develop",
    // 本番ビルド時に必ずデータ取得を実行するように設定
    "build": "npm run fetch-data && gatsby build",
    // startコマンドも同様に(開発用サーバー起動など)
    "start": "npm run develop",
    // 他にも test や serve など...
  }
}

この例では、

  1. fetch-data という名前でデータ取得スクリプトを実行するコマンドを定義しています。
  2. build コマンド(本番用ビルド)の実行時に、まず npm run fetch-data を実行し、その後に gatsby build が実行されるように設定しています。(&& はコマンドを順番に実行する指定子)
  3. 同様に、開発サーバー起動時 (develop, start) にもデータを取得するようにしています(開発中も最新データで確認したい場合)。Next.jsの場合は next devnext build の前に npm run fetch-data && を追加します。

このように設定しておくことで、npm run build コマンドを実行(またはVercelやCloudflare Pagesなどのホスティングサービスで自動ビルドが実行される際)に、

  1. まず fetch-popular-posts.js が実行され、最新の人気記事データが data/popular-posts.json に保存されます。
  2. 次に、Gatsby (またはNext.js) のビルドプロセスが開始され、その中で data/popular-posts.json ファイルを読み込み、人気記事一覧を含む静的HTMLページが生成されます。

この「ビルド時データフェッチ」のアプローチにより、サイト訪問者は常に(ビルド時点での)最新の人気記事一覧を高速な静的ページとして閲覧でき、サーバーへの負荷やAPIの実行時呼び出しを気にする必要がなくなります。これはパフォーマンスとセキュリティの観点からもJAMstack構成の大きなメリットです。

あとは、各フレームワークの方法に従って、ビルド時にJSONデータを読み込み、Reactコンポーネントなどに渡して表示する部分を実装してください。(例: Gatsbyではgatsby-node.jsでJSONを読み込みGraphQLデータレイヤーに追加、Next.jsではgetStaticProps内でfs.readFileを使ってJSONを読み込むなど)


まとめ:静的サイトでもAPI連携で人気記事一覧は実現できる!

JAMstackヘッドレスCMSを採用した静的サイト環境では、WordPressのような従来の動的CMSとは異なり、「人気記事一覧」をリアルタイムで表示することは困難です。

しかし、この記事で解説したように、Google Analytics Data API (GA4) を活用し、サイトのビルドプロセス前にアクセスデータを取得・整形してJSONファイルなどの形で保存し、それをビルド時に静的ページに埋め込むというアプローチを取ることで、静的サイトのメリット(高速表示、高セキュリティ、スケーラビリティ)を維持しつつ、最新の人気記事ランキングを表示することが可能になります。

この方法は、特定のCMSやフロントエンドフレームワークに強く依存しないため、Next.jsGatsby.jsだけでなく、様々なJAMstack構成に応用できます。サービスアカウントキーの安全な管理や、APIリクエストのフィルター設定など、いくつか注意点はありますが、一度仕組みを構築すれば、自動でランキングが更新される便利な機能を実現できます。

ぜひ、ご自身の静的ブログサイトやWebサイトの構成に合わせて、この手法を参考に「人気記事一覧」の実装に挑戦してみてください。

【VRChat】服の裏地が透ける/透明になる問題を修正!MeshFlipperの使い方

VRChatでマントやロングコートなど、丈の長い衣装を着ているアバターを使ったとき、「あれ? フレンド視点だと服の裏地が透明になって透けて見える…」と指摘された経験はありませんか?

自分視点では特に問題ないのに、他人から見るとコートの裏側などが描画されず、体が透けて見えてしまう…。これはVRChatでアバターを使っていると比較的よく遭遇する現象です。

この「衣装の裏地が透ける問題」、実は簡単なUnityツールで解決できる場合があります。この記事では、その原因と、解決策となるツール「MeshFlipper」の使い方について、備忘録も兼ねて解説します。


この記事の内容

  1. なぜVRChatで服の裏地が描画されない(透ける)のか?
  2. 解決策!「MeshFlipper」の導入と使い方(実装手順)
  3. (少し技術的な話)MeshFlipperが裏地問題を解決する仕組み
  4. まとめ:服の裏地が透けるならMeshFlipperを試そう!

なぜVRChatで服の裏地が描画されない(透ける)のか?

VRChat内でコートの裏地が透けて見えている状態の例

この現象の主な原因は、多くの3Dモデルで採用されている「片面レンダリング(または片面描画)」という仕組みにあります。

簡単に言うと、3Dモデルを構成するポリゴン(板のようなもの)は、基本的に「表面」だけを描画し、「裏面」は描画しないことで、コンピューターの描画負荷を軽くしています。体にぴったりフィットした服などは、外側(表面)だけが見えれば十分なので、この仕組みで問題ありません。

しかし、コート、マント、スカートのようにヒラヒラしたり、裾が長かったりする衣装の場合、キャラクターが動いたり、特定の角度から見たりすると、衣装の「裏面」が見えてしまうことがあります。

片面レンダリングでは、この「裏面」は描画対象外なので、結果として何も描画されず、背景や体が透けて見えてしまうのです。これが、他人視点や三人称視点のカメラで見たときに「裏地が透明になっている」「バグで描画されていない」ように見える正体です。

この問題を解決するには、単純に「衣装の裏面もちゃんと描画されるようにする」必要があります。そのための処理を簡単に行ってくれるのが、次にご紹介する「MeshFlipper」というツールです。


解決策!「MeshFlipper」の導入と使い方(実装手順)

衣装の裏地が透ける問題の解決方法を探していたところ、以下の記事で「MeshFlipper」というUnityエディタ拡張ツールの存在を知りました。(情報感謝です!)

Android(Quest)対応方法マニュアルAdvanced!! – もえぎつばさ 様

MeshFlipperは、Unity上で3Dモデルのメッシュデータ(形状データ)を加工し、裏面も描画されるように両面化してくれる非常に便利なツールです。fum1さんがBoothで無料配布してくれています。

【無料】メッシュの裏面を生成するツール / Mesh Flipper (BOOTH)

導入と基本的な使い方は以下の通り、とても簡単です。

MeshFlipperのウィンドウと設定オプション

  1. MeshFlipperをUnityプロジェクトにインポートする
    まず、上記BoothからMeshFlipperをダウンロードします。ダウンロードしたファイルに含まれるMeshFlipper.csというスクリプトファイルを、UnityプロジェクトのAssetsフォルダ内の好きな場所(例えばEditorフォルダなど)にドラッグ&ドロップしてインポートします。
  2. 裏地が透ける衣装のメッシュを選択する
    UnityのHierarchyウィンドウで、修正したいアバターを選択し、その中から裏地が透けてしまう衣装のパーツ(オブジェクト)を探します。そのオブジェクトにアタッチされているSkinned Mesh RendererまたはMesh Rendererコンポーネントを見つけてください。
  3. MeshFlipperウィンドウを開く
    Unityのメニューバーから fum1 > Mesh Flipper を選択し、MeshFlipperの専用ウィンドウを開きます。
  4. オプションを設定する(重要:TwoSidesを選択)
    MeshFlipperウィンドウが開いたら、対象の衣装パーツ(Skinned Mesh Rendererなどが付いたオブジェクト)を選択した状態で、以下のオプションを設定します。

    • TwoSides: このオプションにチェックを入れるのが基本です。これがメッシュを両面化する機能で、チェックを入れると裏面も描画されるようになります。
    • Flip: こちらはポリゴンの面の向き(法線)を反転させる機能です。通常、裏地が透ける問題の解決にはTwoSidesだけで十分です。

    ほとんどの場合、TwoSidesにチェックを入れるだけで裏地が透ける問題は解決します。

  5. 「Create Mesh」ボタンを押して実行する
    オプションを設定したら、「Create Mesh」ボタンをクリックします。すると、MeshFlipperが選択したメッシュを両面化処理し、新しいメッシュデータを作成して自動的に元のメッシュと差し替えてくれます。(元のメッシュデータもバックアップとして残る場合があります)

処理が完了したら、Unityのシーンビューで衣装の裏側から見てみたり、VRChatにアップロードしてフレンドに確認してもらったりしましょう。裏地がちゃんと描画され、透けなくなっていれば成功です!


(少し技術的な話)MeshFlipperが裏地問題を解決する仕組み

少しだけ技術的な話をすると、MeshFlipperは内部で以下のような処理を行っています。

  • 頂点データの複製と法線の反転: 元のメッシュの頂点データをコピーし、コピーした側のポリゴンの向き(法線)を反転させます。
  • メッシュの結合: 元のメッシュ(表面)と、法線を反転させたコピー(裏面用)を一つに結合します。

これにより、実質的に「表面用のポリゴン」と「裏面用のポリゴン」の両方を持つメッシュデータが作成されます。結果として、どちらの方向から見てもポリゴンが描画されるようになり、裏地が透ける問題が解決するという仕組みです。

要するに、MeshFlipperは「本来は存在しなかった(描画されなかった)服の裏側」を擬似的に作ってくれるツール、と理解しておけば大丈夫です。

この複雑な処理をボタン一つで実行してくれるので、特にコートやスカートなど、裏側が見えやすい衣装を扱う際には非常に重宝します。


まとめ:服の裏地が透けるならMeshFlipperを試そう!

VRChatでアバターの衣装(特にコートやマント、スカートなど)の裏地が透けてしまう透明になってしまうという問題に遭遇したら、まずはUnityエディタ拡張ツール「MeshFlipper」の利用を検討してみてください。

簡単な手順でメッシュを両面化でき、多くの場合、これだけで裏地が正常に描画されるようになります。

MeshFlipperは、開発者のfum1さんがBoothで無料配布してくれています。同じ悩みを持つVRChatユーザーやアバター改変を行う方は、ぜひ導入して試してみることをお勧めします!

【無料】メッシュの裏面を生成するツール / Mesh Flipper (BOOTH)

FontToolsでNoto Sans JPのサブセットを作成して読み込み速度を上げる

Webサイトの表示速度が遅い…その原因の一つに、Webフォントのファイルサイズが大きいことが挙げられます。特に日本語フォントは多くの文字を含むため、データ量が膨大になりがちです。この記事では、人気のある日本語フォント「Noto Sans JP」を例に、必要な文字だけを抽出して軽量化する「サブセット化」を行い、ページ読み込み速度を改善する方法を解説します。

様々なツールがある中で、当初「サブセットフォントメーカー」を試しましたが、私の環境では表示崩れが発生しました。最終的にPythonライブラリ「FontTools」を用いることで、OTF情報(文字詰めなどの見た目を整える情報)を維持したまま、Noto Sans JPフォントをWOFF2形式にサブセット化し、ファイルサイズを50%以上削減、読み込み速度を向上させることに成功しました。その具体的な手順と注意点を備忘録としてまとめます。

この記事の内容

  1. フォントのサブセット化とは?なぜページ速度改善に必要なのか
  2. 【失敗談】サブセットフォントメーカーでOTF情報が欠落し表示が崩れた件
  3. 解決策!FontToolsを使った高精度なサブセット作成手順
  4. まとめ:FontToolsによるサブセット化の効果とページ速度への貢献

1. フォントのサブセット化とは?なぜページ速度改善に必要なのか

フォントのサブセット化とは、簡単に言うと、フォントファイルの中からWebサイトで実際に使用する文字だけを抜き出し、不要な文字データを削除してファイルサイズを小さくする技術のことです。「普段使わない難しい漢字や特殊記号まで全部読み込むのは無駄だよね?使う文字だけに絞って軽くすれば、サイトがもっと速く表示されるはず!」という考え方に基づいています。

このブログでも使用している定番の日本語フォント「Noto Sans JP」は、Google Fontsからも入手できる高品質なフォントですが、非常に多くの文字(ひらがな、カタカナ、常用漢字、人名漢字、記号など)を網羅しています。

Noto Sans Japanese (Google Fonts)

そのため、ダウンロードしてそのままWebフォント(例えばWOFF形式)としてサイトに組み込むと、標準の太さ(Regular)や太字(Bold)だけで、それぞれ約4MBものファイルサイズになってしまいます。これでは、ユーザーがページを開くたびに大きなデータをダウンロードする必要があり、表示速度のボトルネックになりかねません。

サブセット化前のNoto Sans JPフォントファイルサイズ(各約4MB)

(↑サブセット化前のフォントファイル。サイズが大きいことがわかります。)

そこで重要になるのが、この巨大なフォントファイルを軽量化する「サブセット化」なのです。


2. 【失敗談】サブセットフォントメーカーでOTF情報が欠落し表示が崩れた件

フォントのサブセット化について調べると、「サブセットフォントメーカー」というGUIツールがよく紹介されています。私もいくつかの解説サイトを参考に、このツールと「WOFFコンバータ」を使ってNoto Sans JP(TTF形式)からサブセットWOFFフォントを作成してみました。

サブセットフォントメーカーの画面

しかし、生成されたフォントをサイトに適用したところ、予期せぬ表示崩れが発生してしまいました。

サブセットフォントメーカーで変換後の表示崩れ(文字間隔が広がる)

(↑左が元の表示、右がサブセットフォントメーカーで変換後の表示。記事タイトル部分の文字間隔が不自然に広がっています。)

原因を調査したところ、どうやら「サブセットフォントメーカー」がフォント変換を行う過程で、Noto Sans JPに含まれる重要なOTF(OpenType Font)情報の一部、特に文字間隔の調整(カーニング、ペアカーニング)を行うための情報(GPOSテーブルなど)を削除してしまったようです。

OTF情報は、文字の形だけでなく、文字と文字の間のアキを自動調整したり、特定の文字の組み合わせで合字(リガチャ)に置き換えたり(GSUBテーブル)といった、フォントの見た目を美しく整えるための重要なデータです。これが失われたために、文字間隔がデフォルトのままになり、レイアウトが崩れてしまったと考えられます。

軽量化はできても、見た目が損なわれては意味がありません。そこで、OTF情報を維持したまま、高精度なサブセット化ができるツールを探し、Python製のライブラリ「FontTools」にたどり着きました。


3. 解決策!FontToolsを使った高精度なサブセット作成手順

FontToolsは、フォントファイルを操作するための強力なPythonライブラリ群です。オープンソースで開発されており、TTFやOTF形式のフォントに対して、サブセット化はもちろん、情報の編集、形式変換など、様々な高度な処理を行うことができます。コマンドラインから手軽に利用できるのも魅力です。

FontTools (GitHubリポジトリ)

FontToolsを使ってNoto Sans JPのサブセットを作成する手順は以下の通りです。

3-1. FontToolsのインストール

FontToolsはPythonライブラリなので、まずお使いの環境にPythonがインストールされていることを確認してください。Pythonが利用できる環境であれば、ターミナル(コマンドプロンプト)で以下のpipコマンドを実行してFontToolsをインストールします。

pip install fonttools brotli zopfli

※ WOFF2形式の扱いや、より高い圧縮率(--with-zopfliオプション)を実現するために、brotlizopfli も一緒にインストールしておくと良いでしょう。

3-2. サブセット化コマンドの実行

次に、ターミナルでpyftsubsetコマンドを使ってサブセット化を実行します。Noto Sans JPの元フォントファイル(例: NotoSansJP-Regular.ttf)があるディレクトリで、以下のコマンドを実行してください。(ファイル名やパスはご自身の環境に合わせて変更してください。)

# 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

同様に、Bold(太字)など他のウェイトのフォントもサブセット化する場合は、元ファイル名と出力ファイル名を変更してコマンドを実行します。

コマンドオプションの解説:

  • NotoSansJP-Regular.ttf

    サブセット化の元となるフォントファイル(TTFまたはOTF)。

  • --output-file=NotoSansJP-Regular.woff2

    出力するサブセットフォントのファイル名とパスを指定。

  • --flavor=woff2

    出力形式をWOFF2に指定。WOFF2はWebフォント用に最適化された形式で、圧縮率が高くファイルサイズを小さくできます。

  • --layout-features='*'

    【重要】 これがOTF情報を保持するためのオプションです。'*'を指定することで、カーニング(文字詰め)やリガチャ(合字)などのOpenTypeレイアウト機能に関する情報をすべて保持します。これにより、サブセットフォントメーカーで発生したような表示崩れを防ぎます。

  • --unicodes='...'

    【重要】 サブセットに含める文字のUnicode範囲を指定します。ここで指定した範囲外の文字はフォントに含まれなくなり、表示できなくなる(文字化けする)ので慎重に設定します。上記コマンド例の範囲は以下をカバーしています。

    • U+0020-007E: 基本的な半角英数字と記号 (ASCII)
    • U+3000-30FF: 全角スペース、句読点、ひらがな、カタカナなど
    • U+4E00-9FAF: CJK統合漢字(一般的に使われる漢字の多くをカバー)
    • U+FF01-FF60: 全角の英数字や記号の一部
    • U+FF65-FF9F: 半角カタカナ

    ※ サイトで使用する文字に合わせて、この範囲を調整する必要があります。例えば、特定の記号や第二水準漢字なども使う場合は、対応するUnicode範囲を追加します。

  • --with-zopfli

    Googleが開発したZopfli圧縮アルゴリズムを使用し、WOFF2ファイルをさらに高圧縮します。ファイルサイズ削減に効果的です。(zopfliライブラリのインストールが必要)

  • --verbose

    サブセット化の処理中に詳細なログを出力します。エラーの原因調査や、どの情報が含まれたか/除外されたかを確認するのに役立ちます。

3-3. 生成されたWOFF2ファイルをCSSで読み込む

コマンドが正常に完了すると、指定した場所に軽量化されたWOFF2形式のサブセットフォントファイルが生成されます。あとは、このファイルをWebサーバーにアップロードし、CSSの@font-face規則を使って読み込むように設定を変更します。

/* 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;
}

これで、軽量化されたサブセットフォントがWebサイトで利用されるようになります。


4. まとめ:FontToolsによるサブセット化の効果とページ速度への貢献

FontToolsを使ってNoto Sans JPのサブセット化を行った結果、以下のような効果が得られました。

FontToolsでサブセット化した後のフォントファイルサイズ(各約1.5MB)

(↑FontToolsでサブセット化後。ファイルサイズが大幅に削減されています!)

これにより、ブラウザがダウンロードするデータ量が減り、ページの読み込み時間も改善されました。以下のネットワーク分析結果は、変更前と変更後のフォント読み込み時間を示しています。

変更前(サブセット化前):

サブセット化前のフォント読み込み時間

変更後(FontToolsでサブセット化後):

FontToolsでサブセット化後のフォント読み込み時間(改善)

ファイルサイズ削減に伴い、読み込み時間も短縮されていることが確認できます。これは、Core Web Vitalsなどのページ速度指標の改善にも繋がります。

FontToolsはコマンドラインツールですが、一度コマンドを理解すれば、OTF情報を保持したまま高精度なサブセット化が可能です。Webサイトの表示速度を改善したい、特に日本語フォントの重さにお悩みの方は、ぜひFontToolsによるサブセット化を試してみてはいかがでしょうか。含める文字範囲(--unicodes)をさらに絞り込むなど、工夫次第でさらなる軽量化も可能です。

SSDをクローン換装してCドライブの容量を500GBから2TBに拡張した備忘録

4年ほど使っている自作PCで、最近以下のような気になる動作が増えてきました。

  • エクスプローラでフォルダを開いたり、ファイルを表示したりするのに妙に時間がかかる。
  • ファイル名の変更中など、ファイル操作をしていると表示が一瞬リセットされる(入力が確定されてイライラ…)。
  • ごく稀にPC全体がプチフリーズし、Windowsのタスクバーなどが再読み込みされるような挙動をする。

メモリ診断やGPUドライバ更新、不要ファイルの削除などを試しても改善せず、「そろそろPCの寿命かな…」と考え始めていました。しかし、その前にふとPCの心臓部とも言えるCドライブの状態を確認してみると…

容量が限界に近いCドライブの状態(赤色表示)

真っ赤!残りの空き容量は5%未満という危険水域でした。

一般的に、WindowsがインストールされているCドライブは、常に10%~15%程度の空き容量を確保しておくことが推奨されています。空き容量が極端に少ないと、Windowsの一時ファイル作成、ページングファイル(仮想メモリ)の確保、システムアップデートなどに支障をきたし、結果としてシステムの不安定化、パフォーマンス低下、読み書き速度の低下(特にSSDの場合)など、様々な不具合を引き起こす原因となります。

そこで今回、この容量不足を解消すべく、現在のCドライブ(約500GBのM.2 SSD)の内容を丸ごと新しい大容量SSD(2TBの2.5インチSSD)にコピー(クローン)して交換(換装)することに挑戦しました。結果的に容量拡張は成功したものの、途中でWindowsが起動しなくなるトラブルにも遭遇し、かなり苦戦しました…。

この記事では、そのSSDクローン換装の具体的な手順、使用したツール、そして遭遇した起動エラー「0xc000000e」の対処法などを、備忘録として詳しくまとめていきます。同じようにCドライブの容量不足に悩んでいる方や、SSD換装を検討している方の参考になれば幸いです。


SSDクローン換装の手順概要

この記事で解説する手順

  1. 事前準備:必要なもの(SSD、ソフト、USBメモリ等)
  2. 換装手順の詳細:クローンからトラブル解決まで
    1. SSDクローンの作成 (Macrium Reflect Free)
    2. Windowsインストールメディアの作成(トラブル対策用)
    3. SSDの物理的な取り付けとBIOS設定、そして起動エラー対処
    4. パーティションの拡張(容量を使い切る)
  3. 換装完了と結果:容量拡張とPCの安定化

事前準備:必要なもの(SSD、ソフト、USBメモリ等)

今回のSSDクローン換装で使用した主な物品とソフトウェアは以下の通りです。

  • 新しいSSD: シリコンパワー 2TB SSD 3D NAND A58 (2.5インチ SATA)
    • 換装先のSSDです。容量や規格(M.2 NVMe, 2.5インチ SATA等)、メーカーはお好みで。今回はセールで安価だったこちらを選びました。元のM.2から2.5インチへの変更ですが、マザーボードに空きSATAポートがあれば問題ありません。

    シリコンパワー 2TB SSD 3D NAND A58 (Amazon)

  • SSDクローンソフト: Macrium Reflect Free
    • 元のSSDの内容を新しいSSDに丸ごとコピー(クローン)するためのソフトウェア。様々なソフトがありますが、今回は無料で評価も高かったこちらを使用しました。

    Macrium Reflect Free – 窓の杜 /
    [参考記事] Macrium Reflect Freeを使ってSSDをクローンし、大容量のSSDに移行する – combat-travor 様

  • USBメモリ: 16GB以上のもの
    • 換装後にWindowsが起動しなくなった場合の修復作業で使用します。Windowsのインストールメディアを作成するために必要です。【重要】この作業は事前にやっておくことを強く推奨します。

    Windows 10メディア作成ツール – Microsoft (Windows 11の場合はこちら)

  • パーティション管理ソフト: Paragon Hard Disk Manager 15 (または同等の機能を持つソフト)
    • 換装後、新しいSSDの全容量をCドライブとして使えるように、パーティションサイズを調整(拡張)するために使用しました。Windows標準の「ディスクの管理」でも可能ですが、より柔軟な操作ができる専用ソフトが便利です。今回は手持ちのソフトを使いましたが、無料のパーティション管理ソフトもあります。
  • SATA-USB変換ケーブル/ケース (任意):
    • 新しいSSDをPC内部に取り付ける前に、USB接続でクローン作業を行う場合に必要です。PC内部に直接接続できる場合は不要。

SSDやソフトウェアは上記以外のものでも構いません。ご自身の環境や予算に合わせて選択してください。ただし、起動トラブルに備えて、Windowsインストールメディアだけは事前に作成しておくことをお勧めします。

BCD (Boot Configuration Data) とは、Windowsの起動に必要な構成情報(どのディスクからOSを起動するかなど)を格納したファイルです。SSD換装後に起動エラーが出る場合、このBCD情報が新しい環境と合わなくなっていることが原因の一つとして考えられます。


換装手順の詳細:クローンからトラブル解決まで

それでは、具体的な換装手順と、私が遭遇したトラブルシューティングについて解説します。

【Step 1】SSDクローンの作成 (Macrium Reflect Free)

まず、現在のCドライブの内容を、新しいSSDに丸ごとコピー(クローン)します。

1. 新しいSSDをPCに接続します。(SATA-USB変換ケーブルを使うか、PC内部の空きポートに接続)

2. クローンソフト(今回はMacrium Reflect Free)を起動します。

3. ソフトの指示に従い、コピー元ドライブ(現在のCドライブ)とコピー先ドライブ(新しいSSD)を選択し、クローン処理を開始します。

Macrium Reflect Freeでのクローン設定画面例

(具体的な操作手順は、前述のcombat-travor様の参考記事が非常に分かりやすいです)

Macrium Reflect Freeでのクローン完了画面

私の環境では、約450GB使用していたCドライブのクローンに約3時間40分かかりました。時間は環境やデータ量によって大きく変動します。

【ポイント】クローン時のパーティションサイズ

クローンソフトの設定で、コピー先のパーティションサイズを指定できる場合があります。基本的には、コピー元と同じサイズでクローンするのが安全です。新しいSSDの残りの容量は、クローン完了時点では「未割り当て」領域として残しておきます。この未割り当て領域は、後ほどStep 4でCドライブと結合します。

💡 クローン時のパーティションサイズは、元のドライブと同じサイズに設定するのが推奨されます。大容量SSDの残りは一旦「未割り当て」のままにします。

クローン完了後、Windowsの「ディスクの管理」(`diskmgmt.msc`)やパーティション管理ソフトで、新しいSSDに元のCドライブと同じ構成のパーティションが作成され、残りが未割り当て領域になっていることを確認しましょう。

パーティション管理ソフトでクローン後のSSDを確認

(↑元のCドライブの内容がコピーされ、残りが未割り当てになっている状態)


【Step 2】Windowsインストールメディアの作成(トラブル対策用)

SSDの換装後、すんなりWindowsが起動すれば良いのですが、環境によっては起動エラーが発生することがあります。私の場合は、以下のブルースクリーンエラーが表示されました。

エラーコード 0xc000000e のブルースクリーン画面

エラーコード「0xc000000e」は、Windowsが起動に必要なデバイス(この場合は新しいSSD)にアクセスできない、または起動構成データ(BCD)に問題がある場合に発生することが多いエラーです。

このエラーを修復するには、Windowsの回復環境からコマンドプロンプトを起動する必要があります。そのために、事前にWindowsインストールメディア(USBメモリ)を作成しておきます。

1. Microsoftの公式サイトから「メディア作成ツール」をダウンロードします。

Windows 10メディア作成ツール / Windows 11メディア作成ツール

2. 16GB以上の空のUSBメモリをPCに接続します。

3. ダウンロードしたメディア作成ツールを実行し、画面の指示に従って「別の PC のインストール メディアを作成する (USB フラッシュ ドライブ、DVD、または ISO ファイル)」を選択し、USBメモリにインストールメディアを作成します。

Windowsメディア作成ツールの画面

このUSBメモリがあれば、万が一Windowsが起動しなくなっても、修復作業を行うことができます。

[参考記事] Windows 10でエラーコード0xc000000eを修正する方法 – MiniTool

[参考記事] 【SSD換装】クローンしたSSDから起動できない時の原因と対処法まとめ – ドクター・エムプレイス


【Step 3】SSDの物理的な取り付けとBIOS設定、そして起動エラー対処

クローン作業とインストールメディア作成が終わったら、いよいよSSDの交換です。

1. PCの電源を完全に切り、電源ケーブルを抜きます。

2. PCケースを開け、古いSSD(元のCドライブ)を取り外し、新しいSSD(クローン済みのもの)を取り付けます。(※静電気対策を忘れずに!)

3. PCケースを閉じ、電源ケーブルを接続し、PCの電源を入れます。

4. 電源を入れた直後(メーカーロゴが表示されている間)に、指定のキー(多くはDELキーかF2キー)を押して、BIOS(UEFI)設定画面に入ります。

5. BIOS設定画面で、「Boot」や「起動」といったメニューを探し、起動順位(Boot Options / Boot Priority)を確認します。ここで、新しく取り付けたSSDが「Windows Boot Manager」として認識され、起動順位の1番目に設定されていることを確認します。(もし認識されていない、または順位が低い場合は設定を変更します)

6. 設定を保存してBIOSを終了し、PCを再起動します。

【トラブル発生】BIOSでは認識されるのに 0xc000000e エラーで起動しない!

私の環境では、ここが最大の難関でした。BIOS上では新しいSSDが起動ドライブとして正しく認識されているにも関わらず、Windowsを起動しようとすると前述のブルースクリーンエラー「0xc000000e」が発生してしまうのです。

最初はクローン失敗を疑い、別のソフト(Paragon)でクローンし直したりしましたが、結果は同じでした。

【解決策】Windowsインストールメディアから起動し、BCDを再構築する

最終的に、Step 2で作成したWindowsインストールメディア(USBメモリ)を使って以下の手順を行うことで解決しました。これは、Windowsの起動構成データ(BCD)を修復・再構築する作業です。

1. PCの電源を切った状態で、Windowsインストールメディア(USBメモリ)を接続します。

2. PCの電源を入れ、すぐにBIOS設定画面に入ります。

3. 起動順位を変更し、USBメモリを最優先(1番目)にして設定を保存・再起動します。

4. USBメモリからWindowsセットアップ画面が起動したら、「コンピューターを修復する」>「トラブルシューティング」>「詳細オプション」>「コマンド プロンプト」を選択します。

5. コマンドプロンプトで、以下のコマンドを順番に実行します。これは、ブートセクタの修復とBCDの再構築を行うためのコマンドです。(詳細な手順は下記の参考記事を参照してください)

  • bootrec /fixmbr
  • bootrec /fixboot (※後述の注意点あり)
  • bootrec /scanos
  • bootrec /rebuildbcd

(場合によっては、diskpartコマンドでEFIパーティションにドライブ文字を割り当てる作業が必要になることもあります)

[参考記事] 【bootrec /fixboot アクセスは拒否されました】コマンドプロンプトからUEFIを修復してWindowsを起動可能にする方法 – ぼくんちのTV 別館

【注意】bootrec /fixboot で「アクセスが拒否されました」エラーが出る場合

bootrec /fixboot でアクセス拒否のエラー

上記のコマンド実行中、bootrec /fixboot で「アクセスが拒否されました (Access is denied.)」というエラーが表示されることがあります。これは、最近のWindows(特にUEFIモードでインストールされている場合)でよく見られる現象のようです。

私の場合は、このエラーを無視して、次の bootrec /scanosbootrec /rebuildbcd を実行したところ、結果的に問題なくWindowsが起動するようになりましたfixbootコマンドは主に古いMBR形式のディスク修復に使われるもので、UEFI環境では必ずしも必要ではない、あるいは別の手順(diskpartを使ったEFIパーティション操作など)が必要な場合があるようです。

もしこのエラーに遭遇し、rebuildbcdまで実行しても起動しない場合は、以下の参考記事にあるような追加の対処法を試す必要があるかもしれません。

[参考記事] 「bootrec /fixboot」でアクセスが拒否されましたと表示される時の対処法! – Windows10 – IT HOOK

BCDの再構築が成功し、コマンドプロンプトを終了してPCを再起動すると、無事に新しいSSDからWindowsが起動するはずです!


【Step 4】パーティションの拡張(容量を使い切る)

無事にWindowsが起動したら、最後の大仕事です。現在、新しいSSDには元のCドライブと同じサイズのパーティションしかなく、残りの大容量スペースは「未割り当て」領域として使えない状態になっています。この未割り当て領域をCドライブと結合し、SSDの全容量を使えるようにします。

パーティション管理ソフトでCドライブと未割り当て領域を表示

この作業は、Windows標準の「ディスクの管理」でも可能ですが、回復パーティションなどが間に挟まっていると拡張できない場合があるため、専用のパーティション管理ソフトを使うのが確実です。

今回は手持ちの「Paragon Hard Disk Manager 15」を使用しました。「パーティションサイズの変更/移動」のような機能を選択し、Cドライブのパーティションを選択して、後方の未割り当て領域を取り込むようにサイズを最大まで広げます。

Paragon Hard Disk Managerでパーティションサイズを拡張する設定

設定を適用すると、通常はPCの再起動が求められ、Windows起動前の画面でパーティションの変更処理が実行されます。処理が完了し、再度Windowsが起動すれば、Cドライブの容量が新しいSSDの最大容量(今回は約2TB)まで拡張されているはずです。

換装完了と結果:容量拡張とPCの安定化

容量が拡張されたCドライブの状態(空き容量十分)

以上の手順を経て、無事にCドライブのSSDを500GBから2TBに換装し、容量を大幅に拡張することができました!

Cドライブの空き容量が十分に確保された(15%以上)ことで、以前発生していたエクスプローラの遅延や表示の再描画、プチフリーズといったPCの不調も嘘のように解消されました。やはり、Cドライブの空き容量不足はシステムの安定性に大きな影響を与えるようです。ドライブが赤色の状態は放置せず、早めに対処(不要ファイルの削除、または今回のような容量拡張)することが重要ですね。

SSDのクローン換装は、手順自体はそれほど複雑ではありませんが、環境によっては起動トラブルが発生する可能性もあります。特にUEFI環境での換装は、BCD周りの問題が起きやすいようです。この記事で紹介したトラブルシューティングが、同じ問題に直面した方の助けになれば幸いです。(※作業は自己責任でお願いします)