Search code examples
3dgodotgodot4

Godot 4.1 - Controlling Camera3D position around CharacterBody3D


New to constructing 3D environments in Godot, and I'm trying to create a scene where the CharacterBody3D (referred to as the focus) remains in the center of the screen (Camera3D above and behind) while the rest of the world rotates/moves around it. Unfortunately, when rotating (q/e keys) the focus travels in an elliptical path instead of staying centered.

The project files are here: Github repository

I have been able to achieve centered rotation and translation (w/a/s/d keys), but the camera jumps forward and cuts the focus in half such that you can only view planets in front of (the top half of the screen) the focus. The modified code looks like:

func rotate_around_focus_character(delta):
# Rotate the camera around the focus character
var theta = deg_to_rad(rotation_speed * delta)
#var rotation_center = focus.global_transform.origin #+ Vector3(0, 10, 0)

if Input.is_action_pressed("rotate_left"):
    global_transform.origin = focus.global_transform.origin
    global_transform.basis = camera_3d.global_transform.basis.rotated(Vector3(0, 1, 0), theta)
    
if Input.is_action_pressed("rotate_right"):
    global_transform.origin = focus.global_transform.origin
    global_transform.basis = camera_3d.global_transform.basis.rotated(Vector3(0, 1, 0), -theta)

and

func track_focus_with_camera(delta):
if focus:
    var camera_3d_pos = camera_3d.global_transform.origin
    print("camera_3d_pos = " + str(camera_3d_pos))
    global_transform.origin = focus.global_transform.origin.lerp(camera_3d.position, 0.1)

The relevant code is located here: res://camera/CameraFocus.gd


Solution

  • Does below script achieve the behavior you want? What I did was keep track of an angle for camera position vector. Base offset Vector is (0, 10, 10) and it is rotated by the amount of current rotation.

    If you are not confident about dealing with 3D cameras (at least in Godot), I advise you to use nested Node3D nodes. For example a structure like this:

    Node3DX  
        Node3DY
            Node3DZ
                Camera itself
    

    Each Node3D is used for rotating around a single axis. I am not arguing this is the best method, but rather it is simpler. For your example I did use this method since your rotations are only around Y axis.

    extends Camera3D
    
    @onready var focus = $"../../Focus"
    @onready var camera_pivot = $".."
    @onready var camera_3d = $"."
    
    var rotation_speed = 60
    
    
    var current_focus_rotation = 0
    
    
    func _process(delta):
        rotate_around_focus_character(delta)
        track_focus_with_camera(delta)
    
    func rotate_around_focus_character(delta):
        # Rotate the camera around the focus character
        var theta = deg_to_rad(rotation_speed * delta)
        #var rotation_center = focus.global_transform.origin #+ Vector3(0, 10, 0)
        
        if Input.is_action_pressed("rotate_left"):
            if (not focus):
                global_transform.origin = camera_3d.global_transform.origin
            global_transform.basis = camera_3d.global_transform.basis.rotated(Vector3(0, 1, 0), theta)
            current_focus_rotation += theta
            
        if Input.is_action_pressed("rotate_right"):
            if (not focus):
                global_transform.origin = camera_3d.global_transform.origin
            global_transform.basis = camera_3d.global_transform.basis.rotated(Vector3(0, 1, 0), -theta)
            current_focus_rotation -= theta
        
    
    func track_focus_with_camera(delta):
        if focus:
            var camera_3d_pos = camera_3d.global_transform.origin
            print("camera_3d_pos = " + str(camera_3d_pos))
            var focus_pos = focus.global_transform.origin #cus.global_transform.origin.lerp(camera_3d.position, 0.1)
            var offset_vector = Vector3(0, 10, 10).rotated(Vector3(0, 1, 0), current_focus_rotation)
            var final_pos = focus_pos + offset_vector
            camera_3d.global_transform.origin = final_pos