You are browsing as a guest. Sign up (or log in) to start making projects!

Wild Coin Catcher

  • 4 Devlogs
  • 9 Total hours

Godot game

Open comments for this post

1h 7m 44s logged

Basically, what I did today was just seeing what window sizes would look better + uploading different bgs/sprites, while talking to my friend artist who was kindly drawing all that for me(@ wild_roe in tiktok/youtube/discord, I think she’s opened in commissions!).
The only coding stuff I did was mirroring the player animation when turning left and making the player change to standing sprite when not moving.
I was making the sound effects and the song in fl studio, but I wasn’t pleased with what came out, so I’m going to do it later/just grab free one from the internet. Also, I’ll add a different font in the game, cuz this one doesn’t really match the stuff going on..

0
0
2
Open comments for this post

5h 10m 16s logged

main.gd spawns the coins and bombs, counts score, makes everything faster over time, handles game over and saves your record.

  • the spawning

The core idea is a Timer node. Every time it times out, I spawn one object at a random x position above the screen. Usually a coin, sometimes a bomb (35% chance). The bomb and coin are separate scenes, so main just does this:
var spawn_x := randf_range(SPAWN_MARGIN, screen_width - SPAWN_MARGIN)
var spawn_position := Vector2(spawn_x, -40.0)

if randf() < BOMB_CHANCE:
var bomb := BombScene.instantiate() as Bomb
bomb.position = spawn_position
bomb.fall_speed = fall_speed * BOMB_FALL_MULTIPLIER
bomb.hit_player.connect(_on_bomb_hit_player)
add_child(bomb)

randf() gives a number from 0 to 1, so < BOMB_CHANCE with 0.35 means 35% bombs. Same code for the coin, just with the “collected” signal instead.

  • the difficulty system

This is the part i’m most proud of. There’s one value called difficulty that goes from 0 to 1, and everything reads from it. It’s computed like this:
func _compute_difficulty() -> float:
var time_progress := time_alive / RAMP_DURATION
var milestones := int(float(score) / SCORE_STEP)
var score_progress := milestones * SCORE_STEP_BONUS
return minf(time_progress + score_progress, 1.0)

Two things feed it: the clock (maxes out on its own after RAMP_DURATION, which is 300 seconds = 5 minutes) and your score (every SCORE_STEP = 10 coins adds a permanent 0.04 bump). The minf(…, 1.0) caps it so nothing gets faster past full difficulty.
And then everything scales off that one value with lerp:
player.speed = lerpf(PLAYER_SPEED_START, PLAYER_SPEED_MAX, difficulty)
spawn_timer.wait_time = lerpf(SPAWN_TIME_START, SPAWN_TIME_MIN, difficulty)
var fall_speed := lerpf(FALL_SPEED_START, FALL_SPEED_MAX, difficulty)

lerpf(a, b, t) just means “slide from a to b as t goes 0 to 1”. Because the player speed scales off the SAME difficulty value as the coins, it stays fair. The numbers are picked so even at max speed you can still cross the whole screen in the time a coin takes to fall. Bombs are what actually kill you, not unreachable coins.
Small trap in that code: see the float(score) part? In GDScript, score / SCORE_STEP with two ints does integer division, so 5 / 10 gives 0 and rounds everything down. Without that float() the milestones just silently don’t work. Tiny thing, cost me a while.

  • connecting everything with signals

Main connects to the signal right when it spawns each object. That way the coin doesn’t know the score exists and the bomb doesn’t know there’s a game over screen. Main is the only one who knows everything.
This is where I lost the most time honestly. Chain of pain:
Got “Class Bomb hides a global script class”. Turned out there were basically two scripts fighting over the same class_name. Had to find the duplicate and kill it.
Then got a Nil crash when spawning. The as Bomb cast in the spawn code was quietly returning null because after fixing #1 the scene’s script situation was messed up. Godot doesn’t crash on a failed cast, it just hands you nothing and lets you crash one line later.
THEN “script inherits from Area2D, can’t be assigned to CollisionShape2D”. While re-attaching the script I put it on the wrong node lol.

  • extra

The logic is done, but the game still looks like colored triangles and squares. I’ll ask my friend(BIG shoutout to an artist @ wild_roe in tiktok and youtube, I’ll ask her to use her character I like a lot) to make the sprites and the background, and the music i’m gonna make myself.

0
0
1
Open comments for this post

39m 12s logged

so I just quickly made the coin script(copy and pasted the bomb script and only changed the signal to collected, so it adds coins instead of stopping the game) and player script:
_physics_process reads your input every frame and moves the player by changing its position. arrow keys/WASD give a direction, and it moves at speed in that direction.
there’s a clamp so you can’t leave the screen — it keeps the player’s x and y inside the screen bounds so you can’t wander off the edges.
in _ready it adds itself to the “player” group - that’s the tag the coins and bombs check for when something touches them. so this one line is what makes the player actually recognizable as “the player” to everything else.
it doesn’t handle score or game-over at all, it just moves and stays on screen.

so now, the player can move!

0
0
1
Open comments for this post

1h 43m 35s logged

ok so I completely forgot to actually keep a journal while building this lol, so i’m just writing down what i did from memory.
rough order so far:
built the nodes first (bunch of Area2Ds + shapes), then started actually coding and now the bomb’s finished

bomb, player and the coin got two children:
a Polygon2D - the red triangle you see. i drew it as a polygon instead of using an image, so there’s no asset/importing stuff to deal with
a CollisionShape2D - a little circle(for coin and the bomb) and a rectangle(for the player). this is the actual hitbox.

the bomb code:
the bomb is an Area2D, which is basically a detection zone.
the main idea is the signal hit_player. the bomb has no clue what the score is or that a game-over screen even exists- it just shouts “hit_player” and main.gd listens and deals with it. keeps the bomb simple and reusable, its only job is to fall and to say when it hit you
_physics_process moves it down every frame with position.y += fall_speed * delta. the * delta is what keeps it falling at the same speed no matter the framerate
if it drops past the bottom of the screen it calls queue_free() to delete itself — otherwise every bomb that misses just keeps falling forever and piles up
_on_area_entered runs when something overlaps it, but coins and the player are all Area2Ds too, so it checks is_in_group(“player”) first to make sure it’s actually the player -> then emits hit_player and removes itself

I’ll make the coin code after that(same Area2D, same falling, same group check - it’ll just emit a “collected” signal and add a point instead of ending the game) and then player+main cuz those 2 will be harder ig

0
0
2

Delete project?

Are you sure you want to permanently delete this project? This action cannot be undone.

All devlogs, followers, and associated data will be removed.

Followers

Loading…