Everything I've ever made so far has been same state, that is, walking everywhere. This time around, I'm looking to use at-will vehicles. Jump on a horse or grab a canoe and go. Whenever and wherever you find a horse or canoe. I'm really not sure how to implement this.

I have everything set up as enums for the different transportation states, I have a visual map of how things should go (can only dive from a swim state, can't sneak while on a horse, etc.), and I use signals for each state.

extends KinematicBody

### CHECK THE MODE OF TRANSPORTATION ###
enum {
	WALK,
	SNEAK,
	HORSE,
	CANOE,
	SWIM,
	DIVE
}
var move_state = WALK
## DEFAULT MOVEMENT SPEEDS ETC. ###
export var speed = 5
export var speed_rotation : float = 50
export var fall_acceleration = 75

var velocity = Vector3.ZERO

signal walking
signal sneaking
signal riding
signal paddling
signal swimming
signal diving

### GET THE ANIMATIONS ###
onready var animationPlayer = $kiyuga/AnimationPlayer

### MAKE IT ALL WORK SOMEHOW ###
func _process(delta):
	match move_state:
		WALK:
			walk_state(delta)
		SNEAK:
			sneak_state(delta)
		HORSE:
			horse_state(delta)
		CANOE:
			canoe_state(delta)
		SWIM:
			swim_state(delta)
		DIVE:
			dive_state(delta)

### ALL LAND TRANSPORTATION STATES HAVE THE ABILITY TO USE RANGED WEAPONS, BEAD ABILITIES, AND CONSUMABLES ###
### CAMERA CONTROL IS ALSO AVAILABLE TO ALL STATES ###

### REGULAR WALK STATE, FOR EXPLORING AND SUCH ###
### CONTROLS INCLUDE HEAVY ATTACK, ATTACK, BEAD, CONSUMABLE, INTERACT, AND DODGE ###
### THESE ARE ALL THE STANDARD, DEFAULT ANIMATIONS ###
### DETECTIONS ARE NORMAL ###
func walk_state(delta):
	var direction = Vector3.ZERO

	if Input.is_action_pressed("move_right"):
		rotation_degrees.y -= speed_rotation * delta
	if Input.is_action_pressed("move_left"):
		rotation_degrees.y += speed_rotation * delta
	if Input.is_action_pressed("move_forward"):
		direction += transform.basis.z
	if Input.is_action_pressed("move_backward"):
		direction -= transform.basis.z
		animationPlayer.play_backwards("Walk1")
	if direction != Vector3.ZERO:
		direction = direction.normalized()
		animationPlayer.play("Walk1")
	if direction == Vector3.ZERO:
		animationPlayer.play("KiyugaIdle")

	velocity.x = direction.x * speed
	velocity.z = direction.z * speed
	velocity.y -= fall_acceleration * delta
	velocity = move_and_slide(velocity, Vector3.UP)

### FROM A WALK STATE, PLAYER CAN TRANSITION TO SNEAK, HORSE, OR CANOE ###
### THERE ARE NO RESTRICTIONS ###
	if Input.is_action_just_released("move_sneak"):
		emit_signal("sneaking")
		move_state = SNEAK

I'm just not sure how to functionally implement it. Should my horse/canoe be a Rigid Body or Kinematic (or a Rigid in Kinematic mode)? Do I animate the bodies separately and just try really, really hard to get them to sync up? Since I would like to add Horse HP and Canoe Integrity, how would I get them to respawn after X amount of time so that I don't run out of horses or canoes? I feel like the canoe won't be too difficult, but the horse is a little intimidating.

  • 	if Input.is_action_just_pressed("E") and canEnterDriverSeat == true:
    		vehicleDriverArea.ActivateVehicleCamera()
    		queue_free()

    Very simple code for entering the vehicle, just make sure you delete the player and keep track of where the vehicle is when you press a button again, you don't get rid of the vehicle when exiting obviously because presumably you want that to be there to use again, the cooldown timer is just to prevent any weirdness happening if a player button mashes.

    	if Input.is_action_just_pressed("E") and isPlayerInVehicle == true and exitVehicleCooldownTimer <= 0.0:
    		DeactivateVehicleCamera()
    		exitVehicleCooldownTimer = 1.0
    extends Area3D
    
    var vehicleBody = null
    
    func _on_VehicleDriverArea_body_entered(body):
    	if body.is_in_group("Player"):
    		body.canEnterDriverSeat = true
    		body.vehicleDriverArea = self
    		vehicleBody.set_process(true)
    
    
    func _on_VehicleDriverArea_body_exited(body):
    	if body.is_in_group("Player"):
    		body.canEnterDriverSeat = false
    		body.vehicleDriverArea = null
    		vehicleBody.set_process(false)
    		
    		
    func ActivateVehicleCamera():
    	vehicleBody.ActivateVehicleCamera()
    func ActivateVehicleCamera():
    	isScriptEnabled = true
    	isPlayerInVehicle = true
    	vehicleCameraRotate.isScriptEnabled = true
    	vehicleCamera.make_current()
    	set_process(true)
    	
    func DeactivateVehicleCamera():
    	isScriptEnabled = false
    	isPlayerInVehicle = false
    	vehicleCameraRotate.isScriptEnabled = false
    	var localPlayer = player.instantiate()
    	get_tree().get_root().add_child(localPlayer)
    	localPlayer.global_transform.origin = exitPointDriverSeat.global_transform.origin
    	set_process(false)

    Area nodes that have built in signals are extremely useful for what you want to do, combine that with some sensible boolean checks and you're good to go, after that it's just a matter of disabling and enabling the right scripts and cameras. I regularly use the built in nodes combined with custom functions these days because it's just so damn optimised for gaming compared to constantly fiddling with code yourself and making it behave the way you want every frame.

    As for your questions about the actual animation of getting in and out, devs have multiple approaches to this, it's a bit of a trend sometimes with indie devs to not even bother with animating at all and most gamers don't seem to really care. Fornite's of course a very successful and famous game and they still haven't really bothered with putting in animations for getting in and out of vehicles but yes that's mostly about timing which is also why you'd want a cooldown timer in there to sync up to the animation as they're getting in the vehicle so nothing weird happens.

    Note: I'm a fan of interactive vehicles as well which is why I posted 😃 you could also use this code for all sorts of stuff like turrets and other things, the magic is just knowing how to disable/enable your various nodes and scripts properly.

	if Input.is_action_just_pressed("E") and canEnterDriverSeat == true:
		vehicleDriverArea.ActivateVehicleCamera()
		queue_free()

Very simple code for entering the vehicle, just make sure you delete the player and keep track of where the vehicle is when you press a button again, you don't get rid of the vehicle when exiting obviously because presumably you want that to be there to use again, the cooldown timer is just to prevent any weirdness happening if a player button mashes.

	if Input.is_action_just_pressed("E") and isPlayerInVehicle == true and exitVehicleCooldownTimer <= 0.0:
		DeactivateVehicleCamera()
		exitVehicleCooldownTimer = 1.0
extends Area3D

var vehicleBody = null

func _on_VehicleDriverArea_body_entered(body):
	if body.is_in_group("Player"):
		body.canEnterDriverSeat = true
		body.vehicleDriverArea = self
		vehicleBody.set_process(true)


func _on_VehicleDriverArea_body_exited(body):
	if body.is_in_group("Player"):
		body.canEnterDriverSeat = false
		body.vehicleDriverArea = null
		vehicleBody.set_process(false)
		
		
func ActivateVehicleCamera():
	vehicleBody.ActivateVehicleCamera()
func ActivateVehicleCamera():
	isScriptEnabled = true
	isPlayerInVehicle = true
	vehicleCameraRotate.isScriptEnabled = true
	vehicleCamera.make_current()
	set_process(true)
	
func DeactivateVehicleCamera():
	isScriptEnabled = false
	isPlayerInVehicle = false
	vehicleCameraRotate.isScriptEnabled = false
	var localPlayer = player.instantiate()
	get_tree().get_root().add_child(localPlayer)
	localPlayer.global_transform.origin = exitPointDriverSeat.global_transform.origin
	set_process(false)

Area nodes that have built in signals are extremely useful for what you want to do, combine that with some sensible boolean checks and you're good to go, after that it's just a matter of disabling and enabling the right scripts and cameras. I regularly use the built in nodes combined with custom functions these days because it's just so damn optimised for gaming compared to constantly fiddling with code yourself and making it behave the way you want every frame.

As for your questions about the actual animation of getting in and out, devs have multiple approaches to this, it's a bit of a trend sometimes with indie devs to not even bother with animating at all and most gamers don't seem to really care. Fornite's of course a very successful and famous game and they still haven't really bothered with putting in animations for getting in and out of vehicles but yes that's mostly about timing which is also why you'd want a cooldown timer in there to sync up to the animation as they're getting in the vehicle so nothing weird happens.

Note: I'm a fan of interactive vehicles as well which is why I posted 😃 you could also use this code for all sorts of stuff like turrets and other things, the magic is just knowing how to disable/enable your various nodes and scripts properly.

    Lethn

    The cooldown timer actually makes a lot of sense; that will probably save me a lot of headache later.

      abizrihays It's simple but sometimes for the sake of preventing any execution order mishaps having even a 0.01 second timer makes all the difference and the gamer will barely notice it while playing.