{"id":23579669,"url":"https://github.com/zapabob/aivtuber-chat-unity","last_synced_at":"2025-05-16T18:12:37.597Z","repository":{"id":268920133,"uuid":"871484809","full_name":"zapabob/AIVtuber-Chat-Unity","owner":"zapabob","description":"UnityProject","archived":false,"fork":false,"pushed_at":"2025-01-25T11:51:19.000Z","size":58,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-12T06:52:39.523Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zapabob.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-12T05:08:38.000Z","updated_at":"2025-01-25T11:51:23.000Z","dependencies_parsed_at":"2024-12-19T18:25:16.036Z","dependency_job_id":"ce3b25c8-f970-41df-9340-af21e3d6defb","html_url":"https://github.com/zapabob/AIVtuber-Chat-Unity","commit_stats":null,"previous_names":["zapabob/aivtuber-chat-unity"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zapabob%2FAIVtuber-Chat-Unity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zapabob%2FAIVtuber-Chat-Unity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zapabob%2FAIVtuber-Chat-Unity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zapabob%2FAIVtuber-Chat-Unity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zapabob","download_url":"https://codeload.github.com/zapabob/AIVtuber-Chat-Unity/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254582909,"owners_count":22095519,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-26T23:12:00.807Z","updated_at":"2025-05-16T18:12:37.578Z","avatar_url":"https://github.com/zapabob.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AIVTuber Controller\r\n\r\nUnity用のAIVTuberコントローラーシステムです。VRMモデルとLLM（大規模言語モデル）を組み合わせて、インタラクティブなバーチャルYouTuberを作成できます。\r\n\r\n## 主な機能\r\n\r\n- 複数のLLMサービス対応（Dify、ローカルLLM、Maria）\r\n- VOICEVOXを使用した音声合成\r\n- VRMモデルの表情制御\r\n- 自動まばたき機能\r\n- エラーハンドリングとリトライ機能\r\n\r\n## 必要要件\r\n\r\n- Unity 2021.3以降\r\n- UniVRM 10.0以降\r\n- VOICEVOX（音声合成用）\r\n\r\n## セットアップ\r\n\r\n1. VRMモデルを`StreamingAssets`フォルダに配置\r\n2. AIVTuberControllerコンポーネントをシーンに追加\r\n3. 必要な設定（LLMサービス、VOICEVOX）を構成\r\n\r\n## 設定項目\r\n\r\n### LLMサービス設定\r\n- エンドポイントURL\r\n- APIキー\r\n- システムプロンプト\r\n- 各種パラメータ（温度、最大トークン数など）\r\n\r\n### 音声設定\r\n- VOICEVOXエンドポイント\r\n- 話者ID\r\n- 音声パラメータ（速度、ピッチ、イントネーション）\r\n\r\n### VRMモデル設定\r\n- モデルパス\r\n- 表情マッピング\r\n\r\n## エラーハンドリング\r\n\r\nシステムは以下のタイプのエラーを処理します：\r\n- 設定エラー\r\n- AIレスポンスエラー\r\n- VRMモデルエラー\r\n- 音声合成エラー\r\n\r\n## ライセンス\r\n\r\nMITライセンス\r\n\r\n## 注意事項\r\n\r\n- VOICEVOXは別途インストールが必要です\r\n- LLMサービスは別途APIキーの取得が必要な場合があります\r\n- VRMモデルの利用規約を確認してください\r\n\r\n## 使用例\r\n\r\n### 基本的な使用方法\r\n```csharp\r\n// AIVTuberControllerの初期化\r\nvar controller = gameObject.AddComponent\u003cAIVTuberController\u003e();\r\ncontroller.settings = new AIVTuberSettings {\r\n    LLMServiceType = LLMServiceType.LocalLLM,\r\n    SystemPrompt = \"あなたはフレンドリーなバーチャルYouTuberです。\"\r\n};\r\n\r\n// メッセージの処理\r\nawait controller.HandleMessage(\"こんにちは！\");\r\n```\r\n\r\n### カスタム感情表現の追加\r\n```csharp\r\n// 感情表現のマッピングをカスタマイズ\r\nemotionToExpression.Add(\"excited\", ExpressionKey.Happy);\r\nemotionToExpression.Add(\"thoughtful\", ExpressionKey.Neutral);\r\n```\r\n\r\n### 音声パラメータの調整\r\n```csharp\r\n// 音声パラメータのカスタマイズ\r\nconfig.VoiceSpeed = 1.2f;      // 話速\r\nconfig.VoicePitch = 0.2f;      // ピッチ\r\nconfig.VoiceIntonation = 1.1f; // イントネーション\r\n```\r\n\r\n## 詳細な使用方法ガイド\r\n\r\n### 1. プロジェクトのセットアップ\r\n\r\n#### 1.1 Unity環境の準備\r\n1. Unity Hub で新規プロジェクトを作成\r\n   - Unity 2021.3以降を選択\r\n   - 3Dテンプレートを使用\r\n   - プロジェクト名を設定\r\n\r\n2. 必要なパッケージのインストール\r\n   ```\r\n   - UniVRM 10.0（Package Managerから）\r\n   - UniTask（Package Managerから）\r\n   - JSON.NET（Package Managerから）\r\n   ```\r\n\r\n#### 1.2 フォルダ構造の設定\r\n```\r\nAssets/\r\n  ├── StreamingAssets/\r\n  │   ├── Models/          # VRMモデルファイル\r\n  │   └── LLMModels/      # ローカルLLMモデル\r\n  ├── Scripts/\r\n  │   ├── Core/           # コア機能\r\n  │   ├── Services/       # LLMサービス\r\n  │   ├── UI/            # UI関連\r\n  │   └── Utils/         # ユーティリティ\r\n  └── Scenes/\r\n      └── Main.scene     # メインシーン\r\n```\r\n\r\n#### 1.3 外部サービスの準備\r\n1. VOICEVOXのセットアップ\r\n   ```bash\r\n   # VOICEVOXのインストール\r\n   1. https://voicevox.hiroshiba.jp/ からダウンロード\r\n   2. インストーラーを実行\r\n   3. デフォルトポート(50021)で起動\r\n   ```\r\n\r\n2. LLMサービスの準備\r\n   ```bash\r\n   # Difyの場合\r\n   1. https://dify.ai/ でアカウント作成\r\n   2. APIキーを取得\r\n   \r\n   # ローカルLLMの場合\r\n   1. モデルをダウンロード\r\n   2. KoboldCPP/llama.cppを設定\r\n   3. APIサーバーを起動\r\n   ```\r\n\r\n### 2. シーンの設定\r\n\r\n#### 2.1 基本シーン構成\r\n```\r\nScene\r\n├── Main Camera\r\n├── Directional Light\r\n├── AIVTuberController\r\n│   ├── VRMModelLoader\r\n│   ├── ErrorNotification\r\n│   └── ChatUI\r\n└── EventSystem\r\n```\r\n\r\n#### 2.2 コンポーネントの設定手順\r\n1. AIVTuberControllerの設定\r\n   ```csharp\r\n   // インスペクターでの設定\r\n   - VRM Model: VRMモデルのプレハブをアタッチ\r\n   - Settings: AIVTuberSettingsアセットを作成してアタッチ\r\n   - Error Display: ErrorNotificationをアタッチ\r\n   ```\r\n\r\n2. カメラの設定\r\n   ```\r\n   Position: (0, 1.5, -2)\r\n   Rotation: (0, 180, 0)\r\n   Field of View: 60\r\n   ```\r\n\r\n### 3. 運用シナリオ別の設定例\r\n\r\n#### 3.1 ゲーム実況配信の場合\r\n```csharp\r\n// システムプロンプトの設定\r\nsettings.SystemPrompt = @\"あなたはゲーム実況が得意なバーチャルYouTuberです。\r\n以下の点に注意して応答してください：\r\n- ゲームの状況を分かりやすく実況\r\n- プレイヤーの行動に対して適切なリアクション\r\n- 視聴者からの質問に丁寧に回答\r\n- ゲームの攻略情報や裏技の共有\r\n- 盛り上がりポイントでの感情表現を豊かに\r\n\r\n感情表現のガイドライン：\r\n[happy] - 良いプレイや成功時\r\n[excited] - 重要なイベントや驚きの展開\r\n[thoughtful] - 戦略を考える場面\r\n[sad] - 失敗やゲームオーバー時\r\n[angry] - 悔しい場面や激しい戦闘時\";\r\n\r\n// 音声設定\r\nconfig.VoiceSettings = new VoiceSettings {\r\n    BaseSpeed = 1.2f,  // 実況向けにやや早め\r\n    ExcitementSpeedMultiplier = 1.3f,  // 盛り上がり時は更に早く\r\n    PitchRange = new Vector2(-0.2f, 0.4f),  // 感情表現の幅を広く\r\n    VolumeRange = new Vector2(0.8f, 1.2f)   // メリハリのある音量\r\n};\r\n```\r\n\r\n#### 3.2 雑談配信の場合\r\n```csharp\r\n// システムプロンプトの設定\r\nsettings.SystemPrompt = @\"あなたは視聴者と楽しく雑談するバーチャルYouTuberです。\r\n以下の特徴を持って応答してください：\r\n- 親しみやすい口調で会話\r\n- 視聴者のコメントに共感的な反応\r\n- 時事ネタやトレンドについて話題提供\r\n- 適度な冗談や軽い話題も取り入れる\r\n- 個人情報や機密情報には触れない\r\n\r\n感情表現のガイドライン：\r\n[happy] - 楽しい話題や共感時\r\n[surprised] - 意外な情報や驚きの展開\r\n[thoughtful] - 深い話題や考察時\r\n[sad] - 共感や励まし\r\n[relaxed] - 通常の会話時\";\r\n\r\n// 音声設定\r\nconfig.VoiceSettings = new VoiceSettings {\r\n    BaseSpeed = 1.0f,  // 標準的な速度\r\n    EmotionSpeedMultiplier = 1.1f,  // 感情による変化を抑えめに\r\n    PitchRange = new Vector2(-0.1f, 0.2f),  // 自然な抑揚\r\n    VolumeRange = new Vector2(0.9f, 1.1f)   // 安定した音量\r\n};\r\n```\r\n\r\n### 4. 高度な設定とカスタマイズ\r\n\r\n#### 4.1 表情制御の詳細設定\r\n```csharp\r\n// 表情ブレンド設定\r\npublic class ExpressionBlendSettings\r\n{\r\n    public float BlendDuration = 0.3f;    // ブレンド時間\r\n    public float HoldDuration = 2.0f;     // 表情維持時間\r\n    public float ReturnDuration = 0.5f;   // 戻り時間\r\n    \r\n    // 表情の強さの設定\r\n    public Dictionary\u003cstring, float\u003e ExpressionStrength = new Dictionary\u003cstring, float\u003e\r\n    {\r\n        [\"happy\"] = 1.0f,\r\n        [\"sad\"] = 0.7f,\r\n        [\"angry\"] = 0.8f,\r\n        [\"surprised\"] = 1.0f,\r\n        [\"relaxed\"] = 0.5f\r\n    };\r\n}\r\n\r\n// 実装例\r\npublic async Task ApplyExpressionWithBlend(string emotion)\r\n{\r\n    var settings = new ExpressionBlendSettings();\r\n    float strength = settings.ExpressionStrength[emotion];\r\n    \r\n    // 現在の表情からブレンド\r\n    float time = 0;\r\n    while (time \u003c settings.BlendDuration)\r\n    {\r\n        float t = time / settings.BlendDuration;\r\n        float weight = Mathf.Lerp(0, strength, t);\r\n        vrmInstance.Runtime.Expression.SetWeight(emotionToExpression[emotion], weight);\r\n        time += Time.deltaTime;\r\n        await Task.Yield();\r\n    }\r\n    \r\n    // 表情を維持\r\n    await Task.Delay((int)(settings.HoldDuration * 1000));\r\n    \r\n    // 徐々に戻す\r\n    time = 0;\r\n    while (time \u003c settings.ReturnDuration)\r\n    {\r\n        float t = time / settings.ReturnDuration;\r\n        float weight = Mathf.Lerp(strength, 0, t);\r\n        vrmInstance.Runtime.Expression.SetWeight(emotionToExpression[emotion], weight);\r\n        time += Time.deltaTime;\r\n        await Task.Yield();\r\n    }\r\n}\r\n```\r\n\r\n#### 4.2 音声合成の詳細設定\r\n```csharp\r\n// 音声パラメータの動的調整\r\npublic class DynamicVoiceSettings\r\n{\r\n    // 基本パラメータ\r\n    public float BaseSpeed = 1.0f;\r\n    public float BasePitch = 0.0f;\r\n    public float BaseIntonation = 1.0f;\r\n    \r\n    // 文脈による調整\r\n    public class ContextualAdjustment\r\n    {\r\n        public float SpeedMultiplier = 1.0f;\r\n        public float PitchOffset = 0.0f;\r\n        public float IntonationMultiplier = 1.0f;\r\n    }\r\n    \r\n    // 文脈別の設定\r\n    public Dictionary\u003cstring, ContextualAdjustment\u003e ContextSettings = new Dictionary\u003cstring, ContextualAdjustment\u003e\r\n    {\r\n        [\"question\"] = new ContextualAdjustment \r\n        { \r\n            SpeedMultiplier = 0.9f,\r\n            PitchOffset = 0.1f,\r\n            IntonationMultiplier = 1.2f\r\n        },\r\n        [\"excitement\"] = new ContextualAdjustment\r\n        {\r\n            SpeedMultiplier = 1.2f,\r\n            PitchOffset = 0.2f,\r\n            IntonationMultiplier = 1.3f\r\n        }\r\n    };\r\n}\r\n\r\n// 実装例\r\npublic void AdjustVoiceForContext(string context)\r\n{\r\n    var settings = new DynamicVoiceSettings();\r\n    if (settings.ContextSettings.TryGetValue(context, out var adjustment))\r\n    {\r\n        config.VoiceSpeed = settings.BaseSpeed * adjustment.SpeedMultiplier;\r\n        config.VoicePitch = settings.BasePitch + adjustment.PitchOffset;\r\n        config.VoiceIntonation = settings.BaseIntonation * adjustment.IntonationMultiplier;\r\n    }\r\n}\r\n```\r\n\r\n### 5. パフォーマンスチューニング\r\n\r\n#### 5.1 メモリ使用量の最適化\r\n```csharp\r\npublic class MemoryOptimizationSettings\r\n{\r\n    // キャッシュ設定\r\n    public int MaxVoiceCacheSize = 100;    // 音声キャッシュの最大数\r\n    public int MaxResponseCacheSize = 50;   // 応答キャッシュの最大数\r\n    public float CacheCleanupInterval = 300f; // クリーンアップ間隔（秒）\r\n    \r\n    // リソース解放のしきい値\r\n    public float UnusedResourceTimeout = 600f;  // 未使用リソースの保持時間\r\n    public float MemoryThreshold = 0.8f;        // メモリ使用率のしきい値\r\n}\r\n\r\n// 実装例\r\npublic class ResourceManager\r\n{\r\n    private MemoryOptimizationSettings settings;\r\n    private Dictionary\u003cstring, CachedResource\u003e resourceCache;\r\n    \r\n    public void Initialize()\r\n    {\r\n        settings = new MemoryOptimizationSettings();\r\n        StartCoroutine(PeriodicCleanup());\r\n    }\r\n    \r\n    private IEnumerator PeriodicCleanup()\r\n    {\r\n        while (true)\r\n        {\r\n            CleanupUnusedResources();\r\n            yield return new WaitForSeconds(settings.CacheCleanupInterval);\r\n        }\r\n    }\r\n    \r\n    private void CleanupUnusedResources()\r\n    {\r\n        var currentTime = Time.time;\r\n        foreach (var resource in resourceCache.Values)\r\n        {\r\n            if (currentTime - resource.LastAccessTime \u003e settings.UnusedResourceTimeout)\r\n            {\r\n                resource.Dispose();\r\n            }\r\n        }\r\n    }\r\n}\r\n```\r\n\r\n### 6. トラブルシューティングガイド\r\n\r\n#### 6.1 一般的な問題の診断と解決\r\n```csharp\r\npublic class DiagnosticTools\r\n{\r\n    // システム状態の診断\r\n    public static SystemDiagnostics GetSystemStatus()\r\n    {\r\n        return new SystemDiagnostics\r\n        {\r\n            MemoryUsage = SystemInfo.systemMemorySize,\r\n            CpuUsage = GetCPUUsage(),\r\n            DiskSpace = GetAvailableDiskSpace(),\r\n            NetworkLatency = CheckNetworkLatency()\r\n        };\r\n    }\r\n    \r\n    // 接続テスト\r\n    public static async Task\u003cbool\u003e TestConnections()\r\n    {\r\n        bool voicevoxOk = await TestVoicevoxConnection();\r\n        bool llmOk = await TestLLMConnection();\r\n        return voicevoxOk \u0026\u0026 llmOk;\r\n    }\r\n    \r\n    // ログ収集\r\n    public static void CollectLogs(string outputPath)\r\n    {\r\n        // システムログ\r\n        // パフォーマンスデータ\r\n        // エラーレポート\r\n        // の収集と保存\r\n    }\r\n}\r\n```\r\n\r\n#### 6.2 エラーコードと対処方法\r\n```\r\nエラーコード一覧：\r\n\r\nVRM001: VRMモデルのロードエラー\r\n- ファイルパスの確認\r\n- モデルの互換性確認\r\n- ファイルの破損チェック\r\n\r\nLLM001: LLMサービス接続エラー\r\n- APIキーの確認\r\n- ネットワーク接続の確認\r\n- エンドポイントURLの確認\r\n\r\nVOX001: VOICEVOX接続エラー\r\n- サービスの起動確認\r\n- ポート番号の確認\r\n- ファイアウォール設定の確認\r\n\r\nMEM001: メモリ不足エラー\r\n- 未使用リソースの解放\r\n- キャッシュのクリア\r\n- システムリソースの確認\r\n```\r\n\r\n## 開発者向け情報\r\n\r\n### アーキテクチャ概要\r\n\r\n- `AIVTuberController`: メインのコントローラークラス\r\n- `ILLMService`: LLMサービスのインターフェース\r\n- `ErrorHandling`: エラー処理システム\r\n- `VRMModelLoader`: VRMモデル読み込み管理\r\n\r\n### 拡張ポイント\r\n\r\n1. 新しいLLMサービスの追加\r\n```csharp\r\npublic class NewLLMService : ILLMService\r\n{\r\n    public bool IsInitialized =\u003e true;\r\n    \r\n    public async Task\u003cstring\u003e GetResponseAsync(string input)\r\n    {\r\n        // サービス固有の実装\r\n        return await Task.FromResult(\"応答\");\r\n    }\r\n}\r\n```\r\n\r\n2. カスタム表情の追加\r\n```csharp\r\npublic void AddCustomExpression(string emotionName, ExpressionKey expressionKey)\r\n{\r\n    emotionToExpression[emotionName] = expressionKey;\r\n}\r\n```\r\n\r\n### パフォーマンス最適化\r\n\r\n- 音声合成のキャッシュ\r\n- 非同期処理の適切な使用\r\n- メモリ管理の注意点\r\n\r\n## 貢献ガイドライン\r\n\r\n### プルリクエスト\r\n\r\n1. 新機能の追加\r\n   - 機能の説明と目的\r\n   - 実装の詳細\r\n   - テストケース\r\n\r\n2. バグ修正\r\n   - バグの再現手順\r\n   - 修正内容の説明\r\n   - テスト結果\r\n\r\n### コーディング規約\r\n\r\n- C#コーディング規約に従う\r\n- XML文書コメントを使用\r\n- 単体テストを作成\r\n\r\n### 開発フロー\r\n\r\n1. Issueの作成\r\n2. ブランチの作成（feature/xxxまたはfix/xxx）\r\n3. 実装\r\n4. テスト\r\n5. プルリクエスト\r\n6. コードレビュー\r\n7. マージ\r\n\r\n## サポート\r\n\r\n問題が解決しない場合は、以下の手順で報告してください：\r\n\r\n1. Issueを作成\r\n2. 問題の詳細な説明\r\n3. 再現手順\r\n4. 環境情報\r\n   - Unityバージョン\r\n   - UniVRMバージョン\r\n   - OS情報\r\n   - 使用しているLLMサービス\r\n\r\n## 更新履歴\r\n\r\n### v1.0.0\r\n- 初期リリース\r\n- 基本機能の実装\r\n- VOICEVOXとの連携\r\n- 3種類のLLMサービス対応\r\n\r\n### 7. UI/UXの設定とカスタマイズ\r\n\r\n#### 7.1 基本的なCanvas階層構造\r\n```\r\nCanvas (Screen Space - Overlay)\r\n├── SafeArea\r\n│   ├── Header\r\n│   │   ├── Title\r\n│   │   └── StatusIndicator\r\n│   ├── ChatArea\r\n│   │   ├── ScrollView\r\n│   │   │   └── Content\r\n│   │   │       ├── MessageContainer\r\n│   │   │       │   ├── UserMessage\r\n│   │   │       │   └── AIResponse\r\n│   │   │       └── MessageTemplate\r\n│   │   └── ScrollBar\r\n│   ├── InputArea\r\n│   │   ├── InputField\r\n│   │   └── SendButton\r\n│   └── EmotePanel\r\n│       ├── EmoteGrid\r\n│       └── EmoteButtons\r\n└── OverlayEffects\r\n    ├── LoadingIndicator\r\n    └── NotificationPanel\r\n```\r\n\r\n#### 7.2 UI要素の詳細設定\r\n```csharp\r\n// Canvas設定\r\nCanvas mainCanvas = gameObject.AddComponent\u003cCanvas\u003e();\r\nmainCanvas.renderMode = RenderMode.ScreenSpaceOverlay;\r\nmainCanvas.sortingOrder = 100;\r\n\r\n// スケーリング設定\r\nCanvasScaler scaler = gameObject.AddComponent\u003cCanvasScaler\u003e();\r\nscaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;\r\nscaler.referenceResolution = new Vector2(1920, 1080);\r\nscaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;\r\nscaler.matchWidthOrHeight = 1.0f;\r\n\r\n// セーフエリアの設定\r\npublic class SafeAreaHandler : MonoBehaviour\r\n{\r\n    private void Awake()\r\n    {\r\n        ApplySafeArea();\r\n    }\r\n\r\n    private void ApplySafeArea()\r\n    {\r\n        var safeArea = Screen.safeArea;\r\n        var rectTransform = GetComponent\u003cRectTransform\u003e();\r\n        \r\n        var anchorMin = safeArea.position;\r\n        var anchorMax = anchorMin + safeArea.size;\r\n        \r\n        anchorMin.x /= Screen.width;\r\n        anchorMin.y /= Screen.height;\r\n        anchorMax.x /= Screen.width;\r\n        anchorMax.y /= Screen.height;\r\n        \r\n        rectTransform.anchorMin = anchorMin;\r\n        rectTransform.anchorMax = anchorMax;\r\n    }\r\n}\r\n```\r\n\r\n#### 7.3 チャットメッセージのレイアウト\r\n```csharp\r\npublic class ChatMessageLayout : MonoBehaviour\r\n{\r\n    [SerializeField] private float maxWidth = 800f;\r\n    [SerializeField] private float padding = 20f;\r\n    [SerializeField] private float spacing = 10f;\r\n    \r\n    // メッセージプレハブの設定\r\n    public class MessageSettings\r\n    {\r\n        public Color userMessageColor = new Color(0.2f, 0.6f, 1f);\r\n        public Color aiMessageColor = new Color(0.8f, 0.8f, 0.8f);\r\n        public float messageFadeInDuration = 0.3f;\r\n        public float messageSpacing = 15f;\r\n        public float cornerRadius = 10f;\r\n    }\r\n    \r\n    // メッセージの追加\r\n    public void AddMessage(string text, bool isUser)\r\n    {\r\n        var settings = new MessageSettings();\r\n        var messageObj = Instantiate(messagePrefab, contentTransform);\r\n        var messageUI = messageObj.GetComponent\u003cChatMessage\u003e();\r\n        \r\n        messageUI.Initialize(text, isUser, settings);\r\n        StartCoroutine(AnimateMessage(messageUI));\r\n    }\r\n    \r\n    // メッセージのアニメーション\r\n    private IEnumerator AnimateMessage(ChatMessage message)\r\n    {\r\n        message.SetAlpha(0f);\r\n        float time = 0;\r\n        \r\n        while (time \u003c message.settings.messageFadeInDuration)\r\n        {\r\n            float alpha = time / message.settings.messageFadeInDuration;\r\n            message.SetAlpha(alpha);\r\n            time += Time.deltaTime;\r\n            yield return null;\r\n        }\r\n        \r\n        message.SetAlpha(1f);\r\n        ScrollToBottom();\r\n    }\r\n}\r\n```\r\n\r\n#### 7.4 インタラクティブ要素のデザイン\r\n```csharp\r\npublic class ChatInputHandler : MonoBehaviour\r\n{\r\n    [SerializeField] private TMP_InputField inputField;\r\n    [SerializeField] private Button sendButton;\r\n    [SerializeField] private float inputDebounceTime = 0.1f;\r\n    \r\n    // 入力フィールドのスタイル設定\r\n    public void ConfigureInputField()\r\n    {\r\n        inputField.textComponent.fontSize = 16;\r\n        inputField.textComponent.color = new Color(0.2f, 0.2f, 0.2f);\r\n        inputField.placeholder.GetComponent\u003cTextMeshProUGUI\u003e().text = \"メッセージを入力...\";\r\n        \r\n        // カスタムプレースホルダーアニメーション\r\n        var placeholderAnimator = inputField.placeholder.gameObject.AddComponent\u003cPlaceholderAnimator\u003e();\r\n        placeholderAnimator.fadeSpeed = 0.5f;\r\n    }\r\n    \r\n    // 送信ボタンのスタイル設定\r\n    public void ConfigureSendButton()\r\n    {\r\n        var buttonColors = sendButton.colors;\r\n        buttonColors.normalColor = new Color(0.2f, 0.6f, 1f);\r\n        buttonColors.highlightedColor = new Color(0.3f, 0.7f, 1f);\r\n        buttonColors.pressedColor = new Color(0.1f, 0.5f, 0.9f);\r\n        sendButton.colors = buttonColors;\r\n        \r\n        // ボタンアニメーション\r\n        var buttonAnimator = sendButton.gameObject.AddComponent\u003cButtonAnimator\u003e();\r\n        buttonAnimator.scaleOnPress = 0.95f;\r\n    }\r\n}\r\n```\r\n\r\n#### 7.5 アクセシビリティとレスポンシブデザイン\r\n```csharp\r\npublic class UIAccessibilityManager : MonoBehaviour\r\n{\r\n    // フォントサイズの動的調整\r\n    public void AdjustFontSizes()\r\n    {\r\n        float screenWidth = Screen.width;\r\n        float baseSize = 16f;\r\n        float scaleFactor = Mathf.Clamp(screenWidth / 1920f, 0.8f, 1.2f);\r\n        \r\n        foreach (var text in FindObjectsOfType\u003cTextMeshProUGUI\u003e())\r\n        {\r\n            text.fontSize = baseSize * scaleFactor;\r\n        }\r\n    }\r\n    \r\n    // コントラスト比の確保\r\n    public void EnsureAccessibleColors()\r\n    {\r\n        // WCAG 2.0 AAガイドラインに準拠\r\n        var backgroundColor = new Color(0.98f, 0.98f, 0.98f);\r\n        var textColor = new Color(0.1f, 0.1f, 0.1f);\r\n        var accentColor = new Color(0.2f, 0.6f, 1f);\r\n    }\r\n}\r\n```\r\n\r\n#### 7.6 アニメーションとトランジション\r\n```csharp\r\npublic class UIAnimationController : MonoBehaviour\r\n{\r\n    // メッセージ送信アニメーション\r\n    public IEnumerator AnimateSendMessage(RectTransform messageRect)\r\n    {\r\n        messageRect.localScale = Vector3.zero;\r\n        \r\n        float time = 0;\r\n        float duration = 0.3f;\r\n        \r\n        while (time \u003c duration)\r\n        {\r\n            float t = time / duration;\r\n            float scale = Mathf.Sin(t * Mathf.PI * 0.5f);\r\n            messageRect.localScale = Vector3.one * scale;\r\n            time += Time.deltaTime;\r\n            yield return null;\r\n        }\r\n        \r\n        messageRect.localScale = Vector3.one;\r\n    }\r\n    \r\n    // 感情表現エフェクト\r\n    public void PlayEmoteEffect(string emotion)\r\n    {\r\n        var effectPrefab = GetEmoteEffectPrefab(emotion);\r\n        var effect = Instantiate(effectPrefab, effectsContainer);\r\n        \r\n        var sequence = DOTween.Sequence();\r\n        sequence.Append(effect.DOScale(1.2f, 0.2f));\r\n        sequence.Append(effect.DOScale(1f, 0.1f));\r\n        sequence.AppendInterval(1f);\r\n        sequence.Append(effect.DOFade(0f, 0.3f));\r\n        sequence.OnComplete(() =\u003e Destroy(effect.gameObject));\r\n    }\r\n}\r\n```\r\n\r\n#### 7.7 パフォーマンス最適化\r\n```csharp\r\npublic class ChatUIOptimizer : MonoBehaviour\r\n{\r\n    [SerializeField] private int maxVisibleMessages = 50;\r\n    [SerializeField] private int recycleThreshold = 20;\r\n    \r\n    // メッセージプール\r\n    private Queue\u003cChatMessage\u003e messagePool = new Queue\u003cChatMessage\u003e();\r\n    private List\u003cChatMessage\u003e activeMessages = new List\u003cChatMessage\u003e();\r\n    \r\n    // メッセージのリサイクル\r\n    private void RecycleMessages()\r\n    {\r\n        if (activeMessages.Count \u003e maxVisibleMessages + recycleThreshold)\r\n        {\r\n            int removeCount = activeMessages.Count - maxVisibleMessages;\r\n            for (int i = 0; i \u003c removeCount; i++)\r\n            {\r\n                var message = activeMessages[i];\r\n                message.gameObject.SetActive(false);\r\n                messagePool.Enqueue(message);\r\n            }\r\n            activeMessages.RemoveRange(0, removeCount);\r\n        }\r\n    }\r\n    \r\n    // レイアウトの最適化\r\n    private void OptimizeLayout()\r\n    {\r\n        // レイアウトグループの更新を制御\r\n        LayoutRebuilder.ForceRebuildLayoutImmediate(contentRect);\r\n        Canvas.ForceUpdateCanvases();\r\n    }\r\n}\r\n```\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzapabob%2Faivtuber-chat-unity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzapabob%2Faivtuber-chat-unity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzapabob%2Faivtuber-chat-unity/lists"}