Merge pull request 'reduce' (#1) from reduce into master

Reviewed-on: #1
This commit is contained in:
topvennie 2024-04-18 16:14:52 +00:00
commit 59f6969d26
14 changed files with 264 additions and 81 deletions

View file

@ -19,7 +19,7 @@ dock_filesystem_split=0
dock_filesystem_display_mode=0 dock_filesystem_display_mode=0
dock_filesystem_file_sort=0 dock_filesystem_file_sort=0
dock_filesystem_file_list_display_mode=1 dock_filesystem_file_list_display_mode=1
dock_filesystem_selected_paths=PackedStringArray("res://") dock_filesystem_selected_paths=PackedStringArray("res://Player.tscn")
dock_filesystem_uncollapsed_paths=PackedStringArray("Favorites", "res://") dock_filesystem_uncollapsed_paths=PackedStringArray("Favorites", "res://")
dock_3="Scene,Import" dock_3="Scene,Import"
dock_4="FileSystem" dock_4="FileSystem"
@ -27,17 +27,17 @@ dock_5="Inspector,Node,History"
[EditorNode] [EditorNode]
open_scenes=PackedStringArray("res://Main.tscn") open_scenes=PackedStringArray("res://Main.tscn", "res://Player.tscn")
current_scene="res://Main.tscn" current_scene="res://Player.tscn"
center_split_offset=0 center_split_offset=0
selected_default_debugger_tab_idx=0 selected_default_debugger_tab_idx=0
selected_main_editor_idx=2 selected_main_editor_idx=2
selected_bottom_panel_item=1 selected_bottom_panel_item=0
[ScriptEditor] [ScriptEditor]
open_scripts=["res://Camera2D.gd", "res://Player.gd"] open_scripts=["res://Main.gd", "res://Player.gd", "res://Tower.gd", "res://UI.gd"]
selected_script="res://Camera2D.gd" selected_script="res://Player.gd"
open_help=[] open_help=[]
script_split_offset=70 script_split_offset=70
list_split_offset=0 list_split_offset=0

View file

@ -1,14 +1,14 @@
ea4bc82a6ad023ab7ee23ee620429895 ea4bc82a6ad023ab7ee23ee620429895
::res://::1713198935 ::res://::1713445642
Camera2D.gd::GDScript::-1::1713198916::0::1::::<>Node<>:: default_env.tres::Environment::2706437284641004306::1713444527::0::1::::<><>::
default_env.tres::Environment::-1::1713198916::0::1::::<><>:: icon.png::CompressedTexture2D::224231617940432086::1713444527::1713444527::1::::<><>::
icon.png::CompressedTexture2D::224231617940432086::1713198714::1713198936::1::::<><>:: Main.gd::GDScript::-1::1713444527::0::1::::<>Node<>::
Main.tscn::PackedScene::-1::1713198916::0::1::::<><>::res://Camera2D.gd<>res://Player.tscn<>res://res/sprites.png<>res://UI.tscn Main.tscn::PackedScene::798496173305188108::1713445635::0::1::::<><>::res://Main.gd<>res://Player.tscn<>uid://1wdcw8ne7j5w::::res://res/sprites.png<>res://UI.tscn
Player.gd::GDScript::-1::1713198916::0::1::::<>Area2D<>:: Player.gd::GDScript::-1::1713444527::0::1::::<>Area2D<>::
Player.tscn::PackedScene::-1::1713198916::0::1::::<><>::res://Player.gd<>res://res/sprites.png Player.tscn::PackedScene::-1::1713444527::0::1::::<><>::res://Player.gd<>res://res/sprites.png
Tower.gd::GDScript::-1::1713198916::0::1::::<>Node2D<>:: Tower.gd::GDScript::-1::1713444527::0::1::::<>Node2D<>::
Tower.tscn::PackedScene::-1::1713198916::0::1::::<><>::res://Tower.gd<>res://res/sprites.png Tower.tscn::PackedScene::-1::1713444527::0::1::::<><>::res://Tower.gd<>res://res/sprites.png
UI.gd::GDScript::-1::1713198916::0::1::::<>CanvasLayer<>:: UI.gd::GDScript::-1::1713444527::0::1::::<>CanvasLayer<>::
UI.tscn::PackedScene::-1::1713198916::0::1::::<><>::res://UI.gd UI.tscn::PackedScene::-1::1713444527::0::1::::<><>::res://UI.gd
::res://res/::1713198714 ::res://res/::1713444527
sprites.png::CompressedTexture2D::1870513174833790166::1713198714::1713198936::1::::<><>:: sprites.png::CompressedTexture2D::1870513174833790166::1713444527::1713444527::1::::<><>::

View file

@ -1,4 +1,6 @@
res://default_env.tres res://default_env.tres
res://Main.tscn res://Main.tscn
res://Camera2D.gd res://Player.tscn
res://Player.gd res://Player.gd
res://Tower.gd
res://Main.gd

View file

@ -1,6 +1,6 @@
[editor_metadata] [editor_metadata]
executable_path="C:/Users/Gebruiker/Desktop/Godot_v4.2.1-stable_win64.exe/Godot_v4.2.1-stable_win64.exe" executable_path="/home/vincent/Documents/programming/godot/Godot_v4.2.1-stable_linux.x86_64"
[debug_options] [debug_options]
@ -9,8 +9,8 @@ run_reload_scripts=true
[recent_files] [recent_files]
scenes=["res://Main.tscn"] scenes=["res://Player.tscn", "res://Main.tscn"]
scripts=["res://Player.gd", "res://Camera2D.gd"] scripts=["res://UI.gd", "res://Main.gd", "res://Tower.gd", "res://Player.gd", "res://Camera2D.gd"]
[linked_properties] [linked_properties]
@ -18,3 +18,4 @@ Node2D:scale=true
Sprite2D:scale=true Sprite2D:scale=true
ParallaxLayer:motion_scale=true ParallaxLayer:motion_scale=true
ParallaxLayer:scale=true ParallaxLayer:scale=true
Area2D:scale=true

View file

@ -1,26 +1,54 @@
[res://Camera2D.gd]
state={
"bookmarks": PackedInt32Array(),
"breakpoints": PackedInt32Array(),
"column": 12,
"folded_lines": Array[int]([]),
"h_scroll_position": 0,
"row": 41,
"scroll_position": 0.0,
"selection": false,
"syntax_highlighter": "GDScript"
}
[res://Player.gd] [res://Player.gd]
state={ state={
"bookmarks": PackedInt32Array(), "bookmarks": PackedInt32Array(),
"breakpoints": PackedInt32Array(), "breakpoints": PackedInt32Array(),
"column": 46, "column": 1,
"folded_lines": Array[int]([]), "folded_lines": Array[int]([]),
"h_scroll_position": 0, "h_scroll_position": 0,
"row": 8, "row": 68,
"scroll_position": 25.0,
"selection": false,
"syntax_highlighter": "GDScript"
}
[res://Tower.gd]
state={
"bookmarks": PackedInt32Array(),
"breakpoints": PackedInt32Array(),
"column": 0,
"folded_lines": Array[int]([]),
"h_scroll_position": 0,
"row": 16,
"scroll_position": 0.0,
"selection": false,
"syntax_highlighter": "GDScript"
}
[res://Main.gd]
state={
"bookmarks": PackedInt32Array(),
"breakpoints": PackedInt32Array(),
"column": 0,
"folded_lines": Array[int]([]),
"h_scroll_position": 0,
"row": 25,
"scroll_position": 0.0,
"selection": false,
"syntax_highlighter": "GDScript"
}
[res://UI.gd]
state={
"bookmarks": PackedInt32Array(),
"breakpoints": PackedInt32Array(),
"column": 0,
"folded_lines": Array[int]([]),
"h_scroll_position": 0,
"row": 19,
"scroll_position": 0.0, "scroll_position": 0.0,
"selection": false, "selection": false,
"syntax_highlighter": "GDScript" "syntax_highlighter": "GDScript"

Binary file not shown.

View file

@ -1,17 +1,16 @@
extends Node extends Node
@export var Tower = preload("res://Tower.tscn");
@onready var screen_size = Vector2(ProjectSettings.get("display/window/size/viewport_width"), ProjectSettings.get("display/window/size/viewport_height")) @onready var screen_size = Vector2(ProjectSettings.get("display/window/size/viewport_width"), ProjectSettings.get("display/window/size/viewport_height"))
@export var tower_density = 250 # Tower variables
@export var Tower = preload("res://Tower.tscn"); # Tower scene
signal spawn @export var tower_density = 250 # How often a tower should be added
var last_built = 300 # Helper variable to adhear the tower density
var last_built = 300
var towers = [] var towers = []
signal spawn # A tower is spawned in
# Build a single tower
func build_tower(): func build_tower():
var tower = Tower.instantiate() var tower = Tower.instantiate()
tower.connect("exit", Callable(self, "_on_Tower_exit")) tower.connect("exit", Callable(self, "_on_Tower_exit"))
@ -25,6 +24,7 @@ func build_tower():
add_child(tower) add_child(tower)
last_built += tower_density last_built += tower_density
# Build all towers
func build_all_towers(): func build_all_towers():
get_tree().call_group("tower", "queue_free") get_tree().call_group("tower", "queue_free")
last_built = 300 last_built = 300
@ -32,6 +32,7 @@ func build_all_towers():
build_tower() build_tower()
func _ready(): func _ready():
# Connect signals
var ui = get_node("UI") var ui = get_node("UI")
var player = get_node("Player") var player = get_node("Player")
player.connect("start", Callable(self, "build_all_towers")) player.connect("start", Callable(self, "build_all_towers"))

View file

@ -1,7 +1,7 @@
[gd_scene load_steps=6 format=3 uid="uid://lm2d6kimhksi"] [gd_scene load_steps=6 format=3 uid="uid://lm2d6kimhksi"]
[ext_resource type="Script" path="res://Camera2D.gd" id="1"] [ext_resource type="Script" path="res://Main.gd" id="1"]
[ext_resource type="PackedScene" path="res://Player.tscn" id="2"] [ext_resource type="PackedScene" uid="uid://bkppr8vsso41l" path="res://Player.tscn" id="2"]
[ext_resource type="Texture2D" uid="uid://1wdcw8ne7j5w" path="res://res/sprites.png" id="3"] [ext_resource type="Texture2D" uid="uid://1wdcw8ne7j5w" path="res://res/sprites.png" id="3"]
[ext_resource type="PackedScene" path="res://UI.tscn" id="4"] [ext_resource type="PackedScene" path="res://UI.tscn" id="4"]

View file

@ -25,9 +25,9 @@ func start_game():
# Called when the node enters the scene tree for the first time. # Called when the node enters the scene tree for the first time.
func _ready(): func _ready():
screen_size = get_viewport_rect().size screen_size = get_viewport_rect().size
# x_offset = get_global_transform().get_origin().x
func _process(delta): # Called every frame
func _process(_delta):
if Input.is_action_just_pressed("ui_accept"): if Input.is_action_just_pressed("ui_accept"):
if not playing: if not playing:
start_game() start_game()
@ -40,6 +40,7 @@ func _process(delta):
if not playing: if not playing:
return return
# Adjust the birds position
speed_y += speed_inc speed_y += speed_inc
position += Vector2(speed, speed_y * speed) position += Vector2(speed, speed_y * speed)
@ -49,17 +50,18 @@ func _process(delta):
emit_signal("passed") emit_signal("passed")
tower_locations.pop_front() tower_locations.pop_front()
# Rotate the bird
update_rotation() update_rotation()
if position.y > screen_size.y: if position.y > screen_size.y:
emit_signal("hit") emit_signal("hit")
print(position)
playing = false playing = false
func update_rotation(): func update_rotation():
rotation = atan(speed_y / speed) rotation = atan(speed_y / speed)
func _on_Player_body_entered(body): # Hit detection
func _on_Player_body_entered(_body):
playing = false playing = false
start_game() start_game()

View file

@ -1,22 +1,22 @@
[gd_scene load_steps=6 format=2] [gd_scene load_steps=7 format=3 uid="uid://bkppr8vsso41l"]
[ext_resource path="res://Player.gd" type="Script" id=1] [ext_resource type="Script" path="res://Player.gd" id="1"]
[ext_resource path="res://res/sprites.png" type="Texture2D" id=2] [ext_resource type="Texture2D" uid="uid://1wdcw8ne7j5w" path="res://res/sprites.png" id="2"]
[sub_resource type="AtlasTexture" id=1] [sub_resource type="AtlasTexture" id="1"]
atlas = ExtResource( 2 ) atlas = ExtResource("2")
region = Rect2(-4, 488, 84, 16) region = Rect2(-4, 488, 84, 16)
[sub_resource type="Animation" id=2] [sub_resource type="Animation" id="2"]
length = 0.8 length = 0.8
loop = true loop_mode = 1
step = 0.2 step = 0.2
tracks/0/type = "value" tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Node2D/Sprite2D:frame") tracks/0/path = NodePath("Node2D/Sprite2D:frame")
tracks/0/interp = 1 tracks/0/interp = 1
tracks/0/loop_wrap = true tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = { tracks/0/keys = {
"times": PackedFloat32Array(0, 0.2, 0.4, 0.6), "times": PackedFloat32Array(0, 0.2, 0.4, 0.6),
"transitions": PackedFloat32Array(1, 1, 1, 1), "transitions": PackedFloat32Array(1, 1, 1, 1),
@ -24,37 +24,41 @@ tracks/0/keys = {
"values": [0, 1, 2, 1] "values": [0, 1, 2, 1]
} }
[sub_resource type="CapsuleShape2D" id=3] [sub_resource type="AnimationLibrary" id="AnimationLibrary_s168r"]
radius = 12.8837 _data = {
"fly": SubResource("2")
}
[sub_resource type="CapsuleShape2D" id="3"]
radius = 2.03742
height = 4.07484 height = 4.07484
[node name="Area2D" type="Area2D"] [node name="Area2D" type="Area2D"]
position = Vector2(9.25964, 6.09653) position = Vector2(9.25964, 6.09653)
scale = Vector2(1, 1.02134) scale = Vector2(1, 1.02134)
script = ExtResource( 1 ) script = ExtResource("1")
__meta__ = {
"_edit_group_": true
}
[node name="Node2D" type="Node2D" parent="."] [node name="Node2D" type="Node2D" parent="."]
[node name="Sprite2D" type="Sprite2D" parent="Node2D"] [node name="Sprite2D" type="Sprite2D" parent="Node2D"]
position = Vector2(-3.46641, -2.14102) position = Vector2(-3.46641, -2.14102)
scale = Vector2(2.14565, 2.14019) scale = Vector2(2.14565, 2.14019)
texture = SubResource( 1 ) texture = SubResource("1")
hframes = 3 hframes = 3
[node name="AnimationPlayer" type="AnimationPlayer" parent="Node2D"] [node name="AnimationPlayer" type="AnimationPlayer" parent="Node2D"]
root_node = NodePath("../..") root_node = NodePath("../..")
libraries = {
"": SubResource("AnimationLibrary_s168r")
}
autoplay = "fly" autoplay = "fly"
anims/fly = SubResource( 2 )
[node name="CollisionShape2D" type="CollisionShape2D" parent="."] [node name="CollisionShape2D" type="CollisionShape2D" parent="."]
rotation = 1.5708 rotation = 1.5708
shape = SubResource( 3 ) shape = SubResource("3")
[node name="Camera2D" type="Camera2D" parent="."] [node name="Camera2D" type="Camera2D" parent="."]
current = true
limit_top = 0 limit_top = 0
limit_bottom = 0 limit_bottom = 0
[connection signal="body_entered" from="." to="." method="_on_Player_body_entered"] [connection signal="body_entered" from="." to="." method="_on_Player_body_entered"]

41
README.md Normal file
View file

@ -0,0 +1,41 @@
# Flappy Bird
This repository contains an example solution for the [Flappy Bird game](https://git.zeus.gent/godot-introduction/getting-started/src/branch/master/flappy-bird).
It's important to note that this is not the only possible solution; yours might differ.
Now that you have starting solution, it's time to add some new and exciting features!
However ,before you start adding new features, it might be a good idea to fix all lingering bugs.
## Bugs
It's a good exercise to try and find a solution on your own; however, if you're really stuck we've added some pointers in the `bugs_spoilers.md` file.
1. It's possible to jump above the pipes. By spamming SPACE you'll never die.
2. When you die because of hitting a pipe, the bird gets reset to the starting position. It would be nicer if it had the same type of ending screen as when you hit the gound, which is the last frame you see.
## New Features
We're using the same method as for the bugs. You can find some pointers, if necessary, on where to begin in `features_spoilers.md`.
### Basic
1. Randomize the color of the pipes
2. Give the player 3 lives.
3. Increase the speed of the bird as the game progresses
### Medium
1. Enchance the UI
2. Add Coins
3. Randomize the size of the gap between pipes
### Advanced
1. Support multiple players
2. Powerups
3. Add a new type of pipe that moves
### Super Advanced
1. Monetize the game by adding a battlepass

View file

@ -8,7 +8,7 @@ func _ready():
add_to_group("tower") add_to_group("tower")
pass # Replace with function body. pass # Replace with function body.
func _on_Tower_body_entered(body): func _on_Tower_body_entered(_body):
emit_signal("hit") emit_signal("hit")
func _on_Visibility_screen_exited(): func _on_Visibility_screen_exited():

17
bugs_spoilers.md Normal file
View file

@ -0,0 +1,17 @@
# Bugs Solution Pointers
As bugs are unique to a certain solution, we'll make references to the code of the example solution. If your own solution has the same bug, the general fix idea can be reused.
## 1. No height limit
As you might have noticed, the game ends when you hit the floor. This gives you the opportunity to recycle some code!
You can look at how that is done in `Player.gd`.
## 2. Ending Screen
Again, you can look at already existing code. Try to look at the differences when you hit the floor and when you hit a tower.
You already know where the check is done if you hit the floor. Checking if a tower is hit is done in the same file.
Hint: Select the Area2D node and look at the attached signals

87
features_spoiler.md Normal file
View file

@ -0,0 +1,87 @@
# Features Solution Pointers
As we want to support self-made solutions as well as the example solution, we're going to give pointers on where to start to implement these features. However, we'll avoid hard references to the example solution.
## Basic
### 1. Randomize Pipe Color
You should have a Sprite node for your towers. Presumably, that node has the tower texture, which you can see on the right-hand side of your screen in the `Inspector` tab. Look for the `visibility` item underneath `CanvasItem`. In there, you'll find `Modulate`. You can use modulate to apply a filter over the texture. Select a color by clicking on the white color picker. Load up your game, and you'll see that the colors of all the pipes have changed!
Now it's up to you to convert it to code and to randomize the value for each pipe :)
### 2. 3 Lives
This feature consists of 3 subproblems:
1. How do you count the number of times the bird has died?
2. How do you show this information to the user?
3. What do you do when someone dies but still has a life left?
The first problem (1) is the easiest. You can use a variable to keep track of the number of lives.
The second problem (2) is already a bit trickier. You'll have to add UI to display this information. You should already have a way to display the current and best score. You can add the number of lives to that scene.
The most difficult problem is the last one. There are multiple possible solutions, but we'll only go over one in detail: invincibility.
A solution is to make the user invincible for the next x seconds. We recommend using a Timer node. There's only one problem left: what if the bird died because they hit the ground or ceiling? Do you teleport them to the center of the screen? Do you restrict it so that they can't go out of the screen, or do you allow them to go out of the screen and deduct another life when the invincibility timer ends?
We'll let you decide; part of game-making is deciding which mechanics and game logic make the most sense for your project.
### 3. Speed Increase
This one sounds very easy, but multiplying the speed variable by a percentage every time `_process` is called isn't the right way. That particular function might get called more often on your PC than on mine. Try to think of a fair way to increase the speed at the same rate for everyone!
## Medium
### 1. UI
It's time to enhance the UI!
Do you want to go for an old-school UI or a more modern layout? It's all up to you!
You can make it as difficult as you like.
Some ideas are:
- Display icons for the amount of lives left.
- Add a startup screen.
- Support a toggleable night mode background (sprite is included).
- Use sprite numbers to show the current score.
### 2. Coins
Depending on your implementation of Flappy Bird, you might need to add a new scene.
Think about what nodes the coins should exist. It should have a sprite, and you should be able to detect any collisions. Therefore, we suggest using a `Sprite2D` and a `CollisionShape2D` inside an `Area2D`. Once you have your scene layout, it's time to think about the signals. You can get away with only one signal, `area_exited`. It's up to you if you want to connect to the signal from the `Coin` scene or the `Player` scene.
The last two steps are spawning in the coins and showing the data. We'll let solving those parts over to you.
### 3. Random Pipe Gaps
Depending on your implementation, this might be very easy or might prove to be quite difficult. We're going to assume you're using the same logic as the example implementation.
## Advanced
If you've reached this point, we're assuming that you're almost a Godot master. Unfortunately, this means the tips on how to achieve the following features will be almost nonexistent.
### 1. Multiplayer
As the author of this document has 0 experience with multiplayer, you're unfortunately on your own! But don't be afraid; I've heard it's easier than you would think...
### 2. Powerups
Be creative! You can make this as easy or as hard as you want. Some ideas are:
- Extra life.
- Speed boost + invincibility.
- Coin magnet.
- Ability to knock down pipes for a certain period.
### 3. Add a new type of pipe that moves
The biggest challenge might be deciding which node to use. Choose carefully between
- Area2D
- StaticBody2D
- CharacterBody2D
- RigidBody2D
This [part of the documentation](https://docs.godotengine.org/en/stable/tutorials/physics/physics_introduction.html) will help
## Super Advanced
### 1. Battlepass
Please don't.