UnityとFirebaseでアバターシステムを実装

目標
Unityでユーザーが以下を自由に編集できるシステムを実装
- 体型の変更(身長・肩幅など)
現状の成果物
ボーンスケーリングにより体型変更が可能(セグメントスケール補正は未実装)
UIはUIツールキット

サーバ設計
Firebaseで認証、API、DBを実装
functions/src
)
プロジェクト構造 (functions/src/
├── application/ # アプリケーションロジック、ユースケース、サービス
│ ├── errors/ # カスタムエラータイプ
│ ├── services/ # ユースケースを調整するアプリケーションサービス
│ ├── validators/ # リクエストバリデーター
│ └── utils/ # ユーティリティ関数 (型ガードなど)
├── config/ # 設定値、定数
├── domain/ # コアドメインロジックとモデル
│ └── avatarSettings.ts # アバター設定のドメインエンティティ/モデル (Height, SkinColor など)
├── infrastructure/ # 外部関心事 (DB、フレームワーク)
│ ├── database/ # データアクセスロジック (Firestore など)
│ └── types/ # インフラストラクチャ固有の型 (Firestore データマッピングなど)
└── presentation/ # API エンドポイント、リクエスト処理、DTO
├── controllers/ # HTTP をアプリケーションサービスにマッピングするリクエストハンドラ
├── middleware/ # Express ミドルウェア (認証、検証など)
└── types/ # プレゼンテーション層固有の型 (API レスポンス、Express 拡張)
レイヤー
-
ドメイン層
- コアビジネスロジックとエンティティ (例:
AvatarSettings
クラス -Height
,SkinColor
等を定義) - 他のレイヤーへの依存関係は無い
- コアビジネスロジックとエンティティ (例:
-
アプリケーション層
- ドメインオブジェクトとインフラストラクチャサービスを調整してユースケースを編成
- リクエストバリデーターもここに配置
-
インフラストラクチャ層
- データベース、外部 API など、外部の関心事についてアプリケーション層によって定義されたインターフェースを実装ドメイン層とアプリケーション層に依存
-
プレゼンテーション層
- 外部とのやり取り (HTTP リクエスト) を処理
- コントローラー、ミドルウェア、API 固有の型 (DTO、レスポンス形式)、フレームワーク固有の拡張機能が含まれる
- アプリケーション層に依存
-
設定層
- アプリケーション全体で使用する定数と設定を含む
- 必要に応じて他のレイヤーからアクセス可能

API エンドポイント
GET /api/v1/status
認証済みユーザー向けのAPIステータス確認エンドポイント
主にクライアントがAPIサーバーとの疎通確認や認証状態を確認するために使用
認証: Authorization: Bearer <token>
ヘッダーに有効な Firebase Auth ID トークンが必要
レスポンス:
-
200 OK (成功): (
index.ts
の実装に基づく){ "action": "status", "message": "認証OK", "timestamp": "2024-08-15T12:00:00.000Z", // ISO 8601 形式のタイムスタンプ "userId": "user-firebase-uid" }
/api/v1/avatar-settings
認証されたユーザーのアバター設定を管理するエンドポイント群
認証: すべてのメソッドで Authorization: Bearer <token>
ヘッダーに有効な Firebase Auth ID トークンが必要
GET /api/v1/avatar-settings
認証されたユーザーの現在のアバター設定を取得
レスポンス:
-
200 OK (成功): (
avatarSettingsController.ts
およびdomain/avatarSettings.ts
のtoJSON
に基づく){ "action": "read", "id": "user-firebase-uid", // ユーザーID (ドメインモデルの id プロパティ) "data": { "id": "user-firebase-uid", "Height": 1.8, "ShoulderWidth": 1.1, "BodyWidth": 1.0, "HeadSize": 1.2, "SkinColorR": 0.8, "SkinColorG": 0.6, "SkinColorB": 0.4, "SkinColorA": 1.0, "HairColorR": 0.1, "HairColorG": 0.1, "HairColorB": 0.1, "HairColorA": 1.0, "createdAt": "2024-08-15T10:00:00.000Z", // ISO 8601 形式 "updatedAt": "2024-08-15T11:00:00.000Z" // ISO 8601 形式 } }
注意:
data
オブジェクトは、AvatarSettings
ドメインモデルのtoJSON()
メソッドによってシリアライズされたもの。タイムスタンプは ISO 8601 文字列として表現 -
401 Unauthorized: 認証トークンが無効な場合など
-
404 Not Found: 設定が見つからない場合
-
500 Internal Server Error: 予期しないサーバーエラーの場合
POST /api/v1/avatar-settings
認証されたユーザーのアバター設定を新規に作成
リクエストボディ:
{
"Height": 1.8,
"ShoulderWidth": 1.1,
"BodyWidth": 1.0,
"HeadSize": 1.2,
"SkinColorR": 0.8, "SkinColorG": 0.6, "SkinColorB": 0.4, "SkinColorA": 1.0,
"HairColorR": 0.1, "HairColorG": 0.1, "HairColorB": 0.1, "HairColorA": 1.0
}
- 必須フィールド:
Height
,ShoulderWidth
,BodyWidth
,HeadSize
,SkinColorR/G/B/A
,HairColorR/G/B/A
。 - バリデーションルール:
-
Height
: 0.5 - 2.0 -
ShoulderWidth
,BodyWidth
: 0.5 - 1.5 -
HeadSize
: 0.5 - 1.5 - 各カラー成分 (
*ColorR/G/B/A
): 0.0 - 1.0 - すべての値は数値
-
レスポンス:
-
201 Created (成功):
{ "action": "create", "id": "user-firebase-uid", "data": { "id": "user-firebase-uid", "Height": 1.8, "ShoulderWidth": 1.1, "BodyWidth": 1.0, "HeadSize": 1.2, "SkinColorR": 0.8, "SkinColorG": 0.6, "SkinColorB": 0.4, "SkinColorA": 1.0, "HairColorR": 0.1, "HairColorG": 0.1, "HairColorB": 0.1, "HairColorA": 1.0, "createdAt": "2024-08-15T11:30:00.000Z", // 作成時のタイムスタンプ (ISO 8601) "updatedAt": "2024-08-15T11:30:00.000Z" // 作成時のタイムスタンプ (ISO 8601) } }
-
400 Bad Request (バリデーションエラー):
{ "error": "リクエストのバリデーションに失敗しました", "code": "VALIDATION_ERROR", "timestamp": "...", "validationErrors": [ { "type": "field", "msg": "Height は 0.5 から 2 の範囲内である必要があります。", "path": "Height", "location": "body", "value": 3.0 }, // ... 他の検証エラー (例: 型エラー、範囲外、必須フィールド欠落) ] }
-
401 Unauthorized: 認証トークンが無効な場合など。
-
500 Internal Server Error: 予期しないサーバーエラーの場合。
PUT /api/v1/avatar-settings
認証されたユーザーの既存のアバター設定を更新
リクエストボディに含まれるフィールドのみが更新対象(部分更新)
リクエストボディ:
{
"Height": 1.9, // 更新したいフィールドを含める
"SkinColorR": 0.9
// 他のフィールドは省略可能
}
- 更新したいフィールドとその値を含める
- バリデーションルールは POST と同様にリクエストに含まれるフィールドに対して適用
レスポンス:
-
200 OK (成功):
{ "action": "update", "id": "user-firebase-uid", "data": { "id": "user-firebase-uid", "Height": 1.9, // 更新された値 "ShoulderWidth": 1.1, // 既存の値 "BodyWidth": 1.0, // 既存の値 "HeadSize": 1.2, // 既存の値 "SkinColorR": 0.9, // 更新された値 "SkinColorG": 0.6, // 既存の値 "SkinColorB": 0.4, // 既存の値 "SkinColorA": 1.0, // 既存の値 "HairColorR": 0.1, // 既存の値 "HairColorG": 0.1, // 既存の値 "HairColorB": 0.1, // 既存の値 "HairColorA": 1.0, // 既存の値 "createdAt": "2024-08-15T10:00:00.000Z", // 既存の値 (ISO 8601) "updatedAt": "2024-08-15T11:45:00.000Z" // 更新時のタイムスタンプ (ISO 8601) } }
-
400 Bad Request (バリデーションエラー): リクエストボディのバリデーションに失敗した場合。(エラー形式は POST と同様)
-
401 Unauthorized: 認証トークンが無効な場合など
-
404 Not Found: 更新対象の設定が見つからない場合
-
500 Internal Server Error: 予期しないサーバーエラーの場合
DELETE /api/v1/avatar-settings
認証されたユーザーのアバター設定を削除
レスポンス:
-
200 OK (成功): (
avatarSettingsController.ts
の実装に基づく){ "action": "delete", "id": "user-firebase-uid", "message": "アバター設定が正常に削除されました" }
-
401 Unauthorized: 認証トークンが無効な場合など
-
404 Not Found: 削除対象の設定が見つからない場合
-
500 Internal Server Error: 予期しないサーバーエラーの場合