パフォーマンス
パフォーマンスバジェット
初回ロード目標: 2MB以下
Vibe Jam 2026 の要件「ロード画面や重いダウンロードは極力なし」に準拠するため、 初回ロードの合計サイズを 2MB以下(gzip圧縮後)に抑える。
| カテゴリ | バジェット | 備考 |
|---|---|---|
| JavaScript(ゲームコア) | 80KB | TypeScript → minify → gzip |
| JavaScript(Three.js) | 150KB | 必要モジュールのみインポート |
| JavaScript(ポストプロセス) | 30KB | Bloom, Glitch 等 |
| テクスチャ・画像 | 500KB | WebP形式、遅延ロード |
| 3Dモデル | 200KB | 低ポリ、glTF形式 |
| 音声 | 300KB | OGG/WebM、初回は最小限のみ |
| HTML + CSS | 20KB | 最小構成 |
| 合計 | ~1.3MB | 目標2MBに対して余裕あり |
Core Web Vitals 目標
| 指標 | 目標値 | 計測方法 |
|---|---|---|
| LCP (Largest Contentful Paint) | < 2.5秒 | ゲームキャンバスの初回描画 |
| FID (First Input Delay) | < 100ms | 初回クリック/タップ応答 |
| CLS (Cumulative Layout Shift) | < 0.1 | レイアウトシフトなし(Canvas固定) |
| FPS | 60fps(PC)/ 30fps(モバイル) | requestAnimationFrame |
アセット最適化戦略
画像フォーマット
| フォーマット | 用途 | 圧縮率 | 対応状況 |
|---|---|---|---|
| WebP | テクスチャ、UI画像 | PNGの70~90%削減 | Chrome/Safari/Firefox 対応 |
| PNG | フォールバック、透過必須 | ベースライン | 全ブラウザ対応 |
| SVG | アイコン、UIパーツ | 最小 | 全ブラウザ対応 |
音声フォーマット
| フォーマット | 用途 | ビットレート | 対応状況 |
|---|---|---|---|
| OGG Vorbis | BGM、SE | 96~128kbps | Chrome/Firefox |
| WebM (Opus) | BGM(高品質) | 64~96kbps | Chrome/Firefox |
| AAC/M4A | Safari フォールバック | 96~128kbps | Safari/iOS |
typescript
// 音声フォーマットのフォールバック実装例
function loadAudio(basePath: string): string {
const audio = document.createElement('audio')
if (audio.canPlayType('audio/ogg; codecs="vorbis"')) {
return `${basePath}.ogg`
} else if (audio.canPlayType('audio/mp4; codecs="mp4a.40.2"')) {
return `${basePath}.m4a`
}
return `${basePath}.mp3` // 最終フォールバック
}遅延ロード戦略
| ロード段階 | タイミング | 内容 | サイズ目安 |
|---|---|---|---|
| 即時 | ページロード | ゲームコア + ロビーUI | ~510KB |
| 遅延 | シーン遷移時 | ダンジョンアセット、BGM | ~500KB |
| バックグラウンド | ロビー滞在中 | 次ステージの先読み | ~300KB |
Three.js バンドル最適化
インポート最適化
Three.js は ES Modules 対応しており、Vite の Tree-shaking で未使用コードを除去できる。
| インポート方式 | バンドルサイズ(gzip後) | 推奨 |
|---|---|---|
import * as THREE from 'three' | ~250KB | NG |
| 個別インポート | ~150KB | OK |
| 個別 + 不要機能除外 | ~100KB | BEST |
必須モジュール一覧
typescript
// レンダリング
import { Scene, PerspectiveCamera, WebGLRenderer } from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// ジオメトリ・マテリアル
import {
BoxGeometry, PlaneGeometry, SphereGeometry,
MeshStandardMaterial, MeshBasicMaterial, ShaderMaterial
} from 'three'
// ライティング
import { AmbientLight, PointLight, SpotLight } from 'three'
// ポストプロセス
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass'
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass'
// ローダー
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { TextureLoader } from 'three'
// パーティクル
import { Points, PointsMaterial, BufferGeometry, Float32BufferAttribute } from 'three'モバイル対応
パフォーマンス段階制御
| 設定 | 解像度 | パーティクル数 | シャドウ | ポストプロセス | 目標FPS |
|---|---|---|---|---|---|
| HIGH | 100% (1920x1080) | 1000 | ON | 全て | 60fps |
| MEDIUM | 75% (1440x810) | 300 | OFF | Bloom のみ | 30fps |
| LOW | 50% (960x540) | 100 | OFF | OFF | 30fps |
LOD (Level of Detail)
| 距離 | ポリゴン数 | テクスチャ解像度 | 用途 |
|---|---|---|---|
| 近距離 (< 5m) | 100% | 512x512 | プレイヤー、近くの敵 |
| 中距離 (5~15m) | 50% | 256x256 | 遠くの敵、オブジェクト |
| 遠距離 (> 15m) | 25% | 128x128 | 背景オブジェクト |
ブラウザ互換性マトリクス
対応ブラウザ
| ブラウザ | バージョン | WebGL2 | Web Audio | localStorage | 対応レベル |
|---|---|---|---|---|---|
| Chrome | 90+ | OK | OK | OK | フルサポート |
| Firefox | 90+ | OK | OK | OK | フルサポート |
| Safari | 15+ | OK | OK | OK | フルサポート |
| Edge | 90+ | OK | OK | OK | フルサポート |
| Chrome Mobile | 90+ | OK | OK | OK | MEDIUM設定推奨 |
| Safari iOS | 15+ | OK | 要タップ | OK | MEDIUM設定推奨 |
| Samsung Internet | 15+ | OK | OK | OK | MEDIUM設定推奨 |
既知の制約
| ブラウザ | 制約 | 対策 |
|---|---|---|
| Safari iOS | Web Audio はユーザー操作後のみ再生可 | タップで AudioContext を resume |
| Safari iOS | ストレージ容量が制限的(7日間未使用で削除) | プレイ毎に localStorage を touch |
| Firefox | SharedArrayBuffer 制限 | 使用しない設計 |
| 全モバイル | タッチ操作 | バーチャルジョイスティック実装 |
localStorage データ戦略
容量管理
| ブラウザ | localStorage 上限 | Rift Survivors 使用量 | 余裕 |
|---|---|---|---|
| Chrome | 5MB | ~4KB | 99.9% |
| Firefox | 5MB | ~4KB | 99.9% |
| Safari | 5MB | ~4KB | 99.9% |
| Mobile | 2.5~5MB | ~4KB | 99.8% |
データクリーンアップ
typescript
// localStorage のバージョン管理
const STORAGE_VERSION = 'v1'
const STORAGE_PREFIX = 'rift_'
function initStorage(): void {
const version = localStorage.getItem(`${STORAGE_PREFIX}version`)
if (version !== STORAGE_VERSION) {
// バージョン不一致時はデータをマイグレーション or リセット
migrateData(version, STORAGE_VERSION)
localStorage.setItem(`${STORAGE_PREFIX}version`, STORAGE_VERSION)
}
}
function getStorageUsage(): number {
let total = 0
for (const key in localStorage) {
if (key.startsWith(STORAGE_PREFIX)) {
total += localStorage[key].length * 2 // UTF-16
}
}
return total // bytes
}
// 最大使用量: 100KB(安全マージン)
const MAX_USAGE = 100 * 1024データ構造
json
{
"rift_version": "v1",
"rift_character": {
"name": "Player",
"level": 15,
"exp": 4500,
"stats": { "hp": 200, "atk": 45, "def": 30, "spd": 12 }
},
"rift_equipment": {
"weapon": { "id": "sword_003", "rarity": "rare", "level": 8 },
"armor": { "id": "armor_001", "rarity": "common", "level": 5 },
"inventory": [...]
},
"rift_progress": {
"completedMissions": ["mission_01", "mission_02"],
"unlockedAreas": ["lobby", "dungeon_1"]
},
"rift_settings": {
"volume": { "bgm": 0.7, "se": 1.0 },
"quality": "high",
"language": "ja"
}
}パフォーマンス監視
開発時の計測
| ツール | 用途 |
|---|---|
| Chrome DevTools Performance | FPS、メモリ使用量の計測 |
| Lighthouse | Core Web Vitals の計測 |
vite-plugin-bundle-analyzer | バンドルサイズの可視化 |
Three.js renderer.info | ドローコール、トライアングル数の確認 |
ランタイム計測(組み込み)
typescript
// FPSカウンター(開発モードのみ)
class PerformanceMonitor {
private frames = 0
private lastTime = performance.now()
update(): void {
this.frames++
const now = performance.now()
if (now - this.lastTime >= 1000) {
const fps = this.frames
this.frames = 0
this.lastTime = now
if (fps < 24) {
console.warn(`Low FPS: ${fps} - consider reducing quality`)
this.suggestQualityReduction()
}
}
}
private suggestQualityReduction(): void {
// 自動品質調整: FPSが低下したら設定を下げる
}
}