Dana Vrajitoru
I254/C297 2D Games Programming

I254/C297 Lab 5 / Homework 5

Date: Wednesday, February 7, 2024.
Due date: Wednesday, February 14, 2024.

In this lab and homework, we will create a platformer game.

Lab Part

Ex. 1. In this lab we'll create a project using animated sprites. We'll be using assets taken from https://opengameart.org/content/free-platformer-game-tileset.

Create a project called platformer and a scene called main inside. Create a Node2D object called Root in the scene. Switch the scene editor to 2D.

a. Background

In the project's settings, in Window, set the size of the viewport to to 1000/700. Download the file

BG.png

Drag it from the file explorer to the File System in your Godot project. Create a node of type TextureRect and drag and drop the BG image over its Texture property in the inspector at the top.

With the adjustments to the project settings, the image should cover the window entirely on the width and run over a little bit at the bottom (that's fine). In the Inspector, in Control, expand Layout and then Transform. Change the value of x in Size to 2000. The image should now be the double of the window size and should look stretched.

Scroll back up in the Inspector and under Stretch Mode change to Tile. Now the rectangle should show two copies of the image. We're doing this because usually in a platformer game, the level goes beyond the window, and the character moves left and right through it to complete the challenge. You can make it wider later if your level needs to be longer. Call this node Background.

b. Tile Map

Download the file

tileMap.png

Drag it to the file system in Godot to import it. Then add a new node as child of the Root of type TileMap. In the inspector, under Tile Set, click on <empty>, and set it as a new tile set.

Below the scene editor, in the are where the Output usually is, the panel should have switched to TileMap. Click on the TileSet tab to its left at the bottom of the panel, and drag the TileMap image from the File System into the darker gray rectangle below Tiles. It will ask if you want to update the tiles in the Atlas. You can answer yes.

To start creating tiles on the screen using the tile set, click on the TileMap tab at the bottom, make sure that the arrow tool is selected in the scene, click on a particular tile at the bottom, then use the mouse as a drawing tool. Note that the tiles are very small here. We'd like to make them a bit larger first.

Click on the TileSet tab again at the bottom below the scene. Scroll down in Setup to the Tile Size and change it from 16 by 16 to 32 by 32. This is the size that will be used to cut the picture into tiles. Now the tile set is partitioned better. Also change the tile size in the Inspector to 32 by 32. This is the size that will be used to partition the scene. For best results, these two sizes should match and be multiples of 16.

When drawing the tiles on the screen, they are still a bit small. In the Inspector, under Node2D, expand Transform and then set the Scale to 2 in both axes.

Place some dirt tiles all around the border of the level so that the player is contained inside the level. Add some more elements to it.

c. Player Character

Download the following file and import it in Godot, based on the animation from https://www.reddit.com/r/animation/comments/zzqndm/lil_practice_walk_cycle_made_in_procreate_based/:

dino.png

Add a node of type CharacterBody2D and call it Player. Add a Sprite2D node as child of the player. Drag and drop the dino over the Texture property of the sprite in the inspector. Scale it down so that it has a reasonable size for your level. Align the top left corner of the sprite with the top left corner of the image (point 0,0).

Add another node as child of the player, of type CollisionShape2D. Set the Shape property of this node to a new RectangleShape2D. Adjust the size of this shape so that it covers the character tightly.

Note that the sprite and the collider don't move together in the scene. For that, we need to save them together as a scene. Right-click on the player node and do Save Branch as Scene. Call it player.

Let's add a script to help our character move around. Double-click on the file player.tscn to open it for editing in a new tab. Add a script to the root node of the scene. The name it will propose, player.gd, is just fine. Before you click OK, make sure that the Template for this script is CharacterBody2D:Basic Movement. This will add some code to the script that is designed to move the character using the physics engine.

Play the game with this functionality. Since the player is now affected by gravity, it will fall and keep falling until it disappears from the scene. To fix this, we need to give physics properties to the tile set as well. For this, click on the tile set object, then in the inspector expand the Tile Set, then Physics Layers. Click on Add Element. This should add a Collision Layer and a Collision Mask.

Then at the bottom of the window below the scene editor, click on the TileSet tab, click on Paint, then from the menu Select a property editor choose Physics Layer 0. Then click on the tiles to paint this way all of those that should collide with the player (all the solid matter ones).

d. Animating the Character

Let's replace the character's sprite with one that is animated. Download the following file and import it to Godot.

dinoIdle.png

Then open the player scene or click on its tab if it's already open. Right-click on the Sprite2D node and click on Change Type. Choose AnimatedSprite2D. In the inspector, expand Animation, and next to Sprite Frames click on <empty> and create a new SpiteFrames. Click on SpriteFrames on the right side (that you just created). At the bottom below the scene editor, a new panel should appear where you can edit the animation.

In this panel, click on a button that looks like a grid, with the caption "Add frames from Sprite Sheet". It should open a dialog where you can select a sprite sheet. Select the image you just added, dinoIdle.png. The easiest way to cut it into frames is to set the Horizontal number to 5 and the Vertical to 1. Then scroll down in this window to see all the frames and drag the mouse over them to select them all, then click on the button Add 5 Frames. Adjust the position and collider size if necessary. With the Sprite2D selected, you can click on the Play button to preview the animation.

If you run the program, it doesn't play the animation yet. Going back to the animation panel, click on the button with the caption "Autoplay on Load" that is to the right of the trash can. Try the animation to see if it works.

Repeat the procedure to create two animations called running and jumping using the following image:

dinoRun.png

Use the entire sequence of frames for the running animation, and the frames 2, 3, and 4 for the jumping animation.

e. Changing Direction

Next, we want the character to be facing in the direction of movement. For this, under Sprite Frames there is a property to Flip H or V. We want to toggle the H one from the script based on the velocity of the player.

To create a reference to the Sprite2D, with the player scene selected, click on the script, and then drag the Sprite2D node towards the top of it, and just before you release it, hold the Ctrl button. This should add the following code to it:

@onready var sprite_2d = $Sprite2D

Then in the function _physics_process, all the way at the bottom, add the following code:

var is_left = velocity.x < 0
sprite_2d.flip_h = is_left

f. Using the Other Animations

We want to set the animation being played to one of the other ones while the character is jumping of running. For that, in the script, in the same function _physics_process, just below the line where the vertical velocity is set to JUMP_VELOCITY, add the following line:

sprite_2d.animation = "jumping"

Then at the top of this function, add the following:

if velocity.x > 1 or velocity.x < -1:
    sprite_2d.animation = "running"
else:
    sprite_2d.animation = "default"

g. Tweaking the Movement

In the same function, in the call to move_towards, divide the last parameter by 5, to make the movement slow down more progressively when you stop pressing the key to move it, or something larger if that works better.

To make the character fall faster after jumping, go to Project Settings, under Physics, 2D, change the Gravity value to something higher, like 1500 or 2000. Then increase the jumping and moving speed to values that work for your level design.

Also in Project Settings, we can configure the game to play with more than just the arrow keys. Open Project Settings, click on the InputMap tab. In the Add New Action box type left, right, and jump. Then next to left, click on the + and add the keyboard options Left and A. For right add Right and D, and for jump Space and D.

Now in the function _physics_process, replace "ui_left", "ui_right", and "ui_accept" with "left", "right", and "jump".

h. Camera Movement

If the level is longer than what you can see on the screen, we need to be able to move the viewport to follow the player. For that, we need to add a Camera2D node to the scene.

Select the main scene in the scene editor, which to 2D, then add a new node as child of the root, of type Camera2D. Then reposition it so that it covers the viewport properly.

Now we need to make the camera move when the player gets close to the border. First, add a script to the camera node. Then add a reference to the player in a similar way as we added the reference to the sprite in the player. The code should look like this:

@onready var player = $"../Player"

Then declare two variables minX and maxX at the top of the class. Assign to them the values 500 and 1500. This is assuming that the viewport has a size of 1000 and the level is twice as large. If that's not the case for you, you'll have to adapt the values to your layout. Declare another variable called half_width and assign it the value 500. This represents half of the length of the screen.

Now add the following code in the function _process in this script:

func _process(delta):
if player.position.x > position.x +  0.75 * half_width:
      position.x +=  half_width
      if position.x > maxX:
      position.x = maxX

This will cause the camera to move to the right with the player. The movement to the left will be part of the homework.

i. Collision

The last part of the lab is to resolve a collision between the player and some objects.

Download the following images:

Mushroom_1.png
Mushroom_2.png
Crate.png

Add one of them to the scene to represent collectibles for the player to pick up. The process should be similar to what we did for the player: as a CreatureBody2D node that has a sprite and a collision shape as children. Create another scene with the and add a couple of them around the scene.

Let's suppose that the collectibles you added are called shroom0, shroom1, shroom2, and so on. Then in the function _physics_process in the player script, add the following code just below move_and_slide():

var collision_info = move_and_collide(velocity*delta)
if collision_info:
    if collision_info.get_collider().name.contains("shroom"):
        collision_info.get_collider().queue_free()

With this addition, the character will be moving faster, so you probably need to adjust the speeds, like cutting the horizontal one in half and adjusting the vertical so that the character can reach whatever platforms you have the way you want it to, but not more.

Now the collectibles will be deleted as the character collides with them. Add a variable in the class called collected initialized as 0 in the function ready. Then every time you delete a collectible in the code above, add 1 to this variable.

Add a label to the scene and make it a child of the camera. Make the label display the value of the collected variable.

Homework Part

Ex. 2 a. Reset Button

Add a button to the scene as a child of the camera (so that it moves with it) with the caption Reset where you call a function from the camera script doing

owner.get_tree().reload_current_scene()

We're adding it to the camera script instead of the player, because the player's current scene might be just itself since it's save as a scene.

b. Camera Movement Left

Implement a second part to the camera movement that moves it to the left if the player gets too close to the left border.

c. Level Editing

Refine the look of your level and add a few more collectibles for the player to collect. Add a label that displays the number of collectibles collected.

d. Enemies

Add an enemy node by a similar procedure to the player and collectible, creating a separate scene for it so that we can add multiple copies. Reset the scene if the player collides with an enemy creature. Use a png file for it either created by yourself, or from a web site providing stock/free images. Give credit to the web site in the homework submission as a comment.

Homework Submission

Take a screen shot of your running program, showing the content of the screen while the game is running, and save it as png or jpg. Submit it along with the enemy png file you used, all the .tscn (scene), and all the .gd (script) files, to Canvas, Assignments - Homework 5. You can submit everything as a zip file if that's more convenient.