your_first_game.rst 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841
  1. .. _doc_your_first_game:
  2. Your First Game
  3. ===============
  4. Overview
  5. --------
  6. This tutorial will guide you through making your first Godot Engine
  7. project. You will learn how the Godot Engine editor works, how to structure
  8. a project, and how to build a 2D game.
  9. .. note:: This project is an introduction to the Godot Engine. It
  10. assumes that you have some programming experience already. If
  11. you're new to programming entirely, you should start here:
  12. :ref:`doc_scripting`.
  13. The game is called *"Dodge the Creeps"*. Your character must move and
  14. avoid the enemies for as long as possible. Here is a preview of the
  15. final result:
  16. .. image:: img/dodge_preview.gif
  17. **Why 2D?**
  18. 3D games are much more complex than 2D ones. You should stick to 2D
  19. until you have a good understanding of the game development process.
  20. Project Setup
  21. -------------
  22. Launch Godot and create a new project. Then, download
  23. :download:`dodge_assets.zip <files/dodge_assets.zip>` - the images and sounds you'll be
  24. using to make the game. Unzip these files in your new project folder.
  25. .. note:: For this tutorial, we will assume you are already familiar with the
  26. Godot Engine editor. If you haven't read :ref:`doc_scenes_and_nodes`, do so now
  27. for an explanation of setting up a project and using the editor.
  28. This game will use "portrait" mode, so we need to adjust the size of the
  29. game window. Click on Project -> Project Settings -> Display -> Window and
  30. set ``Width`` to ``480`` and ``Height`` to ``720``.
  31. Organizing the Project
  32. ~~~~~~~~~~~~~~~~~~~~~~
  33. In this project, we will make 3 independent scenes: ``Player``,
  34. ``Mob``, and ``HUD``, which we will combine into the game's ``Main``
  35. scene. In a larger project, it might be useful to make folders to hold
  36. the various scenes and their scripts, but for this relatively small
  37. game, you can save your scenes and scripts in the root folder, which is
  38. referred to as ``res://``. You can see your project folders in the Filesystem
  39. Dock in the upper left corner:
  40. .. image:: img/filesystem_dock.png
  41. Player Scene
  42. ------------
  43. The first scene we make defines the "Player" object. One of the benefits
  44. of creating a separate Player scene is that we can test it separately, even
  45. before we've created the other parts of the game.
  46. Node Structure
  47. ~~~~~~~~~~~~~~
  48. To begin, click the "Add/Create a New Node" button and add an :ref:`Area2D <class_Area2D>`
  49. node to the scene.
  50. .. image:: img/add_node.png
  51. With ``Area2D`` we can detect other objects that overlap or run into the player.
  52. Change its name to ``Player``. This is the scene's "root" or top-level node.
  53. We can add additional nodes to the player to add functionality.
  54. Before we add any children to the ``Player`` node, we want to make sure we don't
  55. accidentally move or resize them by clicking on them. Select the player node and
  56. click the icon next to the lock - its tooltip says "Makes sure the objects children
  57. are not selectable."
  58. .. image:: img/lock_children.png
  59. Save the scene (click Scene -> Save, or press ``Control+S`` on Windows/Linux or ``Command+S`` on Mac).
  60. .. note:: In this project, we will be following the Godot Engine naming
  61. conventions. Classes (Nodes) use ``CapWords``, variables and
  62. functions use ``snake_case``, and constants use ``ALL_CAPS``.
  63. Sprite Animation
  64. ~~~~~~~~~~~~~~~~
  65. Click on the ``Player`` node and add an :ref:`AnimatedSprite <class_AnimatedSprite>` node as a
  66. child. The ``AnimatedSprite`` will handle the appearance and animations
  67. for our player. Notice that there is a warning symbol next to the node.
  68. An ``AnimatedSprite`` requires a :ref:`SpriteFrames <class_SpriteFrames>` resource, which is a
  69. list of the animation(s) it can display. To create one, find the
  70. ``Frames`` property in the Inspector and click "<null>" ->
  71. "New SpriteFrames". Next, in the same location, click
  72. ``<SpriteFrames>`` to open the "SpriteFrames" panel:
  73. .. image:: img/spriteframes_panel.png
  74. On the left is a list of animations. Click the "default" one and rename
  75. it to "right". Then click the "Add" button to create a second animation
  76. named "up". Drag the two images for each animation into "Animation
  77. Frames" side of the panel:
  78. .. image:: img/spriteframes_panel2.png
  79. The player images are a bit too large for the game window, so we need to
  80. scale them down. Click on the ``AnimatedSprite`` node and set the ``Scale``
  81. property to ``(0.5, 0.5)``. You can find it in the Inspector under the
  82. ``Node2D`` heading.
  83. .. image:: img/player_scale.png
  84. Finally, add a :ref:`CollisionShape2D <class_CollisionShape2D>` as a child
  85. of the ``Player``. This will determine the player's "hitbox", or the
  86. bounds of its collision area. For this character, a ``CapsuleShape2D``
  87. gives the best fit, so next to "Shape" in the Inspector, click
  88. "<null>"" -> "New CapsuleShape2D". Resize the shape to cover the sprite:
  89. .. image:: img/player_coll_shape.png
  90. .. warning:: Remember not to scale the shape's outline! Only use the
  91. size handles (red) to adjust the shape!
  92. When you're finished, your ``Player`` scene should look like this:
  93. .. image:: img/player_scene_nodes.png
  94. Moving the Player
  95. ~~~~~~~~~~~~~~~~~
  96. Now we need to add some functionality that we can't get from a built-in
  97. node, so we'll add a script. Click the ``Player`` node and click the
  98. "Add Script" button:
  99. .. image:: img/add_script_button.png
  100. In the script settings window, you can leave the default settings, just
  101. click "Create":
  102. .. image:: img/attach_node_window.png
  103. .. note:: If this is your first time encountering GDScript please read
  104. :ref:`doc_scripting` first.
  105. Start by declaring the member variables this object will need:
  106. ::
  107. extends Area2D
  108. export (int) var SPEED # how fast the player will move (pixels/sec)
  109. var velocity = Vector2() # the player's movement vector
  110. var screensize # size of the game window
  111. Using the ``export`` keyword on the first variable ``SPEED`` allows us to
  112. set its value in the Inspector. This can be very handy for values that you
  113. want to be able to adjust just like a node's built-in properties. Click on
  114. the ``Player`` node and set the speed property to ``400``.
  115. .. image:: img/export_variable.png
  116. The ``_ready()`` function is called when a node enters the scene tree, so
  117. that's a good time to find the size of the game window:
  118. ::
  119. func _ready():
  120. screensize = get_viewport_rect().size
  121. Now we can use the ``_process()`` function to define what the player will do.
  122. The ``_process()`` function is called on every frame, so we'll use it to update
  123. elements of our game which we expect to be changing often. Here we'll have it:
  124. - check for input
  125. - move in the given direction
  126. - play the appropriate animation.
  127. First, we need to check the inputs - is the player pressing a key? For
  128. this game, we have 4 direction inputs to check. Input actions are defined
  129. in the Project Settings under "Input Map". You can define custom events and
  130. assign different keys, mouse events, or other inputs to them. For this demo,
  131. we will use the default events that are assigned to the arrow keys on the
  132. keyboard.
  133. You can detect whether a key is pressed using
  134. ``Input.is_action_pressed()``, which returns ``true`` if it is pressed
  135. or ``false`` if it isn't.
  136. ::
  137. func _process(delta):
  138. velocity = Vector2()
  139. if Input.is_action_pressed("ui_right"):
  140. velocity.x += 1
  141. if Input.is_action_pressed("ui_left"):
  142. velocity.x -= 1
  143. if Input.is_action_pressed("ui_down"):
  144. velocity.y += 1
  145. if Input.is_action_pressed("ui_up"):
  146. velocity.y -= 1
  147. if velocity.length() > 0:
  148. velocity = velocity.normalized() * SPEED
  149. $AnimatedSprite.play()
  150. else:
  151. $AnimatedSprite.stop()
  152. We check each input and add/subtract from the ``velocity`` to obtain a
  153. total direction. For example, if you hold ``right`` and ``down`` at
  154. the same time, the resulting ``velocity`` vector will be ``(1, 1)``. In
  155. this case, since we're adding a horizontal and a vertical movement, the
  156. player would move *faster* than if it just moved horizontally.
  157. We can prevent that if we *normalize* the velocity, which means we set
  158. its *length* to ``1``, and multiply by the desired speed. This means no
  159. more fast diagonal movement.
  160. .. tip:: If you've never used vector math before (or just need a refresher)
  161. you can see an explanation of vector usage in Godot at :ref:`doc_vector_math`.
  162. It's good to know but won't be necessary for the rest of this tutorial.
  163. We also check whether the player is moving so we can start or stop the
  164. AnimatedSprite animation.
  165. .. tip:: ``$`` returns the node at the relative path from this node, or returns ``null`` if the node is not found.
  166. Since AnimatedSprite is a child of the current node, we can just use ``$AnimatedSprite``.
  167. ``$`` is the short hand for ``get_node()``.
  168. So in the code above, ``$AnimatedSprite.play()`` is the same as ``get_node("AnimatedSprite").play()``.
  169. Now that we have a movement direction, we can update the player's position
  170. and use ``clamp()`` to prevent it from leaving the screen:
  171. ::
  172. position += velocity * delta
  173. position.x = clamp(position.x, 0, screensize.x)
  174. position.y = clamp(position.y, 0, screensize.y)
  175. .. tip:: *Clamping* a value means restricting it to a given minimum/maximum range.
  176. Click "Play the Edited Scene. (F6)" and confirm you can move the player
  177. around the screen in all directions.
  178. .. warning:: If you get an error in the "Debugger" panel that refers to a "null instance",
  179. this likely means you spelled the node name wrong. Node names are case sensitive
  180. and ``$NodeName`` or ``get_node("NodeName")`` must match the name you see in the scene tree.
  181. Choosing Animations
  182. ~~~~~~~~~~~~~~~~~~~
  183. Now that the player can move, we need to change which animation the
  184. AnimatedSprite is playing based on direction. We have a "right"
  185. animation, which should be flipped horizontally (using the ``flip_h``
  186. property) for left movement, and an "up" animation, which should be
  187. flipped vertically (``flip_v``) for downward movement.
  188. Let's place this code at the end of our ``_process()`` function:
  189. ::
  190. if velocity.x != 0:
  191. $AnimatedSprite.animation = "right"
  192. $AnimatedSprite.flip_v = false
  193. $AnimatedSprite.flip_h = velocity.x < 0
  194. elif velocity.y != 0:
  195. $AnimatedSprite.animation = "up"
  196. $AnimatedSprite.flip_v = velocity.y > 0
  197. Play the scene again and check that the animations are correct in each
  198. of the directions. When you're sure that movement is working correctly,
  199. add this line to ``_ready()`` so the player will be hidden when the game
  200. starts:
  201. ::
  202. hide()
  203. Preparing for Collisions
  204. ~~~~~~~~~~~~~~~~~~~~~~~~
  205. We want the player to detect when it is hit by an enemy, but we haven't
  206. made any enemies yet! That's OK because we're going to use Godot's
  207. *signal* functionality to make it work.
  208. Add the following at the top of the script (after ``extends Area2d``):
  209. ::
  210. signal hit
  211. This defines a custom signal called "hit" that we will have our player
  212. emit (send out) when it collides with an enemy. We will use the Area2D to
  213. detect the collision. Select the ``Player`` node and click the "Node" tab
  214. next to the Inspector to see the list of signals the player can emit:
  215. .. image:: img/player_signals.png
  216. Notice our custom "hit" signal is there as well! Since our enemies are
  217. going to be ``RigidBody2D`` nodes, we want the
  218. ``body_entered( Object body )`` signal - that will be emitted when a
  219. body contacts the player. Click "Connect.." and then "Connect" again on
  220. the "Connecting Signal" window - we don't need to change any of those
  221. settings. Godot will automatically create a function called
  222. ``_on_Player_body_entered`` in your player's script.
  223. .. tip:: When connecting a signal, instead of having Godot create a
  224. function for you, you can also give the name of an existing
  225. function that you want to link the signal to.
  226. Add this code to the function:
  227. ::
  228. func _on_Player_body_entered( body ):
  229. hide() # Player disappears after being hit
  230. emit_signal("hit")
  231. $CollisionShape2D.disabled = true
  232. .. Note:: Disabling the area's collision shape means
  233. it won't detect collisions. By turning it off, we make
  234. sure we don't trigger the ``hit`` signal more than once.
  235. The last piece for our player is to add a function we can call to reset
  236. the player when starting a new game.
  237. ::
  238. func start(pos):
  239. position = pos
  240. show()
  241. $CollisionShape2D.disabled = false
  242. Enemy Scene
  243. -----------
  244. Now it's time to make the enemies our player will have to dodge. Their
  245. behavior will not be very complex: mobs will spawn randomly at the edges
  246. of the screen and move in a straight line (in a random direction), then
  247. despawn when they go offscreen.
  248. We will build this into a ``Mob`` scene, which we can then *instance* to
  249. create any number of independent mobs in the game.
  250. Node Setup
  251. ~~~~~~~~~~
  252. Click Scene -> New Scene and we'll create the Mob.
  253. The Mob scene will use the following nodes:
  254. - :ref:`RigidBody2D <class_RigidBody2D>` (named ``Mob``)
  255. - :ref:`AnimatedSprite <class_AnimatedSprite>`
  256. - :ref:`CollisionShape2D <class_CollisionShape2D>`
  257. - :ref:`VisibilityNotifier2D <class_VisibilityNotifier2D>` (named ``Visibility``)
  258. Don't forget to set the children so they can't be selected, like you did with the
  259. Player scene.
  260. In the :ref:`RigidBody2D <class_RigidBody2D>` properties, set ``Gravity Scale`` to ``0`` (so
  261. that the mob will not fall downward). In addition, under the
  262. ``PhysicsBody2D`` section in the Inspector, click the ``Mask`` property and
  263. uncheck the first box. This will ensure that the mobs do not collide with each other.
  264. .. image:: img/set_collision_mask.png
  265. Set up the :ref:`AnimatedSprite <class_AnimatedSprite>` like you did for the player.
  266. This time, we have 3 animations: "fly", "swim", and "walk". Set the ``Playing``
  267. property in the Inspector to "On" and adjust the "Speed (FPS)" setting as shown below.
  268. We'll select one of these randomly so that the mobs will have some variety.
  269. Like the player images, these mob images need to be scaled down. Set the
  270. ``AnimatedSprite``'s ``Scale`` property to ``(0.75, 0.75)``.
  271. .. image:: img/mob_animations.gif
  272. As in the ``Player`` scene, add a ``CapsuleShape2D`` for the
  273. collision. To align the shape with the image, you'll need to set the
  274. ``Rotation Deg`` property to ``90`` under ``Node2D``.
  275. Enemy Script
  276. ~~~~~~~~~~~~
  277. Add a script to the ``Mob`` and add the following member variables:
  278. ::
  279. extends RigidBody2D
  280. export (int) var MIN_SPEED # minimum speed range
  281. export (int) var MAX_SPEED # maximum speed range
  282. var mob_types = ["walk", "swim", "fly"]
  283. We'll pick a random value between ``MIN_SPEED`` and ``MAX_SPEED`` for
  284. how fast each mob will move (it would be boring if they were all moving
  285. at the same speed). Set them to ``150`` and ``250`` in the Inspector. We
  286. also have an array containing the names of the three animations, which
  287. we'll use to select a random one.
  288. Now let's look at the rest of the script. In ``_ready()`` we choose a
  289. random one of the three animation types:
  290. ::
  291. func _ready():
  292. $AnimatedSprite.animation = mob_types[randi() % mob_types.size()]
  293. .. note:: You must use ``randomize()`` if you want
  294. your sequence of "random" numbers to be different every time you run
  295. the scene. We're going to use ``randomize()`` in our ``Main`` scene,
  296. so we won't need it here. ``randi() % n`` is the standard way to get
  297. a random integer between ``0`` and ``n-1``.
  298. The last piece is to make the mobs delete themselves when they leave the
  299. screen. Connect the ``screen_exited()`` signal of the ``Visibility``
  300. node and add this code:
  301. ::
  302. func _on_Visibility_screen_exited():
  303. queue_free()
  304. That completes the `Mob` scene.
  305. Main Scene
  306. ----------
  307. Now it's time to bring it all together. Create a new scene and add a
  308. :ref:`Node <class_Node>` named ``Main``. Click the "Instance" button and select your
  309. saved ``Player.tscn``.
  310. .. image:: img/instance_scene.png
  311. .. note:: See :ref:`doc_instancing` to learn more about instancing.
  312. Now add the following nodes as children of ``Main``, and name them as
  313. shown (values are in seconds):
  314. - :ref:`Timer <class_Timer>` (named ``MobTimer``) - to control how often mobs spawn
  315. - :ref:`Timer <class_Timer>` (named ``ScoreTimer``) - to increment the score every second
  316. - :ref:`Timer <class_Timer>` (named ``StartTimer``) - to give a delay before starting
  317. - :ref:`Position2D <class_Position2D>` (named ``StartPosition``) - to indicate the player's start position
  318. Set the ``Wait Time`` property of each of the ``Timer`` nodes as
  319. follows:
  320. - ``MobTimer``: ``0.5``
  321. - ``ScoreTimer``: ``1``
  322. - ``StartTimer``: ``2``
  323. In addition, set the ``One Shot`` property of ``StartTimer`` to "On" and
  324. set ``Position`` of the ``StartPosition`` node to ``(240, 450)``.
  325. Spawning Mobs
  326. ~~~~~~~~~~~~~
  327. The Main node will be spawning new mobs, and we want them to appear at a
  328. random location on the edge of the screen. Add a :ref:`Path2D <class_Path2D>` named
  329. ``MobPath`` as a child of ``Main``. When you select the ``Path2D`` node
  330. you will see some new buttons appear at the top of the editor:
  331. .. image:: img/path2d_buttons.png
  332. Select the middle one ("Add Point") and draw the path by clicking to add
  333. the points shown. To have the points snap to the grid, make sure "Snap to
  334. Grid" is checked. This option can be found under the "Snapping Options"
  335. button to the left of the "Lock" button. It appears as a series of three
  336. vertical dots.
  337. .. image:: img/draw_path2d.gif
  338. .. important:: Draw the path in *clockwise* order, or your mobs will spawn
  339. pointing *outwards* instead of *inwards*!
  340. After placing point ``4`` in the image, click the "Close Curve" button and
  341. your curve will be complete.
  342. Now that the path is defined, add a :ref:`PathFollow2D <class_PathFollow2D>`
  343. node as a child of ``MobPath`` and name it ``MobSpawnLocation``. This node will
  344. automatically rotate and follow the path as it moves, so we can use it
  345. to select a random position and direction along the path.
  346. Main Script
  347. ~~~~~~~~~~~
  348. Add a script to ``Main``. At the top of the script we use
  349. ``export (PackedScene)`` to allow us to choose the Mob scene we want to
  350. instance.
  351. ::
  352. extends Node
  353. export (PackedScene) var Mob
  354. var score
  355. func _ready():
  356. randomize()
  357. Drag the ``Mob.tscn`` from the "FileSystem" panel and drop it in the
  358. ``Mob`` property.
  359. Next, click on the Player and connect the ``hit`` signal. We want to make a
  360. new function named ``game_over``, which will handle what needs to happen when a
  361. game ends. Type "game_over" in the "Method In Node" box at the bottom of the
  362. "Connecting Signal" window. Add the following code, as well as a ``new_game``
  363. function to set everything up for a new game:
  364. ::
  365. func game_over():
  366. $ScoreTimer.stop()
  367. $MobTimer.stop()
  368. func new_game():
  369. score = 0
  370. $Player.start($StartPosition.position)
  371. $StartTimer.start()
  372. Now connect the ``timeout()`` signal of each of the Timer nodes.
  373. ``StartTimer`` will start the other two timers. ``ScoreTimer`` will
  374. increment the score by 1.
  375. ::
  376. func _on_StartTimer_timeout():
  377. $MobTimer.start()
  378. $ScoreTimer.start()
  379. func _on_ScoreTimer_timeout():
  380. score += 1
  381. In ``_on_MobTimer_timeout()`` we will create a mob instance, pick a
  382. random starting location along the ``Path2D``, and set the mob in
  383. motion. The ``PathFollow2D`` node will automatically rotate as it
  384. follows the path, so we will use that to select the mob's direction as
  385. well as its position.
  386. Note that a new instance must be added to the scene using
  387. ``add_child()``.
  388. ::
  389. func _on_MobTimer_timeout():
  390. # choose a random location on the Path2D
  391. $MobPath/MobSpawnLocation.set_offset(randi())
  392. # create a Mob instance and add it to the scene
  393. var mob = Mob.instance()
  394. add_child(mob)
  395. # set the mob's direction perpendicular to the path direction
  396. var direction = $MobPath/MobSpawnLocation.rotation + PI/2
  397. # set the mob's position to the random location
  398. mob.position = $MobPath/MobSpawnLocation.position
  399. # add some randomness to the direction
  400. direction += rand_range(-PI/4, PI/4)
  401. mob.rotation = direction
  402. # choose the velocity
  403. mob.set_linear_velocity(Vector2(rand_range(mob.MIN_SPEED, mob.MAX_SPEED), 0).rotated(direction))
  404. .. important:: In functions requiring angles, GDScript uses *radians*,
  405. not degrees. If you're more comfortable working with
  406. degrees, you'll need to use the ``deg2rad()`` and
  407. ``rad2deg()`` functions to convert between the two measures.
  408. HUD
  409. ---
  410. The final piece our game needs is a UI: an interface to display things
  411. like score, a "game over" message, and a restart button. Create a new
  412. scene, and add a :ref:`CanvasLayer <class_CanvasLayer>` node named ``HUD`` ("HUD" stands for
  413. "heads-up display", meaning an informational display that appears as an
  414. overlay, on top of the game view).
  415. The :ref:`CanvasLayer <class_CanvasLayer>` node lets us draw our UI elements on
  416. the layer above the rest of the game so that the information it displays doesn't get
  417. covered up by any game elements like the player or the mobs.
  418. The HUD displays the following information:
  419. - Score, changed by ``ScoreTimer``
  420. - A message, such as "Game Over" or "Get Ready!"
  421. - A "Start" button to begin the game
  422. The basic node for UI elements is :ref:`Control <class_Control>`. To create our UI,
  423. we'll use two types of :ref:`Control <class_Control>` nodes: The :ref:`Label <class_Label>`
  424. and the :ref:`Button <class_Button>`.
  425. Create the following children of the ``HUD`` node:
  426. - :ref:`Label <class_Label>` (named ``ScoreLabel``)
  427. - :ref:`Label <class_Label>` (named ``MessageLabel``)
  428. - :ref:`Button <class_Button>` (named ``StartButton``)
  429. - :ref:`Timer <class_Timer>` (named ``MessageTimer``)
  430. .. note:: **Anchors and Margins** ``Control`` nodes have a position and size,
  431. but they also have anchors and margins. Anchors define the
  432. origin, or the reference point for the edges of the node. Margins
  433. update automatically when you move or resize a control node. They
  434. represent the distance from the control node's edges to its anchor.
  435. See :ref:`doc_design_interfaces_with_the_control_nodes` for more details.
  436. Arrange the nodes as shown below. Click the "Anchor" button to
  437. set a Control node's anchor:
  438. .. image:: img/ui_anchor.png
  439. You can drag the nodes to place them manually, or for more precise
  440. placement, use the following settings:
  441. ScoreLabel
  442. ~~~~~~~~~~
  443. - ``Layout``: "Center Top"
  444. - ``Margin``:
  445. - Left: ``-25``
  446. - Top: ``0``
  447. - Right: ``25``
  448. - Bottom: ``100``
  449. - Text: ``0``
  450. MessageLabel
  451. ~~~~~~~~~~~~
  452. - ``Layout``: "Center Bottom"
  453. - ``Margin``:
  454. - Left: ``-100``
  455. - Top: ``-200``
  456. - Right: ``100``
  457. - Bottom: ``-100``
  458. - Text: ``Dodge the Creeps!``
  459. StartButton
  460. ~~~~~~~~~~~
  461. - ``Layout``: "Center"
  462. - ``Margin``:
  463. - Left: ``-60``
  464. - Top: ``70``
  465. - Right: ``60``
  466. - Bottom: ``150``
  467. - Text: ``Start``
  468. The default font for ``Control`` nodes is very small and doesn't scale
  469. well. There is a font file included in the game assets called
  470. "Xolonium-Regular.ttf". To use this font, do the following for each of
  471. the three ``Control`` nodes:
  472. 1. Under "Custom Fonts", choose "New DynamicFont"
  473. .. image:: img/custom_font1.png
  474. 2. Click on the "DynamicFont" you just added, and under "Font Data",
  475. choose "Load" and select the "Xolonium-Regular.ttf" file. You must
  476. also set the font's ``Size``. A setting of ``64`` works well.
  477. .. image:: img/custom_font2.png
  478. Now add this script to the ``HUD``:
  479. ::
  480. extends CanvasLayer
  481. signal start_game
  482. The ``start_game`` signal tells the ``Main`` node that the button
  483. has been pressed.
  484. ::
  485. func show_message(text):
  486. $MessageLabel.text = text
  487. $MessageLabel.show()
  488. $MessageTimer.start()
  489. This function is called when we want to display a message
  490. temporarily, such as "Get Ready". On the ``MessageTimer``, set the
  491. ``Wait Time`` to ``2`` and set the ``One Shot`` property to "On".
  492. ::
  493. func show_game_over():
  494. show_message("Game Over")
  495. yield($MessageTimer, "timeout")
  496. $StartButton.show()
  497. $MessageLabel.text = "Dodge the\nCreeps!"
  498. $MessageLabel.show()
  499. This function is called when the player loses. It will show "Game
  500. Over" for 2 seconds, and then return to the game title and show the
  501. "Start" button.
  502. ::
  503. func update_score(score):
  504. $ScoreLabel.text = str(score)
  505. This function is called in ``Main`` whenever the score changes.
  506. Connect the ``timeout()`` signal of ``MessageTimer`` and the
  507. ``pressed()`` signal of ``StartButton``.
  508. ::
  509. func _on_StartButton_pressed():
  510. $StartButton.hide()
  511. emit_signal("start_game")
  512. func _on_MessageTimer_timeout():
  513. $MessageLabel.hide()
  514. Connecting HUD to Main
  515. ~~~~~~~~~~~~~~~~~~~~~~
  516. Now that we're done creating the ``HUD`` scene, save it and go back to ``Main``.
  517. Instance the ``HUD`` scene in ``Main`` like you did the ``Player`` scene, and place it at the
  518. bottom of tree. The full tree should look like this, so make sure you didn't miss anything:
  519. .. image:: img/completed_main_scene.png
  520. Now we need to connect the ``HUD`` functionality to our ``Main`` script.
  521. This requires a few additions to the ``Main`` scene:
  522. In the Node tab, connect the HUD's ``start_game`` signal to the
  523. ``new_game()`` function.
  524. In ``new_game()``, update the score display and show the "Get Ready"
  525. message:
  526. ::
  527. $HUD.update_score(score)
  528. $HUD.show_message("Get Ready")
  529. In ``game_over()`` we need to call the corresponding ``HUD`` function:
  530. ::
  531. $HUD.show_game_over()
  532. Finally, add this to ``_on_ScoreTimer_timeout()`` to keep the display in
  533. sync with the changing score:
  534. ::
  535. $HUD.update_score(score)
  536. Now you're ready to play! Click the "Play the Project" button. You will
  537. be asked to select a main scene, so choose ``Main.tscn``.
  538. Finishing Up
  539. ------------
  540. We've now completed all the functionality for our game. Below are some
  541. remaining steps to add a bit more "juice" and improve the game
  542. experience. Feel free to expand the gameplay with your own ideas.
  543. Background
  544. ~~~~~~~~~~
  545. The default gray background is not very appealing, so let's change its
  546. color. One way to do this is to use a :ref:`ColorRect <class_ColorRect>` node. Make it the
  547. first node under ``Main`` so that it will be drawn behind the other
  548. nodes. ``ColorRect`` only has one property: ``Color``. Choose a color
  549. you like and drag the size of the ``ColorRect`` so that it covers the
  550. screen.
  551. You can also add a background image, if you have one, by using a
  552. ``Sprite`` node.
  553. Sound Effects
  554. ~~~~~~~~~~~~~
  555. Sound and music can be the single most effective way to add appeal to
  556. the game experience. In your game assets folder, you have two sound
  557. files: "House In a Forest Loop.ogg", for background music, and
  558. "gameover.wav" for when the player loses.
  559. Add two :ref:`AudioStreamPlayer <class_AudioStreamPlayer>` nodes as children of ``Main``. Name one of
  560. them ``Music`` and the other ``DeathSound``. On each one, click on the
  561. ``Stream`` property, select "Load" and choose the corresponding audio
  562. file.
  563. To play the music, add ``$Music.play()`` in the ``new_game()`` function
  564. and ``$Music.stop()`` in the ``game_over()`` function.
  565. Finally, add ``$DeathSound.play()`` in the ``game_over()`` function as
  566. well.
  567. Particles
  568. ~~~~~~~~~
  569. For one last bit of visual appeal, let's add a trail effect to the
  570. player's movement. Choose your ``Player`` scene and add a
  571. :ref:`Particles2D <class_Particles2D>` node named ``Trail``.
  572. There are a very large number of properties to choose from when
  573. configuring particles. Feel free to experiment and create different
  574. effects. For the effect in the example, use the following settings:
  575. .. image:: img/particle_trail_settings.png
  576. You also need to create a ``Material`` by clicking on ``<null>`` and
  577. then "New ParticlesMaterial". The settings for that are below:
  578. .. image:: img/particle_trail_settings2.png
  579. To make the gradient for the "Color Ramp" setting, we want a gradient taking
  580. the alpha (transparency) of the sprite from 0.5 (semi-transparent) to
  581. 0.0 (fully transparent).
  582. Click "New GradientTexture", then under "Gradient", click "New Gradient". You'll
  583. see a window like this:
  584. .. image:: img/color_gradient_ui.png
  585. The left and right boxes represent the start and end colors. Click on each
  586. and then click the large square on the right to choose the color. For the first
  587. color, set the ``A`` (alpha) value to around halfway. For the second, set it
  588. all the way to ``0``.
  589. .. seealso:: See :ref:`Particles2D <class_Particles2D>` for more details on using
  590. particle effects.
  591. Project Files
  592. -------------
  593. You can find a completed version of this project here:
  594. https://github.com/kidscancode/Godot3_dodge/releases