# Yz個人メモ

個人的なメモ

# アルゴリズムの授業

## 1. 線形探索 (Linear Search)

### 1.1 概要

配列（またはリスト）の先頭から順番に目標値と比較し、一致したらその位置を返す。見つからなければ最後まで調べ終えて「未発見」を返す。

### 1.2 計算量

- 最良ケース：O(1) … 先頭にあった場合
- 平均ケース：O(n/2) ≒ O(n)
- 最悪ケース：O(n) … 存在しない場合や末尾にあった場合
- 空間計算量：O(1)

### 1.3 特徴・利点・欠点

- ****利点****
    - ソート不要。要素の順序を問わない
    - 実装が非常にシンプル
- ****欠点****
    - 要素数が大きいと比較回数も増大し、性能悪化
    - 頻繁に検索する場合は不向き

### 1.4 実装例 (C++)

```cpp
// 配列 arr[n] の中から key を線形探索し、見つかればそのインデックスを、
// 見つからなければ -1 を返す
int linearSearch(const std::vector<int>& arr, int key) {
    for (size_t i = 0; i < arr.size(); ++i) {
        if (arr[i] == key) {
            return static_cast<int>(i);
        }
    }
    return -1;
}
```

### 1.5 適用場面

- データ数が少ない、あるいは検索回数が少ない場合
- 配列がソートされていない、かつソートコストをかけたくない場合

---

## 2. 二分探索 (Binary Search)

### 2.1 概要

ソート済み配列の中央要素と比較し、目標値が小さければ左半分、大きければ右半分を再帰的（またはループ）に探索。範囲を半分ずつ絞っていく。

### 2.2 計算量

- 最良・平均・最悪ケース：いずれも O(log n)
- 空間計算量：O(1)（ループ版）／O(log n)（再帰版のスタック分）

### 2.3 特徴・利点・欠点

- ****利点****
    - 大量データでも高速
- ****欠点****
    - 事前にソートが必要（ソートコスト O(n log n)）
    - 配列へのランダムアクセスが前提
    - 実装ミス（境界条件）でバグを生みやすい

### 2.4 実装例 (C++)

```cpp
// ソート済み vector の二分探索
int binarySearch(const std::vector<int>& arr, int key) {
    int left = 0;
    int right = static_cast<int>(arr.size()) - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (arr[mid] == key) {
            return mid;
        } else if (arr[mid] < key) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;
}
```

### 2.5 適用場面

- データが事前にソートされている
- 検索回数が多く、毎回高速に探したい場合

---

## 3. ハッシュ探索 (Hash Search)

### 3.1 概要

ハッシュ関数でキーからテーブルの領域（バケット）を計算し、直接アクセス。バケット内はリストや連想配列で衝突解消（チェイニング、オープンアドレスなど）する。

### 3.2 計算量

- 平均ケース：O(1)
- 最悪ケース：O(n) … ただし適切なリサイズやハッシュ関数でほぼ起こらない
- 空間計算量：O(n)（テーブルサイズに応じたオーバーヘッドあり）

### 3.3 特徴・利点・欠点

- ****利点****
    - 平均O(1)で非常に高速
    - キー → 値 の対応に最適
- ****欠点****
    - メモリ使用量が増える
    - ハッシュ関数の設計／衝突解消の実装が必要
    - 順序付き探索には向かない

### 3.4 実装例 (C++ 標準ライブラリ利用)

```cpp
#include <unordered_map>

// key -> value のマップを作成し、検索
int hashSearch(const std::unordered_map<int, std::string>& mp, int key) {
    auto it = mp.find(key);
    if (it != mp.end()) {
        // 見つかったら値を参照できる
        return it->second;
    }
    // 見つからない
    throw std::out_of_range("キーが存在しません");
}
```

### 3.5 適用場面

- キーとデータをペアで管理したい場合
- 頻繁に挿入・削除・検索を行う動的なデータ構造

---

## 4. 比較まとめ

<table id="bkmrk-%E7%89%B9%E5%BE%B4%E7%B7%9A%E5%BD%A2%E6%8E%A2%E7%B4%A2%E4%BA%8C%E5%88%86%E6%8E%A2%E7%B4%A2%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5%E6%8E%A2%E7%B4%A2%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6"><colgroup><col></col><col></col><col></col><col></col></colgroup><tbody><tr><th>特徴

</th><th>線形探索

</th><th>二分探索

</th><th>ハッシュ探索

</th></tr><tr><td>前提条件

</td><td>なし

</td><td>ソート済み

</td><td>なし

</td></tr><tr><td>平均時間計算量

</td><td>O(n)

</td><td>O(log n)

</td><td>O(1)

</td></tr><tr><td>最悪時間計算量

</td><td>O(n)

</td><td>O(log n)

</td><td>O(n)

</td></tr><tr><td>空間オーバーヘッド

</td><td>ほぼゼロ

</td><td>ほぼゼロ

</td><td>テーブル分あり

</td></tr><tr><td>実装の易しさ

</td><td>◎（簡単）

</td><td>○（やや注意）

</td><td>△（ハッシュ設計）

</td></tr><tr><td>用途

</td><td>小規模or単発探索

</td><td>大規模かつ静的

</td><td>動的なキー／値管理

</td></tr></tbody></table>

---

### 5. 使い分けのポイント

- ****データが小さく、一度だけ検索する****<span style="white-space: pre-wrap;"> → 線形探索</span>
- ****データはソート可能／ソート済みで、繰り返し検索する****<span style="white-space: pre-wrap;"> → 二分探索</span>
- ****キーと値の対応を高速に管理したい／挿入・削除も行う****<span style="white-space: pre-wrap;"> → ハッシュ探索</span>

---

もしそれぞれの詳しい応用例や、別の言語での実装例、あるいは木構造探索（B木・AVL木など）についてもご興味があれば、ぜひお知らせください。

##### あなた:

ぎりぎりＣ言語しか勉強していない人がわかる疑似言語表記で、それぞれのアルゴリズムを説明してみてほしい。

###### ChatGPT:

了解です！  
C言語に慣れている人がすんなり読めるように、「****C言語風の疑似コード（変数宣言あり、for・if中心、関数呼び出しも明示****）」で、  
以下3つの探索アルゴリズムを順に説明します。

---

## 🔍 1. 線形探索（Linear Search）

### ● 概要

先頭から順番に調べていって、見つかったらその位置（インデックス）を返す。

### ● 疑似コード（C風）

```c
int linear_search(int arr[], int size, int key) {
    int i;

    for (i = 0; i < size; i++) {
        if (arr[i] == key) {
            return i; // 見つかったのでその位置を返す
        }
    }

    return -1; // 最後まで見つからなかった
}
```

### ● 説明ポイント

- `<span class="editor-theme-code">arr</span>`<span style="white-space: pre-wrap;"> … 探す対象の配列</span>
- `<span class="editor-theme-code">size</span>`<span style="white-space: pre-wrap;"> … 配列の要素数</span>
- `<span class="editor-theme-code">key</span>`<span style="white-space: pre-wrap;"> … 探したい値</span>
- <span style="white-space: pre-wrap;">見つかると </span>`<span class="editor-theme-code">i</span>`<span style="white-space: pre-wrap;"> を返す、見つからないと </span>`<span class="editor-theme-code">-1</span>`

---

## 🌓 2. 二分探索（Binary Search）

### ● 概要

中央を調べて、半分ずつ範囲を狭めていく方法（※配列がソートされている必要あり）

### ● 疑似コード（C風）

```c
int binary_search(int arr[], int size, int key) {
    int left = 0;
    int right = size - 1;

    while (left <= right) {
        int mid = (left + right) / 2;

        if (arr[mid] == key) {
            return mid; // 見つかった
        } else if (arr[mid] < key) {
            left = mid + 1; // 右半分を探す
        } else {
            right = mid - 1; // 左半分を探す
        }
    }

    return -1; // 見つからない
}
```

### ● 説明ポイント

- `<span class="editor-theme-code">left</span>`<span style="white-space: pre-wrap;"> と </span>`<span class="editor-theme-code">right</span>`<span style="white-space: pre-wrap;"> で調べる範囲を持っておく</span>
- 中央（`<span class="editor-theme-code">mid</span>`<span style="white-space: pre-wrap;">）と </span>`<span class="editor-theme-code">key</span>`<span style="white-space: pre-wrap;"> を比較</span>
- `<span class="editor-theme-code">key</span>`<span style="white-space: pre-wrap;"> が大きければ右、 小さければ左に範囲を狭める</span>

---

## 🔳 3. ハッシュ探索（Hash Search）

### ● 概要

値（key）を使って、直接「探す場所（インデックス）」を計算する方法  
<span style="white-space: pre-wrap;">→ 計算式： </span>`<span class="editor-theme-code">index = hash_function(key)</span>`

※ここでは****衝突がない簡易な例****として説明します

### ● 疑似コード（C風：簡易ハッシュテーブル使用）

```c
#define TABLE_SIZE 10

int hash_function(int key) {
    return key % TABLE_SIZE;
}

int hash_table[TABLE_SIZE]; // 値を保存するテーブル

void insert(int key) {
    int index = hash_function(key);
    hash_table[index] = key; // ※衝突処理は省略
}

int hash_search(int key) {
    int index = hash_function(key);
    if (hash_table[index] == key) {
        return index; // 見つかった
    } else {
        return -1; // 見つからない or 衝突で上書きされた
    }
}
```

### ● 説明ポイント

- `<span class="editor-theme-code">key % TABLE_SIZE</span>`<span style="white-space: pre-wrap;"> で場所を決める</span>
- `<span class="editor-theme-code">insert()</span>`<span style="white-space: pre-wrap;"> で格納、</span>`<span class="editor-theme-code">hash_search()</span>`<span style="white-space: pre-wrap;"> で検索</span>
- ※衝突（同じ場所になること）を処理するには工夫が必要（例：線形探査など）

---

## ✨ 比較まとめ（覚えやすく）

<table id="bkmrk-%E7%A8%AE%E9%A1%9E%E3%82%BD%E3%83%BC%E3%83%88%E5%BF%85%E8%A6%81%E9%80%9F%E3%81%95%EF%BC%88%E7%9B%AE%E5%AE%89%EF%BC%89%E7%B0%A1%E5%8D%98%E3%81%95%E7%89%B9%E5%BE%B4%E7%B7%9A%E5%BD%A2"><colgroup><col></col><col></col><col></col><col></col><col></col></colgroup><tbody><tr><th>種類

</th><th>ソート必要

</th><th>速さ（目安）

</th><th>簡単さ

</th><th>特徴

</th></tr><tr><td>線形探索

</td><td>不要

</td><td>遅い O(n)

</td><td>◎ 簡単

</td><td>一番素直な方法

</td></tr><tr><td>二分探索

</td><td>必要

</td><td>速い O(log n)

</td><td>○ 普通

</td><td>中央から半分に絞っていく

</td></tr><tr><td>ハッシュ探索

</td><td>不要

</td><td>速い O(1)

</td><td>△ やや難

</td><td>直接位置を計算して探す

</td></tr></tbody></table>

# C++の授業

## C++講義計画（C言語からの移行・ゲームオブジェクト構築まで）

### 🎯 目標

<span style="white-space: pre-wrap;">C言語を半年学んだ学生が、C++の基礎文法からオブジェクト指向（継承・ポリモーフィズム）を学び、最終的に </span>`<span class="editor-theme-code">GameObject</span>`<span style="white-space: pre-wrap;"> クラスを継承してプレイヤーや敵を実装できるようにする。</span>

---

## 🧭 全体構成（全15回想定）

<table id="bkmrk-%E5%9B%9E%E3%83%86%E3%83%BC%E3%83%9E%E6%8A%80%E8%A1%93%E7%9B%AE%E6%A8%99%E6%BC%94%E7%BF%92%E4%BE%8B1c%2B%2B%E5%B0%8E%E5%85%A5%E3%83%BBc%E3%81%A8"><colgroup><col></col><col></col><col></col><col></col></colgroup><tbody><tr><th>回</th><th>テーマ</th><th>技術目標</th><th>演習例</th></tr><tr><td>****1****

</td><td>C++導入・Cとの違い

</td><td>`<span class="editor-theme-code">iostream</span>`<span style="white-space: pre-wrap;"> / 名前空間 / </span>`<span class="editor-theme-code">new</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">delete</span>`

</td><td>Cのprintf版→C++出力へ書き換え

</td></tr><tr><td>****2****

</td><td>関数と参照・オーバーロード

</td><td>参照渡し・デフォルト引数・関数多重定義

</td><td>距離計算関数のオーバーロード

</td></tr><tr><td>****3****

</td><td>クラス・コンストラクタ入門

</td><td>`<span class="editor-theme-code">class</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">public</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">private</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">this</span>`

</td><td>Vector2構造体→Vector2クラス化

</td></tr><tr><td>****4****

</td><td>クラスの設計とヘッダ分離

</td><td>`<span class="editor-theme-code">.h</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">.cpp</span>`<span style="white-space: pre-wrap;"> 分離と include 管理</span>

</td><td>Vector2, Rect クラス実装

</td></tr><tr><td>****5****

</td><td>動的メモリとコンテナ基礎

</td><td>`<span class="editor-theme-code">new/delete</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">std::vector</span>`

</td><td>`<span class="editor-theme-code">std::vector</span>`で敵リストを作る

</td></tr><tr><td>****6****

</td><td>コンストラクタ／デストラクタ／コピー

</td><td>RAIIの考え方

</td><td>ファイル読み込み管理クラス

</td></tr><tr><td>****7****

</td><td>継承入門

</td><td>`<span class="editor-theme-code">Base</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">Derived</span>`<span style="white-space: pre-wrap;"> クラス / </span>`<span class="editor-theme-code">protected</span>`

</td><td>`<span class="editor-theme-code">Enemy</span>`<span style="white-space: pre-wrap;"> ← </span>`<span class="editor-theme-code">Slime</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">Bat</span>`<span style="white-space: pre-wrap;"> など作る</span>

</td></tr><tr><td>****8****

</td><td>仮想関数とポリモーフィズム

</td><td>`<span class="editor-theme-code">virtual</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">override</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">GameObject*</span>`管理

</td><td>`<span class="editor-theme-code">GameObject</span>`<span style="white-space: pre-wrap;"> 基底で Draw/Updateを仮想化</span>

</td></tr><tr><td>****9****

</td><td>純粋仮想関数・抽象クラス

</td><td>`<span class="editor-theme-code">=0</span>`<span style="white-space: pre-wrap;"> の概念、基底クラス設計</span>

</td><td>`<span class="editor-theme-code">GameObject</span>`<span style="white-space: pre-wrap;"> を抽象化して管理リストに追加</span>

</td></tr><tr><td>****10****

</td><td>スマートポインタ・メモリ安全化

</td><td>`<span class="editor-theme-code">std::unique_ptr</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">std::shared_ptr</span>`

</td><td>`<span class="editor-theme-code">std::vector<unique_ptr<GameObject>></span>`

</td></tr><tr><td>****11****

</td><td>継承＋多態性の活用

</td><td>`<span class="editor-theme-code">virtual</span>`<span style="white-space: pre-wrap;">呼び出しと </span>`<span class="editor-theme-code">override</span>`確認

</td><td>`<span class="editor-theme-code">Enemy</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">Player</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">Item</span>`<span style="white-space: pre-wrap;"> を一括更新</span>

</td></tr><tr><td>****12****

</td><td>クラス間関係・静的メンバ

</td><td>`<span class="editor-theme-code">static</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">const</span>`, クラス内定数

</td><td>`<span class="editor-theme-code">ObjectManager</span>`<span style="white-space: pre-wrap;"> 実装</span>

</td></tr><tr><td>****13****

</td><td>名前空間・クラス分割整理

</td><td>`<span class="editor-theme-code">namespace</span>`, include guard

</td><td>`<span class="editor-theme-code">game/</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">engine/</span>`<span style="white-space: pre-wrap;"> に分離</span>

</td></tr><tr><td>****14****

</td><td>総合演習：ミニゲーム制作

</td><td>ゲームループ構築

</td><td>コンソール or DxLibで動作確認

</td></tr><tr><td>****15****

</td><td>発表・振り返り

</td><td>設計比較・自己評価

</td><td>ソース＋UML提出・口頭説明

</td></tr></tbody></table>

---

## 🧩 ステップ詳細

### STEP 1〜4：C++基礎をCとの違いで理解

- Cの書き方との比較で、スムーズに導入。
- printf→`<span class="editor-theme-code">std::cout</span>`、struct→class、malloc→new など差分理解。

### STEP 5〜6：クラスとメモリ管理

- C構造体＋関数をクラスに統合。
- `<span class="editor-theme-code">vector</span>`<span style="white-space: pre-wrap;"> と RAII の基本を体験。</span>

### STEP 7〜9：継承とポリモーフィズム

- `<span class="editor-theme-code">GameObject</span>`<span style="white-space: pre-wrap;"> に </span>`<span class="editor-theme-code">virtual void Update()</span>`<span style="white-space: pre-wrap;"> を持たせて多態の体験。</span>
- `<span class="editor-theme-code">Player</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">Enemy</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">Item</span>`<span style="white-space: pre-wrap;"> の派生クラスを一括更新。</span>

for (auto obj : objects) obj-&gt;Update();

### STEP 10〜11：メモリ安全化

- `<span class="editor-theme-code">unique_ptr</span>`導入でC言語的delete漏れを防止。
- `<span class="editor-theme-code">ObjectManager</span>`<span style="white-space: pre-wrap;"> による一元管理へ。</span>

### STEP 12〜15：まとめとゲーム制作

- 名前空間整理とクラス分離で設計の見通しをよくする。
- 最終的にプレイヤー・敵・アイテムなどを`<span class="editor-theme-code">GameObject</span>`継承で統合。

---

## 🎮 最終課題例

<table id="bkmrk-%E3%82%BF%E3%82%A4%E3%83%97%E5%86%85%E5%AE%B9%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%ABrpgcharact"><colgroup><col></col><col></col></colgroup><tbody><tr><td>タイプ

</td><td>内容

</td></tr><tr><td>****コンソールRPG****

</td><td>`<span class="editor-theme-code">Character</span>`<span style="white-space: pre-wrap;"> 継承で </span>`<span class="editor-theme-code">Player</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">Enemy</span>`<span style="white-space: pre-wrap;"> を共通管理</span>

</td></tr><tr><td>****2Dアクション (DxLib)****

</td><td>`<span class="editor-theme-code">GameObject</span>`<span style="white-space: pre-wrap;">継承で </span>`<span class="editor-theme-code">Ball</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">Block</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">Paddle</span>`<span style="white-space: pre-wrap;"> 管理</span>

</td></tr><tr><td>****スネーク風ゲーム****

</td><td>`<span class="editor-theme-code">GameObject</span>`<span style="white-space: pre-wrap;">多態性で </span>`<span class="editor-theme-code">SnakeHead</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">Fruit</span>`<span style="white-space: pre-wrap;"> 管理</span>

</td></tr></tbody></table>

---

## 📘 授業設計のポイント

- 毎回「CからC++へどう変わるか」を比較して示す。
- すぐ動かせるコード例を提示。
- 最終回で「OOPでコードがどう整理されたか」を可視化する。

---

### 💡 まとめ

> 15回で「C構文→クラス→継承→仮想関数→安全なゲーム構造」まで自然に到達できる構成。 最終的には、ポリモーフィズムを使って`<span class="editor-theme-code">GameObject</span>`を扱う“C++らしいゲーム設計”を完成させる。

# １回目　導入回＋文字列

## 第1回授業：C++導入とCとの違い（入出力・文字列・書式制御）

### 🎯 目標

C言語の知識をもとに、C++の基本文法と文字列処理を理解する。

- `<span class="editor-theme-code">iostream</span>`<span style="white-space: pre-wrap;"> を使った標準入出力を理解する。</span>
- `<span class="editor-theme-code">std::string</span>`<span style="white-space: pre-wrap;"> と </span>`<span class="editor-theme-code">char[]</span>`<span style="white-space: pre-wrap;"> の違いを学ぶ。</span>
- `<span class="editor-theme-code">cout</span>`<span style="white-space: pre-wrap;"> による書式制御を体験する。</span>

---

## 🧩 授業構成（90分想定）

<table id="bkmrk-%E6%99%82%E9%96%93%E5%86%85%E5%AE%B9%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%880%E3%80%9C10%E5%88%86c%E3%81%A8c%2B%2B%E3%81%AE%E9%96%A2"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th>時間

</th><th>内容

</th><th>ポイント

</th></tr><tr><td>0〜10分

</td><td>CとC++の関係

</td><td>「C++はCの拡張」だが、文字列処理や入出力が高機能であることを紹介。

</td></tr><tr><td>10〜30分

</td><td>入出力の基本

</td><td>`<span class="editor-theme-code">#include <iostream></span>`

、

`<span class="editor-theme-code">std::cout</span>`

<span style="white-space: pre-wrap;"> / </span>

`<span class="editor-theme-code">std::cin</span>`

<span style="white-space: pre-wrap;"> の使い方。</span>

`<span class="editor-theme-code">using namespace std;</span>`

<span style="white-space: pre-wrap;"> の意味。</span>

</td></tr><tr><td>30〜55分

</td><td>文字列の扱い

</td><td>`<span class="editor-theme-code">char[]</span>`

<span style="white-space: pre-wrap;"> と </span>

`<span class="editor-theme-code">std::string</span>`

<span style="white-space: pre-wrap;"> の違いを説明。代入・連結・比較などを実演。</span>

</td></tr><tr><td>55〜80分

</td><td>書式制御

</td><td>`<span class="editor-theme-code">std::setw</span>`

、

`<span class="editor-theme-code">std::setfill</span>`

、

`<span class="editor-theme-code">std::fixed</span>`

、

`<span class="editor-theme-code">std::setprecision</span>`

<span style="white-space: pre-wrap;"> など </span>

`<span class="editor-theme-code"><iomanip></span>`

<span style="white-space: pre-wrap;"> の使用。表形式出力の練習。</span>

</td></tr><tr><td>80〜90分

</td><td>まとめ・課題説明

</td><td>文字列と入出力のまとめ。次回予告：「関数と参照」へ。

</td></tr></tbody></table>

---

## 🧠 授業のねらい

- C言語で扱いづらかった「文字列」や「入出力フォーマット」がC++で簡潔に扱えることを体感させる。
- 今後の授業で使う`<span class="editor-theme-code">std::string</span>`を習得し、OOPでのクラス利用にもスムーズに入れるようにする。

---

## 📘 板書・スライド例

### Cの例（文字列と出力）

```c
#include <stdio.h>
int main() {
    char name[32];
    printf("Input your name: ");
    scanf("%s", name);
    printf("Hello, %s!\n", name);
    return 0;
}
```

### C++の例（std::string版）

```cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    string name;
    cout << "Input your name: ";
    cin >> name;
    cout << "Hello, " << name << "!" << endl;
    return 0;
}
```

<span style="white-space: pre-wrap;">💬 </span>****ポイント****

- `<span class="editor-theme-code">string</span>`<span style="white-space: pre-wrap;"> は動的にサイズが変わる安全な文字列クラス。</span>
- 代入・連結・比較が簡単。

```cpp
string a = "Hello";
string b = "World";
string c = a + ", " + b;
if (a == "Hello") cout << c << endl;
```

<span style="white-space: pre-wrap;">💡 </span>****比較:****

<table id="bkmrk-%E6%93%8D%E4%BD%9Cc-%28char%5B%5D%29c%2B%2B-%28std"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th>操作

</th><th>C (char\[\])

</th><th>C++ (std::string)

</th></tr><tr><td>代入

</td><td>`<span class="editor-theme-code">strcpy(s, "abc")</span>`

</td><td>`<span class="editor-theme-code">s = "abc";</span>`

</td></tr><tr><td>結合

</td><td>`<span class="editor-theme-code">strcat(a, b)</span>`

</td><td>`<span class="editor-theme-code">a += b;</span>`

</td></tr><tr><td>比較

</td><td>`<span class="editor-theme-code">strcmp(a, b) == 0</span>`

</td><td>`<span class="editor-theme-code">a == b</span>`

</td></tr><tr><td>長さ

</td><td>`<span class="editor-theme-code">strlen(a)</span>`

</td><td>`<span class="editor-theme-code">a.size()</span>`

</td></tr></tbody></table>

---

## ✏️ 書式制御の例

```cpp
#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    double pi = 3.14159265;
    cout << fixed << setprecision(3);
    cout << "pi = " << pi << endl;

    cout << setw(10) << setfill('-') << 42 << endl;
    return 0;
}
```

<span style="white-space: pre-wrap;">💬 </span>****ポイント****

- `<span class="editor-theme-code"><iomanip></span>`<span style="white-space: pre-wrap;"> によりCの</span>`<span class="editor-theme-code">printf</span>`に近い整形出力が可能。
- `<span class="editor-theme-code">setw()</span>`：桁幅指定、`<span class="editor-theme-code">setfill()</span>`：埋め文字、`<span class="editor-theme-code">setprecision()</span>`：小数点桁数指定。

---

## 🧩 演習課題

1. 名前と年齢を入力し、整形して出力するプログラムを作成。  
    ```
    Name: _____
    Age : __ years old
    ```
2. `<span class="editor-theme-code">std::string</span>`を使って2つの単語を入力し、結合して表示する。
3. `<span class="editor-theme-code"><iomanip></span>`を使って3つの数値を整列表示する。

---

## 💬 授業後課題（宿題）

1. `<span class="editor-theme-code">std::string</span>`と`<span class="editor-theme-code">char[]</span>`の違いを、3つの観点（代入・結合・安全性）で説明せよ。
2. 書式制御を使って表のようなスコア表示を作るプログラムを書け。

---

## 🔎 次回予告

> ****第2回：関数と参照・オーバーロード****  
> C++独自の関数設計（参照渡し・デフォルト引数）を学び、関数の柔軟性を理解する。

# 補足資料

## 第1回授業補足編：書式制御とfmtライブラリ入門

### 🎯 目的

C++における出力整形の幅を広げ、より実践的なフォーマット出力を理解する。

- `<span class="editor-theme-code"><iomanip></span>`による標準的な書式指定を整理。
- `<span class="editor-theme-code">std::format</span>`（C++20）や`<span class="editor-theme-code">fmt</span>`ライブラリの書式構文を紹介。
- `<span class="editor-theme-code">printf</span>`との対応関係を示し、Cからの移行をスムーズにする。

---

## 🧩 &lt;iomanip&gt;による標準フォーマットまとめ

<table id="bkmrk-%E6%93%8D%E4%BD%9C%E6%93%8D%E4%BD%9C%E9%96%A2%E6%95%B0%E5%8A%B9%E6%9E%9C%E4%BD%BF%E7%94%A8%E4%BE%8B%E6%A1%81%E5%B9%85%E6%8C%87%E5%AE%9Asetw%28"><colgroup><col></col><col></col><col></col><col></col></colgroup><tbody><tr><th>操作

</th><th>操作関数

</th><th>効果

</th><th>使用例

</th></tr><tr><td>桁幅指定

</td><td>`<span class="editor-theme-code">setw(n)</span>`

</td><td>指定幅に右寄せ

</td><td>`<span class="editor-theme-code">cout << setw(5) << 42;</span>`

<span style="white-space: pre-wrap;"> → </span>

`<span class="editor-theme-code">42</span>`

</td></tr><tr><td>左寄せ

</td><td>`<span class="editor-theme-code">left</span>`

</td><td>出力を左寄せ

</td><td>`<span class="editor-theme-code">cout << left << setw(5) << 42;</span>`

<span style="white-space: pre-wrap;"> → </span>

`<span class="editor-theme-code">42</span>`

</td></tr><tr><td>埋め文字

</td><td>`<span class="editor-theme-code">setfill(c)</span>`

</td><td>空白を指定文字で埋める

</td><td>`<span class="editor-theme-code">cout << setfill('-') << setw(5) << 42;</span>`

<span style="white-space: pre-wrap;"> → </span>

`<span class="editor-theme-code">---42</span>`

</td></tr><tr><td>進数指定

</td><td>`<span class="editor-theme-code">dec</span>`

<span style="white-space: pre-wrap;"> / </span>

`<span class="editor-theme-code">hex</span>`

<span style="white-space: pre-wrap;"> / </span>

`<span class="editor-theme-code">oct</span>`

</td><td>10進・16進・8進表示

</td><td>`<span class="editor-theme-code">cout << hex << 255;</span>`

<span style="white-space: pre-wrap;"> → </span>

`<span class="editor-theme-code">ff</span>`

</td></tr><tr><td>小数点桁数

</td><td>`<span class="editor-theme-code">setprecision(n)</span>`

</td><td>小数点以下の桁数指定

</td><td>`<span class="editor-theme-code">cout << fixed << setprecision(2) << 3.14159;</span>`

<span style="white-space: pre-wrap;"> → </span>

`<span class="editor-theme-code">3.14</span>`

</td></tr><tr><td>浮動小数点表記

</td><td>`<span class="editor-theme-code">fixed</span>`

<span style="white-space: pre-wrap;"> / </span>

`<span class="editor-theme-code">scientific</span>`

</td><td>固定小数点／指数表記

</td><td>`<span class="editor-theme-code">cout << scientific << 0.00123;</span>`

<span style="white-space: pre-wrap;"> → </span>

`<span class="editor-theme-code">1.23e-03</span>`

</td></tr></tbody></table>

<span style="white-space: pre-wrap;">💡 </span>****ポイント****

- `<span class="editor-theme-code">setw()</span>`<span style="white-space: pre-wrap;"> は </span>****次の出力にのみ有効****（毎回指定が必要）。
- `<span class="editor-theme-code">setfill()</span>`<span style="white-space: pre-wrap;"> は </span>****継続して有効****。
- `<span class="editor-theme-code">fixed</span>`<span style="white-space: pre-wrap;"> を指定しない場合、整数部分や小数点以下が自動的に最適化される。</span>

---

## 🧠 応用例：表形式出力

```cpp
#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    cout << left << setw(10) << "Name" << right << setw(8) << "Score" << endl;
    cout << setfill('-') << setw(18) << "" << endl;
    cout << setfill(' ');

    cout << left << setw(10) << "Alice" << right << setw(8) << 95 << endl;
    cout << left << setw(10) << "Bob"   << right << setw(8) << 87 << endl;
    cout << left << setw(10) << "Chris" << right << setw(8) << 100 << endl;
}
```

出力例：

```
Name         Score
------------------
Alice           95
Bob             87
Chris          100
```

---

## 🧩 std::format (C++20）とfmtライブラリ

C++20以降では、Python風の書式指定が可能な`<span class="editor-theme-code">std::format</span>`が導入されています。旧環境では`<span class="editor-theme-code">fmt</span>`ライブラリ（[https://github.com/fmtlib/fmt](https://github.com/fmtlib/fmt)）を使用できます。

### 使い方例

```cpp
#include <format> // C++20以降
#include <iostream>
using namespace std;

int main() {
    string name = "Alice";
    int score = 95;
    double rate = 0.8765;

    cout << format("Name: {:<10} | Score: {:>4d} | Rate: {:.2f}\n", name, score, rate);
}
```

出力：

```
Name: Alice      | Score:   95 | Rate: 0.88
```

<table id="bkmrk-%E6%9B%B8%E5%BC%8F%E6%8C%87%E5%AE%9A%E5%AD%90%E6%84%8F%E5%91%B3%E4%BE%8B%3A%3C10%E5%B7%A6%E5%AF%84%E3%81%9B%E3%83%BB%E5%B9%8510%7B"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th>書式指定子

</th><th>意味

</th><th>例

</th></tr><tr><td>`<span class="editor-theme-code">:<10</span>`

</td><td>左寄せ・幅10

</td><td>`<span class="editor-theme-code">{: <10}</span>`

<span style="white-space: pre-wrap;"> → 左寄せ</span>

</td></tr><tr><td>`<span class="editor-theme-code">:>10</span>`

</td><td>右寄せ・幅10

</td><td>`<span class="editor-theme-code">{: >10}</span>`

<span style="white-space: pre-wrap;"> → 右寄せ</span>

</td></tr><tr><td>`<span class="editor-theme-code">:.2f</span>`

</td><td>小数点以下2桁

</td><td>`<span class="editor-theme-code">{:.2f}</span>`

<span style="white-space: pre-wrap;"> → </span>

`<span class="editor-theme-code">3.14</span>`

</td></tr><tr><td>`<span class="editor-theme-code">:06d</span>`

</td><td>6桁ゼロ埋め

</td><td>`<span class="editor-theme-code">{:06d}</span>`

<span style="white-space: pre-wrap;"> → </span>

`<span class="editor-theme-code">000042</span>`

</td></tr><tr><td>`<span class="editor-theme-code">{}</span>`

</td><td>自動推論

</td><td>デフォルト出力

</td></tr></tbody></table>

### 💬 fmtライブラリでの使用例（C++17以前）

```cpp
#include <fmt/core.h>
#include <string>

int main() {
    std::string name = "Bob";
    int age = 20;
    fmt::print("Hello, {}! You are {} years old.\n", name, age);
}
```

出力：

```
Hello, Bob! You are 20 years old.
```

---

## 🔍 比較：Cのprintf関数との違い

<table id="bkmrk-%E6%93%8D%E4%BD%9Cprintfiostreamfmt-"><colgroup><col></col><col></col><col></col><col></col></colgroup><tbody><tr><th>操作

</th><th>printf

</th><th>iostream

</th><th>fmt / format

</th></tr><tr><td>書式構文

</td><td>`<span class="editor-theme-code">%d %f %s</span>`

</td><td>関数チェーン形式

</td><td>`<span class="editor-theme-code">{}</span>`

<span style="white-space: pre-wrap;"> ベースのテンプレート式</span>

</td></tr><tr><td>型安全性

</td><td>❌ 弱い

</td><td>✅ 強い

</td><td>✅ 強い

</td></tr><tr><td>可読性

</td><td>△

</td><td>○

</td><td>◎

</td></tr><tr><td>柔軟性

</td><td>○

</td><td>○

</td><td>◎（動的組み立て可）

</td></tr></tbody></table>

---

## 💡 まとめ

- `<span class="editor-theme-code"><iomanip></span>`<span style="white-space: pre-wrap;"> は既存Cライクコードに馴染む標準方式。</span>
- `<span class="editor-theme-code">std::format</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">fmt</span>`<span style="white-space: pre-wrap;"> は現代的で読みやすく、ゲームデバッグ出力などに最適。</span>
- どちらも「****Cのprintfより安全で拡張性が高い****」という点を体験させるのが狙い。

---

### 🔎 次にやるとよい演習

1. 数値を右寄せ・左寄せしてスコア表を整形。
2. `<span class="editor-theme-code">fmt::print</span>`<span style="white-space: pre-wrap;"> を使ってデバッグメッセージを出すツール関数を作る。</span>
3. `<span class="editor-theme-code">std::format</span>`<span style="white-space: pre-wrap;"> でログ出力を作り、</span>`<span class="editor-theme-code">cout</span>`版と比較する。

# 2回目　関数と参照・アインズ・ウール・ゴウン

##### 🎯 第2回 関数と参照・オーバーロード

## 🏁 授業目標

- ✳️ C++の関数定義・宣言・呼び出しを理解する
- 🔒 const参照（const &amp;）の使い方を理解する
- 🔁 関数のオーバーロードを使って柔軟な関数設計ができるようにする

---

## 🧠 関数の基本

C++の関数は、処理をまとめて呼び出せる仕組みです。  
関数には「宣言」と「定義」があり、呼び出すときは名前と引数を使います。

```cpp
#include <iostream>
using namespace std;

int add(int a, int b); // 宣言

int main() {
    int result = add(2, 3);
    cout << result << endl; // 5
}

int add(int a, int b) {
    return a + b;
}
```

<span style="white-space: pre-wrap;">💬 </span>****ポイント****

- 関数宣言（プロトタイプ）は main より前に書く
- main のあとに本体（定義）を書くのが一般的

---

## 🧩 const参照の基本

大きなデータをコピーせずに扱いたいときに便利！

```cpp
void show(const string& name) {
    cout << "Hello, " << name << endl;
}
```

<span style="white-space: pre-wrap;">🔒 </span>****const参照のメリット****

- コピーが発生しない → 高速
- 値を変更できない → 安全
- 一時オブジェクトも受け取れる

<table id="bkmrk-%E7%A8%AE%E9%A1%9E%E5%86%85%E5%AE%B9%E7%89%B9%E5%BE%B4%E5%80%A4%E6%B8%A1%E3%81%97%E3%82%B3%E3%83%94%E3%83%BC%E3%81%97%E3%81%A6%E6%B8%A1%E3%81%99%E5%AE%89%E5%85%A8%E3%81%A0%E3%81%8C"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th>種類

</th><th>内容

</th><th>特徴

</th></tr><tr><td>値渡し

</td><td>コピーして渡す

</td><td>安全だが遅い場合もある

</td></tr><tr><td>参照渡し

</td><td>実体を直接操作

</td><td>変更される可能性あり

</td></tr><tr><td>const参照

</td><td>読み取り専用参照

</td><td>安全で効率的

</td></tr></tbody></table>

---

## ⚙️ 関数のオーバーロード

同じ名前で引数が違う関数を複数定義できる！

```cpp
int area(int w, int h) { return w * h; }
double area(double r) { return 3.14 * r * r; }
```

<span style="white-space: pre-wrap;">🔎 </span>****注意****

- 戻り値の型だけ違ってもNG
- 引数の数・型・順序が異なる必要あり

---

## 💡 const参照＋オーバーロードの組み合わせ

```cpp
void print(string& s) { cout << "L-Value: " << s << endl; }
void print(const string& s) { cout << "Const:  " << s << endl; }
```

👉 実体（変数）ならL-Value版、一時文字列ならconst参照版！

---

## 🧪 演習問題

<span style="white-space: pre-wrap;">1️⃣ 2つの整数を足す関数 </span>`<span class="editor-theme-code">add</span>`  
<span style="white-space: pre-wrap;">2️⃣ 名前を受け取って挨拶する </span>`<span class="editor-theme-code">greet(const string&)</span>`  
<span style="white-space: pre-wrap;">3️⃣ 面積を求める </span>`<span class="editor-theme-code">area</span>`<span style="white-space: pre-wrap;"> を整数／実数でオーバーロード</span>

---

## 🏆 まとめ

- 関数は「処理をまとめる箱」
- const参照で安全・高速な引数渡し
- オーバーロードで同じ名前の関数を使い分け

# 補足資料（１．５回目）　C言語の関数

# 🧩 補足資料：C言語の関数の基本

## 🧠 関数とは？

C言語の****関数****は、処理をひとまとめにして何度も呼び出せる仕組みです。  
プログラムの****再利用性****・****可読性****を高めるために使います。

```c
#include <stdio.h>

int main() {
    hello(); // 関数呼び出し
    return 0;
}

// 関数定義
void hello() {
    printf("Hello, world!\\n");
}
```

---

## 🔹 宣言と定義の違い

<table id="bkmrk-%E5%90%8D%E7%A7%B0%E5%BD%B9%E5%89%B2%E6%9B%B8%E3%81%8F%E5%A0%B4%E6%89%80%E4%BE%8B%E9%96%A2%E6%95%B0%E5%AE%A3%E8%A8%80%EF%BC%88%E3%83%97%E3%83%AD%E3%83%88%E3%82%BF%E3%82%A4%E3%83%97"><colgroup><col></col><col></col><col></col><col></col></colgroup><tbody><tr><th>名称

</th><th>役割

</th><th>書く場所

</th><th>例

</th></tr><tr><td>関数宣言（プロトタイプ宣言）

</td><td>関数があることをコンパイラに知らせる

</td><td>mainより上

</td><td>`<span class="editor-theme-code">int add(int a, int b);</span>`

</td></tr><tr><td>関数定義

</td><td>実際の処理内容を書く

</td><td>mainより下でもOK

</td><td>`<span class="editor-theme-code">int add(int a, int b) { return a + b; }</span>`

</td></tr></tbody></table>

<span style="white-space: pre-wrap;">💡 </span>****ポイント****

- 宣言と定義を分けることで、****関数を別ファイルに分割****できる。
- 宣言だけをヘッダファイル（`<span class="editor-theme-code">.h</span>`）に書くのが一般的。

---

## 🔸 引数と仮引数の違い

<table id="bkmrk-%E7%A8%AE%E9%A1%9E%E6%84%8F%E5%91%B3%E4%BE%8B%E4%BB%AE%E5%BC%95%E6%95%B0%EF%BC%88parameter%EF%BC%89%E9%96%A2"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th>種類

</th><th>意味

</th><th>例

</th></tr><tr><td>仮引数（parameter）

</td><td>関数の受け取り側の変数

</td><td>`<span class="editor-theme-code">int add(int a, int b)</span>`

<span style="white-space: pre-wrap;"> の a,b</span>

</td></tr><tr><td>実引数（argument）

</td><td>関数を呼び出すときに渡す値

</td><td>`<span class="editor-theme-code">add(3, 5)</span>`

<span style="white-space: pre-wrap;"> の 3,5</span>

</td></tr></tbody></table>

<span style="white-space: pre-wrap;">🧭 </span>****イメージ図****

```
int add(int a, int b) ← 仮引数
        ↑      ↑
        |      |
   add(3, 5); ← 実引数
```

---

## 🧷 ポインタ渡し

関数に変数の\*\*アドレス（場所）\*\*を渡すことで、呼び出し元の値を直接変更できます。

```c
#include <stdio.h>

void addOne(int* x) {
    *x = *x + 1;
}

int main() {
    int num = 10;
    addOne(&num); // 変数のアドレスを渡す
    printf("%d\\n", num); // → 11
}
```

<span style="white-space: pre-wrap;">💬 </span>****ポイント****

- `<span class="editor-theme-code">int* x</span>`<span style="white-space: pre-wrap;"> は「int型のポインタ」</span>
- `<span class="editor-theme-code">*x</span>`<span style="white-space: pre-wrap;"> は「ポインタの指す実際の値」</span>
- 呼び出し元の変数を直接書き換えることができる

---

## ⚙️ 戻り値・void・手続きと関数

C言語の関数には、****値を返す関数****と\*\*値を返さない手続き（void関数）\*\*の2種類があります。

```c
// 値を返す関数
int add(int a, int b) {
    return a + b;
}

// 値を返さない（void関数）
void printHello() {
    printf("Hello!\\n");
}
```

<span style="white-space: pre-wrap;">💡 </span>****return文****

- 関数の実行を終了し、呼び出し元に値を返す。
- void関数では return を省略可能。

---

## 🧾 C言語の関数まとめ

<table id="bkmrk-%E5%88%86%E9%A1%9E%E8%AA%AC%E6%98%8E%E4%BE%8B%E5%AE%A3%E8%A8%80%EF%BC%88%E3%83%97%E3%83%AD%E3%83%88%E3%82%BF%E3%82%A4%E3%83%97%EF%BC%89%E9%96%A2%E6%95%B0%E3%81%AE%E5%AD%98%E5%9C%A8"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th>分類

</th><th>説明

</th><th>例

</th></tr><tr><td>宣言（プロトタイプ）

</td><td>関数の存在を知らせる

</td><td>`<span class="editor-theme-code">int add(int a, int b);</span>`

</td></tr><tr><td>定義

</td><td>実際の処理を書く

</td><td>`<span class="editor-theme-code">{ return a + b; }</span>`

</td></tr><tr><td>実引数／仮引数

</td><td>呼び出す値と受け取る変数

</td><td>`<span class="editor-theme-code">add(3, 5)</span>`

／

`<span class="editor-theme-code">int add(int a, int b)</span>`

</td></tr><tr><td>値渡し

</td><td>値のコピーを渡す

</td><td>元の変数は変わらない

</td></tr><tr><td>ポインタ渡し

</td><td>アドレスを渡す

</td><td>元の変数が変わる

</td></tr><tr><td>戻り値

</td><td>処理結果を返す

</td><td>`<span class="editor-theme-code">return</span>`

を使う

</td></tr><tr><td>void関数

</td><td>戻り値なしの手続き

</td><td>画面出力など

</td></tr></tbody></table>

---

## 🧠 まとめ

- 宣言＝関数の存在を伝える
- 定義＝関数の中身を書く
- 引数はコピーか参照かで挙動が変わる
- ポインタ渡しはC言語の“参照渡し”のようなもの
- 戻り値の有無で「手続き」と「関数」を区別できる

### 練習問題！

## <span style="white-space: pre-wrap;">🧮 </span>****第1問：平均点と評価を返す関数****

3科目の点数を入力し、****平均点を返す関数****と、  
その平均に応じて****評価（A〜D）を返す関数****を作れ。

```c
float getAverage(int a, int b, int c);
char getRank(float avg);
```

<span style="white-space: pre-wrap;">📘 </span>****仕様****

- `<span class="editor-theme-code">getAverage()</span>`<span style="white-space: pre-wrap;"> … 平均値を計算して返す</span>
- `<span class="editor-theme-code">getRank()</span>`<span style="white-space: pre-wrap;"> … 80点以上→A、60点以上→B、40点以上→C、それ以下→D</span>

<span style="white-space: pre-wrap;">💡 </span>**例**  
<span style="white-space: pre-wrap;">入力: </span>`<span class="editor-theme-code">80 70 90</span>`  
出力:

```
平均点 = 80.0
評価 = A
```

---

## <span style="white-space: pre-wrap;">🔄 </span>****第2問：配列の最大値と最小値を求める関数****

配列とその要素数を受け取り、最大値と最小値を****ポインタ経由で返す関数****を作れ。

```c
void getMinMax(int arr[], int n, int* min, int* max);
```

<span style="white-space: pre-wrap;">📘 </span>****仕様****

- ループで配列を走査し、`<span class="editor-theme-code">*min</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">*max</span>`<span style="white-space: pre-wrap;"> に代入</span>
- mainで表示する

<span style="white-space: pre-wrap;">💡 </span>**例**  
<span style="white-space: pre-wrap;">入力: </span>`<span class="editor-theme-code">{3, 9, 2, 5}</span>`  
<span style="white-space: pre-wrap;">出力: </span>`<span class="editor-theme-code">最大値=9, 最小値=2</span>`

### 💡 ヒント：ポインタで関数内の変数を書き換える例

```cpp
#include <iostream>
using namespace std;

void AddOne(int* x) {
    *x = *x + 1;  // *xを通して実際の変数の中身を変更する
}

int main() {
    int num = 10;
    AddOne(&num);  // numのアドレスを渡す
    cout << "num = " << num << endl;  // 出力：num = 11
}
```

<span style="white-space: pre-wrap;">👉 </span>`<span class="editor-theme-code">*x</span>`<span style="white-space: pre-wrap;"> はアドレス先の実体、</span>`<span class="editor-theme-code">&num</span>`<span style="white-space: pre-wrap;"> は「numの住所」です。</span>

---

## <span style="white-space: pre-wrap;">🔁 </span>****第3問：配列の平均値より大きい値を表示****

整数配列と要素数を引数に受け取り、  
****平均値を求めて、その平均より大きい要素だけを出力****する関数を作れ。

```c
void printAboveAverage(int arr[], int n);
```

<span style="white-space: pre-wrap;">📘 </span>****仕様****

- 関数内で平均値を計算
- 平均より大きい要素をすべて表示

<span style="white-space: pre-wrap;">💡 </span>**例**  
<span style="white-space: pre-wrap;">入力: </span>`<span class="editor-theme-code">{60, 80, 70, 90}</span>`  
出力:

```
平均値: 75.0
平均より大きい値: 80 90
```

---

## <span style="white-space: pre-wrap;">🧷 </span>****第4問：文字列中の特定文字を数える関数****

文字列と文字を受け取り、その文字がいくつ含まれているか数える関数を作れ。

```c
int countChar(char str[], char target);
```

または

```cpp
int CountChar(const string& str, char target);
```

<span style="white-space: pre-wrap;">📘 </span>****仕様****

- `<span class="editor-theme-code">for</span>`<span style="white-space: pre-wrap;">文で </span>`<span class="editor-theme-code">str[i] != '\\0'</span>`<span style="white-space: pre-wrap;"> まで走査</span>
- 一致したらカウントを増やす

<span style="white-space: pre-wrap;">💡 </span>**例**  
<span style="white-space: pre-wrap;">入力: </span>`<span class="editor-theme-code">"banana"</span>`<span style="white-space: pre-wrap;">, 文字 </span>`<span class="editor-theme-code">'a'</span>`  
<span style="white-space: pre-wrap;">出力: </span>`<span class="editor-theme-code">文字 'a' は 3 個含まれています</span>`

### 💡 ヒント：`<span class="editor-theme-code">std::string</span>`を1文字ずつ調べるには？

```cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    string word = "banana";
    for (int i = 0; i < word.size(); i++) {
        cout << word[i] << " ";  // 1文字ずつ出力
    }
    cout << endl;
}
```

<span style="white-space: pre-wrap;">👉 </span>`<span class="editor-theme-code">string</span>`<span style="white-space: pre-wrap;"> は </span>`<span class="editor-theme-code">[]</span>`<span style="white-space: pre-wrap;"> で1文字ずつアクセスできる。</span>  
<span style="white-space: pre-wrap;"> </span>`<span class="editor-theme-code">word.size()</span>`<span style="white-space: pre-wrap;"> で文字数（終端の'\\0'を除く）を取得できる。</span>

---

## <span style="white-space: pre-wrap;">🧩 </span>****第5問：関数を組み合わせて統計出力****

以下の関数を組み合わせて、5人の点数から平均・最高・最低を出力するプログラムを作れ。

```c
float getAverage(int arr[], int n);
int getMax(int arr[], int n);
int getMin(int arr[], int n);
```

<span style="white-space: pre-wrap;">📘 </span>****仕様****

- mainで配列を作り、関数を順に呼び出す
- 結果を整形して表示

<span style="white-space: pre-wrap;">💡 </span>**例**  
<span style="white-space: pre-wrap;">入力: </span>`<span class="editor-theme-code">{70, 85, 60, 90, 75}</span>`  
出力:

```
平均点: 76.0
最高点: 90
最低点: 60
```

# DirectXTK関連（Commonstateってなんぞ）

## ✅ 主な機能

<span style="white-space: pre-wrap;">CommonStates は以下のような「よくある組み合わせの状態オブジェクト」を提供します。 </span>[GitHub+1](https://github.com/microsoft/DirectXTK/wiki/CommonStates?utm_source=chatgpt.com)

- ブレンド状態（BlendState）
    - `<span class="editor-theme-code">Opaque()</span>`<span style="white-space: pre-wrap;"> : ブレンドなし（不透明）</span>
    - `<span class="editor-theme-code">AlphaBlend()</span>`<span style="white-space: pre-wrap;"> : アルファ付き（通常の透過）</span>
    - `<span class="editor-theme-code">Additive()</span>`<span style="white-space: pre-wrap;"> : 加算ブレンド（光・パーティクル用）</span>
    - `<span class="editor-theme-code">NonPremultiplied()</span>`<span style="white-space: pre-wrap;"> : プレマルチプライドでないアルファ用</span>
- 深度／ステンシル状態（DepthStencilState）
    - `<span class="editor-theme-code">DepthDefault()</span>`<span style="white-space: pre-wrap;"> : 通常の深度テスト＆書き込み</span>
    - `<span class="editor-theme-code">DepthNone()</span>`<span style="white-space: pre-wrap;"> : 深度テスト・書き込みとも無効</span>
    - `<span class="editor-theme-code">DepthRead()</span>`<span style="white-space: pre-wrap;"> : 深度確認はするが深度バッファ更新しない／透過用など</span>
    - `<span class="editor-theme-code">DepthReverseZ()</span>`／`<span class="editor-theme-code">DepthReadReverseZ()</span>`<span style="white-space: pre-wrap;"> : 反転Zバッファ使用時用</span>
- ラスタライザ状態（RasterizerState）
    - `<span class="editor-theme-code">CullNone()</span>`<span style="white-space: pre-wrap;"> : カリングなし（両面描画）</span>
    - `<span class="editor-theme-code">CullClockwise()</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">CullCounterClockwise()</span>`<span style="white-space: pre-wrap;"> : カリング方向指定</span>
    - `<span class="editor-theme-code">Wireframe()</span>`<span style="white-space: pre-wrap;"> : ワイヤーフレームモード</span>
- サンプラ状態（SamplerState）
    - `<span class="editor-theme-code">PointWrap()</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">PointClamp()</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">LinearWrap()</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">LinearClamp()</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">AnisotropicWrap()</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">AnisotropicClamp()</span>`<span style="white-space: pre-wrap;"> : テクスチャサンプラの典型</span>

---

## 🧮 使い方（例）

```cpp
std::unique_ptr<DirectX::CommonStates> states = 
    std::make_unique<DirectX::CommonStates>(device);

deviceContext->OMSetBlendState(states->AlphaBlend(), nullptr, 0xFFFFFFFF);
deviceContext->OMSetDepthStencilState(states->DepthDefault(), 0);
deviceContext->RSSetState(states->CullCounterClockwise());
auto sampler = states->LinearWrap();
deviceContext->PSSetSamplers(0, 1, &sampler);
```

<span style="white-space: pre-wrap;">このように、状態オブジェクトを毎回自作せず「典型パターン」を </span>`<span class="editor-theme-code">states</span>`<span style="white-space: pre-wrap;"> から取得して使えます。 </span>[GitHub](https://github.com/microsoft/DirectXTK/wiki/CommonStates?utm_source=chatgpt.com)

---

## 🔍 なぜ使うといいか

- 状態オブジェクト（BlendState 等）の設定を毎回細かく書くと煩雑。CommonStates によって手軽に済む。
- 状態オブジェクトを重複して生成するのを防げる。状態生成は比較的コストがあるため。
- 読みやすく、間違えにくい。例えば「透過用に深度書き込みを止める」など典型パターンが明確。
- プロジェクトが大きくなっても「この状態セットを使えばOK」という共通の仕様にできる。

---

## ⚠️ 注意すべきこと

- CommonStates が提供する「典型の状態」がすべてのケースに最適とは限らない。カスタムなラスタ／ブレンド／深度設定が必要な場合は自分で構築する必要あり。
- 状態オブジェクトを頻繁に切り替えるとパフォーマンスに影響出る可能性あり。CommonStates とは別に描画順／バッチング／状態切り替えを整理するべき。
- サンプラ状態や深度モードを変更することで、既存の描画が崩れる可能性あり（例：カリングなし＋両面描画＝意図しない裏面が見える、深度無効＝描画順が重要になる）。
- ライブラリをリンク／設定ミスすると、「CommonStates の関数が未定義（Linker error）」になることが実際に質問として出ています

<p class="callout info">よく使う設定集</p>

`<span class="editor-theme-code">states</span>`<span style="white-space: pre-wrap;"> は </span>`<span class="editor-theme-code">std::make_unique<CommonStates>(device)</span>`<span style="white-space: pre-wrap;"> 済み前提です。</span>

---

# 1) 不透明3D（デフォルト）

- 用途：普通の3Dモデル（不透明）

```cpp
ctx->OMSetBlendState(states->Opaque(), nullptr, 0xFFFFFFFF);
ctx->OMSetDepthStencilState(states->DepthDefault(), 0);
ctx->RSSetState(states->CullCounterClockwise()); // 右手系ならCCW, 左手系なら適宜
```

- メモ：最初はこれ。描画順の考慮が少なくて済む。

---

# 2) 透明（アルファブレンド）

- 用途：ガラス/フェンス/半透明UI 等

```cpp
ctx->OMSetBlendState(states->AlphaBlend(), nullptr, 0xFFFFFFFF);
ctx->OMSetDepthStencilState(states->DepthRead(), 0); // 深度テストON/書き込みOFF
ctx->RSSetState(states->CullNone()); // 両面必要なら
```

- メモ：****後ろから前****の順に描く。深度書き込みOFFがコツ。

---

# 3) 加算（パーティクル/発光）

- 用途：火花・魔法・グローっぽいもの

```cpp
ctx->OMSetBlendState(states->Additive(), nullptr, 0xFFFFFFFF);
ctx->OMSetDepthStencilState(states->DepthRead(), 0);
ctx->RSSetState(states->CullNone());
```

- メモ：重なるほど明るくなる。順番は透明同様、奥→手前。

---

# 4) 2D/UI/スプライト

- 用途：テクスチャをそのまま貼るUI/スプライト

```cpp
ctx->OMSetBlendState(states->AlphaBlend(), nullptr, 0xFFFFFFFF);
ctx->OMSetDepthStencilState(states->DepthNone(), 0);
ctx->RSSetState(states->CullNone());
auto smp = states->LinearClamp(); // UIはClampが無難
ctx->PSSetSamplers(0, 1, &smp);
```

- メモ：****DepthNone****<span style="white-space: pre-wrap;"> にしておくのが楽。拡大縮小が多いなら Linear。</span>

---

# 5) スカイボックス

- 用途：立方体の内側に空を描く

```cpp
ctx->OMSetBlendState(states->Opaque(), nullptr, 0xFFFFFFFF);
ctx->OMSetDepthStencilState(states->DepthRead(), 0); // 背景なので書かない
ctx->RSSetState(states->CullNone()); // 内側を描くため
```

- メモ：ビュー行列の平行移動成分を消すのも忘れずに。

---

# 6) デバッグ（ワイヤーフレーム）

- 用途：ジオメトリ確認

```cpp
ctx->OMSetBlendState(states->Opaque(), nullptr, 0xFFFFFFFF);
ctx->OMSetDepthStencilState(states->DepthDefault(), 0);
ctx->RSSetState(states->Wireframe());
```

---

# 7) シャドウ用（深度のみパスの基本）

- 用途：単純な深度プリパスや影マップ（※深度バイアスは別途）

```cpp
ctx->OMSetBlendState(states->Opaque(), nullptr, 0xFFFFFFFF);
ctx->OMSetDepthStencilState(states->DepthDefault(), 0);
ctx->RSSetState(states->CullCounterClockwise());
// ピクセルシェーダを外す or 深度書き込み専用PSに
```

- メモ：CommonStates には深度バイアスは含まれないので、必要なら自作 RasterizerState を用意。

---

# 8) ポストプロセス（フルスクリーン）

- 用途：トーンマップ/ブラーなど

```cpp
ctx->OMSetBlendState(states->Opaque(), nullptr, 0xFFFFFFFF);
ctx->OMSetDepthStencilState(states->DepthNone(), 0);
ctx->RSSetState(states->CullNone());
auto smp = states->LinearClamp(); // スクリーンスペースはClampが基本
ctx->PSSetSamplers(0, 1, &smp);
```

---

# 9) サンプラの目安（用途別）

- ドット絵/ピクセルパーフェクト：`<span class="editor-theme-code">PointClamp()</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">PointWrap()</span>`
- 一般/テクスチャ：`<span class="editor-theme-code">LinearWrap()</span>`<span style="white-space: pre-wrap;">（タイル） or </span>`<span class="editor-theme-code">LinearClamp()</span>`（UI/ポスト）
- 高品質リニア：`<span class="editor-theme-code">AnisotropicWrap()</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">AnisotropicClamp()</span>`（斜めに見る床など）

---

# 10) Reverse-Z を使う場合（上級）

- 用途：遠距離のZ精度改善（プロジェクションと深度比較を反転）

```cpp
ctx->OMSetDepthStencilState(states->DepthReverseZ(), 0);        // 不透明
// or 透過時
ctx->OMSetDepthStencilState(states->DepthReadReverseZ(), 0);
```

- メモ：****射影行列のZも反転****が必要（アプリ側対応）。慣れてからでOK。

---

## 使い分けのコツ（超要約）

- ****不透明は DepthDefault / Opaque****、****透過は AlphaBlend / DepthRead****
- ****UI/ポストは DepthNone / CullNone****
- ****粒子は Additive or AlphaBlend****（内容で使い分け）
- ****スカイボックスは DepthRead / CullNone****
- <span style="white-space: pre-wrap;">サンプラは </span>****UI=Clamp、タイル=Wrap、品質=Anisotropic****

# DirectXTK関連（頂点情報）

# 🎯 DirectXTK の主要な頂点構造体一覧（VertexTypes.h）

<table id="bkmrk-%E6%A7%8B%E9%80%A0%E4%BD%93%E5%90%8D%E5%90%AB%E3%81%BE%E3%82%8C%E3%82%8B%E8%A6%81%E7%B4%A0%E3%82%BB%E3%83%9E%E3%83%B3%E3%83%86%E3%82%A3%E3%82%AF%E4%B8%BB%E3%81%AA%E7%94%A8%E9%80%94"><colgroup><col style="width: 156px;"></col><col></col><col style="width: 146px;"></col><col style="width: 195px;"></col><col></col></colgroup><tbody><tr><th>構造体名

</th><th>含まれる要素

</th><th>セマンティクス

</th><th>主な用途

</th><th>備考

</th></tr><tr><td>****VertexPosition****

</td><td>位置

</td><td>`<span class="editor-theme-code">POSITION</span>`

</td><td>最小構成（点・線など）

</td><td><span style="white-space: pre-wrap;">シェーダで </span>

`<span class="editor-theme-code">SV_Position</span>`

<span style="white-space: pre-wrap;"> だけ使う場合</span>

</td></tr><tr><td>****VertexPositionColor****

</td><td>位置＋頂点色

</td><td>`<span class="editor-theme-code">POSITION</span>`

<span style="white-space: pre-wrap;">, </span>

`<span class="editor-theme-code">COLOR</span>`

</td><td>頂点ごとに色を持つラインやデバッグ描画

</td><td>2D線やワイヤーフレーム表示向き

</td></tr><tr><td>****VertexPositionTexture****

</td><td>位置＋UV

</td><td>`<span class="editor-theme-code">POSITION</span>`

<span style="white-space: pre-wrap;">, </span>

`<span class="editor-theme-code">TEXCOORD</span>`

</td><td>テクスチャ貼り付け（Quad, Billboard, Sprite3Dなど）

</td><td>✅ 最も汎用的で、現在あなたが使っている構成

</td></tr><tr><td>****VertexPositionNormal****

</td><td>位置＋法線

</td><td>`<span class="editor-theme-code">POSITION</span>`

<span style="white-space: pre-wrap;">, </span>

`<span class="editor-theme-code">NORMAL</span>`

</td><td>照明（ライティング）を行う3Dメッシュ

</td><td>影・陰影処理に使う基本形

</td></tr><tr><td>****VertexPositionNormalColor****

</td><td>位置＋法線＋頂点色

</td><td>`<span class="editor-theme-code">POSITION</span>`

<span style="white-space: pre-wrap;">, </span>

`<span class="editor-theme-code">NORMAL</span>`

<span style="white-space: pre-wrap;">, </span>

`<span class="editor-theme-code">COLOR</span>`

</td><td>頂点色と照明を併用する場合

</td><td>ゲームモデルなどでよく使われる

</td></tr><tr><td>****VertexPositionNormalTexture****

</td><td>位置＋法線＋UV

</td><td>`<span class="editor-theme-code">POSITION</span>`

<span style="white-space: pre-wrap;">, </span>

`<span class="editor-theme-code">NORMAL</span>`

<span style="white-space: pre-wrap;">, </span>

`<span class="editor-theme-code">TEXCOORD</span>`

</td><td>ライティング＋テクスチャ

</td><td>✅ モデル描画で最も一般的

</td></tr><tr><td>****VertexPositionColorTexture****

</td><td>位置＋色＋UV

</td><td>`<span class="editor-theme-code">POSITION</span>`

<span style="white-space: pre-wrap;">, </span>

`<span class="editor-theme-code">COLOR</span>`

<span style="white-space: pre-wrap;">, </span>

`<span class="editor-theme-code">TEXCOORD</span>`

</td><td>色つきUI・デカールなど

</td><td>色とテクスチャを混ぜたいとき

</td></tr><tr><td>****VertexPositionNormalTangentTexture****

</td><td>位置＋法線＋接線＋UV

</td><td>`<span class="editor-theme-code">POSITION</span>`

<span style="white-space: pre-wrap;">, </span>

`<span class="editor-theme-code">NORMAL</span>`

<span style="white-space: pre-wrap;">, </span>

`<span class="editor-theme-code">TANGENT</span>`

<span style="white-space: pre-wrap;">, </span>

`<span class="editor-theme-code">TEXCOORD</span>`

</td><td>法線マップ（ノーマルマップ）を使う高品質シェーディング

</td><td>⚙️ 高級ライティング用

</td></tr><tr><td>****VertexPositionDualTexture****

</td><td>位置＋UV×2

</td><td>`<span class="editor-theme-code">POSITION</span>`

<span style="white-space: pre-wrap;">, </span>

`<span class="editor-theme-code">TEXCOORD0</span>`

<span style="white-space: pre-wrap;">, </span>

`<span class="editor-theme-code">TEXCOORD1</span>`

</td><td>マルチテクスチャ合成

</td><td>例：ライトマップ＋ディフューズ

</td></tr></tbody></table>

---

# 🧩 それぞれの使い分け方（実例つき）

## 1️⃣ VertexPositionTexture

```cpp
VertexPositionTexture v[] = {
    { { -1,  1, 0 }, { 0, 0 } },
    { {  1,  1, 0 }, { 1, 0 } },
    { {  1, -1, 0 }, { 1, 1 } },
    { { -1, -1, 0 }, { 0, 1 } },
};
```

<span style="white-space: pre-wrap;">→ </span>****Quadや2Dスプライト****、UI表示などに最適。  
`<span class="editor-theme-code">BasicEffect</span>`<span style="white-space: pre-wrap;"> と組み合わせると、</span>`<span class="editor-theme-code">SetTextureEnabled(true)</span>`<span style="white-space: pre-wrap;"> で完結。</span>

---

## 2️⃣ VertexPositionNormalTexture

```cpp
VertexPositionNormalTexture v[] = {
    { {0,1,0}, {0,1,0}, {0,0} },
    { {1,0,0}, {0,1,0}, {1,1} },
    ...
};
```

<span style="white-space: pre-wrap;">→ </span>****ライトを当てたい3Dモデル****や FBX などで使う。  
`<span class="editor-theme-code">BasicEffect::SetLightingEnabled(true)</span>`<span style="white-space: pre-wrap;"> を使えば、方向光・点光源も一瞬で追加できる。</span>

---

## 3️⃣ VertexPositionNormalTangentTexture

```cpp
VertexPositionNormalTangentTexture v[] = {
    { pos, normal, tangent, uv },
};
```

<span style="white-space: pre-wrap;">→ </span>****ノーマルマップ対応モデル****用。  
<span style="white-space: pre-wrap;">DirectXTK の </span>`<span class="editor-theme-code">SkinnedEffect</span>`<span style="white-space: pre-wrap;"> や </span>`<span class="editor-theme-code">NormalMapEffect</span>`<span style="white-space: pre-wrap;"> と組み合わせると最短で使える。</span>

---

## 4️⃣ VertexPositionColor

```cpp
VertexPositionColor v[] = {
    { {0,0,0}, Colors::Red },
    { {1,0,0}, Colors::Green },
};
```

<span style="white-space: pre-wrap;">→ </span>****デバッグ線・グリッド表示****・ワイヤーフレーム描画など。  
`<span class="editor-theme-code">BasicEffect</span>`<span style="white-space: pre-wrap;"> の </span>`<span class="editor-theme-code">SetVertexColorEnabled(true)</span>`<span style="white-space: pre-wrap;"> をオンにすれば、色付き描画可能。</span>

---

# ⚙️ 裏側の仕組み（共通点）

DirectXTK の各頂点構造体は以下のような定義を持っています：

```cpp
struct VertexPositionTexture
{
    DirectX::XMFLOAT3 position;
    DirectX::XMFLOAT2 textureCoordinate;

    static const D3D11_INPUT_ELEMENT_DESC InputElements[2];
    static const UINT InputElementCount = 2;
};
```

`<span class="editor-theme-code">InputElements</span>`<span style="white-space: pre-wrap;"> の定義はライブラリ内にあり、</span>  
`<span class="editor-theme-code">BasicEffect</span>`<span style="white-space: pre-wrap;"> や </span>`<span class="editor-theme-code">CreateInputLayout()</span>`<span style="white-space: pre-wrap;"> にそのまま渡せます：</span>

```cpp
m_device->CreateInputLayout(
    VertexPositionTexture::InputElements,
    VertexPositionTexture::InputElementCount,
    bytecode, bytecodeLength,
    m_inputLayout.ReleaseAndGetAddressOf());
```

> 👉 自分で D3D11\_INPUT\_ELEMENT\_DESC を書かなくてよくなる！

---

# 💡 他のEffectとの対応

<table id="bkmrk-effect%E3%82%AF%E3%83%A9%E3%82%B9%E5%AF%BE%E5%BF%9C%E3%81%99%E3%82%8B%E9%A0%82%E7%82%B9%E6%A7%8B%E9%80%A0%E4%BD%93ba"><colgroup><col></col><col></col></colgroup><tbody><tr><th>Effectクラス

</th><th>対応する頂点構造体

</th></tr><tr><td>****BasicEffect****

</td><td>VertexPosition, VertexPositionColor, VertexPositionTexture, VertexPositionNormalTexture

</td></tr><tr><td>****SkinnedEffect****

</td><td>VertexPositionNormalTexture（＋ボーン情報）

</td></tr><tr><td>****NormalMapEffect****

</td><td>VertexPositionNormalTangentTexture

</td></tr><tr><td>****EnvironmentMapEffect****

</td><td>VertexPositionNormalTexture

</td></tr><tr><td>****DualTextureEffect****

</td><td>VertexPositionDualTexture

</td></tr><tr><td>****AlphaTestEffect****

</td><td>VertexPositionTexture（アルファテスト付きPS）

</td></tr></tbody></table>

> 各 Effect は自分が想定している頂点構造に合わせて内部シェーダを持っています。  
> <span style="white-space: pre-wrap;">つまり、構造体を正しく選ぶだけで </span>`<span class="editor-theme-code">InputLayout</span>`<span style="white-space: pre-wrap;"> とシェーダの整合性が取れる。</span>

---

# 🧭 まとめ

<table id="bkmrk-%E7%9B%AE%E7%9A%84%E9%A0%82%E7%82%B9%E6%A7%8B%E9%80%A0%E4%BD%93%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B-effect%E3%82%B7%E3%83%B3"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th>目的

</th><th>頂点構造体

</th><th>使用する Effect

</th></tr><tr><td>シンプルなテクスチャ付き板ポリ

</td><td>VertexPositionTexture

</td><td>BasicEffect

</td></tr><tr><td>光を当てたい3Dモデル

</td><td>VertexPositionNormalTexture

</td><td>BasicEffect

</td></tr><tr><td>ノーマルマップ付き高品質モデル

</td><td>VertexPositionNormalTangentTexture

</td><td>NormalMapEffect

</td></tr><tr><td>色付きライン・ワイヤー

</td><td>VertexPositionColor

</td><td>BasicEffect

</td></tr><tr><td>2枚テクスチャ合成

</td><td>VertexPositionDualTexture

</td><td>DualTextureEffect

</td></tr></tbody></table>

---

# FBXとufbxで座標合わせをしたときのメモ（どうせUVのときまた頭おかしくなる用）

# 結論（このリポの現状）

<div class="flex max-w-full flex-col grow" id="bkmrk-%E5%BA%A7%E6%A8%99%E7%B3%BB%EF%BC%9Afbx%E8%AA%AD%E8%BE%BC%E6%99%82%E3%81%AB-%E5%8F%B3%E6%89%8B%E7%B3%BB%E3%83%BBy-up"><div class="min-h-8 text-message relative flex w-full flex-col items-end gap-2 text-start break-words whitespace-normal [.text-message+&]:mt-1" data-message-author-role="assistant" data-message-id="6bebac39-1450-47f6-8779-9483f851dee2" data-message-model-slug="gpt-5-thinking" dir="auto"><div class="flex w-full flex-col gap-1 empty:hidden first:pt-[1px]"><div class="markdown prose dark:prose-invert w-full break-words light markdown-new-styling">- **座標系**：FBX読込時に **右手系・Y up** へ正規化してからロードしています（`opts.target_axes = ufbx_axes_right_handed_y_up`）。描画側も **RH の LookAt / PerspectiveFov** を使っているため、**手系・Up軸は一致**しています。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
    
    UfbxAnimSkeleton
    
    <button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "></button> <span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
    
    D3DApp
- **行列の積み上げ**：`EvaluateAt()` で **local を親の world に掛ける（W = L \* Pw）** という標準的な階層合成を行い、かつ **Skin = W \* InvBind** でスキン行列を作っています。FBX特有の pre/post-rotation は `ufbx_evaluate_transform()` が面倒を見てくれるので、**アニメの動きはFBXと同一**になります。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
    
    UfbxAnimSkeleton
- **行列レイアウトの差**：`ufbx_matrix (4x3)` を **DirectX の `XMFLOAT4X4`（行メジャ/平行移動は \_41/\_42/\_43）** に詰め替えており、**転置＋配置は関数で一貫**させています。可視化はこの `_41/_42/_43` を使って正しくワールド位置を取っています。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
    
    UfbxAnimSkeleton
    
    <button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "></button> <span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
    
    BoneVisualizer
- **カメラ**：ボーンのワールド行列群から **中心と半径を推定 → その球が画面に“ちょうど入る”距離に RH ビュー＆プロジェクションを自動計算**。画角・距離が素直な値なので、**モデリングビュー相当の見え方**になります（もちろんツールのプリセットFOVと同じにすればより一致）。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
    
    WinMain
    
    <button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "></button> <span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
    
    WinMain
- **タイトル/FPS**：Unicode タイトル更新や 60fps リミッタは描画の見え方に影響しませんが、WAPI での文字化け対策含め堅牢です。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
    
    D3DApp

</div></div></div></div># 同じ見え方にするためのチェックリスト

<div class="flex max-w-full flex-col grow" id="bkmrk-%E6%89%8B%E7%B3%BB%2Fup%E8%BB%B8%E3%81%AE%E4%B8%80%E8%87%B4-%E3%81%99%E3%81%A7%E3%81%AB-fbx-%E3%83%AD%E3%83%BC"><div class="min-h-8 text-message relative flex w-full flex-col items-end gap-2 text-start break-words whitespace-normal [.text-message+&]:mt-1" data-message-author-role="assistant" data-message-id="6bebac39-1450-47f6-8779-9483f851dee2" data-message-model-slug="gpt-5-thinking" dir="auto"><div class="flex w-full flex-col gap-1 empty:hidden first:pt-[1px]"><div class="markdown prose dark:prose-invert w-full break-words light markdown-new-styling">1. **手系/Up軸の一致**
    
    
    - すでに FBX ロードで **RH, Y-up** へ正規化済み。描画も RH を使用。→ OK。  
        （`opts.target_axes = ufbx_axes_right_handed_y_up` / `XMMatrixLookAtRH` &amp; `XMMatrixPerspectiveFovRH`）<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
        
        UfbxAnimSkeleton
        
        <button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "></button> <span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
        
        D3DApp
2. **行列の詰め替え規約**
    
    
    - `ToDX_4x4_FromUfbxMatrix4x3()` で **回転・並進を DirectX のフィールド配置に揃える**。可視化側は `_41/_42/_43` から位置を取得。→ OK。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
        
        UfbxAnimSkeleton
        
        <button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "></button> <span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
        
        BoneVisualizer
3. **アニメ評価の一貫性**
    
    
    - `ufbx_evaluate_transform()` を local として取り、**親子で掛け上げ**て world を得る。FBXの pre/post 回りは ufbx が吸収。→ OK。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
        
        UfbxAnimSkeleton
4. **カメラ/FOV**
    
    
    - ツール側の FOV と合わせると「同じ見え」。コードは **対象を包む球で距離計算**して RH の VP を生成。ツール側の FOV が 45°なら `XM_PIDIV4` と一致させる。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
        
        WinMain
        
        <button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "></button> <span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
        
        WinMain
5. **クリッピング／遠近**
    
    
    - `zn` は距離に応じて 0.5 以上、`zf` は余裕を持たせて設定。`D3DApp` のリサイズ時も `far=10000`。極端に大きい/小さいモデルでも破綻しにくい。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
        
        WinMain
        
        <button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "></button> <span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
        
        D3DApp
6. **単位（スケール）**
    
    
    - いまのコードは **単位スケールを特別扱いしていません**（メートル/センチの換算はしない）。ただし **カメラ自動フィット**があるので見た目は実用的に問題なし。モデル同士の相対スケール厳密一致が必要なら、UFbxの `scale_factor` 等を使うスケーリング導入を検討（今は未使用）。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
        
        ufbx
7. **Z反転の重複適用に注意**
    
    
    - ルートに **FlipZ を掛けるコードはコメントアウト**のまま。`target_axes` で RH 化しているので **追加の反転は不要**。もしモデルが「鏡像」に見えたら、**反転の二重適用がないか**をまず疑う。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
        
        UfbxAnimSkeleton
8. **可視化の基準**
    
    
    - ボーンラインは **親→子の `_41/_42/_43` の位置を結ぶ**だけなので、座標が正しければ「骨の形」は一致します。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
        
        BoneVisualizer

</div></div></div></div># ありがちな“ズレ”と対処

<div class="flex max-w-full flex-col grow" id="bkmrk-%E5%B7%A6%E5%8F%B3%E5%8F%8D%E8%BB%A2%2F%E9%8F%A1%E5%83%8F%E3%81%AB%E8%A6%8B%E3%81%88%E3%82%8B%E2%86%92-%E3%81%A9%E3%81%93%E3%81%8B%E3%81%A7-%E6%89%8B%E7%B3%BB"><div class="min-h-8 text-message relative flex w-full flex-col items-end gap-2 text-start break-words whitespace-normal [.text-message+&]:mt-1" data-message-author-role="assistant" data-message-id="6bebac39-1450-47f6-8779-9483f851dee2" data-message-model-slug="gpt-5-thinking" dir="auto"><div class="flex w-full flex-col gap-1 empty:hidden first:pt-[1px]"><div class="markdown prose dark:prose-invert w-full break-words light markdown-new-styling">- **左右反転/鏡像に見える**  
    → どこかで **手系変換＋Z反転を二重**にしていないかチェック。今のコードでは `target_axes` が RH 化を担うので **FlipZ を ON にしない**のが正解。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
    
    UfbxAnimSkeleton
- **大きすぎ/小さすぎで消える**  
    → `FitCameraToBounds()` は **半径から距離を幾何学的に決定**しているため、初期表示は安定。遠近クリップは `0.1～10000` なので、通常用途は問題なし。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
    
    WinMain
    
    <button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "></button> <span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
    
    D3DApp
- **モデリングツールの FOV と違う**  
    → ツール側の FOV に合わせて `XM_PIDIV4` の値を変更すれば一致が近づきます。<span class="" data-state="closed"><span class="relative inline-flex items-center"><button class="ms-1 flex h-[25px] text-[10px] leading-[13px] rounded-xl items-center justify-center gap-1 px-2 relative text-token-text-secondary! hover:text-token-text-primary! hover:bg-token-bg-secondary dark:bg-token-main-surface-secondary dark:hover:bg-token-bg-secondary bg-[#f4f4f4] "><svg alt="ファイル" class="h-[16px] w-[16px] object-contain text-token-text-primary! flex-none" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.2598 2.25191C11.8396 2.25191 12.2381 2.24808 12.6201 2.33981L12.8594 2.40719C13.0957 2.48399 13.3228 2.5886 13.5352 2.71871L13.6582 2.79879C13.9416 2.99641 14.1998 3.25938 14.5586 3.61813L15.5488 4.60836L15.833 4.89449C16.0955 5.16136 16.2943 5.38072 16.4482 5.6318L16.5703 5.84957C16.6829 6.07074 16.7691 6.30495 16.8271 6.54684L16.8574 6.69137C16.918 7.0314 16.915 7.39998 16.915 7.90719V13.0839C16.915 13.7728 16.9157 14.3301 16.8789 14.7802C16.8461 15.1808 16.781 15.5417 16.6367 15.8779L16.5703 16.0205C16.3049 16.5413 15.9008 16.9772 15.4053 17.2812L15.1865 17.4033C14.8099 17.5951 14.4041 17.6745 13.9463 17.7119C13.4961 17.7487 12.9391 17.749 12.25 17.749H7.75C7.06092 17.749 6.50395 17.7487 6.05371 17.7119C5.65317 17.6791 5.29227 17.6148 4.95606 17.4707L4.81348 17.4033C4.29235 17.1378 3.85586 16.7341 3.55176 16.2382L3.42969 16.0205C3.23787 15.6439 3.15854 15.2379 3.12109 14.7802C3.08432 14.3301 3.08496 13.7728 3.08496 13.0839V6.91695C3.08496 6.228 3.08433 5.67086 3.12109 5.22066C3.1585 4.76296 3.23797 4.35698 3.42969 3.98043C3.73311 3.38494 4.218 2.90008 4.81348 2.59664C5.19009 2.40484 5.59593 2.32546 6.05371 2.28805C6.50395 2.25126 7.06091 2.25191 7.75 2.25191H11.2598ZM7.75 3.58199C7.03896 3.58199 6.54563 3.58288 6.16211 3.61422C5.78642 3.64492 5.575 3.70168 5.41699 3.78219C5.0718 3.95811 4.79114 4.23874 4.61524 4.58395C4.53479 4.74193 4.47795 4.95354 4.44727 5.32906C4.41595 5.71254 4.41504 6.20609 4.41504 6.91695V13.0839C4.41504 13.7947 4.41594 14.2884 4.44727 14.6718C4.47798 15.0472 4.53477 15.259 4.61524 15.417L4.68555 15.5429C4.86186 15.8304 5.11487 16.0648 5.41699 16.2187L5.54688 16.2744C5.69065 16.3258 5.88016 16.3636 6.16211 16.3867C6.54563 16.418 7.03898 16.4189 7.75 16.4189H12.25C12.961 16.4189 13.4544 16.418 13.8379 16.3867C14.2135 16.356 14.425 16.2992 14.583 16.2187L14.709 16.1474C14.9963 15.9712 15.2308 15.7189 15.3848 15.417L15.4414 15.2861C15.4927 15.1425 15.5297 14.953 15.5527 14.6718C15.5841 14.2884 15.585 13.7947 15.585 13.0839V8.55758L13.3506 8.30953C12.2572 8.18804 11.3976 7.31827 11.2881 6.22359L11.0234 3.58199H7.75ZM12.6113 6.09176C12.6584 6.56193 13.0275 6.93498 13.4971 6.98727L15.5762 7.21871C15.5727 7.13752 15.5686 7.07109 15.5615 7.01266L15.5342 6.85738C15.5005 6.7171 15.4501 6.58135 15.3848 6.45309L15.3145 6.32711C15.2625 6.24233 15.1995 6.16135 15.0928 6.04488L14.6084 5.54879L13.6182 4.55856C13.2769 4.21733 13.1049 4.04904 12.9688 3.94234L12.8398 3.8525C12.7167 3.77705 12.5853 3.71637 12.4482 3.67184L12.3672 3.6484L12.6113 6.09176Z" fill-rule="evenodd"></path></svg></button></span></span>
    
    D3DApp

---

</div></div></div></div>## まとめ

<div class="flex max-w-full flex-col grow" id="bkmrk-fbx%E8%AA%AD%E8%BE%BC%E6%99%82%E3%81%AB-rh%2Fy-up-%E3%81%B8%E6%AD%A3%E8%A6%8F%E5%8C%96"><div class="min-h-8 text-message relative flex w-full flex-col items-end gap-2 text-start break-words whitespace-normal [.text-message+&]:mt-1" data-message-author-role="assistant" data-message-id="6bebac39-1450-47f6-8779-9483f851dee2" data-message-model-slug="gpt-5-thinking" dir="auto"><div class="flex w-full flex-col gap-1 empty:hidden first:pt-[1px]"><div class="markdown prose dark:prose-invert w-full break-words light markdown-new-styling">- **FBX読込時に RH/Y-up へ正規化**し、**DirectX 側も RH の VP**、**行列の詰め替えも統一**、**アニメ評価は ufbx に準拠**しているので、**理屈上はモデリングソフトの見えと一致**します。
- 残る実務的な差異は **FOV/単位スケール/反転の二重適用**。この3点を上のチェックリスト通りに押さえれば、**同じ見え方**に落ち着きます。

</div></div></div></div><div class="relative h-[60px]" id="bkmrk-"><div class="absolute h-[60px]"><div class="text-token-text-secondary flex items-center gap-0.5 text-xs font-semibold">  
</div></div></div>

# クソIT系

### 💻 「IT系」と言われる仕事の実態

日本の専門学校や一般企業が「IT系」と呼ぶ職種は、大きく次のように分かれます：CS=Computer Science

<div class="_tableContainer_1rjym_1" id="bkmrk-%E5%88%86%E9%A1%9E-%E4%BB%95%E4%BA%8B%E5%86%85%E5%AE%B9-%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF%E3%82%B5%E3%82%A4%E3%82%A8%E3%83%B3%E3%82%B9%E3%81%A8"><div class="group _tableWrapper_1rjym_13 flex w-fit flex-col-reverse" tabindex="-1"><table class="w-fit min-w-(--thread-content-width)" data-end="665" data-start="162"><thead data-end="193" data-start="162"><tr data-end="193" data-start="162"><th data-col-size="sm" data-end="167" data-start="162">分類</th><th data-col-size="md" data-end="174" data-start="167">仕事内容</th><th data-col-size="sm" data-end="193" data-start="174">コンピュータサイエンスとの関係</th></tr></thead><tbody data-end="665" data-start="249"><tr data-end="355" data-start="249"><td data-col-size="sm" data-end="274" data-start="249">**① システムエンジニア（SIer系）**</td><td data-col-size="md" data-end="321" data-start="274">顧客の業務システムを設計・構築・納入する。要件定義、設計書作成、発注、ベンダー管理など。</td><td data-col-size="sm" data-end="355" data-start="321">理論よりも**業務知識・調整力中心**。CSとは距離がある。</td></tr><tr data-end="433" data-start="356"><td data-col-size="sm" data-end="377" data-start="356">**② プログラマ（下請け開発）**</td><td data-col-size="md" data-end="395" data-start="377">上流設計に基づきコードを書く。</td><td data-col-size="sm" data-end="433" data-start="395">一部アルゴリズム理解が必要だが、**工学というより技能職**に近い。</td></tr><tr data-end="516" data-start="434"><td data-col-size="sm" data-end="455" data-start="434">**③ インフラ・ネットワーク職**</td><td data-col-size="md" data-end="475" data-start="455">サーバやネットワークの構築・保守。</td><td data-col-size="sm" data-end="516" data-start="475">OS・通信・セキュリティの実務知識中心。理論的なネットワーク科学とは別物。</td></tr><tr data-end="590" data-start="517"><td data-col-size="sm" data-end="537" data-start="517">**④ ITコンサル／導入支援**</td><td data-col-size="md" data-end="561" data-start="537">業務効率化やクラウド導入の提案、運用支援。</td><td data-col-size="sm" data-end="590" data-start="561">**テクノロジー理解より業務コンサル色が強い**。</td></tr><tr data-end="665" data-start="591"><td data-col-size="sm" data-end="616" data-start="591">**⑤ 企業の情報システム部門（情シス）**</td><td data-col-size="md" data-end="635" data-start="616">社内システムの選定・調達・保守。</td><td data-col-size="sm" data-end="665" data-start="635">**CSとは無縁**。購買・ヘルプデスク業務も多い。</td></tr></tbody></table>

</div></div>---

### 🧠 一方の「コンピュータサイエンス」とは

CSは「情報処理の原理」そのものを研究・応用する分野で、主に以下のような内容を含みます。

- アルゴリズムと計算量理論
- データ構造・オートマトン理論
- オペレーティングシステム・コンパイラ
- グラフィックス・AI・機械学習
- 分散システム・ネットワーク理論

これらは\*\*「技術を創る側」\*\*の知識体系であり、実務で言う「ITを使う／導入する側」とは明確に異なります。

---

### 🏢 なぜ専門学校の「IT系」と乖離しているのか

いくつか理由があります：

1. **就職先がCSを必要としない業界が多い**（SIer・受託開発・情シスなど）
2. **教育課程が企業の即戦力（Word/Excel/資格）寄りに設計されている**
3. **「理論よりスキル（ツール操作）」の需要が高い**
4. **日本の「IT産業」が技術開発よりも“システム納入業”中心に発展した歴史**

つまり、日本では「IT＝情報システム導入業界」という構造的な事情があり、  
「CS＝情報科学的探求」とは**異なる職能文化**が形成されています。

---

### 🎯 まとめ

- 日本で「IT系」と呼ばれる仕事の多くは、**CS（理論・開発）ではなく、運用・導入・調整業務**。
- コンピュータサイエンス的な職種は、**研究開発・プロダクト開発・ゲーム開発・AI分野**など、少数派。
- 専門学校の「IT教育」は、どちらかといえば\*\*職業訓練（実務対応）\*\*寄りで、CS的素養を育てるものではない。

# ドラクエ風「商人クラス」で学ぶ getter / setter / public / private

# 🎮 授業進行カンペ＋教材一体版

## ドラクエ風「商人クラス」で学ぶ getter / setter / public / private

---

## 🧭 授業概要

- 対象：専門学校1年（C++基礎中盤）
- 時間：45分 × 1コマ
- 前提：クラス宣言・メンバ関数は既習
- 目的：getter/setter・public/private の基本構造と目的を理解する
- 進行：
    
    
    1. バグる商人（問題提起）
    2. 安全な商人（getter/setter導入）
    3. 演習問題（小課題＋全体レビュー）

---

## 🧾 提示する資料（学生閲覧用 DokuWiki ページ）

BookStackからリンクまたはQRで提示。学生側にはDokuWiki版をコピー配布。

**ページタイトル:**

> 🧭 ドラクエ風「商人クラス」で学ぶ getter / setter / public / private

**構成:**

- バグる商人コード（直接代入で壊れる例）
- 安全な商人コード（getter/setter＋private）
- 解説表：public／private／getter／setter
- 練習問題5問
- チェックポイント

**提示タイミング:**

- 導入5分で「このページを開いてください」
- 教員は1つずつ実行例を見せながら口頭解説

---

## 🧩 授業進行

<table id="bkmrk-%E6%99%82%E9%96%93-%E5%86%85%E5%AE%B9-%E6%95%99%E5%93%A1%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3-0%E3%80%9C5%E5%88%86-%E5%B0%8E"><thead><tr><th>時間</th><th>内容</th><th>教員アクション</th></tr></thead><tbody><tr><td>0〜5分</td><td>導入：商人バグ実行</td><td>「やくそう−100G」など笑いを取りながら興味づけ</td></tr><tr><td>5〜15分</td><td>getter/setterの考え方</td><td>「値の出入口を関数で管理」と板書</td></tr><tr><td>15〜25分</td><td>安全な商人コード実行</td><td>private→public→setterの流れを追う</td></tr><tr><td>25〜35分</td><td>演習問題</td><td>1〜4を個人、5はグループ討論</td></tr><tr><td>35〜40分</td><td>全体レビュー</td><td>setterの制限・public/privateを再確認</td></tr><tr><td>40〜45分</td><td>まとめ＋次回予告</td><td>「次は勇者(Player)と取引します」</td></tr></tbody></table>

---

## 💬 教員トーク例

> 「この商人、在庫2個で始めたのに、勇者が直接 `stock=9999` って書いたらどうなる？」  
> 「マイナス価格にしたら勇者が儲かる！ これはバグ。  
> だから金庫（private）に鍵をかけて、店員（getter/setter）を通すんです。」

---

## 💻 教示コード（投影または配布用）

### ① バグる商人（問題提起）

```cpp
#include <iostream>
#include <string>
using namespace std;

class Merchant {
public:
    string itemName;
    int price;
    int stock;

    void Sell() {
        if (stock > 0) {
            stock--;
            cout << itemName << "を" << price << "Gで売った！" << endl;
        } else {
            cout << "在庫がない！" << endl;
        }
    }
};

int main() {
    Merchant m;
    m.itemName = "やくそう";
    m.price = 8;
    m.stock = 2;

    m.Sell();
    m.Sell();
    m.Sell();

    m.stock = 9999; // ← 不正操作
    m.price = -100; // ← マイナス価格！

    m.Sell();
}

```

---

### ② 安全な商人（getter/setter + private）

```cpp
#include <iostream>
#include <string>
using namespace std;

class Merchant {
private:
    string itemName;
    int price;
    int stock;

public:
    void SetItemName(string name) { itemName = name; }

    void SetPrice(int value) {
        if (value < 0) value = 0;
        if (value > 9999) value = 9999;
        price = value;
    }

    void SetStock(int value) {
        if (value < 0) value = 0;
        stock = value;
    }

    string GetItemName() { return itemName; }
    int GetPrice() { return price; }
    int GetStock() { return stock; }

    void Sell() {
        if (stock > 0) {
            stock--;
            cout << itemName << "を" << price << "Gで売った！" << endl;
        } else {
            cout << "在庫がない！" << endl;
        }
    }
};

int main() {
    Merchant m;
    m.SetItemName("やくそう");
    m.SetPrice(8);
    m.SetStock(2);

    m.Sell();
    m.Sell();
    m.Sell(); // 在庫がない！

    // m.stock = 9999; ← エラー
    // m.price = -100; ← エラー
}

```

---

## 🧩 授業内演習問題（提示用）

### Q1

商人クラスに「商品名」「価格」「在庫」を **private** で定義し、  
それぞれに `Set`／`Get` 関数を作成せよ。

### Q2

`SetPrice()` で価格が **0未満なら0**、**9999より大きければ9999** に制限せよ。

### Q3

`SetStock()` で在庫が **マイナスのとき0に補正** されるようにせよ。

### Q4

`main()`関数で商人を作成し、やくそうを2個販売するコードを書け。  
（3回目の販売時には「在庫がない！」が出力されること）

### Q5（発展）

新しいメンバ関数 `ShowStatus()` を追加し、  
「商品名・価格・在庫」を1行で表示せよ。

---

## 💡 教員用解説・ポイント

<table id="bkmrk-%E5%95%8F%E9%A1%8C-%E3%81%AD%E3%82%89%E3%81%84-%E6%8C%87%E5%B0%8E%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88-%E6%A8%A1%E7%AF%84%E3%82%B3%E3%83%BC%E3%83%89%E6%8A%9C"><thead><tr><th>問題</th><th>ねらい</th><th>指導ポイント</th><th>模範コード抜粋</th></tr></thead><tbody><tr><td>Q1</td><td>getter/setter の型と戻り値の対応</td><td>`void SetXxx(T)` と `T GetXxx()` の対称性を意識</td><td>`void SetItemName(string n){itemName=n;}`</td></tr><tr><td>Q2</td><td>setter での値検証</td><td>if文で範囲制限。固定値ではなく変数を渡す形を見せる</td><td>`if(value<0)value=0;`</td></tr><tr><td>Q3</td><td>状態の整合性保持</td><td>在庫は負数で意味を持たない → 0で丸める</td><td>`if(value<0)value=0;`</td></tr><tr><td>Q4</td><td>実行結果の確認</td><td>出力順に注目。「在庫がない！」が出るまでSell()</td><td>`m.Sell();m.Sell();m.Sell();`</td></tr><tr><td>Q5</td><td>状態表示</td><td>デバッグ・確認に便利。getterの活用を実感させる</td><td>`cout<<"商品:"<<itemName<<" 在庫:"<<stock<<endl;`</td></tr></tbody></table>

---

## ✅ チェックリスト

- [ ]  [ ]  setterで値をチェックできているか
- [ ]  [ ]  private変数を直接触っていないか
- [ ]  [ ]  出力が想定通りか
- [ ]  [ ]  コードに不正アクセス部分（コメントで残してある）が理解できているか

---

## 🧩 発展予告（次回）

- Playerクラスを導入し、`SellTo(Player&)`で取引処理
- has-a関係・参照渡しの導入
- 複数商品対応（vector）へ展開

---

## 🎬 まとめコメント例（授業締め）

> 「今日の目的は“商人の金庫に鍵をかける”でした。  
> 直接代入では壊れる。関数を通せば守れる。  
> getter/setter は、プログラム世界のルールブックです。」

# FBX読み込み処理

## 全体構造（ロード〜描画の流れ）

ざっくりした流れはこうなっています。

```text
[FBXファイル]
    │  (ufbxでロード済み)
    ▼
[ufbx_scene]
    │
    ├─ ExpandAllNodes(scene)   … CPU側メッシュ展開・スキン情報抽出・AABB計算
    │
    ├─ CreateGpuBuffers()      … VB / IB 作成
    │
    └─ CreateEffectsAndTextures(fbx_path, scene)
         └─ テクスチャや BasicEffect の初期化

(ここまでがロード時)

描画時:
    └─ Draw(world, view, proj)
        ├─ CPU スキニング (必要なら)
        ├─ VB を UpdateSubresource で更新
        └─ IA 設定 → BasicEffect → DrawIndexed
```

`<span class="editor-theme-code">UfbxStaticModel</span>`<span style="white-space: pre-wrap;"> のメッシュ関係で主役になるフィールドは（名前だけ）：</span>

```text
mesh_.vertices_        : VertexPNT2 の配列（展開済み頂点）
mesh_.indices_         : uint32_t の配列（三角形インデックス）
mesh_.parts_           : MeshPart の配列（マテリアルごとのサブメッシュ）
mesh_.influences_      : VertexInfluence の配列（頂点ごとのボーン情報）
mesh_.bind_vertices_   : バインドポーズの頂点（＝元の姿）
mesh_.skinned_vertices_: スキニング後の頂点（CPUスキニング結果）
draw_.vb_ / draw_.ib_  : D3D11のVB/IB
draw_.fx_              : BasicEffect
draw_.states_          : CommonStates
draw_.layout_          : 入力レイアウト
```

---

## データ構造イメージ

### 頂点とインデックス

```text
VertexPNT2:
    pos : float3   (頂点位置)
    nrm : float3   (法線)
    uv  : float2   (テクスチャ座標)

頂点バッファ (mesh_.vertices_)
    index: 0   1   2   3   4   5   ...
           +---+---+---+---+---+---+
           | V | V | V | V | V | V |
           +---+---+---+---+---+---+

インデックスバッファ (mesh_.indices_)
    0,1,2 / 3,4,5 / ...
    → TRIANGLELIST で描画
```

### 頂点ごとのボーン影響 VertexInfluence

```text
VertexInfluence:
    uint16_t bone[4];
    float    weight[4];

1頂点 = 最大4本のボーンが影響する
```

```
mesh_.influences_[v]  ← 頂点 v に対するボーン情報
```

### MeshPart（サブメッシュ）

```text
MeshPart:
    const ufbx_material* mat;  // 元FBXのマテリアル
    uint32_t start_index;      // mesh_.indices_ のどこから
    uint32_t index_count;      // 何個インデックスを描くか
    ComPtr<ID3D11ShaderResourceView> srv; // diffuse テクスチャ
```

概念図：

```text
mesh_.indices_:
    [0] [1] [2] [3] [4] [5] [6] [7] ...

MeshPart 0:
    start_index = 0
    index_count = 300

MeshPart 1:
    start_index = 300
    index_count = 150
```

---

## ExpandAllNodes(scene): メッシュ展開

### 1. 初期化

```cpp
mesh_.vertices_.clear();
mesh_.indices_.clear();
mesh_.parts_.clear();
mesh_.influences_.clear();
mesh_.bind_vertices_.clear();
mesh_.skinned_vertices_.clear();
```

AABB 用の min/max も初期化。

```text
bb_min = ( +∞, +∞, +∞ )
bb_max = ( -∞, -∞, -∞ )
```

---

### 2. シーン中の全ノードを走査

```cpp
for each node in scene->nodes:
    mesh = node->mesh
    if !mesh: continue
    …
```

イメージ：

```text
ufbx_scene
    ├─ node0 (meshなし)
    ├─ node1 (mesh A)
    ├─ node2 (mesh B)
    └─ ...
```

`<span class="editor-theme-code">mesh</span>`<span style="white-space: pre-wrap;"> を持っているノードだけメッシュ展開の対象にします。</span>

---

### 3. スキンウェイト抽出 (infl\_per\_vtx)

メッシュがスキンを持っている場合：

```cpp
infl_per_vtx.resize(mesh->num_vertices);
skin = mesh->skin_deformers.data[0];
clusters = skin->clusters;
vtx_list = skin->vertices;
w_list   = skin->weights;
```

ここでやっていること：

```text
FBXのデータ:
    - skin->vertices  : 各頂点に対して "どの weight が付いているか" の範囲
    - skin->weights   : (cluster_index, weight) の配列
    - clusters        : cluster_index → どのボーン(node)か
```

アルゴリズム（概念）：

```text
for each vertex v:
    acc[v] = 空のリスト

for each "skinned vertex" sv in vtx_list:
    v = sv.vertex（実際の頂点番号）
    for k in その頂点に対応する全weight:
        w = weights[weight_begin + k]
        ci = w.cluster_index
        cl = clusters[ci]
        bone_node = cl->bone_node

        // スケルトン側に登録済みならボーン番号を取得
        bone_index = skeleton_.Data().bone_index_of_[bone_node]

        acc[v].push_back( (bone_index, weight) )
```

図にすると：

```text
+---------------------------+
          | ufbx_skin_deformer (skin) |
          +---------------------------+
                | clusters[]        | vertices[] / weights[]
                v                   v
        [ cluster0 ] → bone_node0   vertex0 → (ci, weight)...
        [ cluster1 ] → bone_node1   vertex1 → ...
                |
                └─ bone_node をキーに
                   skeleton_.Data().bone_index_of_ を引く
```

その後、各頂点について

1. `<span class="editor-theme-code">acc[v]</span>`<span style="white-space: pre-wrap;"> を「weight の大きい順にソート」</span>
2. 上位 4 本に絞る
3. 重みを正規化（合計 ≒ 1.0）
4. `<span class="editor-theme-code">VertexInfluence</span>`<span style="white-space: pre-wrap;"> に詰めて </span>`<span class="editor-theme-code">infl_per_vtx[v]</span>`<span style="white-space: pre-wrap;"> に保存</span>

---

### 4. UV セットの決定

```cpp
const ufbx_vertex_vec2* base_uv = nullptr;
if (mesh->vertex_uv.exists) base_uv = &mesh->vertex_uv;
else if (mesh->uv_sets.count > 0 && mesh->uv_sets.data[0].vertex_uv.exists)
    base_uv = &mesh->uv_sets.data[0].vertex_uv;
```

あとでテクスチャとUVセットの名前を見て、`<span class="editor-theme-code">ResolveUVByName()</span>`<span style="white-space: pre-wrap;"> で差し替えもします。</span>

---

### 5. フェイスをマテリアルごとにグループ化

```cpp
std::unordered_map<uint32_t, std::vector<uint32_t>> faces_by_mat;

for each face fi in mesh->faces:
    mi = (mesh->face_material.count > 0) ? mesh->face_material.data[fi] : 0;
    faces_by_mat[mi].push_back(fi);
```

概念：

```text
faces_by_mat:
    mat_index 0 → [faceID 1, 5, 8, ...]
    mat_index 1 → [faceID 0, 2, 3, ...]
```

これを後で MeshPart ごとに展開します。

---

### 6. マテリアルごとに MeshPart 作成＆頂点展開

```cpp
for each (mat_index, face_list) in faces_by_mat:
    MeshPart part;
    part.start_index = mesh_.indices_.size();

    // node / mesh から ufbx_material* を引く
    if (node->materials.count > mat_index) part.mat = node->materials.data[mat_index];
    else if (mesh->materials.count > mat_index) part.mat = mesh->materials.data[mat_index];
```

UV セットの最終決定：

```cpp
tex_for_uv = GetDiffuseTexture(part.mat);
uvv = base_uv;
if (tex_for_uv && tex_for_uv->uv_set.length > 0)
    uvv = ResolveUVByName(mesh, tex_for_uv->uv_set);
```

その後、`<span class="editor-theme-code">face_list</span>`<span style="white-space: pre-wrap;"> の各フェイスを「扇形分割」で三角形にする：</span>

```cpp
for each face f in face_list:
    indices: f.index_begin ... f.index_begin + f.num_indices - 1

    for k = 0 .. (f.num_indices - 3):
        corners[0] = f.index_begin + 0;
        corners[1] = f.index_begin + (k+1);
        corners[2] = f.index_begin + (k+2);
        → この3つで1三角形
```

各 corner について

- <span style="white-space: pre-wrap;">頂点番号 </span>`<span class="editor-theme-code">vtx = mesh->vertex_indices[corner]</span>`
- <span style="white-space: pre-wrap;">位置 </span>`<span class="editor-theme-code">P</span>`：`<span class="editor-theme-code">mesh->vertex_position</span>`<span style="white-space: pre-wrap;"> から取得</span>
- <span style="white-space: pre-wrap;">法線 </span>`<span class="editor-theme-code">N</span>`：`<span class="editor-theme-code">mesh->vertex_normal</span>`<span style="white-space: pre-wrap;"> から取得＋正規化</span>
- <span style="white-space: pre-wrap;">UV </span>`<span class="editor-theme-code">T</span>`<span style="white-space: pre-wrap;">：決定済み </span>`<span class="editor-theme-code">uvv</span>`<span style="white-space: pre-wrap;"> から取得し、</span>`<span class="editor-theme-code">T.y = 1 - v</span>`

そして

```cpp
VertexPNT2 vtx_out = { P, N, T };

VertexInfluence vi;
if (!infl_per_vtx.empty())
    vi = infl_per_vtx[vtx];

mesh_.influences_.push_back(vi);
mesh_.indices_.push_back( mesh_.vertices_.size() );
mesh_.vertices_.push_back( vtx_out );
```

図示すると：

```text
FBX mesh
    ├─ faces[]
    │    ├─ face0 (n角形)
    │    └─ face1 ...
    ├─ vertex_position
    ├─ vertex_normal
    └─ vertex_uv / uv_sets

      ▼ 展開

mesh_.vertices_  (VertexPNT2)
mesh_.indices_   (三角形リスト)
mesh_.influences_(VertexInfluence)
mesh_.parts_     (マテリアル単位)
```

<span style="white-space: pre-wrap;">最後に </span>`<span class="editor-theme-code">part.index_count</span>`<span style="white-space: pre-wrap;"> を計算し、&gt;0 なら </span>`<span class="editor-theme-code">mesh_.parts_.push_back(part)</span>`。

---

### 7. シーン半径（scene\_radius\_）の計算

展開中に AABB を更新しているので、それを使って「おおよその半径」を求めます。

```cpp
ext = (bb_max - bb_min) * 0.5f;
r = max(|ext.x|, |ext.y|, |ext.z|);
if (r < 1e-3f) r = 1.0f;

skeleton_.Data().scene_radius_ = r;
```

これはボーンのデバッグ描画時の軸の長さ決定に使っています。

---

### 8. バインド頂点とスキニング頂点の準備

```cpp
mesh_.bind_vertices_    = mesh_.vertices_;  // バインドポーズ
mesh_.skinned_vertices_ = mesh_.vertices_;  // 初期値：同じ
```

<span style="white-space: pre-wrap;">描画時の CPU スキニングでは </span>`<span class="editor-theme-code">bind_vertices_</span>`<span style="white-space: pre-wrap;"> を入力として </span>`<span class="editor-theme-code">skinned_vertices_</span>`<span style="white-space: pre-wrap;"> を更新します。</span>

---

## CreateGpuBuffers(): VB/IB の作成

<span style="white-space: pre-wrap;">単純に </span>`<span class="editor-theme-code">mesh_.vertices_</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">mesh_.indices_</span>`<span style="white-space: pre-wrap;"> から </span>`<span class="editor-theme-code">ID3D11Buffer</span>`<span style="white-space: pre-wrap;"> を作成しています。</span>

```text
[CPU] mesh_.vertices_  ----CreateBuffer---->  draw_.vb_
[CPU] mesh_.indices_   ----CreateBuffer---->  draw_.ib_
```

特にスキニング対応のための特殊なことはしておらず、`<span class="editor-theme-code">Usage = DEFAULT</span>`<span style="white-space: pre-wrap;"> の普通のVB/IBです。</span>

---

## CreateEffectsAndTextures(): エフェクト&amp;テクスチャ

ここでやっていること：

1. `<span class="editor-theme-code">CommonStates</span>`<span style="white-space: pre-wrap;"> と </span>`<span class="editor-theme-code">BasicEffect</span>`<span style="white-space: pre-wrap;"> を new</span>
2. BasicEffect にライティング設定を行う
3. BasicEffect から VS バイトコードを取り出し、`<span class="editor-theme-code">VertexPNT2</span>`<span style="white-space: pre-wrap;"> に合わせた InputLayout を作成</span>
4. FBX ファイルのディレクトリを求める
5. `<span class="editor-theme-code">mesh_.parts_</span>`<span style="white-space: pre-wrap;"> を走査して diffuse テクスチャを読む</span>

テクスチャの読み方は二通り：

```text
1) tex->content に埋め込みバイナリがある
    → CreateWICTextureFromMemory()

2) tex->filename にパスがある
    → FBXファイルのディレクトリと結合して CreateWICTextureFromFile()
```

<span style="white-space: pre-wrap;">成功すれば </span>`<span class="editor-theme-code">MeshPart::srv</span>`<span style="white-space: pre-wrap;"> に SRV を保持します。</span>

---

## Draw(world, view, proj): CPUスキニング＋描画

### 1. 前提チェック

VB/IB, BasicEffect, layout, indices が揃っているかをチェック。

---

### 2. CPU スキニング

```cpp
if (!mesh_.influences_.empty() && !mesh_.bind_vertices_.empty()) {
    skeleton_.SkinMatrices().resize(skeleton_.Bones().size());

    for i in 0 .. Bones()-1:
        W   = CurrWorld()[i]     // 現在のボーン姿勢 (ワールド)
        G2B = geom_bind_world    // ジオメトリ→ボーン
        skin_mats[i] = G2B * W   // スキン行列
```

スキン行列ののち、`<span class="editor-theme-code">ApplySkinCPU()</span>`<span style="white-space: pre-wrap;"> を呼び出し：</span>

```text
入力:
    skin_mats        : ボーン数
    mesh_.influences_: 頂点数
    mesh_.bind_vertices_: 頂点数

出力:
    mesh_.skinned_vertices_: 頂点数 (更新される)
```

中身は

```text
for each vertex v:
    P = 0, N = 0
    for k=0..3:
        if weight[k] > 0:
            B = skin_mats[bone[k]]
            P += (B * bind_pos) * weight
            N += (B * bind_nrm) * weight
    if any:
        正規化した N と P を skinned_vertices_[v] に書き戻し
    else:
        bind_vertices_[v] をそのままコピー
```

最後に

```cpp
ctx->UpdateSubresource(draw_.vb_.Get(), 0, nullptr,
                       &mesh_.skinned_vertices_[0], 0, 0);
```

で GPU のVBを更新。

---

### 3. IA &amp; ラスタ設定 → MeshPartごとに描画

```cpp
ctx->IASetInputLayout(draw_.layout_.Get());
ctx->IASetVertexBuffers(..., draw_.vb_.Get(), ...);
ctx->IASetIndexBuffer(draw_.ib_.Get(), DXGI_FORMAT_R32_UINT, 0);
ctx->IASetPrimitiveTopology(TRIANGLELIST);

ctx->OMSetDepthStencilState(DepthDefault);
ctx->RSSetState(CullCounterClockwise);
ctx->PSSetSamplers(0, 1, LinearClamp);
```

行列設定：

```cpp
fx->SetWorld(world);
fx->SetView(view);
fx->SetProjection(proj);
```

ブレンドステートは

```text
テクスチャあり    → NonPremultiplied
テクスチャなし    → Opaque
```

を使い分けています。

最後に

```cpp
for each MeshPart part in mesh_.parts_:
    has_tex = (part.srv != nullptr)
    fx->SetTextureEnabled(has_tex);
    if (has_tex) fx->SetTexture(part.srv.Get());
    fx->Apply(ctx);
    ctx->DrawIndexed(part.index_count, part.start_index, 0);
```

---

## まとめ（要点）

- `<span class="editor-theme-code">ExpandAllNodes()</span>`<span style="white-space: pre-wrap;"> で「FBX → 自前メッシュ構造」へ変換</span>
    - 頂点 (`<span class="editor-theme-code">VertexPNT2</span>`)、インデックス、MeshPart、ボーンウェイト (`<span class="editor-theme-code">VertexInfluence</span>`)、AABB を作成
- `<span class="editor-theme-code">CreateGpuBuffers()</span>`<span style="white-space: pre-wrap;"> で VB/IB を確保（この段階ではバインドポーズ形状）</span>
- `<span class="editor-theme-code">CreateEffectsAndTextures()</span>`<span style="white-space: pre-wrap;"> で BasicEffect と テクスチャ（SRV）を用意</span>
- `<span class="editor-theme-code">Draw()</span>`<span style="white-space: pre-wrap;"> で</span>
    - `<span class="editor-theme-code">FbxSkeleton</span>`<span style="white-space: pre-wrap;"> からボーン姿勢をもらい、スキン行列を計算</span>
    - <span style="white-space: pre-wrap;">CPUスキニングで </span>`<span class="editor-theme-code">bind_vertices_ → skinned_vertices_</span>`<span style="white-space: pre-wrap;"> に変換</span>
    - VB を UpdateSubresource → MeshPartごとに BasicEffect で描画

<span style="white-space: pre-wrap;">この構造が、今後 </span>`<span class="editor-theme-code">FbxMesh</span>`<span style="white-space: pre-wrap;"> に切り出すときの「設計単位」になります。</span>  
`<span class="editor-theme-code">ExpandAllNodes / CreateGpuBuffers / CreateEffectsAndTextures / Draw</span>`<span style="white-space: pre-wrap;"> あたりがそのまま </span>`<span class="editor-theme-code">FbxMesh</span>`<span style="white-space: pre-wrap;"> に移るイメージです。</span>

# ゲームの市場規模

# 🎮 日本のゲーム業界：最新市場規模と開発費まとめ（2024–2025）

---

# 1. 日本のゲーム市場規模

## ■ 国内ゲームコンテンツ市場規模（2024年）

- ****2兆3,961億円（前年比 +3.4%）****  
    [\[gamebiz.jp\]](https://gamebiz.jp/news/410304)

要因として、物価上昇による****ゲーム機・周辺機器・課金・サブスクの値上げが市場規模の拡大に直結****している。

## ■ 国内ゲーム人口

- ****5,475万人（2024年、前年比わずかに減）****  
    [\[gamebiz.jp\]](https://gamebiz.jp/news/410304)

PC＋コンシューマーを併用するプレイヤーが増加し、****複数デバイスを活用する層が2478万人に****。  
プレイスタイルがマルチプラットフォーム化していることが市場の維持に寄与。

---

# 2. モバイルゲーム市場（2024年）

日本のゲーム市場の主軸であるモバイルゲームの最新傾向：

## ■ ダウンロード数

- ****3億2,000万DL（前年比 +2.5%）****  
    [\[sensortower.com\]](https://sensortower.com/ja/blog/state-of-mobile-games-in-japan-2024-JP)<span style="white-space: pre-wrap;">, </span>[\[presswalker.jp\]](https://presswalker.jp/press/53923)

## ■ モバイルゲームの収益（IAP）

- ****53億ドル（前年比 -17%）※円安の影響****  
    [\[sensortower.com\]](https://sensortower.com/ja/blog/state-of-mobile-games-in-japan-2024-JP)<span style="white-space: pre-wrap;">, </span>[\[presswalker.jp\]](https://presswalker.jp/press/53923)

## ■ 収益ランキングトップタイトル

- 『モンスターストライク』：****2.9億ドル（1月〜7月）****  
    [\[sensortower.com\]](https://sensortower.com/ja/blog/state-of-mobile-games-in-japan-2024-JP)<span style="white-space: pre-wrap;">, </span>[\[presswalker.jp\]](https://presswalker.jp/press/53923)

## ■ 収益が大きいIP

- ****ドラゴンクエスト：1.7億ドル（2024年1〜7月）****  
    [\[sensortower.com\]](https://sensortower.com/ja/blog/state-of-mobile-games-in-japan-2024-JP)<span style="white-space: pre-wrap;">, </span>[\[presswalker.jp\]](https://presswalker.jp/press/53923)

---

# 3. 世界市場の中での日本の位置づけ

- 世界のゲームコンテンツ市場：****31兆円（2024年）****  
    [\[gamebiz.jp\]](https://gamebiz.jp/news/410304)
- モバイルが約60%を占め、依然として世界の中心。

日本は特に\*\*モバイル3Dアニメゲームの世界最大市場（世界シェア49%）\*\*であることが特徴。  
[\[sensortower.com\]](https://sensortower.com/ja/blog/state-of-mobile-games-in-japan-2024-JP)<span style="white-space: pre-wrap;">, </span>[\[presswalker.jp\]](https://presswalker.jp/press/53923)

---

# 4. ゲーム1本あたりの開発費の実態）

## ■ スマートフォンゲーム開発費（オンラインゲーム会社への調査）

- ****平均開発費：4億9,200万円（2024年）****  
    [\[famitsu.com\]](https://www.famitsu.com/article/202507/46862)
- <span style="white-space: pre-wrap;">10年前（2014年）と比較して </span>****4.7倍****<span style="white-space: pre-wrap;"> に増加</span>  
    [\[famitsu.com\]](https://www.famitsu.com/article/202507/46862)

## ■ ソーシャルゲームの開発費 相場（規模別）

（開発会社ヒアリングベース）

- カジュアル（パズル・2D）：****数百万円〜1,000万円****
- ミッドコア（ストラテジー等）：****数千万円〜数億円****
- ハードコア（RPG / FPS）：****数億円〜数十億円****
- VR/大型オンライン：****数十億円〜数百億円****  
    [\[imitsu.jp\]](https://imitsu.jp/matome/app-developer/3050142321446086)

## ■ コンシューマー大作タイトル例

- ****AAAタイトルは数百億円級****  
    <span style="white-space: pre-wrap;">例：Cyberpunk2077の開発費 ≒ </span>****314億円****  
    （推定に基づき投じられた総額分析）  
    [\[amamiya-ga...nablog.com\]](https://amamiya-gameplanner.hatenablog.com/entry/how_much_create_game_consumer)

## ■ オンラインゲーム運営（1タイトルあたり）

JOGAレポートより：

- 開発費に加えて、****運営サービス関連コスト（サーバー、CS、アップデート費）が年間で数億円規模****  
    [\[japanonlinegame.org\]](https://japanonlinegame.org/2025_0710/)

---

# 5. その他、産業の雇用・構造

- 日本のゲーム産業総就業人口：****約20万人****  
    [\[cesa.or.jp\]](https://www.cesa.or.jp/uploads/2025/release_game_industry_report2025.pdf)
- 平均年収：****708万円****  
    [\[cesa.or.jp\]](https://www.cesa.or.jp/action/industry-research/2024/)

<span style="white-space: pre-wrap;">→ </span>****クリエイティブ産業としては突出して高い生産性がある****ことが明らか。

---

# 6. 学科として強調できるポイント

### ✔ 日本のゲーム市場は「2.4兆円」の巨大産業で安定成長中

### ✔ 世界最大級のモバイルゲーム消費国（特に3Dアニメ系は世界シェア50%）

### ✔ 1タイトルあたりの開発費が急増（スマホ平均5億円 → AAAは300億円級）

### ✔ 高度な専門職が必要で平均年収も高く、人材育成の価値が高い

### ✔ 国内外パブリッシャーが継続投資を増やす構造で「人材不足」が深刻

（専門学校・大学のゲーム学科の存在意義が強い）

# ダイクストラ法・BFS・A* を図で理解する課題（時間補習課題）

## 【学習目標】

この課題では次のことができるようになります。

- BFS（幅優先探索）の基本と動き方を説明できる
- ダイクストラ法の仕組みを理解できる
- A\* が「何を追加したアルゴリズム」か説明できる
- 図を見て 3つの探索アルゴリズムを比較できる
- ゲーム開発での使い分けが説明できる

---

# 🔶【課題図（グラフ構造）】

```
(1)        (1)        (1)
   [S] ------ [A] ------ [B] ------ [G]
     \
      \ (5)
       \
       [C]
         \
          \ (1)
           \
           [B]（同じB）
```

- S：スタート
- G：ゴール
- 数字：その道を通るコスト

---

# ◆ ① BFS（幅優先探索）とは？（穴埋め）

BFS は、  
<span style="white-space: pre-wrap;">スタートから </span>****（ ）****<span style="white-space: pre-wrap;"> の順に探索していくアルゴリズムで、</span>  
<span style="white-space: pre-wrap;">道のコストが </span>****（ ）****<span style="white-space: pre-wrap;"> ときに最短経路を見つけることができる。</span>

選択肢：

- 近いノード
- 深さ
- 同じ
- バラバラ

---

# ◆ ② ダイクストラ法とは？（穴埋め）

ダイクストラ法は、  
<span style="white-space: pre-wrap;">スタートからの </span>****（ ）****<span style="white-space: pre-wrap;"> を更新しながら、</span>  
****（ ）****<span style="white-space: pre-wrap;"> が最も小さいノードを順に確定する方法である。</span>

選択肢：

- 最短距離
- コスト
- 深さ
- 未確定ノード

---

# ◆ ③ A\* アルゴリズムとは？（穴埋め）

<span style="white-space: pre-wrap;">A\* は、ダイクストラ法に </span>****（ ）****<span style="white-space: pre-wrap;"> を追加した方法で、</span>  
<span style="white-space: pre-wrap;">より </span>****（ ）****<span style="white-space: pre-wrap;"> ゴールに到達することを目的としている。</span>

選択肢：

- ヒューリスティック
- ランダム
- 早く
- 正確に

---

# ◆ ④ 図を見て考える：BFS / ダイクストラ / A\* の動き

## Q1. BFS はどのルートを最初に見つける？

<span style="white-space: pre-wrap;">BFS が最初に見つけるルートは </span>****（ ）****  
理由：\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_

---

## Q2. ダイクストラ法が求める “最短経路” は？

- 経路：****（ ）****
- <span style="white-space: pre-wrap;">合計コスト： </span>****（ ）****

ヒント：

- S→A→B→G = 1+1+1 = 3
- S→C→B→G = 5+1+1 = 7

---

## Q3. A\* の式を完成させよう

A\* の評価値 f(n) は次の式で求める：

****f(n) = g(n) + h(n)****

- g(n)：Sからそのノードまでの実コスト
- <span style="white-space: pre-wrap;">h(n)：ゴールまでの </span>****（ ）****

選択肢：

- 予想距離（予想値）
- 完全一致する距離
- 乱数

---

# ◆ ⑤ BFSの理解を深める

## 追加Q1：BFS はどんな順番で探索を進める？

A と B どちらかを選んで、理由を書くこと。

- A：スタート地点から「近いノード」から順番に調べる
- B：コストが小さい道を優先して調べる

****回答：A / B（理由：\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_）****

---

## 追加Q2：図を使って BFS の探索順を書こう

例：S → A → B → C → G のように記述。

****探索順：\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_****

---

## 追加Q3：なぜ BFS は「コスト同じのときだけ」最短経路を求められるのか？

穴埋め：

<span style="white-space: pre-wrap;">BFS は、道のコストがすべて </span>****（ ）****<span style="white-space: pre-wrap;"> とき、</span>  
<span style="white-space: pre-wrap;">スタートからの </span>****（ ）****<span style="white-space: pre-wrap;"> の順に探索するため、</span>  
最短経路が求まる。

選択肢：

- 同じ
- バラバラ
- 深さ（段階）
- 重さ

---

## 追加Q4：コストがバラバラのときに BFS が失敗する例

BFS はどちらを先に探索する？

- ルート1：S → A → B → G（合計3）
- ルート2：S → C → B → G（合計7）

****回答：\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_****  
理由：\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_

---

## 追加Q5：ゲーム開発で BFS が役立つ場面は？

番号を選び、理由を書く。

1. 敵から一定距離以内に逃げたいとき
2. コストがバラバラのマップで探索したい
3. 同じコストのタイルで広いマップを探索するとき

****選んだ番号：\_\_\_****  
理由：\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_

---

## 追加Q6：BFS の弱点

図のように“遠回りだが軽い道”があるとき、BFS はどんな失敗をするか？

****回答：\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_****

---

## 追加Q7：BFS を1行でまとめよ

「BFS は \_\_\_\_\_\_\_\_ を優先し、コストが \_\_\_\_\_\_\_\_ ときに最短経路を求められる」

---

## 追加Q8：BFS とダイクストラの共通点・相違点（穴埋め）

- 共通点：どちらも（ ）に沿って探索し、最終的に（ ）を求める
- 違い：BFS は（ ）が同じときに最短になるが、  
    ダイクストラ法は（ ）が0以上であれば最短を求められる

---

# ◆ ⑥ 3つのアルゴリズムの違いを1行ずつまとめよう

- ****BFS：****<span style="white-space: pre-wrap;"> 道のコストが </span>****（ ）****<span style="white-space: pre-wrap;"> とき、 \_\_\_\_\_\_\_ の順に探索する</span>
- ****ダイクストラ法：****<span style="white-space: pre-wrap;"> コストが0以上なら \_\_\_\_\_\_\_ を必ず見つけられる</span>
- ****A\*：****<span style="white-space: pre-wrap;"> ヒューリスティックを使って \_\_\_\_\_\_\_ 探索する</span>

---

# ◆ ⑦ ゲーム開発での使い分け

1. 草原でコストが全部同じ  
    <span style="white-space: pre-wrap;">→ </span>****（ ）****  
    理由：\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
2. コストがバラバラの道（泥・坂・砂）  
    <span style="white-space: pre-wrap;">→ </span>****（ ）****  
    理由：\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
3. ゴール（敵の位置）が分かっている  
    <span style="white-space: pre-wrap;">→ </span>****（ ）****  
    理由：\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_

---

# ◆ ⑧ 最終まとめ（作文）

****ダイクストラ法・BFS・A\* の違いを、初心者にもわかるよう詳細に説明せよ。****

---

---

# GOMファイルフォーマットを作るよ

Game Oriented Model Format（GOMフォーマット）を作っていくよ。  
シンプルで、アニメーションだけ含んだゲーム制作初心者向けフォーマットだよ。

# GOM（Game Oriented Model Format)草案

# GOMフォーマット仕様書

Game Oriented Model Format (.gom)
Version 1.0 (Complete Specification)

---

# 1. 目的

GOMは、FBX等のDCCデータをゲーム実行向けに最適化し、
オフライン変換して保存するためのフォーマットである。

## 設計目標

* スキニングはモデル（geometry）空間で完結
* インスタンスごとに別Transform・別アニメ時間を安全に扱える
* FBX差（ノード階層・geometry transform）を吸収
* ワールド直スキニング事故を防止
* 実行時にFBXSDKやufbxなどのライブラリに依存しない
* 同一FBXから常に同一GOMが生成される（決定性保証）

---

# 2. 空間設計ポリシー（不変条件）

## Invariant A: 頂点空間

* 頂点は常に geometry（=モデルローカル）空間
* スキニング結果も geometry 空間
* instanceWorld は描画直前に1回のみ適用

### 禁止事項

* スキニング途中で world に焼く
* geometry_to_world を途中適用する

---

# 3. 行列規約（最終固定）

GOM v1では以下を固定する。

* 数値型：float32
* メモリ並び：row-major
* 演算規約：row-vector 右掛け（p' = p * M）
* 行列は常に 4x4
* 座標系：Left-handed / Y-up / meters

---

# 4. ファイル形式

GOMはテキスト形式（GOMT0001）とバイナリ形式（GOMB0001）を持つ。

## マジックナンバー（先頭8バイト）

* GOMT0001
* GOMB0001

---

# 5. テキスト形式（GOMT0001）

UTF-8（BOMなし）、構造リテラル形式。

## 5.1 Skeleton

```
Skeleton = {
  boneCount = <int>,
  bones = [
    {
      parent = <int>,
      name   = "<string>",
      path   = "<string>",
      bindLocal = [[...],[...],[...],[...]]
    }
  ]
}
```

* bones配列順がboneIndex
* pathは同名衝突対策

## 5.2 MeshPart

```
Meshes = [
  {
    name = "<string>",
    meshBindGeoToModel = [[...],[...],[...],[...]],
    vertices = [ { p,n,t,uv,bi,bw } ],
    indices = [ ... ],
    submeshes = [ { materialIndex, indexStart, indexCount } ],
    inverseBind = [ [[...]] ]
  }
]
```

### ウェイト規約（必須）

* 最大4影響
* 合計1.0正規化
* weight降順、同値はboneIndex昇順タイブレーク

## 5.3 Animation

```
Animations = [
  {
    name = "<string>",
    duration = <float>,
    rootBoneIndex = <int>,
    flags = ["HasRootMotion"],
    tracks = [ { bone, keys[] } ]
  }
]
```

補間：T/S=linear、R=slerp

---

# 6. MeshPart規約

## Rule M1

* FBXメッシュノード単位でMeshPart生成
* マテリアル分割はSUB0で表現

## Rule M2

以下の場合はMeshPartを分割する：

* 別スケルトン参照
* inverseBindが異なる
* 頂点レイアウトが異なる

---

# 7. バイナリ形式（GOMB0001）

## 7.1 固定ヘッダ（512 bytes）

Little Endian固定。

### 7.1.1 固定値

* `magic` は常に `GOMB0001`（8 bytes ASCII）
* `headerSize` は常に `512`
* `formatVersion` は常に `1`

### 7.1.2 列挙値（enum）定義

本仕様の enum はすべて `uint32` とする。
未知の enum 値を受け取った場合、読み込みはエラーとする（v1）。

#### endianness（将来用）

v1ではファイルはLittle Endian固定だが、将来拡張のためヘッダに enum を持つ。

* `ENDIAN_LE = 1`（v1は常にこれ）
* `ENDIAN_BE = 2`（予約）

#### coordinateSystem

* `CS_LH_YUP_METERS = 1`

#### matrixConvention

行列の「格納順＋演算規約」を表す。

* `MC_ROW_MAJOR_ROW_VECTOR_RIGHT_MUL = 1`

定義：

* 行列は row-major で格納される（m00,m01,m02,m03,m10,...）
* 位置は row-vector として扱い `p' = p * M`（p=[x y z 1]）

### 7.1.3 validationFlags（bitfield）

`validationFlags` は `uint32`。

* bit0（1<<0）: bindPosePass
* bit1（1<<1）: weightsPass
* bit2（1<<2）: submeshMaterialsPass

予約：bit3以降は将来用。v1では0。

### 7.1.4 ChunkTableの指定

* `chunkTableOffset == 0` の場合：ChunkTableなし
* `chunkTableOffset != 0` の場合：ファイル先頭からのバイトオフセットで ChunkTable 先頭を指す
* `chunkTableCount` はエントリ数

---

```
char   magic[8]              // GOMB0001
uint32 headerSize            // 512
uint32 formatVersion         // 1
uint32 coordinateSystem      // enum
uint32 matrixConvention      // enum
uint8  sourceHash[32]        // SHA-256 raw
uint32 converterVersionStr   // STR0 index
uint32 validationFlags       // bitfield
uint32 chunkTableOffset
uint32 chunkTableCount
uint8  reserved[...]         // 0埋め
```

---

# 8. バイナリ全体の堅牢ルール（バイナリ専用）

## 8.1 Endianness

* Little Endian固定

## 8.2 アライメント

* チャンクは16バイト境界にアライン

## 8.3 チャンク基本構造

```
uint32 fourcc
uint32 size
uint32 version
uint32 flags
byte payload[size]
pad to 16
```

## 8.4 チャンクサイズ上限チェック（必須）

* sizeがファイル末尾を超える場合は即エラー
* sizeが実装上限を超える場合はエラー

## 8.5 未知チャンク

* 未対応fourccはsize分スキップ可能

## 8.6 ChunkTable（任意）

* chunkTableOffset!=0 の場合、ChunkEntry配列を保持

## 8.7 MSH0.flags（任意）

* bit0 = indices16
* bit1 = hasGBTM
* 予約ビットは0

---

# 9. バイナリチャンク厳密レイアウト

## STR0

```
uint32 stringCount
uint32 offsets[stringCount]
uint32 dataSize
uint8  data[dataSize]
```

## SKEL

```
uint32 boneCount
int32 parent[boneCount]
uint32 nameId[boneCount]
uint32 pathId[boneCount]
float32 bindLocal[boneCount][16]
```

## MSH0

```
uint32 meshNameId
uint32 vertexFormat
uint32 vertexCount
uint32 indexCount
uint32 boneCount
uint32 flags
uint32 reserved[2]
```

### VB0

```
uint32 vertexCount
uint32 stride
uint32 vertexFormat
uint32 reserved
uint8  data[vertexCount*stride]
```

### IB0

```
uint32 indexCount
uint32 indexSizeBytes
uint32 reserved[2]
uint8 data[indexCount*indexSizeBytes]
```

### SUB0

```
uint32 submeshCount
struct { int32 materialIndex; uint32 indexStart; uint32 indexCount; }
```

### IBND

```
uint32 boneCount
float32 inverseBind[boneCount][16]
```

### BND0

```
float32 aabbMin[3]
float32 aabbMax[3]
float32 sphereCenter[3]
float32 sphereRadius
```

### GBTM（任意）

```
float32 meshBindGeoToModel[16]
```

## ANIM

```
uint32 clipCount
```

## CLP0

```
uint32 clipNameId
float32 duration
float32 sampleRate
int32 rootBoneIndex
uint32 flags
uint32 trackCount
```

Track構造：

```
int32 boneIndex
uint32 keyCount
for each key:
  float32 time
  float32 tr[3]
  float32 rot[4]
  float32 sc[3]
```

---

# 10. 変換ツール検証（必須）

* Bind整合
* ウェイト合計
* Submesh完全被覆
* boneIndex範囲チェック
* index範囲チェック

---

---

# 11. 実装安全規約（v1推奨）

## 11.1 読み込み上限値（推奨）

実装は以下の上限を持つことを推奨する（超過時はエラー）。

* maxBoneCount = 4096
* maxVertexCount = 10,000,000
* maxIndexCount = 30,000,000
* maxClipCount = 1024
* maxTrackCount = 8192
* maxKeyCount = 1,000,000（クリップ単位）

※ 実装側で変更可能だが、無制限読み込みは禁止。

---

## 11.2 必須チャンク条件（バイナリ）

各 MSH0 は以下の子チャンクを必須とする：

* VB0
* IB0
* SUB0
* IBND
* BND0

GBTM は任意。

IB0.indexSizeBytes は 2 または 4 のみ許可。
それ以外は読み込みエラー。

---

## 11.3 アニメキー時刻規約

* keys[].time は昇順必須
* 0 <= time <= duration
* ループ再生時は time = fmod(time, duration) を推奨

---

## 11.4 inverseBind の定義

inverseBind[i] は、bind姿勢における boneWorld[i] の逆行列に相当する。

スキニングパレットは以下で構成される：

palette[i] = boneWorld_current[i] * inverseBind[i]

これにより、geometry 空間内でスキニングが完結する。

---

End of GOM Specification v1.0

# GOM Format quick Reference

<a id="sec-1"></a>
# GOM + MTA フォーマット 簡易仕様書

**Game Oriented Model Format + Motion TimeLine Animation**

バージョン: GOM v1.0 / MTA v1.0  
最終更新: 2026-03-18

<a id="sec-2"></a>
## 📖 目次

1. [概要](#sec-3)
2. [GOM フォーマット](#sec-6)
3. [MTA フォーマット](#sec-10)
4. [基本規約](#sec-15)
5. [ファイル構造](#sec-20)
6. [主要チャンク](#sec-30)
7. [ウェイト正規化](#sec-36)
8. [決定性保証](#sec-41)
9. [互換性](#sec-45)
10. [実装ガイド](#sec-49)
11. [テキストフォーマット](#sec-53)
12. [よくある質問（FAQ）](#sec-63)
13. [まとめ](#sec-73)

<a id="sec-3"></a>
## 概要

<a id="sec-4"></a>
### GOM（Game Oriented Model）

**目的**: FBX等のDCCデータをゲーム実行向けに最適化し、オフライン変換して保存する

**設計目標**:

* スキニングはモデル（geometry）空間で完結
* インスタンスごとに別Transform・別アニメ時間を安全に扱える
* FBX差（ノード階層・geometry transform）を吸収
* ワールド直スキニング事故を防止
* 実行時にFBXSDKに依存しない
* 同一FBXから常に同一GOMが生成される（決定性保証）

<a id="sec-5"></a>
### MTA（Motion TimeLine Animation / GOMTA）

**目的**: GOMのアニメーション部分のみを独立させた兄弟フォーマット

**特徴**:

* メッシュ/マテリアルを含まない
* スケルトン実体を含まない（skeletonSig のみ）
* 複数のアニメーションファイルを用意して動的に切り替え可能
* 異なる GOM ファイルでも同じスケルトンなら適用可能

---

<a id="sec-6"></a>
## GOM フォーマット

<a id="sec-7"></a>
### ファイル形式

|形式|マジックナンバー|説明|
|-|-|-|
|テキスト|`GOMT0001`|UTF-8、構造リテラル形式|
|バイナリ|`GOMB0001`|Little Endian、16-byte アライン|

<a id="sec-8"></a>
### 含まれる要素

* **Skeleton（SKEL）**: ボーン階層、bind pose
* **Mesh（MSH0）**: 頂点、インデックス、サブメッシュ
* **Material（MATL）**: Phong最小セット、テクスチャ参照
* **Animation（ANIM/CLP0）**: ボーンアニメーション
* **String Table（STR0）**: 文字列テーブル（名前、パス）

<a id="sec-9"></a>
### ファイルサイズ

* ヘッダ: 512 bytes
* チャンク: 可変長（16-byte アライン）

---

<a id="sec-10"></a>
## MTA フォーマット

<a id="sec-11"></a>
### ファイル形式

|形式|マジックナンバー|説明|
|-|-|-|
|バイナリ|`MTAB0001`|Little Endian、16-byte アライン|

**注意**: v1 ではテキスト形式（MTAT0001）は非サポート。詳細は [テキストフォーマット](#sec-53) セクション参照。



<a id="sec-12"></a>
### 含まれる要素

* **String Table（STR0）**: 文字列テーブル（必須）
* **Animation（ANIM/CLP0）**: アニメーションクリップ
* **skeletonSig**: スケルトン互換性チェック（SHA-256, 32 bytes）

<a id="sec-13"></a>
### 除外される要素

* ❌ メッシュ（MSH0, VB0, IB0, SUB0, IBND）
* ❌ マテリアル（MATL）
* ❌ スケルトン実体（SKEL）
* ❌ Geometry Transform（GBTM）

<a id="sec-14"></a>
### スケルトン互換性チェック

**実行時判定**:

```c
if (memcmp(gom.skeletonSig, mta.skeletonSig, 32) != 0) {
  error("Skeleton signature mismatch!");
}
```

**skeletonSig 生成**:

```
SHA-256(
  boneCount (uint32, little-endian) +
  foreach boneIndex in bonePath 辞書順:
    bonePath (UTF-8) + '\0' + parentIndex (int32, little-endian)
)
```

---

<a id="sec-15"></a>
## 基本規約

<a id="sec-16"></a>
### 空間設計ポリシー（Invariant A）

GOM の最重要設計原則。

**Invariant A: 頂点空間**

* 頂点は常に **geometry（=モデルローカル）空間**
* スキニング結果も **geometry 空間**
* `instanceWorld` は **描画直前に1回のみ適用**

**禁止事項**:

* ❌ スキニング途中で world に焼く
* ❌ geometry\_to\_world を途中適用する

**描画時の変換式**:

```cpp
// 正しい変換順序
vec4 skinnedPos = vertex * skinning;           // geometry 空間
vec4 worldPos = skinnedPos * GBTM * instanceWorld;  // world 空間
vec4 clipPos = worldPos * viewProj;            // clip 空間
```

**この設計の利点**:

1. **インスタンシング安全**: 同一メッシュを異なる位置/回転で配置可能
2. **アニメーション独立**: インスタンスごとに異なるアニメ時間を適用可能
3. **FBX差の吸収**: ノード階層の違いを完全に隠蔽
4. **事故防止**: ワールド直スキニングによる破綻を防止

---

<a id="sec-17"></a>
### 行列規約

|項目|値|
|-|-|
|数値型|`float32`|
|メモリ並び|**row-major**|
|演算規約|**row-vector 右掛け**（`p' = p * M`）|
|行列サイズ|4x4（常に）|

**行列演算の具体例**:

```cpp
// ベクトルは行ベクトルとして扱う
vec4 p = {x, y, z, 1.0f};
vec4 p_transformed = p * M;  // 右から掛ける

// 複数の変換
vec4 result = p * M1 * M2 * M3;  // 左から順に適用
```

**メモリレイアウト（row-major）**:

```cpp
float m[16] = {
  m00, m01, m02, m03,  // 第1行
  m10, m11, m12, m13,  // 第2行
  m20, m21, m22, m23,  // 第3行
  m30, m31, m32, m33   // 第4行
};

// アクセス方法
float value = m[row * 4 + col];
```

**変換順序の例**:

```cpp
// スキニング → GBTM → インスタンス変換 → ビュー → プロジェクション
vec4 worldPos = vertex * skinning * GBTM * instanceWorld;
vec4 viewPos = worldPos * view;
vec4 clipPos = viewPos * projection;

// または一度に
vec4 clipPos = vertex * skinning * GBTM * instanceWorld * view * projection;
```

---

<a id="sec-18"></a>
### 座標系

|項目|値|
|-|-|
|ハンドネス|**Left-handed**|
|上方向|**Y-up**|
|単位|**meters**|

<a id="sec-19"></a>
### バイナリ規約

|項目|値|
|-|-|
|エンディアン|**Little Endian**|
|アラインメント|**16-byte**|
|パディング値|**0x00**|

---

<a id="sec-20"></a>
## ファイル構造

<a id="sec-21"></a>
### GOM/MTA バイナリファイル構造

<a id="sec-22"></a>
#### GOM ファイル構造

```
+-------------------------+ offset 0
| Header (512 bytes)      |
+-------------------------+ offset 512
| STR0 Chunk              |
|   header (16)           |
|   payload (...)         |
|   padding (0x00)        |
+-------------------------+ ALIGN16(...)
| MATL Chunk              |
|   header (16)           |
|   payload (...)         |
|   padding (0x00)        |
+-------------------------+ ALIGN16(...)
| SKEL Chunk (任意)       |
|   header (16)           |
|   payload (...)         |
|   padding (0x00)        |
+-------------------------+ ALIGN16(...)
| MSH0 Chunk              |
|   header (16)           |
|   VB0, IB0, SUB0,       |
|   IBND, BND0, GBTM      |
|   padding (0x00)        |
+-------------------------+ ALIGN16(...)
| ANIM Chunk (任意)       |
|   header (16)           |
|   payload (...)         |
|   padding (0x00)        |
+-------------------------+ ALIGN16(...)
| CLP0 Chunk(s) (任意)    |
|   header (16)           |
|   payload (...)         |
|   padding (0x00)        |
+-------------------------+
```

<a id="sec-23"></a>
#### MTA ファイル構造

```
+-------------------------+ offset 0
| Header (512 bytes)      |
|   skeletonSig (32 bytes)|
|   at offset 76          |
+-------------------------+ offset 512
| STR0 Chunk (必須)       |
|   header (16)           |
|   payload (...)         |
|   padding (0x00)        |
+-------------------------+ ALIGN16(...)
| ANIM Chunk              |
|   header (16)           |
|   payload (...)         |
|   padding (0x00)        |
+-------------------------+ ALIGN16(...)
| CLP0 Chunk #1           |
|   header (16)           |
|   payload (...)         |
|   padding (0x00)        |
+-------------------------+ ALIGN16(...)
| CLP0 Chunk #2           |
|   header (16)           |
|   payload (...)         |
|   padding (0x00)        |
+-------------------------+ ALIGN16(...)
| ... (複数クリップ可)    |
+-------------------------+
```

**主な違い**:

* **GOM**: MATL, SKEL, MSH0 を含む（フルモデルデータ）
* **MTA**: STR0, ANIM, CLP0 のみ（アニメーションのみ）
* **MTA ヘッダ**: offset 76 に skeletonSig (32 bytes) を含む

<a id="sec-24"></a>
### ヘッダ構造（512 bytes）

<a id="sec-25"></a>
#### GOM ヘッダ

|オフセット|フィールド|型|サイズ|説明|
|-|-|-|-|-|
|0|magic|char\[8]|8|"GOMB0001"|
|8|headerSize|uint32|4|= 512|
|12|formatVersion|uint32|4|= 1|
|16|coordinateSystem|uint32|4|= 1 (LH/Y-up/meters)|
|20|matrixConvention|uint32|4|= 1 (row-major)|
|24|sourceHash|uint8\[32]|32|SHA-256 of source|
|56|converterVersionStrId|uint32|4|STR0参照ID|
|60|validationFlags|uint32|4|ビットフィールド|
|64|chunkTableOffset|uint64|8|0 = なし|
|72|chunkTableCount|uint32|4|ChunkEntry数|
|76|reserved|uint8\[436]|436|0埋め|

<a id="sec-26"></a>
#### MTA ヘッダ

|オフセット|フィールド|型|サイズ|説明|
|-|-|-|-|-|
|0|magic|char\[8]|8|"MTAB0001"|
|8|headerSize|uint32|4|= 512|
|12|formatVersion|uint32|4|= 1|
|16|coordinateSystem|uint32|4|= 1 (LH/Y-up/meters)|
|20|matrixConvention|uint32|4|= 1 (row-major)|
|24|sourceHash|uint8\[32]|32|SHA-256 of source|
|56|converterVersionStrId|uint32|4|STR0参照ID|
|60|validationFlags|uint32|4|v1 では 0 固定|
|64|chunkTableOffset|uint64|8|0 = なし|
|72|chunkTableCount|uint32|4|ChunkEntry数|
|76|**skeletonSig**|**uint8\[32]**|**32**|**SHA-256 of skeleton**|
|108|reserved|uint8\[404]|404|0埋め|

**重要な違い**:

* GOM: offset 76 から reserved\[436]
* MTA: offset 76 から skeletonSig\[32]、offset 108 から reserved\[404]
GOM ヘッダテーブルと MTA ヘッダテーブルの後に以下を追加：

<a id="sec-27"></a>
### validationFlags ビット定義

`validationFlags` は変換ツールが実行した検証をビットフラグで記録する。

| ビット | マスク | 名称 | 意味 |
|-------|--------|------|------|
| bit0 | 1<<0 | VALIDATED_BIND_MATRICES | inverseBind * bindLocal = Identity を検証済み |
| bit1 | 1<<1 | VALIDATED_WEIGHTS | 全頂点のウェイト合計が 1.0 であることを検証済み |
| bit2 | 1<<2 | VALIDATED_SUBMESHES | サブメッシュが完全被覆（隙間・重複なし）を検証済み |
| bit3 | 1<<3 | VALIDATED_INDICES | 全インデックスが範囲内を検証済み |
| bit4 | 1<<4 | VALIDATED_BONE_INDICES | 全boneIndexが範囲内を検証済み |
| bit5 | 1<<5 | VALIDATED_MATERIAL_INDICES | 全materialIndexが範囲内を検証済み |

その他のビットは予約（v1では0）。

**変換ツール側（書き込み）**:

```cpp
uint32_t flags = 0;
flags |= (1 << 0);  // VALIDATED_BIND_MATRICES
flags |= (1 << 1);  // VALIDATED_WEIGHTS
flags |= (1 << 2);  // VALIDATED_SUBMESHES
flags |= (1 << 3);  // VALIDATED_INDICES
flags |= (1 << 4);  // VALIDATED_BONE_INDICES
flags |= (1 << 5);  // VALIDATED_MATERIAL_INDICES
header.validationFlags = flags;
```

**ローダー側（読み込み）**:

```cpp
// 参考情報として使用してもよいが、
// ローダーは独自の検証を省略してはならない
bool bindValidated = (header.validationFlags & (1 << 0)) != 0;
bool weightsValidated = (header.validationFlags & (1 << 1)) != 0;

// 未知のビットは無視（警告を出してもよい）
uint32_t unknownBits = header.validationFlags & ~0x3F;  // bit0-5以外
if (unknownBits != 0) {
  // 警告: 未知の検証フラグが立っている
}
```

**重要な規約**:

* 変換ツールは実行した検証に対応するビットを立てる
* ローダーは `validationFlags` を参考情報として扱う
* **ローダーは `validationFlags` に関係なく独自の検証を実行しなければならない**
* 未知のビットは無視してよい（警告推奨）

---



<a id="sec-28"></a>
### チャンク共通構造（16 bytes）

```c
struct ChunkHeader {
  uint32_t fourcc;     // チャンク識別子（4文字）
  uint32_t size;       // ペイロードサイズ（バイト）
  uint32_t version;    // v1 では 1 固定
  uint32_t flags;      // v1 では 0 固定
};
```

---

<a id="sec-29"></a>
### fourcc 規約

**フォーマット**:

* 正確に **4 文字** の ASCII 文字
* **大文字のみ**
* 数字を許可
* 文字セット: `[A-Z0-9]`

**3文字 fourcc の扱い**:

3文字の識別子（例: `VB0`, `IB0`）は、null 終端で 4 バイトに格納する：

```c
'VB0\0'  // null終端で4バイト
'IB0\0'  // null終端で4バイト
```

**有効な例**:

* `STR0`, `MATL`, `SKEL`, `MSH0`
* `VB0`, `IB0`, `SUB0`, `IBND`
* `BND0`, `GBTM`, `ANIM`, `CLP0`

**無効な例**:

* `str0` （小文字は不可）
* `Mesh` （小文字を含む）
* `MAT` （長さが 4 でない）
* `MATLL` （長さが 4 を超える）

**実装例**:

```c
// fourcc を uint32 として扱う（little-endian）
uint32_t fourcc_vb0 = 0x30304256;  // 'VB0\0' を little-endian で格納
                                    // V=0x56, B=0x42, 0=0x30, \0=0x00

// バイト配列として比較
if (memcmp(&chunk_fourcc, "VB0\0", 4) == 0) {
    // VB0 チャンク処理
}
```

**注意事項**:

* fourcc は **必ず 4 バイト** として扱う
* 3文字の場合は **null 終端（`\0`）** で埋める
* スペース（` `）埋めは使用しない

---



<a id="sec-30"></a>
## 主要チャンク

<a id="sec-31"></a>
### STR0（文字列テーブル）- 必須

**構造**:

```
stringCount: uint32
offsets: uint32[stringCount]
stringData: char[]  // UTF-8, null終端
```

**規約**:

* 重複排除
* 辞書順ソート（UTF-8 byte-wise）
* ID: 0xFFFFFFFF = なし

<a id="sec-32"></a>
### MATL（Material Slots）- GOM 必須

**構造**:

```
materialSlotCount: uint32
slots: MaterialSlot[materialSlotCount]
```

**MaterialSlot**:

* nameId: uint32
* flags: uint32
* kd, ks, ke: float32\[3]
* shininess, opacity: float32
* diffuseMapId, normalMapId, specularMapId, emissiveMapId: uint32

**デフォルト白マテリアル**:

* kd = (1.0, 1.0, 1.0)
* ks = (0.04, 0.04, 0.04)
* ke = (0.0, 0.0, 0.0)
* shininess = 32.0
* opacity = 1.0

<a id="sec-33"></a>
### SKEL（Skeleton）- GOM 任意

**構造（SoA形式）**:

```
boneCount: uint32
parent: int32[boneCount]        // -1 = 親なし
nameId: uint32[boneCount]       // STR0参照
pathId: uint32[boneCount]       // STR0参照
bindLocal: float32[boneCount][16]  // row-major 4x4
```

**規約**:

* parent\[i] < i（循環参照防止）
* bonePath 辞書順でソート

<a id="sec-34"></a>
### MSH0（MeshPart Container）- GOM 必須

**内部チャンク**:

* VB0: 頂点バッファ
* IB0: インデックスバッファ（uint32）
* SUB0: サブメッシュ定義
* IBND: InverseBind行列
* BND0: BindLocal行列（任意）
* GBTM: MeshBindGeoToModel（任意）

**VF01 頂点フォーマット（72 bytes）**:

```
position: float32 x3   (12 bytes)
normal: float32 x3     (12 bytes)
tangent: float32 x4    (16 bytes)  // w=handedness
uv0: float32 x2        (8 bytes)
boneIndex: uint16 x4   (8 bytes)
boneWeight: float32 x4 (16 bytes)
```

<a id="sec-35"></a>
### ANIM / CLP0（Animation）

**ANIM 構造**:

```
clipCount: uint32
clips: CLP0[clipCount]
```

**CLP0 構造**:

```
nameId: uint32
duration: float32  // seconds
rootBoneIndex: int32
interpMode: uint32  // IM_LINEAR_SLERP = 1
flags: uint32       // HasRootMotion, Loop(予約)
trackCount: uint32
tracks: Track[trackCount]
```

**Track 構造**:

```
boneIndex: uint32
trKeyCount, rotKeyCount, scKeyCount: uint32
trKeys: TrKey[]
rotKeys: RotKey[]
scKeys: ScKey[]
```

**補間規約**:

* Translation/Scale: 線形補間
* Rotation: slerp（dot<0 なら flip）

---

---

<a id="sec-36"></a>
## ウェイト正規化

<a id="sec-37"></a>
### 規約

GOM v1 では、すべての頂点のボーンウェイトは以下の規約に従う：

1. **最大4影響**: 5つ以上の影響は上位4つのみ保持
2. **合計1.0**: ウェイト合計を 1.0 に正規化
3. **ソート規約**:

   * `boneWeight` 降順
   * 同値の場合は `boneIndex` 昇順

<a id="sec-38"></a>
### 正規化アルゴリズム

**入力**: `n` 個のボーン影響 `(boneIndex[i], boneWeight[i])`

**出力**: 最大4個の正規化された影響

**手順**:

```cpp
// 1. weight 降順、同値なら boneIndex 昇順でソート
std::sort(influences.begin(), influences.end(), [](auto& a, auto& b) {
  if (a.weight != b.weight) return a.weight > b.weight;  // 降順
  return a.boneIndex < b.boneIndex;  // 昇順
});

// 2. 上位4つのみ保持
if (influences.size() > 4) {
  influences.resize(4);
}

// 3. 合計を計算
float sum = 0.0f;
for (auto& inf : influences) {
  sum += inf.weight;
}

// 4. 正規化
for (auto& inf : influences) {
  inf.weight /= sum;
}

// 5. 4個未満の場合は0埋め
while (influences.size() < 4) {
  influences.push_back({0, 0.0f});
}
```

<a id="sec-39"></a>
### 例

**例 1: 3影響**

```
入力: bi=[3,1,2], bw=[0.5,0.3,0.2]
出力: bi=[3,1,2,0], bw=[0.5,0.3,0.2,0.0]  // 既に降順、0埋め
```

**例 2: ソートが必要**

```
入力: bi=[1,3,2], bw=[0.3,0.5,0.2]
ソート後: bi=[3,1,2], bw=[0.5,0.3,0.2]
出力: bi=[3,1,2,0], bw=[0.5,0.3,0.2,0.0]
```

**例 3: 5影響（トリミング必要）**

```
入力: bi=[0,1,2,3,4], bw=[0.4,0.3,0.2,0.08,0.02]
上位4つ: bi=[0,1,2,3], bw=[0.4,0.3,0.2,0.08]
合計: 0.98
正規化: bi=[0,1,2,3], bw=[0.408,0.306,0.204,0.082]
```

**例 4: 同値の場合**

```
入力: bi=[3,1,2], bw=[0.5,0.25,0.25]
ソート: bi=[3,1,2], bw=[0.5,0.25,0.25]  // 0.25 同値は boneIndex 昇順
出力: bi=[3,1,2,0], bw=[0.5,0.25,0.25,0.0]
```

<a id="sec-40"></a>
### 検証

変換ツールは以下を保証しなければならない：

* \[x] すべての頂点が 4 影響以下
* \[x] ウェイト合計が 1.0 ± 1e-4
* \[x] boneWeight 降順、同値なら boneIndex 昇順
* \[x] boneIndex が範囲内（`0 <= boneIndex < boneCount`）

---


---

<a id="sec-41"></a>
## 決定性保証

<a id="sec-42"></a>
### ソート規約

すべての順序は **UTF-8 byte-wise 辞書順** で確定:

1. **bonePath**: SKEL の boneIndex 順序
2. **meshNodePath**: MSH0 の MeshPart 順序
3. **materialKey**: MATL の materialIndex 順序
4. **clipName**: MTA の CLP0 順序

<a id="sec-43"></a>
### 比較方法

```c
// UTF-8 byte-wise 比較（memcmp / strcmp）
// ロケール依存禁止
// 大文字小文字折りたたみ禁止
```

<a id="sec-44"></a>
### 浮動小数点

* 出力直前に `float32` へキャスト
* VertexKey は bitwise 一致比較
* 検証は許容誤差（1e-5 推奨）

---

<a id="sec-45"></a>
## 互換性

<a id="sec-46"></a>
### GOM v1.0

**サポート範囲**:

* スキンメッシュ / スキンなしメッシュ
* Phong 最小マテリアル
* ボーンアニメーション（T/R/S）
* ルートモーション抽出（X/Z移動、Y軸回転）

**制限**:

* 頂点フォーマット: VF01 のみ
* UV: 1セットのみ
* インデックス: uint32 のみ
* 頂点カラー: 非対応

<a id="sec-47"></a>
### MTA v1.0

**サポート範囲**:

* 複数アニメーションクリップ
* skeletonSig 互換性チェック
* 決定性保証

**制限**:

* GOM の SKEL と互換性がある場合のみ適用可能
* boneIndex は GOM の boneCount 未満でなければならない

<a id="sec-48"></a>
### 実行時互換性チェック

**GOM 側**:

```c
// SKEL から skeletonSig を計算
uint8_t gom_sig[32];
compute_skeleton_sig(skel, gom_sig);
```

**MTA 側**:

```c
// ヘッダから skeletonSig を読み込み
uint8_t mta_sig[32];
memcpy(mta_sig, mta_header.skeletonSig, 32);
```

**判定**:

```c
if (memcmp(gom_sig, mta_sig, 32) != 0) {
  error("Skeleton mismatch!");
}
```

---

<a id="sec-49"></a>
## 実装ガイド

<a id="sec-50"></a>
### 推奨上限値

|項目|推奨値|
|-|-|
|maxBoneCount|4096|
|maxVertexCount|10,000,000|
|maxIndexCount|30,000,000|
|maxMaterialSlotCount|65,535|
|maxSubmeshCount|65,535|
|maxClipCount|1024|
|maxTrackCount|8192|
|maxKeyCountPerClip|1,000,000|

<a id="sec-51"></a>
### 検証規約

必須チェック:

1. **Bind Pose Consistency**: `inverseBind * bindLocal = Identity`
2. **Weight Normalization**: `sum(boneWeight) = 1.0 ± 1e-4`
3. **Bone Index Range**: `0 <= boneIndex < boneCount`
4. **Index Range**: `0 <= index < vertexCount`
5. **Submesh Coverage**: 重複なし、隙間なし、3の倍数
6. **Material Index Range**: `0 <= materialIndex < materialSlotCount`

<a id="sec-52"></a>
### ローダー実装例

```cpp
void ParseGOM(const uint8_t* data, size_t fileSize) {
  // 1. ヘッダを読み込む（512 bytes）
  const GOMBHeader* header = reinterpret_cast<const GOMBHeader*>(data);
  ValidateHeader(header);
  
  // 2. チャンクを順次読み込む
  size_t offset = 512;
  
  while (offset < fileSize) {
    const ChunkHeader* chunk = reinterpret_cast<const ChunkHeader*>(data + offset);
    
    // サイズ検証
    if (offset + 16 + chunk->size > fileSize) {
      Error("Invalid chunk size");
      return;
    }
    
    // payload 処理
    const uint8_t* payload = data + offset + 16;
    
    switch (chunk->fourcc) {
      case FOURCC('STR0'):
        ParseSTR0(payload, chunk->size);
        break;
      case FOURCC('SKEL'):
        ParseSKEL(payload, chunk->size);
        break;
      // ... 他のチャンク
      default:
        // 未知チャンクはスキップ
        break;
    }
    
    // 次のチャンクに移動（16-byte アライメント）
    offset = ALIGN16(offset + 16 + chunk->size);
  }
}
```

<a id="sec-53"></a>
## テキストフォーマット

<a id="sec-54"></a>
### GOMT0001（GOM テキスト形式）

**概要**:

* UTF-8（BOMなし）
* 構造リテラル形式
* 人間が読み書き可能
* デバッグやプロトタイピングに便利

<a id="sec-55"></a>
#### 基本構造

```
Skeleton = { ... }
MaterialSlots = [ ... ]
Meshes = [ ... ]
Animations = [ ... ]
```

<a id="sec-56"></a>
#### Skeleton 例

```
Skeleton = {
  boneCount = 3,
  bones = [
    { parent = -1, name = "Root", path = "Root", bindLocal = [...] },
    { parent = 0, name = "Spine", path = "Root/Spine", bindLocal = [...] },
    { parent = 1, name = "Arm_L", path = "Root/Spine/Arm_L", bindLocal = [...] }
  ]
}
```

<a id="sec-57"></a>
#### MaterialSlots 例

```
MaterialSlots = [
  {
    name = "DefaultMaterial",
    phong = {
      kd = [1.0, 1.0, 1.0],
      ks = [0.04, 0.04, 0.04],
      ke = [0.0, 0.0, 0.0],
      shininess = 32.0,
      opacity = 1.0
    },
    maps = {
      diffuse = "textures/diffuse.png",
      normal = "",
      specular = "",
      emissive = ""
    }
  }
]
```

<a id="sec-58"></a>
#### Meshes 例

```
Meshes = [
  {
    name = "CubeMesh",
    meshBindGeoToModel = [...],  // 4x4 行列
    vertices = [
      { p = [x, y, z], n = [nx, ny, nz], t = [tx, ty, tz, tw], 
        uv = [u, v], bi = [0, 0, 0, 0], bw = [1.0, 0, 0, 0] },
      // ... 他の頂点
    ],
    indices = [0, 1, 2, 2, 3, 0, ...],
    submeshes = [
      { materialIndex = 0, indexStart = 0, indexCount = 36 }
    ],
    inverseBind = [ [...], [...], [...] ]  // boneCount 個の 4x4 行列
  }
]
```

<a id="sec-59"></a>
#### Animations 例

```
Animations = [
  {
    name = "WalkCycle",
    duration = 1.0,
    rootBoneIndex = 0,
    flags = 0,
    tracks = [
      {
        boneIndex = 0,
        trKeys = [ { time = 0.0, value = [0, 0, 0] }, ... ],
        rotKeys = [ { time = 0.0, value = [0, 0, 0, 1] }, ... ],
        scKeys = [ { time = 0.0, value = [1, 1, 1] }, ... ]
      },
      // ... 他のボーンのトラック
    ]
  }
]
```

<a id="sec-60"></a>
### MTA テキスト形式（v1 では非サポート）

**v1 では MTA のテキスト形式（MTAT0001）は存在しません**。

**理由**:

* 決定性保証の困難（改行コード、空白、エンコーディング等）
* パーサの複雑性増大
* v1 の設計方針（deterministic binary, AI safe）に集中

**v1 でサポートされる形式**:

* ✅ **MTAB0001**（バイナリ形式のみ）
* ❌ **MTAT0001**（テキスト形式は非サポート）

**v2 以降での検討事項**:

* テキスト形式が本当に必要かどうか（デバッグ用途）
* 必要な場合、canonical form をどう定義するか
* バイナリ形式で十分な場合、追加しない可能性もある

**v1 実装者への注意**:

* "MTAT0001" 以外のマジックナンバーは読み込みエラーとする
* エラーメッセージ例: "Unsupported MTA format. Only MTAB0001 is supported in v1."

<a id="sec-61"></a>
### テキスト形式とバイナリ形式の比較

|項目|テキスト形式|バイナリ形式|
|-|-|-|
|可読性|◎ 人間が読める|✕ バイナリ|
|ファイルサイズ|✕ 大きい|◎ 小さい|
|パース速度|△ 遅い|◎ 速い|
|デバッグ|◎ 簡単|△ ツール必要|
|プロトタイピング|◎ 手書き可能|✕ ツール必須|
|プロダクション|△ 非推奨|◎ 推奨|

**推奨用途**:

* **テキスト形式**: 開発中のデバッグ、プロトタイピング、テストデータ作成
* **バイナリ形式**: 最終ビルド、プロダクション環境、大規模データ



<a id="sec-62"></a>
### 変換ツール実装例

```cpp
// 1. FBX を読み込む（FBXSDK）
// 2. bonePath 辞書順でボーンをソート
// 3. meshNodePath 辞書順でメッシュをソート
// 4. materialKey 辞書順でマテリアルをソート
// 5. 頂点を geometry 空間に変換
// 6. ウェイトを正規化（最大4影響、合計1.0）
// 7. GOM/MTA を出力（16-byte アライン）
```

---

---

<a id="sec-63"></a>
## よくある質問（FAQ）

<a id="sec-64"></a>
### Q1: GOM と FBX の違いは？

**A**: GOM は FBX のゲーム実行用最適化版です。

|項目|FBX|GOM|
|-|-|-|
|実行時依存|FBXSDK 必要|不要|
|geometry 空間|ノードごとに異なる|統一|
|決定性|保証なし|保証あり|
|ファイルサイズ|大きい|小さい|
|パース速度|遅い|速い|

---

<a id="sec-65"></a>
### Q2: MTA は必須ですか？

**A**: いいえ、任意です。GOM 単独でも動作します。

**使い分け**:

* **GOM のみ**: ANIM チャンクにアニメーションを含める

  * 小規模プロジェクト、アニメーション数が少ない場合
* **GOM + MTA**: アニメーションを独立管理

  * 大規模プロジェクト、動的なアニメーション切り替えが必要な場合

---

<a id="sec-66"></a>
### Q3: テキスト形式とバイナリ形式、どちらを使うべき？

**A**: 用途によります。

|用途|推奨形式|理由|
|-|-|-|
|デバッグ|GOMT（テキスト）|人間が読める、Git diff が見やすい|
|プロトタイピング|GOMT（テキスト）|手書き可能、素早い修正|
|プロダクション|GOMB（バイナリ）|高速、ファイルサイズ小|
|MTA|MTAB（バイナリのみ）|v1 ではテキスト非サポート|

---

<a id="sec-67"></a>
### Q4: skeletonSig が一致しないエラーが出ます

**A**: 以下を確認してください：

**チェックリスト**:

1. **bonePath が一致しているか**

   * GOM と MTA で同じボーン階層か？
   * ボーン名が完全に一致しているか？
2. **parentIndex が一致しているか**

   * 親子関係が同じか？
3. **boneCount が一致しているか**

   * ボーン数が同じか？

**重要**: `bindLocal` が異なっていても、階層構造（bonePath + parentIndex）が同じなら適用可能です。

**デバッグ方法**:

```cpp
// GOM の skeletonSig を出力
uint8_t gom_sig[32];
compute_skeleton_sig(gom.SKEL, gom_sig);
printf("GOM skeletonSig: ");
for (int i = 0; i < 32; ++i) printf("%02x", gom_sig[i]);
printf("\n");

// MTA の skeletonSig を出力
printf("MTA skeletonSig: ");
for (int i = 0; i < 32; ++i) printf("%02x", mta.header.skeletonSig[i]);
printf("\n");
```

---

<a id="sec-68"></a>
### Q5: 複数の MTA ファイルを同時に使えますか？

**A**: はい、可能です。

**条件**:

1. すべて同じ `skeletonSig`
2. `clipName` が重複しない

**ファイル例**:

```
character.gom
character.idle.mta
character.walk.mta
character.run.mta
character.attack.mta
```

**使い方**:

```cpp
// すべての MTA を読み込む
LoadMTA("character.idle.mta");
LoadMTA("character.walk.mta");
LoadMTA("character.run.mta");

// clipName で再生
PlayClip("idle");
PlayClip("walk");
PlayClip("run");
```

**注意**: GOM と MTA で同じ clipName があるとエラーになります。

---

<a id="sec-69"></a>
### Q6: 頂点フォーマットは変更できますか？

**A**: v1 では VF01 のみサポートです。

**VF01 の内容**:

* position: float32 x3
* normal: float32 x3
* tangent: float32 x4
* uv0: float32 x2
* boneIndex: uint16 x4
* boneWeight: float32 x4

**v2 以降の拡張予定**:

* 複数 UV セット
* 頂点カラー
* 追加の tangent
* カスタム頂点属性

---

<a id="sec-70"></a>
### Q7: 決定性保証とは何ですか？

**A**: 同一の入力から常に同一の出力が生成されることを保証します。

**具体例**:

```bash
<a id="sec-71"></a>
# 同じ FBX を変換すると、常に同じ GOM が生成される
./converter input.fbx -o output1.gom
./converter input.fbx -o output2.gom
diff output1.gom output2.gom  # 差分なし
```

**保証される項目**:

* ボーンの順序（bonePath 辞書順）
* メッシュの順序（meshNodePath 辞書順）
* マテリアルの順序（materialKey 辞書順）
* アニメーションクリップの順序（clipName 辞書順）
* 浮動小数点値（float32 精度で一致）

**なぜ重要か**:

* バージョン管理（Git）で差分が明確
* 自動テストが安定
* ビルドの再現性が保証される

---

<a id="sec-72"></a>
### Q8: ウェイトが正規化されないとどうなりますか？

**A**: スキニング結果が破綻します。

**正常な場合**:

```
boneWeight = [0.5, 0.3, 0.2, 0.0]  // 合計 1.0
```

**異常な場合**:

```
boneWeight = [0.5, 0.3, 0.2, 0.1]  // 合計 1.1（スケールが変わる）
boneWeight = [0.5, 0.3, 0.1, 0.0]  // 合計 0.9（スケールが変わる）
```

**対策**:

* 変換ツールは必ずウェイトを正規化
* ローダーは `validationFlags` を確認（参考情報）
* ローダーは独自に検証（`sum(boneWeight) == 1.0 ± 1e-4`）

---

<a id="sec-73"></a>
## まとめ

<a id="sec-74"></a>
### GOM の利点

* FBX差を完全に吸収
* geometry 空間でスキニング完結
* 決定性保証（同一FBX→同一GOM）
* 実行時にFBXSDK不要

<a id="sec-75"></a>
### MTA の利点

* アニメーションの独立管理
* ファイルサイズ削減（SKEL省略）
* 異なるGOMでも適用可能（skeletonSig一致）
* 複数アニメーションの動的切り替え

<a id="sec-76"></a>
### 詳細仕様

詳細は以下を参照：

* **GOM 詳細仕様**: `Docs/GOM_Docs/spec_Sources/Original/INDEX.md`
* **MTA 詳細仕様**: `Docs/GOM_Docs/spec_Sources/Original/spec_binary/20_mta_format.md`
* **変更履歴**: `Docs/GOM_Docs/spec_Sources/Original/changes/CHANGELOG.md`

---

**作成日**: 2025-01-XX  
**バージョン**: GOM v1.0 / MTA v1.0  
**ライセンス**: プロジェクト固有

---

End of Quick Reference

# Visual Studioの更新うざいよね？

### 設定色々変えたけど、更新のお知らせが来るよ

<span style="white-space: pre-wrap;"> 表題のとおり、残念ながら何回もいろんな設定試したんだけど、更新のお知らせが来ます。</span>  
もうどうしようもないです。

仕方がないので、個人設定を変更してこの更新の通知を止めていきます。  
以下に、やり方を書くので各自やってみてください。  
（学校のPCだけね。おうちのPCはちゃんとアップデートすればいいと思うよ）

#### １．メニューから、ツール→オプションを選ぶ[![スクリーンショット 2026-05-07 120517.png](https://bookstack.yz-learning.com/uploads/images/gallery/2026-05/scaled-1680-/2026-05-07-120517.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2026-05/2026-05-07-120517.png)

#### ２．左の設定項目から、環境を選択

![スクリーンショット 2026-05-07 120551.png](https://bookstack.yz-learning.com/uploads/images/gallery/2026-05/scaled-1680-/2026-05-07-120551.png)#### ３．環境の設定項目がずらずら右ペインに出るので、一番下に行く

[![スクリーンショット 2026-05-07 120624.png](https://bookstack.yz-learning.com/uploads/images/gallery/2026-05/scaled-1680-/2026-05-07-120624.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2026-05/2026-05-07-120624.png)

#### ４．出てきたダイアログ（子ウィンドウ）の検索フォームに注目

[![スクリーンショット 2026-05-07 120648.png](https://bookstack.yz-learning.com/uploads/images/gallery/2026-05/scaled-1680-/2026-05-07-120648.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2026-05/2026-05-07-120648.png)

#### ５．検索フォームに「更新」と入力

[![スクリーンショット 2026-05-07 120700.png](https://bookstack.yz-learning.com/uploads/images/gallery/2026-05/scaled-1680-/2026-05-07-120700.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2026-05/2026-05-07-120700.png)

#### ６．「拡張機能マネージャー」を選択→更新プログラムを自動的にダウンロードする、のチェックを外す  
[![スクリーンショット 2026-05-07 120714.png](https://bookstack.yz-learning.com/uploads/images/gallery/2026-05/scaled-1680-/2026-05-07-120714.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2026-05/2026-05-07-120714.png)

#### ７．同じく「製品の更新プログラム」を選択→更新プログラムを自動的に。。。のチェックを外す

[![スクリーンショット 2026-05-07 120718.png](https://bookstack.yz-learning.com/uploads/images/gallery/2026-05/scaled-1680-/2026-05-07-120718.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2026-05/2026-05-07-120718.png)

#### ８．「OK」を押して設定完了。これで更新の通知が出ないはず。

### 動画で設定の様子を見るならこちら

youtubeボタン押して、でかい画面で見るがいいさ。

<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" height="315" src="https://www.youtube.com/embed/jG9guI9nLg0?si=N7G7Hug5wiOfNLPu" title="YouTube video player" width="560"></iframe>

# DxLibで３Dキャラクターの移動（基本）

## カメラの違いによるキャラクター移動操作

このサンプルでは、同じプレイヤー操作を使いながら、カメラの種類によって見え方や操作感がどのように変わるかを確認します。

使用するカメラは次の3種類です。

- 固定カメラ
- TPSカメラ
- OTSカメラ

<span style="white-space: pre-wrap;">カメラの種類は </span>`<span class="editor-theme-code">CAMERA_MODE</span>`<span style="white-space: pre-wrap;"> で管理しており、</span>`<span class="editor-theme-code">CAMERA_FIXED</span>`、`<span class="editor-theme-code">CAMERA_TPS</span>`、`<span class="editor-theme-code">CAMERA_OTS</span>`<span style="white-space: pre-wrap;"> の3種類が定義されています。</span>  
<span style="white-space: pre-wrap;">実行中は </span>`<span class="editor-theme-code">Cキー</span>`<span style="white-space: pre-wrap;"> を押すことで、固定カメラ → TPSカメラ → OTSカメラ の順に切り替わります。</span>  
サンプルプログラムをクローンして、実行してみてください。  
[https://github.com/youetsux/radioControlMan](https://github.com/youetsux/radioControlMan "サンプルプログラム")

---

### 共通のプレイヤー操作

プレイヤーの基本操作は、どのカメラでも共通です。

<table id="bkmrk-%E3%82%AD%E3%83%BC%E5%8B%95%E4%BD%9Cw%E5%89%8D%E9%80%B2s%E5%BE%8C%E9%80%80a%E5%B7%A6%E5%9B%9E%E8%BB%A2d%E5%8F%B3%E5%9B%9E%E8%BB%A2c%E3%82%AB"><colgroup><col></col><col></col></colgroup><tbody><tr><th>キー

</th><th>動作

</th></tr><tr><td>W

</td><td>前進

</td></tr><tr><td>S

</td><td>後退

</td></tr><tr><td>A

</td><td>左回転

</td></tr><tr><td>D

</td><td>右回転

</td></tr><tr><td>C

</td><td>カメラ切り替え

</td></tr></tbody></table>

<span style="white-space: pre-wrap;">プレイヤーは </span>`<span class="editor-theme-code">Aキー</span>`<span style="white-space: pre-wrap;"> と </span>`<span class="editor-theme-code">Dキー</span>`<span style="white-space: pre-wrap;"> でY軸方向に回転します。</span>  
<span style="white-space: pre-wrap;">その回転角度から、現在の進行方向ベクトル </span>`<span class="editor-theme-code">dir_</span>`<span style="white-space: pre-wrap;"> を計算しています。</span>

```
float rad = DirectX::XMConvertToRadians(rotY_);dir_ = VGet(sinf(rad), 0.0f, cosf(rad));
```

`<span class="editor-theme-code">Wキー</span>`<span style="white-space: pre-wrap;"> を押すと、進行方向 </span>`<span class="editor-theme-code">dir_</span>`<span style="white-space: pre-wrap;"> に向かって前進します。</span>  
`<span class="editor-theme-code">Sキー</span>`<span style="white-space: pre-wrap;"> を押すと、進行方向と逆向きに後退します。</span>

```
pos_ = VAdd(pos_, VScale(dir_, speed_ * dt));
```

つまり、このサンプルの移動は「カメラの向き」ではなく、「キャラクター自身の向き」を基準にしています。

---

## 固定カメラでのラジコン移動操作

### 固定カメラとは

固定カメラは、カメラの位置と注視点が常に決まっているカメラです。

<span style="white-space: pre-wrap;">このサンプルでは、カメラ位置を </span>`<span class="editor-theme-code">(0, 10, -10)</span>`<span style="white-space: pre-wrap;">、注視点を </span>`<span class="editor-theme-code">(0, 0, 0)</span>`<span style="white-space: pre-wrap;"> に固定しています。</span>

```
VECTOR FIXED_POS    = VGet(0.0f, 10.0f, -10.0f);VECTOR FIXED_TARGET = VGet(0.0f, 0.0f, 0.0f);
```

固定カメラモードでは、プレイヤーが移動してもカメラは動きません。  
常に同じ位置からステージを見下ろすような見え方になります。

```
SetCameraPositionAndTarget_UpVecY(FIXED_POS, FIXED_TARGET);
```

### 固定カメラのサンプル動画

<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" height="315" src="https://www.youtube.com/embed/LUn4j4wpv4I?si=lA3FKp4a6dmkfVeu" title="YouTube video player" width="560"></iframe>

### ラジコン操作の特徴

固定カメラでは、画面の上方向とキャラクターの前方向が常に一致するとは限りません。

そのため、操作感としてはラジコンに近くなります。

- `<span class="editor-theme-code">Aキー</span>`：キャラクターが左に向きを変える
- `<span class="editor-theme-code">Dキー</span>`：キャラクターが右に向きを変える
- `<span class="editor-theme-code">Wキー</span>`：キャラクターが向いている方向へ進む
- `<span class="editor-theme-code">Sキー</span>`：キャラクターが向いている方向の逆へ下がる

<span style="white-space: pre-wrap;">例えば、キャラクターが画面手前を向いている状態で </span>`<span class="editor-theme-code">Wキー</span>`<span style="white-space: pre-wrap;"> を押すと、画面上では手前方向に移動します。</span>  
これは、`<span class="editor-theme-code">Wキー</span>`<span style="white-space: pre-wrap;"> が「画面の上へ進む」ではなく、「キャラクターの前へ進む」操作だからです。</span>

### 固定カメラのメリット

固定カメラは、ステージ全体の位置関係を把握しやすいのが特徴です。

プレイヤーがどこにいてもカメラが動かないため、背景や地形の見え方が安定します。  
パズルゲーム、見下ろし型アクション、昔の3Dアドベンチャーゲームなどで使いやすい方式です。

一方で、キャラクターの向きと画面方向がずれるため、慣れるまでは操作が難しく感じることがあります。

---

## TPSカメラによるキャラクター移動

### TPSカメラとは

TPSカメラは、キャラクターの後ろから追いかけるカメラです。  
TPSは Third Person Shooter、または Third Person View のように、三人称視点のゲームでよく使われます。

<span style="white-space: pre-wrap;">このサンプルでは、プレイヤーの進行方向 </span>`<span class="editor-theme-code">playerDir</span>`<span style="white-space: pre-wrap;"> の逆方向にカメラを配置しています。</span>  
つまり、キャラクターの背後にカメラが来るようになっています。

```
VECTOR back = VScale(playerDir, -TPS_DISTANCE);VECTOR camPos = VAdd(playerPos, back);camPos.y += TPS_HEIGHT;
```

<span style="white-space: pre-wrap;">カメラの高さは </span>`<span class="editor-theme-code">TPS_HEIGHT</span>`<span style="white-space: pre-wrap;">、キャラクターからの距離は </span>`<span class="editor-theme-code">TPS_DISTANCE</span>`<span style="white-space: pre-wrap;"> で調整しています。</span>

```
float TPS_DISTANCE = 8.0f;float TPS_HEIGHT   = 4.0f;
```

カメラは、プレイヤーの少し上あたりを見るように設定されています。

```
VECTOR target = VAdd(playerPos, VGet(0.0f, TPS_HEIGHT * 0.5f, 0.0f));SetCameraPositionAndTarget_UpVecY(camPos, target);
```

### TPSカメラサンプル動画

<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" height="315" src="https://www.youtube.com/embed/GKnTGfPF0XU?si=hhVZjsAGT-cZIE94" title="YouTube video player" width="560"></iframe>

### TPSカメラでの操作感

TPSカメラでは、カメラがキャラクターの後ろに回り込むため、キャラクターの前方向が画面奥方向になりやすくなります。

そのため、`<span class="editor-theme-code">Wキー</span>`<span style="white-space: pre-wrap;"> を押したときに「画面の奥へ進む」ように見えます。</span>  
固定カメラよりも、プレイヤーの進行方向が直感的に分かりやすくなります。

ただし、このサンプルではカメラがマウス操作で自由に回る方式ではありません。  
キャラクターの向きに合わせてカメラが後ろへ回り込む形です。

### TPSカメラのメリット

TPSカメラは、キャラクターの進行方向と画面の見え方が一致しやすいため、アクションゲームで使いやすいカメラです。

- キャラクターの前方が見やすい
- 移動方向を把握しやすい
- プレイヤーと周囲の位置関係を見やすい
- 3Dアクションや探索ゲームに向いている

固定カメラと比べると、キャラクターを中心にした操作感になります。

---

## OTSカメラによるキャラクター移動

### OTSカメラとは

OTSカメラは、Over The Shoulder の略で、キャラクターの肩越しに見るカメラです。

TPSカメラと似ていますが、カメラがキャラクターの真後ろではなく、少し右側や左側にずれた位置に置かれます。  
このサンプルでは、プレイヤーの後ろに下がりつつ、右方向にオフセットした位置へカメラを配置しています。

```
VECTOR up = VGet(0.0f, 1.0f, 0.0f);VECTOR right = VNorm(VCross(playerDir, up));VECTOR camPos = playerPos;camPos = VAdd(camPos, VScale(playerDir, -OTS_DISTANCE));camPos = VAdd(camPos, VScale(right, OTS_RIGHT_OFFSET));camPos.y += OTS_HEIGHT;
```

OTSカメラ用の距離、高さ、横方向のずれは、次の値で設定しています。

```
float OTS_DISTANCE     = 5.0f;float OTS_HEIGHT       = 2.5f;float OTS_RIGHT_OFFSET = 2.0f;
```

### OTSカメラのサンプル

<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" height="315" src="https://www.youtube.com/embed/sV5KZHQ82qk?si=CsKKrdsjErNt6jKI" title="YouTube video player" width="560"></iframe>

### 肩越し視点の作り方

<span style="white-space: pre-wrap;">OTSカメラでは、まずプレイヤーの進行方向 </span>`<span class="editor-theme-code">playerDir</span>`<span style="white-space: pre-wrap;"> から、右方向ベクトル </span>`<span class="editor-theme-code">right</span>`<span style="white-space: pre-wrap;"> を作っています。</span>

```
VECTOR right = VNorm(VCross(playerDir, up));
```

その後、カメラ位置を次のように調整しています。

1. プレイヤー位置を基準にする
2. プレイヤーの後ろに下げる
3. 右方向にずらす
4. 高さを加える

これにより、キャラクターの背後やや右側から見る「肩越し視点」になります。

さらに、注視点も少し左側にずらしています。  
これにより、キャラクターが画面の中央に完全に重なるのではなく、少し横に寄った構図になります。

```
VECTOR target = playerPos;target = VAdd(target, VScale(right, -OTS_RIGHT_OFFSET * 0.5f));target.y += OTS_HEIGHT * 0.5f;
```

### OTSカメラでの操作感

OTSカメラでは、キャラクターの近くから肩越しに前方を見るため、プレイヤーの視点に近い感覚になります。

TPSカメラよりもキャラクターに近いため、前方の対象物や敵を狙うようなゲームに向いています。

- シューティング
- アクション
- ホラーゲーム
- 近距離でキャラクターを見せたいゲーム

などでよく使われます。

一方で、カメラがキャラクターに近いため、周囲全体の状況はTPSカメラより少し見づらくなります。

---

## 3種類のカメラの比較

<table id="bkmrk-%E3%82%AB%E3%83%A1%E3%83%A9%E3%82%AB%E3%83%A1%E3%83%A9%E3%81%AE%E4%BD%8D%E7%BD%AE%E6%93%8D%E4%BD%9C%E6%84%9F%E5%90%91%E3%81%84%E3%81%A6%E3%81%84%E3%82%8B%E3%82%B2%E3%83%BC%E3%83%A0"><colgroup><col></col><col></col><col></col><col></col></colgroup><tbody><tr><th>カメラ

</th><th>カメラの位置

</th><th>操作感

</th><th>向いているゲーム

</th></tr><tr><td>固定カメラ

</td><td>常に固定位置

</td><td>ラジコン操作に近い

</td><td>パズル、見下ろし型、固定画面アクション

</td></tr><tr><td>TPSカメラ

</td><td>キャラクターの後ろ

</td><td>進行方向が分かりやすい

</td><td>3Dアクション、探索ゲーム

</td></tr><tr><td>OTSカメラ

</td><td>キャラクターの肩越し

</td><td>狙う・見る感覚が強い

</td><td>TPS、ホラー、アクションシューティング

</td></tr></tbody></table>

---

## プログラム全体の流れ

<span style="white-space: pre-wrap;">このサンプルでは、プレイヤーの </span>`<span class="editor-theme-code">Update()</span>`<span style="white-space: pre-wrap;"> の最後で、プレイヤーの現在位置 </span>`<span class="editor-theme-code">pos_</span>`<span style="white-space: pre-wrap;"> と進行方向 </span>`<span class="editor-theme-code">dir_</span>`<span style="white-space: pre-wrap;"> をカメラに渡しています。</span>

```
Camera::Update(pos_, dir_);
```

カメラ側では、その位置と向きを使って、現在のカメラモードに応じたカメラ位置を計算しています。

つまり、処理の流れは次のようになります。

1. キー入力を確認する
2. プレイヤーの回転角度を更新する
3. <span style="white-space: pre-wrap;">回転角度から進行方向 </span>`<span class="editor-theme-code">dir_</span>`<span style="white-space: pre-wrap;"> を作る</span>
4. `<span class="editor-theme-code">W</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">S</span>`<span style="white-space: pre-wrap;"> キーでプレイヤー位置を移動する</span>
5. プレイヤーの位置と向きをモデルに反映する
6. プレイヤーの位置と進行方向をカメラに渡す
7. カメラモードに応じてカメラ位置を更新する

---

## まとめ

固定カメラ、TPSカメラ、OTSカメラは、同じキャラクター移動を使っていても、プレイヤーに与える操作感が大きく変わります。固定カメラでは、カメラが動かないため、キャラクター自身の向きに合わせて操作するラジコン操作になります。TPSカメラでは、カメラがキャラクターの後ろに回り込むため、進行方向が分かりやすく、3Dアクション向きの操作感になります。OTSカメラでは、キャラクターの肩越しから見ることで、狙う・注視する感覚が強くなり、シューティングやホラーゲームのような演出に向いています。

<p class="callout info">おまけ</p>

## 固定カメラ＋固定方向（上下左右）移動＋なめらかな角度変化

ここまでの固定カメラでは、キャラクターの向きを基準にして前進・後退する「ラジコン操作」を説明しました。

このおまけでは、固定カメラ用の別操作として、****画面方向に対して固定された移動****を行います。

サンプルプログラムのURLは  
[https://github.com/youetsux/radioControllman2](https://github.com/youetsux/radioControllman2 "固定方向移動")

ラジコン操作では、

- `<span class="editor-theme-code">A</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">D</span>`<span style="white-space: pre-wrap;"> でキャラクターの向きを変える</span>
- `<span class="editor-theme-code">W</span>`<span style="white-space: pre-wrap;"> でキャラクターの向いている方向へ進む</span>
- `<span class="editor-theme-code">S</span>`<span style="white-space: pre-wrap;"> でキャラクターの後ろへ下がる</span>

という操作でした。

それに対して、固定方向移動では、

- `<span class="editor-theme-code">W</span>`：奥へ移動
- `<span class="editor-theme-code">S</span>`：手前へ移動
- `<span class="editor-theme-code">A</span>`：左へ移動
- `<span class="editor-theme-code">D</span>`：右へ移動

のように、キーと移動方向を固定します。

固定カメラの場合、カメラの位置が変わらないため、画面上の上下左右とワールド座標の前後左右を対応させやすくなります。

<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" height="315" src="https://www.youtube.com/embed/c2bLJ5hDIKI?si=lrivtBicecaltv-3" title="YouTube video player" width="560"></iframe>

---

### 固定カメラのときだけ移動処理を切り替える

<span style="white-space: pre-wrap;">このサンプルでは、カメラモードが固定カメラのときだけ </span>`<span class="editor-theme-code">UpdateFixedCameraMove()</span>`<span style="white-space: pre-wrap;"> を使い、それ以外のカメラでは </span>`<span class="editor-theme-code">UpdateFreeCameraMove()</span>`<span style="white-space: pre-wrap;"> を使っています。</span>

```
if (Camera::GetMode() == CAMERA_FIXED){	UpdateFixedCameraMove(dt);}else{	UpdateFreeCameraMove(dt);}
```

これにより、固定カメラでは画面方向に合わせた移動、TPSカメラやOTSカメラではキャラクターの向きを基準にした移動、というように操作方法を分けています。

---

### ワールド固定の前後左右ベクトルを用意する

固定方向移動では、まずワールド空間での前方向と右方向を決めています。

```
const VECTOR forward = VGet(0.0f, 0.0f, 1.0f);const VECTOR right   = VGet(1.0f, 0.0f, 0.0f);
```

ここでは、

- `<span class="editor-theme-code">forward</span>`<span style="white-space: pre-wrap;"> がZ軸プラス方向</span>
- `<span class="editor-theme-code">right</span>`<span style="white-space: pre-wrap;"> がX軸プラス方向</span>

を表しています。

つまり、キャラクターが今どちらを向いているかに関係なく、`<span class="editor-theme-code">W</span>`<span style="white-space: pre-wrap;"> を押したらZ軸プラス方向、</span>`<span class="editor-theme-code">D</span>`<span style="white-space: pre-wrap;"> を押したらX軸プラス方向へ移動するようになります。</span>

---

### キー入力から移動方向を作る

<span style="white-space: pre-wrap;">次に、押されているキーに応じて移動方向 </span>`<span class="editor-theme-code">moveVec</span>`<span style="white-space: pre-wrap;"> を作ります。</span>

```
VECTOR moveVec = VGet(0.0f, 0.0f, 0.0f);if (Input::IsKeepKeyDown(KEY_INPUT_W)) moveVec = VAdd(moveVec, forward);if (Input::IsKeepKeyDown(KEY_INPUT_S)) moveVec = VAdd(moveVec, VScale(forward, -1.0f));if (Input::IsKeepKeyDown(KEY_INPUT_D)) moveVec = VAdd(moveVec, right);if (Input::IsKeepKeyDown(KEY_INPUT_A)) moveVec = VAdd(moveVec, VScale(right, -1.0f));
```

この処理では、押されたキーに対応する方向ベクトルを足し合わせています。

例えば、`<span class="editor-theme-code">W</span>`<span style="white-space: pre-wrap;"> と </span>`<span class="editor-theme-code">D</span>`<span style="white-space: pre-wrap;"> を同時に押した場合は、</span>

```
forward + right
```

となるため、右奥方向への移動になります。

---

### 斜め移動が速くならないように正規化する

`<span class="editor-theme-code">W</span>`<span style="white-space: pre-wrap;"> だけを押した場合、移動方向の長さは </span>`<span class="editor-theme-code">1</span>`<span style="white-space: pre-wrap;"> です。</span>

しかし、`<span class="editor-theme-code">W</span>`<span style="white-space: pre-wrap;"> と </span>`<span class="editor-theme-code">D</span>`<span style="white-space: pre-wrap;"> を同時に押した場合、移動方向は斜めになります。</span>  
このまま移動すると、斜め移動だけ距離が長くなり、移動速度が速くなってしまいます。

そこで、移動方向の長さを求めています。

```
float moveLen = sqrtf(moveVec.x * moveVec.x + moveVec.z * moveVec.z);if (moveLen < 0.0001f) return;VECTOR moveDir = VScale(moveVec, 1.0f / moveLen);
```

`<span class="editor-theme-code">moveLen</span>`<span style="white-space: pre-wrap;"> がほぼ0の場合は、何もキーが押されていない状態なので移動処理を終了します。</span>

キー入力がある場合は、`<span class="editor-theme-code">moveVec</span>`<span style="white-space: pre-wrap;"> を </span>`<span class="editor-theme-code">moveLen</span>`<span style="white-space: pre-wrap;"> で割って、長さ1のベクトルにしています。</span>  
これを****正規化****といいます。

正規化することで、上下左右に移動しても、斜めに移動しても、同じ速度で移動できます。

---

### 移動方向へキャラクターを向ける

固定方向移動では、キャラクターの向きで移動方向を決めるのではなく、入力された移動方向へキャラクターを向けます。

<span style="white-space: pre-wrap;">そのため、まず移動方向 </span>`<span class="editor-theme-code">moveDir</span>`<span style="white-space: pre-wrap;"> から目標のY軸回転角 </span>`<span class="editor-theme-code">targetRotY</span>`<span style="white-space: pre-wrap;"> を計算します。</span>

```
float targetRotY = DirectX::XMConvertToDegrees(atan2f(moveDir.x, moveDir.z));
```

`<span class="editor-theme-code">atan2f()</span>`<span style="white-space: pre-wrap;"> を使うことで、X方向とZ方向の成分から、キャラクターが向くべき角度を求めています。</span>

例えば、

<table id="bkmrk-%E5%85%A5%E5%8A%9B%E7%A7%BB%E5%8B%95%E6%96%B9%E5%90%91%E3%82%AD%E3%83%A3%E3%83%A9%E3%82%AF%E3%82%BF%E3%83%BC%E3%81%AE%E5%90%91%E3%81%8Dw%E5%A5%A5%E5%A5%A5%E3%82%92%E5%90%91"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th>入力

</th><th>移動方向

</th><th>キャラクターの向き

</th></tr><tr><td>W

</td><td>奥

</td><td>奥を向く

</td></tr><tr><td>S

</td><td>手前

</td><td>手前を向く

</td></tr><tr><td>A

</td><td>左

</td><td>左を向く

</td></tr><tr><td>D

</td><td>右

</td><td>右を向く

</td></tr><tr><td>W + D

</td><td>右奥

</td><td>右奥を向く

</td></tr></tbody></table>

という動きになります。

### 急に向きを変えず、なめらかに回転させる

<span style="white-space: pre-wrap;">目標角度をそのまま </span>`<span class="editor-theme-code">rotY_</span>`<span style="white-space: pre-wrap;"> に代入すると、キャラクターの向きが一瞬で切り替わってしまいます。</span>

<span style="white-space: pre-wrap;">そこで、このサンプルでは現在の角度 </span>`<span class="editor-theme-code">rotY_</span>`<span style="white-space: pre-wrap;"> と目標角度 </span>`<span class="editor-theme-code">targetRotY</span>`<span style="white-space: pre-wrap;"> の差を求め、少しずつ回転するようにしています。</span>

```
float diff = targetRotY - rotY_;while (diff >  180.0f) diff -= 360.0f;while (diff < -180.0f) diff += 360.0f;float rotStep = rotYSpeed_ * dt;rotY_ += (fabsf(diff) < rotStep) ? diff : (diff > 0.0f ? rotStep : -rotStep);
```

<span style="white-space: pre-wrap;">角度の差が </span>`<span class="editor-theme-code">180度</span>`<span style="white-space: pre-wrap;"> を超える場合は、逆回りした方が近いことがあります。</span>  
そのため、`<span class="editor-theme-code">while</span>`<span style="white-space: pre-wrap;"> 文で角度差を </span>`<span class="editor-theme-code">-180度 ～ 180度</span>`<span style="white-space: pre-wrap;"> の範囲に収めています。</span>

<span style="white-space: pre-wrap;">その後、1フレームで回転できる量 </span>`<span class="editor-theme-code">rotStep</span>`<span style="white-space: pre-wrap;"> を使って、少しずつ目標方向へ回転します。</span>

これにより、キーを押した方向へキャラクターが自然に向き直るようになります。

---

### 実際に移動する

<span style="white-space: pre-wrap;">最後に、正規化した移動方向 </span>`<span class="editor-theme-code">moveDir</span>`<span style="white-space: pre-wrap;"> に速度とデルタタイムを掛けて、プレイヤーの位置に加算します。</span>

```
pos_ = VAdd(pos_, VScale(moveDir, speed_ * dt));dir_ = moveDir;
```

`<span class="editor-theme-code">pos_</span>`<span style="white-space: pre-wrap;"> がプレイヤーの位置です。</span>  
`<span class="editor-theme-code">moveDir</span>`<span style="white-space: pre-wrap;"> の方向へ </span>`<span class="editor-theme-code">speed_ * dt</span>`<span style="white-space: pre-wrap;"> 分だけ移動します。</span>

また、`<span class="editor-theme-code">dir_</span>`<span style="white-space: pre-wrap;"> にも </span>`<span class="editor-theme-code">moveDir</span>`<span style="white-space: pre-wrap;"> を入れています。</span>  
これは、カメラ更新時にプレイヤーの進行方向として使うためです。

<span style="white-space: pre-wrap;">プレイヤーの </span>`<span class="editor-theme-code">Update()</span>`<span style="white-space: pre-wrap;"> の最後では、現在位置 </span>`<span class="editor-theme-code">pos_</span>`<span style="white-space: pre-wrap;"> と進行方向 </span>`<span class="editor-theme-code">dir_</span>`<span style="white-space: pre-wrap;"> をカメラに渡しています。</span>

```
Camera::Update(pos_, dir_);
```

---

## ラジコン操作と固定方向移動の違い

<table id="bkmrk-%E6%93%8D%E4%BD%9C%E6%96%B9%E5%BC%8F%E7%A7%BB%E5%8B%95%E6%96%B9%E5%90%91%E3%81%AE%E5%9F%BA%E6%BA%96%E7%89%B9%E5%BE%B4%E3%83%A9%E3%82%B8%E3%82%B3%E3%83%B3%E6%93%8D%E4%BD%9C%E3%82%AD"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th>操作方式

</th><th>移動方向の基準

</th><th>特徴

</th></tr><tr><td>ラジコン操作

</td><td>キャラクターの向き

</td><td>向きを変えてから前進する

</td></tr><tr><td>固定方向移動

</td><td>ワールド座標・画面方向

</td><td>押したキーの方向へ直接移動する

</td></tr></tbody></table>

ラジコン操作は、キャラクターを「操作している」感覚が強い方式です。  
一方、固定方向移動は、キャラクターを画面上で直接動かしている感覚に近い方式です。

固定カメラのゲームでは、カメラの向きが変わらないため、固定方向移動の方が直感的に操作しやすい場合があります。

ただし、カメラが回転するゲームで固定方向移動をそのまま使うと、画面方向と移動方向がずれて分かりにくくなることがあります。  
そのため、このサンプルでは固定カメラのときだけ固定方向移動を使い、TPSカメラやOTSカメラでは通常のキャラクター向き基準の移動に戻しています。

# AIの課題（５月１２日）

## 見張りモンスターAIを設計せよ

### ねらい

```
敵は何を見ているのか？
↓
それをどんなフラグにするのか？
↓
フラグによってどの行動を選ぶのか？
↓
それをif文にするとどうなるのか？
```

---

## 課題タイトル

### 「お宝を守るモンスターを設計しよう」

---

## シチュエーション

あなたはダンジョンゲームの敵AIを作ることになった。

モンスターは部屋の中にある****宝箱****を守っている。

プレイヤーは部屋に侵入して、宝箱を開けようとする。

モンスターは状況に応じて行動を変える。

---

## モンスターの基本行動

使ってよい行動は以下。

```c++
Idle       その場で待つ
Patrol     決められた範囲を歩く
Chase      プレイヤーを追いかける
Attack     プレイヤーを攻撃する
Return     持ち場に戻る
Guard      宝箱の前に立つ
Search     プレイヤーを探す
```

---

## 使ってよいフラグ

最初はこれだけ与えるとよいです。

```
bool isFindPlayer;       // プレイヤーを見つけた
bool isNearPlayer;       // プレイヤーが近い
bool isAttackRange;      // 攻撃範囲にいる
bool isPlayerNearTreasure; // プレイヤーが宝箱に近い
bool isFarFromHome;      // 持ち場から離れすぎた
bool isLostPlayer;       // プレイヤーを見失った
```

---

## 考えること！

### 1. どの行動を優先するか

例えば、

```
攻撃できるなら攻撃
宝箱に近づいたら追い払う
見つけたら追跡
見失ったら探す
遠くまで行きすぎたら戻る
普段は巡回
```

この優先順位を考える。

---

### 2. if文に直す

最終的にこういう形にする。

```
if (isAttackRange)
{
    Attack();
}
else if (isPlayerNearTreasure)
{
    Chase();
}
else if (isFindPlayer)
{
    Chase();
}
else if (isLostPlayer)
{
    Search();
}
else if (isFarFromHome)
{
    Return();
}
else
{
    Patrol();
}
```

---

# 自習の進め方

## 0〜10分

## 課題説明を読む

配布資料に以下を載せる。

```
あなたはゲームAI担当です。
宝箱を守るモンスターの行動ルールを設計してください。
```

考えるゴールは、

```
フラグ
↓
条件分岐
↓
行動
```

です。

---

## 10〜25分

## ゲーム内状況を洗い出す

### 問い

```
モンスターが判断に使いそうな情報は何か？
```

例：

```
プレイヤーとの距離
プレイヤーの方向
宝箱との距離
自分の持ち場からの距離
HP
プレイヤーを見失った時間
```

---

## 25〜40分

## フラグを作る

<span style="white-space: pre-wrap;">洗い出した情報を </span>`<span class="editor-theme-code">bool</span>`<span style="white-space: pre-wrap;"> に変換する。</span>

例：

```
プレイヤーとの距離が近い
↓
isNearPlayer

攻撃できる距離にいる
↓
isAttackRange

宝箱に近づいている
↓
isPlayerNearTreasure
```

---

## 40〜55分

## 行動の優先順位を決める

ここが面白いところです。

行動パターンでモンスター（AI)の性格が変わる。

### 例1：短気なモンスター

```
攻撃
追跡
探索
巡回
```

### 例2：宝箱優先モンスター

```
宝箱防衛
攻撃
追跡
持ち場に戻る
巡回
```

### 例3：臆病なモンスター

```
宝箱防衛
攻撃
追跡
持ち場に戻る
巡回
```

---

## 55〜70分

## if文にする

疑似コードで書いてみる

```
if (__________)
{
    __________();
}
else if (__________)
{
    __________();
}
else
{
    __________();
}
```

# AI　モンスターの知能

# モンスターに視覚を与える

## ****isFindPlayer****<span style="white-space: pre-wrap;"> は誰が決めているのか？</span>

---

## 1. 前回までの確認

<span style="white-space: pre-wrap;">前回、モンスターの行動を </span>`<span class="editor-theme-code">if文</span>`<span style="white-space: pre-wrap;"> で切り替える考え方を学びました。</span>

例えば、次のような処理です。

```cpp
if (isAttackRange)
{
    Attack();
}
else if (isFindPlayer)
{
    ChasePlayer();
}
else
{
    Idle();
}
```

この処理では、モンスターは次のように行動します。

<table id="bkmrk-%E6%9D%A1%E4%BB%B6%E8%A1%8C%E5%8B%95%E6%94%BB%E6%92%83%E7%AF%84%E5%9B%B2%E3%81%AB%E3%81%84%E3%82%8B%E6%94%BB%E6%92%83%E3%81%99%E3%82%8B%E3%83%97%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC"><colgroup><col></col><col></col></colgroup><tbody><tr><th>条件

</th><th>行動

</th></tr><tr><td>攻撃範囲にいる

</td><td>攻撃する

</td></tr><tr><td>プレイヤーを見つけている

</td><td>追いかける

</td></tr><tr><td>どちらでもない

</td><td>待機する

</td></tr></tbody></table>

このように、条件によって行動を変えることで、モンスターが考えて動いているように見えます。

---

## 2. ここで問い

次の変数に注目してください。

```cpp
isFindPlayer
```

これは、

```text
プレイヤーを見つけているか？
```

を表すフラグです。

しかし、ここで考えるべきことがあります。

---

# `<span class="editor-theme-code">isFindPlayer</span>`<span style="white-space: pre-wrap;"> は誰が決めているのか？</span>

---

プログラムの中で、

```cpp
if (isFindPlayer)
```

と書くことはできます。

しかし、`<span class="editor-theme-code">isFindPlayer</span>`<span style="white-space: pre-wrap;"> が </span>`<span class="editor-theme-code">true</span>`<span style="white-space: pre-wrap;"> になる理由を作らなければ、モンスターはプレイヤーを見つけることができません。</span>

つまり、

```cpp
isFindPlayer = true;
```

になる条件を、こちらで作る必要があります。

---

## 3. ゲームAIには「知覚」が必要

モンスターが行動を決めるには、まずゲームの状況を知る必要があります。

例えば、

- プレイヤーが近くにいる
- プレイヤーが前方にいる
- プレイヤーとの間に壁がない
- プレイヤーが音を立てた
- プレイヤーが攻撃してきた

などです。

<span style="white-space: pre-wrap;">このように、AIがゲームの状況を知る仕組みを </span>****知覚****<span style="white-space: pre-wrap;"> と呼びます。</span>

---

## 4. 人間の「見る」とAIの「見る」は違う

人間は目で映像を見ています。

しかし、ゲームAIは本当に画面を見ているわけではありません。

ゲームAIは、次のような数値や条件を使って「見えているか」を判定します。

```text
距離は近いか？
前方にいるか？
壁に隠れていないか？
```

つまり、ゲームAIの視覚とは、

```text
画像を見ること
```

ではなく、

```text
条件を満たしたら「見えている」とする処理
```

です。

---

## 5. まず一番簡単な視覚を作る

最初は、距離だけで考えます。

例えば、

```text
プレイヤーが10m以内にいたら発見
```

というルールにします。

この場合、モンスターはプレイヤーとの距離を調べます。

```cpp
VECTOR toPlayer = VSub(playerPos, enemyPos);
float distance = VSize(toPlayer);

if (distance < VIEW_DISTANCE)
{
    isFindPlayer = true;
}
else
{
    isFindPlayer = false;
}
```

ここで、`<span class="editor-theme-code">VIEW_DISTANCE</span>`<span style="white-space: pre-wrap;"> は視界距離です。</span>

```cpp
const float VIEW_DISTANCE = 10.0f;
```

---

## 6. 距離だけの視覚の問題点

距離だけで判定すると、かなり単純なAIになります。

例えば、次のような問題があります。

---

### 問題1：後ろにいても見つかる

距離だけで判定すると、プレイヤーがモンスターの後ろにいても見つかってしまいます。

```text
プレイヤー
   ↓
モンスター → 前を向いている
```

本当なら、モンスターの後ろにいるプレイヤーは見えないはずです。

でも距離だけで判定していると、近ければ見つかります。

---

### 問題2：壁越しでも見つかる

距離だけで判定すると、壁の向こうにいるプレイヤーも見つかってしまいます。

```text
モンスター　｜壁｜　プレイヤー
```

本当なら、壁があるので見えないはずです。

でも距離だけで判定していると、近ければ見つかります。

---

## 7. 視覚に必要な条件

より自然な視覚を作るには、次の3つを考えます。

```text
1. 距離
2. 向き
3. 障害物
```

---

## 8. 条件1：距離

まずは距離です。

```text
プレイヤーが遠すぎるなら見えない
```

これは分かりやすい条件です。

```cpp
if (distance > VIEW_DISTANCE)
{
    isFindPlayer = false;
}
```

---

## 9. 条件2：向き

次に、プレイヤーがモンスターの前方にいるかを調べます。

モンスターには向きがあります。

```text
モンスター → 前方
```

プレイヤーが前方にいれば見える。

```text
モンスター → プレイヤー
```

しかし、プレイヤーが後ろにいるなら見えません。

```text
プレイヤー ← モンスター
```

---

## 10. 前方にいるかをどう調べるか

<span style="white-space: pre-wrap;">ここで使うのが </span>****内積****<span style="white-space: pre-wrap;"> です。</span>

必要なベクトルは2つです。

```text
モンスターの前方向
モンスターからプレイヤーへの方向
```

---

## 11. モンスターからプレイヤーへの方向

まず、モンスターからプレイヤーへのベクトルを求めます。

```cpp
VECTOR toPlayer = VSub(playerPos, enemyPos);
```

次に、長さを1にします。

```cpp
float distance = VSize(toPlayer);
VECTOR dirToPlayer = VScale(toPlayer, 1.0f / distance);
```

<span style="white-space: pre-wrap;">この </span>`<span class="editor-theme-code">dirToPlayer</span>`<span style="white-space: pre-wrap;"> が、モンスターから見たプレイヤーの方向です。</span>

---

## 12. モンスターの前方向

次に、モンスターの前方向を用意します。

例として、Y軸回転から前方向を作るなら、次のようになります。

```cpp
VECTOR forward =
{
    sinf(enemyRotY),
    0.0f,
    cosf(enemyRotY)
};
```

ただし、モデルの正面方向や座標系によって、`<span class="editor-theme-code">sinf</span>`<span style="white-space: pre-wrap;"> と </span>`<span class="editor-theme-code">cosf</span>`<span style="white-space: pre-wrap;"> の使い方は変わることがあります。</span>

---

## 13. 内積で角度を調べる

2つのベクトルの内積を取ります。

```cpp
float dot = VDot(forward, dirToPlayer);
```

`<span class="editor-theme-code">dot</span>`<span style="white-space: pre-wrap;"> の値は、だいたい次のように考えられます。</span>

<table id="bkmrk-dot%E3%81%AE%E5%80%A4%E6%84%8F%E5%91%B31.0-%E3%81%AB%E8%BF%91%E3%81%84%E6%AD%A3%E9%9D%A2%E3%81%AB%E3%81%84%E3%82%8B0"><colgroup><col></col><col></col></colgroup><tbody><tr><th>dotの値

</th><th>意味

</th></tr><tr><td>1.0 に近い

</td><td>正面にいる

</td></tr><tr><td>0.0 に近い

</td><td>横にいる

</td></tr><tr><td>-1.0 に近い

</td><td>後ろにいる

</td></tr></tbody></table>

---

## 14. 視野角の判定

例えば、次のようにします。

```cpp
const float VIEW_DOT = 0.5f;

if (dot > VIEW_DOT)
{
    isFindPlayer = true;
}
else
{
    isFindPlayer = false;
}
```

`<span class="editor-theme-code">dot > 0.5f</span>`<span style="white-space: pre-wrap;"> なら、前方のある範囲内にいると判断できます。</span>

---

## 15. VIEW\_DOT の目安

<table id="bkmrk-%E6%9D%A1%E4%BB%B6%E8%A6%8B%E3%81%88%E3%82%8B%E7%AF%84%E5%9B%B2%E3%81%AE%E7%9B%AE%E5%AE%89dot-%3E-0.5f"><colgroup><col></col><col></col></colgroup><tbody><tr><th>条件

</th><th>見える範囲の目安

</th></tr><tr><td>`<span class="editor-theme-code">dot > 0.5f</span>`

</td><td>前方約120度

</td></tr><tr><td>`<span class="editor-theme-code">dot > 0.707f</span>`

</td><td>前方約90度

</td></tr><tr><td>`<span class="editor-theme-code">dot > 0.866f</span>`

</td><td>前方約60度

</td></tr></tbody></table>

数値を大きくすると、視野が狭くなります。

数値を小さくすると、視野が広くなります。

---

## 16. 距離と向きを組み合わせる

距離と向きを組み合わせると、次のような判定になります。

```cpp
bool CheckCanSeePlayer()
{
    VECTOR toPlayer = VSub(playerPos, enemyPos);
    float distance = VSize(toPlayer);

    if (distance > VIEW_DISTANCE)
    {
        return false;
    }

    if (distance < 0.001f)
    {
        return true;
    }

    VECTOR dirToPlayer = VScale(toPlayer, 1.0f / distance);

    VECTOR forward =
    {
        sinf(enemyRotY),
        0.0f,
        cosf(enemyRotY)
    };

    float dot = VDot(forward, dirToPlayer);

    if (dot < VIEW_DOT)
    {
        return false;
    }

    return true;
}
```

この関数は、

```text
プレイヤーが見えているなら true
見えていないなら false
```

を返します。

---

## 17. 条件3：障害物

まだ問題があります。

距離が近くて、前方にいても、壁の向こうにいるなら本当は見えません。

```text
モンスター ---- 壁 ---- プレイヤー
```

この場合は、見えないようにしたいです。

---

## 18. 壁で見えない処理

考え方は単純です。

```text
モンスターからプレイヤーまで線を引く
↓
途中に壁があるか調べる
↓
壁があれば見えない
↓
壁がなければ見える
```

このような判定を、レイ判定やライン判定と呼びます。

---

## 19. 疑似コード

```cpp
bool isHitWall = CheckLineHitWall(enemyPos, playerPos);

if (isHitWall)
{
    return false;
}
```

`<span class="editor-theme-code">CheckLineHitWall</span>`<span style="white-space: pre-wrap;"> は、敵とプレイヤーの間に壁があるか調べる処理だと考えてください。</span>

---

## 20. 最終的な視覚判定

距離、向き、壁を組み合わせると、次のような流れになります。

```cpp
bool CheckCanSeePlayer()
{
    VECTOR toPlayer = VSub(playerPos, enemyPos);
    float distance = VSize(toPlayer);

    // 遠すぎるなら見えない
    if (distance > VIEW_DISTANCE)
    {
        return false;
    }

    // ほぼ同じ位置なら見えている扱い
    if (distance < 0.001f)
    {
        return true;
    }

    // 方向を求める
    VECTOR dirToPlayer = VScale(toPlayer, 1.0f / distance);

    // モンスターの前方向
    VECTOR forward =
    {
        sinf(enemyRotY),
        0.0f,
        cosf(enemyRotY)
    };

    // 前方にいるか調べる
    float dot = VDot(forward, dirToPlayer);

    if (dot < VIEW_DOT)
    {
        return false;
    }

    // 壁があれば見えない
    if (CheckLineHitWall(enemyPos, playerPos))
    {
        return false;
    }

    return true;
}
```

---

## 21. 前回のAIに接続する

視覚判定ができたら、前回のAIに接続できます。

```cpp
isFindPlayer = CheckCanSeePlayer();

if (isAttackRange)
{
    Attack();
}
else if (isFindPlayer)
{
    ChasePlayer();
}
else
{
    Idle();
}
```

これで、

```text
見えたら追いかける
見えなければ待機する
```

というモンスターになります。

---

## 22. 何がAIらしくなるのか

距離だけで判定していたときは、

```text
近ければ必ず見つかる
```

という動きでした。

しかし、視野角や壁判定を入れると、次のようになります。

```text
後ろに回ると気づかれない
壁に隠れると見つからない
正面に出ると追いかけてくる
```

このようなルールがあると、プレイヤーは、

```text
敵に見つかった
敵から隠れた
敵の背後を取った
```

と感じやすくなります。

つまり、AIが少し賢く見えます。

---

## 23. 重要な考え方

ゲームAIは、本当に人間のように世界を理解しているわけではありません。

しかし、次のような情報を使うことで、考えているように見せることができます。

```text
距離
方向
角度
障害物
時間
状態
```

AIは、世界をそのまま理解しているのではなく、ゲームに必要な情報だけを取り出して判断しています。

---

# まとめ

今回のポイントは次の通りです。

```text
isFindPlayer は勝手に決まらない
```

```text
AIには知覚が必要
```

```text
ゲームAIの視覚は画像を見ることではない
```

```text
距離・向き・壁で「見えている」を作る
```

```text
見えているかどうかをフラグにして、行動分岐に使う
```

---

# 最後の確認問題

## 問1

次のフラグは何を表しているか説明しなさい。

```cpp
bool isFindPlayer;
```

---

## 問2

距離だけでプレイヤーを発見するAIには、どんな問題がありますか。  
2つ書きなさい。

---

## 問3

モンスターの前方にプレイヤーがいるかを調べるために使う計算は何ですか。

---

## 問4

壁の向こうにいるプレイヤーを見えないようにするには、どのような判定が必要ですか。

---

## 問5

次の処理の意味を説明しなさい。

```cpp
isFindPlayer = CheckCanSeePlayer();
```

---

# 発展課題

次のようなモンスターを考えなさい。

```text
普段は巡回している。
プレイヤーが視界に入ったら追いかける。
壁の向こうに逃げられたら見失う。
見失ったら、その場で3秒間探す。
それでも見つからなければ巡回に戻る。
```

このモンスターに必要なフラグを考えなさい。

例：

```cpp
bool isFindPlayer;
bool isLostPlayer;
bool isSearching;
bool isPatrol;
```

<span style="white-space: pre-wrap;">さらに、どのような順番で </span>`<span class="editor-theme-code">if文</span>`<span style="white-space: pre-wrap;"> を書けばよいか考えなさい。</span>

# 既存ゲームを改良するよ（何様じゃい！）

# 既存ゲームの「足りない部分」を見つけて進化案を考える手順書

## この課題でやること

この課題では、前回分析したゲームをもとにして、  
「このゲームはどこが弱いのか」  
「どこを足せばもっと面白くなるのか」  
を考えます。

ただし、完全に別のゲームを作るのではありません。

元のゲームの面白さを残したまま、  
足りない部分を補って、少し進化させることが目的です。

---

## 対象ゲーム

前回分析した次のゲームの中から、自分が選んだものを使います。

- パックマン
- クルクルランド
- デビルワールド
- ドンキーコングJR

---

# 手順1 分析したゲーム名を書く

まず、自分が分析したゲーム名を書きます。

## 記入欄

ゲーム名：＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿

---

# 手順2 このゲームの中心的な面白さを一文で書く

足りない部分を考える前に、まず  
「このゲームの一番大事な面白さ」  
を確認します。

ここを考えないまま新要素を足すと、元のゲームと関係ないアイデアになってしまいます。

## 考え方

次の文に当てはめて書いてください。

このゲームの中心的な面白さは、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
である。

## 例

### パックマンの場合

このゲームの中心的な面白さは、  
「敵に追われながら、迷路の中のエサを集める緊張感」  
である。

### クルクルランドの場合

このゲームの中心的な面白さは、  
「自由に止まれない操作で、隠された絵を探す面白さ」  
である。

### デビルワールドの場合

このゲームの中心的な面白さは、  
「迷路が動く中で、進む道を考える面白さ」  
である。

### ドンキーコングJRの場合

このゲームの中心的な面白さは、  
「登る、避ける、助けるを組み合わせたアクションの面白さ」  
である。

## 記入欄

このゲームの中心的な面白さは、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
である。

---

# 手順3 6つの項目でゲームを評価する

次に、ゲームの面白さを6つの項目に分けて評価します。

点数は、1点から5点でつけます。

## 点数の意味

<table id="bkmrk-%E7%82%B9%E6%95%B0-%E6%84%8F%E5%91%B3-5%E7%82%B9-%E3%81%A8%E3%81%A6%E3%82%82%E8%89%AF%E3%81%84%E3%80%82%E4%BB%8A%E3%81%A7%E3%82%82%E5%8D%81%E5%88%86"><thead><tr><th align="right">点数</th><th>意味</th></tr></thead><tbody><tr><td align="right">5点</td><td>とても良い。今でも十分に面白い</td></tr><tr><td align="right">4点</td><td>良い。少しだけ改善できそう</td></tr><tr><td align="right">3点</td><td>普通。悪くはないが、強い特徴とも言えない</td></tr><tr><td align="right">2点</td><td>弱い。もっと工夫できそう</td></tr><tr><td align="right">1点</td><td>かなり弱い。足りない部分として考えやすい</td></tr></tbody></table>

---

# 手順4 「ルールのわかりやすさ」を評価する

## 見るポイント

- 何をすればよいか、すぐに分かるか
- クリア条件や目的が分かりやすいか
- プレイヤーが迷わず遊び始められるか

## 考えるヒント

次のようなことを考えてください。

- 目的は分かりやすいか
- 失敗条件は分かりやすいか
- ルールを覚えるのに時間がかかるか
- 初めて遊んだ人でも理解できそうか

## 記入欄

<table id="bkmrk-%E9%A0%85%E7%9B%AE-%E7%82%B9%E6%95%B0-%E7%90%86%E7%94%B1-%E3%83%AB%E3%83%BC%E3%83%AB%E3%81%AE%E3%82%8F%E3%81%8B%E3%82%8A%E3%82%84%E3%81%99%E3%81%95-"><thead><tr><th>項目</th><th align="right">点数</th><th>理由</th></tr></thead><tbody><tr><td>ルールのわかりやすさ</td><td align="right">＿点</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 手順5 「操作の気持ちよさ」を評価する

## 見るポイント

- 操作していて気持ちよいか
- 思った通りに動かせるか
- 操作の不自由さが、面白さにつながっているか
- 操作のしにくさが、ただのストレスになっていないか

## 考えるヒント

操作が不自由だから悪い、とは限りません。

たとえば、クルクルランドのように  
「自由に止まれないこと」  
が面白さになっている場合もあります。

大事なのは、操作の特徴が面白さにつながっているかどうかです。

## 記入欄

<table id="bkmrk-%E9%A0%85%E7%9B%AE-%E7%82%B9%E6%95%B0-%E7%90%86%E7%94%B1-%E6%93%8D%E4%BD%9C%E3%81%AE%E6%B0%97%E6%8C%81%E3%81%A1%E3%82%88%E3%81%95-%EF%BC%BF%E7%82%B9"><thead><tr><th>項目</th><th align="right">点数</th><th>理由</th></tr></thead><tbody><tr><td>操作の気持ちよさ</td><td align="right">＿点</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 手順6 「報酬の気持ちよさ」を評価する

## 見るポイント

- 得点を取るのが気持ちよいか
- アイテムを取るのが嬉しいか
- クリアしたときに達成感があるか
- 高得点を狙いたくなる仕組みがあるか

## 考えるヒント

報酬とは、プレイヤーが  
「やってよかった」  
と感じるものです。

たとえば、次のようなものがあります。

- 得点が入る
- アイテムを取る
- 敵を倒せる
- ステージをクリアする
- ボーナスが入る
- 音やエフェクトで気持ちよく感じる

## 記入欄

<table id="bkmrk-%E9%A0%85%E7%9B%AE-%E7%82%B9%E6%95%B0-%E7%90%86%E7%94%B1-%E5%A0%B1%E9%85%AC%E3%81%AE%E6%B0%97%E6%8C%81%E3%81%A1%E3%82%88%E3%81%95-%EF%BC%BF%E7%82%B9"><thead><tr><th>項目</th><th align="right">点数</th><th>理由</th></tr></thead><tbody><tr><td>報酬の気持ちよさ</td><td align="right">＿点</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 手順7 「敵・障害物の面白さ」を評価する

## 見るポイント

- 敵や障害物が、プレイヤーに考えさせているか
- 敵の動きに特徴があるか
- 障害物によって、ただの移動ではない遊びになっているか
- 敵や障害物の種類に違いがあるか

## 考えるヒント

敵や障害物は、プレイヤーを邪魔するだけの存在ではありません。

よい敵や障害物は、プレイヤーに  
「どっちへ進むか」  
「今行くか、待つか」  
「危険をおかして得点を狙うか」  
のような判断をさせます。

## 記入欄

<table id="bkmrk-%E9%A0%85%E7%9B%AE-%E7%82%B9%E6%95%B0-%E7%90%86%E7%94%B1-%E6%95%B5%E3%83%BB%E9%9A%9C%E5%AE%B3%E7%89%A9%E3%81%AE%E9%9D%A2%E7%99%BD%E3%81%95-%EF%BC%BF"><thead><tr><th>項目</th><th align="right">点数</th><th>理由</th></tr></thead><tbody><tr><td>敵・障害物の面白さ</td><td align="right">＿点</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 手順8 「ステージの変化」を評価する

## 見るポイント

- ステージごとに違いがあるか
- 場所によって考えることが変わるか
- 同じことの繰り返しになりすぎていないか
- ステージ構造が面白さを作っているか

## 考えるヒント

ステージの変化とは、見た目が変わることだけではありません。

次のような変化も含みます。

- 通路の形が変わる
- 逃げ道が少ない場所がある
- 危険な場所と安全な場所がある
- アイテムの配置が違う
- 敵の出方が変わる
- 動く床や動く迷路などの仕掛けがある

## 記入欄

<table id="bkmrk-%E9%A0%85%E7%9B%AE-%E7%82%B9%E6%95%B0-%E7%90%86%E7%94%B1-%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%AE%E5%A4%89%E5%8C%96-%EF%BC%BF%E7%82%B9-"><thead><tr><th>項目</th><th align="right">点数</th><th>理由</th></tr></thead><tbody><tr><td>ステージの変化</td><td align="right">＿点</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 手順9 「もう一度遊びたさ」を評価する

## 見るポイント

- もう一度挑戦したくなるか
- 前よりうまくなったと感じられるか
- 高得点を狙いたくなるか
- 毎回少し違う遊びになるか

## 考えるヒント

「もう一度遊びたさ」は、リプレイ性とも言います。

次のような要素があると、もう一度遊びたくなりやすいです。

- 高得点を狙える
- タイムを縮められる
- ランダム性がある
- 上達が分かりやすい
- 失敗しても「次はできそう」と思える
- 攻め方を変えられる

## 記入欄

<table id="bkmrk-%E9%A0%85%E7%9B%AE-%E7%82%B9%E6%95%B0-%E7%90%86%E7%94%B1-%E3%82%82%E3%81%86%E4%B8%80%E5%BA%A6%E9%81%8A%E3%81%B3%E3%81%9F%E3%81%95-%EF%BC%BF%E7%82%B9"><thead><tr><th>項目</th><th align="right">点数</th><th>理由</th></tr></thead><tbody><tr><td>もう一度遊びたさ</td><td align="right">＿点</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 手順10 6項目の点数をまとめる

ここまでつけた点数を、1つの表にまとめます。

## 記入欄

<table id="bkmrk-%E8%A9%95%E4%BE%A1%E9%A0%85%E7%9B%AE-%E7%82%B9%E6%95%B0-%E3%83%AB%E3%83%BC%E3%83%AB%E3%81%AE%E3%82%8F%E3%81%8B%E3%82%8A%E3%82%84%E3%81%99%E3%81%95-%EF%BC%BF"><thead><tr><th>評価項目</th><th align="right">点数</th></tr></thead><tbody><tr><td>ルールのわかりやすさ</td><td align="right">＿点</td></tr><tr><td>操作の気持ちよさ</td><td align="right">＿点</td></tr><tr><td>報酬の気持ちよさ</td><td align="right">＿点</td></tr><tr><td>敵・障害物の面白さ</td><td align="right">＿点</td></tr><tr><td>ステージの変化</td><td align="right">＿点</td></tr><tr><td>もう一度遊びたさ</td><td align="right">＿点</td></tr></tbody></table>

---

# 手順11 点数を見える化する

点数を、星の数で見えるようにします。

5点なら星5つ、3点なら星3つです。

## 星の書き方

<table id="bkmrk-%E7%82%B9%E6%95%B0-%E6%98%9F-5%E7%82%B9-%E2%98%85%E2%98%85%E2%98%85%E2%98%85%E2%98%85-4%E7%82%B9-%E2%98%85%E2%98%85%E2%98%85"><thead><tr><th align="right">点数</th><th>星</th></tr></thead><tbody><tr><td align="right">5点</td><td>★★★★★</td></tr><tr><td align="right">4点</td><td>★★★★☆</td></tr><tr><td align="right">3点</td><td>★★★☆☆</td></tr><tr><td align="right">2点</td><td>★★☆☆☆</td></tr><tr><td align="right">1点</td><td>★☆☆☆☆</td></tr></tbody></table>

## 記入例

<table id="bkmrk-%E8%A9%95%E4%BE%A1%E9%A0%85%E7%9B%AE-%E7%82%B9%E6%95%B0-%E6%98%9F-%E3%83%AB%E3%83%BC%E3%83%AB%E3%81%AE%E3%82%8F%E3%81%8B%E3%82%8A%E3%82%84%E3%81%99%E3%81%95"><thead><tr><th>評価項目</th><th align="right">点数</th><th>星</th></tr></thead><tbody><tr><td>ルールのわかりやすさ</td><td align="right">5点</td><td>★★★★★</td></tr><tr><td>操作の気持ちよさ</td><td align="right">4点</td><td>★★★★☆</td></tr><tr><td>報酬の気持ちよさ</td><td align="right">2点</td><td>★★☆☆☆</td></tr><tr><td>敵・障害物の面白さ</td><td align="right">4点</td><td>★★★★☆</td></tr><tr><td>ステージの変化</td><td align="right">2点</td><td>★★☆☆☆</td></tr><tr><td>もう一度遊びたさ</td><td align="right">3点</td><td>★★★☆☆</td></tr></tbody></table>

## 記入欄

<table id="bkmrk-%E8%A9%95%E4%BE%A1%E9%A0%85%E7%9B%AE-%E7%82%B9%E6%95%B0-%E6%98%9F-%E3%83%AB%E3%83%BC%E3%83%AB%E3%81%AE%E3%82%8F%E3%81%8B%E3%82%8A%E3%82%84%E3%81%99%E3%81%95-1"><thead><tr><th>評価項目</th><th align="right">点数</th><th>星</th></tr></thead><tbody><tr><td>ルールのわかりやすさ</td><td align="right">＿点</td><td>＿＿＿＿＿</td></tr><tr><td>操作の気持ちよさ</td><td align="right">＿点</td><td>＿＿＿＿＿</td></tr><tr><td>報酬の気持ちよさ</td><td align="right">＿点</td><td>＿＿＿＿＿</td></tr><tr><td>敵・障害物の面白さ</td><td align="right">＿点</td><td>＿＿＿＿＿</td></tr><tr><td>ステージの変化</td><td align="right">＿点</td><td>＿＿＿＿＿</td></tr><tr><td>もう一度遊びたさ</td><td align="right">＿点</td><td>＿＿＿＿＿</td></tr></tbody></table>

---

# 手順12 足りない部分ランキングを作る

点数が低い項目を見つけます。

点数が低いものほど、足りない部分です。

## やり方

1. 点数が一番低い項目を探す
2. その項目を「足りない部分1位」にする
3. 次に低い項目を「足りない部分2位」にする
4. さらに次に低い項目を「足りない部分3位」にする

## 記入例

<table id="bkmrk-%E9%A0%86%E4%BD%8D-%E8%B6%B3%E3%82%8A%E3%81%AA%E3%81%84%E9%83%A8%E5%88%86-%E7%82%B9%E6%95%B0-%E7%90%86%E7%94%B1-1%E4%BD%8D-%E5%A0%B1"><thead><tr><th align="right">順位</th><th>足りない部分</th><th align="right">点数</th><th>理由</th></tr></thead><tbody><tr><td align="right">1位</td><td>報酬の気持ちよさ</td><td align="right">2点</td><td>エサを集める以外のごほうびが少ないから</td></tr><tr><td align="right">2位</td><td>ステージの変化</td><td align="right">2点</td><td>ステージごとの違いが少なく感じたから</td></tr><tr><td align="right">3位</td><td>もう一度遊びたさ</td><td align="right">3点</td><td>高得点以外の目標が少ないから</td></tr></tbody></table>

## 記入欄

<table id="bkmrk-%E9%A0%86%E4%BD%8D-%E8%B6%B3%E3%82%8A%E3%81%AA%E3%81%84%E9%83%A8%E5%88%86-%E7%82%B9%E6%95%B0-%E7%90%86%E7%94%B1-1%E4%BD%8D-%EF%BC%BF"><thead><tr><th align="right">順位</th><th>足りない部分</th><th align="right">点数</th><th>理由</th></tr></thead><tbody><tr><td align="right">1位</td><td>＿＿＿＿＿＿＿＿</td><td align="right">＿点</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td align="right">2位</td><td>＿＿＿＿＿＿＿＿</td><td align="right">＿点</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td align="right">3位</td><td>＿＿＿＿＿＿＿＿</td><td align="right">＿点</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 手順13 一番補いたい項目を1つ選ぶ

足りない部分ランキングの中から、  
自分が一番改善したい項目を1つ選びます。

この課題では、いきなり全部を直そうとしません。

1つだけ選んで、そこを強くする案を考えます。

## 記入欄

私が補いたい項目は、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
である。

その理由は、  
＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿  
である。

---

# 手順14 なぜ足りないのかを考える

選んだ項目について、  
「なぜ足りないと感じたのか」  
を考えます。

## 考え方

次の文に当てはめてください。

このゲームは、  
「＿＿＿＿＿＿＿＿＿＿」  
が少ないため、  
「＿＿＿＿＿＿＿＿＿＿」  
と感じた。

## 記入例

このゲームは、  
「ステージごとの新しい仕掛け」  
が少ないため、  
「後半が同じことの繰り返し」  
と感じた。

## 記入欄

このゲームは、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
が少ないため、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
と感じた。

---

# 手順15 足りない部分を補うアイデアを3つ出す

いきなり1つに決めず、まずは3つアイデアを出します。

## 注意

元のゲームと関係ないアイデアにしないこと。

たとえば、パックマンを分析しているのに、  
いきなり剣で敵を倒すアクションゲームにすると、  
元の面白さが変わりすぎてしまいます。

## アイデアの方向性

<table id="bkmrk-%E8%B6%B3%E3%82%8A%E3%81%AA%E3%81%84%E9%A0%85%E7%9B%AE-%E3%82%A2%E3%82%A4%E3%83%87%E3%82%A2%E3%81%AE%E6%96%B9%E5%90%91-%E5%A0%B1%E9%85%AC%E3%81%AE%E6%B0%97%E6%8C%81"><thead><tr><th>足りない項目</th><th>アイデアの方向</th></tr></thead><tbody><tr><td>報酬の気持ちよさ</td><td>コンボ、連続ボーナス、レアアイテム、演出の強化</td></tr><tr><td>ステージの変化</td><td>新しい地形、動く床、変化する迷路、特殊エリア</td></tr><tr><td>敵・障害物の面白さ</td><td>新しい敵、動きの違う敵、待ち伏せする敵、進路をふさぐ敵</td></tr><tr><td>操作の気持ちよさ</td><td>新アクション、操作補助、スピード変化、リスクのある技</td></tr><tr><td>もう一度遊びたさ</td><td>タイムアタック、高得点ボーナス、ランダム配置、チャレンジ目標</td></tr><tr><td>ルールのわかりやすさ</td><td>チュートリアル、見た目の整理、目的表示、危険表示</td></tr></tbody></table>

## 記入欄

<table id="bkmrk-%E3%82%A2%E3%82%A4%E3%83%87%E3%82%A2-%E5%86%85%E5%AE%B9-%E3%82%A2%E3%82%A4%E3%83%87%E3%82%A21-%EF%BC%BF%EF%BC%BF%EF%BC%BF%EF%BC%BF%EF%BC%BF%EF%BC%BF"><thead><tr><th>アイデア</th><th>内容</th></tr></thead><tbody><tr><td>アイデア1</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>アイデア2</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>アイデア3</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 手順16 3つのアイデアを比べる

出した3つのアイデアを比べます。

次の3つの基準で考えます。

1. 元のゲームの面白さを壊していないか
2. 足りない部分をちゃんと補えているか
3. 実際にゲームとして遊びやすそうか

## 評価表

<table id="bkmrk-%E3%82%A2%E3%82%A4%E3%83%87%E3%82%A2-%E5%85%83%E3%81%AE%E9%9D%A2%E7%99%BD%E3%81%95%E3%82%92%E5%A3%8A%E3%81%97%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84-%E8%B6%B3%E3%82%8A"><thead><tr><th>アイデア</th><th align="right">元の面白さを壊していない</th><th align="right">足りない部分を補えている</th><th align="right">遊びやすそう</th><th align="right">合計</th></tr></thead><tbody><tr><td>アイデア1</td><td align="right">＿点</td><td align="right">＿点</td><td align="right">＿点</td><td align="right">＿点</td></tr><tr><td>アイデア2</td><td align="right">＿点</td><td align="right">＿点</td><td align="right">＿点</td><td align="right">＿点</td></tr><tr><td>アイデア3</td><td align="right">＿点</td><td align="right">＿点</td><td align="right">＿点</td><td align="right">＿点</td></tr></tbody></table>

各項目は、1点から5点で評価してください。

## 点数の意味

<table id="bkmrk-%E7%82%B9%E6%95%B0-%E6%84%8F%E5%91%B3-5%E7%82%B9-%E3%81%A8%E3%81%A6%E3%82%82%E8%89%AF%E3%81%84-4%E7%82%B9-%E8%89%AF%E3%81%84"><thead><tr><th align="right">点数</th><th>意味</th></tr></thead><tbody><tr><td align="right">5点</td><td>とても良い</td></tr><tr><td align="right">4点</td><td>良い</td></tr><tr><td align="right">3点</td><td>普通</td></tr><tr><td align="right">2点</td><td>少し弱い</td></tr><tr><td align="right">1点</td><td>あまり良くない</td></tr></tbody></table>

---

# 手順17 一番よい進化案を1つ選ぶ

手順16で合計点が高かったアイデアを1つ選びます。

## 記入欄

私が選んだ進化案は、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
である。

選んだ理由は、  
＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿  
である。

---

# 手順18 進化案の内容を詳しく説明する

選んだ進化案について、詳しく説明します。

## 書く内容

次の4つを必ず書いてください。

1. 何を追加するのか
2. いつ出てくるのか
3. プレイヤーはどう行動するのか
4. それによって何が面白くなるのか

## 記入欄

### 追加するもの

＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿

### いつ出てくるか

＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿

### プレイヤーはどう行動するか

＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿

### それによって何が面白くなるか

＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿

---

# 手順19 元のゲームの面白さを壊していないか確認する

最後に、自分の進化案が  
「元のゲームの面白さ」  
を壊していないか確認します。

## チェック項目

<table id="bkmrk-%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E5%86%85%E5%AE%B9-%E3%81%AF%E3%81%84-%2F-%E3%81%84%E3%81%84%E3%81%88-%E5%85%83%E3%81%AE%E3%82%B2%E3%83%BC"><thead><tr><th>チェック内容</th><th>はい / いいえ</th></tr></thead><tbody><tr><td>元のゲームの目的は残っている</td><td>＿＿</td></tr><tr><td>元のゲームの基本操作は残っている</td><td>＿＿</td></tr><tr><td>元のゲームの緊張感や面白さは残っている</td><td>＿＿</td></tr><tr><td>追加した要素が目立ちすぎていない</td><td>＿＿</td></tr><tr><td>別のゲームになりすぎていない</td><td>＿＿</td></tr></tbody></table>

## 判断

上のチェックで「いいえ」が多い場合は、  
アイデアを少し小さくするか、元のゲームに合う形に直してください。

---

# 手順20 進化案を一文でまとめる

最後に、自分の進化案を一文でまとめます。

## 書き方

このゲームは、もともと  
「＿＿＿＿＿＿＿＿＿＿」  
という面白さがある。

しかし、  
「＿＿＿＿＿＿＿＿＿＿」  
が足りないと感じた。

そこで、  
「＿＿＿＿＿＿＿＿＿＿」  
を追加することで、  
「＿＿＿＿＿＿＿＿＿＿」  
という体験を強くする。

## 記入欄

このゲームは、もともと  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という面白さがある。

しかし、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
が足りないと感じた。

そこで、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
を追加することで、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という体験を強くする。

---

# 提出するもの

次の内容を提出してください。

1. ゲーム名
2. 中心的な面白さ
3. 6項目の評価表
4. 星による見える化
5. 足りない部分ランキング
6. 補いたい項目
7. アイデア3つ
8. 選んだ進化案
9. 元のゲームの面白さを壊していないかのチェック
10. 進化案のまとめ文

---

# 書くときの注意

## 1. ただの追加要素で終わらせない

悪い例：

- 新しい敵を追加する
- アイテムを増やす
- ステージを増やす

これだけでは、なぜ面白くなるのかが分かりません。

良い例：

- 進路をふさぐ敵を追加することで、プレイヤーが逃げ道を考える場面を増やす
- 連続でアイテムを取るとボーナスが入るようにして、危険をおかして攻める判断を生む
- ステージの一部が動くようにして、毎回安全な道が変わる緊張感を作る

---

## 2. 「AだからBになる」で書く

説明するときは、できるだけ次の形で書きます。

- 〇〇があるから、プレイヤーは□□を考える
- 〇〇を追加することで、□□という行動が生まれる
- 〇〇が変化するため、□□という緊張感が生まれる
- 〇〇を集めると得点が増えるため、□□したくなる

---

## 3. 元のゲームを別物にしすぎない

進化案は、元のゲームを完全に作り変えることではありません。

たとえば、パックマンに次のような要素を足すと、別のゲームになりすぎる可能性があります。

- 剣で敵を倒す
- ジャンプアクションにする
- RPGのレベル上げを入れる
- 長いストーリーを入れる

元のゲームの面白さを残したまま、少し広げることを意識してください。

---

# 簡単な完成例

## ゲーム名

パックマン

## 中心的な面白さ

敵に追われながら、迷路の中のエサを集める緊張感。

## 6項目評価

<table id="bkmrk-%E8%A9%95%E4%BE%A1%E9%A0%85%E7%9B%AE-%E7%82%B9%E6%95%B0-%E6%98%9F-%E3%83%AB%E3%83%BC%E3%83%AB%E3%81%AE%E3%82%8F%E3%81%8B%E3%82%8A%E3%82%84%E3%81%99%E3%81%95-2"><thead><tr><th>評価項目</th><th align="right">点数</th><th>星</th></tr></thead><tbody><tr><td>ルールのわかりやすさ</td><td align="right">5点</td><td>★★★★★</td></tr><tr><td>操作の気持ちよさ</td><td align="right">4点</td><td>★★★★☆</td></tr><tr><td>報酬の気持ちよさ</td><td align="right">3点</td><td>★★★☆☆</td></tr><tr><td>敵・障害物の面白さ</td><td align="right">5点</td><td>★★★★★</td></tr><tr><td>ステージの変化</td><td align="right">2点</td><td>★★☆☆☆</td></tr><tr><td>もう一度遊びたさ</td><td align="right">4点</td><td>★★★★☆</td></tr></tbody></table>

## 足りない部分ランキング

<table id="bkmrk-%E9%A0%86%E4%BD%8D-%E8%B6%B3%E3%82%8A%E3%81%AA%E3%81%84%E9%83%A8%E5%88%86-%E7%82%B9%E6%95%B0-%E7%90%86%E7%94%B1-1%E4%BD%8D-%E3%82%B9"><thead><tr><th align="right">順位</th><th>足りない部分</th><th align="right">点数</th><th>理由</th></tr></thead><tbody><tr><td align="right">1位</td><td>ステージの変化</td><td align="right">2点</td><td>迷路の形は変わるが、遊び方の変化が少ないと感じたから</td></tr><tr><td align="right">2位</td><td>報酬の気持ちよさ</td><td align="right">3点</td><td>エサを集める以外の特別な報酬が少ないから</td></tr><tr><td align="right">3位</td><td>操作の気持ちよさ</td><td align="right">4点</td><td>操作は分かりやすいが、新しい行動は少ないから</td></tr></tbody></table>

## 選んだ進化案

一定時間ごとに開閉するゲートを迷路に追加する。

## 進化案の説明

迷路の一部に、一定時間で開いたり閉じたりするゲートを追加する。  
プレイヤーは、敵から逃げながら、ゲートが開くタイミングを見て進む必要がある。  
これによって、ただ道を覚えるだけではなく、タイミングを見て進む判断が生まれる。  
元の「追われながらエサを集める緊張感」を残したまま、ステージの変化を増やせる。

## まとめ

このゲームは、もともと  
「敵に追われながらエサを集める緊張感」  
という面白さがある。

しかし、  
「ステージごとの変化」  
が足りないと感じた。

そこで、  
「一定時間で開閉するゲート」  
を追加することで、  
「逃げ道を考えながらタイミングよく進む」  
という体験を強くする。

# 状態遷移でモンスターを管理

# ゲームAI課題：状態遷移でモンスターの行動を管理しよう

## 「今、モンスターは何をしているのか？」を設計する

---

# 1. 今日のテーマ

前回は、モンスターに「視覚」を与える考え方を学びました。

```cpp
isFindPlayer = CheckCanSeePlayer();

```

このように、モンスターがプレイヤーを見つけているかどうかを判定しました。

そして、その結果を使って行動を変えました。

```cpp
if (isAttackRange)
{
    Attack();
}
else if (isFindPlayer)
{
    ChasePlayer();
}
else
{
    Idle();
}

```

この書き方でも、最低限のAIは作れます。

しかし、行動が増えてくると問題が出てきます。

---

# 2. if文だけのAIの問題点

## 問題1：条件が増えると分かりにくい

例えば、モンスターに次の行動を追加したいとします。

- 巡回する
- プレイヤーを見つけたら追いかける
- 近づいたら攻撃する
- 見失ったら探す
- しばらく探して見つからなければ巡回に戻る
- HPが少なくなったら逃げる

これを if 文だけで書くと、次のように複雑になります。

```cpp
if (isLowHp)
{
    Escape();
}
else if (isAttackRange)
{
    Attack();
}
else if (isFindPlayer)
{
    ChasePlayer();
}
else if (isLostPlayer)
{
    Search();
}
else if (isFarFromHome)
{
    ReturnHome();
}
else
{
    Patrol();
}

```

一見よさそうに見えます。

しかし、行動が増えるほど、

```text
どの条件を先に書くべきか
今どの行動中なのか
行動の途中で何をすればよいのか

```

が分かりにくくなります。

---

## 問題2：「今何をしているか」が分かりにくい

例えば、モンスターが攻撃中だとします。

攻撃モーションには時間がかかります。

```text
攻撃開始
↓
攻撃モーション中
↓
ダメージ発生
↓
攻撃終了

```

しかし、毎フレーム if 文だけで行動を決めていると、

```cpp
if (isAttackRange)
{
    Attack();
}

```

が何度も呼ばれて、攻撃モーションが毎回最初から始まってしまうことがあります。

このように、

```text
今は攻撃中である
今は探索中である
今は巡回中である

```

という情報を持たないと、行動を管理しづらくなります。

---

## 問題3：「見失った後」の行動が作りにくい

視覚判定だけで作ると、プレイヤーが見えなくなった瞬間に待機してしまいます。

```cpp
if (isFindPlayer)
{
    ChasePlayer();
}
else
{
    Idle();
}

```

これだと不自然です。

本当は、次のような流れにしたいはずです。

```text
プレイヤーを見つける
↓
追いかける
↓
壁の向こうに逃げられる
↓
見失う
↓
最後に見た場所へ行く
↓
少し探す
↓
見つからなければ巡回に戻る

```

このような「流れのある行動」を作るには、状態管理が必要です。

---

# 3. 状態とは何か

状態とは、

```text
今、そのキャラクターが何をしているか

```

を表すものです。

例えば、モンスターには次のような状態があります。

<table id="bkmrk-%E7%8A%B6%E6%85%8B-%E6%84%8F%E5%91%B3-idle-%E3%81%9D%E3%81%AE%E5%A0%B4%E3%81%A7%E5%BE%85%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B"><thead><tr><th>状態</th><th>意味</th></tr></thead><tbody><tr><td>Idle</td><td>その場で待っている</td></tr><tr><td>Patrol</td><td>巡回している</td></tr><tr><td>Chase</td><td>プレイヤーを追いかけている</td></tr><tr><td>Attack</td><td>攻撃している</td></tr><tr><td>Search</td><td>プレイヤーを探している</td></tr><tr><td>Return</td><td>持ち場に戻っている</td></tr><tr><td>Escape</td><td>逃げている</td></tr></tbody></table>

このように、行動を状態として整理します。

---

# 4. enum class で状態を作る

C++では、状態を `enum class` で作ると分かりやすくなります。

```cpp
enum class EnemyState
{
    Idle,
    Patrol,
    Chase,
    Attack,
    Search,
    Return,
    Escape
};

```

そして、敵が現在どの状態なのかを変数で持ちます。

```cpp
EnemyState state = EnemyState::Patrol;

```

これで、モンスターは

```text
今は Patrol 状態である
今は Chase 状態である
今は Attack 状態である

```

という情報を持てるようになります。

---

# 5. 状態ごとに行動を分ける

状態を持ったら、`switch` 文で行動を分けます。

```cpp
switch (state)
{
case EnemyState::Idle:
    Idle();
    break;

case EnemyState::Patrol:
    Patrol();
    break;

case EnemyState::Chase:
    ChasePlayer();
    break;

case EnemyState::Attack:
    Attack();
    break;

case EnemyState::Search:
    Search();
    break;

case EnemyState::Return:
    ReturnHome();
    break;

case EnemyState::Escape:
    Escape();
    break;
}

```

これで、

```text
現在の状態に応じて
呼び出す行動を変える

```

ことができます。

---

# 6. 状態遷移とは何か

状態遷移とは、

```text
ある状態から、別の状態に切り替わること

```

です。

例えば、

```text
Patrol
↓ プレイヤーを見つけた
Chase

```

これは、

```cpp
if (state == EnemyState::Patrol)
{
    if (isFindPlayer)
    {
        state = EnemyState::Chase;
    }
}

```

と書けます。

---

# 7. 状態遷移の例

モンスターの基本的な状態遷移を考えると、次のようになります。

```text
Patrol
  ↓ プレイヤーを見つけた
Chase
  ↓ 攻撃範囲に入った
Attack
  ↓ 攻撃範囲から出た
Chase
  ↓ プレイヤーを見失った
Search
  ↓ 見つからなかった
Patrol

```

このように、

```text
何が起きたら
どの状態に変わるのか

```

を考えるのが、状態遷移の設計です。

---

# 8. 視覚判定と状態遷移をつなげる

前回作った視覚判定を使います。

```cpp
isFindPlayer = CheckCanSeePlayer();

```

これを状態遷移に使います。

```cpp
if (state == EnemyState::Patrol)
{
    if (isFindPlayer)
    {
        state = EnemyState::Chase;
    }
}

```

つまり、

```text
視覚で見つける
↓
状態を切り替える
↓
行動が変わる

```

という流れになります。

---

# 9. 状態遷移表

状態遷移は、表で考えると分かりやすくなります。

<table id="bkmrk-%E7%8F%BE%E5%9C%A8%E3%81%AE%E7%8A%B6%E6%85%8B-%E6%9D%A1%E4%BB%B6-%E6%AC%A1%E3%81%AE%E7%8A%B6%E6%85%8B-patrol"><thead><tr><th>現在の状態</th><th>条件</th><th>次の状態</th></tr></thead><tbody><tr><td>Patrol</td><td>プレイヤーを見つけた</td><td>Chase</td></tr><tr><td>Chase</td><td>攻撃範囲に入った</td><td>Attack</td></tr><tr><td>Chase</td><td>プレイヤーを見失った</td><td>Search</td></tr><tr><td>Attack</td><td>攻撃範囲から出た</td><td>Chase</td></tr><tr><td>Search</td><td>プレイヤーを見つけた</td><td>Chase</td></tr><tr><td>Search</td><td>3秒探して見つからない</td><td>Patrol</td></tr></tbody></table>

プログラムを書く前に、この表を作ると整理しやすくなります。

---

# 10. 状態遷移図

表だけでなく、図にするとさらに分かりやすくなります。

```text
          プレイヤー発見
   +--------------------+
   |                    v
+--------+          +--------+
| Patrol |          | Chase  |
+--------+          +--------+
   ^                    |
   |                    | 攻撃範囲
   | 3秒探して          v
   | 見つからない    +--------+
+--------+          | Attack |
| Search |          +--------+
+--------+             |
   ^                    |
   | 見失った           | 範囲外
   +--------------------+

```

このように、状態同士のつながりを線で表します。

---

# 11. 今日の課題

## 課題テーマ

```text
見失ったら探すモンスターAIを設計しよう

```

---

## モンスターの仕様

今回設計するモンスターは、次のように動きます。

```text
1. 普段は決められた場所を巡回している
2. プレイヤーが視界に入ったら追いかける
3. プレイヤーが攻撃範囲に入ったら攻撃する
4. プレイヤーが攻撃範囲から出たらまた追いかける
5. プレイヤーを見失ったら、その場で探す
6. 3秒探しても見つからなければ巡回に戻る

```

---

# 12. 使用してよい状態

今回は、次の状態を使います。

```cpp
enum class EnemyState
{
    Patrol,
    Chase,
    Attack,
    Search
};

```

それぞれの意味は次の通りです。

<table id="bkmrk-%E7%8A%B6%E6%85%8B-%E5%86%85%E5%AE%B9-patrol-%E6%B1%BA%E3%82%81%E3%82%89%E3%82%8C%E3%81%9F%E5%A0%B4%E6%89%80"><thead><tr><th>状態</th><th>内容</th></tr></thead><tbody><tr><td>Patrol</td><td>決められた場所を巡回する</td></tr><tr><td>Chase</td><td>プレイヤーを追いかける</td></tr><tr><td>Attack</td><td>プレイヤーを攻撃する</td></tr><tr><td>Search</td><td>プレイヤーを探す</td></tr></tbody></table>

---

# 13. 使用してよいフラグ

今回は、次のフラグを使います。

```cpp
bool isFindPlayer;      // プレイヤーを見つけている
bool isAttackRange;     // 攻撃範囲に入っている
bool isSearchTimeOver;  // 探索時間が終わった

```

---

## フラグの意味

<table id="bkmrk-%E3%83%95%E3%83%A9%E3%82%B0-%E6%84%8F%E5%91%B3-isfindplayer-"><thead><tr><th>フラグ</th><th>意味</th></tr></thead><tbody><tr><td>isFindPlayer</td><td>視界にプレイヤーがいる</td></tr><tr><td>isAttackRange</td><td>プレイヤーが攻撃できる距離にいる</td></tr><tr><td>isSearchTimeOver</td><td>探索時間が終わった</td></tr></tbody></table>

---

# 14. 課題1：状態の意味を説明する

次の状態について、自分の言葉で説明しなさい。

<table id="bkmrk-%E7%8A%B6%E6%85%8B-%E8%87%AA%E5%88%86%E3%81%AE%E8%AA%AC%E6%98%8E-patrol-chas"><thead><tr><th>状態</th><th>自分の説明</th></tr></thead><tbody><tr><td>Patrol</td><td></td></tr><tr><td>Chase</td><td></td></tr><tr><td>Attack</td><td></td></tr><tr><td>Search</td><td></td></tr></tbody></table>

---

# 15. 課題2：状態遷移表を完成させる

次の表を完成させなさい。

<table id="bkmrk-%E7%8F%BE%E5%9C%A8%E3%81%AE%E7%8A%B6%E6%85%8B-%E6%9D%A1%E4%BB%B6-%E6%AC%A1%E3%81%AE%E7%8A%B6%E6%85%8B-patrol-1"><thead><tr><th>現在の状態</th><th>条件</th><th>次の状態</th></tr></thead><tbody><tr><td>Patrol</td><td>`isFindPlayer == true`</td><td></td></tr><tr><td>Chase</td><td>`isAttackRange == true`</td><td></td></tr><tr><td>Chase</td><td>`isFindPlayer == false`</td><td></td></tr><tr><td>Attack</td><td>`isAttackRange == false`</td><td></td></tr><tr><td>Search</td><td>`isFindPlayer == true`</td><td></td></tr><tr><td>Search</td><td>`isSearchTimeOver == true`</td><td></td></tr></tbody></table>

---

# 16. 課題3：状態遷移図を描く

次の状態を使って、状態遷移図を描きなさい。

```text
Patrol
Chase
Attack
Search

```

矢印には、状態が切り替わる条件を書きなさい。

例：

```text
Patrol -- isFindPlayer --> Chase

```

---

# 17. 課題4：状態遷移の疑似コードを書く

次のひな形を使って、状態遷移の疑似コードを書きなさい。

```cpp
if (state == EnemyState::Patrol)
{
    if (__________)
    {
        state = EnemyState::__________;
    }
}
else if (state == EnemyState::Chase)
{
    if (__________)
    {
        state = EnemyState::__________;
    }
    else if (__________)
    {
        state = EnemyState::__________;
    }
}
else if (state == EnemyState::Attack)
{
    if (__________)
    {
        state = EnemyState::__________;
    }
}
else if (state == EnemyState::Search)
{
    if (__________)
    {
        state = EnemyState::__________;
    }
    else if (__________)
    {
        state = EnemyState::__________;
    }
}

```

---

# 18. 課題5：行動処理の疑似コードを書く

状態ごとに、どの関数を呼び出すかを書きなさい。

```cpp
switch (state)
{
case EnemyState::Patrol:
    __________();
    break;

case EnemyState::Chase:
    __________();
    break;

case EnemyState::Attack:
    __________();
    break;

case EnemyState::Search:
    __________();
    break;
}

```

使ってよい関数名：

```cpp
Patrol();
ChasePlayer();
Attack();
Search();

```

---

# 19. 課題6：最終的なUpdate処理を組み立てる

次の流れになるように、`Update()` の疑似コードを完成させなさい。

```text
1. 視覚判定を行う
2. 攻撃範囲判定を行う
3. 探索時間が終わったか調べる
4. 状態遷移を行う
5. 現在の状態に応じた行動を行う

```

---

## ひな形

```cpp
void Enemy::Update()
{
    // 1. 視覚判定
    isFindPlayer = ____________________;

    // 2. 攻撃範囲判定
    isAttackRange = ____________________;

    // 3. 探索時間判定
    isSearchTimeOver = ____________________;

    // 4. 状態遷移
    if (state == EnemyState::Patrol)
    {
        if (__________)
        {
            state = EnemyState::__________;
        }
    }
    else if (state == EnemyState::Chase)
    {
        if (__________)
        {
            state = EnemyState::__________;
        }
        else if (__________)
        {
            state = EnemyState::__________;
        }
    }
    else if (state == EnemyState::Attack)
    {
        if (__________)
        {
            state = EnemyState::__________;
        }
    }
    else if (state == EnemyState::Search)
    {
        if (__________)
        {
            state = EnemyState::__________;
        }
        else if (__________)
        {
            state = EnemyState::__________;
        }
    }

    // 5. 状態ごとの行動
    switch (state)
    {
    case EnemyState::Patrol:
        __________();
        break;

    case EnemyState::Chase:
        __________();
        break;

    case EnemyState::Attack:
        __________();
        break;

    case EnemyState::Search:
        __________();
        break;
    }
}

```

---

# 20. 課題7：このAIの弱点を考える

このモンスターAIには、まだ弱点があります。

例えば、

- Search状態でどこを探すのか決まっていない
- 見失った瞬間のプレイヤー位置を覚えていない
- 攻撃モーション中にすぐChaseへ戻るかもしれない
- Patrolのルートが決まっていない
- 壁を避けて移動できない

などです。

自分で、このAIの弱点を3つ書きなさい。

---

# 21. 課題8：改良案を考える

課題7で書いた弱点を1つ選び、改良案を書きなさい。

例：

```text
弱点：
Search状態でどこを探すのか決まっていない。

改良案：
プレイヤーを最後に見た位置を lastPlayerPos として保存し、
Search状態ではその場所に移動してから周囲を探す。

```

---

# 22. 90分の進め方

<table id="bkmrk-%E6%99%82%E9%96%93-%E5%86%85%E5%AE%B9-0%E3%80%9C15%E5%88%86-%E8%B3%87%E6%96%99%E5%89%8D%E5%8D%8A%E3%82%92%E8%AA%AD%E3%82%80-"><thead><tr><th>時間</th><th>内容</th></tr></thead><tbody><tr><td>0〜15分</td><td>資料前半を読む</td></tr><tr><td>15〜25分</td><td>課題1：状態の意味を説明する</td></tr><tr><td>25〜40分</td><td>課題2：状態遷移表を完成させる</td></tr><tr><td>40〜55分</td><td>課題3：状態遷移図を描く</td></tr><tr><td>55〜70分</td><td>課題4・5：疑似コードを書く</td></tr><tr><td>70〜85分</td><td>課題6：Update処理を組み立てる</td></tr><tr><td>85〜90分</td><td>課題7・8：弱点と改良案を書く</td></tr></tbody></table>

時間が足りない場合、課題8は宿題にしてもよい。

---

# 23. まとめ

今回の重要ポイントは次の通りです。

```text
if文だけでは、行動が増えるほど管理しづらくなる

```

```text
状態とは「今何をしているか」を表す情報

```

```text
状態遷移とは「条件によって状態が切り替わること」

```

```text
視覚判定は、状態遷移の条件として使える

```

```text
状態管理を使うと、見失う・探す・戻るなどの自然な行動を作りやすい

```

---

# ３Dの移動計算（回転して移動）[ゲーム数学、プログラミング基礎］

# 戦車を「向いている方向」に動かすプログラムを作ろう

## 1. 今日の目標

今回は、3D空間で戦車を動かす処理を作ります。

完成すると、次の操作ができるようになります。

<table id="bkmrk-%E3%82%AD%E3%83%BC-%E5%8B%95%E3%81%8D-a%E3%82%AD%E3%83%BC-%E5%B7%A6%E3%81%AB%E5%9B%9E%E8%BB%A2%E3%81%99%E3%82%8B-d%E3%82%AD%E3%83%BC"><thead><tr><th>キー</th><th>動き</th></tr></thead><tbody><tr><td>Aキー</td><td>左に回転する</td></tr><tr><td>Dキー</td><td>右に回転する</td></tr><tr><td>Wキー</td><td>戦車が向いている方向に進む</td></tr></tbody></table>

今回のポイントは、ただ前に進むだけではありません。

```text
ワールドの+Z方向に進むのではなく、
戦車が向いている方向に進む

```

という処理を作ります。

---

## 2. 今日作る処理の方針

今回の移動処理は、次の流れで作ります。

```text
Aキー・Dキーで戦車の角度を変える
↓
現在位置を計算用のベクトル型に変換する
↓
まずは+Z方向に進む移動ベクトルを作る
↓
戦車のY軸回転角度から回転行列を作る
↓
移動ベクトルを回転行列で回転させる
↓
Wキーが押されたら、現在位置に移動ベクトルを足す
↓
計算結果をtransform_.position_に戻す

```

この流れを先に確認しておくと、これから書く1行1行の意味がわかりやすくなります。

---

## 3. なぜ移動ベクトルを回転させるのか

戦車を動かすだけなら、現在位置に移動量を足せば動きます。

しかし今回は、

```text
戦車が向いている方向に進む

```

ようにしたいです。

例えば、移動ベクトルを常に

```text
+Z方向に 0.1 進む

```

としてしまうと、戦車が右を向いても左を向いても、移動方向はずっと+Z方向のままです。

これでは、戦車の見た目は横を向いているのに、移動だけは+Z方向へ滑っていくような動きになります。

そこで、次のように考えます。

```text
まず、前に進む移動ベクトルを作る
↓
その移動ベクトルを、戦車の向きと同じだけ回転させる
↓
回転した移動ベクトルを、現在位置に足す

```

今回一番大事なのは、

```text
移動ベクトルを、戦車の向きに合わせて回転させる

```

という考え方です。

---

## 4. 3D空間での「戦車の向き」

今回の戦車は、最初は `+Z方向` を向いているものとします。

上から見たイメージは次のようになります。

```text
          +Z
           ↑
           |
           |
-X ←---- 戦車 ----→ +X
           |
           |
           ↓
          -Z

```

戦車が左右に向きを変えるときは、主に **Y軸回転** を使います。

Y軸回転とは、上から見たときに向きを変える回転です。

---

## 5. 必要な情報

戦車を動かすには、次の情報が必要です。

<table id="bkmrk-%E5%BF%85%E8%A6%81%E3%81%AA%E6%83%85%E5%A0%B1-%E5%86%85%E5%AE%B9-%E7%8F%BE%E5%9C%A8%E4%BD%8D%E7%BD%AE-%E6%88%A6%E8%BB%8A%E3%81%8C%E4%BB%8A%E3%81%A9%E3%81%93"><thead><tr><th>必要な情報</th><th>内容</th></tr></thead><tbody><tr><td>現在位置</td><td>戦車が今どこにいるか</td></tr><tr><td>回転角度</td><td>戦車がどちらを向いているか</td></tr><tr><td>移動量</td><td>1フレームでどれだけ進むか</td></tr></tbody></table>

今回使う変数は、次のようなものだと考えます。

```cpp
transform_.position_   // 戦車の位置
transform_.rotate_.y   // 戦車のY軸回転

```

---

# 穴埋めでUpdate関数を作る

ここから、戦車の `Update` 関数を少しずつ完成させます。

最初から完成コードを見るのではなく、処理の意味を確認しながら、1行ずつ作っていきます。

---

## 6. AキーとDキーで回転させる

まず、AキーとDキーで戦車の角度を変えます。

<table id="bkmrk-%E3%82%AD%E3%83%BC-%E5%87%A6%E7%90%86-a%E3%82%AD%E3%83%BC-y%E8%BB%B8%E5%9B%9E%E8%BB%A2%E8%A7%92%E5%BA%A6%E3%82%92%E5%B0%8F%E3%81%95%E3%81%8F"><thead><tr><th>キー</th><th>処理</th></tr></thead><tbody><tr><td>Aキー</td><td>Y軸回転角度を小さくする</td></tr><tr><td>Dキー</td><td>Y軸回転角度を大きくする</td></tr></tbody></table>

## 穴埋め 1

次のコードを完成させなさい。

```cpp
// Aキーが押されたら左回転
if (Input::IsKey(DIK_A))
{
    transform_.rotate_.y ____ 2;
}

// Dキーが押されたら右回転
if (Input::IsKey(DIK_D))
{
    transform_.rotate_.y ____ 2;
}

```

### ヒント

左回転では角度を小さくします。  
右回転では角度を大きくします。

---

## 7. 現在位置を計算用の型に変換する

`transform_.position_` は、戦車の現在位置です。

ただし、DirectXMathでベクトル計算をするためには、`XMVECTOR` 型に変換して使います。

そのため、まず現在位置を `XMVECTOR` 型の変数に読み込みます。

## 穴埋め 2

次のコードを完成させなさい。

```cpp
// 戦車の現在地をベクトル型に変換
XMVECTOR vPos = ____________________________;

```

### 使う関数

```cpp
XMLoadFloat3(&transform_.position_)

```

---

## 8. まずは「前に進むベクトル」を作る

次に、1フレーム分の移動ベクトルを作ります。

今回は、戦車が最初に向いている方向を `+Z方向` とします。

そのため、最初の移動ベクトルは次のように考えます。

```text
X方向には進まない
Y方向には進まない
Z方向に 0.1 進む

```

つまり、移動ベクトルは次のようになります。

<table id="bkmrk-%E6%88%90%E5%88%86-%E5%80%A4-%E6%84%8F%E5%91%B3-x-0.0-%E6%A8%AA%E6%96%B9%E5%90%91%E3%81%AB%E3%81%AF%E9%80%B2"><thead><tr><th>成分</th><th align="right">値</th><th>意味</th></tr></thead><tbody><tr><td>X</td><td align="right">0.0</td><td>横方向には進まない</td></tr><tr><td>Y</td><td align="right">0.0</td><td>上下方向には進まない</td></tr><tr><td>Z</td><td align="right">0.1</td><td>前方向に進む</td></tr><tr><td>W</td><td align="right">0.0</td><td>ここでは移動ベクトルとして扱う</td></tr></tbody></table>

## 穴埋め 3

次のコードを完成させなさい。

```cpp
// 1フレームの移動ベクトル
XMVECTOR vMove{ ____ , ____ , ____ , ____ };

```

### 入れる値

```cpp
0.0f
0.0f
0.1f
0.0f

```

---

## 9. このままだと何が問題か

ここまでだと、移動ベクトルは常に次の意味になります。

```text
+Z方向に0.1進む

```

しかし、戦車が回転している場合、このままでは問題があります。

戦車が右を向いても、左を向いても、移動方向はずっと `+Z方向` のままです。

## 穴埋め 4

次の文章を完成させなさい。

```text
このままだと、戦車の見た目が回転しても、
移動方向はずっと __________ 方向のままになる。

そのため、戦車が横を向いているのに、
__________ 方向へ滑るように進んでしまう。

```

---

## 10. 回転行列を作る

移動ベクトルを戦車の向きに合わせるには、移動ベクトルを回転させる必要があります。

ベクトルを回転させるには、回転行列を作ります。

今回はY軸まわりに回転させたいので、使う関数は次のものです。

```cpp
XMMatrixRotationY()

```

ただし、この関数に渡す角度は **度** ではなく **ラジアン** です。

そのため、角度をラジアンに変換する必要があります。

使う関数は次のものです。

```cpp
XMConvertToRadians()

```

## 穴埋め 5

次のコードを完成させなさい。

```cpp
// transform_.rotate_.y度回転させる行列を作成
XMMATRIX mRotY = XMMatrixRotationY(____________________________);

```

### 使う式

```cpp
XMConvertToRadians(transform_.rotate_.y)

```

---

## 11. 移動ベクトルを回転行列で変形する

回転行列を作ったら、移動ベクトルをその行列で変形します。

使う関数は次のものです。

```cpp
XMVector3TransformCoord()

```

この処理によって、

```text
+Z方向に進むベクトル

```

が、

```text
戦車が向いている方向に進むベクトル

```

に変わります。

## 穴埋め 6

次のコードを完成させなさい。

```cpp
// 移動ベクトルを戦車の向きに合わせて回転
vMove = XMVector3TransformCoord(__________, __________);

```

### 入れるもの

```cpp
vMove
mRotY

```

---

## 12. Wキーで前進する

最後に、Wキーが押されたら戦車を移動させます。

移動は、次の考え方です。

```text
現在位置 = 現在位置 + 移動ベクトル

```

つまり、現在位置に、今回作った移動ベクトルを足します。

そのあと、計算した位置を `transform_.position_` に戻します。

## 穴埋め 7

次のコードを完成させなさい。

```cpp
// Wキーが押されたら
if (Input::IsKey(DIK_W))
{
    // 移動
    vPos ____ vMove;

    // 現在地をtransform_.position_に戻す
    XMStoreFloat3(________________________, ________);
}

```

### ヒント

現在位置に移動量を足すので、次のようになります。

```cpp
vPos += vMove;

```

`XMStoreFloat3` では、計算した `vPos` を `transform_.position_` に戻します。

---

# 13. Update関数を完成させよう

ここまでの穴埋めをすべてつなげると、戦車の移動処理になります。

## 穴埋め 8

次の `Update` 関数を完成させなさい。

```cpp
// 更新
void Tank::Update()
{
    // Aキーが押されたら左回転
    if (Input::IsKey(DIK_A))
    {
        transform_.rotate_.y ____ 2;
    }

    // Dキーが押されたら右回転
    if (Input::IsKey(DIK_D))
    {
        transform_.rotate_.y ____ 2;
    }

    // 戦車の現在地をベクトル型に変換
    XMVECTOR vPos = ____________________________;

    // 1フレームの移動ベクトル
    XMVECTOR vMove{ ____ , ____ , ____ , ____ };

    // Y軸回転行列を作成
    XMMATRIX mRotY = XMMatrixRotationY(____________________________);

    // 移動ベクトルを戦車の向きに合わせて回転
    vMove = XMVector3TransformCoord(__________, __________);

    // Wキーが押されたら
    if (Input::IsKey(DIK_W))
    {
        // 移動
        vPos ____ vMove;

        // 計算結果をtransform_.position_に戻す
        XMStoreFloat3(________________________, ________);
    }
}

```

---

# 14. 完成コード

自分で穴埋めを終えてから確認しましょう。

```cpp
// 更新
void Tank::Update()
{
    // Aキーが押されたら左回転
    if (Input::IsKey(DIK_A))
    {
        transform_.rotate_.y -= 2;
    }

    // Dキーが押されたら右回転
    if (Input::IsKey(DIK_D))
    {
        transform_.rotate_.y += 2;
    }

    // 戦車の現在地をベクトル型に変換
    XMVECTOR vPos = XMLoadFloat3(&transform_.position_);

    // 1フレームの移動ベクトル
    XMVECTOR vMove{ 0.0f, 0.0f, 0.1f, 0.0f };

    // transform_.rotate_.y度回転させる行列を作成
    XMMATRIX mRotY = XMMatrixRotationY(XMConvertToRadians(transform_.rotate_.y));

    // 移動ベクトルを戦車の向きに合わせて回転
    vMove = XMVector3TransformCoord(vMove, mRotY);

    // Wキーが押されたら
    if (Input::IsKey(DIK_W))
    {
        // 移動
        vPos += vMove;

        // 現在地をtransform_.position_に戻す
        XMStoreFloat3(&transform_.position_, vPos);
    }
}

```

---

# 15. 処理の流れをもう一度確認する

今回の処理は、次の順番でした。

```text
Aキー・Dキーで戦車の角度を変える
↓
現在位置を計算用のベクトル型に変換する
↓
まずは+Z方向に進む移動ベクトルを作る
↓
戦車のY軸回転角度から回転行列を作る
↓
移動ベクトルを回転行列で回転させる
↓
Wキーが押されたら、現在位置に移動ベクトルを足す
↓
計算結果をtransform_.position_に戻す

```

重要なのは、次の流れです。

```text
前方向の移動ベクトルを作る
↓
戦車の向きに合わせて回転させる
↓
現在位置に足す

```

---

# 16. まとめ穴埋め

次の文章を完成させなさい。

```text
戦車を向いている方向に進ませるには、
まず __________ 方向に進む移動ベクトルを作る。

しかし、そのままだと常に __________ 方向に進んでしまう。

そこで、戦車の __________ 角度を使って、
__________ 行列を作る。

その行列で __________ ベクトルを回転させることで、
戦車の向いている方向に進めるようになる。

最後に、現在位置に __________ を足すことで、
戦車の位置が更新される。

```

---

# 17. 確認問題

## 問題1

次の処理は何をしているか説明しなさい。

```cpp
transform_.rotate_.y += 2;

```

---

## 問題2

次の移動ベクトルは、どの方向にどれだけ進むベクトルか答えなさい。

```cpp
XMVECTOR vMove{ 0.0f, 0.0f, 0.1f, 0.0f };

```

---

## 問題3

なぜ、移動ベクトルを回転させる必要があるのか説明しなさい。

---

## 問題4

次の処理は何をしているか説明しなさい。

```cpp
vPos += vMove;

```

---

## 問題5

`XMMatrixRotationY()` に角度を渡す前に、なぜ `XMConvertToRadians()` を使っているのか説明しなさい。

---

# 18. 解答例

## 穴埋め1

```cpp
transform_.rotate_.y -= 2;
transform_.rotate_.y += 2;

```

---

## 穴埋め2

```cpp
XMLoadFloat3(&transform_.position_)

```

---

## 穴埋め3

```cpp
XMVECTOR vMove{ 0.0f, 0.0f, 0.1f, 0.0f };

```

---

## 穴埋め4

```text
このままだと、戦車の見た目が回転しても、
移動方向はずっと +Z 方向のままになる。

そのため、戦車が横を向いているのに、
+Z 方向へ滑るように進んでしまう。

```

---

## 穴埋め5

```cpp
XMConvertToRadians(transform_.rotate_.y)

```

---

## 穴埋め6

```cpp
vMove = XMVector3TransformCoord(vMove, mRotY);

```

---

## 穴埋め7

```cpp
vPos += vMove;

XMStoreFloat3(&transform_.position_, vPos);

```

---

## 穴埋め8

```cpp
// 更新
void Tank::Update()
{
    // Aキーが押されたら左回転
    if (Input::IsKey(DIK_A))
    {
        transform_.rotate_.y -= 2;
    }

    // Dキーが押されたら右回転
    if (Input::IsKey(DIK_D))
    {
        transform_.rotate_.y += 2;
    }

    // 戦車の現在地をベクトル型に変換
    XMVECTOR vPos = XMLoadFloat3(&transform_.position_);

    // 1フレームの移動ベクトル
    XMVECTOR vMove{ 0.0f, 0.0f, 0.1f, 0.0f };

    // Y軸回転行列を作成
    XMMATRIX mRotY = XMMatrixRotationY(XMConvertToRadians(transform_.rotate_.y));

    // 移動ベクトルを戦車の向きに合わせて回転
    vMove = XMVector3TransformCoord(vMove, mRotY);

    // Wキーが押されたら
    if (Input::IsKey(DIK_W))
    {
        // 移動
        vPos += vMove;

        // 計算結果をtransform_.position_に戻す
        XMStoreFloat3(&transform_.position_, vPos);
    }
}

```

---

## まとめ穴埋め

```text
戦車を向いている方向に進ませるには、
まず +Z 方向に進む移動ベクトルを作る。

しかし、そのままだと常に +Z 方向に進んでしまう。

そこで、戦車の Y軸回転 角度を使って、
回転 行列を作る。

その行列で 移動 ベクトルを回転させることで、
戦車の向いている方向に進めるようになる。

最後に、現在位置に 移動ベクトル を足すことで、
戦車の位置が更新される。

```

---

# 19. まとめ

```text
戦車の移動

1. A/Dキーで角度を変える
2. 前方向の移動ベクトルを作る
3. 戦車の角度から回転行列を作る
4. 移動ベクトルを回転させる
5. 現在位置に足す

重要：
位置を回転させるのではなく、
移動方向を回転させてから位置に足す。

```

# ゲームにおけるフィードバックの研究

# ゲーム研究レポート

# ゲームにおける入力・出力・フィードバックを調べる

## この授業でやること

この授業では、1つのゲームだけを詳しく調べるのではなく、  
いろいろなゲームを見比べながら、  
**入力・出力・フィードバック** の関係を調べます。

ゲームでは、プレイヤーがボタンやキーを押すと、  
ゲーム内で何かが起こります。

そして、その結果をプレイヤーに伝えるために、  
画面・音・エフェクト・振動・UIなどの反応が返ってきます。

この流れを、次のように考えます。

```text
入力
↓
ゲーム内の処理
↓
出力
↓
フィードバック
↓
プレイヤーの理解

```

ゲームプログラマは、  
「入力を受け取って処理する」だけではなく、  
「処理結果をプレイヤーに分かりやすく返す」ことも考える必要があります。

---

## 今日のゴール

授業の最後に、次のことを説明できるようになることを目指します。

- ゲームにおける入力とは何か
- ゲームにおける出力とは何か
- フィードバックとは何か
- 視覚・聴覚・触覚のフィードバックにはどんな違いがあるか
- 入力とフィードバックがどうつながっているか
- プログラマとして、どんな処理を実装する必要があるか
- 自分のゲーム制作に、調べた内容をどう活かせるか

---

## 授業時間の目安

<table id="bkmrk-%E6%99%82%E9%96%93-%E5%86%85%E5%AE%B9-10%E5%88%86-%E5%85%A5%E5%8A%9B%E3%83%BB%E5%87%BA%E5%8A%9B%E3%83%BB%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89"><thead><tr><th align="right">時間</th><th>内容</th></tr></thead><tbody><tr><td align="right">10分</td><td>入力・出力・フィードバックの説明</td></tr><tr><td align="right">10分</td><td>調査するゲームを選ぶ</td></tr><tr><td align="right">15分</td><td>入力と出力を調べる</td></tr><tr><td align="right">20分</td><td>視覚・聴覚・触覚フィードバックを調べる</td></tr><tr><td align="right">15分</td><td>複数の感覚を組み合わせたフィードバックを調べる</td></tr><tr><td align="right">10分</td><td>プログラマとして必要な処理を考える</td></tr><tr><td align="right">10分</td><td>最終まとめを書く</td></tr></tbody></table>

---

# 手順1 入力・出力・フィードバックの意味を確認する

## 入力とは

入力とは、プレイヤーがゲームに対して行う操作のことです。

## 入力の例

- 方向キーを押す
- スティックを倒す
- ボタンを押す
- ボタンを長押しする
- ボタンを離す
- ボタンを連打する
- 複数のボタンを同時に押す
- マウスを動かす
- マウスをクリックする
- 画面をタップする
- 画面をスワイプする
- コントローラーを傾ける

---

## 出力とは

出力とは、入力やゲーム内の処理の結果として、  
ゲームが画面・音・振動・UIなどで表すものです。

## 出力の例

- キャラクターが動く
- キャラクターがジャンプする
- 弾が出る
- 攻撃モーションが出る
- 敵がダメージを受ける
- HPが減る
- スコアが増える
- 効果音が鳴る
- エフェクトが出る
- 画面が揺れる
- メニューが開く
- コントローラーが振動する

---

## フィードバックとは

フィードバックとは、プレイヤーに  
「何が起きたか」  
「操作が成功したか」  
「失敗したか」  
「危険なのか」  
「報酬を得たのか」  
を伝える反応のことです。

## フィードバックの例

- ジャンプしたときにジャンプ音が鳴る
- 攻撃が当たったときにヒットエフェクトが出る
- ダメージを受けたときにキャラクターが点滅する
- コインを取ったときに取得音が鳴る
- 残り時間が少なくなると警告音が鳴る
- HPが減るとHPバーが短くなる
- 爆発が起きると画面が揺れる
- 攻撃を受けるとコントローラーが振動する

---

# 手順2 調査するゲームを複数選ぶ

今回は、1つのゲームだけでなく、複数のゲームを見比べます。

最低でも **3本以上** のゲームを調べてください。  
余裕がある場合は、5本程度調べてもよいです。

実際に遊んでも、プレイ動画で確認してもかまいません。

## 選ぶゲームの例

できれば、違うジャンルのゲームを選んでください。

<table id="bkmrk-%E3%82%B8%E3%83%A3%E3%83%B3%E3%83%AB-%E4%BE%8B-%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3-%E3%82%B9%E3%83%BC%E3%83%91%E3%83%BC%E3%83%9E%E3%83%AA%E3%82%AA"><thead><tr><th>ジャンル</th><th>例</th></tr></thead><tbody><tr><td>アクション</td><td>スーパーマリオ、ロックマン、カービィ</td></tr><tr><td>シューティング</td><td>スペースインベーダー、グラディウス、ツインビー</td></tr><tr><td>パズル</td><td>テトリス、ぷよぷよ、ドクターマリオ</td></tr><tr><td>格闘</td><td>ストリートファイター、スマブラ</td></tr><tr><td>レース</td><td>マリオカート、F-ZERO</td></tr><tr><td>RPG</td><td>ドラクエ、ポケモン</td></tr><tr><td>音楽ゲーム</td><td>太鼓の達人、リズム天国</td></tr><tr><td>スマホゲーム</td><td>パズドラ、モンスト、ツムツム</td></tr></tbody></table>

## 記入欄

<table id="bkmrk-%E7%95%AA%E5%8F%B7-%E3%82%B2%E3%83%BC%E3%83%A0%E5%90%8D-%E3%82%B8%E3%83%A3%E3%83%B3%E3%83%AB-%E8%AA%BF%E3%81%B9%E6%96%B9-1-%EF%BC%BF"><thead><tr><th align="right">番号</th><th>ゲーム名</th><th>ジャンル</th><th>調べ方</th></tr></thead><tbody><tr><td align="right">1</td><td>＿＿＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>実際に遊んだ / 動画で見た / 調べた</td></tr><tr><td align="right">2</td><td>＿＿＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>実際に遊んだ / 動画で見た / 調べた</td></tr><tr><td align="right">3</td><td>＿＿＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>実際に遊んだ / 動画で見た / 調べた</td></tr><tr><td align="right">4</td><td>＿＿＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>実際に遊んだ / 動画で見た / 調べた</td></tr><tr><td align="right">5</td><td>＿＿＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>実際に遊んだ / 動画で見た / 調べた</td></tr></tbody></table>

---

# 手順3 入力の種類を集める

調べたゲームの中から、プレイヤーが行う入力を集めます。

## 入力の種類の例

<table id="bkmrk-%E5%85%A5%E5%8A%9B%E3%81%AE%E7%A8%AE%E9%A1%9E-%E5%86%85%E5%AE%B9-%E6%8A%BC%E3%81%99-%E3%83%9C%E3%82%BF%E3%83%B3%E3%82%921%E5%9B%9E%E6%8A%BC%E3%81%99"><thead><tr><th>入力の種類</th><th>内容</th></tr></thead><tbody><tr><td>押す</td><td>ボタンを1回押す</td></tr><tr><td>長押し</td><td>ボタンを押し続ける</td></tr><tr><td>離す</td><td>押していたボタンを離す</td></tr><tr><td>連打</td><td>何度もボタンを押す</td></tr><tr><td>同時押し</td><td>複数のボタンを同時に押す</td></tr><tr><td>方向入力</td><td>十字キーやスティックで方向を入れる</td></tr><tr><td>タイミング入力</td><td>決まったタイミングで押す</td></tr><tr><td>タップ</td><td>画面を触る</td></tr><tr><td>スワイプ</td><td>画面をなぞる</td></tr><tr><td>ドラッグ</td><td>押したまま動かす</td></tr><tr><td>マウス移動</td><td>カーソルを動かす</td></tr><tr><td>ジャイロ操作</td><td>本体やコントローラーを傾ける</td></tr></tbody></table>

## 記入欄

<table id="bkmrk-%E3%82%B2%E3%83%BC%E3%83%A0%E5%90%8D-%E5%85%A5%E5%8A%9B-%E3%81%9D%E3%81%AE%E5%85%A5%E5%8A%9B%E3%81%A7%E8%B5%B7%E3%81%8D%E3%82%8B%E3%81%93%E3%81%A8-%EF%BC%BF"><thead><tr><th>ゲーム名</th><th>入力</th><th>その入力で起きること</th></tr></thead><tbody><tr><td>＿＿＿＿＿＿</td><td>＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿＿＿</td><td>＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿＿＿</td><td>＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿＿＿</td><td>＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿＿＿</td><td>＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿＿＿</td><td>＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 手順4 入力から出力までの流れを書く

次に、入力したあとに何が起こるのかを調べます。

## 書き方

次の形で考えます。

```text
プレイヤーが入力する
↓
ゲーム内で処理が行われる
↓
画面や音などに出力される
↓
プレイヤーが結果を理解する

```

## 記入例

```text
入力：Aボタンを押す
↓
処理：キャラクターに上向きの速度を与える
↓
出力：キャラクターがジャンプする
↓
フィードバック：ジャンプ音が鳴り、ジャンプアニメーションになる
↓
プレイヤーの理解：ジャンプできたと分かる

```

## 記入欄

<table id="bkmrk-%E3%82%B2%E3%83%BC%E3%83%A0%E5%90%8D-%E5%85%A5%E5%8A%9B-%E3%82%B2%E3%83%BC%E3%83%A0%E5%86%85%E3%81%AE%E5%87%A6%E7%90%86%E3%83%BB%E7%B5%90%E6%9E%9C-%E3%83%97"><thead><tr><th>ゲーム名</th><th>入力</th><th>ゲーム内の処理・結果</th><th>プレイヤーに返る反応</th></tr></thead><tbody><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 手順5 フィードバックを感覚ごとに分類する

ゲームのフィードバックは、プレイヤーの感覚ごとに分けて考えることができます。

主に次の3つに分けます。

<table id="bkmrk-%E7%A8%AE%E9%A1%9E-%E4%BD%BF%E3%81%86%E6%84%9F%E8%A6%9A-%E4%BE%8B-%E8%A6%96%E8%A6%9A%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89%E3%83%90%E3%83%83%E3%82%AF-"><thead><tr><th>種類</th><th>使う感覚</th><th>例</th></tr></thead><tbody><tr><td>視覚フィードバック</td><td>目で見る</td><td>エフェクト、点滅、UI、画面揺れ、アニメーション</td></tr><tr><td>聴覚フィードバック</td><td>耳で聞く</td><td>効果音、BGM変化、警告音、決定音</td></tr><tr><td>触覚フィードバック</td><td>手や体で感じる</td><td>コントローラー振動、スマホの振動、押した感触</td></tr></tbody></table>

---

# 手順6 視覚フィードバックを調べる

## 視覚フィードバックとは

視覚フィードバックとは、画面を見て分かる反応のことです。

プレイヤーは、画面の変化を見ることで、  
自分の操作が成功したのか、失敗したのか、危険なのかを判断します。

## 視覚フィードバックの例

<table id="bkmrk-%E5%A0%B4%E9%9D%A2-%E8%A6%96%E8%A6%9A%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89%E3%83%90%E3%83%83%E3%82%AF-%E4%BC%9D%E3%81%88%E3%81%A6%E3%81%84%E3%82%8B%E3%81%93%E3%81%A8"><thead><tr><th>場面</th><th>視覚フィードバック</th><th>伝えていること</th></tr></thead><tbody><tr><td>攻撃が当たった</td><td>ヒットエフェクトが出る</td><td>攻撃が成功した</td></tr><tr><td>ダメージを受けた</td><td>キャラクターが点滅する</td><td>ダメージを受けた</td></tr><tr><td>危険が近い</td><td>床や敵が赤く光る</td><td>危険な状態である</td></tr><tr><td>アイテムを取った</td><td>アイテムが消え、光る演出が出る</td><td>アイテム取得に成功した</td></tr><tr><td>HPが減った</td><td>HPバーが短くなる</td><td>残り体力が減った</td></tr><tr><td>強い攻撃が当たった</td><td>画面が揺れる</td><td>大きな衝撃があった</td></tr></tbody></table>

## プログラムで必要そうな処理

<table id="bkmrk-%E8%A1%A8%E7%8F%BE-%E5%BF%85%E8%A6%81%E3%81%9D%E3%81%86%E3%81%AA%E5%87%A6%E7%90%86-%E3%82%A8%E3%83%95%E3%82%A7%E3%82%AF%E3%83%88%E8%A1%A8%E7%A4%BA-%E3%82%A8"><thead><tr><th>表現</th><th>必要そうな処理</th></tr></thead><tbody><tr><td>エフェクト表示</td><td>エフェクト生成、位置指定、再生、削除</td></tr><tr><td>点滅</td><td>表示・非表示の切り替え、タイマー管理</td></tr><tr><td>UI更新</td><td>HPやスコアの値を変更し、画面に反映</td></tr><tr><td>画面揺れ</td><td>カメラ位置を短時間だけ変化させる</td></tr><tr><td>色変更</td><td>ダメージ時や警告時に色を変更する</td></tr><tr><td>アニメーション変更</td><td>状態に応じてモーションを切り替える</td></tr></tbody></table>

## 記入欄

<table id="bkmrk-%E3%82%B2%E3%83%BC%E3%83%A0%E5%90%8D-%E5%A0%B4%E9%9D%A2-%E8%A6%96%E8%A6%9A%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89%E3%83%90%E3%83%83%E3%82%AF-%E4%BD%95%E3%82%92"><thead><tr><th>ゲーム名</th><th>場面</th><th>視覚フィードバック</th><th>何を伝えているか</th></tr></thead><tbody><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 手順7 聴覚フィードバックを調べる

## 聴覚フィードバックとは

聴覚フィードバックとは、音で分かる反応のことです。

効果音やBGMによって、プレイヤーは画面を細かく見ていなくても、  
成功・失敗・危険・報酬などに気づくことができます。

## 聴覚フィードバックの例

<table id="bkmrk-%E5%A0%B4%E9%9D%A2-%E8%81%B4%E8%A6%9A%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89%E3%83%90%E3%83%83%E3%82%AF-%E4%BC%9D%E3%81%88%E3%81%A6%E3%81%84%E3%82%8B%E3%81%93%E3%81%A8"><thead><tr><th>場面</th><th>聴覚フィードバック</th><th>伝えていること</th></tr></thead><tbody><tr><td>ジャンプした</td><td>ジャンプ音が鳴る</td><td>ジャンプした</td></tr><tr><td>攻撃が当たった</td><td>ヒット音が鳴る</td><td>攻撃が成功した</td></tr><tr><td>ダメージを受けた</td><td>ダメージ音が鳴る</td><td>失敗・被害を受けた</td></tr><tr><td>アイテムを取った</td><td>取得音が鳴る</td><td>報酬を得た</td></tr><tr><td>メニューを決定した</td><td>決定音が鳴る</td><td>入力が受け付けられた</td></tr><tr><td>危険が近い</td><td>警告音が鳴る</td><td>危険が迫っている</td></tr><tr><td>残り時間が少ない</td><td>BGMが速くなる</td><td>急ぐ必要がある</td></tr></tbody></table>

## プログラムで必要そうな処理

<table id="bkmrk-%E8%A1%A8%E7%8F%BE-%E5%BF%85%E8%A6%81%E3%81%9D%E3%81%86%E3%81%AA%E5%87%A6%E7%90%86-%E5%8A%B9%E6%9E%9C%E9%9F%B3%E5%86%8D%E7%94%9F-%E6%9D%A1%E4%BB%B6%E6%88%90"><thead><tr><th>表現</th><th>必要そうな処理</th></tr></thead><tbody><tr><td>効果音再生</td><td>条件成立時にSEを再生する</td></tr><tr><td>BGM変更</td><td>状態に応じてBGMを切り替える</td></tr><tr><td>音量変更</td><td>距離や状況に応じて音量を変える</td></tr><tr><td>警告音</td><td>危険状態の間、一定間隔で鳴らす</td></tr><tr><td>決定音・キャンセル音</td><td>UI操作時に入力に応じて音を鳴らす</td></tr><tr><td>連続音の制御</td><td>同じ音が鳴りすぎないように制限する</td></tr></tbody></table>

## 記入欄

<table id="bkmrk-%E3%82%B2%E3%83%BC%E3%83%A0%E5%90%8D-%E5%A0%B4%E9%9D%A2-%E8%81%B4%E8%A6%9A%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89%E3%83%90%E3%83%83%E3%82%AF-%E4%BD%95%E3%82%92"><thead><tr><th>ゲーム名</th><th>場面</th><th>聴覚フィードバック</th><th>何を伝えているか</th></tr></thead><tbody><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 手順8 触覚フィードバックを調べる

## 触覚フィードバックとは

触覚フィードバックとは、手や体で感じる反応のことです。

代表的なものは、コントローラーやスマートフォンの振動です。

プレイヤーは振動によって、  
衝撃・ダメージ・成功・危険などを感覚的に理解できます。

## 触覚フィードバックの例

<table id="bkmrk-%E5%A0%B4%E9%9D%A2-%E8%A7%A6%E8%A6%9A%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89%E3%83%90%E3%83%83%E3%82%AF-%E4%BC%9D%E3%81%88%E3%81%A6%E3%81%84%E3%82%8B%E3%81%93%E3%81%A8"><thead><tr><th>場面</th><th>触覚フィードバック</th><th>伝えていること</th></tr></thead><tbody><tr><td>攻撃が当たった</td><td>短く振動する</td><td>攻撃の手応え</td></tr><tr><td>ダメージを受けた</td><td>強く振動する</td><td>被害を受けた</td></tr><tr><td>爆発が起きた</td><td>大きく長めに振動する</td><td>大きな衝撃</td></tr><tr><td>車が悪路を走る</td><td>細かく振動する</td><td>路面の感触</td></tr><tr><td>危険が近い</td><td>弱い振動を繰り返す</td><td>危険が近づいている</td></tr><tr><td>釣りゲームで魚が引く</td><td>不規則に振動する</td><td>魚の動きや抵抗</td></tr></tbody></table>

## プログラムで必要そうな処理

<table id="bkmrk-%E8%A1%A8%E7%8F%BE-%E5%BF%85%E8%A6%81%E3%81%9D%E3%81%86%E3%81%AA%E5%87%A6%E7%90%86-%E7%9F%AD%E3%81%84%E6%8C%AF%E5%8B%95-%E6%88%90%E5%8A%9F%E6%99%82%E3%82%84"><thead><tr><th>表現</th><th>必要そうな処理</th></tr></thead><tbody><tr><td>短い振動</td><td>成功時やヒット時に短時間だけ振動</td></tr><tr><td>強い振動</td><td>ダメージや爆発時に強めに振動</td></tr><tr><td>弱い振動の繰り返し</td><td>危険状態をタイマーで管理して振動</td></tr><tr><td>振動の強弱</td><td>状況に応じて振動の強さを変える</td></tr><tr><td>振動時間の制御</td><td>長すぎないように時間で止める</td></tr><tr><td>機種対応</td><td>振動できる機器か確認する</td></tr></tbody></table>

## 記入欄

<table id="bkmrk-%E3%82%B2%E3%83%BC%E3%83%A0%E5%90%8D-%E5%A0%B4%E9%9D%A2-%E8%A7%A6%E8%A6%9A%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89%E3%83%90%E3%83%83%E3%82%AF-%E4%BD%95%E3%82%92"><thead><tr><th>ゲーム名</th><th>場面</th><th>触覚フィードバック</th><th>何を伝えているか</th></tr></thead><tbody><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 手順9 複数の感覚を組み合わせたフィードバックを調べる

ゲームでは、1つの出来事に対して、  
視覚・聴覚・触覚のフィードバックを同時に使うことが多いです。

## 例：攻撃が当たったとき

<table id="bkmrk-%E6%84%9F%E8%A6%9A-%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89%E3%83%90%E3%83%83%E3%82%AF-%E8%A6%96%E8%A6%9A-%E3%83%92%E3%83%83%E3%83%88%E3%82%A8%E3%83%95%E3%82%A7"><thead><tr><th>感覚</th><th>フィードバック</th></tr></thead><tbody><tr><td>視覚</td><td>ヒットエフェクト、敵の点滅</td></tr><tr><td>聴覚</td><td>ヒット音</td></tr><tr><td>触覚</td><td>短い振動</td></tr></tbody></table>

このように複数の反応を組み合わせることで、  
プレイヤーは「攻撃が当たった」と強く感じることができます。

## 記入欄

<table id="bkmrk-%E3%82%B2%E3%83%BC%E3%83%A0%E5%90%8D-%E5%A0%B4%E9%9D%A2-%E8%A6%96%E8%A6%9A-%E8%81%B4%E8%A6%9A-%E8%A7%A6%E8%A6%9A-%E3%83%97%E3%83%AC%E3%82%A4"><thead><tr><th>ゲーム名</th><th>場面</th><th>視覚</th><th>聴覚</th><th>触覚</th><th>プレイヤーに伝わること</th></tr></thead><tbody><tr><td>＿＿＿＿</td><td>攻撃が当たった</td><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>ダメージを受けた</td><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>アイテムを取った</td><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>危険が近い</td><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿</td></tr><tr><td>＿＿＿＿</td><td>クリアした</td><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿</td><td>＿＿＿＿</td></tr></tbody></table>

---

# 手順10 UIによるフィードバックを調べる

UIは、視覚フィードバックの中でも、プレイヤーに状態を伝える重要な要素です。

## UIの例

- HPバー
- スコア
- 残り時間
- 残機
- 弾数
- スタミナ
- アイテム欄
- ミニマップ
- クエスト表示
- ボタン表示
- チュートリアル表示

## 記入欄

<table id="bkmrk-%E3%82%B2%E3%83%BC%E3%83%A0%E5%90%8D-ui%E8%A1%A8%E7%A4%BA-%E4%BD%95%E3%82%92%E4%BC%9D%E3%81%88%E3%81%A6%E3%81%84%E3%82%8B%E3%81%8B-%E8%A6%8B"><thead><tr><th>ゲーム名</th><th>UI表示</th><th>何を伝えているか</th><th>見やすさ</th></tr></thead><tbody><tr><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>見やすい / 見にくい</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>見やすい / 見にくい</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>見やすい / 見にくい</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>見やすい / 見にくい</td></tr><tr><td>＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿</td><td>見やすい / 見にくい</td></tr></tbody></table>

---

# 手順11 フィードバックが弱いとどうなるか考える

フィードバックが弱いと、プレイヤーは何が起きたのか分かりにくくなります。

## 例

<table id="bkmrk-%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89%E3%83%90%E3%83%83%E3%82%AF%E3%81%8C%E5%BC%B1%E3%81%84%E7%8A%B6%E6%85%8B-%E3%83%97%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC%E3%81%8C%E6%84%9F"><thead><tr><th>フィードバックが弱い状態</th><th>プレイヤーが感じること</th></tr></thead><tbody><tr><td>攻撃が当たっても音が鳴らない</td><td>当たったか分からない</td></tr><tr><td>ダメージを受けても画面変化が少ない</td><td>いつHPが減ったか分からない</td></tr><tr><td>危険な攻撃の予備動作がない</td><td>急にやられたと感じる</td></tr><tr><td>ボタンを押しても反応が薄い</td><td>操作している感じがしない</td></tr><tr><td>UIが見にくい</td><td>状況を判断しにくい</td></tr><tr><td>報酬を取っても演出が弱い</td><td>嬉しさが弱い</td></tr></tbody></table>

## 記入欄

フィードバックが弱いと、プレイヤーは  
＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿  
と感じる。

特にゲームで問題になるのは、  
＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿  
である。

その理由は、  
＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿  
である。

---

# 手順12 入力とフィードバックの関係を図にする

調べた内容から、入力とフィードバックの流れを図の形で整理します。

## 記入欄1

```text
入力：＿＿＿＿＿＿＿＿＿＿
↓
処理：＿＿＿＿＿＿＿＿＿＿
↓
出力：＿＿＿＿＿＿＿＿＿＿
↓
視覚フィードバック：＿＿＿＿＿＿＿＿＿＿
↓
聴覚フィードバック：＿＿＿＿＿＿＿＿＿＿
↓
触覚フィードバック：＿＿＿＿＿＿＿＿＿＿
↓
プレイヤーの理解：＿＿＿＿＿＿＿＿＿＿

```

## 記入欄2

```text
入力：＿＿＿＿＿＿＿＿＿＿
↓
処理：＿＿＿＿＿＿＿＿＿＿
↓
出力：＿＿＿＿＿＿＿＿＿＿
↓
視覚フィードバック：＿＿＿＿＿＿＿＿＿＿
↓
聴覚フィードバック：＿＿＿＿＿＿＿＿＿＿
↓
触覚フィードバック：＿＿＿＿＿＿＿＿＿＿
↓
プレイヤーの理解：＿＿＿＿＿＿＿＿＿＿

```

---

# 手順13 プログラマとして必要な処理を考える

フィードバックは、プログラムで実装する必要があります。

## 例

<table id="bkmrk-%E3%82%84%E3%82%8A%E3%81%9F%E3%81%84%E3%81%93%E3%81%A8-%E5%BF%85%E8%A6%81%E3%81%AA%E5%87%A6%E7%90%86-%E3%82%B8%E3%83%A3%E3%83%B3%E3%83%97%E3%81%95%E3%81%9B%E3%81%9F"><thead><tr><th>やりたいこと</th><th>必要な処理</th></tr></thead><tbody><tr><td>ジャンプさせたい</td><td>入力判定、速度変更、重力、着地判定</td></tr><tr><td>攻撃を当てたい</td><td>攻撃判定、敵との当たり判定、ダメージ処理</td></tr><tr><td>攻撃の手応えを出したい</td><td>効果音、ヒットエフェクト、画面揺れ、ヒットストップ</td></tr><tr><td>HPを減らしたい</td><td>ダメージ計算、HP更新、UI更新</td></tr><tr><td>危険を知らせたい</td><td>攻撃前の予備動作、警告表示、警告音</td></tr><tr><td>アイテム取得を伝えたい</td><td>アイテム削除、スコア加算、取得音、エフェクト</td></tr><tr><td>振動を入れたい</td><td>対応機器確認、振動開始、振動時間管理、振動停止</td></tr></tbody></table>

## 記入欄

<table id="bkmrk-%E4%BD%9C%E3%82%8A%E3%81%9F%E3%81%84%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89%E3%83%90%E3%83%83%E3%82%AF-%E5%BF%85%E8%A6%81%E3%81%9D%E3%81%86%E3%81%AA%E3%83%97%E3%83%AD%E3%82%B0"><thead><tr><th>作りたいフィードバック</th><th>必要そうなプログラム処理</th></tr></thead><tbody><tr><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>＿＿＿＿＿＿＿＿</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 手順14 自分のゲームに使えそうなものを選ぶ

調べた中から、自分がゲーム制作で使ってみたいフィードバックを選びます。

## 記入欄

自分のゲームに使ってみたいフィードバックは、  
＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿  
である。

それを使いたい理由は、  
＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿  
である。

そのフィードバックを入れることで、プレイヤーに  
＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿  
を伝えたい。

実装するとしたら、必要そうな処理は、  
＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿  
である。

---

# 手順15 最終まとめを書く

ここでは、調べた内容をもとに、  
ゲームプログラマとして自分の制作にどう活かせるかをまとめます。

単に  
「音が鳴っていた」  
「エフェクトが出ていた」  
で終わらせないこと。

次の3つを意識して書きます。

1. 何をプレイヤーに伝えるためのフィードバックなのか
2. どの感覚に向けたフィードバックなのか
3. 自分が作るなら、どんな処理として実装するのか

---

## 15-1 入力とフィードバックの関係のまとめ

ゲームでは、プレイヤーが  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という入力をすると、ゲーム内で  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という処理が行われる。

その結果、画面・音・振動などを通して、プレイヤーに  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
というフィードバックが返る。

このフィードバックがあることで、プレイヤーは  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
を理解しやすくなる。

---

## 15-2 視覚フィードバックのまとめ

今回調べたゲームでは、視覚フィードバックとして  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
が使われていた。

これは、プレイヤーに  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
を伝えるための表現である。

たとえば、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という場面で、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
が表示されることで、プレイヤーは  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
と判断できる。

プログラムで実装する場合は、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という処理が必要になると考えられる。

---

## 15-3 聴覚フィードバックのまとめ

今回調べたゲームでは、聴覚フィードバックとして  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
が使われていた。

これは、プレイヤーに  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
を伝えるための音である。

音のフィードバックは、画面を細かく見ていなくても、  
成功・失敗・危険・報酬などに気づきやすくする役割がある。

たとえば、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という場面で、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という音が鳴ることで、プレイヤーは  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
と理解できる。

プログラムで実装する場合は、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という処理が必要になると考えられる。

---

## 15-4 触覚フィードバックのまとめ

今回調べたゲームでは、触覚フィードバックとして  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
が使われていた。

これは、プレイヤーに  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
を手や体の感覚で伝えるための表現である。

触覚フィードバックは、特に  
「衝撃」  
「ダメージ」  
「攻撃の手応え」  
「危険の接近」  
を伝えるときに効果的である。

たとえば、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という場面で、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という振動があることで、プレイヤーは  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
と感じる。

プログラムで実装する場合は、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という処理が必要になると考えられる。

---

## 15-5 複数の感覚を組み合わせたフィードバックのまとめ

ゲームでは、1つの出来事に対して、  
視覚・聴覚・触覚のフィードバックを組み合わせることが多い。

今回調べた中で、特に分かりやすかった組み合わせは、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という場面である。

その場面では、次のフィードバックが使われていた。

<table id="bkmrk-%E6%84%9F%E8%A6%9A-%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89%E3%83%90%E3%83%83%E3%82%AF-%E8%A6%96%E8%A6%9A-%EF%BC%BF%EF%BC%BF%EF%BC%BF%EF%BC%BF%EF%BC%BF%EF%BC%BF"><thead><tr><th>感覚</th><th>フィードバック</th></tr></thead><tbody><tr><td>視覚</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>聴覚</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>触覚</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr></tbody></table>

この組み合わせによって、プレイヤーは  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
を強く理解できる。

プログラマとして実装する場合は、  
1つのイベントが発生したときに、複数の処理を同時に行う必要がある。

## 例

```text
攻撃が当たった
↓
敵にダメージを与える
↓
ヒットエフェクトを出す
↓
ヒット音を鳴らす
↓
敵を点滅させる
↓
短い振動を入れる
↓
スコアやHPを更新する

```

このように、フィードバックは単独ではなく、  
「ゲーム内の出来事に対して、必要な反応をまとめて返す処理」  
として考える必要がある。

---

## 15-6 フィードバックが弱い場合の問題点

フィードバックが弱いと、プレイヤーは  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
と感じやすくなる。

たとえば、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という場面でフィードバックが弱いと、プレイヤーは  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
を理解しにくくなる。

その結果、ゲームは  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
と感じられる可能性がある。

## 例

<table id="bkmrk-%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89%E3%83%90%E3%83%83%E3%82%AF%E3%81%8C%E5%BC%B1%E3%81%84%E5%A0%B4%E9%9D%A2-%E3%83%97%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC%E3%81%8C%E6%84%9F"><thead><tr><th>フィードバックが弱い場面</th><th>プレイヤーが感じる問題</th></tr></thead><tbody><tr><td>攻撃が当たっても音やエフェクトがない</td><td>当たったか分からない</td></tr><tr><td>ダメージを受けても画面変化がない</td><td>いつ失敗したか分からない</td></tr><tr><td>危険な攻撃の予告がない</td><td>理不尽にやられたと感じる</td></tr><tr><td>ボタンを押しても反応が薄い</td><td>操作している感じがしない</td></tr><tr><td>UIの変化が小さい</td><td>状況を判断しにくい</td></tr></tbody></table>

---

## 15-7 プログラマとして意識したいこと

今回の研究を通して、ゲームプログラマとして特に意識したいことは、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
である。

なぜなら、プレイヤーはゲーム内の処理を直接見ることはできず、  
画面・音・振動・UIなどのフィードバックによって、  
ゲーム内で何が起きたかを理解しているからである。

そのため、自分がゲームを作るときは、  
次のような流れで考えたい。

```text
プレイヤーが何を入力したか
↓
ゲーム内で何を処理するか
↓
プレイヤーに何を伝える必要があるか
↓
視覚・聴覚・触覚のどれで伝えるか
↓
どのタイミングで反応を返すか
↓
プログラムとしてどう実装するか

```

---

## 15-8 自分のゲーム制作に活かすこと

自分がゲームを作るとき、特に使ってみたいフィードバックは、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
である。

それを使いたい理由は、  
＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿  
である。

そのフィードバックを入れることで、プレイヤーに  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
を伝えたい。

実装するとしたら、必要な処理は次のようになる。

<table id="bkmrk-%E5%BF%85%E8%A6%81%E3%81%AA%E5%87%A6%E7%90%86-%E5%86%85%E5%AE%B9-%E5%85%A5%E5%8A%9B%E5%88%A4%E5%AE%9A-%EF%BC%BF%EF%BC%BF%EF%BC%BF%EF%BC%BF%EF%BC%BF%EF%BC%BF"><thead><tr><th>必要な処理</th><th>内容</th></tr></thead><tbody><tr><td>入力判定</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>ゲーム内処理</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>視覚フィードバック</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>聴覚フィードバック</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>触覚フィードバック</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>UI更新</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr><tr><td>タイミング制御</td><td>＿＿＿＿＿＿＿＿＿＿＿＿＿＿</td></tr></tbody></table>

---

# 実装チェックリスト

自分でゲームを作るときは、次の項目を確認します。

<table id="bkmrk-%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E9%A0%85%E7%9B%AE-%E3%81%AF%E3%81%84-%2F-%E3%81%84%E3%81%84%E3%81%88-%E5%85%A5%E5%8A%9B%E3%81%97%E3%81%9F"><thead><tr><th>チェック項目</th><th>はい / いいえ</th></tr></thead><tbody><tr><td>入力したことがすぐ分かる反応がある</td><td>＿＿</td></tr><tr><td>成功したことが分かるフィードバックがある</td><td>＿＿</td></tr><tr><td>失敗したことが分かるフィードバックがある</td><td>＿＿</td></tr><tr><td>危険が事前に分かるフィードバックがある</td><td>＿＿</td></tr><tr><td>報酬を得たことが気持ちよく伝わる</td><td>＿＿</td></tr><tr><td>HPやスコアなどの状態変化が見やすい</td><td>＿＿</td></tr><tr><td>音が情報として役立っている</td><td>＿＿</td></tr><tr><td>必要に応じて振動などの触覚表現を使っている</td><td>＿＿</td></tr><tr><td>フィードバックが多すぎて見にくくなっていない</td><td>＿＿</td></tr><tr><td>プレイヤーに何を伝えるための表現か説明できる</td><td>＿＿</td></tr></tbody></table>

---

# 研究全体のまとめ

今回、複数のゲームを調べたことで、  
ゲームのフィードバックには  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という共通点があると分かった。

特に、よく使われていたフィードバックは、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
である。

それは、プレイヤーに  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
を伝えるために使われていた。

プログラマとしてゲームを作るときは、  
入力を受け取って処理するだけでなく、  
その結果をプレイヤーに分かりやすく返す必要がある。

そのために、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という場面では、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という視覚フィードバック、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という聴覚フィードバック、  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
という触覚フィードバックを組み合わせたい。

これにより、プレイヤーは  
「＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿」  
を理解しやすくなり、  
ゲームの操作感や手応えが良くなると考える。

---

# 100〜150字まとめ

## 記入欄

今回の研究では、ゲームの入力に対して、  
＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿  
というフィードバックが返ることで、プレイヤーが  
＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿  
を理解しやすくなると分かった。

自分でゲームを作るときは、  
＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿  
を意識して実装したい。

---

# 提出するもの

次の内容を記入して提出すること。

1. 調べたゲーム一覧
2. 入力の種類の表
3. 入力から出力までの流れ
4. 視覚フィードバックの表
5. 聴覚フィードバックの表
6. 触覚フィードバックの表
7. 複数の感覚を組み合わせたフィードバックの表
8. UIによるフィードバックの表
9. フィードバックが弱いとどうなるか
10. 入力とフィードバックの流れ図
11. プログラマとして必要な処理
12. 自分のゲームに使いたいフィードバック
13. 実装チェックリスト
14. 研究全体のまとめ
15. 100〜150字まとめ

---

# 書くときの注意

## 1. いろいろなジャンルを見る

同じジャンルだけでなく、できれば違うジャンルのゲームを見てください。

ジャンルが違うと、入力やフィードバックの使い方も違います。

---

## 2. 「入力」と「反応」をセットで見る

入力だけを書いても、ゲームの研究にはなりません。

## 悪い例

- Aボタンでジャンプする

## 良い例

- Aボタンを押すとキャラクターがジャンプし、ジャンプ音が鳴り、ジャンプアニメーションに変わるため、プレイヤーはジャンプできたと分かる

---

## 3. 「何を伝えているのか」を考える

フィードバックを見るときは、ただ  
「音が鳴る」  
「光る」  
で終わらせないでください。

そのフィードバックが何を伝えているのかを考えます。

## 例

- アイテム取得音は、報酬を得たことを伝えている
- 敵の予備動作は、危険が近いことを伝えている
- HPバーの減少は、現在の危険度を伝えている
- 画面揺れは、攻撃の強さや衝撃を伝えている

---

## 4. プログラマ視点で考える

フィードバックは、プログラムで作るものです。

たとえば、攻撃が当たったときには、次のような処理が必要です。

```text
攻撃ボタン入力
↓
攻撃アニメーション開始
↓
攻撃判定を出す
↓
敵との当たり判定を調べる
↓
当たったらダメージを与える
↓
ヒット音を鳴らす
↓
ヒットエフェクトを出す
↓
敵を点滅させる
↓
必要なら振動させる
↓
スコアやHPを更新する

```

このように、  
「プレイヤーにどう見せるか」  
だけでなく、  
「内部でどんな処理が必要か」  
も考えましょう。

---

# 簡単な完成例

## 調べたゲーム

- スーパーマリオブラザーズ
- ストリートファイター
- マリオカート

---

## 入力と出力の例

<table id="bkmrk-%E3%82%B2%E3%83%BC%E3%83%A0%E5%90%8D-%E5%85%A5%E5%8A%9B-%E3%82%B2%E3%83%BC%E3%83%A0%E5%86%85%E3%81%AE%E5%87%A6%E7%90%86%E3%83%BB%E7%B5%90%E6%9E%9C-%E3%83%97-1"><thead><tr><th>ゲーム名</th><th>入力</th><th>ゲーム内の処理・結果</th><th>プレイヤーに返る反応</th></tr></thead><tbody><tr><td>スーパーマリオブラザーズ</td><td>ジャンプボタン</td><td>マリオがジャンプする</td><td>ジャンプ音、ジャンプアニメーション</td></tr><tr><td>ストリートファイター</td><td>攻撃ボタン</td><td>キャラクターが攻撃する</td><td>攻撃音、ヒットエフェクト</td></tr><tr><td>マリオカート</td><td>アイテムボタン</td><td>アイテムを使う</td><td>効果音、アイテム演出</td></tr></tbody></table>

---

## 感覚ごとのフィードバック例

<table id="bkmrk-%E3%82%B2%E3%83%BC%E3%83%A0%E5%90%8D-%E5%A0%B4%E9%9D%A2-%E8%A6%96%E8%A6%9A-%E8%81%B4%E8%A6%9A-%E8%A7%A6%E8%A6%9A-%E3%82%B9%E3%83%BC%E3%83%91"><thead><tr><th>ゲーム名</th><th>場面</th><th>視覚</th><th>聴覚</th><th>触覚</th></tr></thead><tbody><tr><td>スーパーマリオブラザーズ</td><td>コイン取得</td><td>コインが消える、スコアが増える</td><td>コイン取得音</td><td>なし</td></tr><tr><td>ストリートファイター</td><td>攻撃ヒット</td><td>ヒットエフェクト、のけぞり</td><td>ヒット音</td><td>振動対応版では振動</td></tr><tr><td>マリオカート</td><td>ダート走行</td><td>車体の動きが変わる</td><td>走行音が変わる</td><td>振動</td></tr></tbody></table>

---

## プログラマ視点のまとめ例

プレイヤーが入力した結果を分かりやすく伝えるためには、  
キャラクターの動きだけでなく、音・エフェクト・UI・振動を組み合わせる必要がある。

たとえば、攻撃が当たった場面では、  
ダメージ処理だけでなく、ヒット音、ヒットエフェクト、敵のリアクション、必要に応じた振動を同時に発生させることで、プレイヤーに手応えを伝えられる。

自分でゲームを作るときは、入力処理とゲーム内処理だけで終わらせず、  
その結果をプレイヤーにどう返すかまで考えて実装したい。

# 状態遷移ふたたび

# ゲームAI課題

# センサー・視界・状態遷移表・状態遷移図を書いてみよう

---

# 1. 今日の課題

今回の課題では、モンスターAIの行動を

```text
センサー
視界
状態遷移表
状態遷移図

```

として整理します。

プログラムは書きません。

しかし、最後に C++ の `switch case` や State パターンへ変換できるように、条件や状態をきちんと整理します。

---

# 2. 今日のゴール

この課題のゴールは、次の4つです。

```text
1. モンスターが何を調べているのかを考える
2. センサーの結果をフラグとして整理する
3. 状態遷移表を書ける
4. 状態遷移図を書ける

```

---

# 3. AIは何を見ているのか

ゲームAIは、人間のように画面を見ているわけではありません。

AIは、ゲーム内の情報を数値や条件として調べています。

例えば、

```text
プレイヤーが見えているか
プレイヤーが攻撃範囲にいるか
探索時間が終わったか

```

などです。

このような情報を調べる仕組みを、ここでは **センサー** と呼びます。

---

# 4. センサーとは何か

センサーとは、

```text
AIが状況を判断するために使う情報を調べる処理

```

です。

例えば、モンスターAIでは次のようなセンサーが考えられます。

<table id="bkmrk-%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC-%E8%AA%BF%E3%81%B9%E3%82%8B%E3%81%93%E3%81%A8-%E8%A6%96%E7%95%8C%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC-%E3%83%97%E3%83%AC"><thead><tr><th>センサー</th><th>調べること</th></tr></thead><tbody><tr><td>視界センサー</td><td>プレイヤーが見えているか</td></tr><tr><td>距離センサー</td><td>プレイヤーが近いか</td></tr><tr><td>攻撃範囲センサー</td><td>攻撃できる距離にいるか</td></tr><tr><td>探索時間センサー</td><td>探索時間が終わったか</td></tr><tr><td>壁センサー</td><td>プレイヤーとの間に壁があるか</td></tr></tbody></table>

---

# 5. 視界センサーの考え方

視界センサーは、

```text
プレイヤーが見えているか？

```

を調べるセンサーです。

ただし、ゲームAIは本当に映像を見ているわけではありません。

多くの場合、次のような条件で「見えている」と判断します。

```text
1. プレイヤーが一定距離以内にいる
2. プレイヤーがモンスターの前方にいる
3. プレイヤーとの間に壁がない

```

---

# 6. 視界センサーの例

```text
距離が近い
かつ
前方にいる
かつ
壁で隠れていない
↓
プレイヤーを見つけた

```

C++で使うなら、次のような関数にできます。

```cpp
bool CheckCanSeePlayer();

```

この関数が `true` なら、

```cpp
isFindPlayer = true;

```

と考えます。

---

# 7. センサーからフラグを作る

AIの判断では、センサーの結果をフラグとして使うと分かりやすくなります。

```cpp
bool isFindPlayer;      // プレイヤーを見つけた
bool isAttackRange;     // 攻撃範囲に入った
bool isLostPlayer;      // プレイヤーを見失った
bool isSearchTimeOver;  // 探索時間が終わった

```

つまり、

```text
センサーで調べる
↓
フラグにする
↓
状態遷移の条件に使う

```

という流れです。

---

# 8. 状態とは何か

状態とは、

```text
今、そのキャラクターが何をしているか

```

を表すものです。

例えば、モンスターには次のような状態があります。

<table id="bkmrk-%E7%8A%B6%E6%85%8B-%E6%84%8F%E5%91%B3-patrol-%E5%B7%A1%E5%9B%9E%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B-"><thead><tr><th>状態</th><th>意味</th></tr></thead><tbody><tr><td>Patrol</td><td>巡回している</td></tr><tr><td>Chase</td><td>プレイヤーを追いかけている</td></tr><tr><td>Attack</td><td>攻撃している</td></tr><tr><td>Search</td><td>プレイヤーを探している</td></tr></tbody></table>

---

# 9. 状態遷移とは何か

状態遷移とは、

```text
ある状態から、別の状態に切り替わること

```

です。

例えば、

```text
Patrol
↓ プレイヤーを見つけた
Chase

```

これは、

```text
巡回中に視界センサーがプレイヤーを発見したので、
追跡状態に変わった

```

という意味です。

---

# 10. 今回作るモンスターAI

今回のモンスターは、次のように動きます。

```text
普段は決められた道を巡回している。
視界センサーでプレイヤーを見つけたら追いかける。
距離センサーで攻撃範囲に入ったと分かったら攻撃する。
攻撃中に視界センサーでプレイヤーを見失ったら、周囲を探す。
探索時間センサーで時間切れになったら、巡回に戻る。

```

---

# 11. 使用する状態

今回使う状態は、次の4つです。

<table id="bkmrk-%E7%8A%B6%E6%85%8B%E5%90%8D-%E5%86%85%E5%AE%B9-patrol-%E6%B1%BA%E3%82%81%E3%82%89%E3%82%8C%E3%81%9F%E9%81%93"><thead><tr><th>状態名</th><th>内容</th></tr></thead><tbody><tr><td>Patrol</td><td>決められた道を巡回する</td></tr><tr><td>Chase</td><td>プレイヤーを追いかける</td></tr><tr><td>Attack</td><td>プレイヤーを攻撃する</td></tr><tr><td>Search</td><td>プレイヤーを探す</td></tr></tbody></table>

---

# 12. 使用するセンサー

今回使うセンサーは、次の3つです。

<table id="bkmrk-%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC%E5%90%8D-%E5%86%85%E5%AE%B9-c%2B%2B%E5%90%91%E3%81%91%E3%81%AE%E9%96%A2%E6%95%B0%E5%90%8D-%E8%A6%96"><thead><tr><th>センサー名</th><th>内容</th><th>C++向けの関数名</th></tr></thead><tbody><tr><td>視界センサー</td><td>プレイヤーが見えているか</td><td>`CheckCanSeePlayer()`</td></tr><tr><td>攻撃範囲センサー</td><td>攻撃できる距離にいるか</td><td>`CheckAttackRange()`</td></tr><tr><td>探索時間センサー</td><td>探索時間が終わったか</td><td>`CheckSearchTimeOver()`</td></tr></tbody></table>

---

# 13. 使用するフラグ

今回使うフラグは、次の4つです。

<table id="bkmrk-%E3%83%95%E3%83%A9%E3%82%B0%E5%90%8D-%E6%84%8F%E5%91%B3-isfindplayer"><thead><tr><th>フラグ名</th><th>意味</th></tr></thead><tbody><tr><td>`isFindPlayer`</td><td>プレイヤーを見つけた</td></tr><tr><td>`isAttackRange`</td><td>攻撃範囲に入った</td></tr><tr><td>`isLostPlayer`</td><td>プレイヤーを見失った</td></tr><tr><td>`isSearchTimeOver`</td><td>探索時間が終わった</td></tr></tbody></table>

---

# 14. センサーとフラグの関係

センサーからフラグを作ると、次のようになります。

```cpp
isFindPlayer = CheckCanSeePlayer();
isAttackRange = CheckAttackRange();
isSearchTimeOver = CheckSearchTimeOver();
isLostPlayer = !isFindPlayer;

```

`!isFindPlayer` は、

```text
プレイヤーを見つけていない

```

という意味です。

つまり、プレイヤーが見えていなければ、見失ったと考えることができます。

---

# 15. 課題1：センサーの意味を説明する

次のセンサーについて、自分の言葉で説明しなさい。

<table id="bkmrk-%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC-%E8%87%AA%E5%88%86%E3%81%AE%E8%AA%AC%E6%98%8E-%E8%A6%96%E7%95%8C%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC-%E6%94%BB%E6%92%83"><thead><tr><th>センサー</th><th>自分の説明</th></tr></thead><tbody><tr><td>視界センサー</td><td></td></tr><tr><td>攻撃範囲センサー</td><td></td></tr><tr><td>探索時間センサー</td><td></td></tr></tbody></table>

---

# 16. 課題2：視界センサーの条件を考える

視界センサーで「プレイヤーが見えている」と判断するには、どのような条件が必要ですか。

下の表を完成させなさい。

<table id="bkmrk-%E6%9D%A1%E4%BB%B6-%E8%AA%AC%E6%98%8E-%E8%B7%9D%E9%9B%A2-%E5%90%91%E3%81%8D-%E5%A3%81"><thead><tr><th>条件</th><th>説明</th></tr></thead><tbody><tr><td>距離</td><td></td></tr><tr><td>向き</td><td></td></tr><tr><td>壁</td><td></td></tr></tbody></table>

---

# 17. 課題3：状態の意味を説明する

次の状態について、自分の言葉で説明しなさい。

<table id="bkmrk-%E7%8A%B6%E6%85%8B-%E8%87%AA%E5%88%86%E3%81%AE%E8%AA%AC%E6%98%8E-patrol-chas"><thead><tr><th>状態</th><th>自分の説明</th></tr></thead><tbody><tr><td>Patrol</td><td></td></tr><tr><td>Chase</td><td></td></tr><tr><td>Attack</td><td></td></tr><tr><td>Search</td><td></td></tr></tbody></table>

---

# 18. 課題4：状態遷移を文章で整理する

次の状態遷移を、センサーやフラグの言葉を使って説明しなさい。

---

## 例

```text
Patrol → Chase

```

説明：

```text
巡回中に視界センサーでプレイヤーを見つけたら、追跡状態に変わる。

```

---

## 問題

<table id="bkmrk-%E9%81%B7%E7%A7%BB-%E8%AA%AC%E6%98%8E-patrol-%E2%86%92-chase"><thead><tr><th>遷移</th><th>説明</th></tr></thead><tbody><tr><td>Patrol → Chase</td><td></td></tr><tr><td>Chase → Attack</td><td></td></tr><tr><td>Attack → Search</td><td></td></tr><tr><td>Search → Patrol</td><td></td></tr></tbody></table>

---

# 19. 課題5：状態遷移表を完成させる

次の表を完成させなさい。

<table id="bkmrk-%E7%8F%BE%E5%9C%A8%E3%81%AE%E7%8A%B6%E6%85%8B-%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC%E3%83%BB%E6%9D%A1%E4%BB%B6-%E3%83%95%E3%83%A9%E3%82%B0-%E6%AC%A1%E3%81%AE"><thead><tr><th>現在の状態</th><th>センサー・条件</th><th>フラグ</th><th>次の状態</th></tr></thead><tbody><tr><td>Patrol</td><td>視界センサーでプレイヤーを見つけた</td><td></td><td></td></tr><tr><td>Chase</td><td>攻撃範囲センサーで攻撃範囲に入った</td><td></td><td></td></tr><tr><td>Attack</td><td>視界センサーでプレイヤーを見失った</td><td></td><td></td></tr><tr><td>Search</td><td>探索時間センサーで時間切れになった</td><td></td><td></td></tr></tbody></table>

---

# 20. 課題6：C++向けの状態遷移表を書く

課題5を使って、C++で使いやすい形の状態遷移表を書きなさい。

<table id="bkmrk-%E7%8F%BE%E5%9C%A8%E3%81%AE%E7%8A%B6%E6%85%8B-%E6%9D%A1%E4%BB%B6-%E6%AC%A1%E3%81%AE%E7%8A%B6%E6%85%8B-enemys"><thead><tr><th>現在の状態</th><th>条件</th><th>次の状態</th></tr></thead><tbody><tr><td>EnemyState::Patrol</td><td></td><td>EnemyState::Chase</td></tr><tr><td>EnemyState::Chase</td><td></td><td>EnemyState::Attack</td></tr><tr><td>EnemyState::Attack</td><td></td><td>EnemyState::Search</td></tr><tr><td>EnemyState::Search</td><td></td><td>EnemyState::Patrol</td></tr></tbody></table>

---

# 21. 課題7：状態遷移図を描く

次の4つの状態を使って、状態遷移図を描きなさい。

```text
Patrol
Chase
Attack
Search

```

矢印には、状態が変わる条件を書きなさい。

---

## 図の例

```text
Patrol -- isFindPlayer --> Chase

```

---

## 作図スペース

```text












```

---

# 22. 課題8：C++のswitch caseに変換する準備

次のひな形の空欄を埋めなさい。

```cpp
switch (state)
{
case EnemyState::Patrol:
    if (__________)
    {
        state = EnemyState::__________;
    }
    break;

case EnemyState::Chase:
    if (__________)
    {
        state = EnemyState::__________;
    }
    break;

case EnemyState::Attack:
    if (__________)
    {
        state = EnemyState::__________;
    }
    break;

case EnemyState::Search:
    if (__________)
    {
        state = EnemyState::__________;
    }
    break;
}

```

---

# 23. 課題9：AIとして自然に見えるか考える

今回のAIは、次の流れでした。

```text
Patrol
↓ 視界センサーでプレイヤーを見つけた
Chase
↓ 攻撃範囲センサーで攻撃範囲に入った
Attack
↓ 視界センサーでプレイヤーを見失った
Search
↓ 探索時間センサーで時間切れ
Patrol

```

このAIには、まだ不自然なところがあります。

例えば、

```text
Chase中にプレイヤーを見失ったらどうなるのか？
Attack中に攻撃範囲から出たらどうなるのか？
Search中にプレイヤーを再発見したらどうなるのか？

```

などです。

このAIに追加した方がよい状態遷移を2つ考えなさい。

<table id="bkmrk-%E8%BF%BD%E5%8A%A0%E3%81%97%E3%81%9F%E3%81%84%E9%81%B7%E7%A7%BB-%E4%BD%BF%E3%81%86%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC%E3%83%BB%E6%9D%A1%E4%BB%B6-%E7%90%86%E7%94%B1"><thead><tr><th>追加したい遷移</th><th>使うセンサー・条件</th><th>理由</th></tr></thead><tbody><tr><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td></tr></tbody></table>

---

# 24. 45分の進め方

<table id="bkmrk-%E6%99%82%E9%96%93-%E5%86%85%E5%AE%B9-0%E3%80%9C5%E5%88%86-%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC%E3%83%BB%E8%A6%96%E7%95%8C%E3%83%BB%E7%8A%B6"><thead><tr><th>時間</th><th>内容</th></tr></thead><tbody><tr><td>0〜5分</td><td>センサー・視界・状態遷移の説明を読む</td></tr><tr><td>5〜10分</td><td>今回のモンスターAIの仕様を確認する</td></tr><tr><td>10〜17分</td><td>課題1・2：センサーと視界条件を整理する</td></tr><tr><td>17〜24分</td><td>課題3・4：状態と遷移を文章で説明する</td></tr><tr><td>24〜32分</td><td>課題5・6：状態遷移表を作る</td></tr><tr><td>32〜40分</td><td>課題7：状態遷移図を描く</td></tr><tr><td>40〜43分</td><td>課題8：switch caseの形にする</td></tr><tr><td>43〜45分</td><td>課題9：改善案を考える</td></tr></tbody></table>

---

# 25. 提出物

次の5つを提出しなさい。

```text
1. センサーの説明
2. 視界センサーの条件
3. 状態遷移表
4. 状態遷移図
5. switch case のひな形

```

---

# 26. まとめ

今回の課題では、プログラムを書く前にAIの行動を整理しました。

重要なのは、次の流れです。

```text
センサーで状況を調べる
↓
フラグにする
↓
状態を決める
↓
状態が変わる条件を決める
↓
状態遷移表を書く
↓
状態遷移図を書く
↓
C++のswitch caseに変換する

```

AIの行動は、いきなりコードを書くよりも、先に表や図で整理すると分かりやすくなります。

# ステートパターン（オブジェクト指向的）

## C++ Stateパターン入門 センサー・状態遷移表・状態遷移図をクラス設計に変換しよう

1. 今日のテーマ 前回は、モンスターAIを次の流れで整理しました。

```text
センサーで状況を調べる
↓
フラグにする
↓
状態を決める
↓
状態遷移表を書く
↓
状態遷移図を書く

```

今回は、その状態遷移表と状態遷移図を、C++の設計に変換します。 今回のテーマは、

```text
Stateパターン

```

## です。

2. 今日のゴール 今日のゴールは、次の3つです。

```text
1. Stateパターンが何のためにあるのか理解する
2. switch case版との違いを理解する
3. 状態遷移表をStateクラスに変換する考え方を理解する

```

今回は座学なので、実際にプログラムを完成させることよりも、

```text
なぜクラスに分けるのか
どのクラスが何を担当するのか
どこで状態を切り替えるのか

```

## を理解することを目標にします。

3. 前回のAIを思い出す 今回扱うモンスターAIは、次の流れです。

```text
Patrol
  ↓ 視界センサーでプレイヤーを見つけた
Chase
  ↓ 攻撃範囲センサーで攻撃範囲に入った
Attack
  ↓ 視界センサーでプレイヤーを見失った
Search
  ↓ 探索時間センサーで時間切れ
Patrol

```

---

4. センサーとフラグ AIは、人間のように画面を見ているわけではありません。 AIは、センサーを使って状況を調べます。 センサー 調べること C++の例 視界センサー プレイヤーが見えているか `CheckCanSeePlayer()`攻撃範囲センサー 攻撃できる距離か `CheckAttackRange()`探索時間センサー 探索時間が終わったか `CheckSearchTimeOver()`これらの結果を、フラグとして使えます。

```cpp
bool isFindPlayer;
bool isAttackRange;
bool isSearchTimeOver;

```

---

5. 状態遷移表 前回作った状態遷移表は、次のようになります。 現在の状態 条件 次の状態 Patrol `isFindPlayer == true` Chase Chase `isAttackRange == true` Attack Attack `isFindPlayer == false` Search Search `isSearchTimeOver == true` Patrol この表が、今回のC++設計のもとになります。

---

6. まずはswitch case版で考える Stateパターンの前に、まずは `switch case` 版を確認します。

```cpp
enum class EnemyState
{
    Patrol,
    Chase,
    Attack,
    Search
};

```

```cpp
void Enemy::Update()
{
    switch (state_)
    {
    case EnemyState::Patrol:
        Patrol();

        if (CheckCanSeePlayer())
        {
            state_ = EnemyState::Chase;
        }
        break;

    case EnemyState::Chase:
        Chase();

        if (CheckAttackRange())
        {
            state_ = EnemyState::Attack;
        }
        break;

    case EnemyState::Attack:
        Attack();

        if (!CheckCanSeePlayer())
        {
            state_ = EnemyState::Search;
        }
        break;

    case EnemyState::Search:
        Search();

        if (CheckSearchTimeOver())
        {
            state_ = EnemyState::Patrol;
        }
        break;
    }
}

```

---

7. switch case版の考え方 `switch case` 版では、Enemyクラスの中にすべての状態処理を書きます。

```text
Enemy::Update()
├ Patrolの処理
├ Chaseの処理
├ Attackの処理
└ Searchの処理

```

## これは最初は分かりやすいです。 しかし、状態が増えてくると、Enemyクラスがどんどん大きくなります。

8. switch case版の困るところ 状態が増えると、次のような問題が出ます。

```text
Enemy::Update() が長くなる
状態ごとの処理が混ざる
修正する場所を探しにくい
新しい状態を追加しづらい

```

例えば、次のような状態を追加したくなったらどうでしょうか。

```text
Damage
Escape
Return
Dead
Stun
Guard

```

## `switch case` の中身がどんどん長くなります。

9. そこでStateパターン Stateパターンは、

```text
状態ごとの処理を、状態ごとのクラスに分ける方法

```

## です。 つまり、`switch case` で分けていた部分を、クラスに分けます。

10. switch case版とStateパターン版の違い switch case版

```text
Enemy
└ Update()
   ├ Patrolの処理
   ├ Chaseの処理
   ├ Attackの処理
   └ Searchの処理

```

Stateパターン版

```text
Enemy
└ 現在のStateを持つ

PatrolState
ChaseState
AttackState
SearchState

```

Stateパターンでは、

```text
状態1つにつき、クラス1つ

```

## にします。

11. Stateパターンの全体像

```text
Enemy
  |
  | 現在の状態を持つ
  v

EnemyStateBase
  ↑
  ├ PatrolState
  ├ ChaseState
  ├ AttackState
  └ SearchState

```

`Enemy` は、現在の状態クラスを1つだけ持ちます。 そして、毎フレームこうします。

```text
今のStateのUpdateを呼ぶ

```

---

12. 親クラス EnemyStateBase まず、すべての状態クラスの親を作ります。

```cpp
class Enemy;

class EnemyStateBase
{
public:
    virtual ~EnemyStateBase() = default;

    virtual void Update(Enemy& enemy) = 0;
};

```

---

13. 親クラスの意味

```cpp
class Enemy;

```

これは前方宣言です。

```text
Enemyというクラスを後で使います

```

## という意味です。

```cpp
virtual ~EnemyStateBase() = default;

```

## これはデストラクタです。 親クラスのポインタで子クラスを消すので、`virtual` を付けます。

```cpp
virtual void Update(Enemy& enemy) = 0;

```

これは、

```text
すべての状態クラスはUpdateを持ちなさい

```

## という意味です。 `= 0` が付いているので、このクラスはそのまま使うのではなく、継承して使います。

14. Enemyクラス Enemyクラスは、現在の状態を持ちます。

```cpp
class Enemy
{
private:
    EnemyStateBase* state_ = nullptr;
    EnemyStateBase* nextState_ = nullptr;

public:
    Enemy();
    ~Enemy();

    void Update();

    void ChangeState(EnemyStateBase* nextState);
    void ApplyStateChange();

    bool CheckCanSeePlayer();
    bool CheckAttackRange();
    bool CheckSearchTimeOver();

    void Patrol();
    void Chase();
    void Attack();
    void Search();
};

```

---

15. state\_とは何か

```cpp
EnemyStateBase* state_;

```

これは、現在の状態です。 例えば、

```text
今は PatrolState
今は ChaseState
今は AttackState

```

## のように、現在の状態を指します。

16. nextState\_とは何か

```cpp
EnemyStateBase* nextState_;

```

これは、次に切り替える予定の状態です。 なぜ `state_` だけではなく、`nextState_` も持つのでしょうか。 理由は、

```text
StateのUpdate中に、今実行中のStateをすぐ消すと危険だから

```

## です。

17. 危険なChangeState 次のような書き方は危険です。

```cpp
void Enemy::ChangeState(EnemyStateBase* nextState)
{
    delete state_;
    state_ = nextState;
}

```

StateのUpdate中にこれを呼ぶと、今実行中のStateを消してしまうことがあります。

```text
PatrolState::Update中
↓
ChangeState(new ChaseState())
↓
delete state_
↓
実行中のPatrolStateが消える

```

## これは分かりにくいバグの原因になります。

18. 安全な考え方 安全な考え方は、次の通りです。

```text
StateのUpdate中
↓
次の状態を予約するだけ
↓
Updateが終わる
↓
Enemyが古いStateを消して入れ替える

```

## そのために、`nextState_` を使います。

19. ChangeState

```cpp
void Enemy::ChangeState(EnemyStateBase* nextState)
{
    delete nextState_;
    nextState_ = nextState;
}

```

## ここでは、まだ現在の `state_` を消しません。 次の状態を `nextState_` に予約するだけです。

20. ApplyStateChange

```cpp
void Enemy::ApplyStateChange()
{
    if (nextState_ == nullptr)
    {
        return;
    }

    delete state_;
    state_ = nextState_;
    nextState_ = nullptr;
}

```

## ここで初めて、古い状態を消して、新しい状態に入れ替えます。

21. Enemy::Update

```cpp
void Enemy::Update()
{
    if (state_ != nullptr)
    {
        state_->Update(*this);
    }

    ApplyStateChange();
}

```

流れはこうです。

```text
現在のStateのUpdateを実行する
↓
Updateが終わる
↓
必要なら状態を切り替える

```

---

22. Enemyの生成と破棄

```cpp
Enemy::Enemy()
{
    state_ = new PatrolState();
}

Enemy::~Enemy()
{
    delete state_;
    delete nextState_;
}

```

## 最初は `PatrolState` から始めます。 Enemyが消えるときに、Stateも消します。

23. 所有者のルール `new/delete` を使うときは、所有者を決めることが大切です。 今回のルールはこれです。

```text
Stateを所有するのはEnemy
StateをdeleteするのもEnemy
Stateクラス自身はdeleteしない

```

Stateクラスは、次のStateを `new` して Enemy に渡すだけです。

---

24. PatrolState

```cpp
class PatrolState : public EnemyStateBase
{
public:
    void Update(Enemy& enemy) override
    {
        enemy.Patrol();

        if (enemy.CheckCanSeePlayer())
        {
            enemy.ChangeState(new ChaseState());
        }
    }
};

```

これは、状態遷移表のこの行に対応しています。 現在の状態 条件 次の状態 Patrol `isFindPlayer == true` Chase 意味：

```text
巡回する
↓
プレイヤーを見つけたら
↓
ChaseStateに切り替える

```

---

25. ChaseState

```cpp
class ChaseState : public EnemyStateBase
{
public:
    void Update(Enemy& enemy) override
    {
        enemy.Chase();

        if (enemy.CheckAttackRange())
        {
            enemy.ChangeState(new AttackState());
        }
    }
};

```

これは、状態遷移表のこの行に対応しています。 現在の状態 条件 次の状態 Chase `isAttackRange == true` Attack 意味：

```text
追いかける
↓
攻撃範囲に入ったら
↓
AttackStateに切り替える

```

---

26. AttackState

```cpp
class AttackState : public EnemyStateBase
{
public:
    void Update(Enemy& enemy) override
    {
        enemy.Attack();

        if (!enemy.CheckCanSeePlayer())
        {
            enemy.ChangeState(new SearchState());
        }
    }
};

```

これは、状態遷移表のこの行に対応しています。 現在の状態 条件 次の状態 Attack `isFindPlayer == false` Search 意味：

```text
攻撃する
↓
プレイヤーを見失ったら
↓
SearchStateに切り替える

```

---

27. SearchState

```cpp
class SearchState : public EnemyStateBase
{
public:
    void Update(Enemy& enemy) override
    {
        enemy.Search();

        if (enemy.CheckSearchTimeOver())
        {
            enemy.ChangeState(new PatrolState());
        }
    }
};

```

これは、状態遷移表のこの行に対応しています。 現在の状態 条件 次の状態 Search `isSearchTimeOver == true` Patrol 意味：

```text
探す
↓
探索時間が終わったら
↓
PatrolStateに戻る

```

---

28. 状態遷移表からStateクラスへの変換 状態遷移表の1行は、Stateクラスの中の `if文` になります。 例： 現在の状態 条件 次の状態 Patrol `CheckCanSeePlayer()` Chase これは、次のように変換できます。

```cpp
class PatrolState : public EnemyStateBase
{
public:
    void Update(Enemy& enemy) override
    {
        enemy.Patrol();

        if (enemy.CheckCanSeePlayer())
        {
            enemy.ChangeState(new ChaseState());
        }
    }
};

```

---

29. switch case版との対応 switch case版 Stateパターン版 `case EnemyState::Patrol:` `PatrolState``case EnemyState::Chase:` `ChaseState``case EnemyState::Attack:` `AttackState``case EnemyState::Search:` `SearchState``state_ = EnemyState::Chase;` `ChangeState(new ChaseState());``switch (state_)` `state_->Update(*this);`

---

30. Stateパターンの良いところ 良いところ1：状態ごとに処理を分けられる

```text
PatrolState
ChaseState
AttackState
SearchState

```

## のように分かれるので、どこに何が書いてあるか分かりやすくなります。

良いところ2：Enemy::Updateが短くなる

```cpp
void Enemy::Update()
{
    if (state_ != nullptr)
    {
        state_->Update(*this);
    }

    ApplyStateChange();
}

```

## Enemyは、現在のStateに処理を任せるだけになります。

良いところ3：状態を追加しやすい 例えば、逃げる状態を追加するなら、

```cpp
class EscapeState : public EnemyStateBase
{
public:
    void Update(Enemy& enemy) override
    {
        enemy.Escape();
    }
};

```

## のように新しいクラスを追加できます。

31. Stateパターンの注意点 Stateパターンは便利ですが、注意点もあります。

```text
クラスが増える
最初はswitch caseより難しく見える
new/deleteの管理が必要

```

そのため、最初は必ず

```text
switch case版
↓
Stateパターン版

```

## の順番で考えると分かりやすいです。

32. 座学で一番大事な理解 Stateパターンを難しく考えすぎる必要はありません。 まずは、こう理解してください。

```text
switch caseで分けていた状態ごとの処理を、
クラスごとに分けたもの

```

## これがStateパターンです。

33. 確認問題 問1 Stateパターンは、何をクラスに分ける方法ですか。

---

## 問2 `EnemyStateBase` は何のためにありますか。

## 問3 `state_` は何を表していますか。

## 問4 `nextState_` は何のためにありますか。

## 問5 次の状態遷移表の1行を、Stateクラスの中の処理として説明しなさい。 現在の状態 条件 次の状態 Chase `CheckAttackRange()` Attack

## 問6 今回の設計で、Stateを `delete` する責任を持つのはどのクラスですか。

34. まとめ 今回のポイントは次の通りです。

```text
AIはセンサーで状況を調べる

```

```text
センサーの結果を使って状態を切り替える

```

```text
状態遷移表は、C++の状態管理に変換できる

```

```text
switch case版は状態遷移の基本を理解しやすい

```

```text
Stateパターンは、状態ごとの処理をクラスに分ける方法

```

```text
Stateをnew/deleteで管理する場合、所有者を決めることが重要

```

```text
今回の設計では、Stateを所有するのはEnemy

```

---

35. 発展：本来はunique\_ptrも使える 今回の資料では、`new/delete` の管理を学ぶために、生ポインタを使いました。 実際のC++では、`std::unique_ptr` を使うとより安全です。

```cpp
std::unique_ptr<EnemyStateBase> state_;
std::unique_ptr<EnemyStateBase> nextState_;

```

ただし、スマートポインタを使う場合でも、

```text
誰が所有するのか
いつ入れ替えるのか
どのクラスが責任を持つのか

```

を理解することは重要です。

# Stateパターンワーク

# C++ Stateパターン入門

## 信号機のStateクラスで考える

---

# 1. 今日の目的

この資料では、Stateパターンを信号機の例で学びます。

難しいゲームAIではなく、次の3つの状態だけを扱います。

```text
赤
青
黄

```

信号機は、時間が来ると状態が変わります。

```text
赤 → 青 → 黄 → 赤

```

このような「状態ごとに処理が違うもの」を、Stateパターンで書いてみます。

---

# 2. Stateパターンを一言でいうと

Stateパターンは、

```text
状態ごとの処理を、状態ごとのクラスに分ける方法

```

です。

信号機なら、次のように分けます。

```text
RedState
BlueState
YellowState

```

状態1つにつき、クラス1つです。

---

# 3. switch文で書くとどうなるか

まず、Stateパターンを使わずに書くと、次のようになります。

```cpp
enum class SignalState
{
    Red,
    Blue,
    Yellow
};

```

```cpp
void Signal::Update()
{
    switch (state_)
    {
    case SignalState::Red:
        Red();

        if (IsTimeOver())
        {
            state_ = SignalState::Blue;
        }
        break;

    case SignalState::Blue:
        Blue();

        if (IsTimeOver())
        {
            state_ = SignalState::Yellow;
        }
        break;

    case SignalState::Yellow:
        Yellow();

        if (IsTimeOver())
        {
            state_ = SignalState::Red;
        }
        break;
    }
}

```

この書き方でも動きます。

ただし、状態が増えると `switch` の中が長くなります。

Stateパターンでは、この `case` ごとの処理をクラスに分けます。

---

# 4. Stateパターン版の全体像

Stateパターン版では、次のような形にします。

```text
Signal
  |
  | 現在のStateを持つ
  v

SignalStateBase
  ↑
  ├ RedState
  ├ BlueState
  └ YellowState

```

`Signal` は、現在のStateを1つだけ持ちます。

毎フレーム、現在のStateの `Update()` を呼びます。

```text
Signal::Update()
↓
今のStateのUpdate()を呼ぶ
↓
Stateの中で必要なら次のStateへ切り替える

```

---

# 5. 親クラス SignalStateBase

まず、すべてのStateの親クラスを作ります。

```cpp
class Signal;

class SignalStateBase
{
public:
    virtual ~SignalStateBase() = default;

    virtual void Update(Signal& signal) = 0;
};

```

ポイントは3つです。

```text
SignalStateBaseは、すべてのStateの親
Update()は、状態ごとの処理を書く場所
= 0 があるので、このクラスをそのまま使うのではなく継承して使う

```

---

# 6. Signalクラス

信号機本体のクラスです。

```cpp
class Signal
{
private:
    SignalStateBase* state_ = nullptr;
    SignalStateBase* nextState_ = nullptr;

public:
    Signal();
    ~Signal();

    void Update();
    void ChangeState(SignalStateBase* nextState);
    void ApplyStateChange();

    bool IsTimeOver();

    void Red();
    void Blue();
    void Yellow();
};

```

`state_` は、現在の状態を表します。

```cpp
SignalStateBase* state_;

```

例えば、次のようなイメージです。

```text
state_ -> RedState
state_ -> BlueState
state_ -> YellowState

```

---

# 7. Signal::Update

`Signal::Update()` は、とても短くなります。

```cpp
void Signal::Update()
{
    if (state_ != nullptr)
    {
        state_->Update(*this);
    }

    ApplyStateChange();
}

```

`Signal` は、現在のStateに処理を任せます。

```text
今がRedStateなら、RedState::Update()
今がBlueStateなら、BlueState::Update()
今がYellowStateなら、YellowState::Update()

```

が呼ばれます。

---

# 8. ChangeState

状態を切り替える関数です。

ただし、ここでは今のStateをすぐには消しません。

次に切り替えるStateを予約します。

```cpp
void Signal::ChangeState(SignalStateBase* nextState)
{
    delete nextState_;
    nextState_ = nextState;
}

```

なぜすぐに切り替えないのかを、次の章で確認します。

---

# 9. Update中に自分を消さないように注意

次のように、`ChangeState()` の中で今のStateをすぐに消す書き方は危険です。

```cpp
void Signal::ChangeState(SignalStateBase* nextState)
{
    delete state_;
    state_ = nextState;
}

```

次のような流れになることがあります。

```text
RedState::Update() 実行中
↓
signal.ChangeState(new BlueState()) を呼ぶ
↓
delete state_ が実行される
↓
今まさに実行中の RedState 自身を消してしまう

```

つまり、Stateの `Update()` の中で状態を切り替えると、

```text
自分のUpdate中に、自分自身をdeleteしてしまう

```

可能性があります。

これは、かなり分かりにくいバグの原因になります。

---

# 10. 安全な考え方

安全にするなら、Stateの `Update()` 中は、すぐに消さないようにします。

考え方は次の通りです。

```text
1. StateのUpdate中は、次のStateを予約するだけ
2. StateのUpdateが終わる
3. そのあとで古いStateを消して、新しいStateに入れ替える

```

イメージはこうです。

```cpp
void Signal::Update()
{
    if (state_ != nullptr)
    {
        state_->Update(*this);
    }

    ApplyStateChange();
}

```

`ChangeState()` では、今の `state_` をすぐに消しません。

```cpp
void Signal::ChangeState(SignalStateBase* nextState)
{
    delete nextState_;
    nextState_ = nextState;
}

```

`Update()` が終わったあとで、まとめて切り替えます。

```cpp
void Signal::ApplyStateChange()
{
    if (nextState_ == nullptr)
    {
        return;
    }

    delete state_;
    state_ = nextState_;
    nextState_ = nullptr;
}

```

このため、`Signal` クラスには `nextState_` も用意します。

```cpp
class Signal
{
private:
    SignalStateBase* state_ = nullptr;
    SignalStateBase* nextState_ = nullptr;
};

```

この資料では信号機のStateクラスを理解することが目的なので、まずは単純な形で説明しています。

ただし、実際に作るときは、

```text
Update中に、今実行中のStateを先に消さない

```

という点を必ず意識します。

---

# 11. Signalの生成と破棄

最初は赤信号から始めます。

```cpp
Signal::Signal()
{
    state_ = new RedState();
}

```

最後に現在のStateを消します。

```cpp
Signal::~Signal()
{
    delete state_;
    delete nextState_;
}

```

---

# 12. RedState

赤信号のStateクラスです。

```cpp
class RedState : public SignalStateBase
{
public:
    void Update(Signal& signal) override
    {
        signal.Red();

        if (signal.IsTimeOver())
        {
            signal.ChangeState(new BlueState());
        }
    }
};

```

赤信号では、まず `signal.Red()` を呼びます。

時間が来たら、青信号に切り替えます。

```text
RedState
↓ 時間が来た
BlueState

```

---

# 13. BlueState

青信号のStateクラスです。

```cpp
class BlueState : public SignalStateBase
{
public:
    void Update(Signal& signal) override
    {
        signal.Blue();

        if (signal.IsTimeOver())
        {
            signal.ChangeState(new YellowState());
        }
    }
};

```

青信号では、まず `signal.Blue()` を呼びます。

時間が来たら、黄信号に切り替えます。

```text
BlueState
↓ 時間が来た
YellowState

```

---

# 14. YellowState

黄信号のStateクラスです。

```cpp
class YellowState : public SignalStateBase
{
public:
    void Update(Signal& signal) override
    {
        signal.Yellow();

        if (signal.IsTimeOver())
        {
            signal.ChangeState(new RedState());
        }
    }
};

```

黄信号では、まず `signal.Yellow()` を呼びます。

時間が来たら、赤信号に戻ります。

```text
YellowState
↓ 時間が来た
RedState

```

---

# 15. 状態遷移表とStateクラス

信号機の状態遷移表は、次のようになります。

<table id="bkmrk-%E7%8F%BE%E5%9C%A8%E3%81%AE%E7%8A%B6%E6%85%8B-%E6%9D%A1%E4%BB%B6-%E6%AC%A1%E3%81%AE%E7%8A%B6%E6%85%8B-red-is"><thead><tr><th>現在の状態</th><th>条件</th><th>次の状態</th></tr></thead><tbody><tr><td>Red</td><td>`IsTimeOver()`</td><td>Blue</td></tr><tr><td>Blue</td><td>`IsTimeOver()`</td><td>Yellow</td></tr><tr><td>Yellow</td><td>`IsTimeOver()`</td><td>Red</td></tr></tbody></table>

この表の1行が、Stateクラス1つに対応します。

例えば、

<table id="bkmrk-%E7%8F%BE%E5%9C%A8%E3%81%AE%E7%8A%B6%E6%85%8B-%E6%9D%A1%E4%BB%B6-%E6%AC%A1%E3%81%AE%E7%8A%B6%E6%85%8B-red-is-1"><thead><tr><th>現在の状態</th><th>条件</th><th>次の状態</th></tr></thead><tbody><tr><td>Red</td><td>`IsTimeOver()`</td><td>Blue</td></tr></tbody></table>

は、次のコードになります。

```cpp
class RedState : public SignalStateBase
{
public:
    void Update(Signal& signal) override
    {
        signal.Red();

        if (signal.IsTimeOver())
        {
            signal.ChangeState(new BlueState());
        }
    }
};

```

---

# 16. switch文版との対応

<table id="bkmrk-switch%E6%96%87%E7%89%88-state%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E7%89%88-"><thead><tr><th>switch文版</th><th>Stateパターン版</th></tr></thead><tbody><tr><td>`case SignalState::Red:`</td><td>`RedState`</td></tr><tr><td>`case SignalState::Blue:`</td><td>`BlueState`</td></tr><tr><td>`case SignalState::Yellow:`</td><td>`YellowState`</td></tr><tr><td>`state_ = SignalState::Blue;`</td><td>`ChangeState(new BlueState());`</td></tr><tr><td>`switch (state_)`</td><td>`state_->Update(*this);`</td></tr></tbody></table>

Stateパターンは、switch文をなくすためのものというより、

```text
状態ごとの処理を、別々のクラスに分ける方法

```

です。

---

# 17. この資料で覚えるC++のポイント

今回覚えるC++のポイントは、次の4つです。

```text
1. 親クラスのポインタで子クラスを扱える
2. virtual関数を使うと、実体のクラスの関数が呼ばれる
3. overrideを書くと、上書きしていることが分かりやすい
4. Stateクラスを切り替えることで、動作を変えられる
5. Update中に、今実行中のStateを先に消さない

```

特に大事なのは、次の1行です。

```cpp
state_->Update(*this);

```

この1行で、現在の状態に合った処理が呼ばれます。

---

# 18. まとめ

Stateパターンは、状態ごとの処理をクラスに分ける方法です。

信号機の例では、次のように分けました。

```text
赤信号の処理   RedState
青信号の処理   BlueState
黄信号の処理   YellowState

```

`Signal` は現在のStateを持ちます。

```cpp
SignalStateBase* state_;

```

毎フレーム、現在のStateの `Update()` を呼びます。

```cpp
state_->Update(*this);

```

状態を切り替えたいときは、別のStateクラスに入れ替えます。

```cpp
signal.ChangeState(new BlueState());

```

ただし、Stateの `Update()` 中にすぐ `delete state_` すると、今実行中のState自身を消してしまう可能性があります。

実際にゲームで使う場合は、次のStateを予約しておき、`Update()` が終わったあとで切り替える形にすると安全です。

---

# 練習問題

---

# 問1

Stateパターンは、何をクラスに分ける方法ですか。

---

# 問2

信号機の例で、次の状態に対応するStateクラス名を書きなさい。

<table id="bkmrk-%E7%8A%B6%E6%85%8B-state%E3%82%AF%E3%83%A9%E3%82%B9%E5%90%8D-%E8%B5%A4-%E9%9D%92-%E9%BB%84"><thead><tr><th>状態</th><th>Stateクラス名</th></tr></thead><tbody><tr><td>赤</td><td></td></tr><tr><td>青</td><td></td></tr><tr><td>黄</td><td></td></tr></tbody></table>

---

# 問3

次の空欄を埋めなさい。

```cpp
class RedState : public SignalStateBase
{
public:
    void Update(Signal& signal) override
    {
        signal.Red();

        if (signal.IsTimeOver())
        {
            signal.ChangeState(new __________());
        }
    }
};

```

---

# 問4

次の状態遷移表を見て、`BlueState` の中で何をすればよいか説明しなさい。

<table id="bkmrk-%E7%8F%BE%E5%9C%A8%E3%81%AE%E7%8A%B6%E6%85%8B-%E6%9D%A1%E4%BB%B6-%E6%AC%A1%E3%81%AE%E7%8A%B6%E6%85%8B-blue-i"><thead><tr><th>現在の状態</th><th>条件</th><th>次の状態</th></tr></thead><tbody><tr><td>Blue</td><td>`IsTimeOver()`</td><td>Yellow</td></tr></tbody></table>

---

# 問5

次のコードの `state_` は何を表していますか。

```cpp
SignalStateBase* state_;

```

---

# 問6

次のコードでは、なぜ `switch` が必要ありませんか。

```cpp
void Signal::Update()
{
    if (state_ != nullptr)
    {
        state_->Update(*this);
    }
}

```

---

# 問7

次の状態を追加したいとします。

```text
点滅

```

状態遷移は次の通りです。

<table id="bkmrk-%E7%8F%BE%E5%9C%A8%E3%81%AE%E7%8A%B6%E6%85%8B-%E6%9D%A1%E4%BB%B6-%E6%AC%A1%E3%81%AE%E7%8A%B6%E6%85%8B-yellow"><thead><tr><th>現在の状態</th><th>条件</th><th>次の状態</th></tr></thead><tbody><tr><td>Yellow</td><td>`IsTimeOver()`</td><td>Blink</td></tr><tr><td>Blink</td><td>`IsTimeOver()`</td><td>Red</td></tr></tbody></table>

このとき、新しく必要になるStateクラス名を書きなさい。

---

# 問8

`BlinkState` のコードを完成させなさい。

```cpp
class BlinkState : public SignalStateBase
{
public:
    void Update(Signal& signal) override
    {
        signal.Blink();

        if (signal.IsTimeOver())
        {
            signal.ChangeState(new __________());
        }
    }
};

```

---

# 問9

switch文版とStateパターン版の違いを、次の言葉を使って説明しなさい。

```text
case
クラス
状態ごとの処理

```

---

# 問10

次のコードの危険な点を説明しなさい。

```cpp
void Signal::ChangeState(SignalStateBase* nextState)
{
    delete state_;
    state_ = nextState;
}

```

ヒント：

```text
StateのUpdate中にChangeStateを呼ぶ
今実行中のState
delete

```

---

# 問11

信号機以外で、Stateパターンが使えそうなゲームの例を1つ考えなさい。

例：

```text
プレイヤーの状態
敵AI
シーン管理

```

---

# 解答例

---

# 問1 解答例

```text
状態ごとの処理を、状態ごとのクラスに分ける方法。

```

---

# 問2 解答例

<table id="bkmrk-%E7%8A%B6%E6%85%8B-state%E3%82%AF%E3%83%A9%E3%82%B9%E5%90%8D-%E8%B5%A4-redst"><thead><tr><th>状態</th><th>Stateクラス名</th></tr></thead><tbody><tr><td>赤</td><td>`RedState`</td></tr><tr><td>青</td><td>`BlueState`</td></tr><tr><td>黄</td><td>`YellowState`</td></tr></tbody></table>

---

# 問3 解答例

```cpp
signal.ChangeState(new BlueState());

```

---

# 問4 解答例

`BlueState` では、まず `signal.Blue()` を呼びます。

その後、`signal.IsTimeOver()` が true なら、`YellowState` に切り替えます。

```cpp
class BlueState : public SignalStateBase
{
public:
    void Update(Signal& signal) override
    {
        signal.Blue();

        if (signal.IsTimeOver())
        {
            signal.ChangeState(new YellowState());
        }
    }
};

```

---

# 問5 解答例

`state_` は、現在の状態を表しています。

信号機の場合、現在が赤なら `RedState`、青なら `BlueState`、黄なら `YellowState` を指します。

---

# 問6 解答例

`state_` の中身が、現在のStateクラスを指しているからです。

`state_->Update(*this);` と書けば、現在のStateに合った `Update()` が呼ばれます。

そのため、`Signal::Update()` の中で `switch` を書く必要がありません。

---

# 問7 解答例

```text
BlinkState

```

---

# 問8 解答例

```cpp
class BlinkState : public SignalStateBase
{
public:
    void Update(Signal& signal) override
    {
        signal.Blink();

        if (signal.IsTimeOver())
        {
            signal.ChangeState(new RedState());
        }
    }
};

```

---

# 問9 解答例

switch文版では、`case` ごとに状態ごとの処理を書きます。

Stateパターン版では、状態ごとの処理をクラスに分けます。

例えば、赤信号の処理は `case SignalState::Red:` ではなく、`RedState` クラスに書きます。

---

# 問10 解答例

危険な点は、Stateの `Update()` 中に `ChangeState()` を呼んだ場合、今実行中のState自身を `delete` してしまう可能性があることです。

例えば、`RedState::Update()` の中で `ChangeState(new BlueState())` を呼ぶと、`delete state_` によって実行中の `RedState` を消してしまう可能性があります。

安全にするには、`ChangeState()` では次のStateを予約するだけにして、`Update()` が終わったあとで入れ替えます。

---

# 問11 解答例

例：

```text
プレイヤーの状態

```

```text
IdleState
RunState
JumpState
AttackState
DamageState

```

のように、プレイヤーの状態ごとにクラスを分けられます。