How to make CharacterBody3D move like RigidBody3D?

Godot Version

4.2.1

Question

Hi

I am making a game with simple plane controls. Here is my code (please excuse how rough it is. Still kinda prototyping right now):

extends CharacterBody3D
class_name Player

@export_category("Maximums")
# Can't fly below this speed
@export var min_flight_speed = 10.0
# Maximum airspeed
@export var max_flight_speed = 30.0

@export_category("Speed")
# Turn rate
@export var turn_speed = 0.75
# Climb/dive rate
@export var pitch_speed = 0.5
# Wings "autolevel" speed
@export var level_speed = 3.0

@export_category("Acceleration and deceleration")
# Throttle change speed
@export var throttle_delta = 30.0
# Acceleration/deceleration
@export var accelertation = 6.0

@export_category("Mesh")
@export var plane_mesh: Node3D

@export_category("Visuals")
@export var pull_ocean : Node3D

# Current speed
var forward_speed = 0.0
# Throttle input speed
var target_speed = 15.0
# Lets us change behavior when grounded
var grounded = false
var engines = false
var bouyant = false

var turn_input = 0.0
var pitch_input = 0.0

@onready var animation = $AnimationPlayer

func _ready() -> void:
	animation.current_animation = "idle"

func _process(delta: float) -> void:
	if pull_ocean:
		pull_ocean.global_position = Vector3(global_position.x, 0, global_position.z)

func get_input(delta):
	# Throttle input
	#if Input.is_action_pressed("throttle_up"):
		#target_speed = min(forward_speed + throttle_delta * delta, max_flight_speed)
	#if Input.is_action_pressed("throttle_down"):
		#var limit = 0.0 if grounded else min_flight_speed
		#target_speed = max(forward_speed - throttle_delta * delta, limit)

	# Turn (roll/yaw) input
	turn_input = 0.0
	if forward_speed > 0.5:
		turn_input -= Input.get_action_strength("roll_right")
		turn_input += Input.get_action_strength("roll_left")
	# Pitch (climb/dive) input
	pitch_input = 0.0
	if not grounded:
		pitch_input -= Input.get_action_strength("pitch_up")
	if forward_speed >= min_flight_speed:
		pitch_input += Input.get_action_strength("pitch_down")

func _physics_process(delta: float) -> void:
	get_input(delta)

	if grounded:
		plane_mesh.rotation.z = 0.0
		$CollisionShape3D.rotation.z = 0.0
	elif engines:
		plane_mesh.rotation.z = move_toward(plane_mesh.rotation.z, turn_input, level_speed * delta)
		$CollisionShape3D.rotation.z = move_toward($CollisionShape3D.rotation.z, turn_input, level_speed * delta)

	if engines:
		transform.basis = transform.basis.rotated(transform.basis.x, pitch_input * pitch_speed * delta)
		transform.basis = transform.basis.rotated(Vector3.UP, turn_input * turn_speed * delta)
		# Accelerate/decelerate
		forward_speed = lerp(forward_speed, target_speed, accelertation * delta)
		# Movement is always forward
		velocity = -transform.basis.z * forward_speed
	else:
		if not is_on_floor():
			velocity.y = min(velocity.y - 1 * delta, 20)
			rotation_degrees.x = lerp(rotation_degrees.x, -90.0, 0.3 * delta)
		else:
			rotation.x = lerp(rotation.x, 0.0, 0.3)

	if is_on_floor():
		if not grounded:
			if rotation.x > deg_to_rad(-30) and rotation.x < deg_to_rad(30):
				rotation.x = 0.0
			else:
				engines = false
				target_speed = 0.0
		velocity.y -= 1
		grounded = true
	else:
		grounded = false

	if global_position.y < pull_ocean.global_position.y + 1:
		if global_position.y < pull_ocean.global_position.y - 5 and rotation_degrees.x < -20:
			get_tree().reload_current_scene()
		else:
			rotation_degrees.x = 0
			global_position.y = pull_ocean.global_position.y + 5

	move_and_slide()


func _on_engine_start_timeout() -> void:
	animation.current_animation = "spin"
	engines = true

What I would like to do, and some of my code currently tries to emulate sorta, is have the CharacterBody3D behave like a RigidBody3D, and kinda just fall or bounce around when the plane crashes into something. My first thought is to possibly swap out the CharacterBody3D with an identical RigidBody3D node, but I worry how seamless I can make that, and wonder if there is a better way of going about this without having to reinvent the physics wheel in the CharacterBody3D code.

Hope all of this makes sense.

Thanks!

I understand your concern. I would definitely just swap the player with the rigidbody. I think it could be very simple. Just move you velocity and torque to the body before adding it to the scene.

I have done something similar to what your describing in my code and it worked decently well for me so I think you should go for it

1 Like

To be clear what do you mean by “behave like a rigid body 3d”
Do you want the body to have no gravity or is there something else?


I tried tracking the player’s position with AnimatableBody3D, but the collision is lagging, sometimes RigidBody3D doesn’t move and RigidBody3D can’t collide the player

Sorry for such a late reply. Life got busy for me and I forgot I made this post. I ended up scrapping the need for this in my game, so I never found a definitive solution.

But to be more clear on what I meant, to my understanding a RigidBody3D moves according to a kind of built in physics engine without explicitly having to program your own movement. For example, if you created a ball, and put it on a decline, when the game runs the RigidBody would naturally roll down the hill without having to program that movement. Or if, in my case, a plane were to have it’s engine stop, it would not only fall according to gravity, but would bounce off of objects that it hits. If it’s wing hit a building, then it would apply force to that wing which would rotate the plane to some extent.

I hope all of this makes sense. This was the effect I wanted. I was going for having a plane bounce around with physics if it crashes, but instead decide to handle crashing a different way.

Not sure what to do with this post, but thank you everybody for the help.