part_one.rst 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  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 this tutorial series, we will cover how:
  9. - To make a first person character that can move, sprint, and jump.
  10. - To make a simple animation state machine for handling animation transitions.
  11. - To add three weapons to the first person character, each using a different way to handle bullet collisions:
  12. - - A knife (using an :ref:`Area <class_Area>`)
  13. - - A pistol (Bullet scenes)
  14. - - A rifle (using a :ref:`Raycast <class_Raycast>`)
  15. - To add two different types of grenades to the first person character:
  16. - - A normal grenade
  17. - - A sticky grenade
  18. - To add the ability to grab and throw :ref:`RigidBody <class_RigidBody>` nodes
  19. - To add joypad input for the player
  20. - To add ammo and reloading for all weapons that consume ammo.
  21. - To add ammo and health pick ups
  22. - - In two sizes: big and small
  23. - To add an automatic turret
  24. - - That can fire using bullet objects or a :ref:`Raycast <class_Raycast>`
  25. - To add targets that break when they've taken enough damage
  26. - To add sounds that play when the guns fire.
  27. - To add a simple main menu:
  28. - - With an options menu for changing how the game runs
  29. - - With a level select screen
  30. - To add a universal pause menu we can access anywhere
  31. .. note:: While this tutorial can be completed by beginners, it is highly
  32. advised to complete :ref:`doc_your_first_game`,
  33. if you are new to Godot and/or game development **before** going through
  34. this tutorial series.
  35. Remember: Making 3D games is much harder than making 2D games. If you do not know
  36. how to make 2D games, you will likely struggle making 3D games.
  37. This tutorial assumes you have experience working with the Godot editor,
  38. basic programming experience in GDScript, and basic experience in game development.
  39. You can find the start assets for this tutorial here: :download:`Godot_FPS_Starter.zip <files/Godot_FPS_Starter.zip>`
  40. The provided starter assets contain an animated 3D model, a bunch of 3D models for making levels,
  41. and a few scenes already configured for this tutorial.
  42. All assets provided (unless otherwise noted) were originally created by TwistedTwigleg, with changes/additions by the Godot community.
  43. All original assets provided for this tutorial are released under the ``MIT`` license.
  44. Feel free to use these assets however you want! All original assets belong to the Godot community, with the other assets belonging to those listed below:
  45. .. note:: The skybox is created by **StumpyStrust** on OpenGameArt. The skybox used is
  46. licensed under ``CC0``.
  47. The font used is **Titillium-Regular**, and is licensed under the ``SIL Open Font License, Version 1.1``.
  48. .. tip:: You can find the finished project for each part at the bottom of each part's page
  49. Part overview
  50. -------------
  51. In this part we will be making a first person player that can move around
  52. the environment.
  53. .. image:: img/PartOneFinished.png
  54. By the end of this part, you will have a working first-person character who can move around the game environment,
  55. sprint, look around with a mouse based first person camera, jump into the air, and turn a flash light on and off.
  56. Getting everything ready
  57. ------------------------
  58. Launch Godot and open up the project included in the starter assets.
  59. .. note:: While these assets are not necessarily required to use the scripts provided in this tutorial,
  60. they will make the tutorial much easier to follow, as there are several pre-setup scenes we
  61. will be using throughout the tutorial series.
  62. First, open the project settings and go to the "Input Map" tab. You'll find several
  63. actions have already been defined. We will be using these actions for our player.
  64. Feel free to change the keys bound to these actions if you want.
  65. _________
  66. Let's take a second to see what we have in the starter assets.
  67. Included in the starter assets are several scenes. For example, in ``res://`` we have 14 scenes, most of which we will be visiting as
  68. we go through this tutorial series.
  69. For now let's open up ``Player.tscn``.
  70. .. note:: There are a bunch of scenes and a few textures in the ``Assets`` folder. You can look at these if you want,
  71. but we will not be exploring through ``Assets`` in this tutorial series. ``Assets`` contains all the models used
  72. for each of the levels, as well as some textures and materials.
  73. Making the FPS movement logic
  74. -----------------------------
  75. Once you have ``Player.tscn`` open, let's take a quick look at how it is set up:
  76. .. image:: img/PlayerSceneTree.png
  77. First, notice how the player's collision shapes are set up. Using a vertical pointing
  78. capsule as the collision shape for the player is fairly common in most first person games.
  79. We are adding a small square to the 'feet' of the player so the player does not
  80. feel like they are balancing on a single point.
  81. We do want the 'feet' slightly higher than the bottom of the capsule so we can roll over slight edges.
  82. Where to place the 'feet' is dependent on your levels and how you want your player to feel.
  83. .. note:: Many times the player will notice the collision shape being circular when
  84. they walk to an edge and slide off. We are adding the small square at the
  85. bottom of the capsule to reduce sliding on, and around, edges.
  86. Another thing to notice is how many nodes are children of ``Rotation_Helper``. This is because
  87. ``Rotation_Helper`` contains all the nodes we want to rotate on the ``X`` axis (up and down).
  88. The reason behind this is so we can rotate ``Player`` on the ``Y`` axis, and ``Rotation_helper`` on
  89. the ``X`` axis.
  90. .. note:: Had we not used ``Rotation_helper``, we would've likely had cases of rotating on
  91. both the ``X`` and ``Y`` axes simultaneously, potentially further degenerating into a state of
  92. rotation on all three axes in some cases.
  93. See :ref:`using transforms <doc_using_transforms>` for more information
  94. _________
  95. Attach a new script to the ``Player`` node and call it ``Player.gd``.
  96. Let's program our player by adding the ability to move around, look around with the mouse, and jump.
  97. Add the following code to ``Player.gd``:
  98. .. tabs::
  99. .. code-tab:: gdscript GDScript
  100. extends KinematicBody
  101. const GRAVITY = -24.8
  102. var vel = Vector3()
  103. const MAX_SPEED = 20
  104. const JUMP_SPEED = 18
  105. const ACCEL = 4.5
  106. var dir = Vector3()
  107. const DEACCEL= 16
  108. const MAX_SLOPE_ANGLE = 40
  109. var camera
  110. var rotation_helper
  111. var MOUSE_SENSITIVITY = 0.05
  112. func _ready():
  113. camera = $Rotation_Helper/Camera
  114. rotation_helper = $Rotation_Helper
  115. Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
  116. func _physics_process(delta):
  117. process_input(delta)
  118. process_movement(delta)
  119. func process_input(delta):
  120. # ----------------------------------
  121. # Walking
  122. dir = Vector3()
  123. var cam_xform = camera.get_global_transform()
  124. var input_movement_vector = Vector2()
  125. if Input.is_action_pressed("movement_forward"):
  126. input_movement_vector.y += 1
  127. if Input.is_action_pressed("movement_backward"):
  128. input_movement_vector.y -= 1
  129. if Input.is_action_pressed("movement_left"):
  130. input_movement_vector.x -= 1
  131. if Input.is_action_pressed("movement_right"):
  132. input_movement_vector.x += 1
  133. input_movement_vector = input_movement_vector.normalized()
  134. # Basis vectors are already normalized.
  135. dir += -cam_xform.basis.z * input_movement_vector.y
  136. dir += cam_xform.basis.x * input_movement_vector.x
  137. # ----------------------------------
  138. # ----------------------------------
  139. # Jumping
  140. if is_on_floor():
  141. if Input.is_action_just_pressed("movement_jump"):
  142. vel.y = JUMP_SPEED
  143. # ----------------------------------
  144. # ----------------------------------
  145. # Capturing/Freeing the cursor
  146. if Input.is_action_just_pressed("ui_cancel"):
  147. if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE:
  148. Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
  149. else:
  150. Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
  151. # ----------------------------------
  152. func process_movement(delta):
  153. dir.y = 0
  154. dir = dir.normalized()
  155. vel.y += delta * GRAVITY
  156. var hvel = vel
  157. hvel.y = 0
  158. var target = dir
  159. target *= MAX_SPEED
  160. var accel
  161. if dir.dot(hvel) > 0:
  162. accel = ACCEL
  163. else:
  164. accel = DEACCEL
  165. hvel = hvel.linear_interpolate(target, accel * delta)
  166. vel.x = hvel.x
  167. vel.z = hvel.z
  168. vel = move_and_slide(vel, Vector3(0, 1, 0), 0.05, 4, deg2rad(MAX_SLOPE_ANGLE))
  169. func _input(event):
  170. if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
  171. rotation_helper.rotate_x(deg2rad(event.relative.y * MOUSE_SENSITIVITY))
  172. self.rotate_y(deg2rad(event.relative.x * MOUSE_SENSITIVITY * -1))
  173. var camera_rot = rotation_helper.rotation_degrees
  174. camera_rot.x = clamp(camera_rot.x, -70, 70)
  175. rotation_helper.rotation_degrees = camera_rot
  176. .. code-tab:: csharp
  177. using Godot;
  178. using System;
  179. public class Player : KinematicBody
  180. {
  181. [Export]
  182. public float Gravity = -24.8f;
  183. [Export]
  184. public float MaxSpeed = 20.0f;
  185. [Export]
  186. public float JumpSpeed = 18.0f;
  187. [Export]
  188. public float Accel = 4.5f;
  189. [Export]
  190. public float Deaccel = 16.0f;
  191. [Export]
  192. public float MaxSlopeAngle = 40.0f;
  193. [Export]
  194. public float MouseSensitivity = 0.05f;
  195. private Vector3 _vel = new Vector3();
  196. private Vector3 _dir = new Vector3();
  197. private Camera _camera;
  198. private Spatial _rotationHelper;
  199. // Called when the node enters the scene tree for the first time.
  200. public override void _Ready()
  201. {
  202. _camera = GetNode<Camera>("Rotation_Helper/Camera");
  203. _rotationHelper = GetNode<Spatial>("Rotation_Helper");
  204. Input.SetMouseMode(Input.MouseMode.Captured);
  205. }
  206. public override void _PhysicsProcess(float delta)
  207. {
  208. ProcessInput(delta);
  209. ProcessMovement(delta);
  210. }
  211. private void ProcessInput(float delta)
  212. {
  213. // -------------------------------------------------------------------
  214. // Walking
  215. _dir = new Vector3();
  216. Transform camXform = _camera.GlobalTransform;
  217. Vector2 inputMovementVector = new Vector2();
  218. if (Input.IsActionPressed("movement_forward"))
  219. inputMovementVector.y += 1;
  220. if (Input.IsActionPressed("movement_backward"))
  221. inputMovementVector.y -= 1;
  222. if (Input.IsActionPressed("movement_left"))
  223. inputMovementVector.x -= 1;
  224. if (Input.IsActionPressed("movement_right"))
  225. inputMovementVector.x += 1;
  226. inputMovementVector = inputMovementVector.Normalized();
  227. // Basis vectors are already normalized.
  228. _dir += -camXform.basis.z * inputMovementVector.y;
  229. _dir += camXform.basis.x * inputMovementVector.x;
  230. // -------------------------------------------------------------------
  231. // -------------------------------------------------------------------
  232. // Jumping
  233. if (IsOnFloor())
  234. {
  235. if (Input.IsActionJustPressed("movement_jump"))
  236. _vel.y = JumpSpeed;
  237. }
  238. // -------------------------------------------------------------------
  239. // -------------------------------------------------------------------
  240. // Capturing/Freeing the cursor
  241. if (Input.IsActionJustPressed("ui_cancel"))
  242. {
  243. if (Input.GetMouseMode() == Input.MouseMode.Visible)
  244. Input.SetMouseMode(Input.MouseMode.Captured);
  245. else
  246. Input.SetMouseMode(Input.MouseMode.Visible);
  247. }
  248. // -------------------------------------------------------------------
  249. }
  250. private void ProcessMovement(float delta)
  251. {
  252. _dir.y = 0;
  253. _dir = _dir.Normalized();
  254. _vel.y += delta * Gravity;
  255. Vector3 hvel = _vel;
  256. hvel.y = 0;
  257. Vector3 target = _dir;
  258. target *= MaxSpeed;
  259. float accel;
  260. if (_dir.Dot(hvel) > 0)
  261. accel = Accel;
  262. else
  263. accel = Deaccel;
  264. hvel = hvel.LinearInterpolate(target, accel * delta);
  265. _vel.x = hvel.x;
  266. _vel.z = hvel.z;
  267. _vel = MoveAndSlide(_vel, new Vector3(0, 1, 0), false, 4, Mathf.Deg2Rad(MaxSlopeAngle));
  268. }
  269. public override void _Input(InputEvent @event)
  270. {
  271. if (@event is InputEventMouseMotion && Input.GetMouseMode() == Input.MouseMode.Captured)
  272. {
  273. InputEventMouseMotion mouseEvent = @event as InputEventMouseMotion;
  274. _rotationHelper.RotateX(Mathf.Deg2Rad(mouseEvent.Relative.y * MouseSensitivity));
  275. RotateY(Mathf.Deg2Rad(-mouseEvent.Relative.x * MouseSensitivity));
  276. Vector3 cameraRot = _rotationHelper.RotationDegrees;
  277. cameraRot.x = Mathf.Clamp(cameraRot.x, -70, 70);
  278. _rotationHelper.RotationDegrees = cameraRot;
  279. }
  280. }
  281. }
  282. This is a lot of code, so let's break it down function by function:
  283. .. tip:: While copy and pasting code is ill advised, as you can learn a lot from manually typing the code in, you can
  284. copy and paste the code from this page directly into the script editor.
  285. If you do this, all the code copied will be using spaces instead of tabs.
  286. To convert the spaces to tabs in the script editor, click the "edit" menu and select "Convert Indent To Tabs".
  287. This will convert all the spaces into tabs. You can select "Convert Indent To Spaces" to convert tabs back into spaces.
  288. _________
  289. First, we define some class variables to dictate how our player will move about the world.
  290. .. note:: Throughout this tutorial, **variables defined outside functions will be
  291. referred to as "class variables"**. This is because we can access any of these
  292. variables from any place in the script.
  293. Let's go through each of the class variables:
  294. - ``GRAVITY``: How strong gravity pulls us down.
  295. - ``vel``: Our :ref:`KinematicBody <class_KinematicBody>`'s velocity.
  296. - ``MAX_SPEED``: The fastest speed we can reach. Once we hit this speed, we will not go any faster.
  297. - ``JUMP_SPEED``: How high we can jump.
  298. - ``ACCEL``: How quickly we accelerate. The higher the value, the sooner we get to max speed.
  299. - ``DEACCEL``: How quickly we are going to decelerate. The higher the value, the sooner we will come to a complete stop.
  300. - ``MAX_SLOPE_ANGLE``: The steepest angle our :ref:`KinematicBody <class_KinematicBody>` will consider as a 'floor'.
  301. - ``camera``: The :ref:`Camera <class_Camera>` node.
  302. - ``rotation_helper``: A :ref:`Spatial <class_Spatial>` node holding everything we want to rotate on the X axis (up and down).
  303. - ``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.
  304. You can tweak many of these variables to get different results. For example, by lowering ``GRAVITY`` and/or
  305. increasing ``JUMP_SPEED`` you can get a more 'floaty' feeling character.
  306. Feel free to experiment!
  307. .. note:: You may have noticed that ``MOUSE_SENSITIVITY`` is written in all caps like the other constants, but ``MOUSE_SENSITIVITY`` is not a constant.
  308. The reason behind this is we want to treat it like a constant variable (a variable that cannot change) throughout our script, but we want to be
  309. able to change the value later when we add customizable settings. So, in an effort to remind ourselves to treat it like a constant, it's named in all caps.
  310. _________
  311. Now let's look at the ``_ready`` function:
  312. First we get the ``camera`` and ``rotation_helper`` nodes and store them into their variables.
  313. Then we need to set the mouse mode to captured, so the mouse cannot leave the game window.
  314. This will hide the mouse and keep it at the center of the screen. We do this for two reasons:
  315. The first reason being we do not want the player to see their mouse cursor as they play.
  316. The second reason is because we do not want the cursor to leave the game window. If the cursor leaves
  317. the game window there could be instances where the player clicks outside the window, and then the game
  318. would lose focus. To assure neither of these issues happens, we capture the mouse cursor.
  319. .. note:: See :ref:`Input documentation <class_Input>` for the various mouse modes. We will only be using
  320. ``MOUSE_MODE_CAPTURED`` and ``MOUSE_MODE_VISIBLE`` in this tutorial series.
  321. _________
  322. Next let's take a look at ``_physics_process``:
  323. All we're doing in ``_physics_process`` is calling two functions: ``process_input`` and ``process_movement``.
  324. ``process_input`` will be where we store all the code relating to player input. We want to call it first, before
  325. anything else, so we have fresh player input to work with.
  326. ``process_movement`` is where we'll send all the data necessary to the :ref:`KinematicBody <class_KinematicBody>`
  327. so it can move through the game world.
  328. _________
  329. Let's look at ``process_input`` next:
  330. First we set ``dir`` to an empty :ref:`Vector3 <class_Vector3>`.
  331. ``dir`` will be used for storing the direction the player intends to move towards. Because we do not
  332. want the player's previous input to effect the player beyond a single ``process_movement`` call, we reset ``dir``.
  333. Next we get the camera's global transform and store it as well, into the ``cam_xform`` variable.
  334. The reason we need the camera's global transform is so we can use its directional vectors.
  335. Many have found directional vectors confusing, so let's take a second to explain how they work:
  336. _________
  337. World space can be defined as: The space in which all objects are placed in, relative to a constant origin point.
  338. Every object, no matter if it is 2D or 3D, has a position in world space.
  339. To put it another way: world space is the space in a universe where every object's position, rotation, and scale
  340. can be measured by a single, known, fixed point called the origin.
  341. In Godot, the origin is at position ``(0, 0, 0)`` with a rotation of ``(0, 0, 0)`` and a scale of ``(1, 1, 1)``.
  342. .. note:: When you open up the Godot editor and select a :ref:`Spatial <class_Spatial>` based node, a gizmo pops up.
  343. Each of the arrows points using world space directions by default.
  344. If you want to move using the world space directional vectors, you'd do something like this:
  345. .. tabs::
  346. .. code-tab:: gdscript GDScript
  347. if Input.is_action_pressed("movement_forward"):
  348. node.translate(Vector3(0, 0, 1))
  349. if Input.is_action_pressed("movement_backward"):
  350. node.translate(Vector3(0, 0, -1))
  351. if Input.is_action_pressed("movement_left"):
  352. node.translate(Vector3(1, 0, 0))
  353. if Input.is_action_pressed("movement_right"):
  354. node.translate(Vector3(-1, 0, 0))
  355. .. code-tab:: csharp
  356. if (Input.IsActionPressed("movement_forward"))
  357. node.Translate(new Vector3(0, 0, 1));
  358. if (Input.IsActionPressed("movement_backward"))
  359. node.Translate(new Vector3(0, 0, -1));
  360. if (Input.IsActionPressed("movement_left"))
  361. node.Translate(new Vector3(1, 0, 0));
  362. if (Input.IsActionPressed("movement_right"))
  363. node.Translate(new Vector3(-1, 0, 0));
  364. .. note:: Notice how we do not need to do any calculations to get world space directional vectors.
  365. We can define a few :ref:`Vector3 <class_Vector3>` variables and input the values pointing in each direction.
  366. Here is what world space looks like in 2D:
  367. .. note:: The following images are just examples. Each arrow/rectangle represents a directional vector
  368. .. image:: img/WorldSpaceExample.png
  369. And here is what it looks like for 3D:
  370. .. image:: img/WorldSpaceExample_3D.png
  371. Notice how in both examples, the rotation of the node does not change the directional arrows.
  372. This is because world space is a constant. No matter how you translate, rotate, or scale an object, world
  373. space will *always point in the same direction*.
  374. Local space is different, because it takes the rotation of the object into account.
  375. Local space can be defined as follows:
  376. The space in which an object's position is the origin of the universe. Because the position
  377. of the origin can be at ``N`` many locations, the values derived from local space change
  378. with the position of the origin.
  379. .. note:: This question from Game Development Stack Exchange has a much better explanation of world space and local space.
  380. https://gamedev.stackexchange.com/questions/65783/what-are-world-space-and-eye-space-in-game-development
  381. (Local space and eye space are essentially the same thing in this context)
  382. To get a :ref:`Spatial <class_Spatial>` node's local space, we need to get its :ref:`Transform <class_Transform>`, so then we
  383. can get the :ref:`Basis <class_Basis>` from the :ref:`Transform <class_Transform>`.
  384. Each :ref:`Basis <class_Basis>` has three vectors: ``X``, ``Y``, and ``Z``.
  385. Each of those vectors point towards each of the local space vectors coming from that object.
  386. To use the :ref:`Spatial <class_Spatial>` node's local directional vectors, we use this code:
  387. .. tabs::
  388. .. code-tab:: gdscript GDScript
  389. if Input.is_action_pressed("movement_forward"):
  390. node.translate(node.global_transform.basis.z.normalized())
  391. if Input.is_action_pressed("movement_backward"):
  392. node.translate(-node.global_transform.basis.z.normalized())
  393. if Input.is_action_pressed("movement_left"):
  394. node.translate(node.global_transform.basis.x.normalized())
  395. if Input.is_action_pressed("movement_right"):
  396. node.translate(-node.global_transform.basis.x.normalized())
  397. .. code-tab:: csharp
  398. if (Input.IsActionPressed("movement_forward"))
  399. node.Translate(node.GlobalTransform.basis.z.Normalized());
  400. if (Input.IsActionPressed("movement_backward"))
  401. node.Translate(-node.GlobalTransform.basis.z.Normalized());
  402. if (Input.IsActionPressed("movement_left"))
  403. node.Translate(node.GlobalTransform.basis.x.Normalized());
  404. if (Input.IsActionPressed("movement_right"))
  405. node.Translate(-node.GlobalTransform.basis.x.Normalized());
  406. Here is what local space looks like in 2D:
  407. .. image:: img/LocalSpaceExample.png
  408. And here is what it looks like for 3D:
  409. .. image:: img/LocalSpaceExample_3D.png
  410. Here is what the :ref:`Spatial <class_Spatial>` gizmo shows when you are using local space mode.
  411. Notice how the arrows follow the rotation of the object on the left, which looks exactly
  412. the same as the 3D example for local space.
  413. .. note:: You can change between local and world space modes by pressing :kbd:`T` or the little cube button
  414. when you have a :ref:`Spatial <class_Spatial>` based node selected.
  415. .. image:: img/LocalSpaceExampleGizmo.png
  416. Local vectors are confusing even for more experienced game developers, so do not worry if this all doesn't make a
  417. lot of sense. The key thing to remember about local vectors is that we are using local coordinates to get direction
  418. from the object's point of view, as opposed to using world vectors, which give direction from the world's point of view.
  419. _________
  420. Okay, back to ``process_input``:
  421. Next we make a new variable called ``input_movement_vector`` and assign it to an empty :ref:`Vector2 <class_Vector2>`.
  422. We will use this to make a virtual axis of sorts, to map the player's input to movement.
  423. .. note:: This may seem overkill for just the keyboard, but this will make sense later when we add joypad input.
  424. Based on which directional movement action is pressed, we add to or subtract from ``input_movement_vector``.
  425. After we've checked each of the directional movement actions, we normalize ``input_movement_vector``. This makes it where ``input_movement_vector``'s values
  426. are within a ``1`` radius unit circle.
  427. Next we add the camera's local ``Z`` vector times ``input_movement_vector.y`` to ``dir``. This is so when the player presses forward or backwards, we add the camera's
  428. local ``Z`` axis so the player moves forward or backwards in relation to the camera.
  429. .. note:: Because the camera is rotated by ``-180`` degrees, we have to flip the ``Z`` directional vector.
  430. Normally forward would be the positive Z axis, so using ``basis.z.normalized()`` would work,
  431. but we are using ``-basis.z.normalized()`` because our camera's Z axis faces backwards in relation
  432. to the rest of the player.
  433. We do the same thing for the camera's local ``X`` vector, and instead of using ``input_movement_vector.y`` we instead use ``input_movement_vector.x``.
  434. This makes it where the player moves left/right in relation to the camera when the player presses left/right.
  435. 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
  436. check to see if the "movement_jump" action has just been pressed. If it has, then we set the player's ``Y`` velocity to
  437. ``JUMP_SPEED``.
  438. Because we're setting the Y velocity, the player will jump into the air.
  439. Then we check for the ``ui_cancel`` action. This is so we can free/capture the mouse cursor when the ``escape`` button
  440. is pressed. We do this because otherwise we'd have no way to free the cursor, meaning it would be stuck until you terminate the
  441. runtime.
  442. To free/capture the cursor, we check to see if the mouse is visible (freed) or not. If it is, we capture it, and if it's not, we make it visible (free it).
  443. That's all we're doing right now for ``process_input``. We'll come back several times to this function as we add more complexities to our player.
  444. _________
  445. Now let's look at ``process_movement``:
  446. First we ensure that ``dir`` does not have any movement on the ``Y`` axis by setting its ``Y`` value to zero.
  447. Next we normalize ``dir`` to ensure we're within a ``1`` radius unit circle. This makes it where we're moving at a constant speed regardless
  448. of whether the player is moving straight or diagonally. If we did not normalize, the player would move faster on the diagonal than when going straight.
  449. Next we add gravity to the player by adding ``GRAVITY * delta`` to the player's ``Y`` velocity.
  450. After that we assign the player's velocity to a new variable (called ``hvel``) and remove any movement on the ``Y`` axis.
  451. Next we set a new variable (``target``) to the player's direction vector.
  452. Then we multiply that by the player's max speed so we know how far the player will move in the direction provided by ``dir``.
  453. After that we make a new variable for acceleration, named ``accel``.
  454. We then take the dot product of ``hvel`` to see if the player is moving according to ``hvel``. Remember, ``hvel`` does not have any
  455. ``Y`` velocity, meaning we are only checking if the player is moving forwards, backwards, left, or right.
  456. If the player is moving according to ``hvel``, then we set ``accel`` to the ``ACCEL`` constant so the player will accelerate, otherwise we set ``accel`` to
  457. our ``DEACCEL`` constant so the player will decelerate.
  458. Then we interpolate the horizontal velocity, set the player's ``X`` and ``Z`` velocity to the interpolated horizontal velocity,
  459. and call ``move_and_slide`` to let the :ref:`KinematicBody <class_KinematicBody>` handle moving the player through the physics world.
  460. .. tip:: All the code in ``process_movement`` is exactly the same as the movement code from the Kinematic Character demo!
  461. _________
  462. The final function we have is the ``_input`` function, and thankfully it's fairly short:
  463. First we make sure that the event we are dealing with is an :ref:`InputEventMouseMotion <class_InputEventMouseMotion>` event.
  464. We also want to check if the cursor is captured, as we do not want to rotate if it is not.
  465. .. note:: See :ref:`Mouse and input coordinates <doc_mouse_and_input_coordinates>` for a list of
  466. possible input events.
  467. If the event is indeed a mouse motion event and the cursor is captured, we rotate
  468. based on the relative mouse motion provided by :ref:`InputEventMouseMotion <class_InputEventMouseMotion>`.
  469. First we rotate the ``rotation_helper`` node on the ``X`` axis, using the relative mouse motion's
  470. ``Y`` value, provided by :ref:`InputEventMouseMotion <class_InputEventMouseMotion>`.
  471. Then we rotate the entire :ref:`KinematicBody <class_KinematicBody>` on the ``Y`` axis by the relative mouse motion's ``X`` value.
  472. .. tip:: Godot converts relative mouse motion into a :ref:`Vector2 <class_Vector2>` where mouse movement going
  473. up and down is ``1`` and ``-1`` respectively. Right and Left movement is
  474. ``1`` and ``-1`` respectively.
  475. Because of how we are rotating the player, we multiply the relative mouse motion's
  476. ``X`` value by ``-1`` so mouse motion going left and right rotates the player left and right
  477. in the same direction.
  478. Finally, we clamp the ``rotation_helper``'s ``X`` rotation to be between ``-70`` and ``70``
  479. degrees so the player cannot rotate themselves upside down.
  480. .. tip:: See :ref:`using transforms <doc_using_transforms>` for more information on rotating transforms.
  481. _________
  482. To test the code, open up the scene named ``Testing_Area.tscn``, if it's not already opened up. We will be using
  483. this scene as we go through the next few tutorial parts, so be sure to keep it open in one of your scene tabs.
  484. Go ahead and test your code either by pressing :kbd:`F6` with ``Testing_Area.tscn`` as the open tab, by pressing the
  485. play button in the top right corner, or by pressing :kbd:`F5`.
  486. You should now be able to walk around, jump in the air, and look around using the mouse.
  487. Giving the player a flash light and the option to sprint
  488. --------------------------------------------------------
  489. Before we get to making the weapons work, there are a couple more things we should add.
  490. Many FPS games have an option to sprint and a flashlight. We can easily add these to our player,
  491. so let's do that!
  492. First we need a few more class variables in our player script:
  493. .. tabs::
  494. .. code-tab:: gdscript GDScript
  495. const MAX_SPRINT_SPEED = 30
  496. const SPRINT_ACCEL = 18
  497. var is_sprinting = false
  498. var flashlight
  499. .. code-tab:: csharp
  500. [Export]
  501. public float MaxSprintSpeed = 30.0f;
  502. [Export]
  503. public float SprintAccel = 18.0f;
  504. private bool _isSprinting = false;
  505. private SpotLight _flashlight;
  506. All the sprinting variables work exactly the same as the non sprinting variables with
  507. similar names.
  508. ``is_sprinting`` is a boolean to track whether the player is currently sprinting, and ``flashlight`` is a variable
  509. we will be using to hold the player's flash light node.
  510. Now we need to add a few lines of code, starting in ``_ready``. Add the following to ``_ready``:
  511. .. tabs::
  512. .. code-tab:: gdscript GDScript
  513. flashlight = $Rotation_Helper/Flashlight
  514. .. code-tab:: csharp
  515. _flashlight = GetNode<SpotLight>("Rotation_Helper/Flashlight");
  516. This gets the ``Flashlight`` node and assigns it to the ``flashlight`` variable.
  517. _________
  518. Now we need to change some of the code in ``process_input``. Add the following somewhere in ``process_input``:
  519. .. tabs::
  520. .. code-tab:: gdscript GDScript
  521. # ----------------------------------
  522. # Sprinting
  523. if Input.is_action_pressed("movement_sprint"):
  524. is_sprinting = true
  525. else:
  526. is_sprinting = false
  527. # ----------------------------------
  528. # ----------------------------------
  529. # Turning the flashlight on/off
  530. if Input.is_action_just_pressed("flashlight"):
  531. if flashlight.is_visible_in_tree():
  532. flashlight.hide()
  533. else:
  534. flashlight.show()
  535. # ----------------------------------
  536. .. code-tab:: csharp
  537. // -------------------------------------------------------------------
  538. // Sprinting
  539. if (Input.IsActionPressed("movement_sprint"))
  540. _isSprinting = true;
  541. else
  542. _isSprinting = false;
  543. // -------------------------------------------------------------------
  544. // -------------------------------------------------------------------
  545. // Turning the flashlight on/off
  546. if (Input.IsActionJustPressed("flashlight"))
  547. {
  548. if (_flashlight.IsVisibleInTree())
  549. _flashlight.Hide();
  550. else
  551. _flashlight.Show();
  552. }
  553. Let's go over the additions:
  554. We set ``is_sprinting`` to ``true`` when the player is holding down the ``movement_sprint`` action, and ``false``
  555. when the ``movement_sprint`` action is released. In ``process_movement`` we'll add the code that makes the player faster when
  556. they sprint. Here in ``process_input`` we are just going to change the ``is_sprinting`` variable.
  557. We do something similar to freeing/capturing the cursor for handling the flashlight. We first check to see if the ``flashlight`` action
  558. was just pressed. If it was, we then check to see if ``flashlight`` is visible in the scene tree. If it is, then we hide it, and if it's not, we show it.
  559. _________
  560. Now we need to change a couple things in ``process_movement``. First, replace ``target *= MAX_SPEED`` with the following:
  561. .. tabs::
  562. .. code-tab:: gdscript GDScript
  563. if is_sprinting:
  564. target *= MAX_SPRINT_SPEED
  565. else:
  566. target *= MAX_SPEED
  567. .. code-tab:: csharp
  568. if (_isSprinting)
  569. target *= MaxSprintSpeed;
  570. else
  571. target *= MaxSpeed;
  572. Now instead of always multiplying ``target`` by ``MAX_SPEED``, we first check to see if the player is sprinting or not.
  573. If the player is sprinting, we instead multiply ``target`` by ``MAX_SPRINT_SPEED``.
  574. Now all that's left is to change the acceleration when sprinting. Change ``accel = ACCEL`` to the following:
  575. .. tabs::
  576. .. code-tab:: gdscript GDScript
  577. if is_sprinting:
  578. accel = SPRINT_ACCEL
  579. else:
  580. accel = ACCEL
  581. .. code-tab:: csharp
  582. if (_isSprinting)
  583. accel = SprintAccel;
  584. else
  585. accel = Accel;
  586. Now, when the player is sprinting, we'll use ``SPRINT_ACCEL`` instead of ``ACCEL``, which will accelerate the player faster.
  587. _________
  588. You should now be able to sprint if you press :kbd:`Shift`, and can toggle the flash light on and off by pressing :kbd:`F`!
  589. Go try it out! You can change the sprint-related class variables to make the player faster or slower when sprinting!
  590. Final notes
  591. -----------
  592. .. image:: img/PartOneFinished.png
  593. Whew! That was a lot of work. Now you have a fully working first person character!
  594. In :ref:`doc_fps_tutorial_part_two` we will add some guns to our player character.
  595. .. note:: At this point we've recreated the Kinematic character demo from first person perspective with sprinting and a flash light!
  596. .. tip:: Currently the player script would be at an ideal state for making all sorts of
  597. first person games. For example: Horror games, platformer games, adventure games, and more!
  598. .. warning:: If you ever get lost, be sure to read over the code again!
  599. You can download the finished project for this part here: :download:`Godot_FPS_Part_1.zip <files/Godot_FPS_Part_1.zip>`