Press Ctrl+S to save the scene
Create a new folder called Scenes
Create a new folder called Player
Save the Player scene as player.tscn
Rename the Coin Grabber folder to
Change the folder colours
We chose Area2D because it can detect when other objects overlap or collide with the player.
Now we will add the player visuals.
Add an AnimatedSprite2D as a child node
Ignore the warnings at this point.
Select the AnimatedSprite2D node
In the Inspector on the right
Now click on the word SpriteFrames
To open the SpriteFrames tab
At the bottom of the screen
Rename the default animation to run
Add two new animations and call them
In the Assets => player folder
Drag the corresponding images into their animations
Set each animations speed to 8.0 FPS
Select the idle animation
This makes idle autoplay by default
Because the Player image is a little small
We will scale it up by doubling its size
Select the AnimatedSprite2D node
In the Inspector on the right find Transform
Set scale to 2.0 on x and y axis
For the Area2D of our Player to detect anything
We must include a CollisionShape2D node
Add one as a child of Player
For the CollisionShape2D to function
Select the CollisionShape2D
Assign a New Rectangle2D shape
Use the orange handles and
fit the shape around the player sprite
or set the size in the Inspector
Notice how the warnings have been resolved!
Now we will program functionality
Into the player by attaching a script
Press the attach script button
You can leave all settings as they are
The first line of every script
describes what node it is attached to
Here the script is attached to an Area2D node
We will define some variables to control
In GDScript, writing types like : int or : Vector2 is optional, but doing so gives you three big advantages:
Catches bugs before you run the game: Godot warns you immediately in the editor if you accidentally try to do something invalid, like adding text to a number.
Better autocomplete: Because Godot knows the exact type, typing a period (.) instantly shows you all available properties and methods for that specific object.
Faster performance: Typed code allows the engine to optimize instructions behind the scenes, making your game run more efficiently.
The @export tag makes a variable adjustable in the Inspector
This is useful for tweaking values while testing the game
int is a data type that stores whole numbers (integers)
Vector2 is a data type that stores two values (x and y)
When the game runs we must repeatedly check for keyboard input, move the player in the given direction and play the correct animation.
Godot provides a function which runs 60 times per second.
This function is called _process
Update Player position on-screen
In GDScript, the -> symbol declares what kind of data a function returns when it finishes.
When you write func _process(delta: float) -> void:, you are explicitly stating: "This function performs some actions, but it does not send any data or answer back to whatever called it."
Why write it if it returns nothing anyway?
Self-documenting code: At a glance, anyone reading your code knows not to expect a value like var result = my_function().
Prevents silent mistakes: If you accidentally add a return 42 inside a -> void function, Godot will immediately highlight it as an error before you even run the game.
Consistency: Just like typing variables (: int, : Vector2), return types keep your codebase clean, predictable, and fully statically typed.
In game development, velocity is speed with a direction (pixels per second). We get the input direction, scale it by our speed to get our true velocity, and then multiply by delta so movement stays consistent regardless of frame rate.
You can run the scene (press F6) and test the movement now.
Notice that the fox can run out of the screen boundary? We can fix that by clamping his position to the screen extents.
To match animation to movement in the process function
If the player is moving we play the "run"
Depending on if they are moving left or right
Note that $ followed by NodeName
Gives us access to the internal properties of a node in our Scene
Eg: $AnimatedSprite2D.animation
Lines 19 & 20 can be confusing so I have shown how they can be re-written to make their purpose move obvious
Take a moment to test your game again to check that it is animating correctly.
Right now, our _process() function is doing two very different jobs:
Handling movement (calculating input and updating position)
Handling visuals (deciding which animation to play and which way the sprite faces)
While this code works fine for a simple player, _process() runs every single frame. As we add mechanics like jumping, taking damage, or sound effects, cramming everything into one function makes scripts difficult to read and debug.
A good rule of thumb in game programming is to give each function a single responsibility. Instead of doing all the heavy lifting itself, _process() can act like a manager, calling small, dedicated helper functions to handle specific tasks.
Let’s refactor our code by splitting movement and animations into their own functions:
The new _process function
Look how neat and tidy it is!
Why Do Some Functions Start with an Underscore (_)?
In Godot and GDScript, a leading underscore indicates that a function is internal or special:
Built-in Engine Callbacks: Functions like _ready(), _process(), and _physics_process() are pre-defined by Godot. The leading underscore is Godot's way of signaling: "The engine calls this automatically behind the scenes—you don't need to call it manually in your code."
Auto-Generated Signal Handlers: When you connect a signal through the editor, Godot automatically names the receiver function with an underscore (such as _on_area_entered).
"Private" Helper Functions: Programmers also use a leading underscore for their own custom functions (like _calculate_score()) as a convention to tell other developers: "This function is meant to be used only inside this script, not called from outside scripts."
In short: if you see a function starting with _, it is either an automatic hook managed by Godot or an internal helper meant to stay within that file.
A new function for handling movement
A new function for handling animation
Now we can create a function for: starting the game
When a player loses a life or starts a brand-new match, we need a way to reset our character back to the centre of the screen, wake them up, and make sure they look ready to play.
Now we can create a function for: ending the game
set_process(true) or set_process(false) turns the process function on or off.
When the player dies we turn it off, since movement and animation code is in the process function - it all stops!
When the player hits a coin or an obstacle, they shouldn't have to worry about updating the score on the screen or triggering a game-over screen. The Player's only job is to announce what just happened to them: "I picked up an item!" or "I got hurt!"
In Godot, this is done using signals. Think of signals like a megaphone: the Player shouts an event out into the game world, and any node that cares—like the UI or the Game Manager—can listen in and react accordingly.
Let's create two signals at the top of our Player script:
Signals need to be connected to the nodes that listen to their broadcast.
Select the Signals tab to the right of Inspector
You can see the custom signals which we made
As well as the built-in signal for Area2D nodes
To connect the signal to our script
Double click the area_entered signal
Godot Connects the signal to a new function in our script
Whenever our player overlaps another area, Godot automatically calls _on_area_entered(area) and hands us the specific object we touched through the area parameter. We can check what that area is, whether it belongs to a coin or an obstacle, and react accordingly.
If it is a coin we call the pickup function
then we emit the pickup signal
If it is an obstacle we emit the hurt signal
and then call the die function
We use area.is_in_group() to check whether the touched object is tagged as a "coins" or "obstacles" group member.
This code references things we haven't built yet (like area.pickup()
We will create the Coin and Obstacle scenes in the very next step and tag them with these exact group names.