Vehicle mode for FPS games with Godot
Tags: programming, showdev, gamedev, todayilearned
Published: 2021-05-14 16:56:48

---
title: Vehicle mode for FPS games with Godot
published: true
description: 
tags: programming, showdev, gamedev, todayilearned
cover_image: https://mednikov.me/wp-content/uploads/2021/05/pexels-vazhnik-7870809-480x320.jpg
canonical_url: https://mednikov.me/vehicle-mode-for-fps-games-in-godot/
---

From all FPS games, my favorite titles are those with vehicle mode, like Battlefield series. Therefore, when I started to work on my own FPS prototype with Godot Engine, I decided to introduce something similar. Surely, I do not pretend to create a complete simulation of battle vehicles; only essential functionality with primitive arcade-like physics. That is how I ended with the following solution. I am still on very early stages in game development, so I admit that my idea is not perfect and maybe I brought concepts from web development, that should be avoided in game programming. However, it is a working concept, and due to the fact, that existing Godot tutorials on vehicle mode are not complete, let me present you my own.

## Player vs. vehicle mode

The first thing first is to distinguish between two main game modes: player mode (this is a first-person mode, where you control the character) and [vehicle mode](https://mednikov.me/vehicle-mode-for-fps-games-in-godot/) (this is a third-person view, where vehicle is controlled). What I found out during my research, that some Godot tutorials treat both modes as a single and just switch cameras views. I don't think it is correct at all, as the whole idea of a vehicle mode in the game like the mentioned Battlefield is to be able to _pick up_ a vehicle. Therefore, if you "carry" vehicle always with player you can't do embark/disembark and also it will be complicated to introduce more than one vehicle. I even don't mention different type of vehicles that can be available in your game - ground vehicles, air vehicles, ships (ships?) and so on.

In my prototype I have a quite naive first person controller, that is placed inside the __Player.gd__ script. This component can move around, rotate camera, fire etc - in other words it is responsible for the player mode. There is a state variable __is_active__, which defines, if the mode is active. If it is not active, the controller will not work. Take a look on the __physics_process()__ implementation:

```gdscript
func _physics_process(delta):
	
	if is_active:
	
		var direction = Vector3()
		var head_basis = head.global_transform.basis
		
		if Input.is_action_pressed("move_forward"):
			direction -= head_basis.z
		elif Input.is_action_pressed("move_backward"):
			direction += head_basis.z
		
		if Input.is_action_pressed("move_left"):
			direction -= head_basis.x
		elif Input.is_action_pressed("move_right"):
			direction += head_basis.x

		direction = direction.normalized()
		
		# gravity
		apply_gravity()
		
		# switch weapons
		switch_weapons()
		
		if Input.is_action_just_pressed("primary_fire"):
			if active_weapon.is_possible_shoot:
				active_weapon.shoot()
				
		if Input.is_action_pressed("reload"):
			active_weapon.reload()
		
		# switch weapons with two buttons (to test gamepad without gamepad):
		if Input.is_action_just_pressed("weapon_switch_up"):
			weapon_switch_up()
		elif Input.is_action_just_pressed("weapon_switch_down"):
			weapon_switch_down()
		
		# jump
		jump()
		
		var speed = run_speed if Input.is_action_pressed("move_run") else normal_speed
		
		velocity = velocity.linear_interpolate(direction * speed, acceleration * delta)
		
		velocity = move_and_slide(velocity, Vector3.UP)
		
		update_ui()
```

When the value of the variable __is_active__ is __false__, this code snippet will not be executed. I introduced two other helper methods, to trigger the player mode, e.g. to enable it, when the player disembarks a vehicle and to disable it, when the player is inside a vehicle. These two methods are called __enable_player()__ and __disable_player()__ respectively. What they do is to turn on or to turn off following aspects of the player controller:

- The value of the __is_active__ variable
- Visibility of the player controller
- Enabling/disenabling of the collision body
- First person camera
- Interaction raycast (we will talk about it later on)

My sample implementations of these methods are presented below:

```gdscript
func enable_player():
	is_active = true
	visible = true
	camera.current = true
	interaction_raycast.enable()
	collision_shape.disabled = false


func disable_player():
	is_active = false
	visible = false
	camera.current = false
	interaction_raycast.disable()
	collision_shape.disabled = true
```

On the other hand I have the __Vehicle.gd__ class which serves as a parent class for all vehicle types. It is responsible for the vehicle mode. There are two helper methods, that are used to trigger the mode: __use_vehicle()__ and __drop_vehicle()__. The vehicle entity also has it is own variable to keep an active stay - __is_activated__ Also these methods are used to switch camera and to notify the player controller to enable or disable the player mode.

Take a look on the sample implementation below:

```gdscript
func use_vehicle():
	print('Use vehicle')
	is_activated = true
	camera.current = true
	do_on_embark()
	get_tree().call_group('Player', 'disable_player')
	get_tree().call_group('UI', 'activate_vehicle_ui', true)


func drop_vehicle():
	print('Drop vehicle')
	is_activated = false
	camera.current = false
	do_on_disembark()
	get_tree().call_group('Player', 'enable_player')
	get_tree().call_group('UI', 'activate_vehicle_ui', false)
```

You may notice here two _callback_ methods __do_on_embark()__ and __do_on_disembark()__. We will return to them later. Before, I would like to talk about how to interact with a vehicle.

## How to embark and to disembark

In the previous subsection, I mentioned an interaction raycast. This component is used by the player controller to interact with various game objects, such as doors, locks, light switches etc. It is decomposed from the main __Player.gd__ script and is placed into the __Interaction.gd__ class. By default, the idea is following: when a player interacts with an interactable object, a hint is shown and a player can use that object. For vehicle it means to embark.

![](https://mednikov.me/wp-content/uploads/2021/05/godot_vehiclemode_interact_with_car-1024x576.png)

So, we can embark the car by pressing the E button. In this situation we call the __use_vehicle__ method to activate the vehicle mode. This works as following:

```gdscript
func _process(delta):
	var collider = get_collider()
	
	if is_colliding():
		if current_collider != collider:
			current_collider = collider
			if collider is Vehicle:
				var text = 'use vehicle'
				set_interaction_text(text)
		
		if Input.is_action_just_pressed('interact'):
			if collider is Vehicle:
				var vehicle = collider as Vehicle
				vehicle.use_vehicle()
```

Please note, that this is short form of the original code, because it also deals with __Interactable__ objects, that are out of the scope of this post; so I limited it to interactions with __Vehicle__ only. With all this logic we now can interact with vehicles and switch between two game modes.

![](https://mednikov.me/wp-content/uploads/2021/05/godot_vehiclemode_embark_and_disemebark.gif)

I have mentioned already what I call callback functions. Being mainly web developer, I took that idea from there, so I admit it may be not good for video games. Because we have a parent class __Vehicle__ that encapsulates core embark/disembark functionality, we may need to do something when __use_vehicle__ and __drop_vehicle__ functions are called without overriding them. That way I introduced two callbacks __do_on_embark__ and __do_on_disembark__, where concrete implementations can put own logic without overriding of parent implementations. There is another callback __do_movement__, but this is a separate story, that we will discover in the next subsection.

## How vehicles do move

Same way as with the first person controller, we have the variable that keeps if the current state is active. For vehicles it is called __is_activated__ and is placed inside the parent class. It is also utilized in the __physics_process()__ function that listens game updates. Once the vehicle mode is activated, the function will call the __do_movement__ callback, which is used to move the vehicle. As there are different types, it can't be implemented in the core class.

```gdscript
func _physics_process(delta):
	if is_activated:
		if Input.is_action_just_pressed("interact"):
			drop_vehicle()
		do_movement(delta)
```

My sandbox project, that you can find on github, contains a sample vehicle implementation - a police car. It is not a perfect simulation of a car behaviour (if at all), and I took concrete logic from [this wonderful kinematic car tutorial](https://kidscancode.org/godot_recipes/3d/kinematic_car/car_base/) from KidsCanCode. You can find explanations there, because I could not copy-paste it and claim that the solution is mine, of course, it is not. What I want to show you instead, is how it is organized.

![](https://mednikov.me/wp-content/uploads/2021/05/godot_vehiclemode_carmode-1024x576.png)

All moving logic of the police car is placed inside the overriden __do_movement__ callback. Due to the fact, that the parent __Vehicle__ class passes the delta value to the callback, we can use it to perform movements of vehicles. For my police car, I did it as following:

```gdscript
func do_movement(delta):
	if is_on_floor():
		handle_input()
		handle_friction(delta)
		handle_steering(delta)
	car_acceleration.y = gravity
	velocity += car_acceleration * delta
	velocity = move_and_slide_with_snap(velocity, -transform.basis.y, Vector3.UP, true)
```

The whole movement implementation is taken from the KidsCanCode, so I would not stop on it. What is crucial here, is that we can use __do_movement__ callback to implement moving of any type of vehicles; not limited to cars, but also helicopters, aircrafts, space ships etc.

## Source code

This post is based on my FPS sandbox project. It available in [this github repository](https://github.com/realyurimednikov/fps-sandbox) and has much more, than just the vehicle mode. Feel free to fork it and experiment with it. If you have questions or suggestions or any valuable feedback, don't hesitate to [contact me](https://mednikov.me/contacts/) or drop a comment.

## Conclusion

That is the way I used to implement the [vehicle mode for FPS game with Godot](https://mednikov.me/vehicle-mode-for-fps-games-in-godot/). I admit that is not the best one, and I excited to listen your solutions, admitting the fact, that I am quite new to Godot Engine and game dev in general. However, this code does its job and provides the complete experience - both interaction and movements and it separates two modes, and provides a possibility to extend the original functionality for any type of vehicles.