├── 9-game-hud ├── hud.gd.uid ├── main.gd.uid ├── map.gd.uid ├── detector.gd.uid ├── main_menu.gd.uid ├── lively_light.gd.uid ├── map_transition.gd.uid ├── movable_object.gd.uid ├── player_character.gd.uid ├── tile_set.png ├── main_menu.gd ├── map_transition.gd ├── player_character.gd ├── hud.gd ├── tile_set.png.import ├── lively_light.gd ├── icon.svg ├── icon.svg.import ├── project.godot ├── lively_light.tscn ├── movable_object.gd ├── hud.tscn ├── movable_object.tscn ├── map.gd ├── detector.gd ├── detector.tscn ├── main_menu.tscn └── tile_set.tres ├── 10-extra-map-info ├── hud.gd.uid ├── main.gd.uid ├── map.gd.uid ├── detector.gd.uid ├── main_menu.gd.uid ├── lively_light.gd.uid ├── map_transition.gd.uid ├── movable_object.gd.uid ├── player_character.gd.uid ├── tile_set.png ├── main_menu.gd ├── map_transition.gd ├── tile_set.png.import ├── lively_light.gd ├── icon.svg ├── icon.svg.import ├── project.godot ├── lively_light.tscn ├── player_character.gd ├── movable_object.gd ├── movable_object.tscn ├── detector.gd ├── detector.tscn ├── hud.gd ├── main_menu.tscn ├── tile_set.tres ├── map.gd └── hud.tscn ├── 12-teleporters ├── hud.gd.uid ├── main.gd.uid ├── map.gd.uid ├── detector.gd.uid ├── lively_light.gd.uid ├── main_menu.gd.uid ├── map_transition.gd.uid ├── movable_object.gd.uid ├── player_character.gd.uid ├── interactive_object.gd.uid ├── teleporters │ ├── teleporter_line.gdshader.uid │ ├── teleporter_source.gd.uid │ ├── teleporter_destination.gd.uid │ ├── teleporter_source.png │ ├── teleporter_destination.png │ ├── teleporter_line.gdshader │ ├── teleporter_destination.gd │ ├── teleporter_source.png.import │ ├── teleporter_destination.png.import │ ├── teleporter_source.gd │ └── teleporter_destination.tscn ├── tile_set.png ├── interactive_object.gd ├── main_menu.gd ├── map_transition.gd ├── tile_set.png.import ├── lively_light.gd ├── icon.svg ├── icon.svg.import ├── project.godot ├── lively_light.tscn ├── movable_object.gd ├── player_character.gd ├── movable_object.tscn ├── detector.gd ├── detector.tscn ├── main_menu.tscn ├── tile_set.tres ├── hud.gd ├── hud.tscn └── map.gd ├── 8-saving-progress ├── main.gd.uid ├── map.gd.uid ├── detector.gd.uid ├── main_menu.gd.uid ├── lively_light.gd.uid ├── map_transition.gd.uid ├── movable_object.gd.uid ├── player_character.gd.uid ├── tile_set.png ├── main_menu.gd ├── map_transition.gd ├── map.gd ├── player_character.gd ├── tile_set.png.import ├── lively_light.gd ├── icon.svg ├── project.godot ├── icon.svg.import ├── lively_light.tscn ├── movable_object.gd ├── movable_object.tscn ├── detector.gd ├── detector.tscn ├── main_menu.tscn ├── tile_set.tres └── main.gd ├── 11-showing-best-scores ├── hud.gd.uid ├── main.gd.uid ├── map.gd.uid ├── detector.gd.uid ├── main_menu.gd.uid ├── lively_light.gd.uid ├── map_transition.gd.uid ├── movable_object.gd.uid ├── player_character.gd.uid ├── tile_set.png ├── main_menu.gd ├── map_transition.gd ├── tile_set.png.import ├── lively_light.gd ├── icon.svg ├── icon.svg.import ├── project.godot ├── player_character.gd ├── lively_light.tscn ├── movable_object.gd ├── movable_object.tscn ├── detector.gd ├── detector.tscn ├── main_menu.tscn ├── tile_set.tres ├── hud.gd ├── hud.tscn └── map.gd ├── 1-tile-map ├── tile_set.png ├── project.godot ├── tile_set.tres ├── tile_set.png.import ├── icon.svg └── icon.svg.import ├── 5-detectors ├── tile_set.png ├── player_character.gd ├── project.godot ├── tile_set.png.import ├── icon.svg ├── lively_light.gd ├── icon.svg.import ├── lively_light.tscn ├── movable_object.gd ├── detector.gd ├── movable_object.tscn ├── detector.tscn └── tile_set.tres ├── 6-multiple-maps ├── tile_set.png ├── player_character.gd ├── project.godot ├── tile_set.png.import ├── lively_light.gd ├── icon.svg ├── icon.svg.import ├── lively_light.tscn ├── movable_object.gd ├── map.gd ├── movable_object.tscn ├── detector.gd ├── detector.tscn └── tile_set.tres ├── 2-player-character ├── tile_set.png ├── player_character.gd ├── project.godot ├── tile_set.png.import ├── icon.svg ├── icon.svg.import └── tile_set.tres ├── 3-movable-objects ├── tile_set.png ├── player_character.gd ├── project.godot ├── tile_set.png.import ├── movable_object.tscn ├── icon.svg ├── icon.svg.import ├── movable_object.gd └── tile_set.tres ├── 4-light-and-shadow ├── tile_set.png ├── player_character.gd ├── project.godot ├── tile_set.png.import ├── lively_light.gd ├── icon.svg ├── icon.svg.import ├── lively_light.tscn ├── movable_object.gd ├── movable_object.tscn └── tile_set.tres ├── 7-map-transitions ├── tile_set.png ├── map_transition.gd ├── map.gd ├── player_character.gd ├── main.gd ├── tile_set.png.import ├── lively_light.gd ├── icon.svg ├── project.godot ├── icon.svg.import ├── lively_light.tscn ├── movable_object.gd ├── movable_object.tscn ├── detector.gd ├── detector.tscn └── tile_set.tres ├── .gitignore ├── LICENSE ├── CHANGELOG.md └── README.md /9-game-hud/hud.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cv3i66vvgu2ra 2 | -------------------------------------------------------------------------------- /9-game-hud/main.gd.uid: -------------------------------------------------------------------------------- 1 | uid://uwkiknpq3u2m 2 | -------------------------------------------------------------------------------- /9-game-hud/map.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bgk0njn8kou4j 2 | -------------------------------------------------------------------------------- /10-extra-map-info/hud.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cv3i66vvgu2ra 2 | -------------------------------------------------------------------------------- /10-extra-map-info/main.gd.uid: -------------------------------------------------------------------------------- 1 | uid://uwkiknpq3u2m 2 | -------------------------------------------------------------------------------- /10-extra-map-info/map.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bgk0njn8kou4j 2 | -------------------------------------------------------------------------------- /12-teleporters/hud.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cv3i66vvgu2ra 2 | -------------------------------------------------------------------------------- /12-teleporters/main.gd.uid: -------------------------------------------------------------------------------- 1 | uid://uwkiknpq3u2m 2 | -------------------------------------------------------------------------------- /12-teleporters/map.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bgk0njn8kou4j 2 | -------------------------------------------------------------------------------- /8-saving-progress/main.gd.uid: -------------------------------------------------------------------------------- 1 | uid://uwkiknpq3u2m 2 | -------------------------------------------------------------------------------- /8-saving-progress/map.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bgk0njn8kou4j 2 | -------------------------------------------------------------------------------- /9-game-hud/detector.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cva53g77vgubi 2 | -------------------------------------------------------------------------------- /9-game-hud/main_menu.gd.uid: -------------------------------------------------------------------------------- 1 | uid://b53qr6le0v04 2 | -------------------------------------------------------------------------------- /10-extra-map-info/detector.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cva53g77vgubi 2 | -------------------------------------------------------------------------------- /10-extra-map-info/main_menu.gd.uid: -------------------------------------------------------------------------------- 1 | uid://b53qr6le0v04 2 | -------------------------------------------------------------------------------- /11-showing-best-scores/hud.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cv3i66vvgu2ra 2 | -------------------------------------------------------------------------------- /11-showing-best-scores/main.gd.uid: -------------------------------------------------------------------------------- 1 | uid://uwkiknpq3u2m 2 | -------------------------------------------------------------------------------- /11-showing-best-scores/map.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bgk0njn8kou4j 2 | -------------------------------------------------------------------------------- /12-teleporters/detector.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cva53g77vgubi 2 | -------------------------------------------------------------------------------- /12-teleporters/lively_light.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cgoe12geigody 2 | -------------------------------------------------------------------------------- /12-teleporters/main_menu.gd.uid: -------------------------------------------------------------------------------- 1 | uid://b53qr6le0v04 2 | -------------------------------------------------------------------------------- /8-saving-progress/detector.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cva53g77vgubi 2 | -------------------------------------------------------------------------------- /8-saving-progress/main_menu.gd.uid: -------------------------------------------------------------------------------- 1 | uid://b53qr6le0v04 2 | -------------------------------------------------------------------------------- /9-game-hud/lively_light.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cgoe12geigody 2 | -------------------------------------------------------------------------------- /9-game-hud/map_transition.gd.uid: -------------------------------------------------------------------------------- 1 | uid://napvaw5g8duc 2 | -------------------------------------------------------------------------------- /9-game-hud/movable_object.gd.uid: -------------------------------------------------------------------------------- 1 | uid://on8oe1ncp37d 2 | -------------------------------------------------------------------------------- /9-game-hud/player_character.gd.uid: -------------------------------------------------------------------------------- 1 | uid://fpaopiu0nhaj 2 | -------------------------------------------------------------------------------- /10-extra-map-info/lively_light.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cgoe12geigody 2 | -------------------------------------------------------------------------------- /10-extra-map-info/map_transition.gd.uid: -------------------------------------------------------------------------------- 1 | uid://napvaw5g8duc 2 | -------------------------------------------------------------------------------- /10-extra-map-info/movable_object.gd.uid: -------------------------------------------------------------------------------- 1 | uid://on8oe1ncp37d 2 | -------------------------------------------------------------------------------- /11-showing-best-scores/detector.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cva53g77vgubi 2 | -------------------------------------------------------------------------------- /11-showing-best-scores/main_menu.gd.uid: -------------------------------------------------------------------------------- 1 | uid://b53qr6le0v04 2 | -------------------------------------------------------------------------------- /12-teleporters/map_transition.gd.uid: -------------------------------------------------------------------------------- 1 | uid://napvaw5g8duc 2 | -------------------------------------------------------------------------------- /12-teleporters/movable_object.gd.uid: -------------------------------------------------------------------------------- 1 | uid://on8oe1ncp37d 2 | -------------------------------------------------------------------------------- /12-teleporters/player_character.gd.uid: -------------------------------------------------------------------------------- 1 | uid://fpaopiu0nhaj 2 | -------------------------------------------------------------------------------- /8-saving-progress/lively_light.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cgoe12geigody 2 | -------------------------------------------------------------------------------- /8-saving-progress/map_transition.gd.uid: -------------------------------------------------------------------------------- 1 | uid://napvaw5g8duc 2 | -------------------------------------------------------------------------------- /8-saving-progress/movable_object.gd.uid: -------------------------------------------------------------------------------- 1 | uid://on8oe1ncp37d 2 | -------------------------------------------------------------------------------- /10-extra-map-info/player_character.gd.uid: -------------------------------------------------------------------------------- 1 | uid://fpaopiu0nhaj 2 | -------------------------------------------------------------------------------- /11-showing-best-scores/lively_light.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cgoe12geigody 2 | -------------------------------------------------------------------------------- /11-showing-best-scores/map_transition.gd.uid: -------------------------------------------------------------------------------- 1 | uid://napvaw5g8duc 2 | -------------------------------------------------------------------------------- /11-showing-best-scores/movable_object.gd.uid: -------------------------------------------------------------------------------- 1 | uid://on8oe1ncp37d 2 | -------------------------------------------------------------------------------- /12-teleporters/interactive_object.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bt8vrxi771m6m 2 | -------------------------------------------------------------------------------- /8-saving-progress/player_character.gd.uid: -------------------------------------------------------------------------------- 1 | uid://fpaopiu0nhaj 2 | -------------------------------------------------------------------------------- /11-showing-best-scores/player_character.gd.uid: -------------------------------------------------------------------------------- 1 | uid://fpaopiu0nhaj 2 | -------------------------------------------------------------------------------- /12-teleporters/teleporters/teleporter_line.gdshader.uid: -------------------------------------------------------------------------------- 1 | uid://u1jp4gvurjpa 2 | -------------------------------------------------------------------------------- /12-teleporters/teleporters/teleporter_source.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cdbb4xwhu0vne 2 | -------------------------------------------------------------------------------- /12-teleporters/teleporters/teleporter_destination.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dwu02xxnacil7 2 | -------------------------------------------------------------------------------- /1-tile-map/tile_set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasper-flick/godot-true-top-down-2d/HEAD/1-tile-map/tile_set.png -------------------------------------------------------------------------------- /9-game-hud/tile_set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasper-flick/godot-true-top-down-2d/HEAD/9-game-hud/tile_set.png -------------------------------------------------------------------------------- /5-detectors/tile_set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasper-flick/godot-true-top-down-2d/HEAD/5-detectors/tile_set.png -------------------------------------------------------------------------------- /12-teleporters/tile_set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasper-flick/godot-true-top-down-2d/HEAD/12-teleporters/tile_set.png -------------------------------------------------------------------------------- /6-multiple-maps/tile_set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasper-flick/godot-true-top-down-2d/HEAD/6-multiple-maps/tile_set.png -------------------------------------------------------------------------------- /10-extra-map-info/tile_set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasper-flick/godot-true-top-down-2d/HEAD/10-extra-map-info/tile_set.png -------------------------------------------------------------------------------- /2-player-character/tile_set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasper-flick/godot-true-top-down-2d/HEAD/2-player-character/tile_set.png -------------------------------------------------------------------------------- /3-movable-objects/tile_set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasper-flick/godot-true-top-down-2d/HEAD/3-movable-objects/tile_set.png -------------------------------------------------------------------------------- /4-light-and-shadow/tile_set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasper-flick/godot-true-top-down-2d/HEAD/4-light-and-shadow/tile_set.png -------------------------------------------------------------------------------- /7-map-transitions/tile_set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasper-flick/godot-true-top-down-2d/HEAD/7-map-transitions/tile_set.png -------------------------------------------------------------------------------- /8-saving-progress/tile_set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasper-flick/godot-true-top-down-2d/HEAD/8-saving-progress/tile_set.png -------------------------------------------------------------------------------- /11-showing-best-scores/tile_set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasper-flick/godot-true-top-down-2d/HEAD/11-showing-best-scores/tile_set.png -------------------------------------------------------------------------------- /12-teleporters/teleporters/teleporter_source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasper-flick/godot-true-top-down-2d/HEAD/12-teleporters/teleporters/teleporter_source.png -------------------------------------------------------------------------------- /12-teleporters/teleporters/teleporter_destination.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasper-flick/godot-true-top-down-2d/HEAD/12-teleporters/teleporters/teleporter_destination.png -------------------------------------------------------------------------------- /12-teleporters/teleporters/teleporter_line.gdshader: -------------------------------------------------------------------------------- 1 | shader_type canvas_item; 2 | render_mode unshaded; 3 | 4 | void fragment() { 5 | COLOR.a *= step(0.75, fract(UV.x * 0.25 - TIME * 0.5)); 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot 4+ specific ignores 2 | .godot/ 3 | 4 | # Godot-specific ignores 5 | .import/ 6 | export.cfg 7 | export_presets.cfg 8 | 9 | # Imported translations (automatically generated from CSV files) 10 | *.translation 11 | 12 | # Mono-specific ignores 13 | .mono/ 14 | data_*/ 15 | mono_crash.*.json 16 | 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /12-teleporters/interactive_object.gd: -------------------------------------------------------------------------------- 1 | class_name InteractiveObject 2 | extends CharacterBody2D 3 | 4 | @export var colission_shape: CollisionShape2D 5 | 6 | 7 | func get_current_rect() -> Rect2: 8 | var rect := colission_shape.shape.get_rect() 9 | rect.position += position 10 | return rect 11 | 12 | 13 | func displace(offset: Vector2) -> void: 14 | position += offset 15 | -------------------------------------------------------------------------------- /2-player-character/player_character.gd: -------------------------------------------------------------------------------- 1 | extends CharacterBody2D 2 | 3 | ## Speed in pixels per second. 4 | @export_range(0, 1000) var speed := 60 5 | 6 | 7 | func _physics_process(_delta: float) -> void: 8 | get_player_input() 9 | move_and_slide() 10 | 11 | 12 | func get_player_input() -> void: 13 | var vector := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") 14 | velocity = vector * speed 15 | -------------------------------------------------------------------------------- /9-game-hud/main_menu.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | @export var first_map: PackedScene 4 | @export var continue_button: Button 5 | 6 | 7 | func _ready() -> void: 8 | if not Main.can_continue(): 9 | continue_button.disabled = true 10 | 11 | func _on_new_game_button_pressed() -> void: 12 | Main.load_map(first_map.resource_path) 13 | 14 | 15 | func _on_continue_button_pressed() -> void: 16 | Main.load_continue_map() 17 | -------------------------------------------------------------------------------- /12-teleporters/main_menu.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | @export var first_map: PackedScene 4 | @export var continue_button: Button 5 | 6 | 7 | func _ready() -> void: 8 | if not Main.can_continue(): 9 | continue_button.disabled = true 10 | 11 | func _on_new_game_button_pressed() -> void: 12 | Main.load_map(first_map.resource_path) 13 | 14 | 15 | func _on_continue_button_pressed() -> void: 16 | Main.load_continue_map() 17 | -------------------------------------------------------------------------------- /10-extra-map-info/main_menu.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | @export var first_map: PackedScene 4 | @export var continue_button: Button 5 | 6 | 7 | func _ready() -> void: 8 | if not Main.can_continue(): 9 | continue_button.disabled = true 10 | 11 | func _on_new_game_button_pressed() -> void: 12 | Main.load_map(first_map.resource_path) 13 | 14 | 15 | func _on_continue_button_pressed() -> void: 16 | Main.load_continue_map() 17 | -------------------------------------------------------------------------------- /8-saving-progress/main_menu.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | @export var first_map: PackedScene 4 | @export var continue_button: Button 5 | 6 | 7 | func _ready() -> void: 8 | if not Main.can_continue(): 9 | continue_button.disabled = true 10 | 11 | func _on_new_game_button_pressed() -> void: 12 | Main.load_map(first_map.resource_path) 13 | 14 | 15 | func _on_continue_button_pressed() -> void: 16 | Main.load_continue_map() 17 | -------------------------------------------------------------------------------- /11-showing-best-scores/main_menu.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | @export var first_map: PackedScene 4 | @export var continue_button: Button 5 | 6 | 7 | func _ready() -> void: 8 | if not Main.can_continue(): 9 | continue_button.disabled = true 10 | 11 | func _on_new_game_button_pressed() -> void: 12 | Main.load_map(first_map.resource_path) 13 | 14 | 15 | func _on_continue_button_pressed() -> void: 16 | Main.load_continue_map() 17 | -------------------------------------------------------------------------------- /12-teleporters/teleporters/teleporter_destination.gd: -------------------------------------------------------------------------------- 1 | class_name TeleporterDestination 2 | extends Area2D 3 | 4 | signal validity_changed(valid: bool) 5 | 6 | @export var sprite: Sprite2D 7 | 8 | @export var particles: GPUParticles2D 9 | 10 | var object_count := 0 11 | 12 | 13 | func _on_body_entered(_body: Node2D) -> void: 14 | if object_count == 0: 15 | validity_changed.emit(false) 16 | object_count += 1 17 | 18 | 19 | func _on_body_exited(_body: Node2D) -> void: 20 | object_count -= 1 21 | if object_count == 0: 22 | validity_changed.emit(true) 23 | -------------------------------------------------------------------------------- /9-game-hud/map_transition.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | @export var animation_player : AnimationPlayer 4 | 5 | var root_window : Window 6 | 7 | 8 | func _ready() -> void: 9 | root_window = get_tree().root 10 | root_window.remove_child.call_deferred(self) 11 | 12 | 13 | func play_exit_map() -> void: 14 | root_window.add_child(self) 15 | animation_player.play("exit_map") 16 | await animation_player.animation_finished 17 | 18 | 19 | func play_enter_map() -> void: 20 | animation_player.play("enter_map") 21 | await animation_player.animation_finished 22 | root_window.remove_child(self) 23 | -------------------------------------------------------------------------------- /10-extra-map-info/map_transition.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | @export var animation_player : AnimationPlayer 4 | 5 | var root_window : Window 6 | 7 | 8 | func _ready() -> void: 9 | root_window = get_tree().root 10 | root_window.remove_child.call_deferred(self) 11 | 12 | 13 | func play_exit_map() -> void: 14 | root_window.add_child(self) 15 | animation_player.play("exit_map") 16 | await animation_player.animation_finished 17 | 18 | 19 | func play_enter_map() -> void: 20 | animation_player.play("enter_map") 21 | await animation_player.animation_finished 22 | root_window.remove_child(self) 23 | -------------------------------------------------------------------------------- /12-teleporters/map_transition.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | @export var animation_player : AnimationPlayer 4 | 5 | var root_window : Window 6 | 7 | 8 | func _ready() -> void: 9 | root_window = get_tree().root 10 | root_window.remove_child.call_deferred(self) 11 | 12 | 13 | func play_exit_map() -> void: 14 | root_window.add_child(self) 15 | animation_player.play("exit_map") 16 | await animation_player.animation_finished 17 | 18 | 19 | func play_enter_map() -> void: 20 | animation_player.play("enter_map") 21 | await animation_player.animation_finished 22 | root_window.remove_child(self) 23 | -------------------------------------------------------------------------------- /7-map-transitions/map_transition.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | @export var animation_player : AnimationPlayer 4 | 5 | var root_window : Window 6 | 7 | 8 | func _ready() -> void: 9 | root_window = get_tree().root 10 | root_window.remove_child.call_deferred(self) 11 | 12 | 13 | func play_exit_map() -> void: 14 | root_window.add_child(self) 15 | animation_player.play("exit_map") 16 | await animation_player.animation_finished 17 | 18 | 19 | func play_enter_map() -> void: 20 | animation_player.play("enter_map") 21 | await animation_player.animation_finished 22 | root_window.remove_child(self) 23 | -------------------------------------------------------------------------------- /8-saving-progress/map_transition.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | @export var animation_player : AnimationPlayer 4 | 5 | var root_window : Window 6 | 7 | 8 | func _ready() -> void: 9 | root_window = get_tree().root 10 | root_window.remove_child.call_deferred(self) 11 | 12 | 13 | func play_exit_map() -> void: 14 | root_window.add_child(self) 15 | animation_player.play("exit_map") 16 | await animation_player.animation_finished 17 | 18 | 19 | func play_enter_map() -> void: 20 | animation_player.play("enter_map") 21 | await animation_player.animation_finished 22 | root_window.remove_child(self) 23 | -------------------------------------------------------------------------------- /11-showing-best-scores/map_transition.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | @export var animation_player : AnimationPlayer 4 | 5 | var root_window : Window 6 | 7 | 8 | func _ready() -> void: 9 | root_window = get_tree().root 10 | root_window.remove_child.call_deferred(self) 11 | 12 | 13 | func play_exit_map() -> void: 14 | root_window.add_child(self) 15 | animation_player.play("exit_map") 16 | await animation_player.animation_finished 17 | 18 | 19 | func play_enter_map() -> void: 20 | animation_player.play("enter_map") 21 | await animation_player.animation_finished 22 | root_window.remove_child(self) 23 | -------------------------------------------------------------------------------- /7-map-transitions/map.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var registered_detector_count := 0 4 | var valid_detector_count := 0 5 | 6 | 7 | func _ready() -> void: 8 | for child_node in get_children(): 9 | var detector := child_node as Detector 10 | if detector: 11 | registered_detector_count += 1 12 | detector.validity_changed.connect(_on_detector_validity_changed) 13 | 14 | 15 | func _on_detector_validity_changed(valid: bool) -> void: 16 | if valid: 17 | valid_detector_count += 1 18 | if valid_detector_count == registered_detector_count: 19 | Main.load_next_map() 20 | else: 21 | valid_detector_count -= 1 22 | -------------------------------------------------------------------------------- /8-saving-progress/map.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var registered_detector_count := 0 4 | var valid_detector_count := 0 5 | 6 | 7 | func _ready() -> void: 8 | for child_node in get_children(): 9 | var detector := child_node as Detector 10 | if detector: 11 | registered_detector_count += 1 12 | detector.validity_changed.connect(_on_detector_validity_changed) 13 | 14 | Main.map_loaded(scene_file_path) 15 | 16 | 17 | func _on_detector_validity_changed(valid: bool) -> void: 18 | if valid: 19 | valid_detector_count += 1 20 | if valid_detector_count == registered_detector_count: 21 | Main.load_next_map() 22 | else: 23 | valid_detector_count -= 1 24 | -------------------------------------------------------------------------------- /5-detectors/player_character.gd: -------------------------------------------------------------------------------- 1 | extends CharacterBody2D 2 | 3 | ## Speed in pixels per second. 4 | @export_range(0, 1000) var speed := 60 5 | 6 | 7 | func _physics_process(_delta: float) -> void: 8 | get_player_input() 9 | if move_and_slide(): 10 | resolve_collisions() 11 | 12 | 13 | func resolve_collisions() -> void: 14 | for i in get_slide_collision_count(): 15 | var collision := get_slide_collision(i) 16 | var body := collision.get_collider() as MovableObject 17 | if body: 18 | body.apply_impact(velocity) 19 | 20 | 21 | func get_player_input() -> void: 22 | var vector := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") 23 | velocity = vector * speed 24 | -------------------------------------------------------------------------------- /9-game-hud/player_character.gd: -------------------------------------------------------------------------------- 1 | extends CharacterBody2D 2 | 3 | ## Speed in pixels per second. 4 | @export_range(0, 1000) var speed := 60 5 | 6 | 7 | func _physics_process(_delta: float) -> void: 8 | get_player_input() 9 | if move_and_slide(): 10 | resolve_collisions() 11 | 12 | 13 | func resolve_collisions() -> void: 14 | for i in get_slide_collision_count(): 15 | var collision := get_slide_collision(i) 16 | var body := collision.get_collider() as MovableObject 17 | if body: 18 | body.apply_impact(velocity) 19 | 20 | 21 | func get_player_input() -> void: 22 | var vector := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") 23 | velocity = vector * speed 24 | -------------------------------------------------------------------------------- /6-multiple-maps/player_character.gd: -------------------------------------------------------------------------------- 1 | extends CharacterBody2D 2 | 3 | ## Speed in pixels per second. 4 | @export_range(0, 1000) var speed := 60 5 | 6 | 7 | func _physics_process(_delta: float) -> void: 8 | get_player_input() 9 | if move_and_slide(): 10 | resolve_collisions() 11 | 12 | 13 | func resolve_collisions() -> void: 14 | for i in get_slide_collision_count(): 15 | var collision := get_slide_collision(i) 16 | var body := collision.get_collider() as MovableObject 17 | if body: 18 | body.apply_impact(velocity) 19 | 20 | 21 | func get_player_input() -> void: 22 | var vector := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") 23 | velocity = vector * speed 24 | -------------------------------------------------------------------------------- /3-movable-objects/player_character.gd: -------------------------------------------------------------------------------- 1 | extends CharacterBody2D 2 | 3 | ## Speed in pixels per second. 4 | @export_range(0, 1000) var speed := 60 5 | 6 | 7 | func _physics_process(_delta: float) -> void: 8 | get_player_input() 9 | if move_and_slide(): 10 | resolve_collisions() 11 | 12 | 13 | func resolve_collisions() -> void: 14 | for i in get_slide_collision_count(): 15 | var collision := get_slide_collision(i) 16 | var body := collision.get_collider() as MovableObject 17 | if body: 18 | body.apply_impact(velocity) 19 | 20 | 21 | func get_player_input() -> void: 22 | var vector := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") 23 | velocity = vector * speed 24 | -------------------------------------------------------------------------------- /4-light-and-shadow/player_character.gd: -------------------------------------------------------------------------------- 1 | extends CharacterBody2D 2 | 3 | ## Speed in pixels per second. 4 | @export_range(0, 1000) var speed := 60 5 | 6 | 7 | func _physics_process(_delta: float) -> void: 8 | get_player_input() 9 | if move_and_slide(): 10 | resolve_collisions() 11 | 12 | 13 | func resolve_collisions() -> void: 14 | for i in get_slide_collision_count(): 15 | var collision := get_slide_collision(i) 16 | var body := collision.get_collider() as MovableObject 17 | if body: 18 | body.apply_impact(velocity) 19 | 20 | 21 | func get_player_input() -> void: 22 | var vector := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") 23 | velocity = vector * speed 24 | -------------------------------------------------------------------------------- /7-map-transitions/player_character.gd: -------------------------------------------------------------------------------- 1 | extends CharacterBody2D 2 | 3 | ## Speed in pixels per second. 4 | @export_range(0, 1000) var speed := 60 5 | 6 | 7 | func _physics_process(_delta: float) -> void: 8 | get_player_input() 9 | if move_and_slide(): 10 | resolve_collisions() 11 | 12 | 13 | func resolve_collisions() -> void: 14 | for i in get_slide_collision_count(): 15 | var collision := get_slide_collision(i) 16 | var body := collision.get_collider() as MovableObject 17 | if body: 18 | body.apply_impact(velocity) 19 | 20 | 21 | func get_player_input() -> void: 22 | var vector := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") 23 | velocity = vector * speed 24 | -------------------------------------------------------------------------------- /8-saving-progress/player_character.gd: -------------------------------------------------------------------------------- 1 | extends CharacterBody2D 2 | 3 | ## Speed in pixels per second. 4 | @export_range(0, 1000) var speed := 60 5 | 6 | 7 | func _physics_process(_delta: float) -> void: 8 | get_player_input() 9 | if move_and_slide(): 10 | resolve_collisions() 11 | 12 | 13 | func resolve_collisions() -> void: 14 | for i in get_slide_collision_count(): 15 | var collision := get_slide_collision(i) 16 | var body := collision.get_collider() as MovableObject 17 | if body: 18 | body.apply_impact(velocity) 19 | 20 | 21 | func get_player_input() -> void: 22 | var vector := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") 23 | velocity = vector * speed 24 | -------------------------------------------------------------------------------- /1-tile-map/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="True Top-Down 2D" 14 | config/features=PackedStringArray("4.3", "Forward Plus") 15 | config/icon="res://icon.svg" 16 | 17 | [display] 18 | 19 | window/size/viewport_width=400 20 | window/size/viewport_height=240 21 | window/size/mode=4 22 | window/stretch/mode="viewport" 23 | window/stretch/scale_mode="integer" 24 | 25 | [filesystem] 26 | 27 | import/blender/enabled=false 28 | -------------------------------------------------------------------------------- /9-game-hud/hud.gd: -------------------------------------------------------------------------------- 1 | extends CanvasLayer 2 | 3 | @export var map_name_label: Label 4 | @export var map_time_label: Label 5 | @export var new_best_map_time_label: Label 6 | 7 | 8 | func _ready() -> void: 9 | visible = false 10 | new_best_map_time_label.visible = false 11 | 12 | 13 | func show_map_info(map: GameMap) -> void: 14 | map_name_label.text = map.map_name 15 | show_map_time(0.0) 16 | visible = true 17 | new_best_map_time_label.visible = false 18 | 19 | 20 | func show_map_time(time: float) -> void: 21 | map_time_label.text = "%ds" % time 22 | 23 | 24 | func show_new_best_map_time(new_time: int) -> void: 25 | new_best_map_time_label.text = "New best time: %ds" % new_time 26 | new_best_map_time_label.visible = true 27 | -------------------------------------------------------------------------------- /2-player-character/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="True Top-Down 2D" 14 | config/version="2.0" 15 | run/main_scene="res://map.tscn" 16 | config/features=PackedStringArray("4.3", "Forward Plus") 17 | config/icon="res://icon.svg" 18 | 19 | [display] 20 | 21 | window/size/viewport_width=400 22 | window/size/viewport_height=240 23 | window/size/mode=4 24 | window/stretch/mode="viewport" 25 | window/stretch/scale_mode="integer" 26 | 27 | [filesystem] 28 | 29 | import/blender/enabled=false 30 | -------------------------------------------------------------------------------- /7-map-transitions/main.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | 4 | func load_next_map() -> void: 5 | var current_map_path := get_tree().current_scene.scene_file_path 6 | var split_path := current_map_path.split(".") 7 | var next_map_number := split_path[1].to_int() + 1 8 | split_path[1] = str(next_map_number).pad_zeros(3) 9 | var next_map_path = ".".join(split_path) 10 | 11 | if not ResourceLoader.exists(next_map_path): 12 | split_path[1] = "001" 13 | next_map_path = ".".join(split_path) 14 | 15 | ResourceLoader.load_threaded_request(next_map_path) 16 | 17 | get_tree().paused = true 18 | await MapTransition.play_exit_map() 19 | 20 | get_tree().change_scene_to_packed( 21 | ResourceLoader.load_threaded_get(next_map_path) 22 | ) 23 | 24 | await MapTransition.play_enter_map() 25 | get_tree().paused = false 26 | -------------------------------------------------------------------------------- /1-tile-map/tile_set.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="TileSet" load_steps=3 format=3 uid="uid://ct1sg2j0vfyxf"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://brq3awcnk4gep" path="res://tile_set.png" id="1_yp80t"] 4 | 5 | [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_6asb7"] 6 | texture = ExtResource("1_yp80t") 7 | 0:0/animation_columns = 2 8 | 0:0/0 = 0 9 | 1:0/0 = 0 10 | 2:0/0 = 0 11 | 0:1/0 = 0 12 | 1:1/0 = 0 13 | 2:1/0 = 0 14 | 0:2/0 = 0 15 | 1:2/0 = 0 16 | 2:2/0 = 0 17 | 0:3/0 = 0 18 | 1:3/0 = 0 19 | 2:3/0 = 0 20 | 0:4/0 = 0 21 | 1:4/0 = 0 22 | 2:4/0 = 0 23 | 0:5/animation_mode = 1 24 | 0:5/animation_frame_0/duration = 1.0 25 | 0:5/animation_frame_1/duration = 1.0 26 | 0:5/0 = 0 27 | 0:6/animation_mode = 1 28 | 0:6/animation_frame_0/duration = 1.0 29 | 0:6/animation_frame_1/duration = 1.0 30 | 0:6/0 = 0 31 | 32 | [resource] 33 | sources/0 = SubResource("TileSetAtlasSource_6asb7") 34 | -------------------------------------------------------------------------------- /5-detectors/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="True Top-Down 2D" 14 | config/version="5.0" 15 | run/main_scene="res://map.tscn" 16 | config/features=PackedStringArray("4.3", "Forward Plus") 17 | config/icon="res://icon.svg" 18 | 19 | [display] 20 | 21 | window/size/viewport_width=400 22 | window/size/viewport_height=240 23 | window/size/mode=4 24 | window/stretch/mode="viewport" 25 | window/stretch/scale_mode="integer" 26 | window/snap/snap_2d_transforms_to_pixel=false 27 | 28 | [filesystem] 29 | 30 | import/blender/enabled=false 31 | 32 | [physics] 33 | 34 | 2d/default_gravity=0.0 35 | -------------------------------------------------------------------------------- /3-movable-objects/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="True Top-Down 2D" 14 | config/version="3.0" 15 | run/main_scene="res://map.tscn" 16 | config/features=PackedStringArray("4.3", "Forward Plus") 17 | config/icon="res://icon.svg" 18 | 19 | [display] 20 | 21 | window/size/viewport_width=400 22 | window/size/viewport_height=240 23 | window/size/mode=4 24 | window/stretch/mode="viewport" 25 | window/stretch/scale_mode="integer" 26 | window/snap/snap_2d_transforms_to_pixel=false 27 | 28 | [filesystem] 29 | 30 | import/blender/enabled=false 31 | 32 | [physics] 33 | 34 | 2d/default_gravity=0.0 35 | -------------------------------------------------------------------------------- /4-light-and-shadow/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="True Top-Down 2D" 14 | config/version="4.0" 15 | run/main_scene="res://map.tscn" 16 | config/features=PackedStringArray("4.3", "Forward Plus") 17 | config/icon="res://icon.svg" 18 | 19 | [display] 20 | 21 | window/size/viewport_width=400 22 | window/size/viewport_height=240 23 | window/size/mode=4 24 | window/stretch/mode="viewport" 25 | window/stretch/scale_mode="integer" 26 | window/snap/snap_2d_transforms_to_pixel=false 27 | 28 | [filesystem] 29 | 30 | import/blender/enabled=false 31 | 32 | [physics] 33 | 34 | 2d/default_gravity=0.0 35 | -------------------------------------------------------------------------------- /6-multiple-maps/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="True Top-Down 2D" 14 | config/version="6.0" 15 | run/main_scene="res://maps/map.001.tscn" 16 | config/features=PackedStringArray("4.3", "Forward Plus") 17 | config/icon="res://icon.svg" 18 | 19 | [display] 20 | 21 | window/size/viewport_width=400 22 | window/size/viewport_height=240 23 | window/size/mode=4 24 | window/stretch/mode="viewport" 25 | window/stretch/scale_mode="integer" 26 | window/snap/snap_2d_transforms_to_pixel=false 27 | 28 | [filesystem] 29 | 30 | import/blender/enabled=false 31 | 32 | [physics] 33 | 34 | 2d/default_gravity=0.0 35 | -------------------------------------------------------------------------------- /1-tile-map/tile_set.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://brq3awcnk4gep" 6 | path="res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://tile_set.png" 14 | dest_files=["res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /12-teleporters/tile_set.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://brq3awcnk4gep" 6 | path="res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://tile_set.png" 14 | dest_files=["res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /5-detectors/tile_set.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://brq3awcnk4gep" 6 | path="res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://tile_set.png" 14 | dest_files=["res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /9-game-hud/tile_set.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://brq3awcnk4gep" 6 | path="res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://tile_set.png" 14 | dest_files=["res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /10-extra-map-info/tile_set.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://brq3awcnk4gep" 6 | path="res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://tile_set.png" 14 | dest_files=["res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /2-player-character/tile_set.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://brq3awcnk4gep" 6 | path="res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://tile_set.png" 14 | dest_files=["res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /3-movable-objects/tile_set.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://brq3awcnk4gep" 6 | path="res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://tile_set.png" 14 | dest_files=["res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /4-light-and-shadow/tile_set.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://brq3awcnk4gep" 6 | path="res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://tile_set.png" 14 | dest_files=["res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /6-multiple-maps/tile_set.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://brq3awcnk4gep" 6 | path="res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://tile_set.png" 14 | dest_files=["res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /7-map-transitions/tile_set.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://brq3awcnk4gep" 6 | path="res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://tile_set.png" 14 | dest_files=["res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /8-saving-progress/tile_set.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://brq3awcnk4gep" 6 | path="res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://tile_set.png" 14 | dest_files=["res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /9-game-hud/lively_light.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | ## Maximum jitter offset per dimension, in pixels. 4 | @export var jitter_offset := 0.25 5 | ## How fast the light changes, in jitters per second. 6 | @export var jitter_speed := 6.0 7 | ## Minimum light energy level caused by jittering. 8 | @export_range(0.0, 1.0) var jitter_min_energy := 0.95 9 | 10 | @export var light_a: PointLight2D 11 | @export var light_b: PointLight2D 12 | 13 | var progress := 0.0 14 | 15 | 16 | func _process(delta: float) -> void: 17 | progress += delta * jitter_speed 18 | if progress >= 1.0: 19 | progress -= 1.0 20 | jitter() 21 | 22 | 23 | func jitter() -> void: 24 | var p := Vector2( 25 | randf_range(-jitter_offset, jitter_offset), 26 | randf_range(-jitter_offset, jitter_offset) 27 | ) 28 | light_a.position = p 29 | light_b.position = p 30 | 31 | var e := randf_range(jitter_min_energy, 1.0) 32 | light_a.energy = e 33 | light_b.energy = e 34 | -------------------------------------------------------------------------------- /1-tile-map/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /11-showing-best-scores/tile_set.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://brq3awcnk4gep" 6 | path="res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://tile_set.png" 14 | dest_files=["res://.godot/imported/tile_set.png-0c317f5c3892f579c700f587287d3095.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /12-teleporters/lively_light.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | ## Maximum jitter offset per dimension, in pixels. 4 | @export var jitter_offset := 0.25 5 | ## How fast the light changes, in jitters per second. 6 | @export var jitter_speed := 6.0 7 | ## Minimum light energy level caused by jittering. 8 | @export_range(0.0, 1.0) var jitter_min_energy := 0.95 9 | 10 | @export var light_a: PointLight2D 11 | @export var light_b: PointLight2D 12 | 13 | var progress := 0.0 14 | 15 | 16 | func _process(delta: float) -> void: 17 | progress += delta * jitter_speed 18 | if progress >= 1.0: 19 | progress -= 1.0 20 | jitter() 21 | 22 | 23 | func jitter() -> void: 24 | var p := Vector2( 25 | randf_range(-jitter_offset, jitter_offset), 26 | randf_range(-jitter_offset, jitter_offset) 27 | ) 28 | light_a.position = p 29 | light_b.position = p 30 | 31 | var e := randf_range(jitter_min_energy, 1.0) 32 | light_a.energy = e 33 | light_b.energy = e 34 | -------------------------------------------------------------------------------- /5-detectors/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /5-detectors/lively_light.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | ## Maximum jitter offset per dimension, in pixels. 4 | @export var jitter_offset := 0.25 5 | ## How fast the light changes, in jitters per second. 6 | @export var jitter_speed := 6.0 7 | ## Minimum light energy level caused by jittering. 8 | @export_range(0.0, 1.0) var jitter_min_energy := 0.95 9 | 10 | @export var light_a: PointLight2D 11 | @export var light_b: PointLight2D 12 | 13 | var progress := 0.0 14 | 15 | 16 | func _process(delta: float) -> void: 17 | progress += delta * jitter_speed 18 | if progress >= 1.0: 19 | progress -= 1.0 20 | jitter() 21 | 22 | 23 | func jitter() -> void: 24 | var p := Vector2( 25 | randf_range(-jitter_offset, jitter_offset), 26 | randf_range(-jitter_offset, jitter_offset) 27 | ) 28 | light_a.position = p 29 | light_b.position = p 30 | 31 | var e := randf_range(jitter_min_energy, 1.0) 32 | light_a.energy = e 33 | light_b.energy = e 34 | -------------------------------------------------------------------------------- /6-multiple-maps/lively_light.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | ## Maximum jitter offset per dimension, in pixels. 4 | @export var jitter_offset := 0.25 5 | ## How fast the light changes, in jitters per second. 6 | @export var jitter_speed := 6.0 7 | ## Minimum light energy level caused by jittering. 8 | @export_range(0.0, 1.0) var jitter_min_energy := 0.95 9 | 10 | @export var light_a: PointLight2D 11 | @export var light_b: PointLight2D 12 | 13 | var progress := 0.0 14 | 15 | 16 | func _process(delta: float) -> void: 17 | progress += delta * jitter_speed 18 | if progress >= 1.0: 19 | progress -= 1.0 20 | jitter() 21 | 22 | 23 | func jitter() -> void: 24 | var p := Vector2( 25 | randf_range(-jitter_offset, jitter_offset), 26 | randf_range(-jitter_offset, jitter_offset) 27 | ) 28 | light_a.position = p 29 | light_b.position = p 30 | 31 | var e := randf_range(jitter_min_energy, 1.0) 32 | light_a.energy = e 33 | light_b.energy = e 34 | -------------------------------------------------------------------------------- /9-game-hud/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /10-extra-map-info/lively_light.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | ## Maximum jitter offset per dimension, in pixels. 4 | @export var jitter_offset := 0.25 5 | ## How fast the light changes, in jitters per second. 6 | @export var jitter_speed := 6.0 7 | ## Minimum light energy level caused by jittering. 8 | @export_range(0.0, 1.0) var jitter_min_energy := 0.95 9 | 10 | @export var light_a: PointLight2D 11 | @export var light_b: PointLight2D 12 | 13 | var progress := 0.0 14 | 15 | 16 | func _process(delta: float) -> void: 17 | progress += delta * jitter_speed 18 | if progress >= 1.0: 19 | progress -= 1.0 20 | jitter() 21 | 22 | 23 | func jitter() -> void: 24 | var p := Vector2( 25 | randf_range(-jitter_offset, jitter_offset), 26 | randf_range(-jitter_offset, jitter_offset) 27 | ) 28 | light_a.position = p 29 | light_b.position = p 30 | 31 | var e := randf_range(jitter_min_energy, 1.0) 32 | light_a.energy = e 33 | light_b.energy = e 34 | -------------------------------------------------------------------------------- /12-teleporters/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /3-movable-objects/movable_object.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://bqy67mam7pouu"] 2 | 3 | [ext_resource type="Script" path="res://movable_object.gd" id="1_hxfrg"] 4 | 5 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_obpkd"] 6 | size = Vector2(8, 8) 7 | 8 | [sub_resource type="Gradient" id="Gradient_mld41"] 9 | 10 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_l17ur"] 11 | gradient = SubResource("Gradient_mld41") 12 | width = 8 13 | height = 8 14 | fill = 2 15 | fill_from = Vector2(0.5, 0.5) 16 | fill_to = Vector2(1, 0.5) 17 | 18 | [node name="MovableObject" type="CharacterBody2D"] 19 | motion_mode = 1 20 | script = ExtResource("1_hxfrg") 21 | 22 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 23 | shape = SubResource("RectangleShape2D_obpkd") 24 | 25 | [node name="Sprite2D" type="Sprite2D" parent="."] 26 | texture_filter = 1 27 | texture = SubResource("GradientTexture2D_l17ur") 28 | -------------------------------------------------------------------------------- /4-light-and-shadow/lively_light.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | ## Maximum jitter offset per dimension, in pixels. 4 | @export var jitter_offset := 0.25 5 | ## How fast the light changes, in jitters per second. 6 | @export var jitter_speed := 6.0 7 | ## Minimum light energy level caused by jittering. 8 | @export_range(0.0, 1.0) var jitter_min_energy := 0.95 9 | 10 | @export var light_a: PointLight2D 11 | @export var light_b: PointLight2D 12 | 13 | var progress := 0.0 14 | 15 | 16 | func _process(delta: float) -> void: 17 | progress += delta * jitter_speed 18 | if progress >= 1.0: 19 | progress -= 1.0 20 | jitter() 21 | 22 | 23 | func jitter() -> void: 24 | var p := Vector2( 25 | randf_range(-jitter_offset, jitter_offset), 26 | randf_range(-jitter_offset, jitter_offset) 27 | ) 28 | light_a.position = p 29 | light_b.position = p 30 | 31 | var e := randf_range(jitter_min_energy, 1.0) 32 | light_a.energy = e 33 | light_b.energy = e 34 | -------------------------------------------------------------------------------- /6-multiple-maps/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /7-map-transitions/lively_light.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | ## Maximum jitter offset per dimension, in pixels. 4 | @export var jitter_offset := 0.25 5 | ## How fast the light changes, in jitters per second. 6 | @export var jitter_speed := 6.0 7 | ## Minimum light energy level caused by jittering. 8 | @export_range(0.0, 1.0) var jitter_min_energy := 0.95 9 | 10 | @export var light_a: PointLight2D 11 | @export var light_b: PointLight2D 12 | 13 | var progress := 0.0 14 | 15 | 16 | func _process(delta: float) -> void: 17 | progress += delta * jitter_speed 18 | if progress >= 1.0: 19 | progress -= 1.0 20 | jitter() 21 | 22 | 23 | func jitter() -> void: 24 | var p := Vector2( 25 | randf_range(-jitter_offset, jitter_offset), 26 | randf_range(-jitter_offset, jitter_offset) 27 | ) 28 | light_a.position = p 29 | light_b.position = p 30 | 31 | var e := randf_range(jitter_min_energy, 1.0) 32 | light_a.energy = e 33 | light_b.energy = e 34 | -------------------------------------------------------------------------------- /8-saving-progress/lively_light.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | ## Maximum jitter offset per dimension, in pixels. 4 | @export var jitter_offset := 0.25 5 | ## How fast the light changes, in jitters per second. 6 | @export var jitter_speed := 6.0 7 | ## Minimum light energy level caused by jittering. 8 | @export_range(0.0, 1.0) var jitter_min_energy := 0.95 9 | 10 | @export var light_a: PointLight2D 11 | @export var light_b: PointLight2D 12 | 13 | var progress := 0.0 14 | 15 | 16 | func _process(delta: float) -> void: 17 | progress += delta * jitter_speed 18 | if progress >= 1.0: 19 | progress -= 1.0 20 | jitter() 21 | 22 | 23 | func jitter() -> void: 24 | var p := Vector2( 25 | randf_range(-jitter_offset, jitter_offset), 26 | randf_range(-jitter_offset, jitter_offset) 27 | ) 28 | light_a.position = p 29 | light_b.position = p 30 | 31 | var e := randf_range(jitter_min_energy, 1.0) 32 | light_a.energy = e 33 | light_b.energy = e 34 | -------------------------------------------------------------------------------- /10-extra-map-info/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /11-showing-best-scores/lively_light.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | ## Maximum jitter offset per dimension, in pixels. 4 | @export var jitter_offset := 0.25 5 | ## How fast the light changes, in jitters per second. 6 | @export var jitter_speed := 6.0 7 | ## Minimum light energy level caused by jittering. 8 | @export_range(0.0, 1.0) var jitter_min_energy := 0.95 9 | 10 | @export var light_a: PointLight2D 11 | @export var light_b: PointLight2D 12 | 13 | var progress := 0.0 14 | 15 | 16 | func _process(delta: float) -> void: 17 | progress += delta * jitter_speed 18 | if progress >= 1.0: 19 | progress -= 1.0 20 | jitter() 21 | 22 | 23 | func jitter() -> void: 24 | var p := Vector2( 25 | randf_range(-jitter_offset, jitter_offset), 26 | randf_range(-jitter_offset, jitter_offset) 27 | ) 28 | light_a.position = p 29 | light_b.position = p 30 | 31 | var e := randf_range(jitter_min_energy, 1.0) 32 | light_a.energy = e 33 | light_b.energy = e 34 | -------------------------------------------------------------------------------- /2-player-character/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /3-movable-objects/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /4-light-and-shadow/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /7-map-transitions/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /8-saving-progress/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /11-showing-best-scores/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT No Attribution (MIT-0) 2 | 3 | Copyright 2025 Jasper FLick 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | software and associated documentation files (the "Software"), to deal in the Software 7 | without restriction, including without limitation the rights to use, copy, modify, 8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | -------------------------------------------------------------------------------- /8-saving-progress/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="True Top-Down 2D" 14 | config/version="8.0" 15 | run/main_scene="uid://bk1rkxiuhfn52" 16 | config/features=PackedStringArray("4.4", "Forward Plus") 17 | config/icon="res://icon.svg" 18 | 19 | [autoload] 20 | 21 | Main="*res://main.gd" 22 | MapTransition="*res://map_transition.tscn" 23 | 24 | [display] 25 | 26 | window/size/viewport_width=400 27 | window/size/viewport_height=240 28 | window/size/mode=4 29 | window/stretch/mode="viewport" 30 | window/stretch/scale_mode="integer" 31 | window/snap/snap_2d_transforms_to_pixel=false 32 | 33 | [filesystem] 34 | 35 | import/blender/enabled=false 36 | 37 | [physics] 38 | 39 | 2d/default_gravity=0.0 40 | -------------------------------------------------------------------------------- /7-map-transitions/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="True Top-Down 2D" 14 | config/version="7.0" 15 | run/main_scene="res://maps/map.001.tscn" 16 | config/features=PackedStringArray("4.3", "Forward Plus") 17 | config/icon="res://icon.svg" 18 | 19 | [autoload] 20 | 21 | Main="*res://main.gd" 22 | MapTransition="*res://map_transition.tscn" 23 | 24 | [display] 25 | 26 | window/size/viewport_width=400 27 | window/size/viewport_height=240 28 | window/size/mode=4 29 | window/stretch/mode="viewport" 30 | window/stretch/scale_mode="integer" 31 | window/snap/snap_2d_transforms_to_pixel=false 32 | 33 | [filesystem] 34 | 35 | import/blender/enabled=false 36 | 37 | [physics] 38 | 39 | 2d/default_gravity=0.0 40 | -------------------------------------------------------------------------------- /1-tile-map/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://hm31mdvjsuxu" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /12-teleporters/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://hm31mdvjsuxu" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /5-detectors/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://hm31mdvjsuxu" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /9-game-hud/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://hm31mdvjsuxu" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /10-extra-map-info/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://hm31mdvjsuxu" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /2-player-character/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://hm31mdvjsuxu" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /3-movable-objects/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://hm31mdvjsuxu" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /4-light-and-shadow/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://hm31mdvjsuxu" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /6-multiple-maps/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://hm31mdvjsuxu" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /7-map-transitions/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://hm31mdvjsuxu" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /8-saving-progress/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://hm31mdvjsuxu" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /9-game-hud/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="True Top-Down 2D" 14 | config/version="9.0" 15 | run/main_scene="uid://bk1rkxiuhfn52" 16 | config/features=PackedStringArray("4.4", "Forward Plus") 17 | config/icon="res://icon.svg" 18 | 19 | [autoload] 20 | 21 | Main="*res://main.gd" 22 | MapTransition="*res://map_transition.tscn" 23 | HUD="*res://hud.tscn" 24 | 25 | [display] 26 | 27 | window/size/viewport_width=400 28 | window/size/viewport_height=240 29 | window/size/mode=4 30 | window/stretch/mode="viewport" 31 | window/stretch/scale_mode="integer" 32 | window/snap/snap_2d_transforms_to_pixel=false 33 | 34 | [filesystem] 35 | 36 | import/blender/enabled=false 37 | 38 | [physics] 39 | 40 | 2d/default_gravity=0.0 41 | -------------------------------------------------------------------------------- /11-showing-best-scores/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://hm31mdvjsuxu" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /12-teleporters/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="True Top-Down 2D" 14 | config/version="12.0" 15 | run/main_scene="uid://bk1rkxiuhfn52" 16 | config/features=PackedStringArray("4.5", "Forward Plus") 17 | config/icon="res://icon.svg" 18 | 19 | [autoload] 20 | 21 | Main="*res://main.gd" 22 | MapTransition="*res://map_transition.tscn" 23 | HUD="*res://hud.tscn" 24 | 25 | [display] 26 | 27 | window/size/viewport_width=400 28 | window/size/viewport_height=240 29 | window/size/mode=4 30 | window/stretch/mode="viewport" 31 | window/stretch/scale_mode="integer" 32 | window/snap/snap_2d_transforms_to_pixel=false 33 | 34 | [filesystem] 35 | 36 | import/blender/enabled=false 37 | 38 | [physics] 39 | 40 | 2d/default_gravity=0.0 41 | -------------------------------------------------------------------------------- /10-extra-map-info/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="True Top-Down 2D" 14 | config/version="10.0" 15 | run/main_scene="uid://bk1rkxiuhfn52" 16 | config/features=PackedStringArray("4.4", "Forward Plus") 17 | config/icon="res://icon.svg" 18 | 19 | [autoload] 20 | 21 | Main="*res://main.gd" 22 | MapTransition="*res://map_transition.tscn" 23 | HUD="*res://hud.tscn" 24 | 25 | [display] 26 | 27 | window/size/viewport_width=400 28 | window/size/viewport_height=240 29 | window/size/mode=4 30 | window/stretch/mode="viewport" 31 | window/stretch/scale_mode="integer" 32 | window/snap/snap_2d_transforms_to_pixel=false 33 | 34 | [filesystem] 35 | 36 | import/blender/enabled=false 37 | 38 | [physics] 39 | 40 | 2d/default_gravity=0.0 41 | -------------------------------------------------------------------------------- /11-showing-best-scores/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="True Top-Down 2D" 14 | config/version="11.0" 15 | run/main_scene="uid://bk1rkxiuhfn52" 16 | config/features=PackedStringArray("4.5", "Forward Plus") 17 | config/icon="res://icon.svg" 18 | 19 | [autoload] 20 | 21 | Main="*res://main.gd" 22 | MapTransition="*res://map_transition.tscn" 23 | HUD="*res://hud.tscn" 24 | 25 | [display] 26 | 27 | window/size/viewport_width=400 28 | window/size/viewport_height=240 29 | window/size/mode=4 30 | window/stretch/mode="viewport" 31 | window/stretch/scale_mode="integer" 32 | window/snap/snap_2d_transforms_to_pixel=false 33 | 34 | [filesystem] 35 | 36 | import/blender/enabled=false 37 | 38 | [physics] 39 | 40 | 2d/default_gravity=0.0 41 | -------------------------------------------------------------------------------- /3-movable-objects/movable_object.gd: -------------------------------------------------------------------------------- 1 | class_name MovableObject 2 | extends CharacterBody2D 3 | 4 | @export_range(0.0, 10.0) var drag := 5.0 5 | @export_range(0.0, 1.0) var impact_response := 0.5 6 | @export var initial_velocity := Vector2.ZERO 7 | 8 | 9 | func _ready() -> void: 10 | velocity = initial_velocity 11 | 12 | 13 | func apply_impact(impact_velocity: Vector2) -> void: 14 | velocity += (impact_velocity - velocity) * impact_response 15 | 16 | 17 | func _physics_process(delta: float) -> void: 18 | if velocity.length_squared() > 1.0: 19 | velocity *= 1.0 - drag * delta 20 | if move_and_slide(): 21 | resolve_collisions() 22 | 23 | 24 | func resolve_collisions() -> void: 25 | var current_velocity := velocity 26 | for i in get_slide_collision_count(): 27 | var collision := get_slide_collision(i) 28 | var body := collision.get_collider() as MovableObject 29 | if body: 30 | apply_impact(body.velocity) 31 | body.apply_impact(current_velocity) 32 | else: 33 | velocity -= velocity.project(collision.get_normal()) 34 | -------------------------------------------------------------------------------- /5-detectors/lively_light.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://c42eiotc23vsq"] 2 | 3 | [ext_resource type="Script" path="res://lively_light.gd" id="1_tpdam"] 4 | 5 | [sub_resource type="Gradient" id="Gradient_k8ld2"] 6 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 7 | 8 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_mhsb8"] 9 | gradient = SubResource("Gradient_k8ld2") 10 | width = 192 11 | height = 192 12 | fill = 1 13 | fill_from = Vector2(0.5, 0.5) 14 | fill_to = Vector2(1, 0.5) 15 | 16 | [node name="LivelyLight" type="Node2D" node_paths=PackedStringArray("light_a", "light_b")] 17 | script = ExtResource("1_tpdam") 18 | light_a = NodePath("LightA") 19 | light_b = NodePath("LightB") 20 | 21 | [node name="LightA" type="PointLight2D" parent="."] 22 | shadow_enabled = true 23 | texture = SubResource("GradientTexture2D_mhsb8") 24 | 25 | [node name="LightB" type="PointLight2D" parent="."] 26 | range_item_cull_mask = 2 27 | shadow_enabled = true 28 | shadow_item_cull_mask = 2 29 | texture = SubResource("GradientTexture2D_mhsb8") 30 | -------------------------------------------------------------------------------- /11-showing-best-scores/player_character.gd: -------------------------------------------------------------------------------- 1 | class_name PlayerCharacter 2 | extends CharacterBody2D 3 | 4 | ## Speed in pixels per second. 5 | @export_range(0, 1000) var speed := 60 6 | 7 | var last_position: Vector2 8 | var travel_distance := 0.0 9 | 10 | 11 | func _ready() -> void: 12 | last_position = position 13 | 14 | 15 | func _process(_delta: float) -> void: 16 | HUD.show_travel_distance(travel_distance) 17 | 18 | 19 | func _physics_process(_delta: float) -> void: 20 | get_player_input() 21 | if move_and_slide(): 22 | resolve_collisions() 23 | 24 | travel_distance += last_position.distance_to(position) 25 | last_position = position 26 | 27 | 28 | func resolve_collisions() -> void: 29 | for i in get_slide_collision_count(): 30 | var collision := get_slide_collision(i) 31 | var body := collision.get_collider() as MovableObject 32 | if body: 33 | body.apply_impact(velocity) 34 | 35 | 36 | func get_player_input() -> void: 37 | var vector := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") 38 | velocity = vector * speed 39 | -------------------------------------------------------------------------------- /4-light-and-shadow/lively_light.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://c42eiotc23vsq"] 2 | 3 | [ext_resource type="Script" path="res://lively_light.gd" id="1_tpdam"] 4 | 5 | [sub_resource type="Gradient" id="Gradient_k8ld2"] 6 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 7 | 8 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_mhsb8"] 9 | gradient = SubResource("Gradient_k8ld2") 10 | width = 192 11 | height = 192 12 | fill = 1 13 | fill_from = Vector2(0.5, 0.5) 14 | fill_to = Vector2(1, 0.5) 15 | 16 | [node name="LivelyLight" type="Node2D" node_paths=PackedStringArray("light_a", "light_b")] 17 | script = ExtResource("1_tpdam") 18 | light_a = NodePath("LightA") 19 | light_b = NodePath("LightB") 20 | 21 | [node name="LightA" type="PointLight2D" parent="."] 22 | shadow_enabled = true 23 | texture = SubResource("GradientTexture2D_mhsb8") 24 | 25 | [node name="LightB" type="PointLight2D" parent="."] 26 | range_item_cull_mask = 2 27 | shadow_enabled = true 28 | shadow_item_cull_mask = 2 29 | texture = SubResource("GradientTexture2D_mhsb8") 30 | -------------------------------------------------------------------------------- /6-multiple-maps/lively_light.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://c42eiotc23vsq"] 2 | 3 | [ext_resource type="Script" path="res://lively_light.gd" id="1_tpdam"] 4 | 5 | [sub_resource type="Gradient" id="Gradient_k8ld2"] 6 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 7 | 8 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_mhsb8"] 9 | gradient = SubResource("Gradient_k8ld2") 10 | width = 192 11 | height = 192 12 | fill = 1 13 | fill_from = Vector2(0.5, 0.5) 14 | fill_to = Vector2(1, 0.5) 15 | 16 | [node name="LivelyLight" type="Node2D" node_paths=PackedStringArray("light_a", "light_b")] 17 | script = ExtResource("1_tpdam") 18 | light_a = NodePath("LightA") 19 | light_b = NodePath("LightB") 20 | 21 | [node name="LightA" type="PointLight2D" parent="."] 22 | shadow_enabled = true 23 | texture = SubResource("GradientTexture2D_mhsb8") 24 | 25 | [node name="LightB" type="PointLight2D" parent="."] 26 | range_item_cull_mask = 2 27 | shadow_enabled = true 28 | shadow_item_cull_mask = 2 29 | texture = SubResource("GradientTexture2D_mhsb8") 30 | -------------------------------------------------------------------------------- /7-map-transitions/lively_light.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://c42eiotc23vsq"] 2 | 3 | [ext_resource type="Script" path="res://lively_light.gd" id="1_tpdam"] 4 | 5 | [sub_resource type="Gradient" id="Gradient_k8ld2"] 6 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 7 | 8 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_mhsb8"] 9 | gradient = SubResource("Gradient_k8ld2") 10 | width = 192 11 | height = 192 12 | fill = 1 13 | fill_from = Vector2(0.5, 0.5) 14 | fill_to = Vector2(1, 0.5) 15 | 16 | [node name="LivelyLight" type="Node2D" node_paths=PackedStringArray("light_a", "light_b")] 17 | script = ExtResource("1_tpdam") 18 | light_a = NodePath("LightA") 19 | light_b = NodePath("LightB") 20 | 21 | [node name="LightA" type="PointLight2D" parent="."] 22 | shadow_enabled = true 23 | texture = SubResource("GradientTexture2D_mhsb8") 24 | 25 | [node name="LightB" type="PointLight2D" parent="."] 26 | range_item_cull_mask = 2 27 | shadow_enabled = true 28 | shadow_item_cull_mask = 2 29 | texture = SubResource("GradientTexture2D_mhsb8") 30 | -------------------------------------------------------------------------------- /9-game-hud/lively_light.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://c42eiotc23vsq"] 2 | 3 | [ext_resource type="Script" uid="uid://cgoe12geigody" path="res://lively_light.gd" id="1_tpdam"] 4 | 5 | [sub_resource type="Gradient" id="Gradient_k8ld2"] 6 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 7 | 8 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_mhsb8"] 9 | gradient = SubResource("Gradient_k8ld2") 10 | width = 192 11 | height = 192 12 | fill = 1 13 | fill_from = Vector2(0.5, 0.5) 14 | fill_to = Vector2(1, 0.5) 15 | 16 | [node name="LivelyLight" type="Node2D" node_paths=PackedStringArray("light_a", "light_b")] 17 | script = ExtResource("1_tpdam") 18 | light_a = NodePath("LightA") 19 | light_b = NodePath("LightB") 20 | 21 | [node name="LightA" type="PointLight2D" parent="."] 22 | shadow_enabled = true 23 | texture = SubResource("GradientTexture2D_mhsb8") 24 | 25 | [node name="LightB" type="PointLight2D" parent="."] 26 | range_item_cull_mask = 2 27 | shadow_enabled = true 28 | shadow_item_cull_mask = 2 29 | texture = SubResource("GradientTexture2D_mhsb8") 30 | -------------------------------------------------------------------------------- /10-extra-map-info/lively_light.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://c42eiotc23vsq"] 2 | 3 | [ext_resource type="Script" uid="uid://cgoe12geigody" path="res://lively_light.gd" id="1_tpdam"] 4 | 5 | [sub_resource type="Gradient" id="Gradient_k8ld2"] 6 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 7 | 8 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_mhsb8"] 9 | gradient = SubResource("Gradient_k8ld2") 10 | width = 192 11 | height = 192 12 | fill = 1 13 | fill_from = Vector2(0.5, 0.5) 14 | fill_to = Vector2(1, 0.5) 15 | 16 | [node name="LivelyLight" type="Node2D" node_paths=PackedStringArray("light_a", "light_b")] 17 | script = ExtResource("1_tpdam") 18 | light_a = NodePath("LightA") 19 | light_b = NodePath("LightB") 20 | 21 | [node name="LightA" type="PointLight2D" parent="."] 22 | shadow_enabled = true 23 | texture = SubResource("GradientTexture2D_mhsb8") 24 | 25 | [node name="LightB" type="PointLight2D" parent="."] 26 | range_item_cull_mask = 2 27 | shadow_enabled = true 28 | shadow_item_cull_mask = 2 29 | texture = SubResource("GradientTexture2D_mhsb8") 30 | -------------------------------------------------------------------------------- /12-teleporters/lively_light.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://c42eiotc23vsq"] 2 | 3 | [ext_resource type="Script" uid="uid://cgoe12geigody" path="res://lively_light.gd" id="1_tpdam"] 4 | 5 | [sub_resource type="Gradient" id="Gradient_k8ld2"] 6 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 7 | 8 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_mhsb8"] 9 | gradient = SubResource("Gradient_k8ld2") 10 | width = 192 11 | height = 192 12 | fill = 1 13 | fill_from = Vector2(0.5, 0.5) 14 | fill_to = Vector2(1, 0.5) 15 | 16 | [node name="LivelyLight" type="Node2D" node_paths=PackedStringArray("light_a", "light_b")] 17 | script = ExtResource("1_tpdam") 18 | light_a = NodePath("LightA") 19 | light_b = NodePath("LightB") 20 | 21 | [node name="LightA" type="PointLight2D" parent="."] 22 | shadow_enabled = true 23 | texture = SubResource("GradientTexture2D_mhsb8") 24 | 25 | [node name="LightB" type="PointLight2D" parent="."] 26 | range_item_cull_mask = 2 27 | shadow_enabled = true 28 | shadow_item_cull_mask = 2 29 | texture = SubResource("GradientTexture2D_mhsb8") 30 | -------------------------------------------------------------------------------- /8-saving-progress/lively_light.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://c42eiotc23vsq"] 2 | 3 | [ext_resource type="Script" uid="uid://cgoe12geigody" path="res://lively_light.gd" id="1_tpdam"] 4 | 5 | [sub_resource type="Gradient" id="Gradient_k8ld2"] 6 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 7 | 8 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_mhsb8"] 9 | gradient = SubResource("Gradient_k8ld2") 10 | width = 192 11 | height = 192 12 | fill = 1 13 | fill_from = Vector2(0.5, 0.5) 14 | fill_to = Vector2(1, 0.5) 15 | 16 | [node name="LivelyLight" type="Node2D" node_paths=PackedStringArray("light_a", "light_b")] 17 | script = ExtResource("1_tpdam") 18 | light_a = NodePath("LightA") 19 | light_b = NodePath("LightB") 20 | 21 | [node name="LightA" type="PointLight2D" parent="."] 22 | shadow_enabled = true 23 | texture = SubResource("GradientTexture2D_mhsb8") 24 | 25 | [node name="LightB" type="PointLight2D" parent="."] 26 | range_item_cull_mask = 2 27 | shadow_enabled = true 28 | shadow_item_cull_mask = 2 29 | texture = SubResource("GradientTexture2D_mhsb8") 30 | -------------------------------------------------------------------------------- /11-showing-best-scores/lively_light.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://c42eiotc23vsq"] 2 | 3 | [ext_resource type="Script" uid="uid://cgoe12geigody" path="res://lively_light.gd" id="1_tpdam"] 4 | 5 | [sub_resource type="Gradient" id="Gradient_k8ld2"] 6 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 7 | 8 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_mhsb8"] 9 | gradient = SubResource("Gradient_k8ld2") 10 | width = 192 11 | height = 192 12 | fill = 1 13 | fill_from = Vector2(0.5, 0.5) 14 | fill_to = Vector2(1, 0.5) 15 | 16 | [node name="LivelyLight" type="Node2D" node_paths=PackedStringArray("light_a", "light_b")] 17 | script = ExtResource("1_tpdam") 18 | light_a = NodePath("LightA") 19 | light_b = NodePath("LightB") 20 | 21 | [node name="LightA" type="PointLight2D" parent="."] 22 | shadow_enabled = true 23 | texture = SubResource("GradientTexture2D_mhsb8") 24 | 25 | [node name="LightB" type="PointLight2D" parent="."] 26 | range_item_cull_mask = 2 27 | shadow_enabled = true 28 | shadow_item_cull_mask = 2 29 | texture = SubResource("GradientTexture2D_mhsb8") 30 | -------------------------------------------------------------------------------- /10-extra-map-info/player_character.gd: -------------------------------------------------------------------------------- 1 | class_name PlayerCharacter 2 | extends CharacterBody2D 3 | 4 | ## Speed in pixels per second. 5 | @export_range(0, 1000) var speed := 60 6 | 7 | var last_position: Vector2 8 | var travel_distance := 0.0 9 | 10 | 11 | func _ready() -> void: 12 | last_position = position 13 | HUD.show_travel_distance(travel_distance) 14 | 15 | 16 | func _process(_delta: float) -> void: 17 | HUD.show_travel_distance(travel_distance) 18 | 19 | 20 | func _physics_process(_delta: float) -> void: 21 | get_player_input() 22 | if move_and_slide(): 23 | resolve_collisions() 24 | 25 | travel_distance += last_position.distance_to(position) 26 | last_position = position 27 | 28 | 29 | func resolve_collisions() -> void: 30 | for i in get_slide_collision_count(): 31 | var collision := get_slide_collision(i) 32 | var body := collision.get_collider() as MovableObject 33 | if body: 34 | body.apply_impact(velocity) 35 | 36 | 37 | func get_player_input() -> void: 38 | var vector := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") 39 | velocity = vector * speed 40 | -------------------------------------------------------------------------------- /5-detectors/movable_object.gd: -------------------------------------------------------------------------------- 1 | class_name MovableObject 2 | extends CharacterBody2D 3 | 4 | @export_range(0.0, 10.0) var drag := 5.0 5 | @export_range(0.0, 1.0) var impact_response := 0.5 6 | @export var initial_velocity := Vector2.ZERO 7 | 8 | 9 | func _ready() -> void: 10 | velocity = initial_velocity 11 | 12 | 13 | func apply_impact(impact_velocity: Vector2) -> void: 14 | velocity += (impact_velocity - velocity) * impact_response 15 | 16 | 17 | func _physics_process(delta: float) -> void: 18 | if velocity.length_squared() > 1.0: 19 | velocity *= 1.0 - drag * delta 20 | if move_and_slide(): 21 | resolve_collisions() 22 | else: 23 | position = round(position) 24 | velocity = Vector2.ZERO 25 | 26 | 27 | func resolve_collisions() -> void: 28 | var current_velocity := velocity 29 | for i in get_slide_collision_count(): 30 | var collision := get_slide_collision(i) 31 | var body := collision.get_collider() as MovableObject 32 | if body: 33 | apply_impact(body.velocity) 34 | body.apply_impact(current_velocity) 35 | else: 36 | velocity -= velocity.project(collision.get_normal()) 37 | -------------------------------------------------------------------------------- /9-game-hud/movable_object.gd: -------------------------------------------------------------------------------- 1 | class_name MovableObject 2 | extends CharacterBody2D 3 | 4 | @export_range(0.0, 10.0) var drag := 5.0 5 | @export_range(0.0, 1.0) var impact_response := 0.5 6 | @export var initial_velocity := Vector2.ZERO 7 | 8 | 9 | func _ready() -> void: 10 | velocity = initial_velocity 11 | 12 | 13 | func apply_impact(impact_velocity: Vector2) -> void: 14 | velocity += (impact_velocity - velocity) * impact_response 15 | 16 | 17 | func _physics_process(delta: float) -> void: 18 | if velocity.length_squared() > 1.0: 19 | velocity *= 1.0 - drag * delta 20 | if move_and_slide(): 21 | resolve_collisions() 22 | else: 23 | position = round(position) 24 | velocity = Vector2.ZERO 25 | 26 | 27 | func resolve_collisions() -> void: 28 | var current_velocity := velocity 29 | for i in get_slide_collision_count(): 30 | var collision := get_slide_collision(i) 31 | var body := collision.get_collider() as MovableObject 32 | if body: 33 | apply_impact(body.velocity) 34 | body.apply_impact(current_velocity) 35 | else: 36 | velocity -= velocity.project(collision.get_normal()) 37 | -------------------------------------------------------------------------------- /10-extra-map-info/movable_object.gd: -------------------------------------------------------------------------------- 1 | class_name MovableObject 2 | extends CharacterBody2D 3 | 4 | @export_range(0.0, 10.0) var drag := 5.0 5 | @export_range(0.0, 1.0) var impact_response := 0.5 6 | @export var initial_velocity := Vector2.ZERO 7 | 8 | 9 | func _ready() -> void: 10 | velocity = initial_velocity 11 | 12 | 13 | func apply_impact(impact_velocity: Vector2) -> void: 14 | velocity += (impact_velocity - velocity) * impact_response 15 | 16 | 17 | func _physics_process(delta: float) -> void: 18 | if velocity.length_squared() > 1.0: 19 | velocity *= 1.0 - drag * delta 20 | if move_and_slide(): 21 | resolve_collisions() 22 | else: 23 | position = round(position) 24 | velocity = Vector2.ZERO 25 | 26 | 27 | func resolve_collisions() -> void: 28 | var current_velocity := velocity 29 | for i in get_slide_collision_count(): 30 | var collision := get_slide_collision(i) 31 | var body := collision.get_collider() as MovableObject 32 | if body: 33 | apply_impact(body.velocity) 34 | body.apply_impact(current_velocity) 35 | else: 36 | velocity -= velocity.project(collision.get_normal()) 37 | -------------------------------------------------------------------------------- /12-teleporters/movable_object.gd: -------------------------------------------------------------------------------- 1 | class_name MovableObject 2 | extends InteractiveObject 3 | 4 | @export_range(0.0, 10.0) var drag := 5.0 5 | @export_range(0.0, 1.0) var impact_response := 0.5 6 | @export var initial_velocity := Vector2.ZERO 7 | 8 | 9 | func _ready() -> void: 10 | velocity = initial_velocity 11 | 12 | 13 | func apply_impact(impact_velocity: Vector2) -> void: 14 | velocity += (impact_velocity - velocity) * impact_response 15 | 16 | 17 | func _physics_process(delta: float) -> void: 18 | if velocity.length_squared() > 1.0: 19 | velocity *= 1.0 - drag * delta 20 | if move_and_slide(): 21 | resolve_collisions() 22 | else: 23 | position = round(position) 24 | velocity = Vector2.ZERO 25 | 26 | 27 | func resolve_collisions() -> void: 28 | var current_velocity := velocity 29 | for i in get_slide_collision_count(): 30 | var collision := get_slide_collision(i) 31 | var body := collision.get_collider() as MovableObject 32 | if body: 33 | apply_impact(body.velocity) 34 | body.apply_impact(current_velocity) 35 | else: 36 | velocity -= velocity.project(collision.get_normal()) 37 | -------------------------------------------------------------------------------- /4-light-and-shadow/movable_object.gd: -------------------------------------------------------------------------------- 1 | class_name MovableObject 2 | extends CharacterBody2D 3 | 4 | @export_range(0.0, 10.0) var drag := 5.0 5 | @export_range(0.0, 1.0) var impact_response := 0.5 6 | @export var initial_velocity := Vector2.ZERO 7 | 8 | 9 | func _ready() -> void: 10 | velocity = initial_velocity 11 | 12 | 13 | func apply_impact(impact_velocity: Vector2) -> void: 14 | velocity += (impact_velocity - velocity) * impact_response 15 | 16 | 17 | func _physics_process(delta: float) -> void: 18 | if velocity.length_squared() > 1.0: 19 | velocity *= 1.0 - drag * delta 20 | if move_and_slide(): 21 | resolve_collisions() 22 | else: 23 | position = round(position) 24 | velocity = Vector2.ZERO 25 | 26 | 27 | func resolve_collisions() -> void: 28 | var current_velocity := velocity 29 | for i in get_slide_collision_count(): 30 | var collision := get_slide_collision(i) 31 | var body := collision.get_collider() as MovableObject 32 | if body: 33 | apply_impact(body.velocity) 34 | body.apply_impact(current_velocity) 35 | else: 36 | velocity -= velocity.project(collision.get_normal()) 37 | -------------------------------------------------------------------------------- /6-multiple-maps/movable_object.gd: -------------------------------------------------------------------------------- 1 | class_name MovableObject 2 | extends CharacterBody2D 3 | 4 | @export_range(0.0, 10.0) var drag := 5.0 5 | @export_range(0.0, 1.0) var impact_response := 0.5 6 | @export var initial_velocity := Vector2.ZERO 7 | 8 | 9 | func _ready() -> void: 10 | velocity = initial_velocity 11 | 12 | 13 | func apply_impact(impact_velocity: Vector2) -> void: 14 | velocity += (impact_velocity - velocity) * impact_response 15 | 16 | 17 | func _physics_process(delta: float) -> void: 18 | if velocity.length_squared() > 1.0: 19 | velocity *= 1.0 - drag * delta 20 | if move_and_slide(): 21 | resolve_collisions() 22 | else: 23 | position = round(position) 24 | velocity = Vector2.ZERO 25 | 26 | 27 | func resolve_collisions() -> void: 28 | var current_velocity := velocity 29 | for i in get_slide_collision_count(): 30 | var collision := get_slide_collision(i) 31 | var body := collision.get_collider() as MovableObject 32 | if body: 33 | apply_impact(body.velocity) 34 | body.apply_impact(current_velocity) 35 | else: 36 | velocity -= velocity.project(collision.get_normal()) 37 | -------------------------------------------------------------------------------- /7-map-transitions/movable_object.gd: -------------------------------------------------------------------------------- 1 | class_name MovableObject 2 | extends CharacterBody2D 3 | 4 | @export_range(0.0, 10.0) var drag := 5.0 5 | @export_range(0.0, 1.0) var impact_response := 0.5 6 | @export var initial_velocity := Vector2.ZERO 7 | 8 | 9 | func _ready() -> void: 10 | velocity = initial_velocity 11 | 12 | 13 | func apply_impact(impact_velocity: Vector2) -> void: 14 | velocity += (impact_velocity - velocity) * impact_response 15 | 16 | 17 | func _physics_process(delta: float) -> void: 18 | if velocity.length_squared() > 1.0: 19 | velocity *= 1.0 - drag * delta 20 | if move_and_slide(): 21 | resolve_collisions() 22 | else: 23 | position = round(position) 24 | velocity = Vector2.ZERO 25 | 26 | 27 | func resolve_collisions() -> void: 28 | var current_velocity := velocity 29 | for i in get_slide_collision_count(): 30 | var collision := get_slide_collision(i) 31 | var body := collision.get_collider() as MovableObject 32 | if body: 33 | apply_impact(body.velocity) 34 | body.apply_impact(current_velocity) 35 | else: 36 | velocity -= velocity.project(collision.get_normal()) 37 | -------------------------------------------------------------------------------- /8-saving-progress/movable_object.gd: -------------------------------------------------------------------------------- 1 | class_name MovableObject 2 | extends CharacterBody2D 3 | 4 | @export_range(0.0, 10.0) var drag := 5.0 5 | @export_range(0.0, 1.0) var impact_response := 0.5 6 | @export var initial_velocity := Vector2.ZERO 7 | 8 | 9 | func _ready() -> void: 10 | velocity = initial_velocity 11 | 12 | 13 | func apply_impact(impact_velocity: Vector2) -> void: 14 | velocity += (impact_velocity - velocity) * impact_response 15 | 16 | 17 | func _physics_process(delta: float) -> void: 18 | if velocity.length_squared() > 1.0: 19 | velocity *= 1.0 - drag * delta 20 | if move_and_slide(): 21 | resolve_collisions() 22 | else: 23 | position = round(position) 24 | velocity = Vector2.ZERO 25 | 26 | 27 | func resolve_collisions() -> void: 28 | var current_velocity := velocity 29 | for i in get_slide_collision_count(): 30 | var collision := get_slide_collision(i) 31 | var body := collision.get_collider() as MovableObject 32 | if body: 33 | apply_impact(body.velocity) 34 | body.apply_impact(current_velocity) 35 | else: 36 | velocity -= velocity.project(collision.get_normal()) 37 | -------------------------------------------------------------------------------- /11-showing-best-scores/movable_object.gd: -------------------------------------------------------------------------------- 1 | class_name MovableObject 2 | extends CharacterBody2D 3 | 4 | @export_range(0.0, 10.0) var drag := 5.0 5 | @export_range(0.0, 1.0) var impact_response := 0.5 6 | @export var initial_velocity := Vector2.ZERO 7 | 8 | 9 | func _ready() -> void: 10 | velocity = initial_velocity 11 | 12 | 13 | func apply_impact(impact_velocity: Vector2) -> void: 14 | velocity += (impact_velocity - velocity) * impact_response 15 | 16 | 17 | func _physics_process(delta: float) -> void: 18 | if velocity.length_squared() > 1.0: 19 | velocity *= 1.0 - drag * delta 20 | if move_and_slide(): 21 | resolve_collisions() 22 | else: 23 | position = round(position) 24 | velocity = Vector2.ZERO 25 | 26 | 27 | func resolve_collisions() -> void: 28 | var current_velocity := velocity 29 | for i in get_slide_collision_count(): 30 | var collision := get_slide_collision(i) 31 | var body := collision.get_collider() as MovableObject 32 | if body: 33 | apply_impact(body.velocity) 34 | body.apply_impact(current_velocity) 35 | else: 36 | velocity -= velocity.project(collision.get_normal()) 37 | -------------------------------------------------------------------------------- /6-multiple-maps/map.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var registered_detector_count := 0 4 | var valid_detector_count := 0 5 | 6 | 7 | func _ready() -> void: 8 | for child_node in get_children(): 9 | var detector := child_node as Detector 10 | if detector: 11 | registered_detector_count += 1 12 | detector.validity_changed.connect(_on_detector_validity_changed) 13 | 14 | 15 | func _on_detector_validity_changed(valid: bool) -> void: 16 | if valid: 17 | valid_detector_count += 1 18 | if valid_detector_count == registered_detector_count: 19 | load_next_map.call_deferred() 20 | else: 21 | valid_detector_count -= 1 22 | 23 | 24 | func load_next_map() -> void: 25 | var current_map_path := get_tree().current_scene.scene_file_path 26 | var split_path := current_map_path.split(".") 27 | var next_map_number := split_path[1].to_int() + 1 28 | split_path[1] = str(next_map_number).pad_zeros(3) 29 | var next_map_path = ".".join(split_path) 30 | 31 | if not ResourceLoader.exists(next_map_path): 32 | split_path[1] = "001" 33 | next_map_path = ".".join(split_path) 34 | 35 | get_tree().change_scene_to_file(next_map_path) 36 | -------------------------------------------------------------------------------- /12-teleporters/player_character.gd: -------------------------------------------------------------------------------- 1 | class_name PlayerCharacter 2 | extends InteractiveObject 3 | 4 | ## Speed in pixels per second. 5 | @export_range(0, 1000) var speed := 60 6 | 7 | var last_position: Vector2 8 | var travel_distance := 0.0 9 | 10 | 11 | func displace(offset: Vector2) -> void: 12 | super(offset) 13 | last_position = position 14 | 15 | 16 | func _ready() -> void: 17 | last_position = position 18 | 19 | 20 | func _process(_delta: float) -> void: 21 | HUD.show_travel_distance(travel_distance) 22 | 23 | 24 | func _physics_process(_delta: float) -> void: 25 | get_player_input() 26 | if move_and_slide(): 27 | resolve_collisions() 28 | 29 | travel_distance += last_position.distance_to(position) 30 | last_position = position 31 | 32 | 33 | func resolve_collisions() -> void: 34 | for i in get_slide_collision_count(): 35 | var collision := get_slide_collision(i) 36 | var body := collision.get_collider() as MovableObject 37 | if body: 38 | body.apply_impact(velocity) 39 | 40 | 41 | func get_player_input() -> void: 42 | var vector := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") 43 | velocity = vector * speed 44 | -------------------------------------------------------------------------------- /12-teleporters/teleporters/teleporter_source.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://blq78ijp12vd1" 6 | path="res://.godot/imported/teleporter_source.png-bab27449a8e4b7df3f31f0d7e6abf57f.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://teleporters/teleporter_source.png" 14 | dest_files=["res://.godot/imported/teleporter_source.png-bab27449a8e4b7df3f31f0d7e6abf57f.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | -------------------------------------------------------------------------------- /12-teleporters/teleporters/teleporter_destination.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cpc3pnpy5ytd2" 6 | path="res://.godot/imported/teleporter_destination.png-f26c94633810de79ddc236a06be64563.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://teleporters/teleporter_destination.png" 14 | dest_files=["res://.godot/imported/teleporter_destination.png-f26c94633810de79ddc236a06be64563.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/uastc_level=0 22 | compress/rdo_quality_loss=0.0 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=false 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/channel_remap/red=0 31 | process/channel_remap/green=1 32 | process/channel_remap/blue=2 33 | process/channel_remap/alpha=3 34 | process/fix_alpha_border=true 35 | process/premult_alpha=false 36 | process/normal_map_invert_y=false 37 | process/hdr_as_srgb=false 38 | process/hdr_clamp_exposure=false 39 | process/size_limit=0 40 | detect_3d/compress_to=1 41 | -------------------------------------------------------------------------------- /4-light-and-shadow/movable_object.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://bqy67mam7pouu"] 2 | 3 | [ext_resource type="Script" path="res://movable_object.gd" id="1_hxfrg"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_irncw"] 6 | polygon = PackedVector2Array(-4, -4, 4, -4, 4, 4, -4, 4) 7 | 8 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_obpkd"] 9 | size = Vector2(8, 8) 10 | 11 | [sub_resource type="Gradient" id="Gradient_mld41"] 12 | 13 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_l17ur"] 14 | gradient = SubResource("Gradient_mld41") 15 | width = 8 16 | height = 8 17 | fill = 2 18 | fill_from = Vector2(0.5, 0.5) 19 | fill_to = Vector2(1, 0.5) 20 | 21 | [node name="MovableObject" type="CharacterBody2D"] 22 | motion_mode = 1 23 | script = ExtResource("1_hxfrg") 24 | 25 | [node name="LightOccluder2D" type="LightOccluder2D" parent="."] 26 | occluder = SubResource("OccluderPolygon2D_irncw") 27 | 28 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 29 | shape = SubResource("RectangleShape2D_obpkd") 30 | 31 | [node name="Sprite2D" type="Sprite2D" parent="."] 32 | light_mask = 2 33 | texture_filter = 1 34 | texture = SubResource("GradientTexture2D_l17ur") 35 | -------------------------------------------------------------------------------- /5-detectors/detector.gd: -------------------------------------------------------------------------------- 1 | extends Area2D 2 | 3 | ## Color for when no objects are on the detector. 4 | @export_color_no_alpha var invalid_color := Color.RED 5 | ## Color for when objects are on the detector. 6 | @export_color_no_alpha var valid_color := Color.GREEN 7 | 8 | ## How fast the detector's color pulses. 9 | @export_range(0.1, 1.0) var pulse_frequency := 0.25 10 | 11 | @export var sprite: Sprite2D 12 | 13 | var object_count := 0 14 | 15 | var pulse_progress := 0.0 16 | 17 | 18 | func _init() -> void: 19 | pulse_progress = randf() 20 | 21 | 22 | func _process(delta: float) -> void: 23 | if object_count == 0: 24 | pulse_progress += delta * pulse_frequency 25 | if pulse_progress >= 1.0: 26 | pulse_progress -= 1.0 27 | 28 | var brightness := cos(pulse_progress * TAU) * 0.25 + 0.75 29 | sprite.modulate = Color( 30 | invalid_color.r * brightness, 31 | invalid_color.g * brightness, 32 | invalid_color.b * brightness 33 | ) 34 | 35 | 36 | func _on_body_entered(_body: Node2D) -> void: 37 | if object_count == 0: 38 | sprite.modulate = valid_color 39 | object_count += 1 40 | 41 | 42 | func _on_body_exited(_body: Node2D) -> void: 43 | object_count -= 1 44 | if object_count == 0: 45 | pulse_progress = 0.0 46 | -------------------------------------------------------------------------------- /5-detectors/movable_object.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://bqy67mam7pouu"] 2 | 3 | [ext_resource type="Script" path="res://movable_object.gd" id="1_hxfrg"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_irncw"] 6 | polygon = PackedVector2Array(-4, -4, 4, -4, 4, 4, -4, 4) 7 | 8 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_obpkd"] 9 | size = Vector2(7.9, 7.9) 10 | 11 | [sub_resource type="Gradient" id="Gradient_mld41"] 12 | 13 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_l17ur"] 14 | gradient = SubResource("Gradient_mld41") 15 | width = 8 16 | height = 8 17 | fill = 2 18 | fill_from = Vector2(0.5, 0.5) 19 | fill_to = Vector2(1, 0.5) 20 | 21 | [node name="MovableObject" type="CharacterBody2D"] 22 | collision_layer = 3 23 | motion_mode = 1 24 | script = ExtResource("1_hxfrg") 25 | 26 | [node name="LightOccluder2D" type="LightOccluder2D" parent="."] 27 | occluder = SubResource("OccluderPolygon2D_irncw") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_obpkd") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | light_mask = 2 34 | texture_filter = 1 35 | texture = SubResource("GradientTexture2D_l17ur") 36 | -------------------------------------------------------------------------------- /6-multiple-maps/movable_object.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://bqy67mam7pouu"] 2 | 3 | [ext_resource type="Script" path="res://movable_object.gd" id="1_hxfrg"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_irncw"] 6 | polygon = PackedVector2Array(-4, -4, 4, -4, 4, 4, -4, 4) 7 | 8 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_obpkd"] 9 | size = Vector2(7.9, 7.9) 10 | 11 | [sub_resource type="Gradient" id="Gradient_mld41"] 12 | 13 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_l17ur"] 14 | gradient = SubResource("Gradient_mld41") 15 | width = 8 16 | height = 8 17 | fill = 2 18 | fill_from = Vector2(0.5, 0.5) 19 | fill_to = Vector2(1, 0.5) 20 | 21 | [node name="MovableObject" type="CharacterBody2D"] 22 | collision_layer = 3 23 | motion_mode = 1 24 | script = ExtResource("1_hxfrg") 25 | 26 | [node name="LightOccluder2D" type="LightOccluder2D" parent="."] 27 | occluder = SubResource("OccluderPolygon2D_irncw") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_obpkd") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | light_mask = 2 34 | texture_filter = 1 35 | texture = SubResource("GradientTexture2D_l17ur") 36 | -------------------------------------------------------------------------------- /7-map-transitions/movable_object.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://bqy67mam7pouu"] 2 | 3 | [ext_resource type="Script" path="res://movable_object.gd" id="1_hxfrg"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_irncw"] 6 | polygon = PackedVector2Array(-4, -4, 4, -4, 4, 4, -4, 4) 7 | 8 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_obpkd"] 9 | size = Vector2(7.9, 7.9) 10 | 11 | [sub_resource type="Gradient" id="Gradient_mld41"] 12 | 13 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_l17ur"] 14 | gradient = SubResource("Gradient_mld41") 15 | width = 8 16 | height = 8 17 | fill = 2 18 | fill_from = Vector2(0.5, 0.5) 19 | fill_to = Vector2(1, 0.5) 20 | 21 | [node name="MovableObject" type="CharacterBody2D"] 22 | collision_layer = 3 23 | motion_mode = 1 24 | script = ExtResource("1_hxfrg") 25 | 26 | [node name="LightOccluder2D" type="LightOccluder2D" parent="."] 27 | occluder = SubResource("OccluderPolygon2D_irncw") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_obpkd") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | light_mask = 2 34 | texture_filter = 1 35 | texture = SubResource("GradientTexture2D_l17ur") 36 | -------------------------------------------------------------------------------- /9-game-hud/hud.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://cw4q2habmqtk8"] 2 | 3 | [ext_resource type="Script" uid="uid://cv3i66vvgu2ra" path="res://hud.gd" id="1_37p78"] 4 | 5 | [node name="HUD" type="CanvasLayer" node_paths=PackedStringArray("map_name_label", "map_time_label", "new_best_map_time_label")] 6 | layer = 3 7 | script = ExtResource("1_37p78") 8 | map_name_label = NodePath("MapName") 9 | map_time_label = NodePath("MapTime") 10 | new_best_map_time_label = NodePath("NewBestMapTime") 11 | 12 | [node name="MapName" type="Label" parent="."] 13 | offset_left = 16.0 14 | offset_right = 75.0 15 | offset_bottom = 16.0 16 | theme_override_font_sizes/font_size = 11 17 | text = "Map Name" 18 | 19 | [node name="MapTime" type="Label" parent="."] 20 | offset_left = 324.0 21 | offset_right = 384.0 22 | offset_bottom = 16.0 23 | theme_override_font_sizes/font_size = 11 24 | text = "0s" 25 | horizontal_alignment = 2 26 | 27 | [node name="NewBestMapTime" type="Label" parent="."] 28 | anchors_preset = 8 29 | anchor_left = 0.5 30 | anchor_top = 0.5 31 | anchor_right = 0.5 32 | anchor_bottom = 0.5 33 | offset_left = -0.5 34 | offset_top = -11.5 35 | offset_right = 0.5 36 | offset_bottom = 11.5 37 | grow_horizontal = 2 38 | grow_vertical = 2 39 | text = "New Best Time" 40 | -------------------------------------------------------------------------------- /9-game-hud/movable_object.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://bqy67mam7pouu"] 2 | 3 | [ext_resource type="Script" uid="uid://on8oe1ncp37d" path="res://movable_object.gd" id="1_hxfrg"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_irncw"] 6 | polygon = PackedVector2Array(-4, -4, 4, -4, 4, 4, -4, 4) 7 | 8 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_obpkd"] 9 | size = Vector2(7.9, 7.9) 10 | 11 | [sub_resource type="Gradient" id="Gradient_mld41"] 12 | 13 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_l17ur"] 14 | gradient = SubResource("Gradient_mld41") 15 | width = 8 16 | height = 8 17 | fill = 2 18 | fill_from = Vector2(0.5, 0.5) 19 | fill_to = Vector2(1, 0.5) 20 | 21 | [node name="MovableObject" type="CharacterBody2D"] 22 | collision_layer = 3 23 | motion_mode = 1 24 | script = ExtResource("1_hxfrg") 25 | 26 | [node name="LightOccluder2D" type="LightOccluder2D" parent="."] 27 | occluder = SubResource("OccluderPolygon2D_irncw") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_obpkd") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | light_mask = 2 34 | texture_filter = 1 35 | texture = SubResource("GradientTexture2D_l17ur") 36 | -------------------------------------------------------------------------------- /10-extra-map-info/movable_object.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://bqy67mam7pouu"] 2 | 3 | [ext_resource type="Script" uid="uid://on8oe1ncp37d" path="res://movable_object.gd" id="1_hxfrg"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_irncw"] 6 | polygon = PackedVector2Array(-4, -4, 4, -4, 4, 4, -4, 4) 7 | 8 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_obpkd"] 9 | size = Vector2(7.9, 7.9) 10 | 11 | [sub_resource type="Gradient" id="Gradient_mld41"] 12 | 13 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_l17ur"] 14 | gradient = SubResource("Gradient_mld41") 15 | width = 8 16 | height = 8 17 | fill = 2 18 | fill_from = Vector2(0.5, 0.5) 19 | fill_to = Vector2(1, 0.5) 20 | 21 | [node name="MovableObject" type="CharacterBody2D"] 22 | collision_layer = 3 23 | motion_mode = 1 24 | script = ExtResource("1_hxfrg") 25 | 26 | [node name="LightOccluder2D" type="LightOccluder2D" parent="."] 27 | occluder = SubResource("OccluderPolygon2D_irncw") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_obpkd") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | light_mask = 2 34 | texture_filter = 1 35 | texture = SubResource("GradientTexture2D_l17ur") 36 | -------------------------------------------------------------------------------- /8-saving-progress/movable_object.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://bqy67mam7pouu"] 2 | 3 | [ext_resource type="Script" uid="uid://on8oe1ncp37d" path="res://movable_object.gd" id="1_hxfrg"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_irncw"] 6 | polygon = PackedVector2Array(-4, -4, 4, -4, 4, 4, -4, 4) 7 | 8 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_obpkd"] 9 | size = Vector2(7.9, 7.9) 10 | 11 | [sub_resource type="Gradient" id="Gradient_mld41"] 12 | 13 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_l17ur"] 14 | gradient = SubResource("Gradient_mld41") 15 | width = 8 16 | height = 8 17 | fill = 2 18 | fill_from = Vector2(0.5, 0.5) 19 | fill_to = Vector2(1, 0.5) 20 | 21 | [node name="MovableObject" type="CharacterBody2D"] 22 | collision_layer = 3 23 | motion_mode = 1 24 | script = ExtResource("1_hxfrg") 25 | 26 | [node name="LightOccluder2D" type="LightOccluder2D" parent="."] 27 | occluder = SubResource("OccluderPolygon2D_irncw") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_obpkd") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | light_mask = 2 34 | texture_filter = 1 35 | texture = SubResource("GradientTexture2D_l17ur") 36 | -------------------------------------------------------------------------------- /11-showing-best-scores/movable_object.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://bqy67mam7pouu"] 2 | 3 | [ext_resource type="Script" uid="uid://on8oe1ncp37d" path="res://movable_object.gd" id="1_hxfrg"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_irncw"] 6 | polygon = PackedVector2Array(-4, -4, 4, -4, 4, 4, -4, 4) 7 | 8 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_obpkd"] 9 | size = Vector2(7.9, 7.9) 10 | 11 | [sub_resource type="Gradient" id="Gradient_mld41"] 12 | 13 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_l17ur"] 14 | gradient = SubResource("Gradient_mld41") 15 | width = 8 16 | height = 8 17 | fill = 2 18 | fill_from = Vector2(0.5, 0.5) 19 | fill_to = Vector2(1, 0.5) 20 | 21 | [node name="MovableObject" type="CharacterBody2D"] 22 | collision_layer = 3 23 | motion_mode = 1 24 | script = ExtResource("1_hxfrg") 25 | 26 | [node name="LightOccluder2D" type="LightOccluder2D" parent="."] 27 | occluder = SubResource("OccluderPolygon2D_irncw") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_obpkd") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | light_mask = 2 34 | texture_filter = 1 35 | texture = SubResource("GradientTexture2D_l17ur") 36 | -------------------------------------------------------------------------------- /9-game-hud/map.gd: -------------------------------------------------------------------------------- 1 | class_name GameMap 2 | extends Node 3 | 4 | const KEY_BEST_TIME := "best_time" 5 | 6 | @export var map_name := "Map" 7 | 8 | var registered_detector_count := 0 9 | var valid_detector_count := 0 10 | var current_map_time := 0.0 11 | var map_save_data: Dictionary 12 | 13 | 14 | func _process(delta: float) -> void: 15 | current_map_time += delta 16 | HUD.show_map_time(current_map_time) 17 | 18 | 19 | func _ready() -> void: 20 | for child_node in get_children(): 21 | var detector := child_node as Detector 22 | if detector: 23 | registered_detector_count += 1 24 | detector.validity_changed.connect(_on_detector_validity_changed) 25 | 26 | Main.map_loaded(self) 27 | HUD.show_map_info(self) 28 | 29 | 30 | func _on_detector_validity_changed(valid: bool) -> void: 31 | if valid: 32 | valid_detector_count += 1 33 | if valid_detector_count == registered_detector_count: 34 | _update_best_time() 35 | Main.load_next_map() 36 | else: 37 | valid_detector_count -= 1 38 | 39 | 40 | func _update_best_time() -> void: 41 | var new_time := floori(current_map_time) 42 | if ( 43 | not map_save_data.has(KEY_BEST_TIME) 44 | or new_time < map_save_data[KEY_BEST_TIME] 45 | ): 46 | map_save_data[KEY_BEST_TIME] = new_time 47 | HUD.show_new_best_map_time(new_time) 48 | -------------------------------------------------------------------------------- /12-teleporters/movable_object.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://bqy67mam7pouu"] 2 | 3 | [ext_resource type="Script" uid="uid://on8oe1ncp37d" path="res://movable_object.gd" id="1_hxfrg"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_irncw"] 6 | polygon = PackedVector2Array(-4, -4, 4, -4, 4, 4, -4, 4) 7 | 8 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_obpkd"] 9 | size = Vector2(7.9, 7.9) 10 | 11 | [sub_resource type="Gradient" id="Gradient_mld41"] 12 | 13 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_l17ur"] 14 | gradient = SubResource("Gradient_mld41") 15 | width = 8 16 | height = 8 17 | fill = 2 18 | fill_from = Vector2(0.5, 0.5) 19 | fill_to = Vector2(1, 0.5) 20 | 21 | [node name="MovableObject" type="CharacterBody2D" node_paths=PackedStringArray("colission_shape")] 22 | collision_layer = 3 23 | motion_mode = 1 24 | script = ExtResource("1_hxfrg") 25 | colission_shape = NodePath("CollisionShape2D") 26 | 27 | [node name="LightOccluder2D" type="LightOccluder2D" parent="."] 28 | occluder = SubResource("OccluderPolygon2D_irncw") 29 | 30 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 31 | shape = SubResource("RectangleShape2D_obpkd") 32 | 33 | [node name="Sprite2D" type="Sprite2D" parent="."] 34 | light_mask = 2 35 | texture_filter = 1 36 | texture = SubResource("GradientTexture2D_l17ur") 37 | -------------------------------------------------------------------------------- /9-game-hud/detector.gd: -------------------------------------------------------------------------------- 1 | class_name Detector 2 | extends Area2D 3 | 4 | signal validity_changed(valid: bool) 5 | 6 | ## Color for when no objects are on the detector. 7 | @export_color_no_alpha var invalid_color := Color.RED 8 | ## Color for when objects are on the detector. 9 | @export_color_no_alpha var valid_color := Color.GREEN 10 | 11 | ## How fast the detector's color pulses. 12 | @export_range(0.1, 1.0) var pulse_frequency := 0.25 13 | 14 | @export var sprite: Sprite2D 15 | 16 | var object_count := 0 17 | 18 | var pulse_progress := 0.0 19 | 20 | 21 | func _init() -> void: 22 | pulse_progress = randf() 23 | 24 | 25 | func _process(delta: float) -> void: 26 | if object_count == 0: 27 | pulse_progress += delta * pulse_frequency 28 | if pulse_progress >= 1.0: 29 | pulse_progress -= 1.0 30 | 31 | var brightness := cos(pulse_progress * TAU) * 0.25 + 0.75 32 | sprite.modulate = Color( 33 | invalid_color.r * brightness, 34 | invalid_color.g * brightness, 35 | invalid_color.b * brightness 36 | ) 37 | 38 | 39 | func _on_body_entered(_body: Node2D) -> void: 40 | if object_count == 0: 41 | sprite.modulate = valid_color 42 | validity_changed.emit(true) 43 | object_count += 1 44 | 45 | 46 | func _on_body_exited(_body: Node2D) -> void: 47 | object_count -= 1 48 | if object_count == 0: 49 | pulse_progress = 0.0 50 | validity_changed.emit(false) 51 | -------------------------------------------------------------------------------- /12-teleporters/detector.gd: -------------------------------------------------------------------------------- 1 | class_name Detector 2 | extends Area2D 3 | 4 | signal validity_changed(valid: bool) 5 | 6 | ## Color for when no objects are on the detector. 7 | @export_color_no_alpha var invalid_color := Color.RED 8 | ## Color for when objects are on the detector. 9 | @export_color_no_alpha var valid_color := Color.GREEN 10 | 11 | ## How fast the detector's color pulses. 12 | @export_range(0.1, 1.0) var pulse_frequency := 0.25 13 | 14 | @export var sprite: Sprite2D 15 | 16 | var object_count := 0 17 | 18 | var pulse_progress := 0.0 19 | 20 | 21 | func _init() -> void: 22 | pulse_progress = randf() 23 | 24 | 25 | func _process(delta: float) -> void: 26 | if object_count == 0: 27 | pulse_progress += delta * pulse_frequency 28 | if pulse_progress >= 1.0: 29 | pulse_progress -= 1.0 30 | 31 | var brightness := cos(pulse_progress * TAU) * 0.25 + 0.75 32 | sprite.modulate = Color( 33 | invalid_color.r * brightness, 34 | invalid_color.g * brightness, 35 | invalid_color.b * brightness 36 | ) 37 | 38 | 39 | func _on_body_entered(_body: Node2D) -> void: 40 | if object_count == 0: 41 | sprite.modulate = valid_color 42 | validity_changed.emit(true) 43 | object_count += 1 44 | 45 | 46 | func _on_body_exited(_body: Node2D) -> void: 47 | object_count -= 1 48 | if object_count == 0: 49 | pulse_progress = 0.0 50 | validity_changed.emit(false) 51 | -------------------------------------------------------------------------------- /6-multiple-maps/detector.gd: -------------------------------------------------------------------------------- 1 | class_name Detector 2 | extends Area2D 3 | 4 | signal validity_changed(valid: bool) 5 | 6 | ## Color for when no objects are on the detector. 7 | @export_color_no_alpha var invalid_color := Color.RED 8 | ## Color for when objects are on the detector. 9 | @export_color_no_alpha var valid_color := Color.GREEN 10 | 11 | ## How fast the detector's color pulses. 12 | @export_range(0.1, 1.0) var pulse_frequency := 0.25 13 | 14 | @export var sprite: Sprite2D 15 | 16 | var object_count := 0 17 | 18 | var pulse_progress := 0.0 19 | 20 | 21 | func _init() -> void: 22 | pulse_progress = randf() 23 | 24 | 25 | func _process(delta: float) -> void: 26 | if object_count == 0: 27 | pulse_progress += delta * pulse_frequency 28 | if pulse_progress >= 1.0: 29 | pulse_progress -= 1.0 30 | 31 | var brightness := cos(pulse_progress * TAU) * 0.25 + 0.75 32 | sprite.modulate = Color( 33 | invalid_color.r * brightness, 34 | invalid_color.g * brightness, 35 | invalid_color.b * brightness 36 | ) 37 | 38 | 39 | func _on_body_entered(_body: Node2D) -> void: 40 | if object_count == 0: 41 | sprite.modulate = valid_color 42 | validity_changed.emit(true) 43 | object_count += 1 44 | 45 | 46 | func _on_body_exited(_body: Node2D) -> void: 47 | object_count -= 1 48 | if object_count == 0: 49 | pulse_progress = 0.0 50 | validity_changed.emit(false) 51 | -------------------------------------------------------------------------------- /10-extra-map-info/detector.gd: -------------------------------------------------------------------------------- 1 | class_name Detector 2 | extends Area2D 3 | 4 | signal validity_changed(valid: bool) 5 | 6 | ## Color for when no objects are on the detector. 7 | @export_color_no_alpha var invalid_color := Color.RED 8 | ## Color for when objects are on the detector. 9 | @export_color_no_alpha var valid_color := Color.GREEN 10 | 11 | ## How fast the detector's color pulses. 12 | @export_range(0.1, 1.0) var pulse_frequency := 0.25 13 | 14 | @export var sprite: Sprite2D 15 | 16 | var object_count := 0 17 | 18 | var pulse_progress := 0.0 19 | 20 | 21 | func _init() -> void: 22 | pulse_progress = randf() 23 | 24 | 25 | func _process(delta: float) -> void: 26 | if object_count == 0: 27 | pulse_progress += delta * pulse_frequency 28 | if pulse_progress >= 1.0: 29 | pulse_progress -= 1.0 30 | 31 | var brightness := cos(pulse_progress * TAU) * 0.25 + 0.75 32 | sprite.modulate = Color( 33 | invalid_color.r * brightness, 34 | invalid_color.g * brightness, 35 | invalid_color.b * brightness 36 | ) 37 | 38 | 39 | func _on_body_entered(_body: Node2D) -> void: 40 | if object_count == 0: 41 | sprite.modulate = valid_color 42 | validity_changed.emit(true) 43 | object_count += 1 44 | 45 | 46 | func _on_body_exited(_body: Node2D) -> void: 47 | object_count -= 1 48 | if object_count == 0: 49 | pulse_progress = 0.0 50 | validity_changed.emit(false) 51 | -------------------------------------------------------------------------------- /7-map-transitions/detector.gd: -------------------------------------------------------------------------------- 1 | class_name Detector 2 | extends Area2D 3 | 4 | signal validity_changed(valid: bool) 5 | 6 | ## Color for when no objects are on the detector. 7 | @export_color_no_alpha var invalid_color := Color.RED 8 | ## Color for when objects are on the detector. 9 | @export_color_no_alpha var valid_color := Color.GREEN 10 | 11 | ## How fast the detector's color pulses. 12 | @export_range(0.1, 1.0) var pulse_frequency := 0.25 13 | 14 | @export var sprite: Sprite2D 15 | 16 | var object_count := 0 17 | 18 | var pulse_progress := 0.0 19 | 20 | 21 | func _init() -> void: 22 | pulse_progress = randf() 23 | 24 | 25 | func _process(delta: float) -> void: 26 | if object_count == 0: 27 | pulse_progress += delta * pulse_frequency 28 | if pulse_progress >= 1.0: 29 | pulse_progress -= 1.0 30 | 31 | var brightness := cos(pulse_progress * TAU) * 0.25 + 0.75 32 | sprite.modulate = Color( 33 | invalid_color.r * brightness, 34 | invalid_color.g * brightness, 35 | invalid_color.b * brightness 36 | ) 37 | 38 | 39 | func _on_body_entered(_body: Node2D) -> void: 40 | if object_count == 0: 41 | sprite.modulate = valid_color 42 | validity_changed.emit(true) 43 | object_count += 1 44 | 45 | 46 | func _on_body_exited(_body: Node2D) -> void: 47 | object_count -= 1 48 | if object_count == 0: 49 | pulse_progress = 0.0 50 | validity_changed.emit(false) 51 | -------------------------------------------------------------------------------- /8-saving-progress/detector.gd: -------------------------------------------------------------------------------- 1 | class_name Detector 2 | extends Area2D 3 | 4 | signal validity_changed(valid: bool) 5 | 6 | ## Color for when no objects are on the detector. 7 | @export_color_no_alpha var invalid_color := Color.RED 8 | ## Color for when objects are on the detector. 9 | @export_color_no_alpha var valid_color := Color.GREEN 10 | 11 | ## How fast the detector's color pulses. 12 | @export_range(0.1, 1.0) var pulse_frequency := 0.25 13 | 14 | @export var sprite: Sprite2D 15 | 16 | var object_count := 0 17 | 18 | var pulse_progress := 0.0 19 | 20 | 21 | func _init() -> void: 22 | pulse_progress = randf() 23 | 24 | 25 | func _process(delta: float) -> void: 26 | if object_count == 0: 27 | pulse_progress += delta * pulse_frequency 28 | if pulse_progress >= 1.0: 29 | pulse_progress -= 1.0 30 | 31 | var brightness := cos(pulse_progress * TAU) * 0.25 + 0.75 32 | sprite.modulate = Color( 33 | invalid_color.r * brightness, 34 | invalid_color.g * brightness, 35 | invalid_color.b * brightness 36 | ) 37 | 38 | 39 | func _on_body_entered(_body: Node2D) -> void: 40 | if object_count == 0: 41 | sprite.modulate = valid_color 42 | validity_changed.emit(true) 43 | object_count += 1 44 | 45 | 46 | func _on_body_exited(_body: Node2D) -> void: 47 | object_count -= 1 48 | if object_count == 0: 49 | pulse_progress = 0.0 50 | validity_changed.emit(false) 51 | -------------------------------------------------------------------------------- /11-showing-best-scores/detector.gd: -------------------------------------------------------------------------------- 1 | class_name Detector 2 | extends Area2D 3 | 4 | signal validity_changed(valid: bool) 5 | 6 | ## Color for when no objects are on the detector. 7 | @export_color_no_alpha var invalid_color := Color.RED 8 | ## Color for when objects are on the detector. 9 | @export_color_no_alpha var valid_color := Color.GREEN 10 | 11 | ## How fast the detector's color pulses. 12 | @export_range(0.1, 1.0) var pulse_frequency := 0.25 13 | 14 | @export var sprite: Sprite2D 15 | 16 | var object_count := 0 17 | 18 | var pulse_progress := 0.0 19 | 20 | 21 | func _init() -> void: 22 | pulse_progress = randf() 23 | 24 | 25 | func _process(delta: float) -> void: 26 | if object_count == 0: 27 | pulse_progress += delta * pulse_frequency 28 | if pulse_progress >= 1.0: 29 | pulse_progress -= 1.0 30 | 31 | var brightness := cos(pulse_progress * TAU) * 0.25 + 0.75 32 | sprite.modulate = Color( 33 | invalid_color.r * brightness, 34 | invalid_color.g * brightness, 35 | invalid_color.b * brightness 36 | ) 37 | 38 | 39 | func _on_body_entered(_body: Node2D) -> void: 40 | if object_count == 0: 41 | sprite.modulate = valid_color 42 | validity_changed.emit(true) 43 | object_count += 1 44 | 45 | 46 | func _on_body_exited(_body: Node2D) -> void: 47 | object_count -= 1 48 | if object_count == 0: 49 | pulse_progress = 0.0 50 | validity_changed.emit(false) 51 | -------------------------------------------------------------------------------- /2-player-character/tile_set.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="TileSet" load_steps=3 format=3 uid="uid://ct1sg2j0vfyxf"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://brq3awcnk4gep" path="res://tile_set.png" id="1_yp80t"] 4 | 5 | [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_6asb7"] 6 | texture = ExtResource("1_yp80t") 7 | 0:0/animation_columns = 2 8 | 0:0/0 = 0 9 | 0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 10 | 1:0/0 = 0 11 | 1:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 12 | 2:0/0 = 0 13 | 2:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 14 | 0:1/0 = 0 15 | 1:1/0 = 0 16 | 2:1/0 = 0 17 | 0:2/0 = 0 18 | 1:2/0 = 0 19 | 2:2/0 = 0 20 | 0:3/0 = 0 21 | 1:3/0 = 0 22 | 2:3/0 = 0 23 | 0:4/0 = 0 24 | 1:4/0 = 0 25 | 2:4/0 = 0 26 | 0:5/animation_mode = 1 27 | 0:5/animation_frame_0/duration = 1.0 28 | 0:5/animation_frame_1/duration = 1.0 29 | 0:5/0 = 0 30 | 0:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 31 | 0:6/animation_mode = 1 32 | 0:6/animation_frame_0/duration = 1.0 33 | 0:6/animation_frame_1/duration = 1.0 34 | 0:6/0 = 0 35 | 0:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 36 | 37 | [resource] 38 | physics_layer_0/collision_layer = 1 39 | sources/0 = SubResource("TileSetAtlasSource_6asb7") 40 | -------------------------------------------------------------------------------- /3-movable-objects/tile_set.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="TileSet" load_steps=3 format=3 uid="uid://ct1sg2j0vfyxf"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://brq3awcnk4gep" path="res://tile_set.png" id="1_yp80t"] 4 | 5 | [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_6asb7"] 6 | texture = ExtResource("1_yp80t") 7 | 0:0/animation_columns = 2 8 | 0:0/0 = 0 9 | 0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 10 | 1:0/0 = 0 11 | 1:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 12 | 2:0/0 = 0 13 | 2:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 14 | 0:1/0 = 0 15 | 1:1/0 = 0 16 | 2:1/0 = 0 17 | 0:2/0 = 0 18 | 1:2/0 = 0 19 | 2:2/0 = 0 20 | 0:3/0 = 0 21 | 1:3/0 = 0 22 | 2:3/0 = 0 23 | 0:4/0 = 0 24 | 1:4/0 = 0 25 | 2:4/0 = 0 26 | 0:5/animation_mode = 1 27 | 0:5/animation_frame_0/duration = 1.0 28 | 0:5/animation_frame_1/duration = 1.0 29 | 0:5/0 = 0 30 | 0:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 31 | 0:6/animation_mode = 1 32 | 0:6/animation_frame_0/duration = 1.0 33 | 0:6/animation_frame_1/duration = 1.0 34 | 0:6/0 = 0 35 | 0:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 36 | 37 | [resource] 38 | physics_layer_0/collision_layer = 1 39 | sources/0 = SubResource("TileSetAtlasSource_6asb7") 40 | -------------------------------------------------------------------------------- /5-detectors/detector.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://by76vb4rt7pue"] 2 | 3 | [ext_resource type="Script" path="res://detector.gd" id="1_nqra2"] 4 | 5 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_wgkqt"] 6 | size = Vector2(12, 12) 7 | 8 | [sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_q0aho"] 9 | light_mode = 1 10 | 11 | [sub_resource type="Gradient" id="Gradient_qyhe4"] 12 | offsets = PackedFloat32Array(0.571429, 1) 13 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 14 | 15 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_1jt0t"] 16 | gradient = SubResource("Gradient_qyhe4") 17 | width = 12 18 | height = 12 19 | fill = 2 20 | fill_from = Vector2(0.5, 0.5) 21 | fill_to = Vector2(1, 0.5) 22 | 23 | [node name="Detector" type="Area2D" node_paths=PackedStringArray("sprite")] 24 | collision_mask = 2 25 | monitorable = false 26 | script = ExtResource("1_nqra2") 27 | sprite = NodePath("Sprite2D") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_wgkqt") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | modulate = Color(1, 0, 0, 1) 34 | texture_filter = 1 35 | material = SubResource("CanvasItemMaterial_q0aho") 36 | texture = SubResource("GradientTexture2D_1jt0t") 37 | 38 | [connection signal="body_entered" from="." to="." method="_on_body_entered"] 39 | [connection signal="body_exited" from="." to="." method="_on_body_exited"] 40 | -------------------------------------------------------------------------------- /6-multiple-maps/detector.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://by76vb4rt7pue"] 2 | 3 | [ext_resource type="Script" path="res://detector.gd" id="1_nqra2"] 4 | 5 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_wgkqt"] 6 | size = Vector2(12, 12) 7 | 8 | [sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_q0aho"] 9 | light_mode = 1 10 | 11 | [sub_resource type="Gradient" id="Gradient_qyhe4"] 12 | offsets = PackedFloat32Array(0.571429, 1) 13 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 14 | 15 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_1jt0t"] 16 | gradient = SubResource("Gradient_qyhe4") 17 | width = 12 18 | height = 12 19 | fill = 2 20 | fill_from = Vector2(0.5, 0.5) 21 | fill_to = Vector2(1, 0.5) 22 | 23 | [node name="Detector" type="Area2D" node_paths=PackedStringArray("sprite")] 24 | collision_mask = 2 25 | monitorable = false 26 | script = ExtResource("1_nqra2") 27 | sprite = NodePath("Sprite2D") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_wgkqt") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | modulate = Color(1, 0, 0, 1) 34 | texture_filter = 1 35 | material = SubResource("CanvasItemMaterial_q0aho") 36 | texture = SubResource("GradientTexture2D_1jt0t") 37 | 38 | [connection signal="body_entered" from="." to="." method="_on_body_entered"] 39 | [connection signal="body_exited" from="." to="." method="_on_body_exited"] 40 | -------------------------------------------------------------------------------- /7-map-transitions/detector.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://by76vb4rt7pue"] 2 | 3 | [ext_resource type="Script" path="res://detector.gd" id="1_nqra2"] 4 | 5 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_wgkqt"] 6 | size = Vector2(12, 12) 7 | 8 | [sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_q0aho"] 9 | light_mode = 1 10 | 11 | [sub_resource type="Gradient" id="Gradient_qyhe4"] 12 | offsets = PackedFloat32Array(0.571429, 1) 13 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 14 | 15 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_1jt0t"] 16 | gradient = SubResource("Gradient_qyhe4") 17 | width = 12 18 | height = 12 19 | fill = 2 20 | fill_from = Vector2(0.5, 0.5) 21 | fill_to = Vector2(1, 0.5) 22 | 23 | [node name="Detector" type="Area2D" node_paths=PackedStringArray("sprite")] 24 | collision_mask = 2 25 | monitorable = false 26 | script = ExtResource("1_nqra2") 27 | sprite = NodePath("Sprite2D") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_wgkqt") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | modulate = Color(1, 0, 0, 1) 34 | texture_filter = 1 35 | material = SubResource("CanvasItemMaterial_q0aho") 36 | texture = SubResource("GradientTexture2D_1jt0t") 37 | 38 | [connection signal="body_entered" from="." to="." method="_on_body_entered"] 39 | [connection signal="body_exited" from="." to="." method="_on_body_exited"] 40 | -------------------------------------------------------------------------------- /9-game-hud/detector.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://by76vb4rt7pue"] 2 | 3 | [ext_resource type="Script" uid="uid://cva53g77vgubi" path="res://detector.gd" id="1_nqra2"] 4 | 5 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_wgkqt"] 6 | size = Vector2(12, 12) 7 | 8 | [sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_q0aho"] 9 | light_mode = 1 10 | 11 | [sub_resource type="Gradient" id="Gradient_qyhe4"] 12 | offsets = PackedFloat32Array(0.571429, 1) 13 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 14 | 15 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_1jt0t"] 16 | gradient = SubResource("Gradient_qyhe4") 17 | width = 12 18 | height = 12 19 | fill = 2 20 | fill_from = Vector2(0.5, 0.5) 21 | fill_to = Vector2(1, 0.5) 22 | 23 | [node name="Detector" type="Area2D" node_paths=PackedStringArray("sprite")] 24 | collision_mask = 2 25 | monitorable = false 26 | script = ExtResource("1_nqra2") 27 | sprite = NodePath("Sprite2D") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_wgkqt") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | modulate = Color(1, 0, 0, 1) 34 | texture_filter = 1 35 | material = SubResource("CanvasItemMaterial_q0aho") 36 | texture = SubResource("GradientTexture2D_1jt0t") 37 | 38 | [connection signal="body_entered" from="." to="." method="_on_body_entered"] 39 | [connection signal="body_exited" from="." to="." method="_on_body_exited"] 40 | -------------------------------------------------------------------------------- /12-teleporters/detector.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://by76vb4rt7pue"] 2 | 3 | [ext_resource type="Script" uid="uid://cva53g77vgubi" path="res://detector.gd" id="1_nqra2"] 4 | 5 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_wgkqt"] 6 | size = Vector2(12, 12) 7 | 8 | [sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_q0aho"] 9 | light_mode = 1 10 | 11 | [sub_resource type="Gradient" id="Gradient_qyhe4"] 12 | offsets = PackedFloat32Array(0.571429, 1) 13 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 14 | 15 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_1jt0t"] 16 | gradient = SubResource("Gradient_qyhe4") 17 | width = 12 18 | height = 12 19 | fill = 2 20 | fill_from = Vector2(0.5, 0.5) 21 | fill_to = Vector2(1, 0.5) 22 | 23 | [node name="Detector" type="Area2D" node_paths=PackedStringArray("sprite")] 24 | collision_mask = 2 25 | monitorable = false 26 | script = ExtResource("1_nqra2") 27 | sprite = NodePath("Sprite2D") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_wgkqt") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | modulate = Color(1, 0, 0, 1) 34 | texture_filter = 1 35 | material = SubResource("CanvasItemMaterial_q0aho") 36 | texture = SubResource("GradientTexture2D_1jt0t") 37 | 38 | [connection signal="body_entered" from="." to="." method="_on_body_entered"] 39 | [connection signal="body_exited" from="." to="." method="_on_body_exited"] 40 | -------------------------------------------------------------------------------- /10-extra-map-info/detector.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://by76vb4rt7pue"] 2 | 3 | [ext_resource type="Script" uid="uid://cva53g77vgubi" path="res://detector.gd" id="1_nqra2"] 4 | 5 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_wgkqt"] 6 | size = Vector2(12, 12) 7 | 8 | [sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_q0aho"] 9 | light_mode = 1 10 | 11 | [sub_resource type="Gradient" id="Gradient_qyhe4"] 12 | offsets = PackedFloat32Array(0.571429, 1) 13 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 14 | 15 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_1jt0t"] 16 | gradient = SubResource("Gradient_qyhe4") 17 | width = 12 18 | height = 12 19 | fill = 2 20 | fill_from = Vector2(0.5, 0.5) 21 | fill_to = Vector2(1, 0.5) 22 | 23 | [node name="Detector" type="Area2D" node_paths=PackedStringArray("sprite")] 24 | collision_mask = 2 25 | monitorable = false 26 | script = ExtResource("1_nqra2") 27 | sprite = NodePath("Sprite2D") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_wgkqt") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | modulate = Color(1, 0, 0, 1) 34 | texture_filter = 1 35 | material = SubResource("CanvasItemMaterial_q0aho") 36 | texture = SubResource("GradientTexture2D_1jt0t") 37 | 38 | [connection signal="body_entered" from="." to="." method="_on_body_entered"] 39 | [connection signal="body_exited" from="." to="." method="_on_body_exited"] 40 | -------------------------------------------------------------------------------- /8-saving-progress/detector.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://by76vb4rt7pue"] 2 | 3 | [ext_resource type="Script" uid="uid://cva53g77vgubi" path="res://detector.gd" id="1_nqra2"] 4 | 5 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_wgkqt"] 6 | size = Vector2(12, 12) 7 | 8 | [sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_q0aho"] 9 | light_mode = 1 10 | 11 | [sub_resource type="Gradient" id="Gradient_qyhe4"] 12 | offsets = PackedFloat32Array(0.571429, 1) 13 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 14 | 15 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_1jt0t"] 16 | gradient = SubResource("Gradient_qyhe4") 17 | width = 12 18 | height = 12 19 | fill = 2 20 | fill_from = Vector2(0.5, 0.5) 21 | fill_to = Vector2(1, 0.5) 22 | 23 | [node name="Detector" type="Area2D" node_paths=PackedStringArray("sprite")] 24 | collision_mask = 2 25 | monitorable = false 26 | script = ExtResource("1_nqra2") 27 | sprite = NodePath("Sprite2D") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_wgkqt") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | modulate = Color(1, 0, 0, 1) 34 | texture_filter = 1 35 | material = SubResource("CanvasItemMaterial_q0aho") 36 | texture = SubResource("GradientTexture2D_1jt0t") 37 | 38 | [connection signal="body_entered" from="." to="." method="_on_body_entered"] 39 | [connection signal="body_exited" from="." to="." method="_on_body_exited"] 40 | -------------------------------------------------------------------------------- /11-showing-best-scores/detector.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://by76vb4rt7pue"] 2 | 3 | [ext_resource type="Script" uid="uid://cva53g77vgubi" path="res://detector.gd" id="1_nqra2"] 4 | 5 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_wgkqt"] 6 | size = Vector2(12, 12) 7 | 8 | [sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_q0aho"] 9 | light_mode = 1 10 | 11 | [sub_resource type="Gradient" id="Gradient_qyhe4"] 12 | offsets = PackedFloat32Array(0.571429, 1) 13 | colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1) 14 | 15 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_1jt0t"] 16 | gradient = SubResource("Gradient_qyhe4") 17 | width = 12 18 | height = 12 19 | fill = 2 20 | fill_from = Vector2(0.5, 0.5) 21 | fill_to = Vector2(1, 0.5) 22 | 23 | [node name="Detector" type="Area2D" node_paths=PackedStringArray("sprite")] 24 | collision_mask = 2 25 | monitorable = false 26 | script = ExtResource("1_nqra2") 27 | sprite = NodePath("Sprite2D") 28 | 29 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 30 | shape = SubResource("RectangleShape2D_wgkqt") 31 | 32 | [node name="Sprite2D" type="Sprite2D" parent="."] 33 | modulate = Color(1, 0, 0, 1) 34 | texture_filter = 1 35 | material = SubResource("CanvasItemMaterial_q0aho") 36 | texture = SubResource("GradientTexture2D_1jt0t") 37 | 38 | [connection signal="body_entered" from="." to="." method="_on_body_entered"] 39 | [connection signal="body_exited" from="." to="." method="_on_body_exited"] 40 | -------------------------------------------------------------------------------- /10-extra-map-info/hud.gd: -------------------------------------------------------------------------------- 1 | extends CanvasLayer 2 | 3 | @export var map_name_label: Label 4 | @export var map_time_label: Label 5 | @export var map_detector_count_label: Label 6 | @export var map_travel_distance: Label 7 | @export var new_best_map_scores_label: Label 8 | 9 | 10 | func _ready() -> void: 11 | visible = false 12 | new_best_map_scores_label.visible = false 13 | 14 | 15 | func show_map_info(map: GameMap) -> void: 16 | map_name_label.text = map.map_name 17 | show_map_time(0.0) 18 | visible = true 19 | new_best_map_scores_label.visible = false 20 | 21 | 22 | func show_map_time(time: float) -> void: 23 | map_time_label.text = "%ds" % time 24 | 25 | 26 | ## Show new best scores, supply -1 to indicate no new best score. 27 | func show_new_best_map_scores(time: int, travel_distance: int) -> void: 28 | var s: String 29 | if time >= 0: 30 | if travel_distance >= 0: 31 | s = ( 32 | "New best time: %ds\nNew best travel distance: %dpx" % 33 | [time, travel_distance] 34 | ) 35 | else: 36 | s = "New best time: %ds" % time 37 | elif travel_distance >= 0: 38 | s = "New best travel distance: %dpx" % travel_distance 39 | else: 40 | return 41 | 42 | new_best_map_scores_label.text = s 43 | new_best_map_scores_label.visible = true 44 | 45 | 46 | func show_map_detector_count(valid: int, total: int) -> void: 47 | map_detector_count_label.text = "%d / %d" % [valid, total] 48 | 49 | 50 | func show_travel_distance(distance: float) -> void: 51 | map_travel_distance.text = "%dpx" % distance 52 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [1.12.0] - 2025-11-29 9 | 10 | ### Added 11 | 12 | - *12-teleporters* 13 | 14 | ## [1.11.0] - 2025-10-24 15 | 16 | ### Added 17 | 18 | - *11-showing-best-scores* 19 | 20 | ## [1.10.0] - 2025-09-05 21 | 22 | ### Added 23 | 24 | - *10-extra-map-info* 25 | 26 | ## [1.9.0] - 2025-08-22 27 | 28 | ### Added 29 | 30 | - *9-game-hud* 31 | 32 | ## [1.8.0] - 2025-6-28 33 | 34 | ### Added 35 | 36 | - *8-saving-progress* 37 | 38 | ## [1.7.0] - 2025-04-30 39 | 40 | ### Added 41 | 42 | - *7-map-transitions* 43 | 44 | ## Fixed 45 | 46 | - Added missing void function types to *6-multiple-maps*. 47 | 48 | ## [1.6.0] - 2025-02-27 49 | 50 | ### Added 51 | 52 | - *6-multiple-maps* 53 | 54 | ## [1.5.0] - 2024-12-24 55 | 56 | ### Added 57 | 58 | - *5-detectors* 59 | 60 | ## [1.4.0] - 2024-10-24 61 | 62 | ### Added 63 | 64 | - *4-light-and-shadow* 65 | 66 | ## [1.3.0] - 2024-08-28 67 | 68 | ### Added 69 | 70 | - *3-movable-objects* 71 | 72 | ## [1.2.1] - 2024-08-21 73 | 74 | ### Changed 75 | 76 | - Upgraded to Godot 4.3. 77 | - Replaced `TileMap` with `TileMapLayer`. 78 | 79 | ## [1.2.0] - 2024-06-27 80 | 81 | ### Added 82 | 83 | - *2-player-character* 84 | 85 | ## [1.1.0] - 2024-04-30 86 | 87 | ### Added 88 | 89 | - *1-tile-map*. 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # True Top-Down 2D 2 | 3 | [This is a tutorial series](https://catlikecoding.com/godot/true-top-down-2d/) that goes through the process of creating a simple true top-down 2D game with Godot Engine 4. 4 | 5 | This series is aimed at beginners who are completely new to game development, but more advanced developers that are new to Godot will also find it useful. 6 | 7 | It is assumed that you went through the [Introduction series](https://catlikecoding.com/godot/introduction) or are familiar with Godot Engine 4. 8 | 9 | Each Individual tutorial has its own numbered folder. 10 | 11 | ## Tutorials 12 | 13 | 1. [Tile Map](https://catlikecoding.com/godot/true-top-down-2d/1-tile-map/) 14 | 2. [Player Character](https://catlikecoding.com/godot/true-top-down-2d/2-player-character/) 15 | 3. [Movable Objects](https://catlikecoding.com/godot/true-top-down-2d/3-movable-objects/) 16 | 4. [Light and Shadow](https://catlikecoding.com/godot/true-top-down-2d/4-light-and-shadow/) 17 | 5. [Detectors](https://catlikecoding.com/godot/true-top-down-2d/5-detectors/) 18 | 6. [Multiple Maps](https://catlikecoding.com/godot/true-top-down-2d/6-multiple-maps/) 19 | 7. [Map Transitions](https://catlikecoding.com/godot/true-top-down-2d/7-map-transitions/) 20 | 8. [Saving Progress](https://catlikecoding.com/godot/true-top-down-2d/8-saving-progress/) 21 | 9. [Game HUD](https://catlikecoding.com/godot/true-top-down-2d/9-game-hud/) 22 | 23 | This is a work in progress. More parts will be added in due time. 24 | 25 | ## Giving Credit 26 | 27 | If you want to credit me in your work you can do so by mentioning [Jasper Flick](https://catlikecoding.com/jasper-flick/) and/or [Catlike Coding](https://catlikecoding.com). 28 | -------------------------------------------------------------------------------- /5-detectors/tile_set.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="TileSet" load_steps=5 format=3 uid="uid://ct1sg2j0vfyxf"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://brq3awcnk4gep" path="res://tile_set.png" id="1_yp80t"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_7ogig"] 6 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 7 | 8 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_co04c"] 9 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 10 | 11 | [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_6asb7"] 12 | texture = ExtResource("1_yp80t") 13 | 0:0/animation_columns = 2 14 | 0:0/0 = 0 15 | 0:0/0/occlusion_layer_0/polygon = SubResource("OccluderPolygon2D_7ogig") 16 | 0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 17 | 1:0/0 = 0 18 | 1:0/0/occlusion_layer_0/polygon = SubResource("OccluderPolygon2D_co04c") 19 | 1:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 20 | 2:0/0 = 0 21 | 2:0/0/occlusion_layer_0/polygon = SubResource("OccluderPolygon2D_co04c") 22 | 2:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 23 | 0:1/0 = 0 24 | 1:1/0 = 0 25 | 2:1/0 = 0 26 | 0:2/0 = 0 27 | 1:2/0 = 0 28 | 2:2/0 = 0 29 | 0:3/0 = 0 30 | 1:3/0 = 0 31 | 2:3/0 = 0 32 | 0:4/0 = 0 33 | 1:4/0 = 0 34 | 2:4/0 = 0 35 | 0:5/animation_mode = 1 36 | 0:5/animation_frame_0/duration = 1.0 37 | 0:5/animation_frame_1/duration = 1.0 38 | 0:5/0 = 0 39 | 0:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 40 | 0:6/animation_mode = 1 41 | 0:6/animation_frame_0/duration = 1.0 42 | 0:6/animation_frame_1/duration = 1.0 43 | 0:6/0 = 0 44 | 0:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 45 | 46 | [resource] 47 | occlusion_layer_0/light_mask = 3 48 | physics_layer_0/collision_layer = 1 49 | sources/0 = SubResource("TileSetAtlasSource_6asb7") 50 | -------------------------------------------------------------------------------- /6-multiple-maps/tile_set.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="TileSet" load_steps=5 format=3 uid="uid://ct1sg2j0vfyxf"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://brq3awcnk4gep" path="res://tile_set.png" id="1_yp80t"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_7ogig"] 6 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 7 | 8 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_co04c"] 9 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 10 | 11 | [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_6asb7"] 12 | texture = ExtResource("1_yp80t") 13 | 0:0/animation_columns = 2 14 | 0:0/0 = 0 15 | 0:0/0/occlusion_layer_0/polygon = SubResource("OccluderPolygon2D_7ogig") 16 | 0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 17 | 1:0/0 = 0 18 | 1:0/0/occlusion_layer_0/polygon = SubResource("OccluderPolygon2D_co04c") 19 | 1:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 20 | 2:0/0 = 0 21 | 2:0/0/occlusion_layer_0/polygon = SubResource("OccluderPolygon2D_co04c") 22 | 2:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 23 | 0:1/0 = 0 24 | 1:1/0 = 0 25 | 2:1/0 = 0 26 | 0:2/0 = 0 27 | 1:2/0 = 0 28 | 2:2/0 = 0 29 | 0:3/0 = 0 30 | 1:3/0 = 0 31 | 2:3/0 = 0 32 | 0:4/0 = 0 33 | 1:4/0 = 0 34 | 2:4/0 = 0 35 | 0:5/animation_mode = 1 36 | 0:5/animation_frame_0/duration = 1.0 37 | 0:5/animation_frame_1/duration = 1.0 38 | 0:5/0 = 0 39 | 0:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 40 | 0:6/animation_mode = 1 41 | 0:6/animation_frame_0/duration = 1.0 42 | 0:6/animation_frame_1/duration = 1.0 43 | 0:6/0 = 0 44 | 0:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 45 | 46 | [resource] 47 | occlusion_layer_0/light_mask = 3 48 | physics_layer_0/collision_layer = 1 49 | sources/0 = SubResource("TileSetAtlasSource_6asb7") 50 | -------------------------------------------------------------------------------- /7-map-transitions/tile_set.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="TileSet" load_steps=5 format=3 uid="uid://ct1sg2j0vfyxf"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://brq3awcnk4gep" path="res://tile_set.png" id="1_yp80t"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_7ogig"] 6 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 7 | 8 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_co04c"] 9 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 10 | 11 | [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_6asb7"] 12 | texture = ExtResource("1_yp80t") 13 | 0:0/animation_columns = 2 14 | 0:0/0 = 0 15 | 0:0/0/occlusion_layer_0/polygon = SubResource("OccluderPolygon2D_7ogig") 16 | 0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 17 | 1:0/0 = 0 18 | 1:0/0/occlusion_layer_0/polygon = SubResource("OccluderPolygon2D_co04c") 19 | 1:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 20 | 2:0/0 = 0 21 | 2:0/0/occlusion_layer_0/polygon = SubResource("OccluderPolygon2D_co04c") 22 | 2:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 23 | 0:1/0 = 0 24 | 1:1/0 = 0 25 | 2:1/0 = 0 26 | 0:2/0 = 0 27 | 1:2/0 = 0 28 | 2:2/0 = 0 29 | 0:3/0 = 0 30 | 1:3/0 = 0 31 | 2:3/0 = 0 32 | 0:4/0 = 0 33 | 1:4/0 = 0 34 | 2:4/0 = 0 35 | 0:5/animation_mode = 1 36 | 0:5/animation_frame_0/duration = 1.0 37 | 0:5/animation_frame_1/duration = 1.0 38 | 0:5/0 = 0 39 | 0:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 40 | 0:6/animation_mode = 1 41 | 0:6/animation_frame_0/duration = 1.0 42 | 0:6/animation_frame_1/duration = 1.0 43 | 0:6/0 = 0 44 | 0:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 45 | 46 | [resource] 47 | occlusion_layer_0/light_mask = 3 48 | physics_layer_0/collision_layer = 1 49 | sources/0 = SubResource("TileSetAtlasSource_6asb7") 50 | -------------------------------------------------------------------------------- /4-light-and-shadow/tile_set.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="TileSet" load_steps=5 format=3 uid="uid://ct1sg2j0vfyxf"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://brq3awcnk4gep" path="res://tile_set.png" id="1_yp80t"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_7ogig"] 6 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 7 | 8 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_co04c"] 9 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 10 | 11 | [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_6asb7"] 12 | texture = ExtResource("1_yp80t") 13 | 0:0/animation_columns = 2 14 | 0:0/0 = 0 15 | 0:0/0/occlusion_layer_0/polygon = SubResource("OccluderPolygon2D_7ogig") 16 | 0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 17 | 1:0/0 = 0 18 | 1:0/0/occlusion_layer_0/polygon = SubResource("OccluderPolygon2D_co04c") 19 | 1:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 20 | 2:0/0 = 0 21 | 2:0/0/occlusion_layer_0/polygon = SubResource("OccluderPolygon2D_co04c") 22 | 2:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 23 | 0:1/0 = 0 24 | 1:1/0 = 0 25 | 2:1/0 = 0 26 | 0:2/0 = 0 27 | 1:2/0 = 0 28 | 2:2/0 = 0 29 | 0:3/0 = 0 30 | 1:3/0 = 0 31 | 2:3/0 = 0 32 | 0:4/0 = 0 33 | 1:4/0 = 0 34 | 2:4/0 = 0 35 | 0:5/animation_mode = 1 36 | 0:5/animation_frame_0/duration = 1.0 37 | 0:5/animation_frame_1/duration = 1.0 38 | 0:5/0 = 0 39 | 0:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 40 | 0:6/animation_mode = 1 41 | 0:6/animation_frame_0/duration = 1.0 42 | 0:6/animation_frame_1/duration = 1.0 43 | 0:6/0 = 0 44 | 0:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 45 | 46 | [resource] 47 | occlusion_layer_0/light_mask = 3 48 | physics_layer_0/collision_layer = 1 49 | sources/0 = SubResource("TileSetAtlasSource_6asb7") 50 | -------------------------------------------------------------------------------- /9-game-hud/main_menu.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://bk1rkxiuhfn52"] 2 | 3 | [ext_resource type="Script" uid="uid://b53qr6le0v04" path="res://main_menu.gd" id="1_06t4h"] 4 | [ext_resource type="PackedScene" uid="uid://df6uf8hq8mdjn" path="res://maps/map.001.tscn" id="2_7vrkn"] 5 | 6 | [node name="MainMenu" type="Control" node_paths=PackedStringArray("continue_button")] 7 | custom_minimum_size = Vector2(100, 80) 8 | layout_mode = 3 9 | anchors_preset = 8 10 | anchor_left = 0.5 11 | anchor_top = 0.5 12 | anchor_right = 0.5 13 | anchor_bottom = 0.5 14 | offset_left = -50.0 15 | offset_top = -40.0 16 | offset_right = 50.0 17 | offset_bottom = 40.0 18 | grow_horizontal = 2 19 | grow_vertical = 2 20 | script = ExtResource("1_06t4h") 21 | first_map = ExtResource("2_7vrkn") 22 | continue_button = NodePath("MenuButtons/ContinueButton") 23 | 24 | [node name="MenuButtons" type="Control" parent="."] 25 | custom_minimum_size = Vector2(100, 80) 26 | layout_mode = 1 27 | anchors_preset = 8 28 | anchor_left = 0.5 29 | anchor_top = 0.5 30 | anchor_right = 0.5 31 | anchor_bottom = 0.5 32 | offset_left = -50.0 33 | offset_top = -40.0 34 | offset_right = 50.0 35 | offset_bottom = 40.0 36 | grow_horizontal = 2 37 | grow_vertical = 2 38 | 39 | [node name="NewGameButton" type="Button" parent="MenuButtons"] 40 | layout_mode = 1 41 | anchors_preset = 10 42 | anchor_right = 1.0 43 | offset_bottom = 31.0 44 | grow_horizontal = 2 45 | text = "New Game" 46 | 47 | [node name="ContinueButton" type="Button" parent="MenuButtons"] 48 | layout_mode = 1 49 | anchors_preset = 12 50 | anchor_top = 1.0 51 | anchor_right = 1.0 52 | anchor_bottom = 1.0 53 | offset_top = -31.0 54 | grow_horizontal = 2 55 | grow_vertical = 0 56 | text = "Continue" 57 | 58 | [connection signal="pressed" from="MenuButtons/NewGameButton" to="." method="_on_new_game_button_pressed"] 59 | [connection signal="pressed" from="MenuButtons/ContinueButton" to="." method="_on_continue_button_pressed"] 60 | -------------------------------------------------------------------------------- /10-extra-map-info/main_menu.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://bk1rkxiuhfn52"] 2 | 3 | [ext_resource type="Script" uid="uid://b53qr6le0v04" path="res://main_menu.gd" id="1_06t4h"] 4 | [ext_resource type="PackedScene" uid="uid://df6uf8hq8mdjn" path="res://maps/map.001.tscn" id="2_7vrkn"] 5 | 6 | [node name="MainMenu" type="Control" node_paths=PackedStringArray("continue_button")] 7 | custom_minimum_size = Vector2(100, 80) 8 | layout_mode = 3 9 | anchors_preset = 8 10 | anchor_left = 0.5 11 | anchor_top = 0.5 12 | anchor_right = 0.5 13 | anchor_bottom = 0.5 14 | offset_left = -50.0 15 | offset_top = -40.0 16 | offset_right = 50.0 17 | offset_bottom = 40.0 18 | grow_horizontal = 2 19 | grow_vertical = 2 20 | script = ExtResource("1_06t4h") 21 | first_map = ExtResource("2_7vrkn") 22 | continue_button = NodePath("MenuButtons/ContinueButton") 23 | 24 | [node name="MenuButtons" type="Control" parent="."] 25 | custom_minimum_size = Vector2(100, 80) 26 | layout_mode = 1 27 | anchors_preset = 8 28 | anchor_left = 0.5 29 | anchor_top = 0.5 30 | anchor_right = 0.5 31 | anchor_bottom = 0.5 32 | offset_left = -50.0 33 | offset_top = -40.0 34 | offset_right = 50.0 35 | offset_bottom = 40.0 36 | grow_horizontal = 2 37 | grow_vertical = 2 38 | 39 | [node name="NewGameButton" type="Button" parent="MenuButtons"] 40 | layout_mode = 1 41 | anchors_preset = 10 42 | anchor_right = 1.0 43 | offset_bottom = 31.0 44 | grow_horizontal = 2 45 | text = "New Game" 46 | 47 | [node name="ContinueButton" type="Button" parent="MenuButtons"] 48 | layout_mode = 1 49 | anchors_preset = 12 50 | anchor_top = 1.0 51 | anchor_right = 1.0 52 | anchor_bottom = 1.0 53 | offset_top = -31.0 54 | grow_horizontal = 2 55 | grow_vertical = 0 56 | text = "Continue" 57 | 58 | [connection signal="pressed" from="MenuButtons/NewGameButton" to="." method="_on_new_game_button_pressed"] 59 | [connection signal="pressed" from="MenuButtons/ContinueButton" to="." method="_on_continue_button_pressed"] 60 | -------------------------------------------------------------------------------- /12-teleporters/main_menu.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://bk1rkxiuhfn52"] 2 | 3 | [ext_resource type="Script" uid="uid://b53qr6le0v04" path="res://main_menu.gd" id="1_06t4h"] 4 | [ext_resource type="PackedScene" uid="uid://df6uf8hq8mdjn" path="res://maps/map.001.tscn" id="2_7vrkn"] 5 | 6 | [node name="MainMenu" type="Control" node_paths=PackedStringArray("continue_button")] 7 | custom_minimum_size = Vector2(100, 80) 8 | layout_mode = 3 9 | anchors_preset = 8 10 | anchor_left = 0.5 11 | anchor_top = 0.5 12 | anchor_right = 0.5 13 | anchor_bottom = 0.5 14 | offset_left = -50.0 15 | offset_top = -40.0 16 | offset_right = 50.0 17 | offset_bottom = 40.0 18 | grow_horizontal = 2 19 | grow_vertical = 2 20 | script = ExtResource("1_06t4h") 21 | first_map = ExtResource("2_7vrkn") 22 | continue_button = NodePath("MenuButtons/ContinueButton") 23 | 24 | [node name="MenuButtons" type="Control" parent="."] 25 | custom_minimum_size = Vector2(100, 80) 26 | layout_mode = 1 27 | anchors_preset = 8 28 | anchor_left = 0.5 29 | anchor_top = 0.5 30 | anchor_right = 0.5 31 | anchor_bottom = 0.5 32 | offset_left = -50.0 33 | offset_top = -40.0 34 | offset_right = 50.0 35 | offset_bottom = 40.0 36 | grow_horizontal = 2 37 | grow_vertical = 2 38 | 39 | [node name="NewGameButton" type="Button" parent="MenuButtons"] 40 | layout_mode = 1 41 | anchors_preset = 10 42 | anchor_right = 1.0 43 | offset_bottom = 31.0 44 | grow_horizontal = 2 45 | text = "New Game" 46 | 47 | [node name="ContinueButton" type="Button" parent="MenuButtons"] 48 | layout_mode = 1 49 | anchors_preset = 12 50 | anchor_top = 1.0 51 | anchor_right = 1.0 52 | anchor_bottom = 1.0 53 | offset_top = -31.0 54 | grow_horizontal = 2 55 | grow_vertical = 0 56 | text = "Continue" 57 | 58 | [connection signal="pressed" from="MenuButtons/NewGameButton" to="." method="_on_new_game_button_pressed"] 59 | [connection signal="pressed" from="MenuButtons/ContinueButton" to="." method="_on_continue_button_pressed"] 60 | -------------------------------------------------------------------------------- /11-showing-best-scores/main_menu.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://bk1rkxiuhfn52"] 2 | 3 | [ext_resource type="Script" uid="uid://b53qr6le0v04" path="res://main_menu.gd" id="1_06t4h"] 4 | [ext_resource type="PackedScene" uid="uid://df6uf8hq8mdjn" path="res://maps/map.001.tscn" id="2_7vrkn"] 5 | 6 | [node name="MainMenu" type="Control" node_paths=PackedStringArray("continue_button")] 7 | custom_minimum_size = Vector2(100, 80) 8 | layout_mode = 3 9 | anchors_preset = 8 10 | anchor_left = 0.5 11 | anchor_top = 0.5 12 | anchor_right = 0.5 13 | anchor_bottom = 0.5 14 | offset_left = -50.0 15 | offset_top = -40.0 16 | offset_right = 50.0 17 | offset_bottom = 40.0 18 | grow_horizontal = 2 19 | grow_vertical = 2 20 | script = ExtResource("1_06t4h") 21 | first_map = ExtResource("2_7vrkn") 22 | continue_button = NodePath("MenuButtons/ContinueButton") 23 | 24 | [node name="MenuButtons" type="Control" parent="."] 25 | custom_minimum_size = Vector2(100, 80) 26 | layout_mode = 1 27 | anchors_preset = 8 28 | anchor_left = 0.5 29 | anchor_top = 0.5 30 | anchor_right = 0.5 31 | anchor_bottom = 0.5 32 | offset_left = -50.0 33 | offset_top = -40.0 34 | offset_right = 50.0 35 | offset_bottom = 40.0 36 | grow_horizontal = 2 37 | grow_vertical = 2 38 | 39 | [node name="NewGameButton" type="Button" parent="MenuButtons"] 40 | layout_mode = 1 41 | anchors_preset = 10 42 | anchor_right = 1.0 43 | offset_bottom = 31.0 44 | grow_horizontal = 2 45 | text = "New Game" 46 | 47 | [node name="ContinueButton" type="Button" parent="MenuButtons"] 48 | layout_mode = 1 49 | anchors_preset = 12 50 | anchor_top = 1.0 51 | anchor_right = 1.0 52 | anchor_bottom = 1.0 53 | offset_top = -31.0 54 | grow_horizontal = 2 55 | grow_vertical = 0 56 | text = "Continue" 57 | 58 | [connection signal="pressed" from="MenuButtons/NewGameButton" to="." method="_on_new_game_button_pressed"] 59 | [connection signal="pressed" from="MenuButtons/ContinueButton" to="." method="_on_continue_button_pressed"] 60 | -------------------------------------------------------------------------------- /9-game-hud/tile_set.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="TileSet" load_steps=5 format=3 uid="uid://ct1sg2j0vfyxf"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://brq3awcnk4gep" path="res://tile_set.png" id="1_yp80t"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_7ogig"] 6 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 7 | 8 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_co04c"] 9 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 10 | 11 | [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_6asb7"] 12 | texture = ExtResource("1_yp80t") 13 | 0:0/animation_columns = 2 14 | 0:0/0 = 0 15 | 0:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_7ogig") 16 | 0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 17 | 1:0/0 = 0 18 | 1:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_co04c") 19 | 1:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 20 | 2:0/0 = 0 21 | 2:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_co04c") 22 | 2:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 23 | 0:1/0 = 0 24 | 1:1/0 = 0 25 | 2:1/0 = 0 26 | 0:2/0 = 0 27 | 1:2/0 = 0 28 | 2:2/0 = 0 29 | 0:3/0 = 0 30 | 1:3/0 = 0 31 | 2:3/0 = 0 32 | 0:4/0 = 0 33 | 1:4/0 = 0 34 | 2:4/0 = 0 35 | 0:5/animation_mode = 1 36 | 0:5/animation_frame_0/duration = 1.0 37 | 0:5/animation_frame_1/duration = 1.0 38 | 0:5/0 = 0 39 | 0:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 40 | 0:6/animation_mode = 1 41 | 0:6/animation_frame_0/duration = 1.0 42 | 0:6/animation_frame_1/duration = 1.0 43 | 0:6/0 = 0 44 | 0:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 45 | 46 | [resource] 47 | occlusion_layer_0/light_mask = 3 48 | physics_layer_0/collision_layer = 1 49 | sources/0 = SubResource("TileSetAtlasSource_6asb7") 50 | -------------------------------------------------------------------------------- /10-extra-map-info/tile_set.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="TileSet" load_steps=5 format=3 uid="uid://ct1sg2j0vfyxf"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://brq3awcnk4gep" path="res://tile_set.png" id="1_yp80t"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_7ogig"] 6 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 7 | 8 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_co04c"] 9 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 10 | 11 | [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_6asb7"] 12 | texture = ExtResource("1_yp80t") 13 | 0:0/animation_columns = 2 14 | 0:0/0 = 0 15 | 0:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_7ogig") 16 | 0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 17 | 1:0/0 = 0 18 | 1:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_co04c") 19 | 1:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 20 | 2:0/0 = 0 21 | 2:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_co04c") 22 | 2:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 23 | 0:1/0 = 0 24 | 1:1/0 = 0 25 | 2:1/0 = 0 26 | 0:2/0 = 0 27 | 1:2/0 = 0 28 | 2:2/0 = 0 29 | 0:3/0 = 0 30 | 1:3/0 = 0 31 | 2:3/0 = 0 32 | 0:4/0 = 0 33 | 1:4/0 = 0 34 | 2:4/0 = 0 35 | 0:5/animation_mode = 1 36 | 0:5/animation_frame_0/duration = 1.0 37 | 0:5/animation_frame_1/duration = 1.0 38 | 0:5/0 = 0 39 | 0:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 40 | 0:6/animation_mode = 1 41 | 0:6/animation_frame_0/duration = 1.0 42 | 0:6/animation_frame_1/duration = 1.0 43 | 0:6/0 = 0 44 | 0:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 45 | 46 | [resource] 47 | occlusion_layer_0/light_mask = 3 48 | physics_layer_0/collision_layer = 1 49 | sources/0 = SubResource("TileSetAtlasSource_6asb7") 50 | -------------------------------------------------------------------------------- /12-teleporters/tile_set.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="TileSet" load_steps=5 format=3 uid="uid://ct1sg2j0vfyxf"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://brq3awcnk4gep" path="res://tile_set.png" id="1_yp80t"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_7ogig"] 6 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 7 | 8 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_co04c"] 9 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 10 | 11 | [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_6asb7"] 12 | texture = ExtResource("1_yp80t") 13 | 0:0/animation_columns = 2 14 | 0:0/0 = 0 15 | 0:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_7ogig") 16 | 0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 17 | 1:0/0 = 0 18 | 1:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_co04c") 19 | 1:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 20 | 2:0/0 = 0 21 | 2:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_co04c") 22 | 2:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 23 | 0:1/0 = 0 24 | 1:1/0 = 0 25 | 2:1/0 = 0 26 | 0:2/0 = 0 27 | 1:2/0 = 0 28 | 2:2/0 = 0 29 | 0:3/0 = 0 30 | 1:3/0 = 0 31 | 2:3/0 = 0 32 | 0:4/0 = 0 33 | 1:4/0 = 0 34 | 2:4/0 = 0 35 | 0:5/animation_mode = 1 36 | 0:5/animation_frame_0/duration = 1.0 37 | 0:5/animation_frame_1/duration = 1.0 38 | 0:5/0 = 0 39 | 0:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 40 | 0:6/animation_mode = 1 41 | 0:6/animation_frame_0/duration = 1.0 42 | 0:6/animation_frame_1/duration = 1.0 43 | 0:6/0 = 0 44 | 0:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 45 | 46 | [resource] 47 | occlusion_layer_0/light_mask = 3 48 | physics_layer_0/collision_layer = 1 49 | sources/0 = SubResource("TileSetAtlasSource_6asb7") 50 | -------------------------------------------------------------------------------- /8-saving-progress/main_menu.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://bk1rkxiuhfn52"] 2 | 3 | [ext_resource type="Script" uid="uid://b53qr6le0v04" path="res://main_menu.gd" id="1_06t4h"] 4 | [ext_resource type="PackedScene" uid="uid://df6uf8hq8mdjn" path="res://maps/map.001.tscn" id="2_7vrkn"] 5 | 6 | [node name="Main Menu" type="Control" node_paths=PackedStringArray("continue_button")] 7 | custom_minimum_size = Vector2(100, 80) 8 | layout_mode = 3 9 | anchors_preset = 8 10 | anchor_left = 0.5 11 | anchor_top = 0.5 12 | anchor_right = 0.5 13 | anchor_bottom = 0.5 14 | offset_left = -50.0 15 | offset_top = -40.0 16 | offset_right = 50.0 17 | offset_bottom = 40.0 18 | grow_horizontal = 2 19 | grow_vertical = 2 20 | script = ExtResource("1_06t4h") 21 | first_map = ExtResource("2_7vrkn") 22 | continue_button = NodePath("Menu Buttons/Continue Button") 23 | 24 | [node name="Menu Buttons" type="Control" parent="."] 25 | custom_minimum_size = Vector2(100, 80) 26 | layout_mode = 1 27 | anchors_preset = 8 28 | anchor_left = 0.5 29 | anchor_top = 0.5 30 | anchor_right = 0.5 31 | anchor_bottom = 0.5 32 | offset_left = -50.0 33 | offset_top = -40.0 34 | offset_right = 50.0 35 | offset_bottom = 40.0 36 | grow_horizontal = 2 37 | grow_vertical = 2 38 | 39 | [node name="New Game Button" type="Button" parent="Menu Buttons"] 40 | layout_mode = 1 41 | anchors_preset = 10 42 | anchor_right = 1.0 43 | offset_bottom = 31.0 44 | grow_horizontal = 2 45 | text = "New Game" 46 | 47 | [node name="Continue Button" type="Button" parent="Menu Buttons"] 48 | layout_mode = 1 49 | anchors_preset = 12 50 | anchor_top = 1.0 51 | anchor_right = 1.0 52 | anchor_bottom = 1.0 53 | offset_top = -31.0 54 | grow_horizontal = 2 55 | grow_vertical = 0 56 | text = "Continue" 57 | 58 | [connection signal="pressed" from="Menu Buttons/New Game Button" to="." method="_on_new_game_button_pressed"] 59 | [connection signal="pressed" from="Menu Buttons/Continue Button" to="." method="_on_continue_button_pressed"] 60 | -------------------------------------------------------------------------------- /8-saving-progress/tile_set.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="TileSet" load_steps=5 format=3 uid="uid://ct1sg2j0vfyxf"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://brq3awcnk4gep" path="res://tile_set.png" id="1_yp80t"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_7ogig"] 6 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 7 | 8 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_co04c"] 9 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 10 | 11 | [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_6asb7"] 12 | texture = ExtResource("1_yp80t") 13 | 0:0/animation_columns = 2 14 | 0:0/0 = 0 15 | 0:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_7ogig") 16 | 0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 17 | 1:0/0 = 0 18 | 1:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_co04c") 19 | 1:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 20 | 2:0/0 = 0 21 | 2:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_co04c") 22 | 2:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 23 | 0:1/0 = 0 24 | 1:1/0 = 0 25 | 2:1/0 = 0 26 | 0:2/0 = 0 27 | 1:2/0 = 0 28 | 2:2/0 = 0 29 | 0:3/0 = 0 30 | 1:3/0 = 0 31 | 2:3/0 = 0 32 | 0:4/0 = 0 33 | 1:4/0 = 0 34 | 2:4/0 = 0 35 | 0:5/animation_mode = 1 36 | 0:5/animation_frame_0/duration = 1.0 37 | 0:5/animation_frame_1/duration = 1.0 38 | 0:5/0 = 0 39 | 0:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 40 | 0:6/animation_mode = 1 41 | 0:6/animation_frame_0/duration = 1.0 42 | 0:6/animation_frame_1/duration = 1.0 43 | 0:6/0 = 0 44 | 0:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 45 | 46 | [resource] 47 | occlusion_layer_0/light_mask = 3 48 | physics_layer_0/collision_layer = 1 49 | sources/0 = SubResource("TileSetAtlasSource_6asb7") 50 | -------------------------------------------------------------------------------- /11-showing-best-scores/tile_set.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="TileSet" load_steps=5 format=3 uid="uid://ct1sg2j0vfyxf"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://brq3awcnk4gep" path="res://tile_set.png" id="1_yp80t"] 4 | 5 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_7ogig"] 6 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 7 | 8 | [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_co04c"] 9 | polygon = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 10 | 11 | [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_6asb7"] 12 | texture = ExtResource("1_yp80t") 13 | 0:0/animation_columns = 2 14 | 0:0/0 = 0 15 | 0:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_7ogig") 16 | 0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 17 | 1:0/0 = 0 18 | 1:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_co04c") 19 | 1:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 20 | 2:0/0 = 0 21 | 2:0/0/occlusion_layer_0/polygon_0/polygon = SubResource("OccluderPolygon2D_co04c") 22 | 2:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 23 | 0:1/0 = 0 24 | 1:1/0 = 0 25 | 2:1/0 = 0 26 | 0:2/0 = 0 27 | 1:2/0 = 0 28 | 2:2/0 = 0 29 | 0:3/0 = 0 30 | 1:3/0 = 0 31 | 2:3/0 = 0 32 | 0:4/0 = 0 33 | 1:4/0 = 0 34 | 2:4/0 = 0 35 | 0:5/animation_mode = 1 36 | 0:5/animation_frame_0/duration = 1.0 37 | 0:5/animation_frame_1/duration = 1.0 38 | 0:5/0 = 0 39 | 0:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 40 | 0:6/animation_mode = 1 41 | 0:6/animation_frame_0/duration = 1.0 42 | 0:6/animation_frame_1/duration = 1.0 43 | 0:6/0 = 0 44 | 0:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8) 45 | 46 | [resource] 47 | occlusion_layer_0/light_mask = 3 48 | physics_layer_0/collision_layer = 1 49 | sources/0 = SubResource("TileSetAtlasSource_6asb7") 50 | -------------------------------------------------------------------------------- /12-teleporters/hud.gd: -------------------------------------------------------------------------------- 1 | extends CanvasLayer 2 | 3 | @export var map_name_label: Label 4 | @export var map_time_label: Label 5 | @export var map_detector_count_label: Label 6 | @export var map_travel_distance: Label 7 | @export var new_best_map_scores_label: Label 8 | 9 | var map_time_text: String 10 | var map_travel_distance_text: String 11 | 12 | 13 | func _ready() -> void: 14 | visible = false 15 | new_best_map_scores_label.visible = false 16 | 17 | 18 | func show_map_info(map: GameMap) -> void: 19 | if map.has_best_time(): 20 | map_time_text = "%%ds (%ds)" % map.get_best_time() 21 | else: 22 | map_time_text = "%ds" 23 | 24 | if map.has_best_travel_distance(): 25 | map_travel_distance_text = ( 26 | "%%dpx (%dpx)" % map.get_best_travel_distance() 27 | ) 28 | else: 29 | map_travel_distance_text = "%dxp" 30 | 31 | map_name_label.text = map.map_name 32 | show_map_time(0.0) 33 | show_travel_distance(0.0) 34 | visible = true 35 | new_best_map_scores_label.visible = false 36 | 37 | 38 | func show_map_time(time: float) -> void: 39 | map_time_label.text = map_time_text % time 40 | 41 | 42 | ## Show new best scores, supply -1 to indicate no new best score. 43 | func show_new_best_map_scores(time: int, travel_distance: int) -> void: 44 | var s: String 45 | if time >= 0: 46 | if travel_distance >= 0: 47 | s = ( 48 | "New best time: %ds\nNew best travel distance: %dpx" % 49 | [time, travel_distance] 50 | ) 51 | else: 52 | s = "New best time: %ds" % time 53 | elif travel_distance >= 0: 54 | s = "New best travel distance: %dpx" % travel_distance 55 | else: 56 | return 57 | 58 | new_best_map_scores_label.text = s 59 | new_best_map_scores_label.visible = true 60 | 61 | 62 | func show_map_detector_count(valid: int, total: int) -> void: 63 | map_detector_count_label.text = "%d / %d" % [valid, total] 64 | 65 | 66 | func show_travel_distance(distance: float) -> void: 67 | map_travel_distance.text = map_travel_distance_text % distance 68 | -------------------------------------------------------------------------------- /11-showing-best-scores/hud.gd: -------------------------------------------------------------------------------- 1 | extends CanvasLayer 2 | 3 | @export var map_name_label: Label 4 | @export var map_time_label: Label 5 | @export var map_detector_count_label: Label 6 | @export var map_travel_distance: Label 7 | @export var new_best_map_scores_label: Label 8 | 9 | var map_time_text: String 10 | var map_travel_distance_text: String 11 | 12 | 13 | func _ready() -> void: 14 | visible = false 15 | new_best_map_scores_label.visible = false 16 | 17 | 18 | func show_map_info(map: GameMap) -> void: 19 | if map.has_best_time(): 20 | map_time_text = "%%ds (%ds)" % map.get_best_time() 21 | else: 22 | map_time_text = "%ds" 23 | 24 | if map.has_best_travel_distance(): 25 | map_travel_distance_text = ( 26 | "%%dpx (%dpx)" % map.get_best_travel_distance() 27 | ) 28 | else: 29 | map_travel_distance_text = "%dxp" 30 | 31 | map_name_label.text = map.map_name 32 | show_map_time(0.0) 33 | show_travel_distance(0.0) 34 | visible = true 35 | new_best_map_scores_label.visible = false 36 | 37 | 38 | func show_map_time(time: float) -> void: 39 | map_time_label.text = map_time_text % time 40 | 41 | 42 | ## Show new best scores, supply -1 to indicate no new best score. 43 | func show_new_best_map_scores(time: int, travel_distance: int) -> void: 44 | var s: String 45 | if time >= 0: 46 | if travel_distance >= 0: 47 | s = ( 48 | "New best time: %ds\nNew best travel distance: %dpx" % 49 | [time, travel_distance] 50 | ) 51 | else: 52 | s = "New best time: %ds" % time 53 | elif travel_distance >= 0: 54 | s = "New best travel distance: %dpx" % travel_distance 55 | else: 56 | return 57 | 58 | new_best_map_scores_label.text = s 59 | new_best_map_scores_label.visible = true 60 | 61 | 62 | func show_map_detector_count(valid: int, total: int) -> void: 63 | map_detector_count_label.text = "%d / %d" % [valid, total] 64 | 65 | 66 | func show_travel_distance(distance: float) -> void: 67 | map_travel_distance.text = map_travel_distance_text % distance 68 | -------------------------------------------------------------------------------- /10-extra-map-info/map.gd: -------------------------------------------------------------------------------- 1 | class_name GameMap 2 | extends Node 3 | 4 | const KEY_BEST_TIME := "best_time" 5 | const KEY_BEST_TRAVEL_DISTANCE := "best_travel_distance" 6 | 7 | @export var map_name := "Map" 8 | 9 | var player_character: PlayerCharacter 10 | var registered_detector_count := 0 11 | var valid_detector_count := 0 12 | var current_map_time := 0.0 13 | var map_save_data: Dictionary 14 | 15 | 16 | func _process(delta: float) -> void: 17 | current_map_time += delta 18 | HUD.show_map_time(current_map_time) 19 | 20 | 21 | func _ready() -> void: 22 | for child_node in get_children(): 23 | var detector := child_node as Detector 24 | if detector: 25 | registered_detector_count += 1 26 | detector.validity_changed.connect(_on_detector_validity_changed) 27 | else: 28 | var pc := child_node as PlayerCharacter 29 | if pc: 30 | player_character = pc 31 | 32 | Main.map_loaded(self) 33 | HUD.show_map_info(self) 34 | HUD.show_map_detector_count( 35 | valid_detector_count, registered_detector_count 36 | ) 37 | 38 | 39 | func _on_detector_validity_changed(valid: bool) -> void: 40 | if valid: 41 | valid_detector_count += 1 42 | if valid_detector_count == registered_detector_count: 43 | _update_best_scores() 44 | Main.load_next_map() 45 | else: 46 | valid_detector_count -= 1 47 | 48 | HUD.show_map_detector_count( 49 | valid_detector_count, registered_detector_count 50 | ) 51 | 52 | 53 | func _update_best_scores() -> void: 54 | var new_time := floori(current_map_time) 55 | if ( 56 | not map_save_data.has(KEY_BEST_TIME) 57 | or new_time < map_save_data[KEY_BEST_TIME] 58 | ): 59 | map_save_data[KEY_BEST_TIME] = new_time 60 | else: 61 | new_time = -1 62 | 63 | var new_travel_distance := floori(player_character.travel_distance) 64 | if ( 65 | not map_save_data.has(KEY_BEST_TRAVEL_DISTANCE) 66 | or new_travel_distance < map_save_data[KEY_BEST_TRAVEL_DISTANCE] 67 | ): 68 | map_save_data[KEY_BEST_TRAVEL_DISTANCE] = new_travel_distance 69 | else: 70 | new_travel_distance = -1 71 | 72 | HUD.show_new_best_map_scores(new_time, new_travel_distance) 73 | -------------------------------------------------------------------------------- /12-teleporters/hud.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://cw4q2habmqtk8"] 2 | 3 | [ext_resource type="Script" uid="uid://cv3i66vvgu2ra" path="res://hud.gd" id="1_37p78"] 4 | 5 | [node name="HUD" type="CanvasLayer" node_paths=PackedStringArray("map_name_label", "map_time_label", "map_detector_count_label", "map_travel_distance", "new_best_map_scores_label")] 6 | layer = 3 7 | script = ExtResource("1_37p78") 8 | map_name_label = NodePath("MapName") 9 | map_time_label = NodePath("MapTime") 10 | map_detector_count_label = NodePath("MapDetectorCount") 11 | map_travel_distance = NodePath("MapTravelDistance") 12 | new_best_map_scores_label = NodePath("NewBestMapScores") 13 | 14 | [node name="MapName" type="Label" parent="."] 15 | offset_left = 16.0 16 | offset_right = 76.0 17 | offset_bottom = 16.0 18 | theme_override_font_sizes/font_size = 11 19 | text = "Map Name" 20 | 21 | [node name="MapTime" type="Label" parent="."] 22 | offset_left = 324.0 23 | offset_right = 384.0 24 | offset_bottom = 16.0 25 | theme_override_font_sizes/font_size = 11 26 | text = "0s" 27 | horizontal_alignment = 2 28 | 29 | [node name="MapDetectorCount" type="Label" parent="."] 30 | anchors_preset = 2 31 | anchor_top = 1.0 32 | anchor_bottom = 1.0 33 | offset_left = 324.0 34 | offset_top = -16.0 35 | offset_right = 384.0 36 | grow_vertical = 0 37 | theme_override_font_sizes/font_size = 11 38 | text = "00 / 00" 39 | horizontal_alignment = 2 40 | 41 | [node name="MapTravelDistance" type="Label" parent="."] 42 | anchors_preset = 2 43 | anchor_top = 1.0 44 | anchor_bottom = 1.0 45 | offset_left = 16.0 46 | offset_top = -16.0 47 | offset_right = 76.0 48 | grow_vertical = 0 49 | theme_override_font_sizes/font_size = 11 50 | text = "0px" 51 | 52 | [node name="NewBestMapScores" type="Label" parent="."] 53 | anchors_preset = 8 54 | anchor_left = 0.5 55 | anchor_top = 0.5 56 | anchor_right = 0.5 57 | anchor_bottom = 0.5 58 | offset_left = -0.5 59 | offset_top = -11.5 60 | offset_right = 0.5 61 | offset_bottom = 11.5 62 | grow_horizontal = 2 63 | grow_vertical = 2 64 | text = "New Best Time 65 | New Best Travel Distance" 66 | horizontal_alignment = 1 67 | -------------------------------------------------------------------------------- /10-extra-map-info/hud.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://cw4q2habmqtk8"] 2 | 3 | [ext_resource type="Script" uid="uid://cv3i66vvgu2ra" path="res://hud.gd" id="1_37p78"] 4 | 5 | [node name="HUD" type="CanvasLayer" node_paths=PackedStringArray("map_name_label", "map_time_label", "map_detector_count_label", "map_travel_distance", "new_best_map_scores_label")] 6 | layer = 3 7 | script = ExtResource("1_37p78") 8 | map_name_label = NodePath("MapName") 9 | map_time_label = NodePath("MapTime") 10 | map_detector_count_label = NodePath("MapDetectorCount") 11 | map_travel_distance = NodePath("MapTravelDistance") 12 | new_best_map_scores_label = NodePath("NewBestMapScores") 13 | 14 | [node name="MapName" type="Label" parent="."] 15 | offset_left = 16.0 16 | offset_right = 76.0 17 | offset_bottom = 16.0 18 | theme_override_font_sizes/font_size = 11 19 | text = "Map Name" 20 | 21 | [node name="MapTime" type="Label" parent="."] 22 | offset_left = 324.0 23 | offset_right = 384.0 24 | offset_bottom = 16.0 25 | theme_override_font_sizes/font_size = 11 26 | text = "0s" 27 | horizontal_alignment = 2 28 | 29 | [node name="MapDetectorCount" type="Label" parent="."] 30 | anchors_preset = 2 31 | anchor_top = 1.0 32 | anchor_bottom = 1.0 33 | offset_left = 324.0 34 | offset_top = -16.0 35 | offset_right = 384.0 36 | grow_vertical = 0 37 | theme_override_font_sizes/font_size = 11 38 | text = "00 / 00" 39 | horizontal_alignment = 2 40 | 41 | [node name="MapTravelDistance" type="Label" parent="."] 42 | anchors_preset = 2 43 | anchor_top = 1.0 44 | anchor_bottom = 1.0 45 | offset_left = 16.0 46 | offset_top = -16.0 47 | offset_right = 76.0 48 | grow_vertical = 0 49 | theme_override_font_sizes/font_size = 11 50 | text = "0px" 51 | 52 | [node name="NewBestMapScores" type="Label" parent="."] 53 | anchors_preset = 8 54 | anchor_left = 0.5 55 | anchor_top = 0.5 56 | anchor_right = 0.5 57 | anchor_bottom = 0.5 58 | offset_left = -0.5 59 | offset_top = -11.5 60 | offset_right = 0.5 61 | offset_bottom = 11.5 62 | grow_horizontal = 2 63 | grow_vertical = 2 64 | text = "New Best Time 65 | New Best Travel Distance" 66 | horizontal_alignment = 1 67 | -------------------------------------------------------------------------------- /11-showing-best-scores/hud.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://cw4q2habmqtk8"] 2 | 3 | [ext_resource type="Script" uid="uid://cv3i66vvgu2ra" path="res://hud.gd" id="1_37p78"] 4 | 5 | [node name="HUD" type="CanvasLayer" node_paths=PackedStringArray("map_name_label", "map_time_label", "map_detector_count_label", "map_travel_distance", "new_best_map_scores_label")] 6 | layer = 3 7 | script = ExtResource("1_37p78") 8 | map_name_label = NodePath("MapName") 9 | map_time_label = NodePath("MapTime") 10 | map_detector_count_label = NodePath("MapDetectorCount") 11 | map_travel_distance = NodePath("MapTravelDistance") 12 | new_best_map_scores_label = NodePath("NewBestMapScores") 13 | 14 | [node name="MapName" type="Label" parent="."] 15 | offset_left = 16.0 16 | offset_right = 76.0 17 | offset_bottom = 16.0 18 | theme_override_font_sizes/font_size = 11 19 | text = "Map Name" 20 | 21 | [node name="MapTime" type="Label" parent="."] 22 | offset_left = 324.0 23 | offset_right = 384.0 24 | offset_bottom = 16.0 25 | theme_override_font_sizes/font_size = 11 26 | text = "0s" 27 | horizontal_alignment = 2 28 | 29 | [node name="MapDetectorCount" type="Label" parent="."] 30 | anchors_preset = 2 31 | anchor_top = 1.0 32 | anchor_bottom = 1.0 33 | offset_left = 324.0 34 | offset_top = -16.0 35 | offset_right = 384.0 36 | grow_vertical = 0 37 | theme_override_font_sizes/font_size = 11 38 | text = "00 / 00" 39 | horizontal_alignment = 2 40 | 41 | [node name="MapTravelDistance" type="Label" parent="."] 42 | anchors_preset = 2 43 | anchor_top = 1.0 44 | anchor_bottom = 1.0 45 | offset_left = 16.0 46 | offset_top = -16.0 47 | offset_right = 76.0 48 | grow_vertical = 0 49 | theme_override_font_sizes/font_size = 11 50 | text = "0px" 51 | 52 | [node name="NewBestMapScores" type="Label" parent="."] 53 | anchors_preset = 8 54 | anchor_left = 0.5 55 | anchor_top = 0.5 56 | anchor_right = 0.5 57 | anchor_bottom = 0.5 58 | offset_left = -0.5 59 | offset_top = -11.5 60 | offset_right = 0.5 61 | offset_bottom = 11.5 62 | grow_horizontal = 2 63 | grow_vertical = 2 64 | text = "New Best Time 65 | New Best Travel Distance" 66 | horizontal_alignment = 1 67 | -------------------------------------------------------------------------------- /8-saving-progress/main.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | const GAME_SAVE_PATH := "user://game.save" 4 | const KEY_MAP_PATH := "map_path" 5 | const GAME_SAVE_VERSION := 1 6 | const KEY_GAME_SAVE_VERSION := "version" 7 | const MAP_PATH_PREFIX := "res://maps/" 8 | 9 | var current_map_path: String 10 | 11 | 12 | func can_continue() -> bool: 13 | return FileAccess.file_exists(GAME_SAVE_PATH) 14 | 15 | 16 | func load_continue_map() -> void: 17 | if not can_continue(): 18 | print("Tried to continue without save file!") 19 | return 20 | 21 | var game_save_file := FileAccess.open(GAME_SAVE_PATH, FileAccess.READ) 22 | var game_save_data := game_save_file.get_var() as Dictionary 23 | if game_save_data[KEY_GAME_SAVE_VERSION] > GAME_SAVE_VERSION: 24 | print("Tried to load unknown future save file format!") 25 | return 26 | 27 | var map_path := game_save_data[KEY_MAP_PATH] as String 28 | if ( 29 | not map_path.begins_with(MAP_PATH_PREFIX) 30 | or map_path.contains("..") 31 | ): 32 | print("Invalid map path!") 33 | return 34 | load_map(map_path) 35 | 36 | 37 | func load_next_map() -> void: 38 | var split_path := current_map_path.split(".") 39 | var next_map_number := split_path[1].to_int() + 1 40 | split_path[1] = str(next_map_number).pad_zeros(3) 41 | var next_map_path = ".".join(split_path) 42 | 43 | if not ResourceLoader.exists(next_map_path): 44 | split_path[1] = "001" 45 | next_map_path = ".".join(split_path) 46 | load_map(next_map_path) 47 | 48 | 49 | func load_map(map_path: String) -> void: 50 | ResourceLoader.load_threaded_request(map_path) 51 | 52 | get_tree().paused = true 53 | await MapTransition.play_exit_map() 54 | 55 | get_tree().change_scene_to_packed( 56 | ResourceLoader.load_threaded_get(map_path) 57 | ) 58 | 59 | await MapTransition.play_enter_map() 60 | get_tree().paused = false 61 | 62 | 63 | func map_loaded(map_path: String) -> void: 64 | current_map_path = map_path 65 | 66 | var game_save_file := FileAccess.open(GAME_SAVE_PATH, FileAccess.WRITE) 67 | game_save_file.store_var({ 68 | KEY_MAP_PATH: map_path, 69 | KEY_GAME_SAVE_VERSION: GAME_SAVE_VERSION, 70 | }) 71 | -------------------------------------------------------------------------------- /12-teleporters/teleporters/teleporter_source.gd: -------------------------------------------------------------------------------- 1 | class_name TeleporterSource 2 | extends Area2D 3 | 4 | ## Color for when the teleporter is free. 5 | @export var valid_color := Color(Color.CYAN, 0.5) 6 | ## Color for when the teleporter is occupied. 7 | @export var invalid_color := Color(Color.MAGENTA, 0.5) 8 | 9 | @export var sprite: Sprite2D 10 | 11 | @export var line: Line2D 12 | 13 | @export var colissionShape: CollisionShape2D 14 | 15 | @export var particles: GPUParticles2D 16 | 17 | @export var destination: TeleporterDestination 18 | 19 | var is_active := true 20 | 21 | var objects: Array[InteractiveObject] = [] 22 | 23 | 24 | func _ready() -> void: 25 | if not destination: 26 | is_active = false 27 | sprite.modulate = invalid_color 28 | set_physics_process(false) 29 | else: 30 | destination.validity_changed.connect(_on_detector_validity_changed) 31 | sprite.modulate = valid_color 32 | destination.sprite.modulate = valid_color 33 | particles.modulate = valid_color 34 | destination.particles.modulate = valid_color 35 | line.modulate = valid_color 36 | line.add_point(Vector2.ZERO) 37 | line.add_point(destination.position - position) 38 | 39 | 40 | func _physics_process(_delta: float) -> void: 41 | if not is_active or objects.is_empty(): 42 | set_physics_process(false) 43 | return 44 | objects.is_empty() 45 | var own_rect := colissionShape.shape.get_rect() 46 | own_rect.position += position 47 | 48 | for object in objects: 49 | if own_rect.encloses(object.get_current_rect()): 50 | object.displace(destination.position - position) 51 | particles.emitting = true 52 | destination.particles.emitting = true 53 | 54 | 55 | func _on_detector_validity_changed(valid: bool) -> void: 56 | is_active = valid 57 | set_physics_process(valid) 58 | var color := valid_color if valid else invalid_color 59 | sprite.modulate = color 60 | destination.sprite.modulate = color 61 | line.modulate = color 62 | 63 | 64 | func _on_body_entered(body: Node2D) -> void: 65 | var object := body as InteractiveObject 66 | if object: 67 | objects.push_back(object) 68 | set_physics_process(true) 69 | 70 | 71 | func _on_body_exited(body: Node2D) -> void: 72 | var index_to_remove := objects.find(body as InteractiveObject) 73 | if index_to_remove >= 0: 74 | objects.remove_at(index_to_remove) 75 | -------------------------------------------------------------------------------- /12-teleporters/teleporters/teleporter_destination.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=9 format=3 uid="uid://da4l644boage6"] 2 | 3 | [ext_resource type="Script" uid="uid://dwu02xxnacil7" path="res://teleporters/teleporter_destination.gd" id="1_a8g44"] 4 | [ext_resource type="Texture2D" uid="uid://cpc3pnpy5ytd2" path="res://teleporters/teleporter_destination.png" id="2_jq1q4"] 5 | 6 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_wgkqt"] 7 | size = Vector2(16, 16) 8 | 9 | [sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_q0aho"] 10 | light_mode = 1 11 | 12 | [sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_qlgbt"] 13 | blend_mode = 1 14 | light_mode = 1 15 | 16 | [sub_resource type="Gradient" id="Gradient_1t2jy"] 17 | offsets = PackedFloat32Array(0, 0.5, 1) 18 | colors = PackedColorArray(1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1) 19 | 20 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_h7m5w"] 21 | gradient = SubResource("Gradient_1t2jy") 22 | width = 8 23 | height = 8 24 | fill = 1 25 | fill_from = Vector2(0.5, 0.5) 26 | fill_to = Vector2(1, 0.5) 27 | 28 | [sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_8soqw"] 29 | particle_flag_disable_z = true 30 | emission_shape = 3 31 | emission_box_extents = Vector3(6, 6, 0) 32 | gravity = Vector3(0, 0, 0) 33 | 34 | [node name="Teleporter Destination" type="Area2D" node_paths=PackedStringArray("sprite", "particles")] 35 | collision_mask = 3 36 | monitorable = false 37 | script = ExtResource("1_a8g44") 38 | sprite = NodePath("Sprite2D") 39 | particles = NodePath("GPUParticles2D") 40 | 41 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 42 | shape = SubResource("RectangleShape2D_wgkqt") 43 | 44 | [node name="Sprite2D" type="Sprite2D" parent="."] 45 | modulate = Color(1, 1, 1, 0.5019608) 46 | texture_filter = 1 47 | material = SubResource("CanvasItemMaterial_q0aho") 48 | texture = ExtResource("2_jq1q4") 49 | 50 | [node name="GPUParticles2D" type="GPUParticles2D" parent="."] 51 | material = SubResource("CanvasItemMaterial_qlgbt") 52 | emitting = false 53 | amount = 30 54 | texture = SubResource("GradientTexture2D_h7m5w") 55 | lifetime = 0.25 56 | one_shot = true 57 | fixed_fps = 60 58 | interpolate = false 59 | fract_delta = false 60 | process_material = SubResource("ParticleProcessMaterial_8soqw") 61 | 62 | [connection signal="body_entered" from="." to="." method="_on_body_entered"] 63 | [connection signal="body_exited" from="." to="." method="_on_body_exited"] 64 | -------------------------------------------------------------------------------- /12-teleporters/map.gd: -------------------------------------------------------------------------------- 1 | class_name GameMap 2 | extends Node 3 | 4 | const KEY_BEST_TIME := "best_time" 5 | const KEY_BEST_TRAVEL_DISTANCE := "best_travel_distance" 6 | 7 | @export var map_name := "Map" 8 | 9 | var player_character: PlayerCharacter 10 | var registered_detector_count := 0 11 | var valid_detector_count := 0 12 | var current_map_time := 0.0 13 | var map_save_data: Dictionary 14 | 15 | 16 | ## Only valid if [method has_best_time] returns true. 17 | func get_best_time() -> int: 18 | return map_save_data[KEY_BEST_TIME] 19 | 20 | 21 | func has_best_time() -> bool: 22 | return map_save_data.has(KEY_BEST_TIME) 23 | 24 | 25 | ## Only valid if [method has_best_travel_distance] returns true. 26 | func get_best_travel_distance() -> int: 27 | return map_save_data[KEY_BEST_TRAVEL_DISTANCE] 28 | 29 | 30 | func has_best_travel_distance() -> bool: 31 | return map_save_data.has(KEY_BEST_TRAVEL_DISTANCE) 32 | 33 | 34 | func _process(delta: float) -> void: 35 | current_map_time += delta 36 | HUD.show_map_time(current_map_time) 37 | 38 | 39 | func _ready() -> void: 40 | for child_node in get_children(): 41 | var detector := child_node as Detector 42 | if detector: 43 | registered_detector_count += 1 44 | detector.validity_changed.connect(_on_detector_validity_changed) 45 | else: 46 | var pc := child_node as PlayerCharacter 47 | if pc: 48 | player_character = pc 49 | 50 | Main.map_loaded(self) 51 | HUD.show_map_info(self) 52 | HUD.show_map_detector_count( 53 | valid_detector_count, registered_detector_count 54 | ) 55 | 56 | 57 | func _on_detector_validity_changed(valid: bool) -> void: 58 | if valid: 59 | valid_detector_count += 1 60 | if valid_detector_count == registered_detector_count: 61 | _update_best_scores() 62 | Main.load_next_map() 63 | else: 64 | valid_detector_count -= 1 65 | 66 | HUD.show_map_detector_count( 67 | valid_detector_count, registered_detector_count 68 | ) 69 | 70 | 71 | func _update_best_scores() -> void: 72 | var new_time := floori(current_map_time) 73 | if not has_best_time() or new_time < get_best_time(): 74 | map_save_data[KEY_BEST_TIME] = new_time 75 | else: 76 | new_time = -1 77 | 78 | var new_travel_distance := floori(player_character.travel_distance) 79 | if ( 80 | not has_best_travel_distance() 81 | or new_travel_distance < get_best_travel_distance() 82 | ): 83 | map_save_data[KEY_BEST_TRAVEL_DISTANCE] = new_travel_distance 84 | else: 85 | new_travel_distance = -1 86 | 87 | HUD.show_new_best_map_scores(new_time, new_travel_distance) 88 | -------------------------------------------------------------------------------- /11-showing-best-scores/map.gd: -------------------------------------------------------------------------------- 1 | class_name GameMap 2 | extends Node 3 | 4 | const KEY_BEST_TIME := "best_time" 5 | const KEY_BEST_TRAVEL_DISTANCE := "best_travel_distance" 6 | 7 | @export var map_name := "Map" 8 | 9 | var player_character: PlayerCharacter 10 | var registered_detector_count := 0 11 | var valid_detector_count := 0 12 | var current_map_time := 0.0 13 | var map_save_data: Dictionary 14 | 15 | 16 | ## Only valid if [method has_best_time] returns true. 17 | func get_best_time() -> int: 18 | return map_save_data[KEY_BEST_TIME] 19 | 20 | 21 | func has_best_time() -> bool: 22 | return map_save_data.has(KEY_BEST_TIME) 23 | 24 | 25 | ## Only valid if [method has_best_travel_distance] returns true. 26 | func get_best_travel_distance() -> int: 27 | return map_save_data[KEY_BEST_TRAVEL_DISTANCE] 28 | 29 | 30 | func has_best_travel_distance() -> bool: 31 | return map_save_data.has(KEY_BEST_TRAVEL_DISTANCE) 32 | 33 | 34 | func _process(delta: float) -> void: 35 | current_map_time += delta 36 | HUD.show_map_time(current_map_time) 37 | 38 | 39 | func _ready() -> void: 40 | for child_node in get_children(): 41 | var detector := child_node as Detector 42 | if detector: 43 | registered_detector_count += 1 44 | detector.validity_changed.connect(_on_detector_validity_changed) 45 | else: 46 | var pc := child_node as PlayerCharacter 47 | if pc: 48 | player_character = pc 49 | 50 | Main.map_loaded(self) 51 | HUD.show_map_info(self) 52 | HUD.show_map_detector_count( 53 | valid_detector_count, registered_detector_count 54 | ) 55 | 56 | 57 | func _on_detector_validity_changed(valid: bool) -> void: 58 | if valid: 59 | valid_detector_count += 1 60 | if valid_detector_count == registered_detector_count: 61 | _update_best_scores() 62 | Main.load_next_map() 63 | else: 64 | valid_detector_count -= 1 65 | 66 | HUD.show_map_detector_count( 67 | valid_detector_count, registered_detector_count 68 | ) 69 | 70 | 71 | func _update_best_scores() -> void: 72 | var new_time := floori(current_map_time) 73 | if not has_best_time() or new_time < get_best_time(): 74 | map_save_data[KEY_BEST_TIME] = new_time 75 | else: 76 | new_time = -1 77 | 78 | var new_travel_distance := floori(player_character.travel_distance) 79 | if ( 80 | not has_best_travel_distance() 81 | or new_travel_distance < get_best_travel_distance() 82 | ): 83 | map_save_data[KEY_BEST_TRAVEL_DISTANCE] = new_travel_distance 84 | else: 85 | new_travel_distance = -1 86 | 87 | HUD.show_new_best_map_scores(new_time, new_travel_distance) 88 | --------------------------------------------------------------------------------