part_one.rst 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. .. _doc_fps_tutorial_part_one:
  2. Part 1
  3. ======
  4. Tutorial introduction
  5. ---------------------
  6. .. image:: img/FinishedTutorialPicture.png
  7. This tutorial series will show you how to make a single player FPS game.
  8. Throughout the course of these tutorials, we will cover how:
  9. - To make a first person character, with sprinting and a flash light.
  10. - To make a simple animation state machine for handling animation transitions.
  11. - To add a pistol, rifle, and knife to the first person character.
  12. - To add ammo and reloading to weapons that consume ammo.
  13. - To add sounds that play when the guns fire.
  14. .. note:: While this tutorial can be completed by beginners, it is highly
  15. advised to complete :ref:`doc_your_first_game`,
  16. if you are new to Godot and/or game development **before** going through
  17. this tutorial series.
  18. Remember: Making 3D games is much harder than making 2D games. If you do not know
  19. how to make 2D games you will likely struggle making 3D games.
  20. This tutorial assumes you know have experience working with the Godot editor,
  21. have basic programming experience in GDScript, and have basic experience in game development.
  22. You can find the start assets for this parts 1 through 3 here: :download:`Godot_FPS_Starter.zip <files/Godot_FPS_Starter.zip>`
  23. .. warning:: A video version of this tutorial series is coming soon!
  24. The provided starter assets contain a animated 3D model, a bunch of 3D models for making levels,
  25. and a few scenes already configured for this tutorial.
  26. All assets provided are created by me (TwistedTwigleg) unless otherwise noted, and are
  27. released under the ``MIT`` license.
  28. .. note:: The skybox is created by **StumpyStrust** on OpenGameArt. The skybox used is
  29. licensed under ``CC0``.
  30. The font used is **Titillium-Regular**, and is licensed under the ``SIL Open Font License, Version 1.1``.
  31. .. note:: You can find the finished project for parts 1 through 3 at the bottom of
  32. :ref:`doc_fps_tutorial_part_three`.
  33. Part Overview
  34. -------------
  35. In this part we will be making a first person player that can move around
  36. the environment.
  37. .. image:: img/PartOneFinished.png
  38. By the end of this part you will have a working first person character with a
  39. mouse based camera that can walk, jump, and sprint around the game environment in
  40. any direction
  41. Getting everything setup
  42. ------------------------
  43. Launch Godot and open up the project included in the starter assets.
  44. .. note:: While these assets are not necessarily required to use the scripts provided in this tutorial,
  45. they will make the tutorial much easier to follow as there are several pre-setup scenes we
  46. will be using throughout the tutorial series.
  47. First, go open the project settings and go to the "Input Map" tab. You'll find several
  48. actions have already been defined. We will be using these actions for our player.
  49. Feel free to change the keys bound to these actions if you want.
  50. While we still have the project settings open, quickly go check if MSAA (MultiSample Anti-Aliasing)
  51. is turned off. We want to make sure MSAA is off because otherwise we will get strange red lines
  52. between the tiles in our level later.
  53. .. tip:: The reason we get those red lines is because we are using lowpoly models
  54. with low resolution textures. MSAA tries to reduce jagged edges between models and
  55. because we are using lowpoly and low resolution textures in this project,
  56. we need to turn it off to avoid texture bleeding.
  57. A bonus with turning off MSAA is we get a more 'retro' looking result.
  58. _________
  59. Lets take a second to see what we have in the starter assets.
  60. Included in the starter assets are five scenes: ``BulletScene.tscn``, ``Player.tscn``,
  61. ``SimpleAudioPlayer.tscn``, ``TestingArea.tscn``, and ``TestLevel.tscn``.
  62. We will visit all of these scenes later, but for now open up ``Player.tscn``.
  63. .. note:: There are a bunch of scenes and a few textures in the ``Assets`` folder. You can look at these if you want,
  64. but we will not be directly using them in this tutorial.
  65. Making the FPS movement logic
  66. -----------------------------
  67. Once you have ``Player.tscn`` open, let's take a quick look at how it is setup
  68. .. image:: img/PlayerSceneTree.png
  69. First, notice how the player's collision shapes are setup. Using a vertical pointing
  70. capsule as the collision shape for the player is fairly common in most first person games.
  71. We are adding a small square to the 'feet' of the player so the player does not
  72. feel like they are balancing on a single point.
  73. .. note:: Many times player will notice how the collision shape being circular when
  74. they walk to an edge and slide off. We are adding the small square at the
  75. bottom of the capsule to reduce sliding on, and around, edges.
  76. Another thing to notice is how many nodes are children of ``Rotation_helper``. This is because
  77. ``Rotation_helper`` contains all of the nodes we want to rotate on the ``X`` axis (up and down).
  78. The reason behind this is so we rotate ``Player`` on the ``Y`` axis, and ``Rotation_helper`` on
  79. the ``X`` axis.
  80. .. note:: If we did not use ``Rotation_helper`` then we'd likely have cases where we are rotating
  81. both the ``X`` and ``Y`` axes at the same time. This can lead to undesirable results, as we then
  82. could rotate on all three axes in some cases.
  83. _________
  84. Attach a new script to the ``Player`` node and call it ``Player.gd``.
  85. Lets program our player by adding the ability to move around, look around with the mouse, and jump.
  86. Add the following code to ``Player.gd``:
  87. ::
  88. extends KinematicBody
  89. const norm_grav = -24.8
  90. var vel = Vector3()
  91. const MAX_SPEED = 20
  92. const JUMP_SPEED = 18
  93. const ACCEL = 3.5
  94. const DEACCEL= 16
  95. const MAX_SLOPE_ANGLE = 40
  96. var camera
  97. var camera_holder
  98. # You may need to adjust depending on the sensitivity of your mouse
  99. const MOUSE_SENSITIVITY = 0.05
  100. var flashlight
  101. func _ready():
  102. camera = $Rotation_helper/Camera
  103. camera_holder = $Rotation_helper
  104. Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
  105. flashlight = $Rotation_helper/Flashlight
  106. func _physics_process(delta):
  107. var dir = Vector3()
  108. var cam_xform = camera.get_global_transform()
  109. if Input.is_action_pressed("movement_forward"):
  110. dir += -cam_xform.basis.z.normalized()
  111. if Input.is_action_pressed("movement_backward"):
  112. dir += cam_xform.basis.z.normalized()
  113. if Input.is_action_pressed("movement_left"):
  114. dir += -cam_xform.basis.x.normalized()
  115. if Input.is_action_pressed("movement_right"):
  116. dir += cam_xform.basis.x.normalized()
  117. if is_on_floor():
  118. if Input.is_action_just_pressed("movement_jump"):
  119. vel.y = JUMP_SPEED
  120. if Input.is_action_just_pressed("flashlight"):
  121. if flashlight.is_visible_in_tree():
  122. flashlight.hide()
  123. else:
  124. flashlight.show()
  125. dir.y = 0
  126. dir = dir.normalized()
  127. var grav = norm_grav
  128. vel.y += delta*grav
  129. var hvel = vel
  130. hvel.y = 0
  131. var target = dir
  132. target *= MAX_SPEED
  133. var accel
  134. if dir.dot(hvel) > 0:
  135. accel = ACCEL
  136. else:
  137. accel = DEACCEL
  138. hvel = hvel.linear_interpolate(target, accel*delta)
  139. vel.x = hvel.x
  140. vel.z = hvel.z
  141. vel = move_and_slide(vel,Vector3(0,1,0), 0.05, 4, deg2rad(MAX_SLOPE_ANGLE))
  142. # (optional, but highly useful) Capturing/Freeing the cursor
  143. if Input.is_action_just_pressed("ui_cancel"):
  144. if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE:
  145. Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
  146. else:
  147. Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
  148. func _input(event):
  149. if event is InputEventMouseMotion && Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
  150. camera_holder.rotate_x(deg2rad(event.relative.y * MOUSE_SENSITIVITY))
  151. self.rotate_y(deg2rad(event.relative.x * MOUSE_SENSITIVITY * -1))
  152. var camera_rot = camera_holder.rotation_degrees
  153. camera_rot.x = clamp(camera_rot.x, -70, 70)
  154. camera_holder.rotation_degrees = camera_rot
  155. This is a lot of code, so let's break it down from top to bottom:
  156. _________
  157. First, we define some global variables to dictate how our player will move about the world.
  158. .. note:: Throughout this tutorial, *variables defined outside functions will be
  159. referred to as "global variables"*. This is because we can access any of these
  160. variables from any place in the script. We can "globally" access them, hence the
  161. name.
  162. Lets go through each of the global variables:
  163. - ``norm_grav``: How strong gravity pulls us down while we are walking.
  164. - ``vel``: Our :ref:`KinematicBody <class_KinematicBody>`'s velocity.
  165. - ``MAX_SPEED``: The fastest speed we can reach. Once we hit this speed, we will not go any faster.
  166. - ``JUMP_SPEED``: How high we can jump.
  167. - ``ACCEL``: How fast we accelerate. The higher the value, the faster we get to max speed.
  168. - ``DEACCEL``: How fast we are going to decelerate. The higher the value, the faster we will come to a complete stop.
  169. - ``MAX_SLOPE_ANGLE``: The steepest angle we can climb.
  170. - ``camera``: The :ref:`Camera <class_Camera>` node.
  171. - ``rotation_helper``: A :ref:`Spatial <class_Spatial>` node holding everything we want to rotate on the X axis (up and down).
  172. - ``MOUSE_SENSITIVITY``: How sensitive the mouse is. I find a value of ``0.05`` works well for my mouse, but you may need to change it based on how sensitive your mouse is.
  173. - ``flashlight``: A :ref:`Spotlight <class_Spotlight>` node that will act as our player's flashlight.
  174. You can tweak many of these variables to get different results. For example, by lowering ``normal_gravity`` and/or
  175. increasing ``JUMP_SPEED`` you can get a more 'floaty' feeling character.
  176. Feel free to experiment!
  177. _________
  178. Now lets look at the ``_ready`` function:
  179. First we get the ``camera`` and ``rotation_helper`` nodes and store them into their variables.
  180. Then we need to set the mouse mode to captured.
  181. This will hide the mouse and keep it at the center of the screen. We do this for two reasons:
  182. The first reason being we do not want to the player to see their mouse cursor as they play.
  183. The second reason is because we do not want the cursor to leave the game window. If the cursor leaves
  184. the game window there could be instances where the player clicks outside the window, and then the game
  185. would lose focus. To assure neither of these issues happen, we capture the mouse cursor.
  186. .. note:: see :ref:`Input documentation <class_Input>` for the various mouse modes. We will only be using
  187. ``MOUSE_MODE_CAPTURED`` and ``MOUSE_MODE_VISIBLE`` in this tutorial series.
  188. We need to use ``_input`` so we can rotate the player and
  189. camera when there is mouse motion.
  190. _________
  191. Next is ``_physics_process``:
  192. We define a directional vector (``dir``) for storing the direction the player intends to move.
  193. Next we get the camera's global transform and store it as well, into the ``cam_xform`` variable.
  194. Now we check for directional input. If we find that the player is moving, we get the ``camera``'s directional
  195. vector in the direction we are wanting to move towards and add (or subtract) it to ``dir``.
  196. Many have found directional vectors confusing, so lets take a second to explain how they work:
  197. _________
  198. World space can be defined as: The space in which all objects are placed in, relative to a constant origin point.
  199. Every object, no matter if it is 2D or 3D, has a position in world space.
  200. To put it another way: world space is the space in a universe where every object's position, rotation, and scale
  201. can be measured by a known, fixed point called the origin.
  202. In Godot, the origin is at position ``(0, 0, 0)`` with a rotation of ``(0, 0, 0)`` and a scale of ``(1, 1, 1)``.
  203. .. note:: When you open up the Godot editor and select a :ref:`Spatial <class_Spatial>` based node, a gizmo pops up.
  204. Each of the arrows points using world space directions by default.
  205. If you want to move using the world space directional vectors, you'd do something like this:
  206. ::
  207. if Input.is_action_pressed("movement_forward"):
  208. node.translate(Vector3(0, 0, 1))
  209. if Input.is_action_pressed("movement_backward"):
  210. node.translate(Vector3(0, 0, -1))
  211. if Input.is_action_pressed("movement_left"):
  212. node.translate(Vector3(1, 0, 0))
  213. if Input.is_action_pressed("movement_right"):
  214. node.translate(Vector3(-1, 0, 0))
  215. .. note:: Notice how we do not need to do any calculations to get world space directional vectors.
  216. We can just define a few :ref:`Vector3 <class_Vector3>` variables and input the values pointing in each direction.
  217. Here is what world space looks like in 2D:
  218. .. note:: The following images are just examples. Each arrow/rectangle represents a directional vector
  219. .. image:: img/WorldSpaceExample.png
  220. And here is what it looks like for 3D:
  221. .. image:: img/WorldSpaceExample_3D.png
  222. Notice how in both examples, the rotation of the node does not change the directional arrows.
  223. This is because world space is a constant. No matter how you translate, rotate, or scale an object, world
  224. space will *always point in the same direction*.
  225. Local space is different, because it takes the rotation of the object into account.
  226. Local space can be defined as follows:
  227. The space in which a object's position is the origin of the universe. Because the position
  228. of the origin can be at ``N`` many locations, the values derived from local space change
  229. with the position of the origin.
  230. .. note:: This stack overflow question has a much better explanation of world space and local space.
  231. https://gamedev.stackexchange.com/questions/65783/what-are-world-space-and-eye-space-in-game-development
  232. (Local space and eye space are essentially the same thing in this context)
  233. To get a :ref:`Spatial <class_Spatial>` node's local space, we need to get its :ref:`Transform <class_Transform>`, so then we
  234. can get the :ref:`Basis <class_Basis>` from the :ref:`Transform <class_Transform>`.
  235. Each :ref:`Basis <class_Basis>` has three vectors: ``X``, ``Y``, and ``Z``.
  236. Each of those vectors point towards each of the local space vectors coming from that object.
  237. To use the a :ref:`Spatial <class_Spatial>` node's local directional vectors, we use this code:
  238. ::
  239. if Input.is_action_pressed("movement_forward"):
  240. node.translate(node.global_transform.basis.z.normalized())
  241. if Input.is_action_pressed("movement_backward"):
  242. node.translate(-node.global_transform.basis.z.normalized())
  243. if Input.is_action_pressed("movement_left"):
  244. node.translate(node.global_transform.basis.x.normalized())
  245. if Input.is_action_pressed("movement_right"):
  246. node.translate(-node.global_transform.basis.x.normalized())
  247. Here is what local space looks like in 2D:
  248. .. image:: img/LocalSpaceExample.png
  249. And here is what it looks like for 3D:
  250. .. image:: img/LocalSpaceExample_3D.png
  251. Here is what the :ref:`Spatial <class_Spatial>` gizmo shows when you are using local space mode.
  252. Notice how the arrows follow the rotation of the object on the left, which looks exactly
  253. the same as the 3D example for local space.
  254. .. note:: You can change between local and world space modes by pressing the little cube button
  255. when you have a :ref:`Spatial <class_Spatial>` based node selected.
  256. .. image:: img/LocalSpaceExampleGizmo.png
  257. Local vectors are confusing even for more experienced game developers, so do not worry if this all doesn't make a
  258. lot of sense. The key thing to remember about local vectors is that we are using local coordinates to get direction
  259. from the object's point of view, as opposed to using world vectors which give direction from the world's point of view.
  260. _________
  261. Back to ``_physics_process``:
  262. When the player pressed any of the directional movement actions, we get the local vector pointing in that direction
  263. and add it to ``dir``.
  264. .. note:: Because the camera is rotated by ``-180`` degrees, we have to flip the directional vectors.
  265. Normally forward would be the positive Z axis, so using ``basis.z.normalized()`` would work,
  266. but we are using ``-basis.z.normalized()`` because our camera's Z axis faces backwards in relation
  267. to the rest of the player.
  268. Next we check if the player is on the floor using :ref:`KinematicBody <class_KinematicBody>`'s ``is_on_floor`` function. If it is, then we
  269. check to see if the "movement_jump" action has just been pressed. If it has, then we set our ``Y`` velocity to
  270. ``JUMP_SPEED``.
  271. Next we check if the flash light action was just pressed. If it was, we then check if the flash light
  272. is visible, or hidden. If it is visible, we hide it. If it is hidden, we make it visible.
  273. Next we assure that our movement vector does not have any movement on the ``Y`` axis, and then we normalize it.
  274. We set a variable to our normal gravity and apply that gravity to our velocity.
  275. After that we assign our velocity to a new variable (called ``hvel``) and remove any movement on the ``Y`` axis.
  276. Next we set a new variable (``target``) to our direction vector. Then we multiply that by our max speed
  277. so we know how far we will can move in the direction provided by ``dir``.
  278. After that we make a new variable for our acceleration, named ``accel``. We then take the dot product
  279. of ``hvel`` to see if we are moving according to ``hvel``. Remember, ``hvel`` does not have any
  280. ``Y`` velocity, meaning we are only checking if we are moving forwards, backwards, left, or right.
  281. If we are moving, then we set ``accel`` to our ``ACCEL`` constant so we accelerate, otherwise we set ``accel` to
  282. our ``DEACCEL`` constant so we decelerate.
  283. Finally, we interpolate our horizontal velocity, set our ``X`` and ``Z`` velocity to the interpolated horizontal velocity,
  284. and then call ``move_and_slide`` to let the :ref:`KinematicBody <class_KinematicBody>` handle moving through the physics world.
  285. .. tip:: All of the code in ``_physics_process`` is almost exactly the same as the movement code from the Kinematic Character demo!
  286. The only thing that is different is how we use the directional vectors, and the flash light!
  287. You can optionally add some code to capture and free the mouse cursor when "ui_cancel" is
  288. pressed. While entirely optional, it is highly recommended for debugging purposes.
  289. _________
  290. The final function we have is the ``_input`` function, and thankfully it's fairly short:
  291. First we make sure that the event we are dealing with is a :ref:`InputEventMouseMotion <class_InputEventMouseMotion>` event.
  292. We also want to check if the cursor is captured, as we do not want to rotate if it is not.
  293. .. tip:: See :ref:`Mouse and input coordinates <doc_mouse_and_input_coordinates>` for a list of
  294. possible input events.
  295. If the event is indeed a mouse motion event and the cursor is captured, we rotate
  296. based on the mouse motion provided by :ref:`InputEventMouseMotion <class_InputEventMouseMotion>`.
  297. First we rotate the ``rotation_helper`` node on the ``X`` axis, using the relative mouse motion's
  298. ``Y`` value, provided by :ref:`InputEventMouseMotion <class_InputEventMouseMotion>`.
  299. Then we rotate the entire :ref:`KinematicBody <class_KinematicBody>` on the ``Y`` axis by the relative mouse motion's ``X`` value.
  300. .. tip:: Godot converts relative mouse motion into a :ref:`Vector2 <class_Vector2>` where mouse movement going
  301. up and down is ``1`` and ``-1`` respectively. Right and Left movement is
  302. ``1`` and ``-1`` respectively.
  303. Because of how we are rotating the player, we multiply the relative mouse motion's
  304. ``X`` value by ``-1`` so mouse motion going left and right rotates the player left and right
  305. in the same direction.
  306. Finally, we clamp the ``rotation_helper``'s ``X`` rotation to be between ``-70`` and ``70``
  307. degrees so we cannot rotate ourselves upside down.
  308. _________
  309. To test the code open up the scene named ``Testing_Area.tscn`` if it's not already opened up. We will be using
  310. this scene as we go through the tutorial, so be sure to keep it open in one of your scene tabs.
  311. Go ahead and test your code either by pressing ``F4`` with ``Testing_Area.tscn`` as the open tab, by pressing the
  312. play button in the top right corner, or by pressing ``F6``.
  313. You should now be able to walk around, jump in the air, and look around using the mouse.
  314. Giving the player the option to sprint
  315. --------------------------------------
  316. Before we get to making the weapons work, there is one more thing we should add.
  317. Many FPS games have an option to sprint, and we can easily add sprinting to our player,
  318. so let's do that!
  319. First we need a few more global variables in our player script:
  320. ::
  321. const MAX_SPRINT_SPEED = 30
  322. const SPRINT_ACCEL = 18
  323. var is_sprinting = false
  324. All of these variables work exactly the same as the non sprinting variables with
  325. similar names. The only that's different is ``is_sprinting``, which is a boolean to track
  326. whether the player is currently sprinting.
  327. Now we just need to change some of the code in our ``_physics_process`` function
  328. so we can add the ability to sprint.
  329. The first thing we need to do is add the following code, preferably by the other input related code:
  330. ::
  331. if Input.is_action_pressed("movement_sprint"):
  332. is_sprinting = true
  333. else:
  334. is_sprinting = false;
  335. This will set ``is_sprinting`` to true when we are holding down the ``movement_sprint`` action, and false
  336. when the ``movement_sprint`` action is released.
  337. Next we need to set our max speed to the higher speed if we are sprinting, and we also need
  338. to change our acceleration to the new acceleration:
  339. ::
  340. var target = dir
  341. # NEW CODE. Replaces "target *= MAX_SPEED"
  342. if is_sprinting:
  343. target *= MAX_SPRINT_SPEED
  344. else:
  345. target *= MAX_SPEED
  346. # Same code as before:
  347. var accel
  348. if dir.dot(hvel) > 0:
  349. # New code. Replaces "accel = ACCEL"
  350. if is_sprinting:
  351. accel = SPRINT_ACCEL
  352. else:
  353. accel = ACCEL
  354. else:
  355. accel = DEACCEL
  356. Now you should be able to sprint if you press the shift button! Go give it a
  357. whirl! You can change the sprint related global variables to make the player faster when sprinting!
  358. Phew! That was a lot of work. Now you have a fully working first person character!
  359. In :ref:`doc_fps_tutorial_part_two` we will add some guns to our player character.
  360. .. note:: At this point we've recreated the Kinematic character demo with sprinting!
  361. .. tip:: Currently the player script would be at an ideal state for making all sorts of
  362. first person games. For example: Horror games, platformer games, adventure games, and more!
  363. .. warning:: If you ever get lost, be sure to read over the code again! You can also
  364. download the finished project at the bottom of :ref:`doc_fps_tutorial_part_three`.