Introduction: Pong

This instructable will serve as an introduction to Godot as well as a tutorial on how to make a simple Pong game.

Supplies

A computer or laptop, Godot Engine, and the assets linked below.

https://github.com/russs123/Pong_tut/blob/main/assets/Bg.png

https://github.com/russs123/Pong_tut/blob/main/assets/pixel_font.ttf

Step 1: Making Our Scene

After opening Godot:

  • Select "Other Node"
  • Next select Sprite2d and rename this node to "Main"
  • This will act as our background and parent node
  • On the right hand side under texture the Bg.png should be selected
  • Finally, in the texture drop down under the CanvasItem section, the Filter should be set to Nearest in order to sharpen the background image.

Step 2: Player and CPU Nodes

For the player controlled paddle:

  • Create a child node "Color Rect"
  • Under "Layout" change "Anchors" to Center Left
  • Under "Transform"
  • Size: change x to 20 and y to 120
  • Position: change y to -60
  • Create a second child node "CollisionShape2D"
  • Change Shape to Rectangle and drag the points around until it matches the Color Rect that was created before.
  • Select the Player node and under Transform
  • Position: change x to 50 and y to 324

For the CPU controlled paddle:

  • Select the Player node and all child nodes inside of it, and hit Ctrl + D to duplicate
  • Under Transform
  • Position: change x to 1080 and y to 324

Step 3: Ball Node

After selecting the Main node

  • Create a "CharacterBody2D" node and rename it "Ball"
  • While selecting the Ball node, create a ColorRect node
  • Under Layout change Anchors to Center
  • Under Transform
  • Size: Change x to 10 and y to 10
  • Position: Change x to -5 and y to -5
  • Under the Ball node, create a CollisionShape2D
  • Change the Shape to Rectangle and make sure that the shape is mirroring the Ball ColorRect node

Step 4: Border Node

After selecting the Main node

  • Create a StaticBody2D and rename it "Borders"
  • Select the Borders node and create a CollisionShape2D and rename it Top
  • Under "Collision" change Layer to 2 and make sure that 1 is deselected
  • Then drag the corners until it matches the top box in the picture attached
  • Select the Top node and duplicate it using Ctrl + D and move it down until it matched the bottom box in the picture attached

Step 5: Score Nodes

After selecting the Main node

  • Create a "Area2D" child node and rename it "ScoreLeft"
  • Adjust the corners until it matches the box on the left side of the image attached
  • Next duplicate the ScoreLeft node and rename it "ScoreRight"
  • Adjust the corners of ScoreRight until it matched the box on the right side of the image attached

Step 6: HUD Nodes

After selecting the Main node

  • Create a "CanvasLayer" child node and rename it "HUD"
  • Within the HUD node:
  • Create a "Label" child node and rename it "Player Score"
  • Within the "Player Score" child node:
  • Set Text to 0
  • Transform:
  • Size: x to 50 and y to 53
  • Position: x to 456 and y to 5
  • Theme Overrides: Insert the font file attached under the materials section
  • Next using Ctrl + D duplicate the Player Score child node and rename it "CPU Score"
  • Within the CPU Score child node
  • Transform:
  • Position: x to 648 and y to 5


Your scene should now look like the image attached.

Step 7: BallTimer Node

After selecting the Main node

  • Create a "Timer" child node and rename it BallTimer
  • Next set "Wait Time" to 1 s and insure:
  • Process Callback = Idle
  • One Shot = On
  • Autostart = On

Step 8: Main Script

  • On the top of the Godot workspace, select the Script tool
  • Select the main parent node and in the top left corner select the icon that looks like a scroll, when you hover over it, it should say "Attach or select an existing script to the selected node." From now on, I will refer to this as the new script button and it is used to add scripts to nodes.
  • After clicking on the new script button, delete what is existing in the window and copy and paste what has been copied below.


extends Sprite2D


var score :=[0, 0]# 0:Player, 1: CPU

const PADDLE_SPEED : int = 500



func _on_ball_timer_timeout():

$Ball.new_ball()



func _on_score_left_body_shape_entered(body_rid, body, body_shape_index, local_shape_index):

score[1] += 1 

$"HUD/CPU Score".text = str(score[1])

$BallTimer.start()


func _on_score_right_body_shape_entered(body_rid, body, body_shape_index, local_shape_index):

score[0] =+ 1

$"HUD/Player Score".text = str(score[0])

$BallTimer.start()

Although these scripts will not look very pretty due to how they transfer between documents, they should function just the same.

Step 9: Player Script

  • Select the Player node and copy and paste the script attached

extends StaticBody2D


var win_height : int

var p_height : int


# Called when the node enters the scene tree for the first time.

func _ready():

win_height = get_viewport_rect().size.y

p_height = $ColorRect.get_size().y


# Called every frame. 'delta' is the elapsed time since the previous frame.

func _process(delta):

if Input.is_action_pressed("ui_up"):

position.y -= get_parent().PADDLE_SPEED * delta

elif Input.is_action_pressed("ui_down"):

position.y += get_parent().PADDLE_SPEED * delta


#limit paddle movement to window

position.y = clamp(position.y, p_height / 2, win_height - p_height / 2)


Step 10: CPU Script

  • Select the CPU node and copy and paste the script attached below

extends StaticBody2D


var ball_pos : Vector2

var dist : int 

var move_by : int

var win_height : int

var p_height : int


# Called when the node enters the scene tree for the first time.

func _ready():

win_height = get_viewport_rect().size.y

p_height = $ColorRect.get_size().y



# Called every frame. 'delta' is the elapsed time since the previous frame.

func _process(delta):

#move paddle towards ball 

ball_pos = $"../Ball".position

dist = position.y - ball_pos.y 

if abs(dist) > get_parent().PADDLE_SPEED * delta:

move_by = get_parent().PADDLE_SPEED * delta * (dist / abs(dist))

else:

move_by = dist

position.y -= move_by

#limit paddle movement to window 

position.y = clamp(position.y, p_height / 2, win_height - p_height / 2)


Step 11: Ball Script

  • Select the Ball node and copy and paste the script attached below


extends CharacterBody2D


var win_size : Vector2

const START_SPEED : int = 500 

const ACCEl : int = 50 

var speed : int

var dir : Vector2

const MAX_Y_VECTOR : float = 0.6 


# Called when the node enters the scenetree for the first time. 

func _ready():

win_size = get_viewport_rect().size

func new_ball():

#randomize start position and direction 

position.x = win_size.x / 2

position.y = randi_range(200, win_size.y -200)

speed = START_SPEED

dir = random_direction()

func _physics_process(delta):

var collision = move_and_collide(dir * speed * delta)

var collider

if collision: 

collider = collision.get_collider()

#if ball hits paddle 

if collider == $"../Player" or collider == $"../CPU":

speed += ACCEl

dir = dir.bounce(collision.get_normal())

#if it hits a wall

else:

dir = dir.bounce(collision.get_normal())

func random_direction():

var new_dir := Vector2()

new_dir.x = [1, -1].pick_random()

new_dir.y = randf_range(-1, 1)

return new_dir.normalized()


func new_direction(collider):

var ball_y = collider.position.y

var pad_y = collider.position.y

var dist = ball_y - pad_y

var new_dir :=Vector2()

#flip the horizontal direction 

if dir.x > 0:

new_dir.x = -1

else:

new_dir.x = 1 

new_dir.y = (dist / (collider.p_height / 2)) * MAX_Y_VECTOR

return new_dir.normalized()


Step 12: Testing

Press F5 and enjoy pong!