Skip to content

Commit 7de0cdc

Browse files
committed
Refactor camera so it can be included in the Player scene
I changed the README accordingly
1 parent 27f348a commit 7de0cdc

File tree

7 files changed

+59
-58
lines changed

7 files changed

+59
-58
lines changed

Game.tscn

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
[gd_scene load_steps=11 format=2]
1+
[gd_scene load_steps=10 format=2]
22

3-
[ext_resource path="res://src/Camera/Camera.tscn" type="PackedScene" id=1]
43
[ext_resource path="res://src/Player/Player.tscn" type="PackedScene" id=2]
54

65
[sub_resource type="SpatialMaterial" id=1]
@@ -32,7 +31,6 @@ points = PoolVector3Array( -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1,
3231
[node name="TestMap" type="Spatial" parent="."]
3332

3433
[node name="Ground" type="MeshInstance" parent="TestMap"]
35-
editor/display_folded = true
3634
mesh = SubResource( 2 )
3735
material/0 = null
3836

@@ -42,10 +40,8 @@ material/0 = null
4240
shape = SubResource( 3 )
4341

4442
[node name="Obstacles" type="Spatial" parent="TestMap"]
45-
editor/display_folded = true
4643

4744
[node name="MeshInstance" type="MeshInstance" parent="TestMap/Obstacles"]
48-
editor/display_folded = true
4945
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -7, 0, -8 )
5046
mesh = SubResource( 4 )
5147
material/0 = SubResource( 5 )
@@ -56,7 +52,6 @@ material/0 = SubResource( 5 )
5652
shape = SubResource( 6 )
5753

5854
[node name="MeshInstance4" type="MeshInstance" parent="TestMap/Obstacles"]
59-
editor/display_folded = true
6055
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 9, 0, -9 )
6156
mesh = SubResource( 4 )
6257
material/0 = SubResource( 5 )
@@ -67,7 +62,6 @@ material/0 = SubResource( 5 )
6762
shape = SubResource( 6 )
6863

6964
[node name="MeshInstance5" type="MeshInstance" parent="TestMap/Obstacles"]
70-
editor/display_folded = true
7165
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 0, -2 )
7266
mesh = SubResource( 4 )
7367
material/0 = SubResource( 5 )
@@ -78,7 +72,6 @@ material/0 = SubResource( 5 )
7872
shape = SubResource( 6 )
7973

8074
[node name="MeshInstance6" type="MeshInstance" parent="TestMap/Obstacles"]
81-
editor/display_folded = true
8275
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 1, -2 )
8376
mesh = SubResource( 4 )
8477
material/0 = SubResource( 5 )
@@ -89,7 +82,6 @@ material/0 = SubResource( 5 )
8982
shape = SubResource( 6 )
9083

9184
[node name="MeshInstance7" type="MeshInstance" parent="TestMap/Obstacles"]
92-
editor/display_folded = true
9385
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 1, -4 )
9486
mesh = SubResource( 4 )
9587
material/0 = SubResource( 5 )
@@ -100,7 +92,6 @@ material/0 = SubResource( 5 )
10092
shape = SubResource( 6 )
10193

10294
[node name="MeshInstance8" type="MeshInstance" parent="TestMap/Obstacles"]
103-
editor/display_folded = true
10495
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 2, -6 )
10596
mesh = SubResource( 4 )
10697
material/0 = SubResource( 5 )
@@ -111,7 +102,6 @@ material/0 = SubResource( 5 )
111102
shape = SubResource( 6 )
112103

113104
[node name="MeshInstance9" type="MeshInstance" parent="TestMap/Obstacles"]
114-
editor/display_folded = true
115105
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 3.99933, -6 )
116106
mesh = SubResource( 4 )
117107
material/0 = SubResource( 5 )
@@ -122,7 +112,6 @@ material/0 = SubResource( 5 )
122112
shape = SubResource( 6 )
123113

124114
[node name="MeshInstance10" type="MeshInstance" parent="TestMap/Obstacles"]
125-
editor/display_folded = true
126115
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 11.8311, -6 )
127116
mesh = SubResource( 4 )
128117
material/0 = SubResource( 5 )
@@ -133,7 +122,6 @@ material/0 = SubResource( 5 )
133122
shape = SubResource( 6 )
134123

135124
[node name="MeshInstance11" type="MeshInstance" parent="TestMap/Obstacles"]
136-
editor/display_folded = true
137125
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 3.9415, 8.03339, -6 )
138126
mesh = SubResource( 4 )
139127
material/0 = SubResource( 5 )
@@ -144,7 +132,6 @@ material/0 = SubResource( 5 )
144132
shape = SubResource( 6 )
145133

146134
[node name="MeshInstance2" type="MeshInstance" parent="TestMap/Obstacles"]
147-
editor/display_folded = true
148135
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -4, 1, -2 )
149136
mesh = SubResource( 4 )
150137
material/0 = SubResource( 5 )
@@ -155,7 +142,6 @@ material/0 = SubResource( 5 )
155142
shape = SubResource( 7 )
156143

157144
[node name="MeshInstance3" type="MeshInstance" parent="TestMap/Obstacles"]
158-
editor/display_folded = true
159145
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 9, 1, -11 )
160146
mesh = SubResource( 4 )
161147
material/0 = SubResource( 5 )
@@ -169,7 +155,4 @@ shape = SubResource( 8 )
169155
transform = Transform( 0.766044, 0.166366, -0.620885, 0.271654, 0.791635, 0.547283, 0.582563, -0.587909, 0.561234, 0, 12, 0 )
170156
shadow_enabled = true
171157

172-
[node name="CameraAnchor" parent="." instance=ExtResource( 1 )]
173-
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.6, 0 )
174-
175158
[node name="Player" parent="." instance=ExtResource( 2 )]

README.md

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,72 @@
1-
- [Credits](#credits)
2-
- [Quick Start Usage](#quick-start-usage)
3-
- [Inner Workings](#inner-workings)
4-
- [Player](#player)
5-
- [Camera](#camera)
1+
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->
2+
**Table of Contents**
3+
4+
- [Quick Start Guide](#quick-start-guide)
5+
- [Controls](#controls)
6+
- [How it works](#how-it-works)
7+
- [Player](#player)
8+
- [Camera](#camera)
69
- [Configuration](#configuration)
710
- [Customization](#customization)
11+
- [Credits](#credits)
812
- [Support our work](#support-our-work)
913

14+
<!-- markdown-toc end -->
15+
1016
An Open Source 3d character and character controller for the Godot game engine.
1117

1218
![The mannequin in-game](./assets/screenshots/prototype-1.png)
1319

1420
This is a third person character controller designed to work both with the keyboard and a gamepad. It features a camera that can auto-rotate or that can be controlled with a joystick.
1521

16-
## Credits
17-
18-
1. The Godot mannequin is a character made by [Luciano Muñoz](https://twitter.com/lucianomunoz_) In blender 2.80.
19-
1. Godot code by Josh aka [Cheeseness](https://twitter.com/ValiantCheese)
20-
1. Additional code by Francois Belair aka [Razoric480](https://twitter.com/Razoric480)
22+
# Quick Start Guide #
2123

22-
## Quick Start Usage
24+
The 3D Third Person Character Controller is made of two scenes:
2325

24-
The 3D Third Person Character Controller is composed of two scenes:
26+
* `Camera.tscn` - A 3D camera rig with a state machine for aiming
27+
* `Player.tscn` - A `KinematicBody` with a state machine for player movement. Contains an instance of `Camera`. It also includes the animated 3D mannequin.
2528

26-
* Camera.tscn - Holds a 3D camera rig with a state machine for aiming
27-
* Player.tscn - Holds the player's kinematic body and state machine for movement and functions, and holds the player's animation rig.
29+
To use the default character, instance `Player` in your game. See `Game.tscn` for an example. In this demo, the obstacles are mesh instances with static body collisions making up a cube world.
2830

29-
The procedure to use the controller as-is, then, is to add both the Camera and Player scenes as instanced children to your 3D scene.
31+
## Controls ##
3032

31-
See `Game.tscn` for a simple example - the obstacles are mesh instances with static body collisions making up a cube world, and has the Camera and Player as instanced children. The camera, additionally, has been moved up to head level.
33+
The game supports both mouse and keyboard, and the gamepad.
3234

33-
In short, the Player scene holds the 3D model and its animations, and the movement logic of the game character. What constitutes as 'forwards' is based on the Camera scene's orientation.
35+
# How it works #
3436

35-
## Inner Workings
37+
## Player ##
3638

37-
### Player
39+
The scene that deals with the movement, collision, and logic of the player. The player is a KinematicBody with a capsule collision shape, and the movement logic is within a [Finite State Machine](http://gameprogrammingpatterns.com/state.html).
3840

39-
The scene that deals with the movement, collision, and logic of the player. The player is a KinematicBody with a capsule collision shape, and the movement logic is within a Finite State Machine.
41+
The scene also holds an instance of the `PlayerMesh` for animation purposes. This scene lives in the `PlayerMesh.tscn` scene. It holds the skeletal rig for the mesh's animation, the 3D model of the body and head sepearately, and the animation tree and player to control the animation workflow of the model. The lot is wrapped up in a spatial node with some logic to transition to which animation based on which state the player is in.
4042

41-
The scene also holds an instance of the PlayerMesh for animation purposes. This scene lives in the `PlayerMesh.tscn` scene. It holds the skeletal rig for the mesh's animation, the 3D model of the body and head sepearately, and the animation tree and player to actually control the animation workflow of the model. The lot is wrapped up in a spatial node with some logic to transition to which animation based on which state the player is in.
43+
## Camera ##
4244

43-
### Camera
45+
The scene that deals with the Camera movement. It follows the Player in the game, but in code it moves and rotates separately from it. It has a `SpringArm` node to help with preventing collision with level geometry - moving the viewpoint forwards to prevent moving the camera inside geometry. It also has a system that holds the raycast for aiming-mode, and the 3D sprite that is a projected reticule. The logic is held in a finite state machine.
4446

45-
The scene that deals with the Camera movement. It is separate from the player, though keeps track of it and follows it. It has a SpringArm node to help with preventing collision with level geometry - moving the viewpoint forwards to prevent moving the camera inside geometry. It also has a system that holds the raycast for aiming-mode, and the 3D sprite that is a projected reticule. The logic is held in a finite state machine.
47+
# Configuration #
4648

47-
## Configuration
49+
To change the player and the camera's behavior, you need to change properties on the corresponding states in their state machine.
4850

49-
Most of the configuration available for player movement are located on the Move state in the Player scene - the player speed and the rotational speed.
51+
Most of the configuration available for player movement are located on the `Move` state in the Player scene - the player speed and the rotational speed.
5052

5153
The Camera has more options. On the main Camera state in the Camera scene are items like the default field of view, whether Y is inverted, and sensitivity.
5254

5355
In addition, the Aim state allows some finer-tuned changes, like whether the aiming camera is first or third person, and by how much it should be offset over-the-shoulder of the character.
5456

55-
## Customization
57+
# Customization #
5658

5759
While the scenes can be modified extensively with new nodes and raw code, the state machine model allow for some simple, new functionality with relative ease.
5860

5961
As an example, there is the `Extensions` folder which contains additional player states for using the aiming view to fire a hookshot that pulls you towards the reticle. Once those states have been added to the Player's `Move` state, you only need to replace the `pass` in Move's `enter` with code like `owner.camera.connect("aim_fired", self, "on_Camera_aim_fired")` and Move's `exit` with code like `owner.camera.disconnect("aim_fired", self, "on_Camera_aim_fired")`
6062

61-
## Support our work
63+
# Credits #
64+
65+
1. The Godot mannequin is a character made by [Luciano Muñoz](https://twitter.com/lucianomunoz_) In blender 2.80.
66+
1. Godot code by Josh aka [Cheeseness](https://twitter.com/ValiantCheese)
67+
1. Additional code by Francois Belair aka [Razoric480](https://twitter.com/Razoric480)
68+
69+
# Support our work #
6270

6371
This free series is sponsored by our [platformer game creation course](https://gdquest.mavenseed.com).
6472

src/Camera/Camera.gd

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
tool
12
extends Spatial
23
"""
34
Simple accessor class to let the nodes in the Camera scene access the player
@@ -10,9 +11,14 @@ signal aim_fired(target_vector)
1011
onready var camera_state: State = $StateMachine/Camera
1112
onready var aim_ray: RayCast = get_node("AimRay")
1213

13-
var player: Player
14+
var player: KinematicBody
1415

1516

1617
func _ready() -> void:
17-
player = get_tree().root.find_node("Player", true, false)
18-
assert(player)
18+
set_as_toplevel(true)
19+
yield(owner, "ready")
20+
player = owner
21+
22+
23+
func _get_configuration_warning() -> String:
24+
return "Missing player node" if not player else ""

src/Camera/Camera.tscn

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
[ext_resource path="res://src/Camera/CameraStates/CameraDefault.gd" type="Script" id=5]
88
[ext_resource path="res://src/Camera/CameraStates/Aim.gd" type="Script" id=6]
99

10-
[node name="CameraAnchor" type="Spatial"]
10+
[node name="Camera" type="Spatial"]
1111
script = ExtResource( 1 )
1212

1313
[node name="SpringArm" type="SpringArm" parent="."]
@@ -29,7 +29,7 @@ texture = ExtResource( 2 )
2929

3030
[node name="StateMachine" type="Node" parent="."]
3131
script = ExtResource( 3 )
32-
initial_state = NodePath("../../CameraAnchor/StateMachine/Camera/Default")
32+
initial_state = NodePath("Camera/Default")
3333

3434
[node name="Camera" type="Node" parent="StateMachine"]
3535
script = ExtResource( 4 )

src/Camera/CameraStates/Camera.gd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,4 @@ static func get_move_direction() -> Vector3:
141141
Input.get_action_strength("move_right") - Input.get_action_strength("move_left"),
142142
0,
143143
Input.get_action_strength("move_back") - Input.get_action_strength("move_front")
144-
)
144+
)

src/Player/Player.gd

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
tool
12
extends KinematicBody
23
class_name Player
34
"""
@@ -6,9 +7,8 @@ camera and its orientation.
67
"""
78

89

9-
var camera: Spatial
10+
onready var camera: Spatial = $Camera
1011

1112

12-
func _ready() -> void:
13-
camera = get_tree().root.find_node("CameraAnchor", true, false)
14-
assert(camera)
13+
func _get_configuration_warning() -> String:
14+
return "Missing camera node" if not camera else ""

src/Player/Player.tscn

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[gd_scene load_steps=9 format=2]
1+
[gd_scene load_steps=10 format=2]
22

33
[ext_resource path="res://src/Player/Player.gd" type="Script" id=1]
44
[ext_resource path="res://src/Player/PlayerMesh.tscn" type="PackedScene" id=2]
@@ -7,6 +7,7 @@
77
[ext_resource path="res://src/Player/MoveStates/Idle.gd" type="Script" id=5]
88
[ext_resource path="res://src/Player/MoveStates/Air.gd" type="Script" id=6]
99
[ext_resource path="res://src/Player/MoveStates/Run.gd" type="Script" id=7]
10+
[ext_resource path="res://src/Camera/Camera.tscn" type="PackedScene" id=8]
1011

1112
[sub_resource type="CapsuleShape" id=1]
1213
radius = 0.25
@@ -16,10 +17,10 @@ height = 1.5239
1617
script = ExtResource( 1 )
1718

1819
[node name="PlayerMesh" parent="." instance=ExtResource( 2 )]
19-
transform = Transform( -1, 0, -1.50996e-007, 0, 1, 0, 1.50996e-007, 0, -1, 0, 0, 0 )
20+
transform = Transform( -1, 0, -1.50996e-07, 0, 1, 0, 1.50996e-07, 0, -1, 0, 0, 0 )
2021

2122
[node name="CollisionShape" type="CollisionShape" parent="."]
22-
transform = Transform( 1, 0, 0, 0, -4.37114e-008, -1, 0, 1, -4.37114e-008, 0, 1, 0 )
23+
transform = Transform( 1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 1, 0 )
2324
shape = SubResource( 1 )
2425

2526
[node name="StateMachine" type="Node" parent="."]
@@ -41,4 +42,7 @@ script = ExtResource( 6 )
4142

4243
[node name="Run" type="Node" parent="StateMachine/Move"]
4344
script = ExtResource( 7 )
45+
46+
[node name="Camera" parent="." instance=ExtResource( 8 )]
47+
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.38644, 0 )
4448
[connection signal="transitioned" from="StateMachine" to="PlayerMesh" method="_on_StateMachine_transitioned"]

0 commit comments

Comments
 (0)