Pong

by Maceo in Craft > Art

107 Views, 0 Favorites, 0 Comments

Pong

pong1.PNG

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

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.

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

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

Border Node

pongborderdemo.PNG

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

Score Nodes

Capture.PNG

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

HUD Nodes

pongfinal iamge.PNG

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.

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

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.

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)


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)


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()


Testing

Press F5 and enjoy pong!