ADR-0004: Canvas APIの採用¶
ゲーム描画技術としてHTML5 Canvas APIを採用する
日付: 2025-07-31
ステータス¶
2025-07-31 提案されました 2025-07-31 受け入れられました
コンテキスト¶
ぷよぷよゲーム開発において、リアルタイムなゲーム描画を実現するための技術選択が必要でした:
ゲーム描画要件¶
- リアルタイム描画: 60FPSでの滑らかな描画
- 複雑な図形: ぷよの円形、連鎖エフェクト、UI要素
- 動的更新: ゲーム状態に応じた即座な画面更新
- ブラウザ対応: 主要ブラウザでの安定動作
技術的制約¶
- パフォーマンス: フレームレート維持
- 保守性: 開発・デバッグの容易性
- ブラウザ互換性: 幅広い環境での動作
- 学習コスト: チームの習得容易性
比較対象¶
DOM + CSS¶
- 利点: 宣言的な記述、CSSアニメーション活用
- 欠点: 大量要素での性能劣化、細かい制御の困難
SVG¶
- 利点: ベクターグラフィックス、拡大縮小対応
- 欠点: 複雑なアニメーションでの性能問題
WebGL¶
- 利点: GPU活用による高性能
- 欠点: 学習コストの高さ、複雑性
Canvas API¶
- 利点: 細かい描画制御、良好なパフォーマンス
- 欠点: 低レベルAPI、イベント処理の複雑性
決定¶
HTML5 Canvas APIをメイン描画技術として採用する
採用理由¶
- 適切なパフォーマンス
- 60FPSゲームに十分な性能
- ブラウザ最適化による安定性
-
メモリ使用量の効率性
-
細かい描画制御
- ピクセルレベルの制御
- カスタム図形の自由度
-
アニメーション実装の柔軟性
-
学習コストの適正性
- 2D描画APIの理解しやすさ
- 豊富なドキュメントと事例
-
デバッグツールの充実
-
ブラウザ互換性
- 主要ブラウザでの安定サポート
- プログレッシブエンハンスメント対応
-
モバイルブラウザでの動作
-
エコシステム
- TypeScriptとの良好な統合
- 豊富なライブラリとツール
- アクティブなコミュニティ
実装方針¶
// 基本的な描画アーキテクチャ
export class GameRenderer {
private canvas: HTMLCanvasElement
private context: CanvasRenderingContext2D
render(game: Game): void {
this.clearCanvas()
this.renderField(game.getField())
this.renderCurrentPuyo(game.getCurrentPuyo())
this.renderUI(game.getScore(), game.getState())
}
private clearCanvas(): void {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
}
}
描画戦略¶
- レンダリングパイプライン
- 画面クリア → フィールド描画 → UI描画
- フレームベースの更新
-
部分更新の最適化
-
座標系設計
- ゲーム座標 ↔ 画面座標の変換
- レスポンシブ対応
-
高DPI対応
-
パフォーマンス最適化
- 必要部分のみ再描画
- オフスクリーンCanvasの活用
- 描画コールの最小化
影響¶
ポジティブな影響¶
- ゲーム体験の向上
- 滑らかなアニメーション
- 即座のレスポンス
-
視覚的な魅力
-
開発効率
- 直感的な描画API
- デバッグの容易性
-
段階的な実装
-
保守性
- 明確な描画責務分離
- テスタブルな描画ロジック
-
モジュール化された構造
-
拡張性
- エフェクト追加の容易性
- UI要素の柔軟な配置
- 将来機能への対応
ネガティブな影響¶
- 複雑性の増加
- 低レベルAPI操作
- 座標計算の複雑性
-
イベント処理の実装
-
パフォーマンス制約
- CPU描画による限界
- 大量オブジェクト時の性能劣化
-
バッテリー消費の増加
-
アクセシビリティ
- スクリーンリーダー対応の困難
- キーボードナビゲーション
- 代替テキストの実装
コンプライアンス¶
この決定の遵守を確認する方法:
技術的検証¶
-
描画性能測定
# フレームレート測定 # ブラウザDevToolsのPerformanceタブ # 60FPS維持の確認
-
ブラウザ互換性テスト
- Chrome, Firefox, Safari, Edge
- デスクトップ・モバイル環境
-
Canvas APIサポート状況
-
パフォーマンスベンチマーク
- 描画時間測定: 16ms以内(60FPS)
- メモリ使用量監視
- CPU使用率の確認
実装品質検証¶
- コード構造
- 描画ロジックの分離
- レンダラークラスの責務明確化
-
座標変換の一元化
-
描画品質
- アンチエイリアス設定
- 高DPI対応
-
レスポンシブデザイン
-
デバッグ支援
- 描画情報の可視化
- パフォーマンス計測
- エラーハンドリング
運用検証¶
- ユーザビリティ
- 操作レスポンス性
- 視覚的フィードバック
-
ゲーム体験の品質
-
アクセシビリティ対応
- 代替操作手段の提供
- 色覚対応
- コントラスト確保
備考¶
著者¶
プロジェクトチーム
関連決定¶
- ADR-0001: TypeScriptの採用(型安全な描画処理)
- ADR-0003: DDDアーキテクチャの採用(描画層の分離)
- ADR-0005: Vercelデプロイの採用(ブラウザ実行環境)
参考資料¶
実装統計¶
描画パフォーマンス¶
指標 | 目標値 | 実績値 |
---|---|---|
フレームレート | 60FPS | 58-60FPS |
描画時間 | <16ms | 8-12ms |
メモリ使用量 | <50MB | 25-35MB |
描画要素数¶
- フィールドセル: 78個(6×13)
- 現在のぷよ: 2個
- UI要素: 5個(スコア、状態等)
- エフェクト: 可変(連鎖時)
将来の検討事項¶
短期改善¶
- 部分更新の最適化
- エフェクトシステムの強化
- レスポンシブ対応の改善
長期検討¶
- WebGL移行の評価
- WebAssembly活用
- オフスクリーン描画の導入
実装例¶
基本描画パターン¶
export class PuyoRenderer {
static renderPuyo(
context: CanvasRenderingContext2D,
x: number,
y: number,
color: PuyoColor,
cellSize: number
): void {
const centerX = x * cellSize + cellSize / 2
const centerY = y * cellSize + cellSize / 2
const radius = cellSize * 0.4
// 影の描画
context.fillStyle = 'rgba(0, 0, 0, 0.2)'
context.beginPath()
context.arc(centerX + 2, centerY + 2, radius, 0, Math.PI * 2)
context.fill()
// メインの描画
context.fillStyle = this.getColorString(color)
context.beginPath()
context.arc(centerX, centerY, radius, 0, Math.PI * 2)
context.fill()
// ハイライト
context.fillStyle = 'rgba(255, 255, 255, 0.3)'
context.beginPath()
context.arc(centerX - radius * 0.3, centerY - radius * 0.3, radius * 0.3, 0, Math.PI * 2)
context.fill()
}
}
最適化された描画ループ¶
export class GameRenderer {
private animationFrameId: number | null = null
startRenderLoop(game: Game): void {
const renderFrame = () => {
this.render(game)
this.animationFrameId = requestAnimationFrame(renderFrame)
}
renderFrame()
}
stopRenderLoop(): void {
if (this.animationFrameId) {
cancelAnimationFrame(this.animationFrameId)
this.animationFrameId = null
}
}
}