Getting Started with 2D Physics in Godot

Explore Godot’s physics engines to create dynamic games. Learn collision handling, add sound effects, and build engaging gameplay where luck and skill intersect! By Ken Lee.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 4 of 5 of this article. Click here to view the first page.

Pegs Placement

Unlike the marble, there are numerous pegs in the scene, and you’ll soon learn how to duplicate them.

Additionally, you’ll learn how to use the snap tools provided by Godot to place the pegs in a regular pattern.

Now, return to the main scene by opening the main_scene.tscn file to create the pegs.

Once the scene is opened, add a new Node2D named Pegs to contain all the pegs. This will help organize your pegs better.

Next, add another Node2D as a child node of Pegs and name it PegRow1. This node will contain the pegs in the first row.
Set its Transform properties so that it appears at the desired position. In this case, set the position to (x: 0, y: 450).

Now, drag and drop the peg.tscn file, which represents the Peg, onto the PegRow1 node. This action will add the Peg scene as a child of PegRow1.

Adjust its Transform ▸ position properties to (x: 0, y: 0). Then, rename the created peg node to Peg1. This helps to distinguish it from others.

To add more pegs, duplicate the Peg1 node by selecting Peg1, then pressing Cmd+D on Mac or Ctrl+D on Windows.

Duplicate Peg1 node five times to create a total of six pegs in PegRow1.


Pegs Layout Design

The pegs you created may not be well positioned. Luckily, it’s easy to adjust their positions using the snap tool.

Setting Up the Snap Tool

This is the design of the pegs layout you’re aiming for:


Pegs Layout Design

Before using the snap tool to adjust the peg’s positions, you need to configure the snap setting.

Open the Snapping Option by clicking on the three-dot icon on the toolbar. Then, select Configure Snap.

When the Configure Snap dialog opens, set the Grid Offset to (85, 0) and the Grid Step to (110, 10). After the configuration, it should look like this:


Configure Snap

Press Shift+G to activate the Grid Snap. When the Grid Snap too is active, the icon color turns blue and the grid lines appear on the screen.

Grid Snap On

Position Pegs Using Snap Tools

When the Grid Snap is active, you can move the pegs according to the configured grid settings rather than one pixel at a time. This feature helps you accurately position the pegs.

Now, move Peg1 to Peg6 to their corresponding positions using the red arrows as a guide. As you move them, they will snap into place according to the grid spacing of 110 pixels.


Setting Pegs position

To create the next two rows, duplicate PegRow1 without setting everything again. Select PegRow1 and press Command+D on macOS or Control+D on Windows to duplicate it. A duplicate of PegRow1 is created and automatically named PegRow2.

To ensure the pegs of the two groups do not overlap, adjust the position of PegRow2. Select PegRow2 in the Scene panel, then modify its Transform ▸ Position property to x: 55, y: 630.

After adjusting the position of PegRow2, you may notice that the Peg6 node is beyond the game board. Select the node and press Command+backspace on Mac or Delete on Windows to remove it.


Remove the extra peg

Duplicate PegRow1 again using Command+D on Mac or Control+D on Windows to create the PegRow3 node. Move PegRow3 to a lower area by adjusting the Transform ▸ Position property to x: 0, y: 800.

You’ve created all the pegs and the game board should be like this:


All Pegs position ready

Running the scene again. You’ll see the marble bouncing off the pegs when it hits them and make them move in random paths.


After adding the extra peg

Fixing the Marble Stuck at the Peg

When testing the game, you may notice the following problem:
Some marbles are stuck at the pegs.


Demo of marble and pegs ready

To fix this, you can write a function to apply a random force to the marble on the collision and make it escape from the pegs.

Now, open marble.tscn, select the Marble node and modify the Contact Monitor setting in the Inspector view as follows:

  • Solver ▸ Max Contacts Reported : 1
  • Solver ▸ Contact Monitor : On

Then, create a new script for the Marble now.

Click the dropdown arrow at the Script property in the Inspector view and select New Script.

The Attach Node Script dialog will be displayed. Change the path to res://scripts/marble.gd and click Create. It’ll create and attach the script to the Marble node.


Complete game flow ready

Now, open the marble.gd script file and add the following function to apply a random force to the marble:

func _add_random_force():
    var dir_x = 0
    if randf_range(-1, 1) > 0:
        dir_x = 1
    else:
        dir_x = -1

    var force_x = randf_range(60, 100) * dir_x
    var force_y = randf_range(120, 150)

    var force = Vector2(force_x, force_y)
    self.apply_central_impulse(force)

Then, invoke _add_random_force within the _on_body_entered function:

func _on_body_entered(body):
    _add_random_force()

Next, bind the body_entered signal to the _on_body_entered function. To do so, select Marble in the Scene View. Then, switch to the Node view, right-click the body_entered signal and select the Connect option.

When the dialog opens, choose the _on_body_entered function in the Received Method property. Click the Connect button to confirm and close the dialog.

Return to the main scene main_scene.tscn and run the game. You’ll observe the marble bouncing off the pegs and not getting stuck.


Demo of marble stuck fixed

Making a Complete Game Flow

At this point, you’ve built all the core gameplay elements, including the marble, pegs, and score triggers. However, the game remains incomplete without a full game flow. The missing parts are marble count deduction and game over handling.

Marble Count Deduction

The marble count indicates how many times the player can drop the marble. It’s similar to lives or credits in other games. When the player runs out of marbles, the game is over.

To track the marble count, create variables to monitor the number of marbles available and the number of marbles that have dropped in the score zone.

Open the main_scene.gd script file in your Godot project. Then, add the following code at the beginning of the script:

const MAX_MARBLE_COUNT = 7
var marble_count = MAX_MARBLE_COUNT
var dropped_count = 0

The MAX_MARBLE_COUNT constant defines maximum number of marbles that can be dropped in a game. The marble_count variable tracks the number of marbles remaining.

The dropped_count variable tracks the number of marbles that have dropped in the scoring zone. This variable is used to check the game over condition.

Next, update the marble_count variable to the User Interface at the beginning of the game. Navigate to the _ready() function and modify as below:

func _ready():
    total_score = 0
    $GUI.set_score(total_score)

    $GUI.set_marble_count(marble_count)

Then, modify the _spawn_marble function to prevent spawning when no marbles are left, and update the marble count to the user interface after a marble is spawned.

Here is code snippet:

func _spawn_marble(_position):
    #1 
    if(marble_count <= 0):
        return
	
    # original code (START)
    var border = $GameBoard.get_node("DropBorder") 

    var borderRect = border.get_global_rect()
    var borderY = borderRect.position.y

    if(_position.y >= borderY):
        return

    var marble_instance = marble.instantiate()

    marble_instance.position = _position
    marble_instance.name = "marble"

    add_child(marble_instance)
    # original code (END)
	
    #2 
    marble_count -= 1
    $GUI.set_marble_count(marble_count)

Here’s the explanation of the code:

  1. Prevent spawning any marble when the marble count is zero.
  2. Update the new marble count to the user interface

Run the game again and you’ll see the marble count decreasing when a marble is dropped.


Complete game flow ready