# 機能設計書: カメラシステム（2D/3D）

## 1. 機能概要

### 1.1 機能の目的

カメラシステムは、Godot Engineにおける視点制御の基盤を提供する。2Dと3D両方のカメラをサポートし、ビューポートの描画範囲を決定する。位置スムージング、回転スムージング、制限境界、ドラッグマージンなどの高度な機能により、滑らかなカメラ移動を実現する。

### 1.2 主要な責務

| 責務 | 説明 |
|------|------|
| 視点変換 | カメラ変換行列の計算とビューポートへの適用 |
| カレントカメラ管理 | アクティブなカメラの管理と切り替え |
| スムージング | 位置/回転のスムーズな補間 |
| 制限境界 | カメラ移動範囲の制限 |
| 投影設定 | 透視投影/正射投影/フラスタム投影 |
| 物理補間 | フレームレート非依存のスムーズな動き |

### 1.3 アーキテクチャ上の位置づけ

```
+------------------+
|   Game Scripts   |
+------------------+
         |
         v
+--------+----------+
|                   |
v                   v
+----------+   +----------+
| Camera2D |   | Camera3D |
+----------+   +----------+
     |              |
     v              v
+----------+   +----------+
|  Node2D  |   |  Node3D  |
+----------+   +----------+
     |              |
     v              v
+------------------+
|    Viewport      |
+------------------+
         |
         v
+------------------+
| RenderingServer  |
+------------------+
```

## 2. Camera2D クラス

### 2.1 クラス構造

```cpp
// scene/2d/camera_2d.h 35-115行目
class Camera2D : public Node2D {
    GDCLASS(Camera2D, Node2D);

    // 位置関連
    Point2 camera_pos;
    Point2 smoothed_camera_pos;
    Vector2 offset;
    Vector2 zoom = Vector2(1, 1);
    Vector2 zoom_scale = Vector2(1, 1);

    // スムージング
    real_t position_smoothing_speed = 5.0;
    bool position_smoothing_enabled = false;
    real_t rotation_smoothing_speed = 5.0;
    bool rotation_smoothing_enabled = false;

    // 制限境界
    bool limit_enabled = true;
    int limit[4] = { -10000000, -10000000, 10000000, 10000000 };
    bool limit_smoothing_enabled = false;

    // ドラッグ
    real_t drag_margin[4] = { 0.2, 0.2, 0.2, 0.2 };
    bool drag_horizontal_enabled = false;
    bool drag_vertical_enabled = false;

    // 物理補間
    struct InterpolationData {
        Transform2D xform_curr;
        Transform2D xform_prev;
        uint32_t last_update_physics_tick = UINT32_MAX;
        bool accepting_resets = false;
    } _interpolation_data;
};
```

### 2.2 列挙型定義

```cpp
// scene/2d/camera_2d.h 39-47行目
enum AnchorMode {
    ANCHOR_MODE_FIXED_TOP_LEFT,  // 固定（左上基準）
    ANCHOR_MODE_DRAG_CENTER      // ドラッグセンター
};

enum Camera2DProcessCallback {
    CAMERA2D_PROCESS_PHYSICS,    // 物理フレーム
    CAMERA2D_PROCESS_IDLE        // アイドルフレーム
};
```

### 2.3 get_camera_transform（変換取得）

```cpp
// scene/2d/camera_2d.cpp 117-267行目
Transform2D Camera2D::get_camera_transform() {
    Size2 screen_size = _get_camera_screen_size();
    Point2 new_camera_pos = get_global_position();

    // ドラッグセンターモードの場合
    if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) {
        // 水平ドラッグ制限
        if (drag_horizontal_enabled) {
            camera_pos.x = MIN(camera_pos.x, new_camera_pos.x + screen_size.x * 0.5 * zoom_scale.x * drag_margin[SIDE_LEFT]);
            camera_pos.x = MAX(camera_pos.x, new_camera_pos.x - screen_size.x * 0.5 * zoom_scale.x * drag_margin[SIDE_RIGHT]);
        }
        // 垂直ドラッグ制限
        if (drag_vertical_enabled) {
            camera_pos.y = MIN(camera_pos.y, new_camera_pos.y + screen_size.y * 0.5 * zoom_scale.y * drag_margin[SIDE_TOP]);
            camera_pos.y = MAX(camera_pos.y, new_camera_pos.y - screen_size.y * 0.5 * zoom_scale.y * drag_margin[SIDE_BOTTOM]);
        }
    }

    // 位置スムージング
    if (position_smoothing_enabled) {
        real_t delta = physics_process ? get_physics_process_delta_time() : get_process_delta_time();
        real_t c = position_smoothing_speed * delta;
        smoothed_camera_pos = ((camera_pos - smoothed_camera_pos) * c) + smoothed_camera_pos;
        ret_camera_pos = smoothed_camera_pos;
    }

    // 回転スムージング
    if (rotation_smoothing_enabled && !ignore_rotation) {
        real_t step = rotation_smoothing_speed * delta;
        camera_angle = Math::lerp_angle(camera_angle, get_global_rotation(), step);
    }

    // 制限境界の適用
    if (limit_enabled) {
        // 水平/垂直制限の適用...
    }

    // 変換行列の構築
    Transform2D xform;
    xform.scale_basis(zoom_scale);
    if (!ignore_rotation) {
        xform.set_rotation(camera_angle);
    }
    xform.set_origin(screen_rect.position);

    return xform.affine_inverse();
}
```

### 2.4 _update_scroll（スクロール更新）

```cpp
// scene/2d/camera_2d.cpp 37-68行目
void Camera2D::_update_scroll() {
    if (!is_inside_tree() || !viewport) {
        return;
    }

    if (is_current()) {
        Size2 screen_size = _get_camera_screen_size();

        Transform2D xform;
        if (is_physics_interpolated_and_enabled()) {
            // 物理補間
            xform = _interpolation_data.xform_prev.interpolate_with(
                _interpolation_data.xform_curr,
                Engine::get_singleton()->get_physics_interpolation_fraction());
        } else {
            xform = get_camera_transform();
        }

        viewport->set_canvas_transform(xform);

        // ParallaxBackground/Layerへの通知
        get_tree()->call_group(group_name, SNAME("_camera_moved"), xform, screen_offset, adj_screen_pos);
    }
}
```

## 3. Camera3D クラス

### 3.1 クラス構造

```cpp
// scene/3d/camera_3d.h 40-106行目
class Camera3D : public Node3D {
    GDCLASS(Camera3D, Node3D);

    ProjectionType mode = PROJECTION_PERSPECTIVE;

    // 補間対応プロパティ
    InterpolatedProperty<real_t> fov = 75.0;
    InterpolatedProperty<real_t> size = 1.0;
    InterpolatedProperty<Vector2> frustum_offset;
    InterpolatedProperty<real_t> _near = 0.05;
    InterpolatedProperty<real_t> _far = 4000.0;

    // オフセット
    real_t v_offset = 0.0;
    real_t h_offset = 0.0;

    // RenderingServer連携
    RID camera;
    RID scenario_id;
    uint32_t layers = 0xfffff;

    // 環境/属性
    Ref<Environment> environment;
    Ref<CameraAttributes> attributes;
    Ref<Compositor> compositor;

    // ドップラー追跡
    DopplerTracking doppler_tracking = DOPPLER_TRACKING_DISABLED;
    Ref<VelocityTracker3D> velocity_tracker;
};
```

### 3.2 列挙型定義

```cpp
// scene/3d/camera_3d.h 44-59行目
enum ProjectionType {
    PROJECTION_PERSPECTIVE,   // 透視投影
    PROJECTION_ORTHOGONAL,    // 正射投影
    PROJECTION_FRUSTUM        // フラスタム投影
};

enum KeepAspect {
    KEEP_WIDTH,               // 幅維持
    KEEP_HEIGHT               // 高さ維持
};

enum DopplerTracking {
    DOPPLER_TRACKING_DISABLED,    // 無効
    DOPPLER_TRACKING_IDLE_STEP,   // アイドルステップ
    DOPPLER_TRACKING_PHYSICS_STEP // 物理ステップ
};
```

### 3.3 投影設定メソッド

```cpp
// scene/3d/camera_3d.cpp 292-338行目
void Camera3D::set_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far) {
    if (!force_change && fov == p_fovy_degrees && p_z_near == _near && p_z_far == _far && mode == PROJECTION_PERSPECTIVE) {
        return;
    }

    fov = p_fovy_degrees;
    _near = p_z_near;
    _far = p_z_far;
    mode = PROJECTION_PERSPECTIVE;

    RenderingServer::get_singleton()->camera_set_perspective(camera, fov, _near, _far);
    update_gizmos();
}

void Camera3D::set_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far) {
    size = p_size;
    _near = p_z_near;
    _far = p_z_far;
    mode = PROJECTION_ORTHOGONAL;

    RenderingServer::get_singleton()->camera_set_orthogonal(camera, size, _near, _far);
    update_gizmos();
}

void Camera3D::set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, real_t p_z_far) {
    size = p_size;
    frustum_offset = p_offset;
    _near = p_z_near;
    _far = p_z_far;
    mode = PROJECTION_FRUSTUM;

    RenderingServer::get_singleton()->camera_set_frustum(camera, size, frustum_offset, _near, _far);
    update_gizmos();
}
```

### 3.4 カメラ変換取得

```cpp
// scene/3d/camera_3d.cpp 253-266行目
Transform3D Camera3D::_get_adjusted_camera_transform(const Transform3D &p_xform) const {
    Transform3D tr = p_xform.orthonormalized();
    tr.origin += tr.basis.get_column(1) * v_offset;  // 垂直オフセット
    tr.origin += tr.basis.get_column(0) * h_offset;  // 水平オフセット
    return tr;
}

Transform3D Camera3D::get_camera_transform() const {
    if (is_physics_interpolated_and_enabled() && !Engine::get_singleton()->is_in_physics_frame()) {
        return _get_adjusted_camera_transform(_get_cached_global_transform_interpolated());
    }

    return _get_adjusted_camera_transform(get_global_transform());
}
```

## 4. 通知処理

### 4.1 Camera2D通知処理

```cpp
// scene/2d/camera_2d.cpp 286-453行目
void Camera2D::_notification(int p_what) {
    switch (p_what) {
        case NOTIFICATION_INTERNAL_PROCESS:
            _update_scroll();
            break;

        case NOTIFICATION_INTERNAL_PHYSICS_PROCESS:
            if (is_physics_interpolated_and_enabled()) {
                _ensure_update_interpolation_data();
                _interpolation_data.xform_curr = get_camera_transform();
            } else {
                _update_scroll();
            }
            break;

        case NOTIFICATION_ENTER_TREE:
            viewport = custom_viewport ? custom_viewport : get_viewport();
            canvas = get_canvas();
            group_name = "__cameras_" + itos(viewport->get_viewport_rid().get_id());
            add_to_group(group_name);

            if (enabled && !viewport->get_camera_2d()) {
                make_current();
            }

            _update_process_callback();
            first = true;
            _update_scroll();
            break;

        case NOTIFICATION_EXIT_TREE:
            if (is_current()) {
                clear_current();
            }
            viewport = nullptr;
            break;
    }
}
```

### 4.2 Camera3D通知処理

```cpp
// scene/3d/camera_3d.cpp 180-251行目
void Camera3D::_notification(int p_what) {
    switch (p_what) {
        case NOTIFICATION_ENTER_WORLD:
            viewport = get_viewport();
            bool first_camera = viewport->_camera_3d_add(this);
            if (current || first_camera) {
                viewport->_camera_3d_set(this);
            }
            break;

        case NOTIFICATION_TRANSFORM_CHANGED:
            _request_camera_update();
            if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
                velocity_tracker->update_position(get_global_transform().origin);
            }
            break;

        case NOTIFICATION_EXIT_WORLD:
            if (is_current()) {
                clear_current();
                current = true;  // 再入時のために保持
            }
            viewport->_camera_3d_remove(this);
            viewport = nullptr;
            break;

        case NOTIFICATION_BECAME_CURRENT:
            viewport->find_world_3d()->_register_camera(this);
            _update_process_mode();
            break;

        case NOTIFICATION_LOST_CURRENT:
            viewport->find_world_3d()->_remove_camera(this);
            _update_process_mode();
            break;
    }
}
```

## 5. コードリーディングガイド

### 5.1 推奨読解順序

1. **Camera2Dの基本構造** (camera_2d.h 35-115行目)
   - メンバ変数の役割を理解
   - InterpolationDataの構造

2. **get_camera_transform()** (camera_2d.cpp 117-267行目)
   - 変換行列の計算フロー
   - スムージングとリミットの適用

3. **_update_scroll()** (camera_2d.cpp 37-68行目)
   - ビューポートへの変換適用
   - 物理補間の処理

4. **Camera3Dの基本構造** (camera_3d.h 40-106行目)
   - InterpolatedPropertyの使用
   - 投影モードの種類

5. **set_perspective/orthogonal/frustum** (camera_3d.cpp 292-338行目)
   - RenderingServerへの投影設定

### 5.2 読解のコツ

- `zoom_scale`は`1/zoom`の計算結果
- `smoothed_camera_pos`はスムージング適用後の位置
- `first`フラグは初回更新を示す
- `_interpolation_data`は物理補間用のデータ保持
- `InterpolatedProperty`は補間対応の値ラッパー

### 5.3 プログラム呼び出し階層図

```
Camera2D
========

set_zoom()
    |
    +---> zoom_scale = 1 / zoom
    |
    +---> _update_scroll()
              |
              +---> get_camera_transform()
              |         |
              |         +---> ドラッグ計算
              |         +---> 位置スムージング
              |         +---> 回転スムージング
              |         +---> 制限境界適用
              |
              +---> viewport->set_canvas_transform()

Camera3D
========

set_perspective()
    |
    +---> fov, _near, _far 更新
    |
    +---> RS::camera_set_perspective()
    |
    +---> update_gizmos()

NOTIFICATION_TRANSFORM_CHANGED
    |
    +---> _request_camera_update()
              |
              +---> _update_camera()
                        |
                        +---> RS::camera_set_transform()
```

### 5.4 データフロー図

```
Camera2D
========

+----------------+     get_global_position()    +----------------+
|   Target Node  |----------------------------->|    Camera2D    |
+----------------+                               +----------------+
                                                        |
                                                        v
                                                get_camera_transform()
                                                        |
                                                        v
+----------------+     set_canvas_transform()   +----------------+
|    Viewport    |<-----------------------------|  Transform2D   |
+----------------+                               +----------------+

Camera3D
========

+----------------+     set_perspective()        +----------------+
|   GDScript     |----------------------------->|   Camera3D     |
+----------------+                               +----------------+
                                                        |
                                                        v
+----------------+     camera_set_perspective() +----------------+
|RenderingServer |<-----------------------------|    RID camera  |
+----------------+                               +----------------+
```

### 5.5 関連ファイル一覧

| ファイル | 役割 |
|----------|------|
| `scene/2d/camera_2d.h/cpp` | Camera2Dクラス |
| `scene/3d/camera_3d.h/cpp` | Camera3Dクラス |
| `scene/main/viewport.h/cpp` | カメラ管理、変換適用 |
| `core/math/transform_2d.h` | 2D変換行列 |
| `core/math/transform_3d.h` | 3D変換行列 |
| `core/math/projection.h` | 投影行列 |
| `servers/rendering/rendering_server.h` | カメラAPI |

## 6. 設計上の考慮点

### 6.1 2Dと3Dの違い

| 項目 | Camera2D | Camera3D |
|------|----------|----------|
| 変換適用先 | Viewport canvas transform | RenderingServer camera |
| 投影 | なし（ズームのみ） | 透視/正射/フラスタム |
| スムージング | 組み込み | なし（自作必要） |
| 制限境界 | 組み込み | なし |
| 物理補間 | InterpolationData | InterpolatedProperty |

### 6.2 パフォーマンス最適化

1. **遅延更新**: 変更時のみ_update_scroll()を呼び出し
2. **物理補間**: フレームレート非依存のスムーズな動き
3. **キャッシュ**: zoom_scaleの事前計算

### 6.3 物理補間の仕組み

```
物理ステップ N      物理ステップ N+1
     |                    |
     v                    v
xform_prev --------> xform_curr
           \       /
            \     /
             \   /
              \ /
               v
     補間された変換
     (fraction = 0.0 ~ 1.0)
```

## 7. 通知定数

```cpp
// Camera3D固有
NOTIFICATION_BECAME_CURRENT = 50  // カレントカメラになった
NOTIFICATION_LOST_CURRENT = 51    // カレントカメラでなくなった
```

## 8. 使用例

### 8.1 Camera2Dの設定

```gdscript
extends Camera2D

func _ready():
    # スムージング有効化
    position_smoothing_enabled = true
    position_smoothing_speed = 5.0

    # 制限境界設定
    limit_left = 0
    limit_top = 0
    limit_right = 1000
    limit_bottom = 1000

    # ドラッグマージン設定
    drag_horizontal_enabled = true
    drag_vertical_enabled = true

    # カレントカメラに設定
    make_current()
```

### 8.2 Camera3Dの設定

```gdscript
extends Camera3D

func _ready():
    # 透視投影
    set_perspective(75.0, 0.05, 100.0)

    # または正射投影
    # set_orthogonal(10.0, 0.05, 100.0)

    # 環境設定
    environment = preload("res://default_env.tres")

    # カレントカメラに設定
    make_current()
```

### 8.3 スムーズなカメラ追従

```gdscript
extends Camera2D

var target: Node2D

func _process(delta):
    if target:
        global_position = global_position.lerp(target.global_position, delta * 5.0)
```
