From 4221882f581c3040c219ea90f9c0cdba75527f2a Mon Sep 17 00:00:00 2001 From: PetePete1984 Date: Mon, 30 Jul 2018 14:02:20 +0200 Subject: [PATCH] Update Space Shooter demo to 2.1.5 Major changes: - added Readme - included sensible directory structure - resizable window now uses stretching - main menu UI organized in containers - less reliance on get_parent() or get_node("..") calls - more usage of signals and groups - split the large level scene into enemies & tilemap scenes - moved the hud out of the player scene and into the main game scene - player can now shoot by holding the space bar Room for improvement: - the enemies use very similar code and could be generalized more --- 2d/space_shooter/README.md | 39 ++ .../{ => assets}/tileset_edit.tscn | 3 +- 2d/space_shooter/asteroid.gd | 43 --- .../{ => effects/background}/bg_gradient.png | Bin .../effects/background/bg_gradient.png.flags | 2 + .../{ => effects/background}/big_star.png | Bin .../effects/background/big_star.png.flags | 2 + .../{ => effects/background}/parallax.tscn | 8 +- .../{ => effects/background}/small_star.png | Bin .../effects/background/small_star.png.flags | 2 + .../{ => effects/particles}/explosion.tscn | 3 +- .../{ => effects/particles}/fire.png | Bin .../effects/particles/fire.png.flags | 2 + .../{ => effects/sounds}/sound_explode.wav | Bin .../{ => effects/sounds}/sound_shoot.wav | Bin 2d/space_shooter/enemies/asteroid/asteroid.gd | 59 +++ .../{ => enemies/asteroid}/asteroid.tscn | 55 ++- .../{ => enemies/asteroid}/meteorite.png | Bin .../enemies/asteroid/meteorite.png.flags | 2 + 2d/space_shooter/enemies/shooter/enemy2.gd | 77 ++++ .../{ => enemies/shooter}/enemy2.png | Bin .../enemies/shooter/enemy2.png.flags | 2 + .../{ => enemies/shooter}/enemy2.tscn | 52 ++- .../{ => enemies/shooter}/enemy_shot.gd | 13 +- .../{ => enemies/shooter}/enemy_shot.png | Bin .../enemies/shooter/enemy_shot.png.flags | 2 + .../{ => enemies/shooter}/enemy_shot.tscn | 45 ++- 2d/space_shooter/enemies/ufo/enemy1.gd | 41 ++ 2d/space_shooter/{ => enemies/ufo}/enemy1.png | Bin 2d/space_shooter/enemies/ufo/enemy1.png.flags | 2 + .../{ => enemies/ufo}/enemy1.tscn | 77 +++- 2d/space_shooter/enemies/ufo/enemy1_rail.gd | 33 ++ 2d/space_shooter/enemy1.gd | 36 -- 2d/space_shooter/enemy2.gd | 47 --- 2d/space_shooter/engine.cfg | 7 +- 2d/space_shooter/game_screen/hud/game_hud.gd | 25 ++ .../game_screen/hud/game_hud.tscn | 77 ++++ .../game_screen/level/enemies.tscn | 284 ++++++++++++++ 2d/space_shooter/game_screen/level/level.gd | 33 ++ 2d/space_shooter/game_screen/level/level.tscn | 29 ++ .../game_screen/level/level_map.gd | 12 + .../game_screen/level/level_map.tscn | 18 + .../{ => game_screen/level}/tile.png | Bin .../game_screen/level/tile.png.flags | 2 + .../game_screen/level/tilemap_level.tscn | 24 ++ .../{ => game_screen/level}/tileset.tres | 6 +- 2d/space_shooter/game_state.gd | 72 +++- 2d/space_shooter/level.tscn | 354 ------------------ 2d/space_shooter/main_menu.gd | 11 - 2d/space_shooter/main_menu.tscn | 64 ---- 2d/space_shooter/main_menu/main_menu.gd | 18 + 2d/space_shooter/main_menu/main_menu.tscn | 106 ++++++ 2d/space_shooter/main_menu/space.png | Bin 0 -> 67109 bytes 2d/space_shooter/main_menu/space.png.flags | 2 + 2d/space_shooter/player/player_ship.tscn | 176 +++++++++ .../player/player_ship_on_rail.tscn | 32 ++ 2d/space_shooter/player/rail.gd | 27 ++ 2d/space_shooter/player/ship.gd | 105 ++++++ 2d/space_shooter/{ => player}/ship.png | Bin 2d/space_shooter/player/ship.png.flags | 2 + 2d/space_shooter/{ => player}/shoot.png | Bin 2d/space_shooter/player/shoot.png.flags | 2 + 2d/space_shooter/{ => player}/shot.gd | 16 +- 2d/space_shooter/{ => player}/shot.tscn | 53 ++- 2d/space_shooter/rail.gd | 19 - 2d/space_shooter/ship.gd | 80 ---- 2d/space_shooter/ship.tscn | 229 ----------- 67 files changed, 1559 insertions(+), 973 deletions(-) create mode 100644 2d/space_shooter/README.md rename 2d/space_shooter/{ => assets}/tileset_edit.tscn (90%) delete mode 100644 2d/space_shooter/asteroid.gd rename 2d/space_shooter/{ => effects/background}/bg_gradient.png (100%) create mode 100644 2d/space_shooter/effects/background/bg_gradient.png.flags rename 2d/space_shooter/{ => effects/background}/big_star.png (100%) create mode 100644 2d/space_shooter/effects/background/big_star.png.flags rename 2d/space_shooter/{ => effects/background}/parallax.tscn (94%) rename 2d/space_shooter/{ => effects/background}/small_star.png (100%) create mode 100644 2d/space_shooter/effects/background/small_star.png.flags rename 2d/space_shooter/{ => effects/particles}/explosion.tscn (92%) rename 2d/space_shooter/{ => effects/particles}/fire.png (100%) create mode 100644 2d/space_shooter/effects/particles/fire.png.flags rename 2d/space_shooter/{ => effects/sounds}/sound_explode.wav (100%) rename 2d/space_shooter/{ => effects/sounds}/sound_shoot.wav (100%) create mode 100644 2d/space_shooter/enemies/asteroid/asteroid.gd rename 2d/space_shooter/{ => enemies/asteroid}/asteroid.tscn (73%) rename 2d/space_shooter/{ => enemies/asteroid}/meteorite.png (100%) create mode 100644 2d/space_shooter/enemies/asteroid/meteorite.png.flags create mode 100644 2d/space_shooter/enemies/shooter/enemy2.gd rename 2d/space_shooter/{ => enemies/shooter}/enemy2.png (100%) create mode 100644 2d/space_shooter/enemies/shooter/enemy2.png.flags rename 2d/space_shooter/{ => enemies/shooter}/enemy2.tscn (70%) rename 2d/space_shooter/{ => enemies/shooter}/enemy_shot.gd (65%) rename 2d/space_shooter/{ => enemies/shooter}/enemy_shot.png (100%) create mode 100644 2d/space_shooter/enemies/shooter/enemy_shot.png.flags rename 2d/space_shooter/{ => enemies/shooter}/enemy_shot.tscn (71%) create mode 100644 2d/space_shooter/enemies/ufo/enemy1.gd rename 2d/space_shooter/{ => enemies/ufo}/enemy1.png (100%) create mode 100644 2d/space_shooter/enemies/ufo/enemy1.png.flags rename 2d/space_shooter/{ => enemies/ufo}/enemy1.tscn (60%) create mode 100644 2d/space_shooter/enemies/ufo/enemy1_rail.gd delete mode 100644 2d/space_shooter/enemy1.gd delete mode 100644 2d/space_shooter/enemy2.gd create mode 100644 2d/space_shooter/game_screen/hud/game_hud.gd create mode 100644 2d/space_shooter/game_screen/hud/game_hud.tscn create mode 100644 2d/space_shooter/game_screen/level/enemies.tscn create mode 100644 2d/space_shooter/game_screen/level/level.gd create mode 100644 2d/space_shooter/game_screen/level/level.tscn create mode 100644 2d/space_shooter/game_screen/level/level_map.gd create mode 100644 2d/space_shooter/game_screen/level/level_map.tscn rename 2d/space_shooter/{ => game_screen/level}/tile.png (100%) create mode 100644 2d/space_shooter/game_screen/level/tile.png.flags create mode 100644 2d/space_shooter/game_screen/level/tilemap_level.tscn rename 2d/space_shooter/{ => game_screen/level}/tileset.tres (68%) delete mode 100644 2d/space_shooter/level.tscn delete mode 100644 2d/space_shooter/main_menu.gd delete mode 100644 2d/space_shooter/main_menu.tscn create mode 100644 2d/space_shooter/main_menu/main_menu.gd create mode 100644 2d/space_shooter/main_menu/main_menu.tscn create mode 100644 2d/space_shooter/main_menu/space.png create mode 100644 2d/space_shooter/main_menu/space.png.flags create mode 100644 2d/space_shooter/player/player_ship.tscn create mode 100644 2d/space_shooter/player/player_ship_on_rail.tscn create mode 100644 2d/space_shooter/player/rail.gd create mode 100644 2d/space_shooter/player/ship.gd rename 2d/space_shooter/{ => player}/ship.png (100%) create mode 100644 2d/space_shooter/player/ship.png.flags rename 2d/space_shooter/{ => player}/shoot.png (100%) create mode 100644 2d/space_shooter/player/shoot.png.flags rename 2d/space_shooter/{ => player}/shot.gd (69%) rename 2d/space_shooter/{ => player}/shot.tscn (73%) delete mode 100644 2d/space_shooter/rail.gd delete mode 100644 2d/space_shooter/ship.gd delete mode 100644 2d/space_shooter/ship.tscn diff --git a/2d/space_shooter/README.md b/2d/space_shooter/README.md new file mode 100644 index 00000000..d951dbea --- /dev/null +++ b/2d/space_shooter/README.md @@ -0,0 +1,39 @@ +# Space Shooter + +## Introduction +In this on-rails shoot-em-up demo, the player gets to control a Space ship flying through a 2D version of Space, while firing their lasers by hitting the Space bar. +Various enemies will enter the screen from the right and try their hardest to destroy the player's ship. +Shooting these enemies will award points and the highest score achieved is kept in a one-entry leaderboard. +Avoiding the blocky obstacles and the enemies is key to survival and high scores, so good luck and have fun! + +## Controls +* WSAD or Arrow Keys to move the ship +* Space to fire lasers +* Escape / ESC to stop playing and return to the main menu + +--- +## Godot Concepts presented in the demo +### Editor Workflow +* Importing assets (images, sounds) +* Using Scenes to group Nodes into small, mostly self-contained units of functionality +* Using a TileMap and TileSet to place obstacles in the level +* Use of AnimationPlayer nodes to both animate properties (position, rotation) as well as trigger functions +* Using a Parallax Background to give an impression of speed and distance traveled + +### Scripting +* Using signals to communicate between Nodes that are created in different Scenes +* Using groups to tag and identify Nodes +* Interactions between KinematicBody2D and Area2D nodes for hit detection / collision +* Use of VisibilityNotifier2D to remove Nodes that move off screen +* Dynamically instancing loaded Scenes as Nodes + +### GUI +* GUI Containers for organization and positioning +* GUI Controls to start and stop gameplay +* Use of a CanvasLayer to keep GUI always on top of the gameplay screen + +### Miscellaneous +* Persisting a "savegame" in the user directory for the highscore + +### Interactivity +* Player input for movement and firing diff --git a/2d/space_shooter/tileset_edit.tscn b/2d/space_shooter/assets/tileset_edit.tscn similarity index 90% rename from 2d/space_shooter/tileset_edit.tscn rename to 2d/space_shooter/assets/tileset_edit.tscn index 87152a18..2d3410ab 100644 --- a/2d/space_shooter/tileset_edit.tscn +++ b/2d/space_shooter/assets/tileset_edit.tscn @@ -1,6 +1,7 @@ [gd_scene load_steps=3 format=1] -[ext_resource path="res://tile.png" type="Texture" id=1] +[ext_resource path="res://game_screen/level/tile.png" type="Texture" id=1] + [sub_resource type="RectangleShape2D" id=1] diff --git a/2d/space_shooter/asteroid.gd b/2d/space_shooter/asteroid.gd deleted file mode 100644 index ab71f413..00000000 --- a/2d/space_shooter/asteroid.gd +++ /dev/null @@ -1,43 +0,0 @@ - -extends Area2D - -# Member variables -const SPEED = -200 -const Y_RANDOM = 10 - -var points = 1 -var speed_y = 0.0 -var destroyed = false - - -func _fixed_process(delta): - translate(Vector2(SPEED, speed_y)*delta) - - -func _ready(): - speed_y = rand_range(-Y_RANDOM, Y_RANDOM) - - -func destroy(): - if (destroyed): - return - destroyed = true - get_node("anim").play("explode") - set_fixed_process(false) - get_node("sfx").play("sound_explode") - # Accumulate points - get_node("/root/game_state").points += 1 - - -func is_enemy(): - return not destroyed - - -func _on_visibility_enter_screen(): - set_fixed_process(true) - # Make it spin! - get_node("anim").play("spin") - - -func _on_visibility_exit_screen(): - queue_free() diff --git a/2d/space_shooter/bg_gradient.png b/2d/space_shooter/effects/background/bg_gradient.png similarity index 100% rename from 2d/space_shooter/bg_gradient.png rename to 2d/space_shooter/effects/background/bg_gradient.png diff --git a/2d/space_shooter/effects/background/bg_gradient.png.flags b/2d/space_shooter/effects/background/bg_gradient.png.flags new file mode 100644 index 00000000..226e8a38 --- /dev/null +++ b/2d/space_shooter/effects/background/bg_gradient.png.flags @@ -0,0 +1,2 @@ +filter=false +gen_mipmaps=false diff --git a/2d/space_shooter/big_star.png b/2d/space_shooter/effects/background/big_star.png similarity index 100% rename from 2d/space_shooter/big_star.png rename to 2d/space_shooter/effects/background/big_star.png diff --git a/2d/space_shooter/effects/background/big_star.png.flags b/2d/space_shooter/effects/background/big_star.png.flags new file mode 100644 index 00000000..226e8a38 --- /dev/null +++ b/2d/space_shooter/effects/background/big_star.png.flags @@ -0,0 +1,2 @@ +filter=false +gen_mipmaps=false diff --git a/2d/space_shooter/parallax.tscn b/2d/space_shooter/effects/background/parallax.tscn similarity index 94% rename from 2d/space_shooter/parallax.tscn rename to 2d/space_shooter/effects/background/parallax.tscn index 0cbd0f5b..2b02f97c 100644 --- a/2d/space_shooter/parallax.tscn +++ b/2d/space_shooter/effects/background/parallax.tscn @@ -1,8 +1,8 @@ [gd_scene load_steps=4 format=1] -[ext_resource path="res://bg_gradient.png" type="Texture" id=1] -[ext_resource path="res://small_star.png" type="Texture" id=2] -[ext_resource path="res://big_star.png" type="Texture" id=3] +[ext_resource path="res://effects/background/bg_gradient.png" type="Texture" id=1] +[ext_resource path="res://effects/background/small_star.png" type="Texture" id=2] +[ext_resource path="res://effects/background/big_star.png" type="Texture" id=3] [node name="parallax" type="ParallaxBackground"] @@ -20,6 +20,7 @@ scroll/ignore_camera_zoom = true [node name="bg_layer" type="ParallaxLayer" parent="."] motion/scale = Vector2( 0.2, 1 ) +motion/offset = Vector2( 0, 0 ) motion/mirroring = Vector2( 1024, 0 ) [node name="gradient" type="Sprite" parent="bg_layer"] @@ -136,6 +137,7 @@ texture = ExtResource( 2 ) [node name="bg_layer2" type="ParallaxLayer" parent="."] motion/scale = Vector2( 0.5, 1 ) +motion/offset = Vector2( 0, 0 ) motion/mirroring = Vector2( 1024, 0 ) [node name="Sprite" type="Sprite" parent="bg_layer2"] diff --git a/2d/space_shooter/small_star.png b/2d/space_shooter/effects/background/small_star.png similarity index 100% rename from 2d/space_shooter/small_star.png rename to 2d/space_shooter/effects/background/small_star.png diff --git a/2d/space_shooter/effects/background/small_star.png.flags b/2d/space_shooter/effects/background/small_star.png.flags new file mode 100644 index 00000000..226e8a38 --- /dev/null +++ b/2d/space_shooter/effects/background/small_star.png.flags @@ -0,0 +1,2 @@ +filter=false +gen_mipmaps=false diff --git a/2d/space_shooter/explosion.tscn b/2d/space_shooter/effects/particles/explosion.tscn similarity index 92% rename from 2d/space_shooter/explosion.tscn rename to 2d/space_shooter/effects/particles/explosion.tscn index 7c8f94be..923e1d58 100644 --- a/2d/space_shooter/explosion.tscn +++ b/2d/space_shooter/effects/particles/explosion.tscn @@ -1,6 +1,7 @@ [gd_scene load_steps=3 format=1] -[ext_resource path="res://fire.png" type="Texture" id=1] +[ext_resource path="res://effects/particles/fire.png" type="Texture" id=1] + [sub_resource type="ColorRamp" id=1] diff --git a/2d/space_shooter/fire.png b/2d/space_shooter/effects/particles/fire.png similarity index 100% rename from 2d/space_shooter/fire.png rename to 2d/space_shooter/effects/particles/fire.png diff --git a/2d/space_shooter/effects/particles/fire.png.flags b/2d/space_shooter/effects/particles/fire.png.flags new file mode 100644 index 00000000..226e8a38 --- /dev/null +++ b/2d/space_shooter/effects/particles/fire.png.flags @@ -0,0 +1,2 @@ +filter=false +gen_mipmaps=false diff --git a/2d/space_shooter/sound_explode.wav b/2d/space_shooter/effects/sounds/sound_explode.wav similarity index 100% rename from 2d/space_shooter/sound_explode.wav rename to 2d/space_shooter/effects/sounds/sound_explode.wav diff --git a/2d/space_shooter/sound_shoot.wav b/2d/space_shooter/effects/sounds/sound_shoot.wav similarity index 100% rename from 2d/space_shooter/sound_shoot.wav rename to 2d/space_shooter/effects/sounds/sound_shoot.wav diff --git a/2d/space_shooter/enemies/asteroid/asteroid.gd b/2d/space_shooter/enemies/asteroid/asteroid.gd new file mode 100644 index 00000000..e6dee047 --- /dev/null +++ b/2d/space_shooter/enemies/asteroid/asteroid.gd @@ -0,0 +1,59 @@ +extends Area2D + +# horizontal movement speed +const SPEED = -200 + +# how many points does the player get for destroying this enemy type? +const POINTS = 1 + +# used for a slight vertical drift, defines the range +const Y_RANDOM = 10 + +# vertical movement for this asteroid instance +var speed_y = 0.0 + +# used to store this asteroid's motion direction +var motion = Vector2() + +var destroyed = false + +# notifies listeners about death and sends the point value along with it +signal enemy_died(score) + +func _fixed_process(delta): + # constant movement + translate(motion * delta) + +func _ready(): + # determine this asteroid's vertical drift + speed_y = rand_range(-Y_RANDOM, Y_RANDOM) + # store the movement direction because it doesn't change for this asteroid + motion = Vector2(SPEED, speed_y) + +func destroy(): + # skip if already destroyed + if (destroyed): + return + + # set the state to destroyed + destroyed = true + # stop processing + set_fixed_process(false) + # play on-death effects + get_node("anim").play("explode") + get_node("sfx").play("sound_explode") + # inform listeners about death, while sending the point value along with it + emit_signal("enemy_died", POINTS) + # disable physics interactions + call_deferred("set_enable_monitoring", false) + call_deferred("set_monitorable", false) + +func _on_visibility_enter_screen(): + # start moving once the asteroid enters the screen + set_fixed_process(true) + # Make it spin! + get_node("anim").play("spin") + +func _on_visibility_exit_screen(): + # remove the asteroid when it leaves the screen + queue_free() diff --git a/2d/space_shooter/asteroid.tscn b/2d/space_shooter/enemies/asteroid/asteroid.tscn similarity index 73% rename from 2d/space_shooter/asteroid.tscn rename to 2d/space_shooter/enemies/asteroid/asteroid.tscn index ad57f6ef..4d517d9d 100644 --- a/2d/space_shooter/asteroid.tscn +++ b/2d/space_shooter/enemies/asteroid/asteroid.tscn @@ -1,8 +1,11 @@ [gd_scene load_steps=9 format=1] -[ext_resource path="res://asteroid.gd" type="Script" id=1] -[ext_resource path="res://meteorite.png" type="Texture" id=2] -[ext_resource path="res://sound_explode.wav" type="Sample" id=3] +[ext_resource path="res://enemies/asteroid/asteroid.gd" type="Script" id=1] +[ext_resource path="res://enemies/asteroid/meteorite.png" type="Texture" id=2] +[ext_resource path="res://effects/sounds/sound_explode.wav" type="Sample" id=3] + + + [sub_resource type="CircleShape2D" id=1] @@ -18,15 +21,35 @@ step = 0.1 tracks/0/type = "value" tracks/0/path = NodePath("particles:config/emitting") tracks/0/interp = 1 -tracks/0/keys = { "cont":false, "times":FloatArray( 0, 0.1 ), "transitions":FloatArray( 1, 1 ), "values":[ true, false ] } +tracks/0/imported = false +tracks/0/keys = { +"times": FloatArray( 0, 0.1 ), +"transitions": FloatArray( 1, 1 ), +"update": 1, +"values": [ true, false ] +} tracks/1/type = "value" tracks/1/path = NodePath("sprite:visibility/visible") tracks/1/interp = 1 -tracks/1/keys = { "cont":false, "times":FloatArray( 0 ), "transitions":FloatArray( 1 ), "values":[ false ] } +tracks/1/imported = false +tracks/1/keys = { +"times": FloatArray( 0 ), +"transitions": FloatArray( 1 ), +"update": 1, +"values": [ false ] +} tracks/2/type = "method" tracks/2/path = NodePath(".") tracks/2/interp = 1 -tracks/2/keys = { "times":FloatArray( 0.7 ), "transitions":FloatArray( 1 ), "values":[ { "args":[ ], "method":"queue_free" } ] } +tracks/2/imported = false +tracks/2/keys = { +"times": FloatArray( 0.7 ), +"transitions": FloatArray( 1 ), +"values": [ { +"args": [ ], +"method": "queue_free" +} ] +} [sub_resource type="Animation" id=3] @@ -36,7 +59,13 @@ step = 0.1 tracks/0/type = "value" tracks/0/path = NodePath("sprite:transform/rot") tracks/0/interp = 1 -tracks/0/keys = { "cont":true, "times":FloatArray( 0, 3 ), "transitions":FloatArray( 1, 1 ), "values":[ 0.0, 360.0 ] } +tracks/0/imported = false +tracks/0/keys = { +"times": FloatArray( 0, 3 ), +"transitions": FloatArray( 1, 1 ), +"update": 0, +"values": [ 0.0, 360.0 ] +} [sub_resource type="ColorRamp" id=4] @@ -45,9 +74,16 @@ colors = ColorArray( 1, 1, 1, 1, 1, 1, 1, 0 ) [sub_resource type="SampleLibrary" id=5] -samples/sound_explode = { "db":0.0, "pitch":1.0, "sample":ExtResource( 3 ) } +samples/sound_explode = { +"db": 0.0, +"pitch": 1.0, +"priority": 0, +"sample": ExtResource( 3 ) +} -[node name="asteroid" type="Area2D"] +[node name="asteroid" type="Area2D" groups=[ +"enemy", +]] input/pickable = true shapes/0/shape = SubResource( 1 ) @@ -90,6 +126,7 @@ rect = Rect2( -10, -10, 20, 20 ) config/amount = 32 config/lifetime = 0.5 config/emitting = false +config/process_mode = 1 config/half_extents = Vector2( 20, 20 ) config/explosiveness = 0.1 config/texture = ExtResource( 2 ) diff --git a/2d/space_shooter/meteorite.png b/2d/space_shooter/enemies/asteroid/meteorite.png similarity index 100% rename from 2d/space_shooter/meteorite.png rename to 2d/space_shooter/enemies/asteroid/meteorite.png diff --git a/2d/space_shooter/enemies/asteroid/meteorite.png.flags b/2d/space_shooter/enemies/asteroid/meteorite.png.flags new file mode 100644 index 00000000..226e8a38 --- /dev/null +++ b/2d/space_shooter/enemies/asteroid/meteorite.png.flags @@ -0,0 +1,2 @@ +filter=false +gen_mipmaps=false diff --git a/2d/space_shooter/enemies/shooter/enemy2.gd b/2d/space_shooter/enemies/shooter/enemy2.gd new file mode 100644 index 00000000..99bf653a --- /dev/null +++ b/2d/space_shooter/enemies/shooter/enemy2.gd @@ -0,0 +1,77 @@ +extends Area2D + +# horizontal movement speed +const SPEED = -220 + +# how many points does the player get for destroying this ship? +const POINTS = 10 + +# time between shots in seconds +const SHOOT_INTERVAL = 1 + +# the enemy's projectile scene +const Shot = preload("res://enemies/shooter/enemy_shot.tscn") + +# used to store this enemy's motion direction +var motion = Vector2() + +var destroyed = false + +# remaining timeout until the enemy can fire again +var shoot_timeout = 0 + +# the node in the tree where this enemy should spawn its bullets into +var projectile_container + +# the Position2D that defines where the bullets will be spawned +onready var shoot_from = get_node("shoot_from") + +# used to notify listeners about this enemy's death +signal enemy_died(score) + +func _ready(): + motion = Vector2(SPEED, 0) + +func _fixed_process(delta): + # the enemy constantly moves + translate(motion * delta) + + # count down the time until the next shot + if shoot_timeout > 0.0: + shoot_timeout -= delta + + if (shoot_timeout <= 0): + shoot_timeout = SHOOT_INTERVAL + + if projectile_container != null: + # Instance a shot + var shot = Shot.instance() + # Set position to "shoot_from" Position2D node's global position + shot.set_pos(shoot_from.get_global_pos()) + # Add it to the projectile container, making its movement independent from ours + projectile_container.add_child(shot) + +func set_projectile_container(container): + projectile_container = container + +func destroy(): + # skip if already destroyed + if (destroyed): + return + destroyed = true + + # stop processing + set_fixed_process(false) + # play on-death effects + get_node("sfx").play("sound_explode") + get_node("anim").play("explode") + # inform listeners about death, while sending the point value along with it + emit_signal("enemy_died", POINTS) + call_deferred("set_enable_monitoring", false) + call_deferred("set_monitorable", false) + +func _on_visibility_enter_screen(): + set_fixed_process(true) + +func _on_visibility_exit_screen(): + queue_free() diff --git a/2d/space_shooter/enemy2.png b/2d/space_shooter/enemies/shooter/enemy2.png similarity index 100% rename from 2d/space_shooter/enemy2.png rename to 2d/space_shooter/enemies/shooter/enemy2.png diff --git a/2d/space_shooter/enemies/shooter/enemy2.png.flags b/2d/space_shooter/enemies/shooter/enemy2.png.flags new file mode 100644 index 00000000..226e8a38 --- /dev/null +++ b/2d/space_shooter/enemies/shooter/enemy2.png.flags @@ -0,0 +1,2 @@ +filter=false +gen_mipmaps=false diff --git a/2d/space_shooter/enemy2.tscn b/2d/space_shooter/enemies/shooter/enemy2.tscn similarity index 70% rename from 2d/space_shooter/enemy2.tscn rename to 2d/space_shooter/enemies/shooter/enemy2.tscn index 56d9f497..36a8b734 100644 --- a/2d/space_shooter/enemy2.tscn +++ b/2d/space_shooter/enemies/shooter/enemy2.tscn @@ -1,9 +1,12 @@ [gd_scene load_steps=8 format=1] -[ext_resource path="res://enemy2.gd" type="Script" id=1] -[ext_resource path="res://enemy2.png" type="Texture" id=2] -[ext_resource path="res://explosion.tscn" type="PackedScene" id=3] -[ext_resource path="res://sound_explode.wav" type="Sample" id=4] +[ext_resource path="res://enemies/shooter/enemy2.gd" type="Script" id=1] +[ext_resource path="res://enemies/shooter/enemy2.png" type="Texture" id=2] +[ext_resource path="res://effects/particles/explosion.tscn" type="PackedScene" id=3] +[ext_resource path="res://effects/sounds/sound_explode.wav" type="Sample" id=4] + + + [sub_resource type="ConvexPolygonShape2D" id=1] @@ -18,21 +21,48 @@ step = 0.1 tracks/0/type = "value" tracks/0/path = NodePath("explosion:config/emitting") tracks/0/interp = 1 -tracks/0/keys = { "cont":false, "times":FloatArray( 0, 0.1 ), "transitions":FloatArray( 1, 1 ), "values":[ true, false ] } +tracks/0/imported = false +tracks/0/keys = { +"times": FloatArray( 0, 0.1 ), +"transitions": FloatArray( 1, 1 ), +"update": 1, +"values": [ true, false ] +} tracks/1/type = "value" tracks/1/path = NodePath("sprite:visibility/visible") tracks/1/interp = 1 -tracks/1/keys = { "cont":false, "times":FloatArray( 0 ), "transitions":FloatArray( 1 ), "values":[ false ] } +tracks/1/imported = false +tracks/1/keys = { +"times": FloatArray( 0 ), +"transitions": FloatArray( 1 ), +"update": 1, +"values": [ false ] +} tracks/2/type = "method" tracks/2/path = NodePath(".") tracks/2/interp = 1 -tracks/2/keys = { "times":FloatArray( 0.9 ), "transitions":FloatArray( 1 ), "values":[ { "args":[ ], "method":"queue_free" } ] } +tracks/2/imported = false +tracks/2/keys = { +"times": FloatArray( 0.9 ), +"transitions": FloatArray( 1 ), +"values": [ { +"args": [ ], +"method": "queue_free" +} ] +} [sub_resource type="SampleLibrary" id=3] -samples/sound_explode = { "db":0.0, "pitch":1.0, "sample":ExtResource( 4 ) } +samples/sound_explode = { +"db": 0.0, +"pitch": 1.0, +"priority": 0, +"sample": ExtResource( 4 ) +} -[node name="enemy2" type="Area2D"] +[node name="enemy2" type="Area2D" groups=[ +"enemy", +]] input/pickable = true shapes/0/shape = SubResource( 1 ) @@ -57,9 +87,7 @@ texture = ExtResource( 2 ) [node name="explosion" parent="." instance=ExtResource( 3 )] -transform/rot = -91.1436 -config/explosiveness = 0.1 -params/gravity_strength = 9.8 +config/process_mode = 1 [node name="anim" type="AnimationPlayer" parent="."] diff --git a/2d/space_shooter/enemy_shot.gd b/2d/space_shooter/enemies/shooter/enemy_shot.gd similarity index 65% rename from 2d/space_shooter/enemy_shot.gd rename to 2d/space_shooter/enemies/shooter/enemy_shot.gd index 6c782b69..05e2d972 100644 --- a/2d/space_shooter/enemy_shot.gd +++ b/2d/space_shooter/enemies/shooter/enemy_shot.gd @@ -1,4 +1,3 @@ - extends Area2D # Member variables @@ -6,19 +5,18 @@ const SPEED = -800 var hit = false +var motion = Vector2() func _process(delta): - translate(Vector2(delta*SPEED, 0)) - + translate(motion * delta) func _ready(): + motion = Vector2(SPEED, 0) set_process(true) - func is_enemy(): return true - func _hit_something(): if (hit): return @@ -26,6 +24,9 @@ func _hit_something(): set_process(false) get_node("anim").play("splash") - func _on_visibility_exit_screen(): queue_free() + +func _on_enemy_shot_area_enter(area): + if area.is_in_group("player"): + area.take_damage() \ No newline at end of file diff --git a/2d/space_shooter/enemy_shot.png b/2d/space_shooter/enemies/shooter/enemy_shot.png similarity index 100% rename from 2d/space_shooter/enemy_shot.png rename to 2d/space_shooter/enemies/shooter/enemy_shot.png diff --git a/2d/space_shooter/enemies/shooter/enemy_shot.png.flags b/2d/space_shooter/enemies/shooter/enemy_shot.png.flags new file mode 100644 index 00000000..226e8a38 --- /dev/null +++ b/2d/space_shooter/enemies/shooter/enemy_shot.png.flags @@ -0,0 +1,2 @@ +filter=false +gen_mipmaps=false diff --git a/2d/space_shooter/enemy_shot.tscn b/2d/space_shooter/enemies/shooter/enemy_shot.tscn similarity index 71% rename from 2d/space_shooter/enemy_shot.tscn rename to 2d/space_shooter/enemies/shooter/enemy_shot.tscn index da6ed6f8..f7a14518 100644 --- a/2d/space_shooter/enemy_shot.tscn +++ b/2d/space_shooter/enemies/shooter/enemy_shot.tscn @@ -1,7 +1,8 @@ [gd_scene load_steps=6 format=1] -[ext_resource path="res://enemy_shot.gd" type="Script" id=1] -[ext_resource path="res://enemy_shot.png" type="Texture" id=2] +[ext_resource path="res://enemies/shooter/enemy_shot.gd" type="Script" id=1] +[ext_resource path="res://enemies/shooter/enemy_shot.png" type="Texture" id=2] + [sub_resource type="RectangleShape2D" id=1] @@ -21,15 +22,35 @@ step = 0.1 tracks/0/type = "value" tracks/0/path = NodePath("hit_splash:config/emitting") tracks/0/interp = 1 -tracks/0/keys = { "cont":false, "times":FloatArray( 0, 0.1 ), "transitions":FloatArray( 1, 1 ), "values":[ true, false ] } +tracks/0/imported = false +tracks/0/keys = { +"times": FloatArray( 0, 0.1 ), +"transitions": FloatArray( 1, 1 ), +"update": 1, +"values": [ true, false ] +} tracks/1/type = "method" tracks/1/path = NodePath(".") tracks/1/interp = 1 -tracks/1/keys = { "times":FloatArray( 0.9 ), "transitions":FloatArray( 1 ), "values":[ { "args":[ ], "method":"queue_free" } ] } +tracks/1/imported = false +tracks/1/keys = { +"times": FloatArray( 0.9 ), +"transitions": FloatArray( 1 ), +"values": [ { +"args": [ ], +"method": "queue_free" +} ] +} tracks/2/type = "value" tracks/2/path = NodePath("sprite:visibility/visible") tracks/2/interp = 1 -tracks/2/keys = { "cont":false, "times":FloatArray( 0 ), "transitions":FloatArray( 1 ), "values":[ false ] } +tracks/2/imported = false +tracks/2/keys = { +"times": FloatArray( 0 ), +"transitions": FloatArray( 1 ), +"update": 1, +"values": [ false ] +} [node name="enemy_shot" type="Area2D"] @@ -45,9 +66,8 @@ script/script = ExtResource( 1 ) [node name="visibility" type="VisibilityNotifier2D" parent="."] -transform/pos = Vector2( 1.8353, -0.0742126 ) -transform/scale = Vector2( 1.54149, 0.770745 ) -rect = Rect2( -10, -10, 20, 20 ) +transform/pos = Vector2( 1, 0 ) +rect = Rect2( -13, -5, 24, 10 ) [node name="sprite" type="Sprite" parent="."] @@ -58,12 +78,16 @@ texture = ExtResource( 2 ) shape = SubResource( 1 ) trigger = false _update_shape_index = -1 +__meta__ = { +"_edit_lock_": true +} [node name="hit_splash" type="Particles2D" parent="."] config/amount = 32 config/lifetime = 0.5 config/emitting = false +config/process_mode = 1 config/explosiveness = 0.1 params/direction = 0.0 params/spread = 180.0 @@ -82,6 +106,9 @@ params/hue_variation = 0.0 params/anim_speed_scale = 1.0 params/anim_initial_pos = 0.0 color/color_ramp = SubResource( 2 ) +__meta__ = { +"_edit_lock_": true +} [node name="anim" type="AnimationPlayer" parent="."] @@ -94,6 +121,8 @@ playback/speed = 1.0 blend_times = [ ] autoplay = "" +[connection signal="area_enter" from="." to="." method="_on_enemy_shot_area_enter"] + [connection signal="exit_screen" from="visibility" to="." method="_on_visibility_exit_screen"] diff --git a/2d/space_shooter/enemies/ufo/enemy1.gd b/2d/space_shooter/enemies/ufo/enemy1.gd new file mode 100644 index 00000000..2ac38c48 --- /dev/null +++ b/2d/space_shooter/enemies/ufo/enemy1.gd @@ -0,0 +1,41 @@ +extends Area2D + +# how many points does the player get for destroying this ship? +const POINTS = 5 + +var destroyed = false + +# used to notify listeners about this enemy's death +signal enemy_died(score) + +func _ready(): + # the ship will start its zigzag movement at a random offset in its animation path + # this skip is visible for about one frame + # as a workaround, we hide the node until the animation seeking is later completed + hide() + +func destroy(): + # skip if already destroyed + if (destroyed): + return + + # set the state to destroyed + destroyed = true + # play on-death effects + # take note of how the explode animation also frees the parent node after 0.9 seconds + get_node("anim").play("explode") + get_node("sfx").play("sound_explode") + # inform listeners about death, while sending the point value along with it + emit_signal("enemy_died", POINTS) + # disable physics interactions + call_deferred("set_enable_monitoring", false) + call_deferred("set_monitorable", false) + +# this signal is connected in the editor +func _on_visibility_enter_screen(): + get_node("anim").play("zigzag") + # randomly offset the animation's start point + get_node("anim").seek(randf()*2.0) + # as mentioned in _ready, show the node after seeking to the random start point in the animation + show() + \ No newline at end of file diff --git a/2d/space_shooter/enemy1.png b/2d/space_shooter/enemies/ufo/enemy1.png similarity index 100% rename from 2d/space_shooter/enemy1.png rename to 2d/space_shooter/enemies/ufo/enemy1.png diff --git a/2d/space_shooter/enemies/ufo/enemy1.png.flags b/2d/space_shooter/enemies/ufo/enemy1.png.flags new file mode 100644 index 00000000..226e8a38 --- /dev/null +++ b/2d/space_shooter/enemies/ufo/enemy1.png.flags @@ -0,0 +1,2 @@ +filter=false +gen_mipmaps=false diff --git a/2d/space_shooter/enemy1.tscn b/2d/space_shooter/enemies/ufo/enemy1.tscn similarity index 60% rename from 2d/space_shooter/enemy1.tscn rename to 2d/space_shooter/enemies/ufo/enemy1.tscn index 5fd44353..a95c2e22 100644 --- a/2d/space_shooter/enemy1.tscn +++ b/2d/space_shooter/enemies/ufo/enemy1.tscn @@ -1,9 +1,13 @@ -[gd_scene load_steps=9 format=1] +[gd_scene load_steps=10 format=1] + +[ext_resource path="res://enemies/ufo/enemy1_rail.gd" type="Script" id=1] +[ext_resource path="res://enemies/ufo/enemy1.gd" type="Script" id=2] +[ext_resource path="res://enemies/ufo/enemy1.png" type="Texture" id=3] +[ext_resource path="res://effects/particles/explosion.tscn" type="PackedScene" id=4] +[ext_resource path="res://effects/sounds/sound_explode.wav" type="Sample" id=5] + + -[ext_resource path="res://enemy1.gd" type="Script" id=1] -[ext_resource path="res://enemy1.png" type="Texture" id=2] -[ext_resource path="res://explosion.tscn" type="PackedScene" id=3] -[ext_resource path="res://sound_explode.wav" type="Sample" id=4] [sub_resource type="ConvexPolygonShape2D" id=1] @@ -18,15 +22,35 @@ step = 0.1 tracks/0/type = "value" tracks/0/path = NodePath("sprite:visibility/visible") tracks/0/interp = 1 -tracks/0/keys = { "cont":false, "times":FloatArray( 0 ), "transitions":FloatArray( 1 ), "values":[ false ] } +tracks/0/imported = false +tracks/0/keys = { +"times": FloatArray( 0 ), +"transitions": FloatArray( 1 ), +"update": 1, +"values": [ false ] +} tracks/1/type = "value" tracks/1/path = NodePath("explosion:config/emitting") tracks/1/interp = 1 -tracks/1/keys = { "cont":false, "times":FloatArray( 0, 0.1 ), "transitions":FloatArray( 1, 1 ), "values":[ true, false ] } +tracks/1/imported = false +tracks/1/keys = { +"times": FloatArray( 0, 0.1 ), +"transitions": FloatArray( 1, 1 ), +"update": 1, +"values": [ true, false ] +} tracks/2/type = "method" tracks/2/path = NodePath("..") tracks/2/interp = 1 -tracks/2/keys = { "times":FloatArray( 0.9 ), "transitions":FloatArray( 1 ), "values":[ { "args":[ ], "method":"queue_free" } ] } +tracks/2/imported = false +tracks/2/keys = { +"times": FloatArray( 0.9 ), +"transitions": FloatArray( 1 ), +"values": [ { +"args": [ ], +"method": "queue_free" +} ] +} [sub_resource type="Animation" id=3] @@ -36,15 +60,32 @@ step = 0.1 tracks/0/type = "value" tracks/0/path = NodePath(".:transform/pos") tracks/0/interp = 1 -tracks/0/keys = { "cont":true, "times":FloatArray( 0, 1 ), "transitions":FloatArray( -1.86607, -1.86607 ), "values":[ Vector2( 0, -100 ), Vector2( 0, 100 ) ] } +tracks/0/imported = false +tracks/0/keys = { +"times": FloatArray( 0, 1 ), +"transitions": FloatArray( -1.86607, -1.86607 ), +"update": 0, +"values": [ Vector2( 0, -100 ), Vector2( 0, 100 ) ] +} [sub_resource type="SampleLibrary" id=4] -samples/sound_explode = { "db":0.0, "pitch":1.0, "sample":ExtResource( 4 ) } +samples/sound_explode = { +"db": 0.0, +"pitch": 1.0, +"priority": 0, +"sample": ExtResource( 5 ) +} -[node name="enemy1" type="Node2D"] +[node name="enemy1" type="Node2D" groups=[ +"enemy", +]] -[node name="area" type="Area2D" parent="."] +script/script = ExtResource( 1 ) + +[node name="area" type="Area2D" parent="." groups=[ +"enemy", +]] transform/pos = Vector2( 0, -100 ) input/pickable = true @@ -55,7 +96,7 @@ gravity_vec = Vector2( 0, 1 ) gravity = 98.0 linear_damp = 0.1 angular_damp = 1.0 -script/script = ExtResource( 1 ) +script/script = ExtResource( 2 ) [node name="collision" type="CollisionPolygon2D" parent="area"] @@ -66,13 +107,11 @@ trigger = false [node name="sprite" type="Sprite" parent="area"] -texture = ExtResource( 2 ) +texture = ExtResource( 3 ) -[node name="explosion" parent="area" instance=ExtResource( 3 )] +[node name="explosion" parent="area" instance=ExtResource( 4 )] -transform/rot = -91.1436 -config/explosiveness = 0.1 -params/gravity_strength = 9.8 +config/process_mode = 1 [node name="anim" type="AnimationPlayer" parent="area"] @@ -103,6 +142,4 @@ config/pitch_random = 0.0 [connection signal="enter_screen" from="area/visibility" to="area" method="_on_visibility_enter_screen"] -[connection signal="exit_screen" from="area/visibility" to="area" method="_on_visibility_exit_screen"] - diff --git a/2d/space_shooter/enemies/ufo/enemy1_rail.gd b/2d/space_shooter/enemies/ufo/enemy1_rail.gd new file mode 100644 index 00000000..2f06cdc0 --- /dev/null +++ b/2d/space_shooter/enemies/ufo/enemy1_rail.gd @@ -0,0 +1,33 @@ +extends Node2D + +# the rail's horizontal movement speed +const SPEED = -200 + +# used to store this enemy rail's motion direction +var motion = Vector2() + +# keep references to child nodes +onready var ship = get_node("area") +onready var visibility = get_node("area/visibility") + +signal enemy_died(score) + +func _ready(): + # connect to the actual ship's death signal so it can be relayed further up the tree + # the relay is required because the player collides with the ship's area node, but the level connects to each enemy scene's root node + ship.connect("enemy_died", self, "on_ship_died") + # once the ship is in range, start moving the rail + visibility.connect("enter_screen", self, "set_fixed_process", [true]) + # once the ship leaves the screen, remove the entire node + visibility.connect("exit_screen", self, "queue_free") + motion = Vector2(SPEED, 0) + +func _fixed_process(delta): + # constant movement + translate(motion * delta) + +func on_ship_died(score): + # stop moving the rail + set_fixed_process(false) + # relay the death signal upwards + emit_signal("enemy_died", score) diff --git a/2d/space_shooter/enemy1.gd b/2d/space_shooter/enemy1.gd deleted file mode 100644 index e127eb27..00000000 --- a/2d/space_shooter/enemy1.gd +++ /dev/null @@ -1,36 +0,0 @@ - -extends Area2D - -# Member variables -const SPEED = -200 - -var destroyed=false - - -func _fixed_process(delta): - get_parent().translate(Vector2(SPEED*delta, 0)) - - -func is_enemy(): - return not destroyed - - -func destroy(): - if (destroyed): - return - destroyed = true - get_node("anim").play("explode") - set_fixed_process(false) - get_node("sfx").play("sound_explode") - # Accumulate points - get_node("/root/game_state").points += 5 - - -func _on_visibility_enter_screen(): - set_fixed_process(true) - get_node("anim").play("zigzag") - get_node("anim").seek(randf()*2.0) # Make it start from any pos - - -func _on_visibility_exit_screen(): - queue_free() diff --git a/2d/space_shooter/enemy2.gd b/2d/space_shooter/enemy2.gd deleted file mode 100644 index 773c266d..00000000 --- a/2d/space_shooter/enemy2.gd +++ /dev/null @@ -1,47 +0,0 @@ - -extends Area2D - -# Member variables -const SPEED = -220 -const SHOOT_INTERVAL = 1 - -var shoot_timeout = 0 -var destroyed=false - - -func _fixed_process(delta): - translate(Vector2(SPEED*delta, 0)) - shoot_timeout -= delta - - if (shoot_timeout < 0): - shoot_timeout = SHOOT_INTERVAL - - # Instance a shot - var shot = preload("res://enemy_shot.tscn").instance() - # Set pos as "shoot_from" Position2D node - shot.set_pos(get_node("shoot_from").get_global_pos()) - # Add it to parent, so it has world coordinates - get_parent().add_child(shot) - - -func is_enemy(): - return not destroyed - - -func destroy(): - if (destroyed): - return - destroyed = true - get_node("anim").play("explode") - set_fixed_process(false) - get_node("sfx").play("sound_explode") - # Accumulate points - get_node("/root/game_state").points += 10 - - -func _on_visibility_enter_screen(): - set_fixed_process(true) - - -func _on_visibility_exit_screen(): - queue_free() diff --git a/2d/space_shooter/engine.cfg b/2d/space_shooter/engine.cfg index 177cf886..69cb818b 100644 --- a/2d/space_shooter/engine.cfg +++ b/2d/space_shooter/engine.cfg @@ -1,15 +1,18 @@ [application] name="Space Shooter" -main_scene="res://main_menu.tscn" +main_scene="res://main_menu/main_menu.tscn" icon="res://icon.png" [autoload] -game_state="res://game_state.gd" +game_state="*res://game_state.gd" [display] +resizable=true +stretch_mode="2d" +stretch_aspect="keep" width=1024 height=600 diff --git a/2d/space_shooter/game_screen/hud/game_hud.gd b/2d/space_shooter/game_screen/hud/game_hud.gd new file mode 100644 index 00000000..3623f7b1 --- /dev/null +++ b/2d/space_shooter/game_screen/hud/game_hud.gd @@ -0,0 +1,25 @@ +extends CanvasLayer + +onready var score_label = get_node("score_points") +onready var return_button = get_node("back_to_menu") +onready var game_over_label = get_node("game_over") + +signal return_to_menu + +func _ready(): + return_button.connect("pressed", self, "_on_return_to_menu") + set_process(true) + set_process_input(true) + +func _process(delta): + if Input.is_action_pressed("ui_cancel"): + _on_return_to_menu() + +func update_score(score): + score_label.set_text(str(score)) + +func _on_return_to_menu(): + emit_signal("return_to_menu") + +func game_over(): + game_over_label.show() \ No newline at end of file diff --git a/2d/space_shooter/game_screen/hud/game_hud.tscn b/2d/space_shooter/game_screen/hud/game_hud.tscn new file mode 100644 index 00000000..05ae9d8b --- /dev/null +++ b/2d/space_shooter/game_screen/hud/game_hud.tscn @@ -0,0 +1,77 @@ +[gd_scene load_steps=2 format=1] + +[ext_resource path="res://game_screen/hud/game_hud.gd" type="Script" id=1] + + +[node name="hud" type="CanvasLayer"] + +layer = 1 +offset = Vector2( 0, 0 ) +rotation = 0.0 +scale = Vector2( 1, 1 ) +script/script = ExtResource( 1 ) + +[node name="score" type="Label" parent="."] + +focus/ignore_mouse = true +focus/stop_mouse = true +size_flags/horizontal = 2 +size_flags/vertical = 0 +margin/left = 15.0 +margin/top = 13.0 +margin/right = 66.0 +margin/bottom = 26.0 +text = "SCORE:" +percent_visible = 1.0 +lines_skipped = 0 +max_lines_visible = -1 + +[node name="score_points" type="Label" parent="."] + +focus/ignore_mouse = true +focus/stop_mouse = true +size_flags/horizontal = 2 +size_flags/vertical = 0 +margin/left = 70.0 +margin/top = 13.0 +margin/right = 121.0 +margin/bottom = 26.0 +text = "0" +align = 1 +percent_visible = 1.0 +lines_skipped = 0 +max_lines_visible = -1 + +[node name="back_to_menu" type="Button" parent="."] + +focus/ignore_mouse = false +focus/stop_mouse = true +size_flags/horizontal = 2 +size_flags/vertical = 2 +margin/left = 911.0 +margin/top = 10.0 +margin/right = 1006.0 +margin/bottom = 31.0 +toggle_mode = false +enabled_focus_mode = 2 +shortcut = null +text = "Back to Menu" +flat = false + +[node name="game_over" type="Label" parent="."] + +visibility/visible = false +focus/ignore_mouse = true +focus/stop_mouse = true +size_flags/horizontal = 2 +size_flags/vertical = 0 +margin/left = 482.0 +margin/top = 286.0 +margin/right = 564.0 +margin/bottom = 299.0 +text = "GAME_OVER" +percent_visible = 1.0 +lines_skipped = 0 +max_lines_visible = -1 + + diff --git a/2d/space_shooter/game_screen/level/enemies.tscn b/2d/space_shooter/game_screen/level/enemies.tscn new file mode 100644 index 00000000..f653d826 --- /dev/null +++ b/2d/space_shooter/game_screen/level/enemies.tscn @@ -0,0 +1,284 @@ +[gd_scene load_steps=4 format=1] + +[ext_resource path="res://enemies/asteroid/asteroid.tscn" type="PackedScene" id=1] +[ext_resource path="res://enemies/ufo/enemy1.tscn" type="PackedScene" id=2] +[ext_resource path="res://enemies/shooter/enemy2.tscn" type="PackedScene" id=3] + + + + +[node name="enemies" type="Node2D"] + +[node name="asteroid" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 1797.52, 105.736 ) + +[node name="asteroid1" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 1666.61, 304.621 ) + +[node name="asteroid2" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 1988.85, 443.086 ) + +[node name="asteroid3" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 2595.58, 103.219 ) + +[node name="asteroid4" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 3229.99, 299.586 ) + +[node name="asteroid5" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 3592.52, 541.269 ) + +[node name="asteroid6" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 4571.84, 216.508 ) + +[node name="asteroid7" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 4571.84, 284.481 ) + +[node name="asteroid8" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 4571.84, 360.007 ) + +[node name="asteroid9" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 5140.8, 108.254 ) + +[node name="asteroid10" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 5168.5, 475.814 ) + +[node name="asteroid11" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 5767.67, 113.289 ) + +[node name="asteroid12" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 6107.53, 480.849 ) + +[node name="asteroid13" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 6364.32, 105.736 ) + +[node name="asteroid14" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 6731.88, 573.997 ) + +[node name="asteroid15" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 7033.99, 166.157 ) + +[node name="asteroid16" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 6424.74, 352.454 ) + +[node name="asteroid17" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 7263.08, 80.5608 ) + +[node name="asteroid18" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 7177.49, 541.269 ) + +[node name="asteroid19" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 7066.71, 344.902 ) + +[node name="asteroid20" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 7655.82, 118.324 ) + +[node name="asteroid21" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 7540.01, 324.762 ) + +[node name="asteroid22" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 7764.07, 566.445 ) + +[node name="asteroid23" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 7872.33, 216.508 ) + +[node name="asteroid24" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 8458.91, 95.666 ) + +[node name="asteroid25" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 8786.19, 231.613 ) + +[node name="asteroid26" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 8599.89, 551.339 ) + +[node name="asteroid27" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 8353.17, 289.516 ) + +[node name="asteroid28" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 8995.14, 95.6658 ) + +[node name="asteroid29" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 9294.73, 579.032 ) + +[node name="asteroid30" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 9392.91, 140.981 ) + +[node name="asteroid31" parent="." instance=ExtResource( 1 )] + +transform/pos = Vector2( 9644.67, 281.963 ) + +[node name="enemy1" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 2920.34, 365.042 ) + +[node name="enemy2" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 3894.62, 506.024 ) + +[node name="enemy3" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 4325.12, 302.104 ) + +[node name="enemy4" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 4753.1, 506.024 ) + +[node name="enemy5" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 5158.43, 211.473 ) + +[node name="enemy6" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 5490.74, 349.937 ) + +[node name="enemy7" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 5765.15, 546.305 ) + +[node name="enemy8" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 6142.78, 244.201 ) + +[node name="enemy9" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 6701.67, 221.543 ) + +[node name="enemy10" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 6701.67, 352.455 ) + +[node name="enemy11" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 6706.71, 500.989 ) + +[node name="enemy12" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 6711.74, 566.445 ) + +[node name="enemy13" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 7157.35, 332.314 ) + +[node name="enemy14" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 7421.69, 511.059 ) + +[node name="enemy15" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 7887.43, 239.166 ) + +[node name="enemy16" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 8463.95, 382.665 ) + +[node name="enemy17" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 9065.64, 244.201 ) + +[node name="enemy18" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 8967.46, 566.445 ) + +[node name="enemy19" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 9483.55, 422.946 ) + +[node name="enemy20" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 9687.47, 234.131 ) + +[node name="enemy21" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 9815.86, 579.033 ) + +[node name="enemy22" parent="." instance=ExtResource( 2 )] + +transform/pos = Vector2( 9815.86, 579.033 ) + +[node name="enemy2 2" parent="." instance=ExtResource( 3 )] + +transform/pos = Vector2( 4759.97, 278.527 ) + +[node name="enemy23" parent="." instance=ExtResource( 3 )] + +transform/pos = Vector2( 6277.15, 559.36 ) + +[node name="enemy24" parent="." instance=ExtResource( 3 )] + +transform/pos = Vector2( 7136.77, 100.438 ) + +[node name="enemy25" parent="." instance=ExtResource( 3 )] + +transform/pos = Vector2( 7766.93, 370.996 ) + +[node name="enemy26" parent="." instance=ExtResource( 3 )] + +transform/pos = Vector2( 7890.23, 309.35 ) + +[node name="enemy27" parent="." instance=ExtResource( 3 )] + +transform/pos = Vector2( 8006.67, 237.43 ) + +[node name="enemy28" parent="." instance=ExtResource( 3 )] + +transform/pos = Vector2( 8664.23, 257.978 ) + +[node name="enemy29" parent="." instance=ExtResource( 3 )] + +transform/pos = Vector2( 8660.8, 357.297 ) + +[node name="enemy30" parent="." instance=ExtResource( 3 )] + +transform/pos = Vector2( 8657.38, 453.191 ) + +[node name="enemy31" parent="." instance=ExtResource( 3 )] + +transform/pos = Vector2( 9475.9, 189.483 ) + +[node name="enemy32" parent="." instance=ExtResource( 3 )] + +transform/pos = Vector2( 9564.95, 234.005 ) + +[node name="enemy33" parent="." instance=ExtResource( 3 )] + +transform/pos = Vector2( 9674.54, 281.952 ) + +[node name="enemy34" parent="." instance=ExtResource( 3 )] + +transform/pos = Vector2( 9575.22, 391.545 ) + +[node name="enemy35" parent="." instance=ExtResource( 3 )] + +transform/pos = Vector2( 9458.78, 446.342 ) + + diff --git a/2d/space_shooter/game_screen/level/level.gd b/2d/space_shooter/game_screen/level/level.gd new file mode 100644 index 00000000..c13295ef --- /dev/null +++ b/2d/space_shooter/game_screen/level/level.gd @@ -0,0 +1,33 @@ +extends Node2D + +onready var hud = get_node("hud") +onready var player_rail = get_node("player_ship_on_rail") +onready var player_ship = player_rail.player_ship +onready var level_map = get_node("level_map") +onready var projectiles = get_node("projectiles") + +func _ready(): + # tell the player ship where to instance its projectiles + player_ship.set_projectile_container(projectiles) + # connect to the player's death signal + player_ship.connect("player_died", self, "on_player_died") + # find all enemies in the currently loaded level and connect to their death signals for scoring + for enemy in level_map.enemy_container.get_children(): + # for instances of enemy1, this check is being done against their root "rail" Node2D + # the actual collision later checks their area's group + if enemy.is_in_group("enemy"): + enemy.connect("enemy_died", self, "on_enemy_died") + hud.connect("return_to_menu", self, "on_return_to_menu") + +# notifies the hud and game state about the player's death +func on_player_died(): + game_state.game_over() + hud.game_over() + +# notifies the game_state and hud about enemy deaths, which report the enemy's point value +func on_enemy_died(score): + game_state.points += score + hud.update_score(game_state.points) + +func on_return_to_menu(): + game_state.abort_game() \ No newline at end of file diff --git a/2d/space_shooter/game_screen/level/level.tscn b/2d/space_shooter/game_screen/level/level.tscn new file mode 100644 index 00000000..ef5a8e8d --- /dev/null +++ b/2d/space_shooter/game_screen/level/level.tscn @@ -0,0 +1,29 @@ +[gd_scene load_steps=6 format=1] + +[ext_resource path="res://game_screen/level/level.gd" type="Script" id=1] +[ext_resource path="res://player/player_ship_on_rail.tscn" type="PackedScene" id=2] +[ext_resource path="res://effects/background/parallax.tscn" type="PackedScene" id=3] +[ext_resource path="res://game_screen/hud/game_hud.tscn" type="PackedScene" id=4] +[ext_resource path="res://game_screen/level/level_map.tscn" type="PackedScene" id=5] + + + + + +[node name="space_shooter_game" type="Node2D"] + +script/script = ExtResource( 1 ) + +[node name="player_ship_on_rail" parent="." instance=ExtResource( 2 )] + +[node name="parallax" parent="." instance=ExtResource( 3 )] + +[node name="hud" parent="." instance=ExtResource( 4 )] + +[node name="level_map" parent="." instance=ExtResource( 5 )] + +[node name="projectiles" type="Node2D" parent="." groups=[ +"projectile_container", +]] + + diff --git a/2d/space_shooter/game_screen/level/level_map.gd b/2d/space_shooter/game_screen/level/level_map.gd new file mode 100644 index 00000000..9e4485cf --- /dev/null +++ b/2d/space_shooter/game_screen/level/level_map.gd @@ -0,0 +1,12 @@ +extends Node2D + +onready var enemy_container = get_node("enemies") +onready var enemy_projectile_container = get_node("enemy_projectiles") + +func _ready(): + var all_enemies = enemy_container.get_children() + for enemy in all_enemies: + # find all enemies who need a projectile container because they can shoot + if enemy.has_method("set_projectile_container"): + # tell those enemies about the container + enemy.set_projectile_container(enemy_projectile_container) \ No newline at end of file diff --git a/2d/space_shooter/game_screen/level/level_map.tscn b/2d/space_shooter/game_screen/level/level_map.tscn new file mode 100644 index 00000000..a85e9232 --- /dev/null +++ b/2d/space_shooter/game_screen/level/level_map.tscn @@ -0,0 +1,18 @@ +[gd_scene load_steps=4 format=1] + +[ext_resource path="res://game_screen/level/level_map.gd" type="Script" id=1] +[ext_resource path="res://game_screen/level/enemies.tscn" type="PackedScene" id=2] +[ext_resource path="res://game_screen/level/tilemap_level.tscn" type="PackedScene" id=3] + + +[node name="LevelMap" type="Node2D"] + +script/script = ExtResource( 1 ) + +[node name="enemies" parent="." instance=ExtResource( 2 )] + +[node name="TileMap" parent="." instance=ExtResource( 3 )] + +[node name="enemy_projectiles" type="Node2D" parent="."] + + diff --git a/2d/space_shooter/tile.png b/2d/space_shooter/game_screen/level/tile.png similarity index 100% rename from 2d/space_shooter/tile.png rename to 2d/space_shooter/game_screen/level/tile.png diff --git a/2d/space_shooter/game_screen/level/tile.png.flags b/2d/space_shooter/game_screen/level/tile.png.flags new file mode 100644 index 00000000..226e8a38 --- /dev/null +++ b/2d/space_shooter/game_screen/level/tile.png.flags @@ -0,0 +1,2 @@ +filter=false +gen_mipmaps=false diff --git a/2d/space_shooter/game_screen/level/tilemap_level.tscn b/2d/space_shooter/game_screen/level/tilemap_level.tscn new file mode 100644 index 00000000..07ea15ef --- /dev/null +++ b/2d/space_shooter/game_screen/level/tilemap_level.tscn @@ -0,0 +1,24 @@ +[gd_scene load_steps=2 format=1] + +[ext_resource path="res://game_screen/level/tileset.tres" type="TileSet" id=1] + + +[node name="TileMap" type="TileMap"] + +mode = 0 +tile_set = ExtResource( 1 ) +cell/size = Vector2( 32, 32 ) +cell/quadrant_size = 16 +cell/custom_transform = Matrix32( 1, 0, 0, 1, 0, 0 ) +cell/half_offset = 2 +cell/tile_origin = 0 +cell/y_sort = false +collision/use_kinematic = false +collision/friction = 1.0 +collision/bounce = 0.0 +collision/layers = 1 +collision/mask = 1 +occluder/light_mask = 1 +tile_data = IntArray( 16, 0, 17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24, 0, 25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32, 0, 33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 0, 39, 0, 63, 0, 64, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0, 105, 0, 106, 0, 107, 0, 108, 0, 109, 0, 110, 0, 111, 0, 112, 0, 113, 0, 114, 0, 115, 0, 116, 0, 117, 0, 118, 0, 119, 0, 120, 0, 121, 0, 122, 0, 123, 0, 124, 0, 125, 0, 126, 0, 127, 0, 128, 0, 145, 0, 184, 0, 65556, 0, 65557, 0, 65558, 0, 65559, 0, 65560, 0, 65561, 0, 65562, 0, 65563, 0, 65564, 0, 65565, 0, 65566, 0, 65567, 0, 65568, 0, 65569, 0, 65570, 0, 65571, 0, 65572, 0, 65573, 0, 65574, 0, 65575, 0, 65599, 0, 65600, 0, 65637, 0, 65638, 0, 65639, 0, 65640, 0, 65641, 0, 65642, 0, 65643, 0, 65644, 0, 65645, 0, 65646, 0, 65647, 0, 65648, 0, 65649, 0, 65650, 0, 65651, 0, 65652, 0, 65653, 0, 65654, 0, 65655, 0, 65656, 0, 65657, 0, 65658, 0, 65659, 0, 65660, 0, 65661, 0, 65662, 0, 65663, 0, 65664, 0, 65681, 0, 65720, 0, 131094, 0, 131095, 0, 131096, 0, 131097, 0, 131098, 0, 131099, 0, 131100, 0, 131101, 0, 131102, 0, 131103, 0, 131104, 0, 131105, 0, 131106, 0, 131107, 0, 131108, 0, 131109, 0, 131135, 0, 131136, 0, 131137, 0, 131176, 0, 131177, 0, 131178, 0, 131179, 0, 131180, 0, 131181, 0, 131182, 0, 131183, 0, 131184, 0, 131185, 0, 131186, 0, 131187, 0, 131188, 0, 131189, 0, 131190, 0, 131191, 0, 131192, 0, 131193, 0, 131194, 0, 131195, 0, 131196, 0, 131197, 0, 131198, 0, 131199, 0, 131217, 0, 131256, 0, 196631, 0, 196632, 0, 196633, 0, 196634, 0, 196635, 0, 196636, 0, 196637, 0, 196638, 0, 196639, 0, 196640, 0, 196641, 0, 196642, 0, 196671, 0, 196672, 0, 196673, 0, 196716, 0, 196717, 0, 196718, 0, 196719, 0, 196720, 0, 196721, 0, 196722, 0, 196723, 0, 196724, 0, 196725, 0, 196726, 0, 196727, 0, 196728, 0, 196729, 0, 196730, 0, 196753, 0, 196792, 0, 262169, 0, 262170, 0, 262171, 0, 262172, 0, 262173, 0, 262174, 0, 262175, 0, 262207, 0, 262208, 0, 262209, 0, 262256, 0, 262257, 0, 262258, 0, 262259, 0, 262260, 0, 262261, 0, 262262, 0, 262263, 0, 262289, 0, 262328, 0, 327707, 0, 327708, 0, 327743, 0, 327744, 0, 327825, 0, 327826, 0, 327827, 0, 327828, 0, 327829, 0, 327830, 0, 327831, 0, 327832, 0, 327833, 0, 327834, 0, 327835, 0, 327836, 0, 327845, 0, 327846, 0, 327847, 0, 327848, 0, 327849, 0, 327850, 0, 327851, 0, 327852, 0, 327853, 0, 327854, 0, 327855, 0, 327856, 0, 327857, 0, 327864, 0, 327911, 0, 327912, 0, 328010, 0, 328011, 0, 328012, 0, 328013, 0, 328014, 0, 393372, 0, 393393, 0, 393400, 0, 393445, 0, 393446, 0, 393447, 0, 393448, 0, 393548, 0, 393549, 0, 393550, 0, 458908, 0, 458929, 0, 458936, 0, 458981, 0, 458982, 0, 458983, 0, 524444, 0, 524465, 0, 524472, 0, 524517, 0, 524518, 0, 589980, 0, 590001, 0, 590008, 0, 655516, 0, 655521, 0, 655522, 0, 655523, 0, 655524, 0, 655525, 0, 655526, 0, 655537, 0, 655544, 0, 655646, 0, 721041, 0, 721042, 0, 721043, 0, 721044, 0, 721045, 0, 721046, 0, 721052, 0, 721062, 0, 721073, 0, 721080, 0, 721181, 0, 721182, 0, 721183, 0, 721184, 0, 786577, 0, 786588, 0, 786598, 0, 786616, 0, 786717, 0, 786718, 0, 786719, 0, 786720, 0, 852113, 0, 852124, 0, 852134, 0, 852152, 0, 852253, 0, 852254, 0, 852255, 0, 917573, 0, 917574, 0, 917575, 0, 917576, 0, 917577, 0, 917578, 0, 917579, 0, 917580, 0, 917581, 0, 917582, 0, 917583, 0, 917584, 0, 917585, 0, 917649, 0, 917660, 0, 917670, 0, 917688, 0, 983106, 0, 983107, 0, 983108, 0, 983109, 0, 983110, 0, 983111, 0, 983112, 0, 983113, 0, 983114, 0, 983115, 0, 983116, 0, 983117, 0, 983118, 0, 983119, 0, 983120, 0, 983121, 0, 983122, 0, 983185, 0, 983206, 0, 983224, 0, 1048640, 0, 1048641, 0, 1048642, 0, 1048643, 0, 1048644, 0, 1048645, 0, 1048646, 0, 1048647, 0, 1048648, 0, 1048649, 0, 1048650, 0, 1048651, 0, 1048652, 0, 1048653, 0, 1048654, 0, 1048655, 0, 1048656, 0, 1048657, 0, 1048658, 0, 1048659, 0, 1048721, 0, 1048742, 0, 1048826, 0, 1048828, 0, 1114175, 0, 1114176, 0, 1114177, 0, 1114178, 0, 1114179, 0, 1114180, 0, 1114181, 0, 1114182, 0, 1114183, 0, 1114184, 0, 1114185, 0, 1114186, 0, 1114187, 0, 1114188, 0, 1114189, 0, 1114190, 0, 1114191, 0, 1114192, 0, 1114193, 0, 1114194, 0, 1114195, 0, 1114196, 0, 1114197, 0, 1114198, 0, 1114257, 0, 1114278, 0, 1114360, 0, 1114361, 0, 1114362, 0, 1114363, 0, 1114365, 0, 1114480, 0, 1114481, 0, 1179707, 0, 1179708, 0, 1179709, 0, 1179710, 0, 1179711, 0, 1179712, 0, 1179713, 0, 1179714, 0, 1179715, 0, 1179716, 0, 1179717, 0, 1179718, 0, 1179719, 0, 1179720, 0, 1179721, 0, 1179722, 0, 1179723, 0, 1179724, 0, 1179725, 0, 1179726, 0, 1179727, 0, 1179728, 0, 1179729, 0, 1179730, 0, 1179731, 0, 1179732, 0, 1179733, 0, 1179734, 0, 1179735, 0, 1179736, 0, 1179737, 0, 1179738, 0, 1179739, 0, 1179740, 0, 1179741, 0, 1179793, 0, 1179814, 0, 1179896, 0, 1179898, 0, 1179899, 0, 1179900, 0, 1179901, 0, 1180015, 0, 1180016, 0, 1180017, 0, 1180018, 0, 1180019, 0, 1245244, 0, 1245245, 0, 1245246, 0, 1245247, 0, 1245248, 0, 1245249, 0, 1245250, 0, 1245251, 0, 1245252, 0, 1245253, 0, 1245254, 0, 1245255, 0, 1245256, 0, 1245257, 0, 1245258, 0, 1245259, 0, 1245260, 0, 1245261, 0, 1245262, 0, 1245263, 0, 1245264, 0, 1245265, 0, 1245266, 0, 1245267, 0, 1245268, 0, 1245269, 0, 1245270, 0, 1245271, 0, 1245272, 0, 1245273, 0, 1245274, 0, 1245275, 0, 1245276, 0, 1245277, 0, 1245329, 0, 1245350, 0, 1245432, 0, 1245433, 0, 1245434, 0, 1245435, 0, 1245480, 0, 1245481, 0, 1245482, 0, 1245551, 0, 1245552, 0, 1245553, 0, 1245554, 0, 1310886, 0, 1310905, 0, 1311015, 0, 1311016, 0, 1311017, 0, 1311018, 0 ) + + diff --git a/2d/space_shooter/tileset.tres b/2d/space_shooter/game_screen/level/tileset.tres similarity index 68% rename from 2d/space_shooter/tileset.tres rename to 2d/space_shooter/game_screen/level/tileset.tres index 0a62a004..975f8a3e 100644 --- a/2d/space_shooter/tileset.tres +++ b/2d/space_shooter/game_screen/level/tileset.tres @@ -1,6 +1,7 @@ [gd_resource type="TileSet" load_steps=3 format=1] -[ext_resource path="res://tile.png" type="Texture" id=1] +[ext_resource path="res://game_screen/level/tile.png" type="Texture" id=1] + [sub_resource type="RectangleShape2D" id=1] @@ -12,9 +13,12 @@ extents = Vector2( 16, 16 ) 0/name = "block" 0/texture = ExtResource( 1 ) 0/tex_offset = Vector2( 0, 0 ) +0/modulate = Color( 1, 1, 1, 1 ) 0/region = Rect2( 0, 0, 0, 0 ) 0/occluder_offset = Vector2( 16, 16 ) 0/navigation_offset = Vector2( 16, 16 ) 0/shape_offset = Vector2( 16, 16 ) 0/shapes = [ SubResource( 1 ) ] +0/one_way_collision_direction = Vector2( 0, 0 ) +0/one_way_collision_max_depth = 0.0 diff --git a/2d/space_shooter/game_state.gd b/2d/space_shooter/game_state.gd index 0aa5e1f4..d035c770 100644 --- a/2d/space_shooter/game_state.gd +++ b/2d/space_shooter/game_state.gd @@ -1,22 +1,80 @@ - +# Script has to extend any Node class because it's an Autoload +# Autoloads are put into the Scene Tree, and only Nodes can live there extends Node -# Member variables +# points in current round var points = 0 +# maximum points ever achieved var max_points = 0 +# file path to the highscore file, stored in the user directory +# see the docs for the actual path, which depends on operating system / platform +# http://docs.godotengine.org/en/2.1/learning/features/misc/data_paths.html +const HIGHSCORE_PATH = "user://highscore" + +# preloading the game's menu and actual game screen +const main_menu_scene = preload("res://main_menu/main_menu.tscn") +const main_level_scene = preload("res://game_screen/level/level.tscn") + +var menu +var game func _ready(): - var f = File.new() # Load high score - if (f.open("user://highscore", File.READ) == OK): - max_points = f.get_var() + _load_high_score() +func start_game(): + # reset the points + points = 0 + _reset_game() + + # instance the game scene + game = main_level_scene.instance() + # tell the scene tree to switch the current scene + get_tree().get_root().add_child(game) + +func abort_game(): + _reset_game() + + menu = main_menu_scene.instance() + get_tree().get_root().add_child(menu) func game_over(): if (points > max_points): max_points = points # Save high score - var f = File.new() - f.open("user://highscore", File.WRITE) + _save_high_score() + +func _reset_game(): + if game != null: + game.hide() + game.queue_free() + game = null + + if menu != null: + menu.hide() + menu.queue_free() + menu = null + +func _load_high_score(): + # start off with 0 max points in a fresh game + max_points = 0 + # initialize a file handler + var f = File.new() + # check for existing highscore file + if f.file_exists(HIGHSCORE_PATH): + # if it exists, try to open the file in READ mode + if f.open(HIGHSCORE_PATH, File.READ) == OK: + # read the current high score as a godot Variant and store it in max_points + max_points = f.get_var() + # always close the file handle + f.close() + +func _save_high_score(): + var f = File.new() + # try to open the highscore file in WRITE mode, which creates a new file if it doesn't exist + if f.open(HIGHSCORE_PATH, File.WRITE) == OK: + # store the max points as a godot Variant f.store_var(max_points) + # always close the file handle + f.close() \ No newline at end of file diff --git a/2d/space_shooter/level.tscn b/2d/space_shooter/level.tscn deleted file mode 100644 index 840d66bd..00000000 --- a/2d/space_shooter/level.tscn +++ /dev/null @@ -1,354 +0,0 @@ -[gd_scene load_steps=7 format=1] - -[ext_resource path="res://tileset.tres" type="TileSet" id=1] -[ext_resource path="res://ship.tscn" type="PackedScene" id=2] -[ext_resource path="res://asteroid.tscn" type="PackedScene" id=3] -[ext_resource path="res://enemy1.tscn" type="PackedScene" id=4] -[ext_resource path="res://enemy2.tscn" type="PackedScene" id=5] -[ext_resource path="res://parallax.tscn" type="PackedScene" id=6] - -[node name="Node2D" type="Node2D"] - -[node name="TileMap" type="TileMap" parent="."] - -mode = 0 -tile_set = ExtResource( 1 ) -cell/size = Vector2( 32, 32 ) -cell/quadrant_size = 16 -cell/custom_transform = Matrix32( 1, 0, 0, 1, 0, 0 ) -cell/half_offset = 2 -cell/tile_origin = 0 -cell/y_sort = false -collision/use_kinematic = false -collision/friction = 1.0 -collision/bounce = 0.0 -collision/layers = 1 -collision/mask = 1 -occluder/light_mask = 1 -tile_data = IntArray( 16, 0, 17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24, 0, 25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32, 0, 33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 0, 39, 0, 63, 0, 64, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0, 105, 0, 106, 0, 107, 0, 108, 0, 109, 0, 110, 0, 111, 0, 112, 0, 113, 0, 114, 0, 115, 0, 116, 0, 117, 0, 118, 0, 119, 0, 120, 0, 121, 0, 122, 0, 123, 0, 124, 0, 125, 0, 126, 0, 127, 0, 128, 0, 145, 0, 184, 0, 65556, 0, 65557, 0, 65558, 0, 65559, 0, 65560, 0, 65561, 0, 65562, 0, 65563, 0, 65564, 0, 65565, 0, 65566, 0, 65567, 0, 65568, 0, 65569, 0, 65570, 0, 65571, 0, 65572, 0, 65573, 0, 65574, 0, 65575, 0, 65599, 0, 65600, 0, 65637, 0, 65638, 0, 65639, 0, 65640, 0, 65641, 0, 65642, 0, 65643, 0, 65644, 0, 65645, 0, 65646, 0, 65647, 0, 65648, 0, 65649, 0, 65650, 0, 65651, 0, 65652, 0, 65653, 0, 65654, 0, 65655, 0, 65656, 0, 65657, 0, 65658, 0, 65659, 0, 65660, 0, 65661, 0, 65662, 0, 65663, 0, 65664, 0, 65681, 0, 65720, 0, 131094, 0, 131095, 0, 131096, 0, 131097, 0, 131098, 0, 131099, 0, 131100, 0, 131101, 0, 131102, 0, 131103, 0, 131104, 0, 131105, 0, 131106, 0, 131107, 0, 131108, 0, 131109, 0, 131135, 0, 131136, 0, 131137, 0, 131176, 0, 131177, 0, 131178, 0, 131179, 0, 131180, 0, 131181, 0, 131182, 0, 131183, 0, 131184, 0, 131185, 0, 131186, 0, 131187, 0, 131188, 0, 131189, 0, 131190, 0, 131191, 0, 131192, 0, 131193, 0, 131194, 0, 131195, 0, 131196, 0, 131197, 0, 131198, 0, 131199, 0, 131217, 0, 131256, 0, 196631, 0, 196632, 0, 196633, 0, 196634, 0, 196635, 0, 196636, 0, 196637, 0, 196638, 0, 196639, 0, 196640, 0, 196641, 0, 196642, 0, 196671, 0, 196672, 0, 196673, 0, 196716, 0, 196717, 0, 196718, 0, 196719, 0, 196720, 0, 196721, 0, 196722, 0, 196723, 0, 196724, 0, 196725, 0, 196726, 0, 196727, 0, 196728, 0, 196729, 0, 196730, 0, 196753, 0, 196792, 0, 262169, 0, 262170, 0, 262171, 0, 262172, 0, 262173, 0, 262174, 0, 262175, 0, 262207, 0, 262208, 0, 262209, 0, 262256, 0, 262257, 0, 262258, 0, 262259, 0, 262260, 0, 262261, 0, 262262, 0, 262263, 0, 262289, 0, 262328, 0, 327707, 0, 327708, 0, 327743, 0, 327744, 0, 327825, 0, 327826, 0, 327827, 0, 327828, 0, 327829, 0, 327830, 0, 327831, 0, 327832, 0, 327833, 0, 327834, 0, 327835, 0, 327836, 0, 327845, 0, 327846, 0, 327847, 0, 327848, 0, 327849, 0, 327850, 0, 327851, 0, 327852, 0, 327853, 0, 327854, 0, 327855, 0, 327856, 0, 327857, 0, 327864, 0, 327911, 0, 327912, 0, 328010, 0, 328011, 0, 328012, 0, 328013, 0, 328014, 0, 393372, 0, 393393, 0, 393400, 0, 393445, 0, 393446, 0, 393447, 0, 393448, 0, 393548, 0, 393549, 0, 393550, 0, 458908, 0, 458929, 0, 458936, 0, 458981, 0, 458982, 0, 458983, 0, 524444, 0, 524465, 0, 524472, 0, 524517, 0, 524518, 0, 589980, 0, 590001, 0, 590008, 0, 655516, 0, 655521, 0, 655522, 0, 655523, 0, 655524, 0, 655525, 0, 655526, 0, 655537, 0, 655544, 0, 655646, 0, 721041, 0, 721042, 0, 721043, 0, 721044, 0, 721045, 0, 721046, 0, 721052, 0, 721062, 0, 721073, 0, 721080, 0, 721181, 0, 721182, 0, 721183, 0, 721184, 0, 786577, 0, 786588, 0, 786598, 0, 786616, 0, 786717, 0, 786718, 0, 786719, 0, 786720, 0, 852113, 0, 852124, 0, 852134, 0, 852152, 0, 852253, 0, 852254, 0, 852255, 0, 917573, 0, 917574, 0, 917575, 0, 917576, 0, 917577, 0, 917578, 0, 917579, 0, 917580, 0, 917581, 0, 917582, 0, 917583, 0, 917584, 0, 917585, 0, 917649, 0, 917660, 0, 917670, 0, 917688, 0, 983106, 0, 983107, 0, 983108, 0, 983109, 0, 983110, 0, 983111, 0, 983112, 0, 983113, 0, 983114, 0, 983115, 0, 983116, 0, 983117, 0, 983118, 0, 983119, 0, 983120, 0, 983121, 0, 983122, 0, 983185, 0, 983206, 0, 983224, 0, 1048640, 0, 1048641, 0, 1048642, 0, 1048643, 0, 1048644, 0, 1048645, 0, 1048646, 0, 1048647, 0, 1048648, 0, 1048649, 0, 1048650, 0, 1048651, 0, 1048652, 0, 1048653, 0, 1048654, 0, 1048655, 0, 1048656, 0, 1048657, 0, 1048658, 0, 1048659, 0, 1048721, 0, 1048742, 0, 1048826, 0, 1048828, 0, 1114175, 0, 1114176, 0, 1114177, 0, 1114178, 0, 1114179, 0, 1114180, 0, 1114181, 0, 1114182, 0, 1114183, 0, 1114184, 0, 1114185, 0, 1114186, 0, 1114187, 0, 1114188, 0, 1114189, 0, 1114190, 0, 1114191, 0, 1114192, 0, 1114193, 0, 1114194, 0, 1114195, 0, 1114196, 0, 1114197, 0, 1114198, 0, 1114257, 0, 1114278, 0, 1114360, 0, 1114361, 0, 1114362, 0, 1114363, 0, 1114365, 0, 1114480, 0, 1114481, 0, 1179707, 0, 1179708, 0, 1179709, 0, 1179710, 0, 1179711, 0, 1179712, 0, 1179713, 0, 1179714, 0, 1179715, 0, 1179716, 0, 1179717, 0, 1179718, 0, 1179719, 0, 1179720, 0, 1179721, 0, 1179722, 0, 1179723, 0, 1179724, 0, 1179725, 0, 1179726, 0, 1179727, 0, 1179728, 0, 1179729, 0, 1179730, 0, 1179731, 0, 1179732, 0, 1179733, 0, 1179734, 0, 1179735, 0, 1179736, 0, 1179737, 0, 1179738, 0, 1179739, 0, 1179740, 0, 1179741, 0, 1179793, 0, 1179814, 0, 1179896, 0, 1179898, 0, 1179899, 0, 1179900, 0, 1179901, 0, 1180015, 0, 1180016, 0, 1180017, 0, 1180018, 0, 1180019, 0, 1245244, 0, 1245245, 0, 1245246, 0, 1245247, 0, 1245248, 0, 1245249, 0, 1245250, 0, 1245251, 0, 1245252, 0, 1245253, 0, 1245254, 0, 1245255, 0, 1245256, 0, 1245257, 0, 1245258, 0, 1245259, 0, 1245260, 0, 1245261, 0, 1245262, 0, 1245263, 0, 1245264, 0, 1245265, 0, 1245266, 0, 1245267, 0, 1245268, 0, 1245269, 0, 1245270, 0, 1245271, 0, 1245272, 0, 1245273, 0, 1245274, 0, 1245275, 0, 1245276, 0, 1245277, 0, 1245329, 0, 1245350, 0, 1245432, 0, 1245433, 0, 1245434, 0, 1245435, 0, 1245480, 0, 1245481, 0, 1245482, 0, 1245551, 0, 1245552, 0, 1245553, 0, 1245554, 0, 1310886, 0, 1310905, 0, 1311015, 0, 1311016, 0, 1311017, 0, 1311018, 0 ) - -[node name="rail" parent="." instance=ExtResource( 2 )] - -[node name="enemies" type="Node2D" parent="."] - -[node name="asteroid" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 1797.52, 105.736 ) -linear_damp = 0.1 - -[node name="asteroid1" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 1666.61, 304.621 ) -linear_damp = 0.1 - -[node name="asteroid2" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 1988.85, 443.086 ) -linear_damp = 0.1 - -[node name="asteroid3" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 2595.58, 103.219 ) -linear_damp = 0.1 - -[node name="asteroid4" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 3229.99, 299.586 ) -linear_damp = 0.1 - -[node name="asteroid5" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 3592.52, 541.269 ) -linear_damp = 0.1 - -[node name="asteroid6" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 4571.84, 216.508 ) -linear_damp = 0.1 - -[node name="asteroid7" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 4571.84, 284.481 ) -linear_damp = 0.1 - -[node name="asteroid8" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 4571.84, 360.007 ) -linear_damp = 0.1 - -[node name="asteroid9" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 5140.8, 108.254 ) -linear_damp = 0.1 - -[node name="asteroid10" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 5168.5, 475.814 ) -linear_damp = 0.1 - -[node name="asteroid11" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 5767.67, 113.289 ) -linear_damp = 0.1 - -[node name="asteroid12" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 6107.53, 480.849 ) -linear_damp = 0.1 - -[node name="asteroid13" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 6364.32, 105.736 ) -linear_damp = 0.1 - -[node name="asteroid14" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 6731.88, 573.997 ) -linear_damp = 0.1 - -[node name="asteroid15" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 7033.99, 166.157 ) -linear_damp = 0.1 - -[node name="asteroid16" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 6424.74, 352.454 ) -linear_damp = 0.1 - -[node name="asteroid17" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 7263.08, 80.5608 ) -linear_damp = 0.1 - -[node name="asteroid18" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 7177.49, 541.269 ) -linear_damp = 0.1 - -[node name="asteroid19" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 7066.71, 344.902 ) -linear_damp = 0.1 - -[node name="asteroid20" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 7655.82, 118.324 ) -linear_damp = 0.1 - -[node name="asteroid21" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 7540.01, 324.762 ) -linear_damp = 0.1 - -[node name="asteroid22" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 7764.07, 566.445 ) -linear_damp = 0.1 - -[node name="asteroid23" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 7872.33, 216.508 ) -linear_damp = 0.1 - -[node name="asteroid24" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 8458.91, 95.666 ) -linear_damp = 0.1 - -[node name="asteroid25" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 8786.19, 231.613 ) -linear_damp = 0.1 - -[node name="asteroid26" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 8599.89, 551.339 ) -linear_damp = 0.1 - -[node name="asteroid27" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 8353.17, 289.516 ) -linear_damp = 0.1 - -[node name="asteroid28" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 8995.14, 95.6658 ) -linear_damp = 0.1 - -[node name="asteroid29" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 9294.73, 579.032 ) -linear_damp = 0.1 - -[node name="asteroid30" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 9392.91, 140.981 ) -linear_damp = 0.1 - -[node name="asteroid31" parent="enemies" instance=ExtResource( 3 )] - -transform/pos = Vector2( 9644.67, 281.963 ) -linear_damp = 0.1 - -[node name="enemy1" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 2920.34, 365.042 ) - -[node name="enemy2" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 3894.62, 506.024 ) - -[node name="enemy3" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 4325.12, 302.104 ) - -[node name="enemy4" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 4753.1, 506.024 ) - -[node name="enemy5" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 5158.43, 211.473 ) - -[node name="enemy6" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 5490.74, 349.937 ) - -[node name="enemy7" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 5765.15, 546.305 ) - -[node name="enemy8" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 6142.78, 244.201 ) - -[node name="enemy9" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 6701.67, 221.543 ) - -[node name="enemy10" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 6701.67, 352.455 ) - -[node name="enemy11" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 6706.71, 500.989 ) - -[node name="enemy12" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 6711.74, 566.445 ) - -[node name="enemy13" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 7157.35, 332.314 ) - -[node name="enemy14" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 7421.69, 511.059 ) - -[node name="enemy15" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 7887.43, 239.166 ) - -[node name="enemy16" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 8463.95, 382.665 ) - -[node name="enemy17" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 9065.64, 244.201 ) - -[node name="enemy18" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 8967.46, 566.445 ) - -[node name="enemy19" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 9483.55, 422.946 ) - -[node name="enemy20" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 9687.47, 234.131 ) - -[node name="enemy21" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 9815.86, 579.033 ) - -[node name="enemy22" parent="enemies" instance=ExtResource( 4 )] - -transform/pos = Vector2( 9815.86, 579.033 ) - -[node name="enemy2 2" parent="enemies" instance=ExtResource( 5 )] - -transform/pos = Vector2( 4759.97, 278.527 ) -linear_damp = 0.1 - -[node name="enemy23" parent="enemies" instance=ExtResource( 5 )] - -transform/pos = Vector2( 6277.15, 559.36 ) -linear_damp = 0.1 - -[node name="enemy24" parent="enemies" instance=ExtResource( 5 )] - -transform/pos = Vector2( 7136.77, 100.438 ) -linear_damp = 0.1 - -[node name="enemy25" parent="enemies" instance=ExtResource( 5 )] - -transform/pos = Vector2( 7766.93, 370.996 ) -linear_damp = 0.1 - -[node name="enemy26" parent="enemies" instance=ExtResource( 5 )] - -transform/pos = Vector2( 7890.23, 309.35 ) -linear_damp = 0.1 - -[node name="enemy27" parent="enemies" instance=ExtResource( 5 )] - -transform/pos = Vector2( 8006.67, 237.43 ) -linear_damp = 0.1 - -[node name="enemy28" parent="enemies" instance=ExtResource( 5 )] - -transform/pos = Vector2( 8664.23, 257.978 ) -linear_damp = 0.1 - -[node name="enemy29" parent="enemies" instance=ExtResource( 5 )] - -transform/pos = Vector2( 8660.8, 357.297 ) -linear_damp = 0.1 - -[node name="enemy30" parent="enemies" instance=ExtResource( 5 )] - -transform/pos = Vector2( 8657.38, 453.191 ) -linear_damp = 0.1 - -[node name="enemy31" parent="enemies" instance=ExtResource( 5 )] - -transform/pos = Vector2( 9475.9, 189.483 ) -linear_damp = 0.1 - -[node name="enemy32" parent="enemies" instance=ExtResource( 5 )] - -transform/pos = Vector2( 9564.95, 234.005 ) -linear_damp = 0.1 - -[node name="enemy33" parent="enemies" instance=ExtResource( 5 )] - -transform/pos = Vector2( 9674.54, 281.952 ) -linear_damp = 0.1 - -[node name="enemy34" parent="enemies" instance=ExtResource( 5 )] - -transform/pos = Vector2( 9575.22, 391.545 ) -linear_damp = 0.1 - -[node name="enemy35" parent="enemies" instance=ExtResource( 5 )] - -transform/pos = Vector2( 9458.78, 446.342 ) -linear_damp = 0.1 - -[node name="parallax" parent="." instance=ExtResource( 6 )] - - diff --git a/2d/space_shooter/main_menu.gd b/2d/space_shooter/main_menu.gd deleted file mode 100644 index 0c7e0de5..00000000 --- a/2d/space_shooter/main_menu.gd +++ /dev/null @@ -1,11 +0,0 @@ - -extends Control - - -func _ready(): - get_node("score").set_text("HIGH SCORE: " + str(get_node("/root/game_state").max_points)) - - -func _on_play_pressed(): - get_node("/root/game_state").points = 0 - get_tree().change_scene("res://level.tscn") diff --git a/2d/space_shooter/main_menu.tscn b/2d/space_shooter/main_menu.tscn deleted file mode 100644 index e5179161..00000000 --- a/2d/space_shooter/main_menu.tscn +++ /dev/null @@ -1,64 +0,0 @@ -[gd_scene load_steps=2 format=1] - -[ext_resource path="res://main_menu.gd" type="Script" id=1] - -[node name="main_screen" type="Control"] - -anchor/right = 1 -anchor/bottom = 1 -focus/ignore_mouse = false -focus/stop_mouse = true -size_flags/horizontal = 2 -size_flags/vertical = 2 -margin/left = 0.0 -margin/top = 0.0 -margin/right = 0.0 -margin/bottom = 0.0 -script/script = ExtResource( 1 ) - -[node name="title" type="Label" parent="."] - -focus/ignore_mouse = true -focus/stop_mouse = true -size_flags/horizontal = 2 -margin/left = 405.0 -margin/top = 86.0 -margin/right = 547.0 -margin/bottom = 99.0 -text = "S P A C E S H O O T E R" -percent_visible = 1.0 -lines_skipped = 0 -max_lines_visible = -1 - -[node name="score" type="Label" parent="."] - -focus/ignore_mouse = true -focus/stop_mouse = true -size_flags/horizontal = 2 -margin/left = 349.0 -margin/top = 204.0 -margin/right = 585.0 -margin/bottom = 218.0 -text = "HIGH SCORE:" -align = 1 -percent_visible = 1.0 -lines_skipped = 0 -max_lines_visible = -1 - -[node name="play" type="Button" parent="."] - -focus/ignore_mouse = false -focus/stop_mouse = true -size_flags/horizontal = 2 -size_flags/vertical = 2 -margin/left = 412.0 -margin/top = 390.0 -margin/right = 535.0 -margin/bottom = 442.0 -toggle_mode = false -text = "PLAY" -flat = false - -[connection signal="pressed" from="play" to="." method="_on_play_pressed"] - - diff --git a/2d/space_shooter/main_menu/main_menu.gd b/2d/space_shooter/main_menu/main_menu.gd new file mode 100644 index 00000000..faa2e3ec --- /dev/null +++ b/2d/space_shooter/main_menu/main_menu.gd @@ -0,0 +1,18 @@ +extends Control + +# get a reference to the score label +onready var score_label = get_node("VBoxContainer/score") +# set up a string format template for the high score display +const HIGH_SCORE_TEXT = "HIGH SCORE: %d" + +func _ready(): + # game_state is an Autoloaded Singleton (see Project Settings), making it globally available and persistent + game_state.menu = self + # grab the current highscore from game_state and update the score label + score_label.set_text(HIGH_SCORE_TEXT % game_state.max_points) + +# response function for the "play" button's "pressed" signal +# the connection is set up on the "play" node, using the "Signals" sub-tab in the "Node" dock +func _on_play_pressed(): + # tell the game_state to start a new game, which resets the current score to 0 and switches to the level scene + game_state.start_game() \ No newline at end of file diff --git a/2d/space_shooter/main_menu/main_menu.tscn b/2d/space_shooter/main_menu/main_menu.tscn new file mode 100644 index 00000000..d0d7a9bf --- /dev/null +++ b/2d/space_shooter/main_menu/main_menu.tscn @@ -0,0 +1,106 @@ +[gd_scene load_steps=4 format=1] + +[ext_resource path="res://main_menu/main_menu.gd" type="Script" id=1] +[ext_resource path="res://main_menu/space.png" type="Texture" id=2] +[ext_resource path="res://effects/background/parallax.tscn" type="PackedScene" id=3] + +[node name="main_screen" type="Control"] + +anchor/right = 1 +anchor/bottom = 1 +focus/ignore_mouse = false +focus/stop_mouse = true +size_flags/horizontal = 2 +size_flags/vertical = 2 +margin/left = 0.0 +margin/top = 0.0 +margin/right = 0.0 +margin/bottom = 0.0 +script/script = ExtResource( 1 ) + +[node name="VBoxContainer" type="VBoxContainer" parent="."] + +anchor/right = 1 +anchor/bottom = 1 +focus/ignore_mouse = false +focus/stop_mouse = false +size_flags/horizontal = 3 +size_flags/vertical = 3 +margin/left = 0.0 +margin/top = 0.0 +margin/right = 0.0 +margin/bottom = 0.0 +custom_constants/separation = 16 +alignment = 1 + +[node name="TextureFrame" type="TextureFrame" parent="VBoxContainer"] + +focus/ignore_mouse = true +focus/stop_mouse = true +size_flags/horizontal = 2 +size_flags/vertical = 2 +margin/left = 0.0 +margin/top = 62.0 +margin/right = 1024.0 +margin/bottom = 230.0 +texture = ExtResource( 2 ) +stretch_mode = 6 + +[node name="title" type="Label" parent="VBoxContainer"] + +rect/min_size = Vector2( 1024, 100 ) +focus/ignore_mouse = true +focus/stop_mouse = true +size_flags/horizontal = 3 +size_flags/vertical = 0 +margin/left = 0.0 +margin/top = 246.0 +margin/right = 1024.0 +margin/bottom = 346.0 +text = "S P A C E S H O O T E R" +align = 1 +valign = 1 +percent_visible = 1.0 +lines_skipped = 0 +max_lines_visible = -1 + +[node name="score" type="Label" parent="VBoxContainer"] + +rect/min_size = Vector2( 0, 100 ) +focus/ignore_mouse = true +focus/stop_mouse = true +size_flags/horizontal = 3 +size_flags/vertical = 0 +margin/left = 0.0 +margin/top = 362.0 +margin/right = 1024.0 +margin/bottom = 462.0 +text = "HIGH SCORE:" +align = 1 +valign = 1 +percent_visible = 1.0 +lines_skipped = 0 +max_lines_visible = -1 + +[node name="play" type="Button" parent="VBoxContainer"] + +rect/min_size = Vector2( 120, 60 ) +focus/ignore_mouse = false +focus/stop_mouse = true +size_flags/horizontal = 0 +size_flags/vertical = 0 +margin/left = 452.0 +margin/top = 478.0 +margin/right = 572.0 +margin/bottom = 538.0 +toggle_mode = false +enabled_focus_mode = 2 +shortcut = null +text = "PLAY" +flat = false + +[node name="parallax" parent="." instance=ExtResource( 3 )] + +[connection signal="pressed" from="VBoxContainer/play" to="." method="_on_play_pressed"] + + diff --git a/2d/space_shooter/main_menu/space.png b/2d/space_shooter/main_menu/space.png new file mode 100644 index 0000000000000000000000000000000000000000..9448abc3e34a386b9e55e6fcd4224d0f9b5d3895 GIT binary patch literal 67109 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 0.0: + shot_timer -= delta + + # the player can shoot if the timer is back to zero + can_shoot = shot_timer <= 0.0 + + # if the player is alive, allowed to shoot and pressing space to shoot.. + if (can_shoot and shooting and not killed): + # instance a shot + var shot = Shot.instance() + # Use the Position2D named "shoot_from" as reference + shot.set_pos(shot_anchor.get_global_pos()) + # add the shot to projectile container so it moves independently from the ship + if projectile_container != null: + projectile_container.add_child(shot) + # Play sound + get_node("sfx").play("shoot") + # delay the next shot + shot_timer = SHOT_COOLDOWN + +func _ready(): + screen_size = get_viewport().get_rect().size + set_fixed_process(true) + +func _hit_something(): + if (killed): + return + killed = true + # disable the collider + call_deferred("set_enable_monitoring", false) + call_deferred("set_monitorable", false) + # play on-death effects + get_node("anim").play("explode") + get_node("sfx").play("sound_explode") + # notify listeners that the player died + emit_signal("player_died") + # disable processing + set_fixed_process(false) + +# the block tiles in a level have StaticBody2D colliders, touching them kills the player ship +func _on_ship_body_enter(body): + _hit_something() + +# colliding with the area of an enemy (asteroid, enemy1, enemy2 scenes) kills the player ship +func _on_ship_area_enter(area): + # check if the colliding node is in the "enemy" node group + if area.is_in_group("enemy"): + _hit_something() + +# setup function to obtain a reference to the bullet container node +func set_projectile_container(container): + projectile_container = container + +# other objects (enemy projectiles) use this to tell the player ship that it was hit +func take_damage(): + _hit_something() + \ No newline at end of file diff --git a/2d/space_shooter/ship.png b/2d/space_shooter/player/ship.png similarity index 100% rename from 2d/space_shooter/ship.png rename to 2d/space_shooter/player/ship.png diff --git a/2d/space_shooter/player/ship.png.flags b/2d/space_shooter/player/ship.png.flags new file mode 100644 index 00000000..226e8a38 --- /dev/null +++ b/2d/space_shooter/player/ship.png.flags @@ -0,0 +1,2 @@ +filter=false +gen_mipmaps=false diff --git a/2d/space_shooter/shoot.png b/2d/space_shooter/player/shoot.png similarity index 100% rename from 2d/space_shooter/shoot.png rename to 2d/space_shooter/player/shoot.png diff --git a/2d/space_shooter/player/shoot.png.flags b/2d/space_shooter/player/shoot.png.flags new file mode 100644 index 00000000..226e8a38 --- /dev/null +++ b/2d/space_shooter/player/shoot.png.flags @@ -0,0 +1,2 @@ +filter=false +gen_mipmaps=false diff --git a/2d/space_shooter/shot.gd b/2d/space_shooter/player/shot.gd similarity index 69% rename from 2d/space_shooter/shot.gd rename to 2d/space_shooter/player/shot.gd index 936dde73..3d9a4f36 100644 --- a/2d/space_shooter/shot.gd +++ b/2d/space_shooter/player/shot.gd @@ -1,40 +1,38 @@ - extends Area2D # Member variables const SPEED = 800 var hit = false - +var motion = Vector2() func _process(delta): - translate(Vector2(delta*SPEED, 0)) - + translate(motion * delta) func _ready(): + motion = Vector2(SPEED, 0) set_process(true) - func _hit_something(): if (hit): return hit = true set_process(false) get_node("anim").play("splash") - + # disable collisions + call_deferred("set_enable_monitoring", false) + call_deferred("set_monitorable", false) func _on_visibility_exit_screen(): queue_free() - func _on_shot_area_enter(area): # Hit an enemy or asteroid if (area.has_method("destroy")): - # Duck typing at it's best + # Duck typing at its best area.destroy() _hit_something() - func _on_shot_body_enter(body): # Hit the tilemap _hit_something() diff --git a/2d/space_shooter/shot.tscn b/2d/space_shooter/player/shot.tscn similarity index 73% rename from 2d/space_shooter/shot.tscn rename to 2d/space_shooter/player/shot.tscn index 2913eaf5..756b9360 100644 --- a/2d/space_shooter/shot.tscn +++ b/2d/space_shooter/player/shot.tscn @@ -1,7 +1,8 @@ [gd_scene load_steps=6 format=1] -[ext_resource path="res://shot.gd" type="Script" id=1] -[ext_resource path="res://shoot.png" type="Texture" id=2] +[ext_resource path="res://player/shot.gd" type="Script" id=1] +[ext_resource path="res://player/shoot.png" type="Texture" id=2] + [sub_resource type="RectangleShape2D" id=1] @@ -21,17 +22,39 @@ step = 0.1 tracks/0/type = "value" tracks/0/path = NodePath("hit_splash:config/emitting") tracks/0/interp = 1 -tracks/0/keys = { "cont":false, "times":FloatArray( 0, 0.1 ), "transitions":FloatArray( 1, 1 ), "values":[ true, false ] } +tracks/0/imported = false +tracks/0/keys = { +"times": FloatArray( 0, 0.1 ), +"transitions": FloatArray( 1, 1 ), +"update": 1, +"values": [ true, false ] +} tracks/1/type = "method" tracks/1/path = NodePath(".") tracks/1/interp = 1 -tracks/1/keys = { "times":FloatArray( 1 ), "transitions":FloatArray( 1 ), "values":[ { "args":[ ], "method":"queue_free" } ] } +tracks/1/imported = false +tracks/1/keys = { +"times": FloatArray( 1 ), +"transitions": FloatArray( 1 ), +"values": [ { +"args": [ ], +"method": "queue_free" +} ] +} tracks/2/type = "value" tracks/2/path = NodePath("sprite:visibility/visible") tracks/2/interp = 1 -tracks/2/keys = { "cont":false, "times":FloatArray( 0 ), "transitions":FloatArray( 1 ), "values":[ false ] } +tracks/2/imported = false +tracks/2/keys = { +"times": FloatArray( 0 ), +"transitions": FloatArray( 1 ), +"update": 1, +"values": [ false ] +} -[node name="shot" type="Area2D"] +[node name="shot" type="Area2D" groups=[ +"player_shot", +]] input/pickable = true shapes/0/shape = SubResource( 1 ) @@ -45,25 +68,30 @@ script/script = ExtResource( 1 ) [node name="visibility" type="VisibilityNotifier2D" parent="."] -transform/pos = Vector2( 1.8353, -0.0742126 ) -transform/scale = Vector2( 1.54149, 0.770745 ) -rect = Rect2( -10, -10, 20, 20 ) +rect = Rect2( -10, -4, 20, 8 ) [node name="sprite" type="Sprite" parent="."] texture = ExtResource( 2 ) +__meta__ = { +"_edit_lock_": true +} [node name="collision" type="CollisionShape2D" parent="."] shape = SubResource( 1 ) trigger = false _update_shape_index = -1 +__meta__ = { +"_edit_lock_": true +} [node name="hit_splash" type="Particles2D" parent="."] config/amount = 32 config/lifetime = 0.5 config/emitting = false +config/process_mode = 1 config/explosiveness = 0.1 params/direction = 0.0 params/spread = 180.0 @@ -82,6 +110,9 @@ params/hue_variation = 0.0 params/anim_speed_scale = 1.0 params/anim_initial_pos = 0.0 color/color_ramp = SubResource( 2 ) +__meta__ = { +"_edit_lock_": true +} [node name="anim" type="AnimationPlayer" parent="."] @@ -94,10 +125,10 @@ playback/speed = 1.0 blend_times = [ ] autoplay = "" -[connection signal="body_enter" from="." to="." method="_on_shot_body_enter"] - [connection signal="area_enter" from="." to="." method="_on_shot_area_enter"] +[connection signal="body_enter" from="." to="." method="_on_shot_body_enter"] + [connection signal="exit_screen" from="visibility" to="." method="_on_visibility_exit_screen"] diff --git a/2d/space_shooter/rail.gd b/2d/space_shooter/rail.gd deleted file mode 100644 index a9559806..00000000 --- a/2d/space_shooter/rail.gd +++ /dev/null @@ -1,19 +0,0 @@ - -extends Node2D - -# Member variables -const SPEED = 200 -var offset = 0 - - -func stop(): - set_fixed_process(false) - - -func _fixed_process(delta): - offset += delta*SPEED - set_pos(Vector2(offset, 0)) - - -func _ready(): - set_fixed_process(true) diff --git a/2d/space_shooter/ship.gd b/2d/space_shooter/ship.gd deleted file mode 100644 index 43751112..00000000 --- a/2d/space_shooter/ship.gd +++ /dev/null @@ -1,80 +0,0 @@ - -extends Area2D - -# Member variables -const SPEED = 200 - -var screen_size -var prev_shooting = false -var killed = false - - -func _fixed_process(delta): - var motion = Vector2() - if Input.is_action_pressed("move_up"): - motion += Vector2(0, -1) - if Input.is_action_pressed("move_down"): - motion += Vector2(0, 1) - if Input.is_action_pressed("move_left"): - motion += Vector2(-1, 0) - if Input.is_action_pressed("move_right"): - motion += Vector2(1, 0) - var shooting = Input.is_action_pressed("shoot") - - var pos = get_pos() - - pos += motion*delta*SPEED - if (pos.x < 0): - pos.x = 0 - if (pos.x > screen_size.x): - pos.x = screen_size.x - if (pos.y < 0): - pos.y = 0 - if (pos.y > screen_size.y): - pos.y = screen_size.y - - set_pos(pos) - - if (shooting and not prev_shooting and not killed): - # Just pressed - var shot = preload("res://shot.tscn").instance() - # Use the Position2D as reference - shot.set_pos(get_node("shootfrom").get_global_pos()) - # Put it two parents above, so it is not moved by us - get_node("../..").add_child(shot) - # Play sound - get_node("sfx").play("shoot") - - prev_shooting = shooting - - # Update points counter - get_node("../hud/score_points").set_text(str(get_node("/root/game_state").points)) - - -func _ready(): - screen_size = get_viewport().get_rect().size - set_fixed_process(true) - - -func _hit_something(): - if (killed): - return - killed = true - get_node("anim").play("explode") - get_node("sfx").play("sound_explode") - get_node("../hud/game_over").show() - get_node("/root/game_state").game_over() - get_parent().stop() - - -func _on_ship_body_enter(body): - _hit_something() - - -func _on_ship_area_enter(area): - if (area.has_method("is_enemy") and area.is_enemy()): - _hit_something() - - -func _on_back_to_menu_pressed(): - get_tree().change_scene("res://main_menu.tscn") diff --git a/2d/space_shooter/ship.tscn b/2d/space_shooter/ship.tscn deleted file mode 100644 index be6bf6b8..00000000 --- a/2d/space_shooter/ship.tscn +++ /dev/null @@ -1,229 +0,0 @@ -[gd_scene load_steps=12 format=1] - -[ext_resource path="res://rail.gd" type="Script" id=1] -[ext_resource path="res://ship.gd" type="Script" id=2] -[ext_resource path="res://ship.png" type="Texture" id=3] -[ext_resource path="res://fire.png" type="Texture" id=4] -[ext_resource path="res://explosion.tscn" type="PackedScene" id=5] -[ext_resource path="res://sound_shoot.wav" type="Sample" id=6] -[ext_resource path="res://sound_explode.wav" type="Sample" id=7] - -[sub_resource type="ConvexPolygonShape2D" id=1] - -custom_solver_bias = 0.0 -points = Vector2Array( 25.9104, 1.3603, -20.5637, 14.8656, -20.5637, -15.3227 ) - -[sub_resource type="ColorRamp" id=2] - -offsets = FloatArray( 0, 0.474062, 0.653631, 1 ) -colors = ColorArray( 0.154794, 0.413313, 0.991004, 1, 0.555474, 0.971578, 0, 1, 0.82934, 0.989088, 0.616085, 0.383915, 1, 1, 1, 0 ) - -[sub_resource type="Animation" id=3] - -length = 1.0 -loop = false -step = 0.1 -tracks/0/type = "value" -tracks/0/path = NodePath("sprite:visibility/visible") -tracks/0/interp = 1 -tracks/0/keys = { "cont":false, "times":FloatArray( 0, 0.1 ), "transitions":FloatArray( 1, 1 ), "values":[ true, false ] } -tracks/1/type = "value" -tracks/1/path = NodePath("thruster:config/emitting") -tracks/1/interp = 1 -tracks/1/keys = { "cont":false, "times":FloatArray( 0 ), "transitions":FloatArray( 1 ), "values":[ false ] } -tracks/2/type = "value" -tracks/2/path = NodePath("explosion:config/emitting") -tracks/2/interp = 1 -tracks/2/keys = { "cont":false, "times":FloatArray( 0, 0.1 ), "transitions":FloatArray( 1, 1 ), "values":[ true, false ] } - -[sub_resource type="SampleLibrary" id=4] - -samples/shoot = { "db":0.0, "pitch":1.0, "sample":ExtResource( 6 ) } -samples/sound_explode = { "db":0.0, "pitch":1.0, "sample":ExtResource( 7 ) } - -[node name="rail" type="Node2D"] - -script/script = ExtResource( 1 ) - -[node name="ship" type="Area2D" parent="."] - -transform/pos = Vector2( 253.607, 282.275 ) -input/pickable = true -shapes/0/shape = SubResource( 1 ) -shapes/0/transform = Matrix32( 1, 0, 0, 1, 0, 0 ) -shapes/0/trigger = false -gravity_vec = Vector2( 0, 1 ) -gravity = 98.0 -linear_damp = 0.1 -angular_damp = 1.0 -script/script = ExtResource( 2 ) - -[node name="sprite" type="Sprite" parent="ship"] - -texture = ExtResource( 3 ) - -[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="ship"] - -build_mode = 0 -polygon = Vector2Array( -20.5637, -15.3227, 25.9104, 1.3603, -20.5637, 14.8656 ) -shape_range = Vector2( -1, -1 ) -trigger = false - -[node name="thruster" type="Particles2D" parent="ship"] - -visibility/blend_mode = 1 -transform/pos = Vector2( -26.528, -0.358481 ) -transform/rot = -91.1436 -config/amount = 32 -config/lifetime = 2.0 -config/time_scale = 5.0 -config/emitting = false -config/local_space = false -config/texture = ExtResource( 4 ) -params/direction = 0.0 -params/spread = 10.0 -params/linear_velocity = 20.0 -params/spin_velocity = 0.0 -params/orbit_velocity = 0.0 -params/gravity_direction = 0.0 -params/gravity_strength = 0.0 -params/radial_accel = 0.0 -params/tangential_accel = 0.0 -params/damping = 0.0 -params/initial_angle = 0.0 -params/initial_size = 1.0 -params/final_size = 1.0 -params/hue_variation = 0.0 -params/anim_speed_scale = 1.0 -params/anim_initial_pos = 0.0 -color/color_ramp = SubResource( 2 ) - -[node name="anim" type="AnimationPlayer" parent="ship"] - -playback/process_mode = 1 -playback/default_blend_time = 0.0 -root/root = NodePath("..") -anims/explode = SubResource( 3 ) -playback/active = true -playback/speed = 1.0 -blend_times = [ ] -autoplay = "" - -[node name="shootfrom" type="Position2D" parent="ship"] - -transform/pos = Vector2( 35.3307, 0.875969 ) - -[node name="explosion" parent="ship" instance=ExtResource( 5 )] - -transform/rot = -91.1436 -config/explosiveness = 0.1 -params/gravity_strength = 9.8 - -[node name="sfx" type="SamplePlayer" parent="ship"] - -config/polyphony = 1 -config/samples = SubResource( 4 ) -default/volume_db = 0.0 -default/pitch_scale = 1.0 -default/pan = 0.0 -default/depth = 0.0 -default/height = 0.0 -default/filter/type = 0 -default/filter/cutoff = 0.0 -default/filter/resonance = 0.0 -default/filter/gain = 0.0 -default/reverb_room = 2 -default/reverb_send = 0.0 -default/chorus_send = 0.0 - -[node name="camera" type="Camera2D" parent="."] - -anchor_mode = 0 -rotating = false -current = true -zoom = Vector2( 1, 1 ) -limit/left = -10000000 -limit/top = -10000000 -limit/right = 10000000 -limit/bottom = 10000000 -drag_margin/h_enabled = true -drag_margin/v_enabled = true -smoothing/enable = false -smoothing/speed = 5.0 -drag_margin/left = 0.2 -drag_margin/top = 0.2 -drag_margin/right = 0.2 -drag_margin/bottom = 0.2 - -[node name="hud" type="CanvasLayer" parent="."] - -layer = 1 -offset = Vector2( 0, 0 ) -rotation = 0.0 -scale = Vector2( 1, 1 ) - -[node name="score" type="Label" parent="hud"] - -focus/ignore_mouse = true -focus/stop_mouse = true -size_flags/horizontal = 2 -margin/left = 15.0 -margin/top = 13.0 -margin/right = 66.0 -margin/bottom = 26.0 -text = "SCORE:" -percent_visible = 1.0 -lines_skipped = 0 -max_lines_visible = -1 - -[node name="score_points" type="Label" parent="hud"] - -focus/ignore_mouse = true -focus/stop_mouse = true -size_flags/horizontal = 2 -margin/left = 70.0 -margin/top = 13.0 -margin/right = 121.0 -margin/bottom = 26.0 -text = "0" -align = 1 -percent_visible = 1.0 -lines_skipped = 0 -max_lines_visible = -1 - -[node name="back_to_menu" type="Button" parent="hud"] - -focus/ignore_mouse = false -focus/stop_mouse = true -size_flags/horizontal = 2 -size_flags/vertical = 2 -margin/left = 911.0 -margin/top = 10.0 -margin/right = 1006.0 -margin/bottom = 31.0 -toggle_mode = false -text = "Back to Menu" -flat = false - -[node name="game_over" type="Label" parent="hud"] - -visibility/visible = false -focus/ignore_mouse = true -focus/stop_mouse = true -size_flags/horizontal = 2 -margin/left = 482.0 -margin/top = 286.0 -margin/right = 564.0 -margin/bottom = 299.0 -text = "GAME_OVER" -percent_visible = 1.0 -lines_skipped = 0 -max_lines_visible = -1 - -[connection signal="body_enter" from="ship" to="ship" method="_on_ship_body_enter"] - -[connection signal="area_enter" from="ship" to="ship" method="_on_ship_area_enter"] - -[connection signal="pressed" from="hud/back_to_menu" to="ship" method="_on_back_to_menu_pressed"] - -