In last devlog, I wrote what I've made till 1st April. So in this devlog, it's gonna be about 2nd April to 5th April works.
Remember objects
So I wanted to show the result when player builds one burger, like showing what it looks like and then after 3 seconds count-down, player can build another new burger. So I simply duplicated stacked_ing item and added as a child node to result plate node. And just like I repositioned and rescaled on plate, I did it too. Here's what it looks like:
# add to result
var result_instance = stacked_ing[i].duplicate()
result_instance.sleep_mode()
result.add_child(result_instance)
# set result items position and scale
var local_position = plate.to_local(original_position)
result_instance.position = local_position
var relative_scale = stacked_ing[i].global_scale / plate.global_scale
result_instance.global_scale = result.global_scale * relative_scale * 0.9
First bun
And then, I wanted to put first bun when player starts the game so I made the function about it.
# instantiate first bun
func first_bun():
frozen = true
var instance = start_bun.instantiate()
ins_ingredient.append(instance)
stacked_ing.append(null)
plate.add_child(instance)
instance.position = Vector2(0,0)
result_manager.ready_newBurger = false
finish_pause = false
frozen is about.. making player not be able to move the plate, because even though ingredients collided with the plate, it just didn't go to the plate node as a child node as soon as it collides, so if you move the plate re-ally quickly then the plate stops at it collided with the plate, but then when the plate is moved and in other position then the ingredient goes to the child node at that time.
I think it's because I'm repositioning after it's added to plate node. So it collided so it stopped, but didn't move to plate node but then after a little it moves to plate node and set global position there. I'm.. gonna think about it later.
Display ingredient number
And then I added this bill panel, so player can see how many layers it's stacked including bottom/top bun.
Global.score += 1
When stacked_ing item moves to plate node, the score will be added.
Finish a burger
When player click this finish burger button then the right hand appears with top bun, and you press button again when you want to drop and when the top bun safely landed on the top of the burger then one burger is finished officialy! So with implementing result canvas and finishing burger, game play looks like this:
Sounds and looks easy to code? No.. it made me annoyed so much too. As you can see, the fading away animation seems weird(animation is gonna be added on day 6th) and first bun is still there, hold up I'm gonna show you how I fixed later.
I made a right hand node, The control node's anchor is set to right centre.
hand.position.x += move_speed * move_direction * delta
if move_direction == -1 && left_gap >= 0 && left_gap <= 5:
move_direction *= -1
if move_direction == 1 && right_gap >= 0 && right_gap <= 5:
move_direction *= -1
So the hand is gonna move at the end of the right side of screen to the left side of screen, back and forth.
func _on_finish_button_button_down():
if !Global.game_over:
finish_pause = true
if !right_hand.moving:
right_hand.move_hand()
finish_bun()
elif !finishing:
#drop the burger
var bun = last_pos.get_child(0)
right_hand.moving = false
# bun falls down
bun.freeze = false
finishing = true
When it's not game over, if you press the finish burger button then finish_pause is true so the ingredients stop instantiating, and right hand will start moving. And finish_bun function will be called, which instantiate finish bun scene to the "Last bun pos" node in right hand node. So the bun follows as hand is moving too.
And when you press again drop the burger, make the last bun not freeze so it falls. Sim..ple!
Collider to detect game over
This is for the collider for detecting game over. When ingredients collide with it then it's game over.
func _process(delta):
viewport_height = get_viewport().size.y
var shape = RectangleShape2D.new()
shape.extents.y = viewport_height * 1.2
set_shape(shape)
var viewport_size = get_viewport().get_size()
var relative_position = Vector2(
position.x / viewport_size.x,
position.y / viewport_size.y
)
position = relative_position * get_viewport_rect().size
This script is for the right and left side of the collider. Screen size could be changed and I want to be same as screen size length, so I needed to code this. And for the bottom collider, it's width instead of length.
I know you can set control node anchor and offset value to left wide or right wide, bottom wide... But just node2D doesn't have that so I wasn't sure if they're gonna be resized too as a child node. So I just coded.. maybe it's stupid way to do that but hey it works.
Result canvas
Okay so I made result canvas that shows the result of the burger you built, but I wanted to slide in from the left side of screen and show the result for 3 seconds and slide back into the outside of screen. So, I just simply used animation.
Anchor is at the left centre, BG will move and modulate of countdown 1 to 3 are gonna set to 0 when it's not their turn. The result is like this:
Continue after building burger
After building one burger, and after count-down first bun should be instantiated again so player can keep continuing stacking up.
if new_instance.is_in_group("top"):
clean = true
In the if statement where stacked_ing items is moved to plate node, if the ingredient is in the group called "top" then it means it's top bun so the burger is gonna be built, then clean is true.
if clean:
clean_plate()
if new_burger && !Global.game_over:
result_manager.sliding()
finish_pause = true
right_hand.disappear= true
print(right_hand.disappear)
new_burger = false
if result_manager.ready_newBurger:
total_burger += 1
result_clean()
first_bun()
result_manager.ready_newBurger = false
So when it's clean, clear the arra(ins_ingredient, stacked_ing) and new_burger bool value is gonna set to true in clean_plate() function. After clear the array result canvas is gonna show up and the ready_newBurger is set to true after the result canvas animation is done. So it means result was shown, and it's time for new bun. Clear the hamburger nodes in the result canvas and put the first bun.
And in first_bun() function, finish_pause will set to false then the ingredients will start beein instantiated.
Showing earning text
Global.score += 1
if new_instance.is_in_group("level1"):
earned_coinTxt.text = str("+ 10 coin")
save_earning += 10
coin_anim.play("coin")
With the score state, we're gonna add money to save_earning and show whenever the ingredient is stacked up. Like this:
Ingredient Animation
if body.is_in_group("center"):
in_middle = true
if !self.is_in_group("bottom"):
if !animated:
anim.landed()
animated = true
freeze = true
set_freeze_mode(FREEZE_MODE_KINEMATIC)
In on_body_entered function, if it's collided with collider that is in the middle above the ingredient, and it's not start bun and not animated yet then start landed animation, and make it freeze.
For the first bun, it's in the process function. For some reason, it thinks it's collided with its centre collider even though literally it's not colliding at all since centre collider is above the thing, I had to do the same code only for first bun in the process function. I don't know why.
if collided:
if !in_middle:
#print("not in the middle")
freeze = false
set_linear_damp(0)
elif self.is_in_group("bottom"):
if !animated:
anim.landed()
animated = true
freeze = true
set_freeze_mode(FREEZE_MODE_KINEMATIC)
Add money
current_earning += save_earning
coinTxt.text = str(current_earning);
When burger is finished, add save_earning to current_earning and show the earned money from finishing the burger to the coinTxt label.
Perfect
If player stack up ingreidents in the middle of the plate, then showing perfect animation. So it's not about in the middle of the ingredient that is the top at that time, it should be middle of the plate.
if !new_instance.is_in_group("bottom") && int(new_instance.position.x) >= -5 && int(new_instance.position.x) <= 5:
total_perfect += 1
perfect_anim.play("perfect")
And the animation is like this:
Particle for ingredient
Somehow I made particle watching Godot tutorial on YouTube. And since it's no gonna be loop and only show when ingredient lands on plate, you need to unclick the "emitting" and then go to script, code like this:
var new_particle = ing_particle.instantiate()
new_particle.one_shot = true
stacked_ing[i].add_child(new_particle)
new_particle.emitting = true
Make one_shot true, then add to node as a child node then make emitting true.
Game over canvas
I want to make plate to go to bottom bosition and game over canvas to sliding in(I like this too much lol) and show the result. Like this:
Just like result canvas, I used animation for sliding in motion. And change z-index(this is cool idk if Unity has this) to make the canvas go on top of everything, and plate on the top of game over canvas.
And as you can see there's best score, and total earning labels. For this, it's not that simple as "playerprefs" in Unity, but you can simply implement in Godot too.
extends Node
const SCOREFILE = "user://scorefile.save"
const COINFILE = "user://coinfile.save"
func _ready():
#debug_set_zero()
load_data()
func debug_set_zero():
var scorefile = FileAccess.open(SCOREFILE, FileAccess.WRITE_READ)
scorefile.store_32(0)
var coinfile = FileAccess.open(COINFILE, FileAccess.WRITE_READ)
coinfile.store_32(0)
func save_data():
# save score
var score_file = FileAccess.open(SCOREFILE, FileAccess.WRITE_READ)
score_file.store_32(Global.best_score)
# save coin
var coin_file = FileAccess.open(COINFILE, FileAccess.WRITE_READ)
coin_file.store_32(Global.total_earning)
func load_data():
# load score
var score_file = FileAccess.open(SCOREFILE, FileAccess.READ)
if FileAccess.file_exists(SCOREFILE):
Global.best_score = score_file.get_32()
# load coin
var coin_file = FileAccess.open(COINFILE, FileAccess.READ)
if FileAccess.file_exists(COINFILE):
Global.total_earning = coin_file.get_32()
Make save file, and save it. Simple!
func set_savedData():
# set high score
if Global.best_score < Global.score:
Global.best_score = Global.score
# set coin
if !Global.game_over:
current_earning += save_earning
Global.total_earning += save_earning
coinTxt.text = str(format_number(Global.total_earning))
SaveLoad.save_data()
save_earning = 0
And make function of reading saved data and set the best score and total earning.
Start/Continue canvas
When the game starts, like the game "FALLCUP" I made with Unity for the first time, I wanna set start canvas visible and pause then start the game by clicking screen.
This time I won't stop the time, I'll just set bool like when the ready canvas is on then player can't move plate and can't instantiate ingredients. It looks like this:
func gameOver():
set_savedData()
# show game over canvas, and clean other plates
frozen = true
isOver = true
hand.position = Vector2(get_viewport().size.x / 2, get_viewport().size.y - 50)
end_anim.play("game over")
end_scoreTxt.text = str(Global.score, " layers burger")
total_burgerTxt.text = str(total_burger, " burgers have made")
total_coinTxt.text = str("Earned ", current_earning, " coins / Total: ", Global.total_earning)
best_scoreTxt.text = str("Best score: ", Global.best_score)
And this is game over function.
Earning number format
The money jar UI, it shows total earning. When it's over 1000 then it should be like 1K. So I coded for that:
func format_number(num):
if num < 1000:
return str(num)
elif num < 1000000:
var thousand = num / 1000
var hundred = (num % 1000) / 100
return str(thousand) + "." + str(hundred) + "K"
else:
var million = num / 1000000
return str(million) + "M"
And in ready function, coinTxt.text = str(format_number(Global.total_earning)) call the format function so it works and shows 1.4K instead of 1489.
So far, what I like about Godot is it's very light. It doens't take 5 mins to open one project, it's really just like 5 seconds. And in Unity if you change game object name then in animation it says it can't find game object called "whatever it was named before" but in Godot it's not like that, and you can add any node that is in the scene to animation.
And.. what I don't like about, is I can't see the scene while it's running the game. In Unity you can see where the object instantiated, you can move object while playing in the scene but Godot is more like.. pygame or some other IDE? If you hit play then it just shows new window and you can't see anything behind the game in the scene.
I think I've made more than a half by now? I just need to fix some physics tricky part, and add shop, collection... and some more. I'll take a break studying C laguage(cause I think my basic knowledge is fading away) then continue making. See ya then!