Godot 4.1 - Controlling Camera3D position around CharacterBody3D

452 views Asked by At

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

1

There are 1 answers

0
unlut On BEST ANSWER

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