In this develog, I'll talk about what I've done during 16th April to 22nd April, on the last day I built game and published to itch dot io.
Red warning sign
Game play core is made already at this point I thought, so I was thinking what can make game more fun. Making best score is always thrilling and fun right? So I thought making game available to stack up ingredients infinitely. But.. I tried camera zoom in and out with Canavs Layer , but after that button didnt' work, layout went super weird so I just quit. 😃
So instead, I made warning red sign at the top of the screen when the last ingredient on plate is over the certain y position. If it has too many layers reaching the top then game over.
Tween coin
After building one burger, coin will pop up from the plate and fall, then go to the money UI panel. I wanted single coin to draw curvy line when it falls and that's the thing I needed to figure it out hard.
In Coin.gd script, make function that instantiates coin scene and make coin fall drawing curve and move to the destination(money UI).
Firsf of all, I used tween. I watched tutorials on YouTube and they all added "Tween" node but I don't have it?
No result for "Tween"...
Apparently, they don't have it anymore in Godot4. So I needed to just create_tween() in the script.
var tween = create_tween()
I made coin move to destination position and modulate to 0 so it looks like fading away.
Bezier movement
So yeah, this worked as I intended. But what about coin falls? This was the first time I ever used Tween, even in Unity too I've never used, I just made animation. So I thought tween has more options like making curves when it moves etc. But no.. So I needed to figure out how to implement as I want.
And I figured it out.
From Godot docs
func _quadratic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, t: float):
var q0 = p0.lerp(p1, t)
var q1 = p1.lerp(p2, t)
var r = q0.lerp(q1, t)
return r
So there's a thing called Beziers.
Bezier curves are a mathematical approximation of natural geometric shapes. We use them to represent a curve with as little information as possible and with a high level of flexibility.
Simply, with few(more than 3 I guess, since well if there's 2 then it's just straight line 🤷♀️) positions, it makes curve to the destination from the departure.
In this case start point is the plate position and fall position is where it's gonna stop movement and middle point is somewhere between those two positions. So I made Bezier Movement.gd script. mid_pos is middle position(P1) and it's Vector2(randi_range(position.x, fall_pos.x), randi_range(position.y, fall_pos.y)) This script is attached to the coin scene and coin scene is also has coin.gd for the tween. In that script coin scene is instantiated at the plate position. So first position(P0) is its(coin scene's) position. And fall_pos is Vector2(randi_range(50, view.x - 50), view.y - 60). Since window size could be resized, by user or me(if I ever decide to change size) So I set to 50 to current viewport x position - 50. For example, if window size is 500 then it'll be 50 - 450, if it's 1,000 then 50 - 950. Simple!
func _process(delta):
timer += delta
if timer < 0.5:
position = bezier(position, mid_pos, fall_pos, timer)
And there's bezier function just like the example one in Godot docs. And you need to call it in the process functions or whatever else that has delta as parameter(I think. I'm.. new to Godot).
So I made to make curve line moving from its position to destination(fall_pos). I added if statement because just position = bezier(position, mid_pos, fall_pos, delta) makes coin going back to where it fell after tween, moving to the money UI. So it's weird, I added statement so it only move till 0.5 seconds(approximately since it's less than) and can't move again since timer(time) keeps ticking.
It kinda looks like just falls but some ones are little curve, it depends on the mid_pos I suppose. I'm content with the result, so I moved on.
In day 10, I worked on making collection book. Just with VBoxContainer or HBoxContainer, it was very confusing at first since whenever I add something to those containers as child nodes it just disappears. But I figured it out, add control node first then add labels or panel, whatever you want then it won't disappear.
I made struct. Was thinking what method I can use, and thought struct is the best way even though I've never used in game coding. So collection class has struct, that has name: String, unlock: bool, path: String, requirement_label: String
name is its name like tomato, cheese, and unlock is whether if it's unlocked or not, and path is its scene path, requirement_label is for showing on the collection UI.
In the Global.gd script which is added to Autoload,
const CollectionClass = preload("res://script/CollectionClass.gd")
var collection_ins
collection_ins = CollectionClass.new()
add these and from other script they can call Global.collection_ins.collection[i].name like this.
So in the script where instantiates ingredients scenes,
var new_ingredient = load(Global.collection_ins.collection[i].path)
ingredients.append(new_ingredient)
get the path and instantiate that scene. Also check if it's unlocked and if it is then add that index number to array, and in the instantiate function they randomly choose number in that array and instantiate one in that order.
And update requirement text like onion_req.text = "Serve over 20 layers burger to unlock." this.
Unlock data is saved in save file.
func save_collection():
var collection_file = FileAccess.open(COLLECTIONFILE, FileAccess.WRITE_READ)
for i in range(Global.collection_ins.collection.size()):
collection_file.store_string(str(Global.collection_ins.collection[i].unlock))
if i < Global.collection_ins.collection.size() - 1:
collection_file.store_string(",")
I was thinking of selling the ingredient at the shop but just decided to leave it secret, like achievement. So requirement_label is added after.
So same as I implemented collection, I used struct. And in Global.gd instantiate class so it can be called from other scripts.
Sold and equip data is saved in save file just like collection one.
func save_shop():
var shop_file = FileAccess.open(SHOPFILE, FileAccess.WRITE_READ)
var equip_file = FileAccess.open(EQUIPFILE, FileAccess.WRITE_READ)
for i in range(Global.shop.shop.size()):
shop_file.store_string(str(Global.shop.shop[i].sold))
equip_file.store_string(str(Global.shop.shop[i].equipped)
if i < Global.shop.shop.size() - 1:
shop_file.store_string(",")
equip_file.store_string(",")
In day 12, I made pop-up message when ingredient is unlocked.
It was quite difficult, I just made pop-up panel and made it appar when it's unlocked(manuall check like layer is over 20 then instantiate pop-up with nth ingredient img..) but didn't think of some could be unlocked at the same time.
So I needed to make array and put instantiated pop-up panel to there. And pop-up panel has script, inside it has bool value and set it to true when pop-up is disappearing after showing up.
# if there's a achievement box appeared already, so it's 2 including this one
for i in box_ins.size():
if !box_ins[i].done:
if i == box_ins.size() - 1:
count = i
else: count += 1
else:
box_ins[i] = new_ins
count = i
break
new_ins.position = Vector2(0, -40 * count)
Check through array that has pop-up panel that has been showed up and if done is true then set newly instantiated pop-up panel to array in that order. And set count as that index, and set the position y with -40 * count.
By doing so I could make it like this:
Also I changed UI design, computer not the book. Game over bg image too, but still I thought it's shitty so I needed to change again.
New UI design
I changed some UI design in day 12 but wasn't content with it so in the morning with fresh air I drew new design.
Right hand movement fixing
Right hand movement is decided by the viewport size. Check the gap between hand position and end of the screen(that's decided depending on the viewport size) But that was set in the ready function so I added to process so if window size is resized duiring the play then right hand can move correctly in the screen.
Glod/Blue coins
Add bool value parameter in the coin.gd script, if first one is true then it means it's blue coins.
if stacked_items >= 10:
var i = stacked_items/10
save_earning += 10 * i
For every 10 layers you get 10 coins more. And it's blue coin.
If all the ingredients on the plate is perfect, which means it stacked right in the middle, then you get gold coin. That gives you 100 coins.
Setting and Audio
Added sound slider in the setting screen.
@export
var bus_name: String
var bus_index: int
func _ready():
bus_index = AudioServer.get_bus_index(bus_name)
value_changed.connect(_on_value_changed)
func _on_value_changed(value: float):
AudioServer.set_bus_volume_db(
bus_index,
linear_to_db(value)
)
This script is attatched to each sliders, add bus name from inspector(@export makes it available). So that slider is gonna control that audio bus.
What is bus? Here:
And use AudioStreamPlayer2D, add loop .mp4 and check autoplay as music. Bus is Music.
Added some sfx too, all the audio stream player2d is added to Node2D and in Global.gd get node of it and call audio play function in Global.gd from other scripts. But this causes some problem.
So after googling I found why. When scene is reloaded, audio is freed. And since Global.gd is literally global script, it won't be freed but in that script it's getting audio node that will be freed when scene is reload. So after reloading(replay when it's game over) the sfx didn't play so I needed to make Node2D with AudioStreamPlayer2Ds into scene. I changed name to Sfx, and added Node2D called Audio Manager. It got the same role as Global.gd had, get the Sfx scene and instantiate, add to child.
const sfx = preload("res://scenes/sfx.tscn")
var audio
func _ready():
audio = sfx.instantiate()
add_child(audio)
And then in Global.gd, get node of the audio manager. then from other scripts instead of call like Global.audio.button_sound_play(), call Global.audio_manager.audio.button_sound_play()
This fixed the problem, after reloading scene now it plays sfx without any problem. I found out the reason why it's freed from googling but never found solution so I needed to figure it out on my own, I stayed up late night but at that night I was so proud of myself.
ETC
Coin goes to the wrong position of the destination, money UI, I changed position to global position of the destination and it worked fine.
Added coin jar phases, jar is gonna be filled with coins depends on the total earning amount. For example, if it's over 100 then little full, over 500 then half full. Simply added sprites.
When game over screen shows up, I added dancing burger in the backgroun:
Also I figured out some facts:
▷ I've been using the fourth one to resize anything. But turned out it's for scale, and first one that looks like mouse is resizing. I knew it like why they don't have resize icon in Godot?? So I changed scales of the fonts, and thought it's very weird like I want to just make font box bigger not with the font itself too. But I was just doing wrong..
▷ If all of a sudden engine can't find get_node that was fine before, then have a look about other parts of the code. For me 80% it was from other codes in the script.
New Menu design
In day 14 I also drew new design for menu.
PowerPoint has such a strong feature for picture, so I used it for the background. It looks so amazing 😃
And then I added how to play canvas in the menu.
I wanted to add gif but apparently you cant in Godot, and I tried to add video instead then it should be .ogg extension and I failed convert .mp4 to ogg, so I just decided to animate with the sprite. It was a little hassle but I did it anyway.
Build
This game was for the mobile but I ended up deciding to build webGL. And change .html file after building, to change loading bar colour.
And I uploaded on itch dot io, it perfectly worked.
Including the time when I was planning to make this drawing designs, and break time between day 8 to day 9, It took more than a month but actual developing period was 2 weeks. I was surprised, it never feels like that 🤣 If I didn't have any expereinces or knowledge of game making, definitely it'd take more time or come out ugly.
Some poeple might think it took too long, but to me who used this engine for the first time, I think it came out pretty well in short time and I'm supre proud of myself 👍 This is the power source that keeps me coding, facing problems then figuring out, implementing what I just imagined in my brain. I'm happy game coding is fit to me 😁
You can check my game here.
I feel like I've grew more. I'm thinking of using other language for making games too. See ya till next time then!