diff --git a/instancing_starter/Ball.tscn b/instancing_starter/Ball.tscn
new file mode 100644
index 0000000..bd04b41
--- /dev/null
+++ b/instancing_starter/Ball.tscn
@@ -0,0 +1,20 @@
+[gd_scene load_steps=4 format=3 uid="uid://dm5jrnxmxnuof"]
+
+[ext_resource type="Texture2D" uid="uid://dv6rx85s1gy3i" path="res://assets/ballBlue_10.png" id="1"]
+
+[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_ehj7a"]
+bounce = 0.5
+
+[sub_resource type="CircleShape2D" id="2"]
+radius = 37.1452
+
+[node name="Ball" type="RigidBody2D"]
+physics_material_override = SubResource("PhysicsMaterial_ehj7a")
+gravity_scale = 10.0
+
+[node name="Sprite2D" type="Sprite2D" parent="."]
+scale = Vector2(0.5, 0.5)
+texture = ExtResource("1")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+shape = SubResource("2")
diff --git a/instancing_starter/Main.gd b/instancing_starter/Main.gd
new file mode 100644
index 0000000..b9d468c
--- /dev/null
+++ b/instancing_starter/Main.gd
@@ -0,0 +1,9 @@
+extends Node
+
+@export var Ball: PackedScene
+
+func _input(event):
+ if event.is_action_pressed("click"):
+ var new_ball = Ball.instantiate()
+ new_ball.position = get_viewport().get_mouse_position()
+ add_child(new_ball)
diff --git a/instancing_starter/Main.tscn b/instancing_starter/Main.tscn
new file mode 100644
index 0000000..63083de
--- /dev/null
+++ b/instancing_starter/Main.tscn
@@ -0,0 +1,151 @@
+[gd_scene load_steps=13 format=3 uid="uid://bc04jgq832nvc"]
+
+[ext_resource type="Script" path="res://Main.gd" id="1"]
+[ext_resource type="PackedScene" uid="uid://dm5jrnxmxnuof" path="res://Ball.tscn" id="2"]
+[ext_resource type="Texture2D" uid="uid://dkq1swbmmxmbq" path="res://assets/Bouncy Platform Long.png" id="3"]
+
+[sub_resource type="PhysicsMaterial" id="1"]
+bounce = 0.5
+
+[sub_resource type="CapsuleShape2D" id="2"]
+radius = 28.7617
+height = 309.37
+
+[sub_resource type="PhysicsMaterial" id="3"]
+bounce = 0.5
+
+[sub_resource type="PhysicsMaterial" id="4"]
+bounce = 0.5
+
+[sub_resource type="PhysicsMaterial" id="5"]
+bounce = 0.5
+
+[sub_resource type="PhysicsMaterial" id="6"]
+bounce = 0.5
+
+[sub_resource type="PhysicsMaterial" id="7"]
+bounce = 0.5
+
+[sub_resource type="PhysicsMaterial" id="8"]
+bounce = 0.5
+
+[sub_resource type="PhysicsMaterial" id="9"]
+bounce = 0.5
+
+[node name="Main" type="Node"]
+script = ExtResource("1")
+Ball = ExtResource("2")
+
+[node name="WallContainer" type="Node" parent="."]
+
+[node name="Wall2" type="StaticBody2D" parent="WallContainer"]
+position = Vector2(880.952, 495.157)
+rotation = -0.264907
+physics_material_override = SubResource("1")
+
+[node name="Sprite2D" type="Sprite2D" parent="WallContainer/Wall2"]
+texture = ExtResource("3")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="WallContainer/Wall2"]
+rotation = 1.5708
+shape = SubResource("2")
+
+[node name="Wall7" type="StaticBody2D" parent="WallContainer"]
+position = Vector2(462.444, 216.865)
+rotation = -1.02983
+physics_material_override = SubResource("3")
+
+[node name="Sprite2D" type="Sprite2D" parent="WallContainer/Wall7"]
+texture = ExtResource("3")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="WallContainer/Wall7"]
+rotation = 1.5708
+shape = SubResource("2")
+
+[node name="Wall8" type="StaticBody2D" parent="WallContainer"]
+position = Vector2(585.534, 221.147)
+rotation = 1.07416
+physics_material_override = SubResource("4")
+
+[node name="Sprite2D" type="Sprite2D" parent="WallContainer/Wall8"]
+texture = ExtResource("3")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="WallContainer/Wall8"]
+rotation = 1.5708
+shape = SubResource("2")
+
+[node name="Wall8" type="StaticBody2D" parent="WallContainer/Wall8"]
+position = Vector2(-351.915, 334.235)
+rotation = 1.07416
+physics_material_override = SubResource("4")
+
+[node name="Sprite2D" type="Sprite2D" parent="WallContainer/Wall8/Wall8"]
+texture = ExtResource("3")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="WallContainer/Wall8/Wall8"]
+rotation = 1.5708
+shape = SubResource("2")
+
+[node name="Wall4" type="StaticBody2D" parent="WallContainer"]
+position = Vector2(996.874, 342.897)
+rotation = -1.5708
+physics_material_override = SubResource("5")
+
+[node name="Sprite2D" type="Sprite2D" parent="WallContainer/Wall4"]
+texture = ExtResource("3")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="WallContainer/Wall4"]
+rotation = 1.5708
+shape = SubResource("2")
+
+[node name="Wall1" type="StaticBody2D" parent="WallContainer"]
+position = Vector2(148.047, 491.522)
+rotation = 0.315662
+physics_material_override = SubResource("6")
+
+[node name="Sprite2D" type="Sprite2D" parent="WallContainer/Wall1"]
+texture = ExtResource("3")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="WallContainer/Wall1"]
+rotation = 1.5708
+shape = SubResource("2")
+
+[node name="Wall5" type="StaticBody2D" parent="WallContainer"]
+position = Vector2(30.2087, 337.685)
+rotation = 1.5708
+physics_material_override = SubResource("7")
+
+[node name="Sprite2D" type="Sprite2D" parent="WallContainer/Wall5"]
+texture = ExtResource("3")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="WallContainer/Wall5"]
+rotation = 1.5708
+shape = SubResource("2")
+
+[node name="Wall3" type="StaticBody2D" parent="WallContainer"]
+position = Vector2(397.133, 570.384)
+physics_material_override = SubResource("8")
+
+[node name="Sprite2D" type="Sprite2D" parent="WallContainer/Wall3"]
+texture = ExtResource("3")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="WallContainer/Wall3"]
+rotation = 1.5708
+shape = SubResource("2")
+
+[node name="Wall6" type="StaticBody2D" parent="WallContainer"]
+position = Vector2(699.667, 571.226)
+physics_material_override = SubResource("9")
+
+[node name="Sprite2D" type="Sprite2D" parent="WallContainer/Wall6"]
+texture = ExtResource("3")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="WallContainer/Wall6"]
+rotation = 1.5708
+shape = SubResource("2")
+
+[node name="Ball" parent="." instance=ExtResource("2")]
+position = Vector2(427, 95)
+
+[node name="Ball2" parent="." instance=ExtResource("2")]
+position = Vector2(336, 115)
diff --git a/instancing_starter/README.md b/instancing_starter/README.md
new file mode 100644
index 0000000..9656d83
--- /dev/null
+++ b/instancing_starter/README.md
@@ -0,0 +1,5 @@
+# Instancing project starter
+
+Used by the "Step by step" tutorial:
+
+https://docs.godotengine.org/en/latest/getting_started/step_by_step/index.html
diff --git a/instancing_starter/assets/Bouncy Platform Long.png b/instancing_starter/assets/Bouncy Platform Long.png
new file mode 100644
index 0000000..aca8330
Binary files /dev/null and b/instancing_starter/assets/Bouncy Platform Long.png differ
diff --git a/instancing_starter/assets/Bouncy Platform Long.png.import b/instancing_starter/assets/Bouncy Platform Long.png.import
new file mode 100644
index 0000000..6c8f690
--- /dev/null
+++ b/instancing_starter/assets/Bouncy Platform Long.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dkq1swbmmxmbq"
+path="res://.godot/imported/Bouncy Platform Long.png-9b2628c180785c5ec622adb5c4ee7b17.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/Bouncy Platform Long.png"
+dest_files=["res://.godot/imported/Bouncy Platform Long.png-9b2628c180785c5ec622adb5c4ee7b17.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/instancing_starter/assets/Bouncy Platform Medium.png b/instancing_starter/assets/Bouncy Platform Medium.png
new file mode 100644
index 0000000..f256404
Binary files /dev/null and b/instancing_starter/assets/Bouncy Platform Medium.png differ
diff --git a/instancing_starter/assets/Bouncy Platform Medium.png.import b/instancing_starter/assets/Bouncy Platform Medium.png.import
new file mode 100644
index 0000000..03dcd2b
--- /dev/null
+++ b/instancing_starter/assets/Bouncy Platform Medium.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://blqm0e7gm75u3"
+path="res://.godot/imported/Bouncy Platform Medium.png-7896338a5f44da1e83084c154a3c4a9e.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/Bouncy Platform Medium.png"
+dest_files=["res://.godot/imported/Bouncy Platform Medium.png-7896338a5f44da1e83084c154a3c4a9e.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/instancing_starter/assets/ballBlue_10.png b/instancing_starter/assets/ballBlue_10.png
new file mode 100644
index 0000000..caa9c2b
Binary files /dev/null and b/instancing_starter/assets/ballBlue_10.png differ
diff --git a/instancing_starter/assets/ballBlue_10.png.import b/instancing_starter/assets/ballBlue_10.png.import
new file mode 100644
index 0000000..936661b
--- /dev/null
+++ b/instancing_starter/assets/ballBlue_10.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dv6rx85s1gy3i"
+path="res://.godot/imported/ballBlue_10.png-a6ac20f1eeff46860499a0f8c7bba4ff.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/ballBlue_10.png"
+dest_files=["res://.godot/imported/ballBlue_10.png-a6ac20f1eeff46860499a0f8c7bba4ff.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/instancing_starter/icon.svg b/instancing_starter/icon.svg
new file mode 100644
index 0000000..adc26df
--- /dev/null
+++ b/instancing_starter/icon.svg
@@ -0,0 +1 @@
+
diff --git a/instancing_starter/icon.svg.import b/instancing_starter/icon.svg.import
new file mode 100644
index 0000000..4d5fc7c
--- /dev/null
+++ b/instancing_starter/icon.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dlj0lug17ph0p"
+path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://icon.svg"
+dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/instancing_starter/project.godot b/instancing_starter/project.godot
new file mode 100644
index 0000000..f9d88e9
--- /dev/null
+++ b/instancing_starter/project.godot
@@ -0,0 +1,45 @@
+; Engine configuration file.
+; It's best edited using the editor UI and not directly,
+; since the parameters that go here are not all obvious.
+;
+; Format:
+; [section] ; section goes between []
+; param=value ; assign values to parameters
+
+config_version=5
+
+[application]
+
+config/name="instancing"
+run/main_scene="res://Main.tscn"
+config/features=PackedStringArray("4.3")
+config/icon="res://icon.svg"
+
+[importer_defaults]
+
+texture={
+"compress/hdr_mode": 0,
+"compress/lossy_quality": 0.7,
+"compress/mode": 0,
+"compress/normal_map": 0,
+"detect_3d": true,
+"flags/anisotropic": false,
+"flags/filter": false,
+"flags/mipmaps": false,
+"flags/repeat": 0,
+"flags/srgb": 2,
+"process/HDR_as_SRGB": false,
+"process/fix_alpha_border": true,
+"process/premult_alpha": false,
+"size_limit": 0,
+"stream": false,
+"svg/scale": 1.0
+}
+
+[input]
+
+click={
+"deadzone": 0.5,
+"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
+]
+}
diff --git a/my-first-project/.gitattributes b/my-first-project/.gitattributes
new file mode 100644
index 0000000..8ad74f7
--- /dev/null
+++ b/my-first-project/.gitattributes
@@ -0,0 +1,2 @@
+# Normalize EOL for all files that Git considers text files.
+* text=auto eol=lf
diff --git a/my-first-project/.gitignore b/my-first-project/.gitignore
new file mode 100644
index 0000000..0af181c
--- /dev/null
+++ b/my-first-project/.gitignore
@@ -0,0 +1,3 @@
+# Godot 4+ specific ignores
+.godot/
+/android/
diff --git a/my-first-project/icon_test/icon.svg b/my-first-project/icon_test/icon.svg
new file mode 100644
index 0000000..9d8b7fa
--- /dev/null
+++ b/my-first-project/icon_test/icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/my-first-project/icon_test/icon.svg.import b/my-first-project/icon_test/icon.svg.import
new file mode 100644
index 0000000..e9cfdf1
--- /dev/null
+++ b/my-first-project/icon_test/icon.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c6v86v04wkr45"
+path="res://.godot/imported/icon.svg-4fb04d907557270e983a13dd9087d07b.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://icon_test/icon.svg"
+dest_files=["res://.godot/imported/icon.svg-4fb04d907557270e983a13dd9087d07b.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/my-first-project/icon_test/icon_script.gd b/my-first-project/icon_test/icon_script.gd
new file mode 100644
index 0000000..dfe3221
--- /dev/null
+++ b/my-first-project/icon_test/icon_script.gd
@@ -0,0 +1,56 @@
+extends Sprite2D
+
+signal health_changed(old_value, new_value)
+signal health_depleted
+
+var speed: int = 40
+var max_speed: int = 500
+var angular_speed: float = PI * 0.15
+var motion: Vector2 = Vector2()
+var angular_motion: float = 0
+
+var react_to_input: bool = true
+
+var health: int = 100
+
+func _ready() -> void:
+ var timer: Timer = get_node("Timer")
+ timer.timeout.connect(_on_timer_timeout)
+
+func _on_timer_timeout() -> void:
+ modulate.a = 1 if modulate.a == 0.5 else 0.5
+
+func _process(delta: float) -> void:
+ angular_motion = signf(angular_motion) * abs(angular_motion) * 0.9
+ motion = motion * 0.9
+
+ if react_to_input:
+ var direction: float = 0
+ if Input.is_action_pressed("ui_left"):
+ direction -= 1
+ if Input.is_action_pressed("ui_right"):
+ direction += 1
+ angular_motion += angular_speed * direction * delta
+
+ if Input.is_action_pressed("ui_up"):
+ var velocity: Vector2 = Vector2.UP.rotated(rotation) * speed
+ motion += velocity * delta
+ if Input.is_action_pressed("ui_down"):
+ var velocity: Vector2 = Vector2.DOWN.rotated(rotation) * speed
+ motion += velocity * delta
+
+ if motion.length() > max_speed:
+ motion = motion.normalized() * max_speed
+
+ rotation += angular_motion
+ position += motion
+
+
+func _on_button_pressed() -> void:
+ react_to_input = not react_to_input
+ var new_health: int = health - 10
+ health_changed.emit(health, new_health)
+ health = new_health
+ if health <= 0:
+ health_depleted.emit()
+ queue_free()
diff --git a/my-first-project/icon_test/sprite_2d.tscn b/my-first-project/icon_test/sprite_2d.tscn
new file mode 100644
index 0000000..417d026
--- /dev/null
+++ b/my-first-project/icon_test/sprite_2d.tscn
@@ -0,0 +1,9 @@
+[gd_scene load_steps=3 format=3 uid="uid://blig1lf8unayu"]
+
+[ext_resource type="Texture2D" uid="uid://c6v86v04wkr45" path="res://icon_test/icon.svg" id="1_vtio7"]
+[ext_resource type="Script" path="res://icon_test/icon_script.gd" id="2_wc458"]
+
+[node name="Sprite2D" type="Sprite2D"]
+position = Vector2(562, 266)
+texture = ExtResource("1_vtio7")
+script = ExtResource("2_wc458")
diff --git a/my-first-project/main_scene.tscn b/my-first-project/main_scene.tscn
new file mode 100644
index 0000000..9f8316f
--- /dev/null
+++ b/my-first-project/main_scene.tscn
@@ -0,0 +1,20 @@
+[gd_scene load_steps=2 format=3 uid="uid://daea00yor4kbu"]
+
+[ext_resource type="PackedScene" uid="uid://blig1lf8unayu" path="res://icon_test/sprite_2d.tscn" id="1_yqsq6"]
+
+[node name="Node2D" type="Node2D"]
+
+[node name="Sprite2D" parent="." instance=ExtResource("1_yqsq6")]
+
+[node name="Timer" type="Timer" parent="Sprite2D"]
+wait_time = 0.5
+autostart = true
+
+[node name="Button" type="Button" parent="."]
+offset_left = 748.0
+offset_top = 524.0
+offset_right = 1094.0
+offset_bottom = 600.0
+text = "Click me to get free VBucks"
+
+[connection signal="pressed" from="Button" to="Sprite2D" method="_on_button_pressed"]
diff --git a/my-first-project/project.godot b/my-first-project/project.godot
new file mode 100644
index 0000000..d279318
--- /dev/null
+++ b/my-first-project/project.godot
@@ -0,0 +1,16 @@
+; Engine configuration file.
+; It's best edited using the editor UI and not directly,
+; since the parameters that go here are not all obvious.
+;
+; Format:
+; [section] ; section goes between []
+; param=value ; assign values to parameters
+
+config_version=5
+
+[application]
+
+config/name="my-first-project"
+run/main_scene="res://main_scene.tscn"
+config/features=PackedStringArray("4.3", "Forward Plus")
+config/icon="res://icon_test/icon.svg"
diff --git a/tutorial-game/.gitattributes b/tutorial-game/.gitattributes
new file mode 100644
index 0000000..8ad74f7
--- /dev/null
+++ b/tutorial-game/.gitattributes
@@ -0,0 +1,2 @@
+# Normalize EOL for all files that Git considers text files.
+* text=auto eol=lf
diff --git a/tutorial-game/.gitignore b/tutorial-game/.gitignore
new file mode 100644
index 0000000..0af181c
--- /dev/null
+++ b/tutorial-game/.gitignore
@@ -0,0 +1,3 @@
+# Godot 4+ specific ignores
+.godot/
+/android/
diff --git a/tutorial-game/art/House In a Forest Loop.ogg b/tutorial-game/art/House In a Forest Loop.ogg
new file mode 100644
index 0000000..ece3640
Binary files /dev/null and b/tutorial-game/art/House In a Forest Loop.ogg differ
diff --git a/tutorial-game/art/House In a Forest Loop.ogg.import b/tutorial-game/art/House In a Forest Loop.ogg.import
new file mode 100644
index 0000000..7a8e821
--- /dev/null
+++ b/tutorial-game/art/House In a Forest Loop.ogg.import
@@ -0,0 +1,19 @@
+[remap]
+
+importer="oggvorbisstr"
+type="AudioStreamOggVorbis"
+uid="uid://dn8pr56ynvupw"
+path="res://.godot/imported/House In a Forest Loop.ogg-1a6a72ae843ad792b7039931227e8d50.oggvorbisstr"
+
+[deps]
+
+source_file="res://art/House In a Forest Loop.ogg"
+dest_files=["res://.godot/imported/House In a Forest Loop.ogg-1a6a72ae843ad792b7039931227e8d50.oggvorbisstr"]
+
+[params]
+
+loop=false
+loop_offset=0
+bpm=0
+beat_count=0
+bar_beats=4
diff --git a/tutorial-game/art/beam.png b/tutorial-game/art/beam.png
new file mode 100644
index 0000000..36f46fd
Binary files /dev/null and b/tutorial-game/art/beam.png differ
diff --git a/tutorial-game/art/beam.png.import b/tutorial-game/art/beam.png.import
new file mode 100644
index 0000000..318010e
--- /dev/null
+++ b/tutorial-game/art/beam.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://balvj8gbkw5fr"
+path="res://.godot/imported/beam.png-84cf46e1bc926f466474690eb358b1bc.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/beam.png"
+dest_files=["res://.godot/imported/beam.png-84cf46e1bc926f466474690eb358b1bc.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/tutorial-game/art/enemyFlyingAlt_1.png b/tutorial-game/art/enemyFlyingAlt_1.png
new file mode 100644
index 0000000..1ef132f
Binary files /dev/null and b/tutorial-game/art/enemyFlyingAlt_1.png differ
diff --git a/tutorial-game/art/enemyFlyingAlt_1.png.import b/tutorial-game/art/enemyFlyingAlt_1.png.import
new file mode 100644
index 0000000..19983e5
--- /dev/null
+++ b/tutorial-game/art/enemyFlyingAlt_1.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cb18usx4c7w2e"
+path="res://.godot/imported/enemyFlyingAlt_1.png-559f599b16c69b112c1b53f6332e9489.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/enemyFlyingAlt_1.png"
+dest_files=["res://.godot/imported/enemyFlyingAlt_1.png-559f599b16c69b112c1b53f6332e9489.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/tutorial-game/art/enemyFlyingAlt_2.png b/tutorial-game/art/enemyFlyingAlt_2.png
new file mode 100644
index 0000000..a50f70a
Binary files /dev/null and b/tutorial-game/art/enemyFlyingAlt_2.png differ
diff --git a/tutorial-game/art/enemyFlyingAlt_2.png.import b/tutorial-game/art/enemyFlyingAlt_2.png.import
new file mode 100644
index 0000000..7ec7484
--- /dev/null
+++ b/tutorial-game/art/enemyFlyingAlt_2.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cmk8ralale8i4"
+path="res://.godot/imported/enemyFlyingAlt_2.png-31dc7310eda6e1b721224f3cd932c076.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/enemyFlyingAlt_2.png"
+dest_files=["res://.godot/imported/enemyFlyingAlt_2.png-31dc7310eda6e1b721224f3cd932c076.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/tutorial-game/art/enemySwimming_1.png b/tutorial-game/art/enemySwimming_1.png
new file mode 100644
index 0000000..9a79114
Binary files /dev/null and b/tutorial-game/art/enemySwimming_1.png differ
diff --git a/tutorial-game/art/enemySwimming_1.png.import b/tutorial-game/art/enemySwimming_1.png.import
new file mode 100644
index 0000000..c005d56
--- /dev/null
+++ b/tutorial-game/art/enemySwimming_1.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://blmdxdbb1lxjq"
+path="res://.godot/imported/enemySwimming_1.png-dd0e11759dc3d624c8a704f6e98a3d80.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/enemySwimming_1.png"
+dest_files=["res://.godot/imported/enemySwimming_1.png-dd0e11759dc3d624c8a704f6e98a3d80.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/tutorial-game/art/enemySwimming_2.png b/tutorial-game/art/enemySwimming_2.png
new file mode 100644
index 0000000..f81aade
Binary files /dev/null and b/tutorial-game/art/enemySwimming_2.png differ
diff --git a/tutorial-game/art/enemySwimming_2.png.import b/tutorial-game/art/enemySwimming_2.png.import
new file mode 100644
index 0000000..56a1275
--- /dev/null
+++ b/tutorial-game/art/enemySwimming_2.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c5m4s7lkl2237"
+path="res://.godot/imported/enemySwimming_2.png-4c0cbc0732264c4ea3290340bd4a0a62.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/enemySwimming_2.png"
+dest_files=["res://.godot/imported/enemySwimming_2.png-4c0cbc0732264c4ea3290340bd4a0a62.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/tutorial-game/art/enemyWalking_1.png b/tutorial-game/art/enemyWalking_1.png
new file mode 100644
index 0000000..9ad7aab
Binary files /dev/null and b/tutorial-game/art/enemyWalking_1.png differ
diff --git a/tutorial-game/art/enemyWalking_1.png.import b/tutorial-game/art/enemyWalking_1.png.import
new file mode 100644
index 0000000..bac9ef0
--- /dev/null
+++ b/tutorial-game/art/enemyWalking_1.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://pldyiivp2t2n"
+path="res://.godot/imported/enemyWalking_1.png-5af6eedbe61b701677d490ffdc1e6471.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/enemyWalking_1.png"
+dest_files=["res://.godot/imported/enemyWalking_1.png-5af6eedbe61b701677d490ffdc1e6471.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/tutorial-game/art/enemyWalking_2.png b/tutorial-game/art/enemyWalking_2.png
new file mode 100644
index 0000000..c9e233e
Binary files /dev/null and b/tutorial-game/art/enemyWalking_2.png differ
diff --git a/tutorial-game/art/enemyWalking_2.png.import b/tutorial-game/art/enemyWalking_2.png.import
new file mode 100644
index 0000000..96f9d31
--- /dev/null
+++ b/tutorial-game/art/enemyWalking_2.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://kvg7h516ydee"
+path="res://.godot/imported/enemyWalking_2.png-67c480ed60c35e95f5acb0436246b935.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/enemyWalking_2.png"
+dest_files=["res://.godot/imported/enemyWalking_2.png-67c480ed60c35e95f5acb0436246b935.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/tutorial-game/art/gameover.wav b/tutorial-game/art/gameover.wav
new file mode 100644
index 0000000..77f865a
Binary files /dev/null and b/tutorial-game/art/gameover.wav differ
diff --git a/tutorial-game/art/gameover.wav.import b/tutorial-game/art/gameover.wav.import
new file mode 100644
index 0000000..e189d21
--- /dev/null
+++ b/tutorial-game/art/gameover.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://ceqgou1w6cvld"
+path="res://.godot/imported/gameover.wav-98c95c744b35280048c2bd093cf8a356.sample"
+
+[deps]
+
+source_file="res://art/gameover.wav"
+dest_files=["res://.godot/imported/gameover.wav-98c95c744b35280048c2bd093cf8a356.sample"]
+
+[params]
+
+force/8_bit=false
+force/mono=false
+force/max_rate=false
+force/max_rate_hz=44100
+edit/trim=false
+edit/normalize=false
+edit/loop_mode=0
+edit/loop_begin=0
+edit/loop_end=-1
+compress/mode=0
diff --git a/tutorial-game/art/particle_enemy_trail.png b/tutorial-game/art/particle_enemy_trail.png
new file mode 100644
index 0000000..a60919d
Binary files /dev/null and b/tutorial-game/art/particle_enemy_trail.png differ
diff --git a/tutorial-game/art/particle_enemy_trail.png.import b/tutorial-game/art/particle_enemy_trail.png.import
new file mode 100644
index 0000000..33c155b
--- /dev/null
+++ b/tutorial-game/art/particle_enemy_trail.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c87nnwec6q1i1"
+path="res://.godot/imported/particle_enemy_trail.png-771e06200b879359b131d7641ab54673.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/particle_enemy_trail.png"
+dest_files=["res://.godot/imported/particle_enemy_trail.png-771e06200b879359b131d7641ab54673.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/tutorial-game/art/playerGrey_up1.png b/tutorial-game/art/playerGrey_up1.png
new file mode 100644
index 0000000..5596dbc
Binary files /dev/null and b/tutorial-game/art/playerGrey_up1.png differ
diff --git a/tutorial-game/art/playerGrey_up1.png.import b/tutorial-game/art/playerGrey_up1.png.import
new file mode 100644
index 0000000..c837af7
--- /dev/null
+++ b/tutorial-game/art/playerGrey_up1.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://blnfsjbjj5027"
+path="res://.godot/imported/playerGrey_up1.png-6bd114d0a6beac91f48e3a7314d44564.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/playerGrey_up1.png"
+dest_files=["res://.godot/imported/playerGrey_up1.png-6bd114d0a6beac91f48e3a7314d44564.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/tutorial-game/art/playerGrey_up2.png b/tutorial-game/art/playerGrey_up2.png
new file mode 100644
index 0000000..07c63b8
Binary files /dev/null and b/tutorial-game/art/playerGrey_up2.png differ
diff --git a/tutorial-game/art/playerGrey_up2.png.import b/tutorial-game/art/playerGrey_up2.png.import
new file mode 100644
index 0000000..17a4520
--- /dev/null
+++ b/tutorial-game/art/playerGrey_up2.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b3snl7ecix3gw"
+path="res://.godot/imported/playerGrey_up2.png-d6aba85f5f2675ebc7045efa7552ee79.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/playerGrey_up2.png"
+dest_files=["res://.godot/imported/playerGrey_up2.png-d6aba85f5f2675ebc7045efa7552ee79.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/tutorial-game/art/playerGrey_walk1.png b/tutorial-game/art/playerGrey_walk1.png
new file mode 100644
index 0000000..6145887
Binary files /dev/null and b/tutorial-game/art/playerGrey_walk1.png differ
diff --git a/tutorial-game/art/playerGrey_walk1.png.import b/tutorial-game/art/playerGrey_walk1.png.import
new file mode 100644
index 0000000..a1edc7e
--- /dev/null
+++ b/tutorial-game/art/playerGrey_walk1.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bply3mtijfqjw"
+path="res://.godot/imported/playerGrey_walk1.png-c4773fe7a7bf85d7ab732eb4458c2742.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/playerGrey_walk1.png"
+dest_files=["res://.godot/imported/playerGrey_walk1.png-c4773fe7a7bf85d7ab732eb4458c2742.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/tutorial-game/art/playerGrey_walk2.png b/tutorial-game/art/playerGrey_walk2.png
new file mode 100644
index 0000000..3a699a9
Binary files /dev/null and b/tutorial-game/art/playerGrey_walk2.png differ
diff --git a/tutorial-game/art/playerGrey_walk2.png.import b/tutorial-game/art/playerGrey_walk2.png.import
new file mode 100644
index 0000000..a75bc49
--- /dev/null
+++ b/tutorial-game/art/playerGrey_walk2.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dx8sxdl3n3wiy"
+path="res://.godot/imported/playerGrey_walk2.png-34d2d916366100182d08037c51884043.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/playerGrey_walk2.png"
+dest_files=["res://.godot/imported/playerGrey_walk2.png-34d2d916366100182d08037c51884043.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/tutorial-game/fonts/FONTLOG.txt b/tutorial-game/fonts/FONTLOG.txt
new file mode 100644
index 0000000..826c7d8
--- /dev/null
+++ b/tutorial-game/fonts/FONTLOG.txt
@@ -0,0 +1,253 @@
+Please distribute this file along with the Xolonium fonts when possible.
+
+
+Source
+
+ Find the sourcefiles of Xolonium at
+
+
+
+Credits
+
+ Xolonium is created with FontForge ,
+ Inkscape , Python , and
+ FontTools .
+
+ It originated as a custom font for the open-source
+ game Xonotic . With many thanks to the
+ Xonotic community for your support.
+
+
+Supported OpenType features
+
+ case Provides case sensitive placement of punctuation,
+ brackets, and math symbols for uppercase text.
+ frac Replaces number/number sequences with diagonal fractions.
+ Numbers that touch a slash should not exceed 10 digits.
+ kern Provides kerning for Latin, Greek, and Cyrillic scripts.
+ locl Dutch: Replaces j with a stressed version if it follows í.
+ Sami: Replaces n-form Eng with the preferred N-form version.
+ Romanian and Moldovan: Replaces ŞşŢţ with the preferred ȘșȚț.
+ pnum Replaces monospaced digits with proportional versions.
+ sinf Replaces digits with scientific inferiors below the baseline.
+ subs Replaces digits with subscript versions on the baseline.
+ sups Replaces digits with superscript versions.
+ zero Replaces zero with a slashed version.
+
+
+Supported glyph sets
+
+ Adobe Latin 3
+ OpenType W1G
+ ISO 8859-1 Western European
+ ISO 8859-2 Central European
+ ISO 8859-3 South European
+ ISO 8859-4 North European
+ ISO 8859-5 Cyrillic
+ ISO 8859-7 Greek
+ ISO 8859-9 Turkish
+ ISO 8859-10 Nordic
+ ISO 8859-13 Baltic Rim
+ ISO 8859-14 Celtic
+ ISO 8859-15 Western European
+ ISO 8859-16 South-Eastern European
+
+
+Available glyphs
+
+ !"#$%&'()*+,-./0123456789:;<=>?
+ @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
+ `abcdefghijklmnopqrstuvwxyz{|}~
+
+ ¡¢£¤¥¦§¨©ª«¬ ®¯°±²³´µ¶·¸¹º»¼½¾¿
+ ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß
+ àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ
+ ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğ
+ ĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľ
+ ĿŀŁłŃńŅņŇňŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞş
+ ŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽž
+ ƒǺǻǼǽǾǿȘșȚțȷ
+
+ ˆˇˉ˘˙˚˛˜˝
+
+ ͺ;΄΅Ά·ΈΉΊΌΎΏΐ
+ ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰ
+ αβγδεζηθικλμνξοπρςστυφχψωϊϋόύώ
+
+ ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОП
+ РСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмноп
+ рстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџ
+ ѢѣѲѳѴѵҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩ
+ ҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽӀӁӂӇӈӋӌӏӐӑӒӓ
+ ӔӕӖӗӘәӜӝӞӟӠӡӢӣӤӥӦӧӨөӮӯӰӱӲӳӴӵӶӷӸӹ
+ Ԥԥ
+
+ ḂḃḊḋḞḟṀṁṖṗṠṡṪṫẀẁẂẃẄẅẞỲỳ
+
+ ‒–—―‘’‚‛“”„‟†‡•…‰′″‹›‽‾⁄
+ ⁰⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎
+ ₤₦₩₫€₯₱₹₺₽₿
+ ℅ℓ№℗™Ω℮
+ ⅛⅜⅝⅞
+ ←↑→↓
+ ∂∆∏∑−∕∙√∞∟∫≈≠≤≥
+ ⌖
+ ■▬▮▰▲▶▼◀◆◊●◢◣◤◥
+ ☄★☠☢☣⚙⚛⚠⚡⛔
+ ❇❈❌❤❰❱❲❳
+ fffiflffiffl
+ 🌌🌍🌎🌏👽💣🔥🔫
+ 😁😃😄😆😇😈😉😊😎😐😒😕😘
+ 😛😝😞😟😠😣😭😮😲😴😵
+ 🚀
+
+
+Debugging glyphs
+
+ U+EFFD Font version
+ U+F000 Font hinting indicator
+
+
+Changelog
+
+ Xolonium 4.1 2016-11-22 Severin Meyer
+ Reverted frac OpenType feature to a more stable implementation
+
+ Xolonium 4.0 2016-10-08 Severin Meyer
+ Decreased width of most glyphs
+ Thinner vertical stems in Xolonium-Regular
+ Thicker horizontal stems in Xolonium-Bold
+ Revised diagonal stems
+ Lowered middle bars
+ Revised diacritical bars
+ Added glyphs:
+ ӏẞ₿
+ U+2007 U+2008 U+2009 U+200A U+202F
+ U+EFFD U+F000
+ Revised glyphs:
+ $&,JKQRXkwxy~¢¤ßǻ˜ζκλμξφЖУжћѴѵ∕₱₺₦₩€ℓ№≈ffffiffl
+ ❤🌍🌎🌏😁😄😇😈😉😊😘😭😮😴🚀
+ Removed uncommon glyphs:
+ ʼnſʼҌҍҎҏҾҿӃӄӇӈӚӛӪӫӬӭ
+ U+0312 U+0313 U+0326
+ Simplified OpenType features pnum, zero, and case
+ Removed OpenType feature dlig
+ Revised vertical metrics
+ Merged outlines of composite glyphs in otf version
+ Added ttf version with custom outlines and instructions
+ Added woff and woff2 version
+
+ Xolonium 3.1 2015-06-10 Severin Meyer
+ Added currency glyphs:
+ ₦₩₫₱₹₺₽
+ Revised glyph:
+ ₯
+ Relicensed public release under the SIL Open Font License 1.1
+
+ Xolonium 3.0 2015-05-04 Severin Meyer
+ Decreased width of glyphs
+ Decreased descender height
+ Increased height of super/subscript glyphs
+ Revised width of dashes, underscore, and overscore
+ Sharper bends with more circular proportions
+ Decreased stroke thickness of mathematical glyphs
+ Revised diacritical marks
+ Revised diacritical bars
+ Revised Cyrillic hooks
+ Revised glyphs:
+ GQRYjmuwßŊŒſƒǻfffiffiffl
+ ΞΨΩδζιξπςστυφω
+ ЉЄДЛУЭЯбдлэяєљђєћѢѣҨҩҼҽӃӄӘә
+ #$&'()*,/69?@[]{}~¡£¤¥§©®¿
+ ‹›₤€₯ℓ№℗℮←↑→↓∂∏∑∞≈▰☄❈❰❱❲❳😝
+ Raised vertical position of mathematical glyphs
+ Unified advance width of numeral and monetary glyphs
+ Unified advance width of mathematical glyphs
+ Revised bearings
+ Rewrote kern feature
+ Bolder Xolonium-Bold with improved proportions
+ Updated glyph names to conform to the AGLFN 1.7
+ Revised hints and PS Private Dictionary
+ Added glyphs:
+ ӶӷԤԥ
+ Added OpenType features:
+ case frac liga locl pnum sinf subs sups zero
+
+ Xolonium 2.4 2014-12-23 Severin Meyer
+ Added dingbats:
+ ⛔💣🔥
+ Revised size and design of emoticons
+ Revised dingbats:
+ ⌖☄☠☣⚙⚛⚠⚡❇❈🌌🌍🌎🌏🔫
+ Removed dingbat:
+ 💥
+
+ Xolonium 2.3 2014-08-14 Severin Meyer
+ Bugfixed ε and έ, thanks to bowzee for the feedback
+
+ Xolonium 2.2 2014-03-01 Severin Meyer
+ Added dingbats:
+ ⌖◆●❌💥
+ Revised dingbats:
+ •←↑→↓◊☄★☠☣⚙⚛⚠⚡❇❈❤🌌🌍🌎🌏👽🔫🚀
+ Removed dingbats:
+ ♻✪💡📡🔋🔧🔭
+
+ Xolonium 2.1 2013-10-20 Severin Meyer
+ Added dingbats:
+ ←↑→↓❰❱❲❳■▬▮▰▲▶▼◀◢◣◤◥
+ ☄★☠☢☣♻⚙⚛⚠⚡✪❇❈❤
+ 🌌🌍🌎🌏👽💡📡🔋🔧🔫🔭🚀
+ 😁😃😄😆😇😈😉😊😎😐😒😕
+ 😘😛😝😞😟😠😣😭😮😲😴😵
+
+ Xolonium 2.0.1 2013-07-12 Severin Meyer
+ Reorganised and simplified files
+
+ Xolonium 2.0 2012-08-11 Severin Meyer
+ Revised bends
+ Revised thickness of uppercase diagonal stems
+ Revised diacritical marks
+ Revised hints and PS Private Dictionary
+ Revised glyphs:
+ *1469@DPRly{}§©®¶ÐÞƒΘΞαεζνξνυЄЉЊ
+ ЏБЗЛУЧЪЫЬЭЯбзлчъыьэяєљњџ•€∂∙√∞∫≠
+ Completed glyph sets:
+ Adobe Latin 3
+ OpenType World Glyph Set 1 (W1G)
+ Ghostscript Standard (ghostscript-fonts-std-8.11)
+ Added OpenType kern feature
+ Added Xolonium-Bold
+
+ Xolonium 1.2 2011-02-12 Severin Meyer
+ Revised glyphs:
+ D·Ðı
+ Completed glyph sets:
+ ISO 8859-7 (Greek)
+ Unicode Latin Extended-A block
+ Added glyphs:
+ †‡•…‰⁄™∂∑−√∞≠≤≥
+
+ Xolonium 1.1 2011-01-17 Severin Meyer
+ Revised placement of cedilla and ogonek in accented glyphs
+ Revised glyphs:
+ ,;DKTjkvwxy¥§Ð˛€
+ Completed glyph sets:
+ ISO 8859-2 (Central European)
+ ISO 8859-3 (South European, Esperanto)
+ ISO 8859-4 (North European)
+ ISO 8859-5 (Cyrillic)
+ ISO 8859-9 (Turkish)
+ ISO 8859-10 (Nordic)
+ ISO 8859-13 (Baltic Rim)
+ ISO 8859-14 (Celtic)
+ ISO 8859-16 (South-Eastern European)
+ Added glyphs:
+ ȷʼ̒ ЀЍѐѝ‒–—‘’‚‛“”„‟‹›
+
+ Xolonium 1.0 2011-01-04 Severin Meyer
+ Completed glyph sets:
+ ISO 8859-1 (Western European)
+ ISO 8859-15 (Western European)
+ Added glyphs:
+ ĄĆĘŁŃŚŹŻąćęłńśźżıˆˇ˙˚˛˜
diff --git a/tutorial-game/fonts/LICENSE.txt b/tutorial-game/fonts/LICENSE.txt
new file mode 100644
index 0000000..cd6cd46
--- /dev/null
+++ b/tutorial-game/fonts/LICENSE.txt
@@ -0,0 +1,94 @@
+Copyright 2011-2016 Severin Meyer ,
+with Reserved Font Name Xolonium.
+
+This Font Software is licensed under the SIL Open Font License,
+Version 1.1. This license is copied below, and is also available
+with a FAQ at
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/tutorial-game/fonts/Xolonium-Regular.ttf b/tutorial-game/fonts/Xolonium-Regular.ttf
new file mode 100644
index 0000000..031d627
Binary files /dev/null and b/tutorial-game/fonts/Xolonium-Regular.ttf differ
diff --git a/tutorial-game/fonts/Xolonium-Regular.ttf.import b/tutorial-game/fonts/Xolonium-Regular.ttf.import
new file mode 100644
index 0000000..3d6dd45
--- /dev/null
+++ b/tutorial-game/fonts/Xolonium-Regular.ttf.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="font_data_dynamic"
+type="FontFile"
+uid="uid://dcbt7bfqcw4qq"
+path="res://.godot/imported/Xolonium-Regular.ttf-bc2981e3069cff4c34dd7c8e2bb73fba.fontdata"
+
+[deps]
+
+source_file="res://fonts/Xolonium-Regular.ttf"
+dest_files=["res://.godot/imported/Xolonium-Regular.ttf-bc2981e3069cff4c34dd7c8e2bb73fba.fontdata"]
+
+[params]
+
+Rendering=null
+antialiasing=1
+generate_mipmaps=false
+disable_embedded_bitmaps=true
+multichannel_signed_distance_field=false
+msdf_pixel_range=8
+msdf_size=48
+allow_system_fallback=true
+force_autohinter=false
+hinting=1
+subpixel_positioning=1
+oversampling=0.0
+Fallbacks=null
+fallbacks=[]
+Compress=null
+compress=true
+preload=[]
+language_support={}
+script_support={}
+opentype_features={}
diff --git a/tutorial-game/icon.svg b/tutorial-game/icon.svg
new file mode 100644
index 0000000..9d8b7fa
--- /dev/null
+++ b/tutorial-game/icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/tutorial-game/icon.svg.import b/tutorial-game/icon.svg.import
new file mode 100644
index 0000000..882deb1
--- /dev/null
+++ b/tutorial-game/icon.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bty8n6l6gmmk8"
+path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://icon.svg"
+dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/tutorial-game/project.godot b/tutorial-game/project.godot
new file mode 100644
index 0000000..6c034ec
--- /dev/null
+++ b/tutorial-game/project.godot
@@ -0,0 +1,63 @@
+; Engine configuration file.
+; It's best edited using the editor UI and not directly,
+; since the parameters that go here are not all obvious.
+;
+; Format:
+; [section] ; section goes between []
+; param=value ; assign values to parameters
+
+config_version=5
+
+[application]
+
+config/name="tutorial-game"
+run/main_scene="res://scenes/main.tscn"
+config/features=PackedStringArray("4.3", "Forward Plus")
+config/icon="res://icon.svg"
+
+[display]
+
+window/size/viewport_width=480
+window/size/viewport_height=720
+window/stretch/mode="canvas_items"
+
+[input]
+
+move_right={
+"deadzone": 0.5,
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
+, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":14,"pressure":0.0,"pressed":true,"script":null)
+, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null)
+]
+}
+move_left={
+"deadzone": 0.5,
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
+, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":13,"pressure":0.0,"pressed":true,"script":null)
+, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null)
+]
+}
+move_up={
+"deadzone": 0.5,
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
+, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":11,"pressure":0.0,"pressed":true,"script":null)
+, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null)
+]
+}
+move_down={
+"deadzone": 0.5,
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
+, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":12,"pressure":0.0,"pressed":true,"script":null)
+, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":1.0,"script":null)
+]
+}
+start_game={
+"deadzone": 0.5,
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null)
+]
+}
diff --git a/tutorial-game/scenes/HUD/hud.gd b/tutorial-game/scenes/HUD/hud.gd
new file mode 100644
index 0000000..37fe9dc
--- /dev/null
+++ b/tutorial-game/scenes/HUD/hud.gd
@@ -0,0 +1,33 @@
+extends CanvasLayer
+
+signal start_game
+var score: int = 0
+
+
+func show_timeout_message(text: String) -> void:
+ $Message.text = text
+ $Message.show()
+ $MessageTimer.start()
+
+func show_game_over():
+ show_timeout_message("Game Over")
+ await $MessageTimer.timeout
+
+ $Message.text = "Dodge the Mobs!"
+ $Message.show()
+ # Make a one-shot timer and wait for it to finish.
+ await get_tree().create_timer(1.0).timeout
+ $StartButton.show()
+
+
+func update_score(score):
+ $ScoreLabel.text = str(score)
+
+
+func _on_start_button_pressed():
+ $StartButton.hide()
+ start_game.emit()
+
+
+func _on_message_timer_timeout():
+ $Message.hide()
diff --git a/tutorial-game/scenes/HUD/hud.tscn b/tutorial-game/scenes/HUD/hud.tscn
new file mode 100644
index 0000000..18ce7e7
--- /dev/null
+++ b/tutorial-game/scenes/HUD/hud.tscn
@@ -0,0 +1,68 @@
+[gd_scene load_steps=5 format=3 uid="uid://d22yt81uknksv"]
+
+[ext_resource type="Script" path="res://scenes/HUD/hud.gd" id="1_2cj2s"]
+[ext_resource type="FontFile" uid="uid://dcbt7bfqcw4qq" path="res://fonts/Xolonium-Regular.ttf" id="1_f2ns1"]
+
+[sub_resource type="InputEventAction" id="InputEventAction_y4w8n"]
+action = &"start_game"
+
+[sub_resource type="Shortcut" id="Shortcut_f88su"]
+events = [SubResource("InputEventAction_y4w8n")]
+
+[node name="HUD" type="CanvasLayer"]
+script = ExtResource("1_2cj2s")
+
+[node name="ScoreLabel" type="Label" parent="."]
+anchors_preset = 5
+anchor_left = 0.5
+anchor_right = 0.5
+offset_left = -189.5
+offset_top = 29.97
+offset_right = 189.5
+offset_bottom = 107.97
+grow_horizontal = 2
+size_flags_horizontal = 4
+theme_override_fonts/font = ExtResource("1_f2ns1")
+theme_override_font_sizes/font_size = 64
+text = "0"
+horizontal_alignment = 1
+
+[node name="Message" type="Label" parent="."]
+anchors_preset = 8
+anchor_left = 0.5
+anchor_top = 0.5
+anchor_right = 0.5
+anchor_bottom = 0.5
+offset_left = -174.5
+offset_top = -79.5
+offset_right = 174.5
+offset_bottom = 79.5
+grow_horizontal = 2
+grow_vertical = 2
+size_flags_horizontal = 4
+theme_override_fonts/font = ExtResource("1_f2ns1")
+theme_override_font_sizes/font_size = 64
+horizontal_alignment = 1
+autowrap_mode = 2
+
+[node name="StartButton" type="Button" parent="."]
+anchors_preset = 7
+anchor_left = 0.5
+anchor_top = 1.0
+anchor_right = 0.5
+anchor_bottom = 1.0
+offset_left = -90.0
+offset_top = -140.0
+offset_right = 90.0
+offset_bottom = -82.0
+grow_horizontal = 2
+grow_vertical = 0
+shortcut = SubResource("Shortcut_f88su")
+text = "Start Game"
+
+[node name="MessageTimer" type="Timer" parent="."]
+wait_time = 2.0
+one_shot = true
+
+[connection signal="pressed" from="StartButton" to="." method="_on_start_button_pressed"]
+[connection signal="timeout" from="MessageTimer" to="." method="_on_message_timer_timeout"]
diff --git a/tutorial-game/scenes/main.gd b/tutorial-game/scenes/main.gd
new file mode 100644
index 0000000..2cf6865
--- /dev/null
+++ b/tutorial-game/scenes/main.gd
@@ -0,0 +1,74 @@
+extends Node2D
+
+@export var mob_scene: PackedScene
+
+var score: int = 0
+
+
+func _ready() -> void:
+ $Player.hide()
+
+
+func _process(delta: float) -> void:
+ pass
+
+
+func _on_game_start_timer_timeout() -> void:
+ $ScoreIncrementTimer.start()
+ $MobSpawnTimer.start()
+
+
+func new_game():
+ score = 0
+
+ $HUD.update_score(score)
+ $HUD.show_timeout_message("Get Ready")
+
+ $Music.play()
+
+ $Player.start($PlayerStartPosition.position)
+
+ $GameStartTimer.start()
+
+
+func game_over():
+ $Music.stop()
+ $DeathSound.play()
+
+ $ScoreIncrementTimer.stop()
+ $MobSpawnTimer.stop()
+
+ $Player.disable()
+
+ $HUD.show_game_over()
+
+ get_tree().call_group("mobs", "dissolve")
+
+
+func _on_player_hit() -> void:
+ game_over()
+
+
+func _on_mob_spawn_timer_timeout() -> void:
+ $Path2D/MobSpawnLocation.progress_ratio = randf()
+
+ var mob: RigidBody2D = mob_scene.instantiate()
+ mob.position = $Path2D/MobSpawnLocation.position
+ mob.rotation = $Path2D/MobSpawnLocation.rotation + PI / 2
+ mob.rotation += randf_range(-PI / 4, PI / 4)
+ # ensure the mob spawns off screen
+ mob.position += Vector2(-100, 0).rotated(mob.rotation)
+
+ var velocity: Vector2 = Vector2(randf_range(150.0, 250.0), 0.0)
+ mob.linear_velocity = velocity.rotated(mob.rotation)
+
+ add_child(mob)
+
+
+func _on_score_increment_timer_timeout() -> void:
+ score += 1
+ $HUD.update_score(score)
+
+
+func _on_hud_start_game() -> void:
+ new_game()
diff --git a/tutorial-game/scenes/main.tscn b/tutorial-game/scenes/main.tscn
new file mode 100644
index 0000000..5b912ef
--- /dev/null
+++ b/tutorial-game/scenes/main.tscn
@@ -0,0 +1,64 @@
+[gd_scene load_steps=9 format=3 uid="uid://lnu6rbfrobcj"]
+
+[ext_resource type="Script" path="res://scenes/main.gd" id="1_5ln4w"]
+[ext_resource type="PackedScene" uid="uid://b67uwwuoqaj8k" path="res://scenes/player/player.tscn" id="2_4emuo"]
+[ext_resource type="PackedScene" uid="uid://dft0l6kiatv8r" path="res://scenes/mob/mob.tscn" id="3_cb8gr"]
+[ext_resource type="PackedScene" uid="uid://d22yt81uknksv" path="res://scenes/HUD/hud.tscn" id="4_7sfdk"]
+[ext_resource type="AudioStream" uid="uid://dn8pr56ynvupw" path="res://art/House In a Forest Loop.ogg" id="5_uatno"]
+[ext_resource type="AudioStream" uid="uid://ceqgou1w6cvld" path="res://art/gameover.wav" id="6_4k0ar"]
+[ext_resource type="PackedScene" uid="uid://dllre5tl0hejs" path="res://scenes/particle/wind_trail.tscn" id="7_3ydc3"]
+
+[sub_resource type="Curve2D" id="Curve2D_tqiio"]
+_data = {
+"points": PackedVector2Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 480, 0, 0, 0, 0, 0, 480, 720, 0, 0, 0, 0, 0, 720, 0, 0, 0, 0, 0, 0)
+}
+point_count = 5
+
+[node name="Node2D" type="Node2D"]
+script = ExtResource("1_5ln4w")
+mob_scene = ExtResource("3_cb8gr")
+
+[node name="ColorRect" type="ColorRect" parent="."]
+z_index = -2
+offset_right = 480.0
+offset_bottom = 720.0
+color = Color(0.524642, 0.597468, 0.684174, 1)
+
+[node name="Player" parent="." instance=ExtResource("2_4emuo")]
+
+[node name="PlayerStartPosition" type="Marker2D" parent="."]
+position = Vector2(240, 450)
+
+[node name="MobSpawnTimer" type="Timer" parent="."]
+wait_time = 0.5
+
+[node name="ScoreIncrementTimer" type="Timer" parent="."]
+
+[node name="GameStartTimer" type="Timer" parent="."]
+wait_time = 2.0
+one_shot = true
+
+[node name="Path2D" type="Path2D" parent="."]
+curve = SubResource("Curve2D_tqiio")
+
+[node name="MobSpawnLocation" type="PathFollow2D" parent="Path2D"]
+
+[node name="HUD" parent="." instance=ExtResource("4_7sfdk")]
+
+[node name="Music" type="AudioStreamPlayer2D" parent="."]
+stream = ExtResource("5_uatno")
+volume_db = -5.622
+parameters/looping = true
+
+[node name="DeathSound" type="AudioStreamPlayer2D" parent="."]
+stream = ExtResource("6_4k0ar")
+
+[node name="WindTrail" parent="." instance=ExtResource("7_3ydc3")]
+position = Vector2(-503, 360)
+visibility_rect = Rect2(-1000, -1000, 2000, 2000)
+
+[connection signal="hit" from="Player" to="." method="_on_player_hit"]
+[connection signal="timeout" from="MobSpawnTimer" to="." method="_on_mob_spawn_timer_timeout"]
+[connection signal="timeout" from="ScoreIncrementTimer" to="." method="_on_score_increment_timer_timeout"]
+[connection signal="timeout" from="GameStartTimer" to="." method="_on_game_start_timer_timeout"]
+[connection signal="start_game" from="HUD" to="." method="_on_hud_start_game"]
diff --git a/tutorial-game/scenes/mob/blink.gdshader b/tutorial-game/scenes/mob/blink.gdshader
new file mode 100644
index 0000000..5aa2141
--- /dev/null
+++ b/tutorial-game/scenes/mob/blink.gdshader
@@ -0,0 +1,10 @@
+shader_type canvas_item;
+
+uniform vec4 blink_color: source_color;
+uniform float blink_intensity = 0;
+
+void fragment() {
+ vec4 color = texture(TEXTURE, UV);
+ color = mix(color, blink_color, blink_intensity * color.a);
+ COLOR = color;
+}
diff --git a/tutorial-game/scenes/mob/dissolve.tres b/tutorial-game/scenes/mob/dissolve.tres
new file mode 100644
index 0000000..4ca8730
--- /dev/null
+++ b/tutorial-game/scenes/mob/dissolve.tres
@@ -0,0 +1,212 @@
+[gd_resource type="VisualShader" load_steps=17 format=3 uid="uid://ddqioqbov85a2"]
+
+[sub_resource type="VisualShaderNodeMix" id="VisualShaderNodeMix_6xgmc"]
+output_port_for_preview = 0
+
+[sub_resource type="VisualShaderNodeMix" id="VisualShaderNodeMix_ledym"]
+output_port_for_preview = 0
+default_input_values = [0, Vector3(0, 0, 0), 1, Vector3(1, 1, 1), 2, Vector3(0.5, 0.5, 0.5)]
+op_type = 3
+
+[sub_resource type="VisualShaderNodeColorConstant" id="VisualShaderNodeColorConstant_mlnkv"]
+constant = Color(0.960814, 0.604173, 0.390603, 1)
+
+[sub_resource type="VisualShaderNodeFloatOp" id="VisualShaderNodeFloatOp_aol56"]
+default_input_values = [0, 0.0, 1, 0.07]
+operator = 1
+
+[sub_resource type="VisualShaderNodeIf" id="VisualShaderNodeIf_n1s11"]
+output_port_for_preview = 0
+
+[sub_resource type="VisualShaderNodeRemap" id="VisualShaderNodeRemap_x7l5e"]
+default_input_values = [1, 0.0, 2, 1.0, 3, 0.0, 4, 1.07]
+
+[sub_resource type="VisualShaderNodeFloatOp" id="VisualShaderNodeFloatOp_0qr2v"]
+default_input_values = [0, 0.0, 1, 1.0]
+
+[sub_resource type="VisualShaderNodeFloatParameter" id="VisualShaderNodeFloatParameter_7noaw"]
+output_port_for_preview = 0
+parameter_name = "DissolveEffectWidth"
+hint = 1
+max = 0.2
+default_value_enabled = true
+default_value = 0.08
+
+[sub_resource type="VisualShaderNodeTexture2DParameter" id="VisualShaderNodeTexture2DParameter_nfnwc"]
+parameter_name = "Noise"
+
+[sub_resource type="VisualShaderNodeTexture" id="VisualShaderNodeTexture_8olli"]
+output_port_for_preview = 0
+source = 5
+
+[sub_resource type="VisualShaderNodeIf" id="VisualShaderNodeIf_vaaem"]
+output_port_for_preview = 0
+
+[sub_resource type="VisualShaderNodeFloatConstant" id="VisualShaderNodeFloatConstant_kg0xq"]
+
+[sub_resource type="VisualShaderNodeFloatConstant" id="VisualShaderNodeFloatConstant_tslfp"]
+constant = 1.0
+
+[sub_resource type="VisualShaderNodeFloatParameter" id="VisualShaderNodeFloatParameter_bvbxm"]
+output_port_for_preview = 0
+parameter_name = "DissolveValue"
+hint = 1
+
+[sub_resource type="VisualShaderNodeInput" id="VisualShaderNodeInput_v6qcn"]
+input_name = "texture"
+
+[sub_resource type="VisualShaderNodeTexture" id="VisualShaderNodeTexture_u8gkl"]
+expanded_output_ports = [0]
+source = 5
+
+[resource]
+code = "shader_type canvas_item;
+render_mode blend_mix;
+
+uniform sampler2D Noise;
+uniform float DissolveValue : hint_range(0, 1);
+uniform float DissolveEffectWidth : hint_range(0, 0.20000000298023) = 0.07999999821186;
+
+
+
+void fragment() {
+// ColorConstant:12
+ vec4 n_out12p0 = vec4(0.960814, 0.604173, 0.390603, 1.000000);
+
+
+ vec4 n_out9p0;
+// Texture2D:9
+ n_out9p0 = texture(TEXTURE, UV);
+ float n_out9p4 = n_out9p0.a;
+
+
+ vec4 n_out3p0;
+// Texture2D:3
+ n_out3p0 = texture(Noise, UV);
+
+
+// FloatParameter:7
+ float n_out7p0 = DissolveValue;
+
+
+// FloatParameter:18
+ float n_out18p0 = DissolveEffectWidth;
+
+
+// FloatOp:17
+ float n_in17p1 = 1.00000;
+ float n_out17p0 = n_out18p0 + n_in17p1;
+
+
+ float n_out15p0;
+// Remap:15
+ float n_in15p1 = 0.00000;
+ float n_in15p2 = 1.00000;
+ float n_in15p3 = 0.00000;
+ {
+ float __input_range = n_in15p2 - n_in15p1;
+ float __output_range = n_out17p0 - n_in15p3;
+ n_out15p0 = n_in15p3 + __output_range * ((n_out7p0 - n_in15p1) / __input_range);
+ }
+
+
+// FloatOp:13
+ float n_out13p0 = n_out15p0 - n_out18p0;
+
+
+// FloatConstant:5
+ float n_out5p0 = 0.000000;
+
+
+// FloatConstant:6
+ float n_out6p0 = 1.000000;
+
+
+ vec3 n_out14p0;
+// If:14
+ float n_in14p2 = 0.00001;
+ vec3 n_in14p3 = vec3(0.00000, 0.00000, 0.00000);
+ if(abs(n_out3p0.x - n_out13p0) < n_in14p2)
+ {
+ n_out14p0 = n_in14p3;
+ }
+ else if(n_out3p0.x < n_out13p0)
+ {
+ n_out14p0 = vec3(n_out6p0);
+ }
+ else
+ {
+ n_out14p0 = vec3(n_out5p0);
+ }
+
+
+// Mix:11
+ vec3 n_out11p0 = mix(vec3(n_out12p0.xyz), vec3(n_out9p0.xyz), n_out14p0);
+
+
+ vec3 n_out4p0;
+// If:4
+ float n_in4p2 = 0.00001;
+ vec3 n_in4p3 = vec3(0.00000, 0.00000, 0.00000);
+ if(abs(n_out3p0.x - n_out7p0) < n_in4p2)
+ {
+ n_out4p0 = n_in4p3;
+ }
+ else if(n_out3p0.x < n_out7p0)
+ {
+ n_out4p0 = vec3(n_out6p0);
+ }
+ else
+ {
+ n_out4p0 = vec3(n_out5p0);
+ }
+
+
+// Mix:10
+ float n_in10p0 = 0.00000;
+ float n_out10p0 = mix(n_in10p0, n_out9p4, n_out4p0.x);
+
+
+// Output:0
+ COLOR.rgb = n_out11p0;
+ COLOR.a = n_out10p0;
+
+
+}
+"
+mode = 1
+flags/light_only = false
+nodes/fragment/0/position = Vector2(1160, -80)
+nodes/fragment/2/node = SubResource("VisualShaderNodeTexture2DParameter_nfnwc")
+nodes/fragment/2/position = Vector2(-1260, 120)
+nodes/fragment/3/node = SubResource("VisualShaderNodeTexture_8olli")
+nodes/fragment/3/position = Vector2(-880, 140)
+nodes/fragment/4/node = SubResource("VisualShaderNodeIf_vaaem")
+nodes/fragment/4/position = Vector2(80, 380)
+nodes/fragment/5/node = SubResource("VisualShaderNodeFloatConstant_kg0xq")
+nodes/fragment/5/position = Vector2(-540, 700)
+nodes/fragment/6/node = SubResource("VisualShaderNodeFloatConstant_tslfp")
+nodes/fragment/6/position = Vector2(-540, 800)
+nodes/fragment/7/node = SubResource("VisualShaderNodeFloatParameter_bvbxm")
+nodes/fragment/7/position = Vector2(-1300, 740)
+nodes/fragment/8/node = SubResource("VisualShaderNodeInput_v6qcn")
+nodes/fragment/8/position = Vector2(-680, -320)
+nodes/fragment/9/node = SubResource("VisualShaderNodeTexture_u8gkl")
+nodes/fragment/9/position = Vector2(-360, -420)
+nodes/fragment/10/node = SubResource("VisualShaderNodeMix_6xgmc")
+nodes/fragment/10/position = Vector2(800, -60)
+nodes/fragment/11/node = SubResource("VisualShaderNodeMix_ledym")
+nodes/fragment/11/position = Vector2(800, -440)
+nodes/fragment/12/node = SubResource("VisualShaderNodeColorConstant_mlnkv")
+nodes/fragment/12/position = Vector2(560, -460)
+nodes/fragment/13/node = SubResource("VisualShaderNodeFloatOp_aol56")
+nodes/fragment/13/position = Vector2(-600, 420)
+nodes/fragment/14/node = SubResource("VisualShaderNodeIf_n1s11")
+nodes/fragment/14/position = Vector2(80, -60)
+nodes/fragment/15/node = SubResource("VisualShaderNodeRemap_x7l5e")
+nodes/fragment/15/position = Vector2(-960, 560)
+nodes/fragment/17/node = SubResource("VisualShaderNodeFloatOp_0qr2v")
+nodes/fragment/17/position = Vector2(-1220, 560)
+nodes/fragment/18/node = SubResource("VisualShaderNodeFloatParameter_7noaw")
+nodes/fragment/18/position = Vector2(-1720, 500)
+nodes/fragment/connections = PackedInt32Array(2, 0, 3, 2, 3, 0, 4, 0, 5, 0, 4, 4, 6, 0, 4, 5, 8, 0, 9, 2, 4, 0, 10, 2, 10, 0, 0, 1, 9, 4, 10, 1, 9, 0, 11, 1, 12, 0, 11, 0, 11, 0, 0, 0, 3, 0, 14, 0, 13, 0, 14, 1, 5, 0, 14, 4, 6, 0, 14, 5, 14, 0, 11, 2, 7, 0, 15, 0, 15, 0, 13, 0, 7, 0, 4, 1, 17, 0, 15, 4, 18, 0, 13, 1, 18, 0, 17, 0)
diff --git a/tutorial-game/scenes/mob/enemy_output_particles.tscn b/tutorial-game/scenes/mob/enemy_output_particles.tscn
new file mode 100644
index 0000000..070e194
--- /dev/null
+++ b/tutorial-game/scenes/mob/enemy_output_particles.tscn
@@ -0,0 +1,29 @@
+[gd_scene load_steps=4 format=3 uid="uid://d0k2dheh1y3ff"]
+
+[ext_resource type="Texture2D" uid="uid://c87nnwec6q1i1" path="res://art/particle_enemy_trail.png" id="1_u0ygt"]
+
+[sub_resource type="Curve" id="Curve_r2a8g"]
+_data = [Vector2(0, 0.620991), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
+point_count = 2
+
+[sub_resource type="Gradient" id="Gradient_uu82e"]
+offsets = PackedFloat32Array(0, 0.808333, 1)
+colors = PackedColorArray(1, 1, 1, 0.568627, 1, 1, 1, 0.788235, 1, 1, 1, 0)
+
+[node name="CPUParticles2D" type="CPUParticles2D"]
+amount = 5
+lifetime = 0.7
+explosiveness = 0.38
+texture = ExtResource("1_u0ygt")
+spread = 9.0
+gravity = Vector2(0, 0)
+initial_velocity_min = 182.78
+initial_velocity_max = 207.98
+radial_accel_max = 100.0
+tangential_accel_min = -200.0
+tangential_accel_max = 200.0
+damping_min = 100.0
+damping_max = 300.0
+scale_amount_max = 1.5
+scale_amount_curve = SubResource("Curve_r2a8g")
+color_ramp = SubResource("Gradient_uu82e")
diff --git a/tutorial-game/scenes/mob/mob.gd b/tutorial-game/scenes/mob/mob.gd
new file mode 100644
index 0000000..7e3913c
--- /dev/null
+++ b/tutorial-game/scenes/mob/mob.gd
@@ -0,0 +1,40 @@
+extends RigidBody2D
+
+@export var blink_shader: Shader
+@export var dissolve_shader: Shader
+
+func _ready():
+ add_to_group("mobs")
+ var mob_types: PackedStringArray = $AnimatedSprite2D.sprite_frames.get_animation_names()
+ $AnimatedSprite2D.play(mob_types[randi() % mob_types.size()])
+ set_shader_dissolve_value(1.0)
+
+func _process(delta: float) -> void:
+ var spread: int = 25
+ $TrailParticles.position.y = randi() % (spread * 2) - spread
+
+func _on_visible_on_screen_notifier_2d_screen_exited() -> void:
+ await get_tree().create_timer(1.0).timeout
+ queue_free()
+
+func hit_player():
+ $DissolveParticles.restart()
+ $DissolveParticles.emitting = true
+
+func dissolve():
+ $AnimatedSprite2D.material.shader = blink_shader
+ var tween_1: Tween = get_tree().create_tween()
+ tween_1.tween_method(set_shader_blink_intensity, 1.0, 0.0, 0.5)
+ await get_tree().create_timer(0.5).timeout
+ $AnimatedSprite2D.material.shader = dissolve_shader
+ var tween_2: Tween = get_tree().create_tween()
+ tween_2.tween_method(set_shader_dissolve_value, 1.0, 0.0, 1)
+ await get_tree().create_timer(1).timeout
+ queue_free()
+
+func set_shader_blink_intensity(value: float) -> void:
+ $AnimatedSprite2D.material.set_shader_parameter("blink_intensity", value)
+ $AnimatedSprite2D.material.set_shader_parameter("blink_color", Color(1, 1, 1, 1))
+
+func set_shader_dissolve_value(value: float) -> void:
+ $AnimatedSprite2D.material.set_shader_parameter("DissolveValue", value)
diff --git a/tutorial-game/scenes/mob/mob.tscn b/tutorial-game/scenes/mob/mob.tscn
new file mode 100644
index 0000000..81b7133
--- /dev/null
+++ b/tutorial-game/scenes/mob/mob.tscn
@@ -0,0 +1,125 @@
+[gd_scene load_steps=19 format=3 uid="uid://dft0l6kiatv8r"]
+
+[ext_resource type="Script" path="res://scenes/mob/mob.gd" id="1_4r5kl"]
+[ext_resource type="Texture2D" uid="uid://cb18usx4c7w2e" path="res://art/enemyFlyingAlt_1.png" id="2_hsio7"]
+[ext_resource type="Shader" uid="uid://ddqioqbov85a2" path="res://scenes/mob/dissolve.tres" id="3_35rgv"]
+[ext_resource type="Texture2D" uid="uid://cmk8ralale8i4" path="res://art/enemyFlyingAlt_2.png" id="3_360e7"]
+[ext_resource type="Texture2D" uid="uid://blmdxdbb1lxjq" path="res://art/enemySwimming_1.png" id="4_4ch56"]
+[ext_resource type="Texture2D" uid="uid://c5m4s7lkl2237" path="res://art/enemySwimming_2.png" id="5_7p3vg"]
+[ext_resource type="Texture2D" uid="uid://pldyiivp2t2n" path="res://art/enemyWalking_1.png" id="6_q2v7h"]
+[ext_resource type="Texture2D" uid="uid://kvg7h516ydee" path="res://art/enemyWalking_2.png" id="7_oe468"]
+[ext_resource type="PackedScene" uid="uid://d0k2dheh1y3ff" path="res://scenes/mob/enemy_output_particles.tscn" id="8_hy8dr"]
+[ext_resource type="Shader" path="res://scenes/mob/blink.gdshader" id="9_ral8v"]
+
+[sub_resource type="FastNoiseLite" id="FastNoiseLite_gogsm"]
+
+[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_euyb8"]
+noise = SubResource("FastNoiseLite_gogsm")
+
+[sub_resource type="ShaderMaterial" id="ShaderMaterial_hjs33"]
+shader = ExtResource("3_35rgv")
+shader_parameter/DissolveValue = 1.0
+shader_parameter/DissolveEffectWidth = 0.2
+shader_parameter/Noise = SubResource("NoiseTexture2D_euyb8")
+
+[sub_resource type="SpriteFrames" id="SpriteFrames_6vbq8"]
+animations = [{
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("2_hsio7")
+}, {
+"duration": 1.0,
+"texture": ExtResource("3_360e7")
+}],
+"loop": true,
+"name": &"fly",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("4_4ch56")
+}, {
+"duration": 1.0,
+"texture": ExtResource("5_7p3vg")
+}],
+"loop": true,
+"name": &"swim",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("6_q2v7h")
+}, {
+"duration": 1.0,
+"texture": ExtResource("7_oe468")
+}],
+"loop": true,
+"name": &"walk",
+"speed": 5.0
+}]
+
+[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_o56se"]
+radius = 34.0
+height = 102.0
+
+[sub_resource type="Gradient" id="Gradient_0jlai"]
+offsets = PackedFloat32Array(0.882075, 1)
+colors = PackedColorArray(1, 1, 1, 1, 1, 1, 1, 0)
+
+[sub_resource type="GradientTexture1D" id="GradientTexture1D_885pt"]
+gradient = SubResource("Gradient_0jlai")
+
+[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_78ell"]
+lifetime_randomness = 1.0
+particle_flag_disable_z = true
+emission_shape = 1
+emission_sphere_radius = 50.0
+direction = Vector3(1, -1, 0)
+spread = 27.537
+initial_velocity_min = 200.0
+initial_velocity_max = 400.0
+gravity = Vector3(0, 500, 0)
+damping_min = 100.0
+damping_max = 400.0
+scale_min = 3.0
+scale_max = 5.0
+color_ramp = SubResource("GradientTexture1D_885pt")
+hue_variation_min = -0.9
+hue_variation_max = -0.7
+
+[node name="Mob" type="RigidBody2D" groups=["mobs"]]
+collision_mask = 0
+gravity_scale = 0.0
+script = ExtResource("1_4r5kl")
+blink_shader = ExtResource("9_ral8v")
+dissolve_shader = ExtResource("3_35rgv")
+metadata/_edit_group_ = true
+
+[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
+material = SubResource("ShaderMaterial_hjs33")
+scale = Vector2(0.75, 0.75)
+sprite_frames = SubResource("SpriteFrames_6vbq8")
+animation = &"walk"
+frame = 1
+frame_progress = 0.748916
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+rotation = 1.5708
+shape = SubResource("CapsuleShape2D_o56se")
+
+[node name="VisibleOnScreenNotifier2D" type="VisibleOnScreenNotifier2D" parent="."]
+
+[node name="TrailParticles" parent="." instance=ExtResource("8_hy8dr")]
+z_index = -1
+position = Vector2(-18.48, 0)
+rotation = 3.14159
+
+[node name="DissolveParticles" type="GPUParticles2D" parent="."]
+emitting = false
+amount = 40
+process_material = SubResource("ParticleProcessMaterial_78ell")
+lifetime = 0.6
+one_shot = true
+explosiveness = 0.69
+
+[connection signal="screen_exited" from="VisibleOnScreenNotifier2D" to="." method="_on_visible_on_screen_notifier_2d_screen_exited"]
diff --git a/tutorial-game/scenes/mob/mob_test_scene.tscn b/tutorial-game/scenes/mob/mob_test_scene.tscn
new file mode 100644
index 0000000..690561e
--- /dev/null
+++ b/tutorial-game/scenes/mob/mob_test_scene.tscn
@@ -0,0 +1,8 @@
+[gd_scene load_steps=2 format=3 uid="uid://csl66x55e6do8"]
+
+[ext_resource type="PackedScene" uid="uid://dft0l6kiatv8r" path="res://scenes/mob/mob.tscn" id="1_x7vm4"]
+
+[node name="MobTestScene" type="Node2D"]
+
+[node name="Mob" parent="." instance=ExtResource("1_x7vm4")]
+position = Vector2(248, 320)
diff --git a/tutorial-game/scenes/particle/Implosion.tscn b/tutorial-game/scenes/particle/Implosion.tscn
new file mode 100644
index 0000000..e0e9103
--- /dev/null
+++ b/tutorial-game/scenes/particle/Implosion.tscn
@@ -0,0 +1,24 @@
+[gd_scene load_steps=5 format=3 uid="uid://cj5s61jxwvux4"]
+
+[ext_resource type="Texture2D" uid="uid://cklr5rdvk4ptu" path="res://scenes/particle/Line1.png" id="1"]
+
+[sub_resource type="Gradient" id="1"]
+offsets = PackedFloat32Array(0, 0.158654, 0.860577, 0.908654, 1)
+colors = PackedColorArray(1, 1, 1, 0, 1, 1, 1, 1, 0.388235, 0.388235, 0.388235, 0.737255, 0.133333, 0.133333, 0.133333, 0.211765, 0, 0, 0, 0)
+
+[sub_resource type="GradientTexture1D" id="2"]
+gradient = SubResource("1")
+width = 100
+
+[sub_resource type="ParticleProcessMaterial" id="3"]
+emission_shape = 2
+emission_sphere_radius = 128.0
+gravity = Vector3(0, 0, 0)
+scale_min = 0.0
+scale_max = 8.62
+color_ramp = SubResource("2")
+
+[node name="Implosion" type="GPUParticles2D"]
+amount = 50
+process_material = SubResource("3")
+texture = ExtResource("1")
diff --git a/tutorial-game/scenes/particle/Line1.png b/tutorial-game/scenes/particle/Line1.png
new file mode 100644
index 0000000..94fdfab
Binary files /dev/null and b/tutorial-game/scenes/particle/Line1.png differ
diff --git a/tutorial-game/scenes/particle/Line1.png.import b/tutorial-game/scenes/particle/Line1.png.import
new file mode 100644
index 0000000..1863e34
--- /dev/null
+++ b/tutorial-game/scenes/particle/Line1.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cklr5rdvk4ptu"
+path="res://.godot/imported/Line1.png-012e7d81ae6db70c73357746e496d841.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://scenes/particle/Line1.png"
+dest_files=["res://.godot/imported/Line1.png-012e7d81ae6db70c73357746e496d841.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/tutorial-game/scenes/particle/my_implosion.tscn b/tutorial-game/scenes/particle/my_implosion.tscn
new file mode 100644
index 0000000..d144499
--- /dev/null
+++ b/tutorial-game/scenes/particle/my_implosion.tscn
@@ -0,0 +1,26 @@
+[gd_scene load_steps=4 format=3 uid="uid://cbrqb0c16241l"]
+
+[ext_resource type="Texture2D" uid="uid://balvj8gbkw5fr" path="res://art/beam.png" id="1_5jqot"]
+
+[sub_resource type="Gradient" id="Gradient_73n8c"]
+offsets = PackedFloat32Array(0, 0.0416667, 0.191667, 0.9, 1)
+colors = PackedColorArray(0, 0, 0, 0, 0.215686, 0.215686, 0.215686, 0, 1, 1, 1, 1, 0.360784, 0.360784, 0.360784, 0.686275, 1, 1, 1, 0)
+
+[sub_resource type="Curve" id="Curve_qkera"]
+min_value = -1.0
+_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
+point_count = 2
+
+[node name="CPUParticles2D" type="CPUParticles2D"]
+amount = 30
+lifetime = 0.7
+texture = ExtResource("1_5jqot")
+emission_shape = 2
+emission_sphere_radius = 128.0
+particle_flag_align_y = true
+gravity = Vector2(0, 0)
+radial_accel_min = -300.0
+color_ramp = SubResource("Gradient_73n8c")
+hue_variation_min = 0.89
+hue_variation_max = 0.99
+hue_variation_curve = SubResource("Curve_qkera")
diff --git a/tutorial-game/scenes/particle/wind_trail.tscn b/tutorial-game/scenes/particle/wind_trail.tscn
new file mode 100644
index 0000000..1b6625c
--- /dev/null
+++ b/tutorial-game/scenes/particle/wind_trail.tscn
@@ -0,0 +1,60 @@
+[gd_scene load_steps=10 format=3 uid="uid://dllre5tl0hejs"]
+
+[sub_resource type="Gradient" id="Gradient_t4ven"]
+colors = PackedColorArray(1, 1, 1, 0.262745, 1, 1, 1, 1)
+
+[sub_resource type="GradientTexture1D" id="GradientTexture1D_h623q"]
+gradient = SubResource("Gradient_t4ven")
+
+[sub_resource type="Gradient" id="Gradient_1cigd"]
+offsets = PackedFloat32Array(0, 0.0990566, 0.353774, 0.740566, 1)
+colors = PackedColorArray(1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
+
+[sub_resource type="GradientTexture1D" id="GradientTexture1D_lalpw"]
+gradient = SubResource("Gradient_1cigd")
+
+[sub_resource type="Curve" id="Curve_2tn03"]
+min_value = -200.0
+max_value = 200.0
+_data = [Vector2(0, 0), 0.0, 320.251, 0, 1, Vector2(0.116667, 37.3626), 0.0, 0.0, 0, 0, Vector2(0.2625, -32.967), -572.684, 0.0, 0, 0, Vector2(0.391667, 41.7582), 0.0, 0.0, 0, 0, Vector2(0.516667, -24.1758), 0.0, 0.0, 0, 0, Vector2(0.666667, 32.967), 0.0, 0.0, 0, 0, Vector2(0.8, -32.967), 0.0, 0.0, 0, 0, Vector2(0.9, 41.7582), 0.0, 0.0, 0, 0, Vector2(1, -32.967), 0.0, 0.0, 0, 0]
+point_count = 9
+
+[sub_resource type="CurveTexture" id="CurveTexture_2y6o4"]
+curve = SubResource("Curve_2tn03")
+
+[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_jyv0q"]
+particle_flag_disable_z = true
+emission_shape = 3
+emission_box_extents = Vector3(200, 400, 1)
+spread = 10.0
+initial_velocity_min = 257.25
+initial_velocity_max = 257.25
+gravity = Vector3(50, 15, 0)
+tangential_accel_min = -2.23517e-06
+tangential_accel_max = 6.0
+tangential_accel_curve = SubResource("CurveTexture_2y6o4")
+damping_max = 100.0
+scale_min = 2.0
+scale_max = 3.0
+color_ramp = SubResource("GradientTexture1D_lalpw")
+color_initial_ramp = SubResource("GradientTexture1D_h623q")
+
+[sub_resource type="Gradient" id="Gradient_x41e1"]
+offsets = PackedFloat32Array(0, 0.290984, 0.762295, 1)
+colors = PackedColorArray(0.74902, 0.921569, 0.878431, 0, 0.542767, 0.908156, 0.871601, 1, 0.690008, 0.918154, 0.744606, 1, 0.780392, 0.937255, 0.831373, 0)
+
+[sub_resource type="GradientTexture2D" id="GradientTexture2D_ooxhm"]
+gradient = SubResource("Gradient_x41e1")
+width = 1
+height = 32
+fill_to = Vector2(0, 1)
+
+[node name="WindTrail" type="GPUParticles2D"]
+amount = 15
+process_material = SubResource("ParticleProcessMaterial_jyv0q")
+texture = SubResource("GradientTexture2D_ooxhm")
+lifetime = 5.0
+preprocess = 2.0
+randomness = 1.0
+trail_enabled = true
+trail_lifetime = 0.6
diff --git a/tutorial-game/scenes/player/player.gd b/tutorial-game/scenes/player/player.gd
new file mode 100644
index 0000000..8b7db4f
--- /dev/null
+++ b/tutorial-game/scenes/player/player.gd
@@ -0,0 +1,74 @@
+extends Area2D
+
+signal hit
+@export var max_speed: float = 200
+@export var acceleration: float = 75
+@export var friction: float = 0.2
+
+var player_velocity: Vector2 = Vector2.ZERO
+var screen_size: Vector2
+var screen_area: Rect2
+
+
+func _ready() -> void:
+ screen_size = get_viewport_rect().size
+
+ var player_size: Vector2 = $CollisionShape2D.shape.get_rect().size
+
+ screen_area = Rect2(Vector2.ZERO, screen_size)
+ screen_area.position.x += player_size.x / 2
+ screen_area.position.y += player_size.y / 2
+ screen_area.size.x -= player_size.x / 2
+ screen_area.size.y -= player_size.y / 2
+
+
+func _process(delta: float) -> void:
+ var apply_velocity: Vector2 = Vector2.ZERO
+ if Input.is_action_pressed("move_right"):
+ apply_velocity += Vector2.RIGHT
+ if Input.is_action_pressed("move_left"):
+ apply_velocity += Vector2.LEFT
+ if Input.is_action_pressed("move_up"):
+ apply_velocity += Vector2.UP
+ if Input.is_action_pressed("move_down"):
+ apply_velocity += Vector2.DOWN
+ apply_velocity = apply_velocity.normalized()
+
+ player_velocity -= player_velocity * friction
+ player_velocity += apply_velocity * acceleration * delta
+ if player_velocity.length() > max_speed:
+ player_velocity = player_velocity.normalized() * max_speed
+
+ if player_velocity.length() < 0.1:
+ player_velocity = Vector2.ZERO
+
+ position += player_velocity
+ position = position.clamp(screen_area.position, screen_area.size)
+
+ if player_velocity.y > 0:
+ $AnimatedSprite2D.flip_v = true
+ elif player_velocity.y < 0:
+ $AnimatedSprite2D.flip_v = false
+
+ if player_velocity.length() > 0:
+ $AnimatedSprite2D.play()
+ else:
+ $AnimatedSprite2D.stop()
+
+
+func start(pos):
+ position = pos
+ show()
+ $CollisionShape2D.disabled = false
+
+
+func disable():
+ hide()
+ # Must be deferred as we can't change physics properties on a physics callback.
+ $CollisionShape2D.set_deferred("disabled", true)
+
+
+func _on_body_entered(body):
+ if body.is_in_group("mobs"):
+ hit.emit()
+ body.hit_player()
diff --git a/tutorial-game/scenes/player/player.tscn b/tutorial-game/scenes/player/player.tscn
new file mode 100644
index 0000000..b2c071a
--- /dev/null
+++ b/tutorial-game/scenes/player/player.tscn
@@ -0,0 +1,51 @@
+[gd_scene load_steps=8 format=3 uid="uid://b67uwwuoqaj8k"]
+
+[ext_resource type="Script" path="res://scenes/player/player.gd" id="1_dqs50"]
+[ext_resource type="Texture2D" uid="uid://blnfsjbjj5027" path="res://art/playerGrey_up1.png" id="1_p1pgv"]
+[ext_resource type="Texture2D" uid="uid://b3snl7ecix3gw" path="res://art/playerGrey_up2.png" id="2_yvyxf"]
+[ext_resource type="Texture2D" uid="uid://bply3mtijfqjw" path="res://art/playerGrey_walk1.png" id="3_qoqvm"]
+[ext_resource type="Texture2D" uid="uid://dx8sxdl3n3wiy" path="res://art/playerGrey_walk2.png" id="4_0jjgm"]
+
+[sub_resource type="SpriteFrames" id="SpriteFrames_by15q"]
+animations = [{
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("1_p1pgv")
+}, {
+"duration": 1.0,
+"texture": ExtResource("2_yvyxf")
+}],
+"loop": true,
+"name": &"up",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("3_qoqvm")
+}, {
+"duration": 1.0,
+"texture": ExtResource("4_0jjgm")
+}],
+"loop": true,
+"name": &"walk",
+"speed": 5.0
+}]
+
+[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_kmbv5"]
+radius = 27.0
+height = 68.0
+
+[node name="Player" type="Area2D"]
+position = Vector2(70, 76)
+script = ExtResource("1_dqs50")
+metadata/_edit_group_ = true
+
+[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
+scale = Vector2(0.5, 0.5)
+sprite_frames = SubResource("SpriteFrames_by15q")
+animation = &"up"
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+shape = SubResource("CapsuleShape2D_kmbv5")
+
+[connection signal="body_entered" from="." to="." method="_on_body_entered"]