I have a characterbody3d that acts as a spaceship that I have temporarily given movement using the keyboard and then I have a characterbody3d that acts as a player that I have placed inside the ship, but outside the ship node. When the ship moves, the player should stay still because it is floating inside it, but when it collides with a wall of the ship (I have colliders3d for that), it should collide and follow the movement of the ship. What is happening to me now is that the ship stops completely and the player stays completely still. I'm not very good at this, I'm about 5 hours into godot and maybe I'm using a characterbody3d for the ship when it should be something else or maybe there's no way to do what I say automatically and I need to program something to do it. such an effect, I don't know. I use godot 4 and my question is more to see how I approach this to obtain the desired result. If you need it, I can pass the project on to you since it is just a test, nothing more.

I have tried several things both in code and touching the properties of the objects and the colliders, but I can't get anything

This is my node structure:

  • Node3D
    • Ship (Characterbody3D)
      • Mesh
      • Collider1 (represents one of the walls of the ship)
      • Collider2 (represents one of the walls of the ship)
      • Collider3 (represents one of the walls of the ship)
      • Collider4 (represents one of the walls of the ship)
    • Player (Characterbody3D)
      • Camera3D
      • Collider1 (Capsule3D)

This is the code associated with the characterbody3d that acts as a ship:

extends CharacterBody3D

@export var max_speed = 50.0 # FIXME velocidad de la luz 99%
@export var acceleration = 0.6
@export var pitch_speed = 1.5
@export var roll_speed = 1.9
@export var yaw_speed = 1.25  # Set lower for linked roll/yaw
@export var input_response = 8.0

var horizontal_speed = 0.0
var vertical_speed = 0.0
var forward_speed = 0.0
var pitch_input = 0.0
var roll_input = 0.0
var yaw_input = 0.0

func get_input(delta):
    if Input.is_action_pressed("STRAFE_RIGHT"):
        horizontal_speed = lerp(horizontal_speed, max_speed, acceleration * delta)
    if Input.is_action_pressed("STRAFE_LEFT"):
        horizontal_speed = lerp(horizontal_speed, -max_speed, acceleration * delta)
    if Input.is_action_pressed("STRAFE_FRONT"):
        forward_speed = lerp(forward_speed, max_speed, acceleration * delta)
    if Input.is_action_pressed("STRAFE_BACK"):
        forward_speed = lerp(forward_speed, -max_speed, acceleration * delta)
    if Input.is_action_pressed("STRAFE_UP"):
        vertical_speed = lerp(vertical_speed, max_speed, acceleration * delta)
    if Input.is_action_pressed("STRAFE_DOWN"):
        vertical_speed = lerp(vertical_speed, -max_speed, acceleration * delta)

    if Input.is_action_pressed("ROTATE_LEFT"):
        yaw_input = lerp(yaw_input, yaw_speed, acceleration * delta)
    if Input.is_action_pressed("ROTATE_RIGHT"):
        yaw_input = lerp(yaw_input, -yaw_speed, acceleration * delta)
    if Input.is_action_pressed("ROTATE_UP"):
        roll_input = lerp(roll_input, roll_speed, acceleration * delta)
    if Input.is_action_pressed("ROTATE_DOWN"):
        roll_input = lerp(roll_input, -roll_speed, acceleration * delta)
    if Input.is_action_pressed("TURN_LEFT"):
        pitch_input = lerp(pitch_input, pitch_speed, acceleration * delta)
    if Input.is_action_pressed("TURN_RIGHT"):
        pitch_input = lerp(pitch_input, -pitch_speed, acceleration * delta)

    #pitch_input = lerp(pitch_input, Input.get_axis("pitch_down", "pitch_up"), input_response * delta)
    #roll_input = lerp(roll_input, Input.get_axis("roll_right", "roll_left"), input_response * delta)
    # yaw_input = lerp(yaw_input, Input.get_axis("yaw_right", "yaw_left"), input_response * delta)
    #yaw_input = roll_input
    # TODO recoger también las pulsaciones directas con el ratón en los botones

func _physics_process(delta):
    transform.basis = transform.basis.rotated(transform.basis.z, roll_input * roll_speed * delta)
    transform.basis = transform.basis.rotated(transform.basis.x, pitch_input * pitch_speed * delta)
    transform.basis = transform.basis.rotated(transform.basis.y, yaw_input * yaw_speed * delta)
    transform.basis = transform.basis.orthonormalized()

    velocity = (-transform.basis.z * horizontal_speed) + (-transform.basis.x * forward_speed) + (transform.basis.y * vertical_speed)
    #move_and_collide(velocity * delta)
    #if move_and_collide(velocity * delta):
    #   for i in get_slide_collision_count():
    #       var col = get_slide_collision(i)
    #       if col.get_collider() is CharacterBody3D:
    #           col.get_collider().apply_force(col.get_normal() * 1000)

And this is the one with the player who has a camera with the script:

extends Camera3D

const ray_length = 1000

#@export var pushForce = 500
#@export var bodyPath: NodePath = ".."
#@onready var body: CharacterBody3D = get_node(bodyPath)

var v = Vector3()

func _ready():

func _process(delta):

func _input(event):
    if event is InputEventMouseMotion:
        v.y -= (event.relative.x * SENSITIVITY)
        v.x -= (event.relative.y * SENSITIVITY)
        v.x = clamp(v.x, -80, 90)
    #if event is InputEventMouseButton:
    #   if event.button_index == MOUSE_BUTTON_LEFT:
    #       if event.pressed:
    #           var camera = $Camera3D
    #           var from = camera.project_ray_origin(event.position)
    #           var to = from + camera.project_ray_normal(event.position) * ray_length
    #           print("Hit at point: ", to.position)

func _physics_process(delta):
    rotation_degrees.x = v.x
    rotation_degrees.y = v.y
    #if body.move_and_slide(): # true if collided
    #   for i in body.get_slide_collision_count():
    #       var col = body.get_slide_collision(i)
    #       if col.get_collider() is CharacterBody3D:
                #col.get_collider().apply_force(col.get_normal() * -pushForce)
                #body.velocity(col.get_normal() * -pushForce)
  • \$\begingroup\$ Does this answer your question? Stay Upright Within Rotating Area - Edit: I know the title does not help, but the question is also about moving inside an spaceship. \$\endgroup\$
    – Theraot
    Oct 6, 2023 at 10:48
  • \$\begingroup\$ Thank you for your comment, but it is not what I need, because in my case there is no gravity, the player is floating inside the spaceship and must be still if it moves, it should only move when it collides with one of the walls of the spaceship. \$\endgroup\$
    – track3r
    Oct 6, 2023 at 11:38
  • \$\begingroup\$ I think even in that case, I'd be inclined to simulate physics within a frame of reference that moves with the ship. In that frame, the ship is stationary. The drifting of free-moving objects within the ship is then affected by "fictitious forces" like the centripetal and coriolis forces you feel in a rotating frame of reference. Each time the ship "moves", you compute the perceived forces for objects inside the ship and apply them to each object. This cuts down on how many dynamic bodies and dynamic-dynamic collisions you need to process, for better performance, and leads to fewer glitches. \$\endgroup\$
    – DMGregory
    Oct 6, 2023 at 16:37
  • \$\begingroup\$ This is not the case, keep in mind that there is no gravity inside the spaceship and the objects inside will only move when they collide with the walls of the ship or other objects. \$\endgroup\$
    – track3r
    Oct 7, 2023 at 4:51

1 Answer 1


The main issue is this: You are responsible for moving the Characterbody3D.

This means that - barring an AnimatableBody3D serving as a moving platform※ - no interaction with another body will move the CharacterBody3D. In fact, nothing will push the CharacterBody3D, unless you write code for that.

※: In Godot 3 KinematicBody was the recommended way to make a moving platform, so yes a KinematicBody could ride on another. In Godot 4 AnimatableBody3D is the recommended way to make a moving platform. This means that Making the ship a AnimatableBody3D might be a solution.

This is good, in particular for beginners, because it means that at no point you will be fighting the physics engines. That is, at no point you will be trying to move the CharacterBody3D one way, when the physics engine wants to move it another way.

The basic physic bodies work like this:

  • StaticBody3D: It does not move.
  • CharacterBody3D: You move it.
  • RigidBody3D: The physics engine moves it.

Of course this is a simplification.

Thus, picking between a Characterbody3D and RigidBody3D for a player character ultimately boils down to this question: can other things push it?

And in this case, it seems the ship should push the player character, thus implementing the player character as a RigidBody3D is a solution.

With that said, it does not mean we cannot script the ship pushing the player character. And looking at your commented code it seems you were on the right track...

Thus scripting the movement of the CharacterBody3D is a solution.

No, a CharacterBody3D does not have apply_force, those are RigidBody3D. And no, velocity is not a method.

You want something like this:

for i in get_slide_collision_count():
    var collision := get_slide_collision(i)
    var character := collision.get_collider() as CharacterBody3D
    if is_instance_valid(character):
        character.velocity = velocity.project(collision.get_normal())

I'm not sure this is the desired behavior, but I expect that adding this code to the ship results in it "pushing" the player character, and hopefully you can tweak it from there.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .