part_two.rst 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239
  1. .. _doc_fps_tutorial_part_two:
  2. Part 2
  3. ======
  4. Part overview
  5. -------------
  6. In this part we will be giving our player weapons to play with.
  7. .. image:: img/PartTwoFinished.png
  8. By the end of this part, you will have a player that can fire a pistol,
  9. rifle, and attack using a knife. The player will also now have animations with transitions,
  10. and the weapons will interact with objects in the environment.
  11. .. note:: You are assumed to have finished :ref:`doc_fps_tutorial_part_one` before moving on to this part of the tutorial.
  12. The finished project from :ref:`doc_fps_tutorial_part_one` will be the starting project for part 2
  13. Let's get started!
  14. Making a system to handle animations
  15. ------------------------------------
  16. First we need a way to handle changing animations. Open up ``Player.tscn`` and select the :ref:`AnimationPlayer <class_AnimationPlayer>`
  17. Node (``Player`` -> ``Rotation_Helper`` -> ``Model`` -> ``Animation_Player``).
  18. Create a new script called ``AnimationPlayer_Manager.gd`` and attach that to the :ref:`AnimationPlayer <class_AnimationPlayer>`.
  19. Add the following code to ``AnimationPlayer_Manager.gd``:
  20. ::
  21. extends AnimationPlayer
  22. # Structure -> Animation name :[Connecting Animation states]
  23. var states = {
  24. "Idle_unarmed":["Knife_equip", "Pistol_equip", "Rifle_equip", "Idle_unarmed"],
  25. "Pistol_equip":["Pistol_idle"],
  26. "Pistol_fire":["Pistol_idle"],
  27. "Pistol_idle":["Pistol_fire", "Pistol_reload", "Pistol_unequip", "Pistol_idle"],
  28. "Pistol_reload":["Pistol_idle"],
  29. "Pistol_unequip":["Idle_unarmed"],
  30. "Rifle_equip":["Rifle_idle"],
  31. "Rifle_fire":["Rifle_idle"],
  32. "Rifle_idle":["Rifle_fire", "Rifle_reload", "Rifle_unequip", "Rifle_idle"],
  33. "Rifle_reload":["Rifle_idle"],
  34. "Rifle_unequip":["Idle_unarmed"],
  35. "Knife_equip":["Knife_idle"],
  36. "Knife_fire":["Knife_idle"],
  37. "Knife_idle":["Knife_fire", "Knife_unequip", "Knife_idle"],
  38. "Knife_unequip":["Idle_unarmed"],
  39. }
  40. var animation_speeds = {
  41. "Idle_unarmed":1,
  42. "Pistol_equip":1.4,
  43. "Pistol_fire":1.8,
  44. "Pistol_idle":1,
  45. "Pistol_reload":1,
  46. "Pistol_unequip":1.4,
  47. "Rifle_equip":2,
  48. "Rifle_fire":6,
  49. "Rifle_idle":1,
  50. "Rifle_reload":1.45,
  51. "Rifle_unequip":2,
  52. "Knife_equip":1,
  53. "Knife_fire":1.35,
  54. "Knife_idle":1,
  55. "Knife_unequip":1,
  56. }
  57. var current_state = null
  58. var callback_function = null
  59. func _ready():
  60. set_animation("Idle_unarmed")
  61. connect("animation_finished", self, "animation_ended")
  62. func set_animation(animation_name):
  63. if animation_name == current_state:
  64. print ("AnimationPlayer_Manager.gd -- WARNING: animation is already ", animation_name)
  65. return true
  66. if has_animation(animation_name):
  67. if current_state != null:
  68. var possible_animations = states[current_state]
  69. if animation_name in possible_animations:
  70. current_state = animation_name
  71. play(animation_name, -1, animation_speeds[animation_name])
  72. return true
  73. else:
  74. print ("AnimationPlayer_Manager.gd -- WARNING: Cannot change to ", animation_name, " from ", current_state)
  75. return false
  76. else:
  77. current_state = animation_name
  78. play(animation_name, -1, animation_speeds[animation_name])
  79. return true
  80. return false
  81. func animation_ended(anim_name):
  82. # UNARMED transitions
  83. if current_state == "Idle_unarmed":
  84. pass
  85. # KNIFE transitions
  86. elif current_state == "Knife_equip":
  87. set_animation("Knife_idle")
  88. elif current_state == "Knife_idle":
  89. pass
  90. elif current_state == "Knife_fire":
  91. set_animation("Knife_idle")
  92. elif current_state == "Knife_unequip":
  93. set_animation("Idle_unarmed")
  94. # PISTOL transitions
  95. elif current_state == "Pistol_equip":
  96. set_animation("Pistol_idle")
  97. elif current_state == "Pistol_idle":
  98. pass
  99. elif current_state == "Pistol_fire":
  100. set_animation("Pistol_idle")
  101. elif current_state == "Pistol_unequip":
  102. set_animation("Idle_unarmed")
  103. elif current_state == "Pistol_reload":
  104. set_animation("Pistol_idle")
  105. # RIFLE transitions
  106. elif current_state == "Rifle_equip":
  107. set_animation("Rifle_idle")
  108. elif current_state == "Rifle_idle":
  109. pass;
  110. elif current_state == "Rifle_fire":
  111. set_animation("Rifle_idle")
  112. elif current_state == "Rifle_unequip":
  113. set_animation("Idle_unarmed")
  114. elif current_state == "Rifle_reload":
  115. set_animation("Rifle_idle")
  116. func animation_callback():
  117. if callback_function == null:
  118. print ("AnimationPlayer_Manager.gd -- WARNING: No callback function for the animation to call!")
  119. else:
  120. callback_function.call_func()
  121. Lets go over what this script is doing:
  122. _________
  123. Lets start with this script's class variables:
  124. - ``states``: A dictionary for holding our animation states. (Further explanation below)
  125. - ``animation_speeds``: A dictionary for holding all the speeds at which we want to play our animations.
  126. - ``current_state``: A variable for holding the name of the animation state we are currently in.
  127. - ``callback_function``: A variable for holding the callback function. (Further explanation below)
  128. If you are familiar with state machines, then you may have noticed that ``states`` is structured
  129. like a basic state machine. Here is roughly how ``states`` is set up:
  130. ``states`` is a dictionary with the key being the name of the current state, and the value being
  131. an array holding all the animations (states) we can transition to. For example, if we are currently in the
  132. ``Idle_unarmed`` state, we can only transition to ``Knife_equip``, ``Pistol_equip``, ``Rifle_equip``, and
  133. ``Idle_unarmed``.
  134. If we try to transition to a state that is not included in the possible transitions states for the state we are in,
  135. then we get a warning message and the animation does not change. We can also automatically
  136. transition from some states into others, as will be explained further below in ``animation_ended``
  137. .. note:: For the sake of keeping this tutorial simple, we are not using a 'proper'
  138. state machine. If you are interested to know more about state machines,
  139. see the following articles:
  140. - (Python example) https://dev.to/karn/building-a-simple-state-machine-in-python
  141. - (C# example) https://www.codeproject.com/Articles/489136/UnderstandingplusandplusImplementingplusStateplusP
  142. - (Wiki article) https://en.wikipedia.org/wiki/Finite-state_machine
  143. ``animation_speeds`` is how fast each animation will play. Some of the animations are a little slow
  144. and in an effort to make everything look smooth, we need to play them at faster speeds.
  145. .. tip:: Notice that all of the firing animations are faster than their normal speed. Remember this for later!
  146. ``current_state`` will hold the name of the animation state we are currently in.
  147. Finally, ``callback_function`` will be a :ref:`FuncRef <class_FuncRef>` passed in by the player for spawning bullets
  148. at the proper frame of animation. A :ref:`FuncRef <class_FuncRef>` allows us to pass in a function as an argument,
  149. effectively allowing us to call a function from another script, which is how we will use it later.
  150. _________
  151. Now let's look at ``_ready``.
  152. First we are setting our animation to ``Idle_unarmed`` using the ``set_animation`` function,
  153. so we for sure start in that animation.
  154. Next we connect the ``animation_finished`` signal to this script and assign it to call ``animation_ended``.
  155. This means whenever an animation is finished, ``animation_ended`` will be called.
  156. _________
  157. Lets look at ``set_animation`` next.
  158. ``set_animation`` changes the animation to the animation named ``animation_name``
  159. *if* we can transition to it. In other words, if the animation state we are currently in
  160. has the passed in animation state name in ``states``, then we will change to that animation.
  161. Firstly, we check if the passed in animation name is the same name as the animation currently playing.
  162. If they are the same, then we write a warning to the console and return ``true``.
  163. Secondly, we see if :ref:`AnimationPlayer <class_AnimationPlayer>` has the animation with the name ``animation_name`` using ``has_animation``.
  164. If it does not, we return ``false``.
  165. Thirdly, we check whether ``current_state`` is set. If ``current_state`` is *not* currently set, then we
  166. set ``current_state`` to the passed in animation name and tell :ref:`AnimationPlayer <class_AnimationPlayer>` to start playing the animation with
  167. a blend time of ``-1`` at the speed set in ``animation_speeds`` and then we return ``true``.
  168. .. note:: Blend time is how long to blend/mix the two animations together.
  169. By putting in a value of ``-1``, the new animation instantly plays, overriding whatever animation is already playing.
  170. If you put in a value of ``1``, for one second the new animation will play with increasing strength, blending the two animations together for one second
  171. before playing only the new animation. This leads to a smooth transition between animations, which looks great when you are changing from
  172. a walking animation to a running animation.
  173. We set the blend time to ``-1`` because we want to instantly change animations.
  174. If we have a state in ``current_state``, then we get all the possible states we can transition to.
  175. If the animation name is in the list of possible transitions, we set ``current_state`` to the passed
  176. in animation (``animation_name``), tell :ref:`AnimationPlayer <class_AnimationPlayer>` to play the animation with a blend time of ``-1`` at the speed set in
  177. ``animation_speeds`` and return ``true``.
  178. _________
  179. Now lets look at ``animation_ended``.
  180. ``animation_ended`` is the function that will be called by :ref:`AnimationPlayer <class_AnimationPlayer>` when it's done playing an animation.
  181. For certain animation states, we may need to transition into another state when it's finished. To handle this, we
  182. check for every possible animation state. If we need to, we will transition into another state.
  183. .. warning:: If you are using your own animated models, make sure that none of the animations are set
  184. to loop. Looping animations do not send the ``animation_finished`` signal when they reach
  185. the end of the animation and are about to loop again.
  186. .. note:: The transitions in ``animation_ended`` would ideally be part of the data in ``states``, but in
  187. an effort to make the tutorial easier to understand, we'll hard code each state transition
  188. in ``animation_ended``.
  189. _________
  190. Finally, there is ``animation_callback``. This function will be called by a call method track in our animations.
  191. If we have a :ref:`FuncRef <class_FuncRef>` assigned to ``callback_function``, then we call that passed in function. If we do not
  192. have a :ref:`FuncRef <class_FuncRef>` assigned to ``callback_function``, we print out a warning to the console.
  193. .. tip:: Try running ``Testing_Area.tscn`` to make sure there are no runtime issues. If the game runs but nothing
  194. seems to have changed, then everything is working correctly.
  195. Getting the animations ready
  196. ----------------------------
  197. Now that we have a working animation manager, we need to call it from our player script.
  198. Before that, though, we need to set some animation callback tracks in our firing animations.
  199. Open up ``Player.tscn`` if you don't have it open and navigate to the :ref:`AnimationPlayer <class_AnimationPlayer>` node
  200. (``Player`` -> ``Rotation_Helper`` -> ``Model`` -> ``Animation_Player``).
  201. We need to attach a call method track to three of our animations: The firing animation for the pistol, rifle, and knife.
  202. Let's start with the pistol. Click the animation drop down list and select "Pistol_fire".
  203. Now scroll down to the bottom of the list of animation tracks. The final item in the list should read
  204. ``Armature/Skeleton:Left_UpperPointer``. Now above the list, click the "Add track" button, to the left of the time line
  205. .. image:: img/AnimationPlayerAddTrack.png
  206. This will bring up a window with a few choices. We want to add a call method track, so click the
  207. option that reads "Call Method Track". This will open a window showing the entire node tree. Navigate to the
  208. :ref:`AnimationPlayer <class_AnimationPlayer>` node, select it, and press OK.
  209. .. image:: img/AnimationPlayerCallFuncTrack.png
  210. Now at the bottom of list of animation tracks you will have a green track that reads "AnimationPlayer".
  211. Now we need to add the point where we want to call our callback function. Scrub the timeline until you
  212. reach the point where the muzzle starts to flash.
  213. .. note:: The timeline is the window where all the points in our animation are stored. Each of the little
  214. points represents a point of animation data.
  215. Scrubbing the timeline means moving ourselves through the animation. So when we say "scrub the timeline
  216. until you reach a point", what we mean is move through the animation window until you reach the point
  217. on the timeline.
  218. Also, the muzzle of a gun is the end point where the bullet comes out. The muzzle flash is the flash of
  219. light that escapes the muzzle when a bullet is fired. The muzzle is also sometimes referred to as the
  220. barrel of the gun.
  221. .. tip:: For finer control when scrubbing the timeline, press ``control`` and scroll forward with the mouse wheel to zoom in.
  222. Scrolling backwards will zoom out.
  223. You can also change how the timeline scrubbing snaps by changing the value in ``Step (s)`` to a lower/higher value.
  224. Once you get to a point you like, right click on the row for "Animation Player" and press insert key.
  225. In the empty name field, enter ``animation_callback`` and press ``enter``.
  226. .. image:: img/AnimationPlayerInsertKey.png
  227. Now when we are playing this animation the call method track will be triggered at that specific point of the animation.
  228. _________
  229. Let's repeat the process for the rifle and knife firing animations!
  230. .. note:: Because the process is exactly the same as the pistol, the process is going to explained in a little less depth.
  231. Follow the steps from above if you get lost! It is exactly the same, just on a different animation.
  232. Go to the "Rifle_fire" animation from the animation drop down. Add the call method track once you reach the bottom of the
  233. animation track list by clicking the "Add Track" button above the list. Find the point where the muzzle starts
  234. to flash and right click and press ``Insert Key`` to add a call method track point at that position on the track.
  235. Type "animation_callback" into the name field of the pop up which opened and press ``enter``.
  236. Now we need to apply the callback method track to the knife animation. Select the "Knife_fire" animation and scroll to the bottom of the
  237. animation tracks. Click the "Add Track" button above the list and add a method track.
  238. Next find a point around the first third of the animation to place the animation callback method point at.
  239. .. note:: We will not actually be firing the knife, and the animation is a stabbing animation rather than a firing one.
  240. For this tutorial we are reusing the gun firing logic for our knife, so the animation has been named in a style that
  241. is consistent with the other animations.
  242. From there right click on the timeline and click "Insert Key". Put "animation_callback" into the name field and press ``enter``.
  243. .. tip:: Be sure to save your work!
  244. With that done, we are almost ready to start adding the ability to fire to our player script! We need to set up one last scene:
  245. The scene for our bullet object.
  246. Creating the bullet scene
  247. -------------------------
  248. There are several ways to handle a gun's bullets in video games. In this tutorial series,
  249. we will be exploring two of the more common ways: Objects, and raycasts.
  250. _________
  251. One of the two ways is using a bullet object. This will be an object that travels through the world and handles
  252. its own collision code. In this method we create/spawn a bullet object in the direction our gun is facing, and then
  253. it travels forward.
  254. There are several advantages to this method. The first being we do not have to store the bullets in our player. We can simply create the bullet
  255. and then move on, and the bullet itself with handle checking for collisions, sending the proper signal(s) to the object it collides with, and destroying itself.
  256. Another advantage is we can have more complex bullet movement. If we want to make the bullet fall ever so slightly as time goes on, we can make the bullet
  257. controlling script slowly push the bullet towards the ground. Using an object also makes the bullet take time to reach its target, it doesn't instantly
  258. hit whatever it's pointed at. This feels more realistic because nothing in real life moves instantly from one point to another.
  259. One of the huge disadvantages is performance. While having each bullet calculate their own paths and handle their own collision allows for a lot of flexibility,
  260. it comes at the cost of performance. With this method we are calculating every bullet's movement every step, and while this may not be a problem for a few dozen
  261. bullets, it can become a huge problem when you potentially have several hundred bullets.
  262. Despite the performance hit, many first person shooters include some form of object bullets. Rocket launchers are a prime example because in many
  263. first person shooters, rockets do not just instantly explode at their target position. You can also find bullets as objects many times with grenades
  264. because they generally bounce around the world before exploding.
  265. .. note:: While I cannot say for sure this is the case, these games *probably* use bullet objects in some form or another:
  266. (These are entirely from my observations. **They may be entirely wrong**. I have never worked on **any** of the following games)
  267. - Halo (Rocket launchers, fragmentation grenades, sniper rifles, brute shot, and more)
  268. - Destiny (Rocket launchers, grenades, fusion rifles, sniper rifles, super moves, and more)
  269. - Call of Duty (Rocket launchers, grenades, ballistic knives, crossbows, and more)
  270. - Battlefield (Rocket launchers, grenades, claymores, mortars, and more)
  271. Another disadvantage with bullet objects is networking. Bullet objects have to sync the positions (at least) with all the clients that are connected
  272. to the server.
  273. While we are not implementing any form of networking (as that would be in its own entire tutorial series), it is a consideration
  274. to keep in mind when creating your first person shooter, especially if you plan on adding some form of networking in the future.
  275. _________
  276. The other way of handling bullet collisions we will be looking at is raycasting.
  277. This method is extremely common in guns that have fast moving bullets that rarely change trajectory over time.
  278. Instead of creating a bullet object and sending it through space, we instead send a ray starting from the barrel/muzzle of the gun forwards.
  279. We set the raycast's origin to the starting position of the bullet, and based on the length we can adjust how far the bullet 'travels' through space.
  280. .. note:: While I cannot say for sure this is the case, these games *probably* use raycasts in some form or another:
  281. (These are entirely from my observations. **They may be entirely wrong**. I have never worked on **any** of the following games)
  282. - Halo (Assault rifles, DMRs, battle rifles, covenant carbine, spartan laser, and more)
  283. - Destiny (Auto rifles, pulse rifles, scout rifles, hand cannons, machine guns, and more)
  284. - Call of Duty (Assault rifles, light machine guns, sub machine guns, pistols, and more)
  285. - Battlefield (Assault rifles, SMGs, carbines, pistols, and more)
  286. One huge advantage of this method is that it's light on performance.
  287. Sending a couple hundred rays through space is *much* easier for the computer to calculate than sending a couple hundred
  288. bullet objects.
  289. Another advantage is we can instantly know if we've hit something or not exactly when we call for it. For networking this is important because we do not need
  290. to sync the bullet movements over the Internet, we only need to send whether or not the raycast hit.
  291. Raycasting does have some disadvantages, though. One major disadvantage is we cannot easily cast a ray in anything but a linear line.
  292. This means we can only fire in a straight line for however long our ray length is. You can create the illusion of bullet movement by casting
  293. multiple rays at different positions, but not only is this hard to implement in code, it is also heavier on performance.
  294. Another disadvantage is we cannot see the bullet. With bullet objects we can actually see the bullet travel through space if we attach a mesh
  295. to it, but because raycasts happen instantly, we do not have a decent way of showing the bullets. You could draw a line from the origin of the
  296. raycast to the point where the raycast collided, and that is one popular way of showing raycasts. Another way is simply not drawing the raycast
  297. at all, because theoretically the bullets move so fast our eyes could not see it anyway.
  298. _________
  299. Let's get the bullet object set up. This is what our pistol will create when the "Pistol_fire" animation callback function is called.
  300. Open up ``Bullet_Scene.tscn``. The scene contains :ref:`Spatial <class_Spatial>` node called bullet, with a :ref:`MeshInstance <class_MeshInstance>`
  301. and an :ref:`Area <class_Area>` with a :ref:`CollisionShape <class_CollisionShape>` children to it.
  302. Create a new script called ``Bullet_script.gd`` and attach it to the ``Bullet`` :ref:`Spatial <class_Spatial>`.
  303. We are going to move the entire bullet object at the root (``Bullet``). We will be using the :ref:`Area <class_Area>` to check whether or not we've collided with something
  304. .. note:: Why are we using an :ref:`Area <class_Area>` and not a :ref:`RigidBody <class_RigidBody>`? The main reason we're not using a :ref:`RigidBody <class_RigidBody>`
  305. is because we do not want the bullet to interact with other :ref:`RigidBody <class_RigidBody>` nodes.
  306. By using an :ref:`Area <class_Area>` we are ensuring that none of the other :ref:`RigidBody <class_RigidBody>` nodes, including other bullets, will be effected.
  307. Another reason is simply because it is easier to detect collisions with an :ref:`Area <class_Area>`!
  308. Here's the script that will control our bullet:
  309. ::
  310. extends Spatial
  311. var BULLET_SPEED = 70
  312. var BULLET_DAMAGE = 15
  313. const KILL_TIMER = 4
  314. var timer = 0
  315. var hit_something = false
  316. func _ready():
  317. $Area.connect("body_entered", self, "collided")
  318. func _physics_process(delta):
  319. var forward_dir = global_transform.basis.z.normalized()
  320. global_translate(forward_dir * BULLET_SPEED * delta)
  321. timer += delta
  322. if timer >= KILL_TIMER:
  323. queue_free()
  324. func collided(body):
  325. if hit_something == false:
  326. if body.has_method("bullet_hit"):
  327. body.bullet_hit(BULLET_DAMAGE, global_transform)
  328. hit_something = true
  329. queue_free()
  330. Let's go through the script:
  331. _________
  332. First we define a few class variables:
  333. - ``BULLET_SPEED``: The speed at which the bullet travels.
  334. - ``BULLET_DAMAGE``: The damage the bullet will cause to anything with which it collides.
  335. - ``KILL_TIMER``: How long the bullet can last without hitting anything.
  336. - ``timer``: A float for tracking how long the bullet has been alive.
  337. - ``hit_something``: A boolean for tracking whether or not we've hit something.
  338. With the exception of ``timer`` and ``hit_something``, all of these variables
  339. change how the bullet interacts with the world.
  340. .. note:: The reason we are using a kill timer is so we do not have a case where we
  341. get a bullet travelling forever. By using a kill timer, we can ensure that
  342. no bullets will travel forever and consume resources.
  343. .. tip:: As in :ref:`doc_fps_tutorial_part_one`, we have a couple all uppercase class variables. The reason behind this is the same
  344. as the reason given in :ref:`doc_fps_tutorial_part_one`: We want to treat these variables like constants, but we want to be
  345. able to change them. In this case we will later need to change the damage and speed of these bullets,
  346. so we need them to be variables and not constants.
  347. _________
  348. In ``_ready`` we set the area's ``body_entered`` signal to ourself so that it calls
  349. the ``collided`` function when a body enters the area.
  350. _________
  351. ``_physics_process`` gets the bullet's local ``Z`` axis. If you look at the scene
  352. in local mode, you will find that the bullet faces the positive local ``Z`` axis.
  353. Next we translate the entire bullet by that forward direction, multiplying in our speed and delta time.
  354. After that we add delta time to our timer and check whether the timer has reached a value as big or greater
  355. than our ``KILL_TIME`` constant. If it has, we use ``queue_free`` to free the bullet.
  356. _________
  357. In ``collided`` we check whether we've hit something yet.
  358. Remember that ``collided`` is only called when a body has entered the :ref:`Area <class_Area>` node.
  359. If the bullet has not already collided with something, we then proceed to check if the body the bullet has collided
  360. with has a function/method called ``bullet_hit``. If it does, we call it and pass in the bullet's damage and the bullet's global transform
  361. so we can get the bullet's rotation and position.
  362. .. note:: in ``collided``, the passed in body can be a :ref:`StaticBody <class_StaticBody>`,
  363. :ref:`RigidBody <class_RigidBody>`, or :ref:`KinematicBody <class_KinematicBody>`
  364. We set the Bullet's ``hit_something`` variable to ``true`` because regardless of whether or not the body that
  365. the bullet collided with has the ``bullet_hit`` function/method, it has hit something and so we need to make sure the bullet does not hit anything else.
  366. Then we free the bullet using ``queue_free``.
  367. .. tip:: You may be wondering why we even have a ``hit_something`` variable if we
  368. free the bullet using ``queue_free`` as soon as it hits something.
  369. The reason we need to track whether we've hit something or not is because ``queue_free``
  370. does not immediately free the node, so the bullet could collide with another body
  371. before Godot has a chance to free it. By tracking whether the bullet has hit something,
  372. we can make sure that the bullet will only hit one object.
  373. _________
  374. Before we start programming the player again, let's take a quick look at ``Player.tscn``.
  375. Open up ``Player.tscn`` again.
  376. Expand ``Rotation_Helper`` and notice how it has two nodes: ``Gun_Fire_Points`` and
  377. ``Gun_Aim_Point``.
  378. ``Gun_aim_point`` is the point that the bullets will be aiming at. Notice how it
  379. is lined up with the center of the screen and pulled a distance forward on the Z
  380. axis. ``Gun_aim_point`` will serve as the point where the bullets will for sure collide
  381. with as it goes along.
  382. .. note:: There is a invisible mesh instance for debugging purposes. The mesh is
  383. a small sphere that visually shows at which target the bullets will be aiming.
  384. Open up ``Gun_Fire_Points`` and you'll find three more :ref:`Spatial <class_Spatial>` nodes, one for each
  385. weapon.
  386. Open up ``Rifle_Point`` and you'll find a :ref:`Raycast <class_Raycast>` node. This is where
  387. we will be sending the raycasts for our rifle's bullets.
  388. The length of the raycast will dictate how far our bullets will travel.
  389. We are using a :ref:`Raycast <class_Raycast>` node to handle the rifle's bullet because
  390. we want to fire lots of bullets quickly. If we use bullet objects, it is quite possible
  391. we could run into performance issues on older machines.
  392. .. note:: If you are wondering from where the positions of the points came, they
  393. are the rough positions of the ends of each weapon. You can see this by
  394. going to ``AnimationPlayer``, selecting one of the firing animations
  395. and scrubbing through the timeline. The point for each weapon should mostly line
  396. up with the end of each weapon.
  397. Open up ``Knife_Point`` and you'll find an :ref:`Area <class_Area>` node. We are using an :ref:`Area <class_Area>` for the knife
  398. because we only care for all the bodies close to us, and because our knife does
  399. not fire into space. If we were making a throwing knife, we would likely spawn a bullet
  400. object that looks like a knife.
  401. Finally, we have ``Pistol_Point``. This is the point where we will be creating/instancing
  402. our bullet objects. We do not need any additional nodes here, as the bullet handles all
  403. of its own collision detection.
  404. Now that we've seen how we will handle our other weapons, and where we will spawn the bullets,
  405. let's start working on making them work.
  406. .. note:: You can also look at the HUD nodes if you want. There is nothing fancy there and other
  407. than using a single :ref:`Label <class_Label>`, we will not be touching any of those nodes.
  408. Check :ref:`doc_design_interfaces_with_the_control_nodes` for a tutorial on using GUI nodes.
  409. Creating the first weapon
  410. -------------------------
  411. Lets write the code for each of our weapons, starting with the pistol.
  412. Select ``Pistol_Point`` (``Player`` -> ``Rotation_Helper`` -> ``Gun_Fire_Points`` -> ``Pistol_Point``) and create a new script called ``Weapon_Pistol.gd``.
  413. Add the following code to ``Weapon_Pistol.gd``:
  414. ::
  415. extends Spatial
  416. const DAMAGE = 15
  417. const IDLE_ANIM_NAME = "Pistol_idle"
  418. const FIRE_ANIM_NAME = "Pistol_fire"
  419. var is_weapon_enabled = false
  420. var bullet_scene = preload("Bullet_Scene.tscn")
  421. var player_node = null
  422. func _ready():
  423. pass
  424. func fire_weapon():
  425. var clone = bullet_scene.instance()
  426. var scene_root = get_tree().root.get_children()[0]
  427. scene_root.add_child(clone)
  428. clone.global_transform = self.global_transform
  429. clone.scale = Vector3(4, 4, 4)
  430. clone.BULLET_DAMAGE = DAMAGE
  431. func equip_weapon():
  432. if player_node.animation_manager.current_state == IDLE_ANIM_NAME:
  433. is_weapon_enabled = true
  434. return true
  435. if player_node.animation_manager.current_state == "Idle_unarmed":
  436. player_node.animation_manager.set_animation("Pistol_equip")
  437. return false
  438. func unequip_weapon():
  439. if player_node.animation_manager.current_state == IDLE_ANIM_NAME:
  440. if player_node.animation_manager.current_state != "Pistol_unequip":
  441. player_node.animation_manager.set_animation("Pistol_unequip")
  442. if player_node.animation_manager.current_state == "Idle_unarmed":
  443. is_weapon_enabled = false
  444. return true
  445. else:
  446. return false
  447. Let's go over how the script works.
  448. _________
  449. First we define some class variables we'll need in the script:
  450. * ``DAMAGE``: The amount of damage a single bullet does.
  451. * ``IDLE_ANIM_NAME``: The name of the pistol's idle animation.
  452. * ``FIRE_ANIM_NAME``: The name of the pistol's fire animation.
  453. * ``is_weapon_enabled``: A variable for checking whether this weapon is in use/enabled.
  454. * ``bullet_scene``: The bullet scene we worked on earlier.
  455. * ``player_node``: A variable to hold ``Player.gd``.
  456. The reason we define most of these variables is so we can use them in ``Player.gd``.
  457. Each of the weapons we'll make will have all these variables (minus ``bullet_scene``) so we have
  458. a consistent interface to interact with in ``Player.gd``. By using the same variables/functions in each
  459. weapon, we can interact with them without having to know which weapon we are using, which makes our code
  460. much more modular because we can add weapons without having to change much of the code in ``Player.gd`` and it will just work.
  461. We could write all the code in ``Player.gd``, but then ``Player.gd`` will get increasingly harder to manage as we add weapons.
  462. By using a modular design with a consistent interface, we can keep ``Player.gd`` nice and neat, while also making it easier to add/remove/modify weapons.
  463. _________
  464. In ``_ready`` we simply pass over it.
  465. There is one thing of note though, an assumption that we'll fill in ``Player.gd`` at some point.
  466. We are going to assume that ``Player.gd`` will pass themselves in before calling any of the functions in ``Weapon_Pistol.gd``.
  467. While this can lead to situations where the player does not pass themselves in (because we forget), we would have to have a long string
  468. of ``get_parent`` calls to traverse up the scene tree to retrieve the player. This does not look pretty (``get_parent().get_parent().get_parent()`` and so on)
  469. and it is relatively safe to assume we will remember to pass ourselves to each weapon in ``Player.gd``.
  470. _________
  471. Next let's look at ``fire_weapon``:
  472. The first thing we do is instance the bullet scene we made earlier.
  473. .. tip:: By instancing the scene, we are creating a new node holding all the node(s) in the scene we instanced, effectively cloning that scene.
  474. Then we add a ``clone`` to the first child node of the root of the scene we are currently in. By doing this, we're making it a child of the root node of the currently loaded scene.
  475. In other words, we are adding a ``clone`` as a child of the first node (whatever is at the top of the scene tree) in the currently loaded/opened scene.
  476. If the currently loaded/open scene is ``Testing_Area.tscn``, we'd be adding our ``clone`` as a child of ``Testing_Area``, the root node in that scene.
  477. .. warning:: As mentioned later below in the section on adding sounds, this method makes an assumption. This will be explained later
  478. in the section on adding sounds in :ref:`doc_fps_tutorial_part_three`
  479. Next we set the global transform of the clone to the ``Pistol_Aim_Point``'s global transform. The reason we do this is so the bullet is spawned at the end of the pistol.
  480. You can see that ``Pistol_Aim_Point`` is positioned right at the end of the pistol by clicking the :ref:`AnimationPlayer <class_AnimationPlayer>` and
  481. scrolling through ``Pistol_fire``. You'll find the position is more or less at the end of the pistol when it fires.
  482. Next we scale it up by a factor of ``4`` because the bullet scene is a little too small by default.
  483. Then we set the bullet's damage (``BULLET_DAMAGE``) to the amount of damage a single pistol bullet does (``DAMAGE``).
  484. _________
  485. Now let's look at ``equip_weapon``:
  486. The first thing we do is check to see whether the animation manager is in the pistol's idle animation.
  487. If we are in the pistol's idle animation, we set ``is_weapon_enabled`` to ``true`` and return ``true`` because the pistol has successfully
  488. been equipped.
  489. Because we know our pistol's ``equip`` animation automatically transitions to the pistol's idle animation, if we are in the pistol's
  490. idle animation the pistol must have finished playing the equip animation.
  491. .. note:: We know these animations will transition because we wrote the code to make them transition in ``Animation_Manager.gd``
  492. Next we check to see if the player is in the ``Idle_unarmed`` animation state. Because all unequipping animations go to this state, and because any
  493. weapon can be equipped from this state, we change animations to ``Pistol_equip`` if the player is in the ``Idle_unarmed`` state.
  494. Since we know ``Pistol_equip`` will transition to ``Pistol_idle``, we do not need to do any more additional processing for equipping weapons,
  495. but since we were not able to equip the pistol yet, we return ``false``.
  496. _________
  497. Finally, let's look at ``unequip_weapon``:
  498. ``unequip_weapon`` is similar to ``equip_weapon``, but instead we're checking things in reverse.
  499. First we check to see whether the player is in the idle animation state. Then we check to make sure the player is not in the ``Pistol_unequip`` animation.
  500. If the player is not in the ``Pistol_unequip`` animation, we want to play the ``pistol_unequip`` animation.
  501. .. note:: You may be wondering why we are checking to see whether the player is in the pistol's idle animation, and then making sure the player is not unequipping right after.
  502. The reason behind the additional check is because we could (in rare cases) call ``unequip_weapon`` twice before we've had a chance to process ``set_animation``,
  503. so we add this additional check to make sure the unequip animation plays.
  504. Next we check to see whether the player is in ``Idle_unarmed``, which is the animation state we will transition into from ``Pistol_unequip``. If the player is in ``Idle_unarmed``, then we set
  505. ``is_weapon_enabled`` to ``false`` since we are no longer using this weapon, and return ``true`` because we have successfully unequipped the pistol.
  506. If the player is not in ``Idle_unarmed``, we return ``false`` because we have not yet successfully unequipped the pistol.
  507. Creating the other two weapons
  508. ------------------------------
  509. Now that we have all the code we'll need for the pistol, let's add the code for the rifle and knife next.
  510. Select ``Rifle_Point`` (``Player`` -> ``Rotation_Helper`` -> ``Gun_Fire_Points`` -> ``Rifle_Point``) and create a new script called ``Weapon_Rifle.gd``,
  511. then add the following:
  512. ::
  513. extends Spatial
  514. const DAMAGE = 4
  515. const IDLE_ANIM_NAME = "Rifle_idle"
  516. const FIRE_ANIM_NAME = "Rifle_fire"
  517. var is_weapon_enabled = false
  518. var player_node = null
  519. func _ready():
  520. pass
  521. func fire_weapon():
  522. var ray = $Ray_Cast
  523. ray.force_raycast_update()
  524. if ray.is_colliding():
  525. var body = ray.get_collider()
  526. if body == player_node:
  527. pass
  528. elif body.has_method("bullet_hit"):
  529. body.bullet_hit(DAMAGE, ray.global_transform)
  530. func equip_weapon():
  531. if player_node.animation_manager.current_state == IDLE_ANIM_NAME:
  532. is_weapon_enabled = true
  533. return true
  534. if player_node.animation_manager.current_state == "Idle_unarmed":
  535. player_node.animation_manager.set_animation("Rifle_equip")
  536. return false
  537. func unequip_weapon():
  538. if player_node.animation_manager.current_state == IDLE_ANIM_NAME:
  539. if player_node.animation_manager.current_state != "Rifle_unequip":
  540. player_node.animation_manager.set_animation("Rifle_unequip")
  541. if player_node.animation_manager.current_state == "Idle_unarmed":
  542. is_weapon_enabled = false
  543. return true
  544. return false
  545. Most of this is exactly the same as ``Weapon_Pistol.gd``, so we're only going to look at what's changed: ``fire_weapon``.
  546. The first thing we do is get the :ref:`Raycast <class_Raycast>` node, which is a child of ``Rifle_Point``.
  547. Next we force the :ref:`Raycast <class_Raycast>` to update using ``force_raycast_update``. This will force the :ref:`Raycast <class_Raycast>` to detect collisions when we call it, meaning
  548. we get a frame perfect collision check with the 3D physics world.
  549. Then we check to see if the :ref:`Raycast <class_Raycast>` collided with something.
  550. If the :ref:`Raycast <class_Raycast>` has collided with something, we first get the collision body it collided with. This can be a :ref:`StaticBody <class_StaticBody>`,
  551. :ref:`RigidBody <class_RigidBody>`, or a :ref:`KinematicBody <class_KinematicBody>`.
  552. Next we want to make sure the body we've collided with is not the player, since we (probably) do not want to give the player the ability to shoot themselves in the foot.
  553. If the body is not the player, we then check to see if it has a function/method called ``bullet_hit``. If it does, we call it and pass in the amount of
  554. damage this bullet does (``DAMAGE``), and the global transform of the :ref:`Raycast <class_Raycast>` so we can tell from which direction the bullet came.
  555. _________
  556. Now all we need to do is write the code for the knife.
  557. Select ``Knife_Point`` (``Player`` -> ``Rotation_Helper`` -> ``Gun_Fire_Points`` -> ``Knife_Point``) and create a new script called ``Weapon_Knife.gd``,
  558. then add the following:
  559. ::
  560. extends Spatial
  561. const DAMAGE = 40
  562. const IDLE_ANIM_NAME = "Knife_idle"
  563. const FIRE_ANIM_NAME = "Knife_fire"
  564. var is_weapon_enabled = false
  565. var player_node = null
  566. func _ready():
  567. pass
  568. func fire_weapon():
  569. var area = $Area
  570. var bodies = area.get_overlapping_bodies()
  571. for body in bodies:
  572. if body == player_node:
  573. continue
  574. if body.has_method("bullet_hit"):
  575. body.bullet_hit(DAMAGE, area.global_transform)
  576. func equip_weapon():
  577. if player_node.animation_manager.current_state == IDLE_ANIM_NAME:
  578. is_weapon_enabled = true
  579. return true
  580. if player_node.animation_manager.current_state == "Idle_unarmed":
  581. player_node.animation_manager.set_animation("Knife_equip")
  582. return false
  583. func unequip_weapon():
  584. if player_node.animation_manager.current_state == IDLE_ANIM_NAME:
  585. player_node.animation_manager.set_animation("Knife_unequip")
  586. if player_node.animation_manager.current_state == "Idle_unarmed":
  587. is_weapon_enabled = false
  588. return true
  589. return false
  590. As with ``Weapon_Rifle.gd``, the only differences are in ``fire_weapon``, so let's look at that:
  591. The first thing we do is get the :ref:`Area <class_Area>` child node of ``Knife_Point``.
  592. Next we want to get all the collision bodies inside the :ref:`Area <class_Area>` using ``get_overlapping_bodies``. This will return a
  593. list of every body that touches the :ref:`Area <class_Area>`.
  594. We next want to go through each of those bodies.
  595. First we check to make sure the body is not the player, because we do not want to let the player be able to stab themselves. If the body is the player,
  596. we use ``continue`` so we jump and look at the next body in ``bodies``.
  597. If we have not jumped to the next body, we then check to see if the body has the ``bullet_hit`` function/method. If it does,
  598. we call it, passing in the amount of damage a single knife swipe does (``DAMAGE``) and the global transform of the :ref:`Area <class_Area>`.
  599. .. note:: While we could attempt to calculate a rough location for where the knife hit exactly, we
  600. are not going to because using the :ref:`Area <class_Area>`'s position works well enough and the extra time
  601. needed to calculate a rough position for each body is not worth the effort.
  602. Making the weapons work
  603. -----------------------
  604. Let's start making the weapons work in ``Player.gd``.
  605. First let's start by adding some class variables we'll need for the weapons:
  606. ::
  607. # Place before _ready
  608. var animation_manager
  609. var current_weapon_name = "UNARMED"
  610. var weapons = {"UNARMED":null, "KNIFE":null, "PISTOL":null, "RIFLE":null}
  611. const WEAPON_NUMBER_TO_NAME = {0:"UNARMED", 1:"KNIFE", 2:"PISTOL", 3:"RIFLE"}
  612. const WEAPON_NAME_TO_NUMBER = {"UNARMED":0, "KNIFE":1, "PISTOL":2, "RIFLE":3}
  613. var changing_weapon = false
  614. var changing_weapon_name = "UNARMED"
  615. var health = 100
  616. var UI_status_label
  617. Let's go over what these new variables will do:
  618. - ``animation_manager``: This will hold the :ref:`AnimationPlayer <class_AnimationPlayer>` node and its script, which we wrote previously.
  619. - ``current_weapon_name``: The name of the weapon we are currently using. It has four possible values: ``UNARMED``, ``KNIFE``, ``PISTOL``, and ``RIFLE``.
  620. - ``weapons``: A dictionary that will hold all the weapon nodes.
  621. - ``WEAPON_NUMBER_TO_NAME``: A dictionary allowing us to convert from a weapon's number to its name. We'll use this for changing weapons.
  622. - ``WEAPON_NAME_TO_NUMBER``: A dictionary allowing us to convert from a weapon's name to its number. We'll use this for changing weapons.
  623. - ``changing_weapon``: A boolean to track whether or not we are changing guns/weapons.
  624. - ``changing_weapon_name``: The name of the weapon we want to change to.
  625. - ``health``: How much health our player has. In this part of the tutorial we will not be using it.
  626. - ``UI_status_label``: A label to show how much health we have, and how much ammo we have both in our gun and in reserve.
  627. _________
  628. Next we need to add a few things in ``_ready``. Here's the new ``_ready`` function:
  629. ::
  630. func _ready():
  631. camera = $Rotation_Helper/Camera
  632. rotation_helper = $Rotation_Helper
  633. animation_manager = $Rotation_Helper/Model/Animation_Player
  634. animation_manager.callback_function = funcref(self, "fire_bullet")
  635. Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
  636. weapons["KNIFE"] = $Rotation_Helper/Gun_Fire_Points/Knife_Point
  637. weapons["PISTOL"] = $Rotation_Helper/Gun_Fire_Points/Pistol_Point
  638. weapons["RIFLE"] = $Rotation_Helper/Gun_Fire_Points/Rifle_Point
  639. var gun_aim_point_pos = $Rotation_Helper/Gun_Aim_Point.global_transform.origin
  640. for weapon in weapons:
  641. var weapon_node = weapons[weapon]
  642. if weapon_node != null:
  643. weapon_node.player_node = self
  644. weapon_node.look_at(gun_aim_point_pos, Vector3(0, 1, 0))
  645. weapon_node.rotate_object_local(Vector3(0, 1, 0), deg2rad(180))
  646. current_weapon_name = "UNARMED"
  647. changing_weapon_name = "UNARMED"
  648. UI_status_label = $HUD/Panel/Gun_label
  649. flashlight = $Rotation_Helper/Flashlight
  650. Let's go over what's changed.
  651. First we get the :ref:`AnimationPlayer <class_AnimationPlayer>` node and assign it to the ``animation_manager`` variable. Then we set the callback function
  652. to a :ref:`FuncRef <class_FuncRef>` that will call the player's ``fire_bullet`` function. Right now we haven't written the ``fire_bullet`` function,
  653. but we'll get there soon.
  654. Next we get all the weapon nodes and assign them to ``weapons``. This will allow us to access the weapon nodes only with their name
  655. (``KNIFE``, ``PISTOL``, or ``RIFLE``).
  656. We then get ``Gun_Aim_Point``'s global position so we can rotate the player's weapons to aim at it.
  657. Then we go through each weapon in ``weapons``.
  658. We first get the weapon node. If the weapon node is not ``null``, we then set its ``player_node`` variable to this script (``Player.gd``).
  659. Then we have it look at ``gun_aim_point_pos`` using the ``look_at`` function, and then rotate it by ``180`` degrees on the ``Y`` axis.
  660. .. note:: We rotate all of those weapon points by ``180`` degrees on their ``Y`` axis because our camera is pointing backwards.
  661. If we did not rotate all of these weapon points by ``180`` degrees, all of the weapons would fire backwards.
  662. Then we set ``current_weapon_name`` and ``changing_weapon_name`` to ``UNARMED``.
  663. Finally, we get the UI :ref:`Label <class_Label>` from our HUD.
  664. _________
  665. Let's add a new function call to ``_physics_process`` so we can change weapons. Here's the new code:
  666. ::
  667. func _physics_process(delta):
  668. process_input(delta)
  669. process_movement(delta)
  670. process_changing_weapons(delta)
  671. Now we will call ``process_changing_weapons``.
  672. _________
  673. Now let's add all the player input code for the weapons in ``process_input``. Add the following code:
  674. ::
  675. # ----------------------------------
  676. # Changing weapons.
  677. var weapon_change_number = WEAPON_NAME_TO_NUMBER[current_weapon_name]
  678. if Input.is_key_pressed(KEY_1):
  679. weapon_change_number = 0
  680. if Input.is_key_pressed(KEY_2):
  681. weapon_change_number = 1
  682. if Input.is_key_pressed(KEY_3):
  683. weapon_change_number = 2
  684. if Input.is_key_pressed(KEY_4):
  685. weapon_change_number = 3
  686. if Input.is_action_just_pressed("shift_weapon_positive"):
  687. weapon_change_number += 1
  688. if Input.is_action_just_pressed("shift_weapon_negative"):
  689. weapon_change_number -= 1
  690. weapon_change_number = clamp(weapon_change_number, 0, WEAPON_NUMBER_TO_NAME.size() - 1)
  691. if changing_weapon == false:
  692. if WEAPON_NUMBER_TO_NAME[weapon_change_number] != current_weapon_name:
  693. changing_weapon_name = WEAPON_NUMBER_TO_NAME[weapon_change_number]
  694. changing_weapon = true
  695. # ----------------------------------
  696. # ----------------------------------
  697. # Firing the weapons
  698. if Input.is_action_pressed("fire"):
  699. if changing_weapon == false:
  700. var current_weapon = weapons[current_weapon_name]
  701. if current_weapon != null:
  702. if animation_manager.current_state == current_weapon.IDLE_ANIM_NAME:
  703. animation_manager.set_animation(current_weapon.FIRE_ANIM_NAME)
  704. # ----------------------------------
  705. Let's go over the additions, starting with how we're changing weapons.
  706. First we get the current weapon's number and assign it to ``weapon_change_number``.
  707. Then we check to see if any of the number keys (keys 1-4) are pressed. If they are, we set
  708. ``weapon_change_number`` to the value mapped at that key.
  709. .. note:: The reason key 1 is mapped to ``0`` is because the first element in a list is mapped to zero, not one. Most list/array accessors
  710. in most programming languages start at ``0`` instead of ``1``. See https://en.wikipedia.org/wiki/Zero-based_numbering for more information.
  711. Next we check to see if ``shift_weapon_positive`` or ``shift_weapon_negative`` is pressed. If one of them is, we add/subtract ``1`` from
  712. ``weapon_change_number``.
  713. Because the player may have shifted ``weapon_change_number`` outside of the number of weapons the player has, we clamp it so it cannot exceed the maximum number of weapons
  714. the player has and it ensures ``weapon_change_number`` is ``0`` or more.
  715. Then we check to make sure the player is not already changing weapons. If the player is not, we then check to see if the weapon the player wants to change to
  716. is a new weapon and not the weapon the player is currently using. If the weapon the player is wanting to change to is a new weapon, we then set ``changing_weapon_name`` to
  717. the weapon at ``weapon_change_number`` and set ``changing_weapon`` to true.
  718. For firing the weapon we first check to see if the ``fire`` action is pressed.
  719. Then we check to make sure the player is not changing weapons.
  720. Next we get the weapon node for the current weapon.
  721. If the current weapon node does not equal null, and the player is in its ``IDLE_ANIM_NAME`` state, we set the player's animation
  722. to the current weapon's ``FIRE_ANIM_NAME``.
  723. _________
  724. Let's add ``process_changing_weapons`` next.
  725. Add the following code:
  726. ::
  727. func process_changing_weapons(delta):
  728. if changing_weapon == true:
  729. var weapon_unequipped = false
  730. var current_weapon = weapons[current_weapon_name]
  731. if current_weapon == null:
  732. weapon_unequipped = true
  733. else:
  734. if current_weapon.is_weapon_enabled == true:
  735. weapon_unequipped = current_weapon.unequip_weapon()
  736. else:
  737. weapon_unequipped = true
  738. if weapon_unequipped == true:
  739. var weapon_equipped = false
  740. var weapon_to_equip = weapons[changing_weapon_name]
  741. if weapon_to_equip == null:
  742. weapon_equipped = true
  743. else:
  744. if weapon_to_equip.is_weapon_enabled == false:
  745. weapon_equipped = weapon_to_equip.equip_weapon()
  746. else:
  747. weapon_equipped = true
  748. if weapon_equipped == true:
  749. changing_weapon = false
  750. current_weapon_name = changing_weapon_name
  751. changing_weapon_name = ""
  752. Let's go over what's happening here:
  753. The first thing we do is make sure we've received input to change weapons. We do this by making sure ``changing_weapons`` is ``true``.
  754. Next we define a variable (``weapon_unequipped``) so we can check whether the current weapon has been successfully unequipped or not.
  755. Then we get the current weapon from ``weapons``.
  756. If the current weapon is not ``null``, then we need to check whether the weapon is enabled. If the weapon is enabled, we call its ``unequip_weapon`` function
  757. so it will start the unequip animation. If the weapon is not enabled, we set ``weapon_unequipped`` to ``true`` because the weapon has successfully been unequipped.
  758. If the current weapon is ``null``, then we can simply set ``weapon_unequipped`` to ``true``. The reason we do this check is because there is no weapon script/node for
  759. ``UNARMED``, but there is also no animations for ``UNARMED``, so we can just start equipping the weapon the player wants to change to.
  760. If the player has successfully unequipped the current weapon (``weapon_unequipped == true``), we need to equip the new weapon.
  761. First we define a new variable (``weapon_equipped``) for tracking whether the player has successfully equipped the new weapon or not.
  762. Then we get the weapon the player wants to change to. If the weapon the player wants to change to is not ``null``, we then check to see whether it's enabled or not.
  763. If it is not enabled, we call its ``equip_weapon`` function so it starts to equip the weapon. If the weapon is enabled, we set ``weapon_equipped`` to ``true``.
  764. If the weapon the player wants to change to is ``null``, we simply set ``weapon_equipped`` to ``true`` because we do not have any node/script for ``UNARMED``,
  765. nor do we have any animations.
  766. Finally, we check to see whether the player has successfully equipped the new weapon. If (s)he has done so, we set ``changing_weapon`` to ``false`` because the player is no
  767. longer changing weapons.
  768. We also set ``current_weapon_name`` to ``changing_weapon_name`` since the current weapon has changed, and then we set ``changing_weapon_name`` to an empty string.
  769. _________
  770. Now, we need to add one more function to the player, and then the player is ready to start firing the weapons!
  771. We need to add ``fire_bullet``, which will be called by the :ref:`AnimationPlayer <class_AnimationPlayer>` at those
  772. points we set earlier in the :ref:`AnimationPlayer <class_AnimationPlayer>` function track:
  773. ::
  774. func fire_bullet():
  775. if changing_weapon == true:
  776. return
  777. weapons[current_weapon_name].fire_weapon()
  778. Let's go over what this function does:
  779. First we check to see whether the player is changing weapons. If the player is changing weapons, we do not want shoot, so we ``return``.
  780. .. tip:: Calling ``return`` stops the rest of the function from being called. In this case, we are not returning a variable
  781. because we are only interested in not running the rest of the code, and because we are not looking for a returned
  782. variable either when we call this function.
  783. Then we tell the current weapon the player is using to fire by calling its ``fire_weapon`` function.
  784. .. tip:: Remember how we mentioned the speed of the animations for firing was faster than
  785. the other animations? By changing the firing animation speeds, you can change how
  786. fast the weapon fires bullets!
  787. _______
  788. Before we are ready to test our new weapons, we still have a little bit of work to do.
  789. Creating some test subjects
  790. ---------------------------
  791. Create a new script by going to the scripting window, clicking "file", and selecting new.
  792. Name this script ``RigidBody_hit_test`` and make sure it extends :ref:`RigidBody <class_RigidBody>`.
  793. Now we need to add this code:
  794. ::
  795. extends RigidBody
  796. const BASE_BULLET_BOOST = 9;
  797. func _ready():
  798. pass
  799. func bullet_hit(damage, bullet_global_trans):
  800. var direction_vect = bullet_global_trans.basis.z.normalized() * BASE_BULLET_BOOST;
  801. apply_impulse((bullet_global_trans.origin - global_transform.origin).normalized(), direction_vect * damage)
  802. Let's go over how ``bullet_hit`` works:
  803. First we get the bullet's forward directional vector. This is so we can tell from which direction the bullet will hit the :ref:`RigidBody <class_RigidBody>`.
  804. We will use this to push the :ref:`RigidBody <class_RigidBody>` in the same direction as the bullet.
  805. .. note:: We need to boost the directional vector by ``BASE_BULLET_BOOST`` so the bullets pack a bit more of a punch
  806. and move the :ref:`RigidBody <class_RigidBody>` nodes in a visible way. You can just set ``BASE_BULLET_BOOST`` to lower or higher values if you want
  807. less or more of a reaction when the bullets collide with the :ref:`RigidBody <class_RigidBody>`.
  808. Then we apply an impulse using ``apply_impulse``.
  809. First, we need to calculate the position for the impulse.
  810. Because ``apply_impulse`` takes a vector relative to the :ref:`RigidBody <class_RigidBody>`, we need to calculate the distance from
  811. the :ref:`RigidBody <class_RigidBody>` to the bullet. We do this by subtracting the :ref:`RigidBody <class_RigidBody>`'s global origin/position from the bullet's global origin/position.
  812. This gets us the distance from the :ref:`RigidBody <class_RigidBody>` to the bullet. We normalize this vector so the size of the collider does not effect how much
  813. the bullets move the :ref:`RigidBody <class_RigidBody>`.
  814. Finally, we need to calculate the force for the impulse. For this, we use the direction the bullet is facing and multiply it by the bullet's damage.
  815. This gives a nice result and for stronger bullets, we get a stronger result.
  816. _______
  817. Now we need to attach this script to all of the :ref:`RigidBody <class_RigidBody>` nodes we want to affect.
  818. Open up ``Testing_Area.tscn`` and select all the cubes parented to the ``Cubes`` node.
  819. .. tip:: If you select the top cube, and then hold down ``shift`` and select the last cube, Godot will
  820. select all the cubes in-between!
  821. Once you have all the cubes selected, scroll down in the inspector until you get to
  822. the "scripts" section. Click the drop down and select "Load". Open your newly created ``RigidBody_hit_test.gd`` script.
  823. Final notes
  824. -----------
  825. .. image:: img/PartTwoFinished.png
  826. That was a lot of code! But now, with all that done, you can go and give your weapons a test!
  827. You should now be able to fire as many bullets as you want at the cubes and
  828. they will move in response to the bullets colliding with them.
  829. In :ref:`doc_fps_tutorial_part_three`, we will add ammo to the weapons, as well as some sounds!
  830. .. warning:: If you ever get lost, be sure to read over the code again!
  831. You can download the finished project for this part here: :download:`Godot_FPS_Part_2.zip <files/Godot_FPS_Part_2.zip>`