Godot Version
4.7.1
Question
I’m working on a 3D survival game where the player travels between islands by piloting a sky ship.
The world is infinite and the ship is customizable. Very similar to the game Raft, but in the air, and the islands/levels should persist.
The player can jump to other ships, glide and climb. These traversal options are important because they are a fundamental aspect of aerial combat.
The main issues are:
-
Handling movement inside a ship (There is technically no “inside” or “outside”. The transition must be seamless, so using different scenes wouldn’t work that well).
-
Mitigating floating point errors (Because the map is infinite, the player will encounter them at some point)
-
Landing at different places should also be smooth
Real Moving Ships and Origin Rebasing
I was thinking of making the ships actually move and when the player’s position exceeds a threshold, shift the map back to the origin.
I would have to keep track of the “real position” of the player by using floats (they’re 64 bit by default) and not Vector3s and pass that to the chunks system, instead of the node position.
This is more flexible and intuitive. But the issues with moving ships remain.
I’ve tried making ships an AnimatableBody3D and moving them in code:
class_name Ship
extends AnimatableBody3D
@export var speed : float = 20.0
func _physics_process(delta : float) -> void:
move_forward(delta)
func move_forward(delta : float) -> void:
var forward := global_basis.z
var velocity := forward * speed
global_position += velocity * delta
It makes the player inherit the ship’s velocity automatically and works well for most cases: Such as walking and jumping. However, there are still a lot of side effects.
Climbing doesn’t work, because CharacterBodies only apply the velocity of the AnimatableBody if it’s the floor, not a wall. I was able to fix this to an extent by applying the AnimatableBody’s velocity to the player.
But it’s a partial fix. Climbing itself does work, but during the first frames, it’s possible for the ship to move so fast, that it makes the player detach quickly and stop colliding with the wall.
Same with vaulting, taking cover or other states that use ray casts to make the player “snap” to a position, but that position was intended to be static. The state finds a position, but by the time the character reaches that position, the ship has already left it behind, even if it only takes a few ms.
Foot placement IK breaks because the difference between the previous and current position changes quickly and drastically.
Rigid bodies break at high speeds. They remain on the ship, but interaction with the player stops working.
And ragdolls break partially too. Because I check whether the velocity of the hips bone is stationary in order to make a character stand back up. But this is never true because the velocity of the hips bone is always moving.
Some of these issues would be somewhat easy to fix and some could be pretty challenging (Mainly the ones that involve a position being “left behind”, as it’s inherent to moving ships).
So it makes me wonder if I should go for a “simpler” solution:
Ships that don’t actually move. The world does
Another option is to not actually move any ships and move the world around them. This gets rid of all traversal and floating point issues, but things like landing at a specific island or ships with different velocities become harder. It is less flexible in case I wanted to change how ships move and I don’t know how efficient it is to move the entire map every frame. It might introduce issues with lighting, physics or jitter.
Reparenting the player to the ship
I thought this would be the most reliable option, but once I tried it, I found it to be the worst one. It doesn’t fix any of the issues and the player’s movement seems to be constantly “fighting” the ship’s movement regardless of whether the Ship is a CharacterBody3D or AnimatableBody3D.
I would really appreciate it if someone could guide me in the right direction. Thanks!