Godot入門:SpringArm3Dの使い方と活用法

はじめに

TPSゲームやアクション系ゲームでカメラを操作するとき、壁の中にカメラが埋まってしまった経験はありませんか?SpringArm3Dはそのような問題を自動的に解決してくれるノードです。このノードを使えば、キャラクターの背後からカメラが常に見やすい位置に自動調整されます。

SpringArm3Dとは?

SpringArm3Dは、カメラがキャラクターや障害物に埋まらないようにするTPS・アクションカメラ用のノードです。スプリングアーム(腕)が障害物を検出すると、自動的にカメラを手前に引き寄せます。

継承ツリー:

SpringArm3D
  ↓
Node3D
  ↓
Node




SpringArm3Dの動作原理
SpringArm3Dはキャラクターからカメラへの距離を保ちながら、障害物があると自動的に縮んでカメラを手前に寄せます

このノードを使うべき場面

使うべき場面:

  • Third Person Shooter(TPS)・アクションゲームのカメラ制御
  • アクションRPGの背後視点カメラ
  • 探索ゲームで狭い屋内での移動時のカメラ調整
  • キャラクターとカメラ間の距離を一定に保ちながら柔軟に対応する必要があるゲーム
  • 壁や天井などの障害物に埋まるカメラの問題を解決したい場合

使わない場面:

  • FPS(一人称視点)ゲーム(直接Camera3Dを配置すれば十分)
  • 固定カメラまたは自動カメラパス追従

主なプロパティと機能

プロパティ 説明 使用例
spring_length float アームの最大長さ(m)。カメラとキャラクターの基本距離 spring_arm.spring_length = 5.0
shape Shape3D 障害物判定に用いる形状(SphereShape3D、CapsuleShape3D等) spring_arm.shape = SphereShape3D.new()
collision_mask int どのフィジクスレイヤーの障害物を判定するか spring_arm.collision_mask = 0b0001
margin float カメラと障害物の間隔マージン(m) spring_arm.margin = 0.3

重要メソッド

メソッド 戻り値 説明 使用例
get_hit_length() float 現在のアーム長(障害物がある場合は短縮された長さ) var current_len = spring_arm.get_hit_length()

GDScriptコード例1:基本的なTPSカメラセットアップ


extends Node3D

@onready var character = $Character  # CharacterBody3Dなど
@onready var spring_arm = $Character/SpringArm3D
@onready var camera = $Character/SpringArm3D/Camera3D

@export var mouse_sensitivity = 0.005
@export var spring_length = 5.0

func _ready():
    # SpringArm3Dの設定
    spring_arm.spring_length = spring_length

    # 障害物検出用の形状を設定(球形)
    var sphere_shape = SphereShape3D.new()
    sphere_shape.radius = 0.5
    spring_arm.shape = sphere_shape

    # カメラが壁との間に最小0.3m離れるよう設定
    spring_arm.margin = 0.3

    Input.mouse_mode = Input.MOUSE_MODE_CAPTURED

func _physics_process(delta):
    # カメラの回転をマウス入力で制御
    var mouse_motion = Input.get_last_mouse_velocity()

    # Y軸周りの回転(上下)
    spring_arm.rotate_y(-mouse_motion.x * mouse_sensitivity * delta)

    # X軸周りの回転(左右)。ピッチ制限を加える
    var pitch = spring_arm.rotation.x
    pitch -= mouse_motion.y * mouse_sensitivity * delta
    pitch = clamp(pitch, -PI / 2, PI / 2)
    spring_arm.rotation.x = pitch

    # 現在のアーム長を表示(デバッグ用)
    var current_length = spring_arm.get_hit_length()
    print("カメラ距離: %.2f m" % current_length)

GDScriptコード例2:キャラクターの移動とカメラの連携


extends CharacterBody3D

@onready var spring_arm = $SpringArm3D

@export var move_speed = 7.0
@export var spring_arm_height = 1.7

func _ready():
    # SpringArm3D初期設定
    spring_arm.position.y = spring_arm_height  # 目の高さ
    spring_arm.spring_length = 4.5

    var sphere = SphereShape3D.new()
    sphere.radius = 0.4
    spring_arm.shape = sphere

func _physics_process(delta):
    # キャラクター移動
    var input_dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
    var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()

    if direction:
        velocity.x = direction.x * move_speed
        velocity.z = direction.z * move_speed
    else:
        velocity.x = move_toward(velocity.x, 0, move_speed)
        velocity.z = move_toward(velocity.z, 0, move_speed)

    # キャラクターの向きをカメラ方向に合わせる
    if direction:
        look_at(global_position + direction, Vector3.UP)

    velocity.y -= 9.8 * delta
    velocity = move_and_slide(velocity)

もっと使いこなす:カスタマイズできるパラメータ

まずは基本を動かしてみてから、余裕が出たら試してみてください。

パラメータ 説明 実装例
position(継承) Vector3 キャラクター相対でのアームの起点位置 spring_arm.position = Vector3(0, 1.7, 0)
rotation(継承) Vector3 アームの向き(ラジアン) spring_arm.rotation.x = -PI / 6 # 仰角
collision_group int 自身が属するフィジクスグループ spring_arm.collision_group = 0b0001
exclude_parent bool 親ノード(キャラクター)を障害物判定から除外するか spring_arm.exclude_parent = true(通常true)
shape(複数形状) Shape3D CapsuleShape3Dなら狭い通路でも対応 var capsule = CapsuleShape3D.new(); capsule.radius = 0.3; capsule.height = 0.6

カスタマイズの実装例:複数カメラ距離のプリセット


extends Node3D

@onready var spring_arm = $Character/SpringArm3D

var camera_distances = {
    "close": 2.0,
    "normal": 5.0,
    "far": 8.0
}

var current_distance = "normal"

func _ready():
    set_camera_distance(current_distance)

func _process(delta):
    # 数字キーでカメラ距離を切り替え
    if Input.is_action_just_pressed("ui_1"):
        set_camera_distance("close")
    elif Input.is_action_just_pressed("ui_2"):
        set_camera_distance("normal")
    elif Input.is_action_just_pressed("ui_3"):
        set_camera_distance("far")

func set_camera_distance(preset_name: String):
    if preset_name in camera_distances:
        current_distance = preset_name
        spring_arm.spring_length = camera_distances[preset_name]
        print("カメラ距離: %s (%.1f m)" % [preset_name, camera_distances[preset_name]])

まとめ

SpringArm3Dは、TPS・アクションゲームのカメラ制御を劇的に改善する必須ノードです。

  • カメラが壁や障害物に埋まる問題を自動的に解決します
  • spring_lengthとshapeプロパティを調整するだけで、ゲーム特性に合ったカメラ距離を実現できます
  • get_hit_length()で現在のカメラ距離を取得し、UI表示やエフェクト制御に活用できます

次回は、視線判定に必須の「RayCast3D」について詳しく解説します。

シリーズ:Godot 4 ノード解説

001~040:各種ノード

041~080:
041: Marker3D | 042: Node3D | 043: Camera3D | 044: Light3D | 045: MeshInstance3D |
046: AnimationPlayer | 047: AnimationTree | 048: Skeleton3D | 049: IKSkeleton3D | 050: Skin |
051: CollisionShape3D | 052: RigidBody3D | 053: CharacterBody3D | 054: StaticBody3D | 055: Area3D |
056: PhysicsDirectSpaceState3D | 057: ShapeCast3D | 058: RayCast3D | 059: Decal | 060: GPUParticles3D |
061: CPUParticles3D | 062: WorldEnvironment | 063: OmniLight3D | 064: SpotLight3D | 065: DirectionalLight3D |
066: ReflectionProbe | 067: LightmapGI | 068: VoxelGI | 069: Occluder3D | 070: VisualInstance3D |
071: BoneAttachment3D | 072: VehicleBody3D | 073: SpringArm3D | 074: RayCast3D | 075: ShapeCast3D |
076: MultiMeshInstance3D | 077: Node | 078: CanvasLayer | 079: Control | 080: Label

この記事はGodot 4.xをもとに執筆しています。

コメント