simple_2d_game.rst 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. .. _doc_simple_2d_game:
  2. Simple 2D game
  3. ==============
  4. Pong
  5. ~~~~
  6. In this simple tutorial, a basic game of Pong will be created. There are
  7. plenty of more complex examples in the demos included with the engine,
  8. but this should get one introduced to basic functionality for 2D Games.
  9. Assets
  10. ~~~~~~
  11. Some assets are included for this tutorial:
  12. :download:`pong_assets.zip </files/pong_assets.zip>`.
  13. Scene setup
  14. ~~~~~~~~~~~
  15. For the sake of the old times, the game will be in 640x400 pixels
  16. resolution. This can be configured in the Project Settings (see :ref:`doc_scenes_and_nodes-configuring_the_project`). The default background color should be set to black:
  17. .. image:: /img/clearcolor.png
  18. Create a :ref:`class_Node2D` node for the project root. Node2D is the base
  19. type for the 2D engine. After this, add some sprites (:ref:`class_Sprite`
  20. node) and set each to the corresponding texture. The final scene layout
  21. should look similar to this (note: the ball is in the middle!):
  22. .. image:: /img/pong_layout.png
  23. The scene tree should, then, look similar to this:
  24. .. image:: /img/pong_nodes.png
  25. Save the scene as "pong.scn" and set it as the main scene in the project
  26. properties.
  27. .. _doc_simple_2d_game-input_actions_setup:
  28. Input actions setup
  29. ~~~~~~~~~~~~~~~~~~~
  30. There are so many input methods for video games... Keyboard, Joypad,
  31. Mouse, Touchscreen (Multitouch). Yet this is pong. The only input that
  32. matters is for the pads going up and down.
  33. Handling all possible input methods can be very frustrating and take a
  34. lot of code. The fact that most games allow controller customization
  35. makes this worse. For this, Godot created the "Input Actions". An action
  36. is defined, then input methods that trigger it are added.
  37. Open the project properties dialog again, but this time move to the
  38. "Input Map" tab.
  39. On it, add 4 actions:
  40. ``left_move_up``, ``left_move_down``, ``right_move_up``, ``right_move_down``.
  41. Assign the keys that you desire. A/Z (for the left player) and Up/Down (for the right player) as keys
  42. should work in most cases.
  43. .. image:: /img/inputmap.png
  44. Script
  45. ~~~~~~
  46. Create a script for the root node of the scene and open it (as explained
  47. in :ref:`doc_scripting-adding_a_script`). The script will inherit Node2D:
  48. ::
  49. extends Node2D
  50. func _ready():
  51. pass
  52. In the constructor, two things will be done. The first is to enable
  53. processing, and the second to store some useful values. Such values are
  54. the dimensions of the screen and the pad:
  55. ::
  56. extends Node2D
  57. var screen_size
  58. var pad_size
  59. func _ready():
  60. screen_size = get_viewport_rect().size
  61. pad_size = get_node("left").get_texture().get_size()
  62. set_process(true)
  63. Then, some variables used for in-game processing will be added:
  64. ::
  65. #speed of the ball (in pixels/second)
  66. var ball_speed = 80
  67. #direction of the ball (normal vector)
  68. var direction = Vector2(-1, 0)
  69. #constant for pad speed (also in pixels/second)
  70. const PAD_SPEED = 150
  71. Finally, the process function:
  72. ::
  73. func _process(delta):
  74. Get some useful values for computation. The first is the ball position
  75. (from the node), the second is the rectangle (``Rect2``) for each of the pads.
  76. Sprites center their textures by default, so a small adjustment of ``pad_size / 2``
  77. must be added.
  78. ::
  79. var ball_pos = get_node("ball").get_pos()
  80. var left_rect = Rect2( get_node("left").get_pos() - pad_size/2, pad_size )
  81. var right_rect = Rect2( get_node("right").get_pos() - pad_size/2, pad_size )
  82. Since the ball position was obtained, integrating it should be simple:
  83. ::
  84. ball_pos += direction * ball_speed * delta
  85. Then, now that the ball has a new position, it should be tested against
  86. everything. First, the floor and the roof:
  87. ::
  88. if ( (ball_pos.y < 0 and direction.y < 0) or (ball_pos.y > screen_size.y and direction.y > 0)):
  89. direction.y = -direction.y
  90. If one of the pads was touched, change direction and increase speed a
  91. little.
  92. ::
  93. if ( (left_rect.has_point(ball_pos) and direction.x < 0) or (right_rect.has_point(ball_pos) and direction.x > 0)):
  94. direction.x = -direction.x
  95. ball_speed *= 1.1
  96. direction.y = randf() * 2.0 - 1
  97. direction = direction.normalized()
  98. If the ball went out of the screen, it's game over. Game restarts:
  99. ::
  100. if (ball_pos.x < 0 or ball_pos.x > screen_size.x):
  101. ball_pos = screen_size * 0.5 # ball goes to screen center
  102. ball_speed = 80
  103. direction = Vector2(-1, 0)
  104. Once everything was done with the ball, the node is updated with the new
  105. position:
  106. ::
  107. get_node("ball").set_pos(ball_pos)
  108. Only update the pads according to player input. The Input class is
  109. really useful here:
  110. ::
  111. #move left pad
  112. var left_pos = get_node("left").get_pos()
  113. if (left_pos.y > 0 and Input.is_action_pressed("left_move_up")):
  114. left_pos.y += -PAD_SPEED * delta
  115. if (left_pos.y < screen_size.y and Input.is_action_pressed("left_move_down")):
  116. left_pos.y += PAD_SPEED * delta
  117. get_node("left").set_pos(left_pos)
  118. #move right pad
  119. var right_pos = get_node("right").get_pos()
  120. if (right_pos.y > 0 and Input.is_action_pressed("right_move_up")):
  121. right_pos.y += -PAD_SPEED * delta
  122. if (right_pos.y < screen_size.y and Input.is_action_pressed("right_move_down")):
  123. right_pos.y += PAD_SPEED * delta
  124. get_node("right").set_pos(right_pos)
  125. And that's it! A simple Pong was written with a few lines of code.