From faea70955d8a470c6c47437a080dbdd074228ce7 Mon Sep 17 00:00:00 2001 From: Julian Murgia Date: Sat, 22 Oct 2016 16:55:08 +0200 Subject: [PATCH] Rewritten some parts of pong tutorial to make them clearer (hopefully). --- img/pong_nodes.png | Bin 9943 -> 8817 bytes tutorials/step_by_step/simple_2d_game.rst | 230 ++++++++++++---------- 2 files changed, 131 insertions(+), 99 deletions(-) diff --git a/img/pong_nodes.png b/img/pong_nodes.png index 086e8f45eeeacab6c2d38efc1dabffff2620bff4..6d7a7105ecaaebc0ae5ec5a9de9a480679853092 100644 GIT binary patch literal 8817 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 U+)pkGs1AU#yoTIY8S}9J0Y*4ohX4Qo diff --git a/tutorials/step_by_step/simple_2d_game.rst b/tutorials/step_by_step/simple_2d_game.rst index 39ab7e643..cd01bc428 100644 --- a/tutorials/step_by_step/simple_2d_game.rst +++ b/tutorials/step_by_step/simple_2d_game.rst @@ -10,17 +10,19 @@ In this simple tutorial, a basic game of Pong will be created. There are plenty of more complex examples in the demos included with the engine, but this should get one introduced to basic functionality for 2D Games. +Run Godot Engine and start a new project. + Assets ~~~~~~ Some assets are included for this tutorial: -:download:`pong_assets.zip `. +:download:`pong_assets.zip `. Unzip its content in your project folder. Scene setup ~~~~~~~~~~~ For the sake of the old times, the game will be in 640x400 pixels -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: +resolution. This can be configured in the Project Settings (see :ref:`doc_scenes_and_nodes-configuring_the_project`) under Scene/Project settings menu. The default background color should be set to black: .. image:: /img/clearcolor.png @@ -31,13 +33,23 @@ a custom name for each node, and set the texture for each sprite in the Inspector. The result should look similar to this (note: the ball is in the middle!): -.. image:: /img/pong_layout.png - -The scene tree should, then, look similar to this: - .. image:: /img/pong_nodes.png -Save the scene as "pong.scn" and set it as the main scene in the project +Name them "left", "right", "separator" and "ball". Set the each sprite node the corresponding texture available in the ZIP archive previously downloaded. + +Set nodes positions : + - "left" node : (67.6875, 183.2079) + - "right" node : (577, 187) + - "separator" node : (320, 200) + - "ball" node : (320.2829, 188) + + +The final scene layout should look similar to this (note: the ball is in the middle!): + +.. image:: /img/pong_layout.png + + +Save the scene as "pong.tscn" and set it as the main scene in the project properties. .. _doc_simple_2d_game-input_actions_setup: @@ -45,19 +57,14 @@ properties. Input actions setup ~~~~~~~~~~~~~~~~~~~ -There are so many input methods for video games... Keyboard, Joypad, -Mouse, Touchscreen (Multitouch). Yet this is pong. The only input that -matters is for the pads going up and down. +Video games can be played using various input methods: Keyboard, Joypad, Mouse, Touchscreen (multitouch)... Godot is able to use them all. However, it would be interesting to define the inputs as "Input Actions" instead of hardware actions that you'd manage separately. This way, any input method can be used: each of them only require the user to connect buttons to game actions that you defined. -Handling all possible input methods can be very frustrating and take a -lot of code. The fact that most games allow controller customization -makes this worse. For this, Godot created the "Input Actions". An action -is defined, then input methods that trigger it are added. +This is Pong. The only input that matters is for the pads going up and down. -Open the project properties dialog again, but this time move to the +Open the project properties dialog again (Scene/Project settings), but this time move to the "Input Map" tab. -On it, add 4 actions: +In this tab, add 4 actions: ``left_move_up``, ``left_move_down``, ``right_move_up``, ``right_move_down``. Assign the keys that you desire. A/Z (for the left player) and Up/Down (for the right player) as keys should work in most cases. @@ -68,127 +75,152 @@ Script ~~~~~~ Create a script for the root node of the scene and open it (as explained -in :ref:`doc_scripting-adding_a_script`). The script will inherit Node2D: +in :ref:`doc_scripting-adding_a_script`). This script inherits Node2D: :: - extends Node2D + extends Node2D + + func _ready(): + pass + + +First things first, we need to define some members for our script so it can store useful values. Such values are the dimensions of the screen and the pad. + +:: + + extends Node2D + + # Member variables + var screen_size + var pad_size + + func _ready(): + pass + + +As you know, the ``_ready()`` function is the first function called (after ``_enter_tree()`` which we don't need here). In this function, two things have to be done. The first one is to enable +processing: this is the purpose of the ``set_process(true)`` function. The second one is to initalize our two member variables. + +:: + + extends Node2D + + # Member variables + var screen_size + var pad_size + + func _ready(): + screen_size = get_viewport_rect().size + pad_size = get_node("left").get_texture().get_size() + set_process(true) + +We initialize the ``pad_size`` variable by getting one of the pads nodes (the left one here), and obtain its texture size. The ``screen_size`` is initialized using the ``get_viewport_rect()`` which returns a Rect object corresponding to the game window, and we store its size. + + +Now, we need to add some other members to our script in order to make our ball move. + +:: + + extends Node2D + + # Member variables + var screen_size + var pad_size + + # constant for pad speed (in pixels/second) + const INITIAL_BALL_SPEED = 80 + # speed of the ball (also in pixels/second) + var ball_speed = INITIAL_BALL_SPEED + # constant for pads speed + const PAD_SPEED = 150 func _ready(): - pass + screen_size = get_viewport_rect().size + pad_size = get_node("left").get_texture().get_size() + set_process(true) -In the constructor, two things will be done. The first is to enable -processing, and the second to store some useful values. Such values are -the dimensions of the screen and the pad: + + +Finally, the ``_process()`` function. All the code below is contained by this function. + +We have to init some useful values for computation. The first one is the ball position (from the node), the second one is the rectangle (``Rect2``) for each pad. These rectangles will be used for collisions tests between the ball and the pads. Sprites center their textures by default, so a small adjustment of ``pad_size / 2`` must be added. :: + func _process(delta): + var ball_pos = get_node("ball").get_pos() + var left_rect = Rect2( get_node("left").get_pos() - pad_size*0.5, pad_size ) + var right_rect = Rect2( get_node("right").get_pos() - pad_size*0.5, pad_size ) - extends Node2D - - var screen_size - var pad_size - - func _ready(): - screen_size = get_viewport_rect().size - pad_size = get_node("left").get_texture().get_size() - set_process(true) - -Then, some variables used for in-game processing will be added: +Now, let's add some movement to the ball in the ``_process()`` function. Since the ball position is stored in the ``ball_pos`` variable, integrating it is simple: :: - #speed of the ball (in pixels/second) + # Integrate new ball postion + ball_pos += direction * ball_speed * delta - var ball_speed = 80 - #direction of the ball (normal vector) +This code line is called at each iteration of the ``_process()`` function. That means the ball position will be updated at each new frame. - var direction = Vector2(-1, 0) - #constant for pad speed (also in pixels/second) - - const PAD_SPEED = 150 - -Finally, the process function: +Then, now that the ball has a new position, we need to test if it collides with anything, that is the window borders and the pads. First, the floor and the roof: :: - func _process(delta): + # Flip when touching roof or floor + if ((ball_pos.y < 0 and direction.y < 0) or (ball_pos.y > screen_size.y and direction.y > 0)): + direction.y = -direction.y -Get some useful values for computation. The first is the ball position -(from the node), the second is the rectangle (``Rect2``) for each of the pads. -Sprites center their textures by default, so a small adjustment of ``pad_size / 2`` -must be added. +Second, the pads: if one of the pads was touched, we need to invert the direction of the ball on the X axis so it goes back, and define a new random Y direction using ``randf()`` function. We also increase its speed a little. :: - var ball_pos = get_node("ball").get_pos() - var left_rect = Rect2( get_node("left").get_pos() - pad_size/2, pad_size ) - var right_rect = Rect2( get_node("right").get_pos() - pad_size/2, pad_size ) + # Flip, change direction and increase speed when touching pads + if ((left_rect.has_point(ball_pos) and direction.x < 0) or (right_rect.has_point(ball_pos) and direction.x > 0)): + direction.x = -direction.x + direction.y = randf()*2.0 - 1 + direction = direction.normalized() + ball_speed *= 1.1 -Since the ball position was obtained, integrating it should be simple: +If the ball went out of the screen, it's game over. That is, we test if the X position of the ball is less than 0 or greater than the screen width. If so, the game then restarts: :: - ball_pos += direction * ball_speed * delta + # Check gameover + if (ball_pos.x < 0 or ball_pos.x > screen_size.x): + ball_pos = screen_size*0.5 + ball_speed = INITIAL_BALL_SPEED + direction = Vector2(-1, 0) -Then, now that the ball has a new position, it should be tested against -everything. First, the floor and the roof: +Once everything was done with the ball, the node is updated with the new position which was computed before: :: - if ( (ball_pos.y < 0 and direction.y < 0) or (ball_pos.y > screen_size.y and direction.y > 0)): - direction.y = -direction.y + get_node("ball").set_pos(ball_pos) -If one of the pads was touched, change direction and increase speed a -little. +Pads movement: we only update the pads according to player input. This is done using the Input class: :: - if ( (left_rect.has_point(ball_pos) and direction.x < 0) or (right_rect.has_point(ball_pos) and direction.x > 0)): - direction.x = -direction.x - ball_speed *= 1.1 - direction.y = randf() * 2.0 - 1 - direction = direction.normalized() + #move left pad + var left_pos = get_node("left").get_pos() -If the ball went out of the screen, it's game over. Game restarts: + if (left_pos.y > 0 and Input.is_action_pressed("left_move_up")): + left_pos.y += -PAD_SPEED * delta + if (left_pos.y < screen_size.y and Input.is_action_pressed("left_move_down")): + left_pos.y += PAD_SPEED * delta -:: + get_node("left").set_pos(left_pos) - if (ball_pos.x < 0 or ball_pos.x > screen_size.x): - ball_pos = screen_size * 0.5 # ball goes to screen center - ball_speed = 80 - direction = Vector2(-1, 0) + #move right pad + var right_pos = get_node("right").get_pos() -Once everything was done with the ball, the node is updated with the new -position: + if (right_pos.y > 0 and Input.is_action_pressed("right_move_up")): + right_pos.y += -PAD_SPEED * delta + if (right_pos.y < screen_size.y and Input.is_action_pressed("right_move_down")): + right_pos.y += PAD_SPEED * delta -:: - - get_node("ball").set_pos(ball_pos) - -Only update the pads according to player input. The Input class is -really useful here: - -:: - - #move left pad - var left_pos = get_node("left").get_pos() - - if (left_pos.y > 0 and Input.is_action_pressed("left_move_up")): - left_pos.y += -PAD_SPEED * delta - if (left_pos.y < screen_size.y and Input.is_action_pressed("left_move_down")): - left_pos.y += PAD_SPEED * delta - - get_node("left").set_pos(left_pos) - - #move right pad - var right_pos = get_node("right").get_pos() - - if (right_pos.y > 0 and Input.is_action_pressed("right_move_up")): - right_pos.y += -PAD_SPEED * delta - if (right_pos.y < screen_size.y and Input.is_action_pressed("right_move_down")): - right_pos.y += PAD_SPEED * delta - - get_node("right").set_pos(right_pos) + get_node("right").set_pos(right_pos) + +We use the 4 actions previously defined in the Input actions setup section. When the player activates the according key, the corresponding action is triggered. When the action is triggered, we simply compute a new position for the pad in the wished direction. Finally, we set this new position to the node. And that's it! A simple Pong was written with a few lines of code.