Tag Archives: code

pongodot day 6: scoring

Time to handle scoring. The first step is to create goals. For this we are going to use an Area node, attached to it is a CollisionShape2D that defines the region. We create one of these on each side of the field. Not sure why I’m saying we, I did it.

GOOOOOOOOOOOL!

Now, it’s time to learn a little bit about signals in Godot. Signals are Godot’s version of an observer pattern, they allow you to have a trigger on one node be reacted to from another. According to Godot’s documentation this is to help limit coupling and keeps your code more flexible. In our use case here, for example, we can send a signal from the goals to the game controller, allowing it to react to the ball entering the zone.

We’ll start with the player goal. In the editor we’ll go to the node tab to expose the available signals.

Double clicking on body_entered brings up this screen, where we hope to connect to the script on the game scene Node.

Not really a need to change the receiver method name, so I click Connect.

And now the editor has added a new function to our GameController script where we’ll process the signal.

For testing purposes we’ll start with something simple. When the ball enters the area, we’ll delete it. Also, I’m going to take this chance to fix some of last week’s tech debt, and make it so only one ball exists at a time.

comPaddle and ball are a bit too coupled at the moment, so it’s causing extra headaches.

With that we can now delete the ball, as well as prevent another ball from being fired while a ball is active.

Next, time to add a score. For this we are going to have our GameController handle and track the score for both the player and the com. When the PlayerGoal is entered we need to make sure to increment the Com score (and vice-versa for the other goal).

Next we need a way to display the score. I spent a bit of time to download a free pixel font, and spent a bit more time googling and learning how to import that font into Godot, but I finally got my two labels made, and I’m pretty happy with the look of them. Next I just need to change the label text for each to match the current score value associated with it.

$PlayerScoreLabel is another way in code to get the Node called PlayerScoreLabel

With this it’s just a matter of pressing go and seeing if it works.

And, almost immediately I ran into a problem.

No, this one isn’t a gif

Before even launching my first ball the score is already 3 to 2. It took me a bit to figure out the problem which is that my paddles are technically partially inside my scoring area, so they are triggering the scores to go up before the initialization even finishes…

The easy solution would be to simply move the paddles slightly so that they are not inside the score zone. The better solution was to learn a little bit about collision layers and masks in Godot.

By setting up separate collisions layers for the paddles and balls, and making the score area only care about the ball layer with a mask, it should solve my issue.

setting up the layers in project settings

In project settings I made three layers, named Paddles, Ball and Goals.

Player Paddle

In the inspector I can set the collision layers and masks for different object. For my Paddles I set them on layer 1, and have them mask on 1 and 2.

Ball

For the ball, it lives on layer 2, and has a mask on all three layers.

goals

And then the goals live on layer 3, and only listen to layer 2.

And that fixed my issue. Now, when I start up the game the score is 0 to 0. As much as I would have appreciated a bit of a handicap in my favor, better to fix the issue and have everything behave as I want it to.

And with that, we can finally play Pong!

pong

After each ball pressing spacebar will fire another ball from the player side. Of course, you can’t win or lose yet, as you play the scores will just increase with no real end to the game. But, that’s a problem for another day. Here is the final code for my GameController script after today:

extends Node2D

var ballscene = load("res://Ball.tscn")
onready var comPaddle = get_node("Com Paddle")
onready var playerPadd = get_node("Player Paddle")
var current_ball

var playerScore = 0
var comScore = 0

func _init():
	playerScore = 0
	comScore = 0

func _process(_delta):
	$PlayerScoreLabel.text = str(playerScore)
	$ComScoreLabel.text = str(comScore)
	if Input.is_action_just_pressed("launch"):
		if current_ball != null:
			return
		current_ball = ballscene.instance()
		current_ball.position.x = playerPadd.position.x + 20
		current_ball.position.y = playerPadd.position.y
		add_child(current_ball)
		comPaddle.ball = current_ball

func _delete_ball():
	if current_ball != null:
		current_ball.queue_free()
		current_ball = null
		comPaddle.ball = null

func _on_PlayerGoal_body_entered(body):
	_delete_ball()
	comScore += 1

func _on_ComGoal_body_entered(body):
	_delete_ball()
	playerScore += 1

Tomorrow I plan to add victory/defeat, and maybe a start screen.

-Ike

pongodot day 5: ball launching

Before getting into the meat of today’s changes, two small changes I made yesterday after I finished the main bulk of yesterday’s changes.

First off I wanted to make it so the ball reacts differently when you hit it with different parts of the paddle. I did this by comparing the height of the ball to the height of the KinematicBody2D that it collides with. Since only the paddles have that Node type I was able to check that I had hit a paddle with is_class()

lines 25-26 are a weird solution to a particular edge case that I’m not going to go into right now

I might play around more with the exact thresholds for where on the paddle we get different angles, but I was happy with this change.

The other small change I did was to add some sprites down the middle of the playfield.

No, this one doesn’t move.

Originally, my plan for today was to get the scoring mechanisms added in, but as I started to break down what that required I realized that it was a bit bigger of a task than my mantra of “one small change a day” for this month-long project. So, I’m breaking that down into smaller chunks.

Today, my goal is to be able to spawn a new ball into the scene, and have everything work. After a point is scored the ball is destroyed and then it is launched by the side that scored. We could simply move the ball, however, I think the system works better if we are able to spawn the ball anew.

So, to start with, we need to create a ball to spawn. There is one in our scene already, but this isn’t really what we are needing. Drawing on my Unity experience, in Unity I would here create a prefab, and it turns out, in Godot we do something similar.

In Godot everything is nodes. Our game scene is really just a node. So, what we need to do is essentially create a new scene that contains our ball node that we can then load in. This works similar to prefabs in Unity, if Unity Scenes were also basically prefabs.

Ball is it’s own scene now.

So, I copied the structure of the Ball in our previous scene, and created a new scene, Ball.tscn. This new scene is where our ball will be housed. Next, we need a way to spawn the ball in.

It’s time for a game controller. In the Game Scene, on the root node I added a new script, called GameController. This script will be responsible for tracking interactions between nodes, as well as eventually tracking and displaying the score. For now, however, it will simply spawn the ball.

short and sweet.

So, let’s go ahead and run the scene and see what happens.

ball is spawned in by the script at the start.

Now, you might notice this broke something. Our “COM” paddle was just following the ball, however, since there is no ball in the tree at the start it’s unable to get it, so we need to instead be able to tell the paddle that there is now a ball available.

We tell the paddle what the ball is

With this change, the com paddle is now able to properly follow the ball. But! We can still do better. Instead of simply spawning the ball at a given location at the start, we instead use that input we cleverly set up yesterday and didn’t use. We’ll wait until the player presses space, and then launch the ball a point a few pixels away from the player paddle. That script looks like this.

no, your script names are inconsistent when it comes to presence of spaces.

Alright, so I’m not totally confident this will work, but let’s go ahead and try it.

working as intended.

Alright, so you might have noticed a few problems there. I should probably instead check for the first frame the spacebar is pressed, and only spawn then, rather than, you know, every frame.

Some quick fixes to the script and it now looks like this:

Should be better.

And with that, the issue is fixed. I can still press the spacebar multiple times to spit out additional balls, but fixing that (as well as deleting balls when you score) is tomorrow’s problem.

there we go.

-Ike

pongodot day 2: i am ball

Today’s goal was to add in a ball and get it to start moving. Pretty simple, and wanted to get a basic understanding of the scripting system in Godot.

Once again, using gimp I made a ball, keeping it simple it’s just a white 64×64 pixel square. Importing that into Godot and slapping it into the tree was quick enough.

Truly the most riveting of games

But just dragging things and assembling them on a screen was yesterday’s task, so today let’s get it to move.

Godot does support C#, which is the language that I am most familiar with, having used it most in both my professional career as well as in past game projects. However, the goal of this project is to learn, so instead, let’s instead use the built-in scripting language, GDScript.

GDScript also makes sense to use since it is better integrated into Godot, designed to work natively. It’s syntax is very reminiscent of Python, which I have used in the past, and there is a lot about Python that I like, just haven’t had as much opportunity to use it in years.

Quick right click on the ball in the scene, click Add Script and we are off to the races.

The little scroll icon means I clicked the right thing.

More complex scripting will come later, but for now we are in extremely basic mode. For tonight’s brief session, the goal is to just get the ball to move diagonally on the screen. Everything else can wait for tomorrow.

See if you can spot the error.

Take one had a small issue. I made my ball out of a sprite, and it turns out this was probably not the best approach. As a sprite I don’t have access to move_and_slide, so, I need to make a few adjustments.

More nodes.

This is another area where I need to adjust my thought process. In unity all these properties would just be part of the same object, but in Godot I need to nest them in the tree structure. Again, under the hood it doesn’t work all that differently, it’s just a matter of shifting my perspective of how things are laid out.

With that I just needed to change the script I wrote so it extends KinematicBody2D instead of Sprite, and boom! I could now use move_and_slide to move the little cube across the screen.

I tried using code blocks in WordPress, but they don’t capture the highlighting the right way for GDScript.

A lot can be done to clean this up, but for now I hit play and presto! It works! Rarely do things just work smoothly on the second try, so I’m feeling pretty good about this.

woo!

Of course, it doesn’t obey the walls, but that is to be expected (as I didn’t code anything to make it obey the walls). That’s tomorrow’s problem. For now, I’m going to finish sipping on this improved whiskey cocktail and relish in another step closer.

Sipping on this while I coded was probably not helping, tbh.

-Ike