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
This commit is contained in:
PetePete1984
2018-07-30 14:02:20 +02:00
parent 9587296412
commit 4221882f58
67 changed files with 1559 additions and 973 deletions

View File

@@ -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

View File

@@ -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]

View File

@@ -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()

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,2 @@
filter=false
gen_mipmaps=false

View File

Before

Width:  |  Height:  |  Size: 275 B

After

Width:  |  Height:  |  Size: 275 B

View File

@@ -0,0 +1,2 @@
filter=false
gen_mipmaps=false

View File

@@ -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"]

View File

Before

Width:  |  Height:  |  Size: 90 B

After

Width:  |  Height:  |  Size: 90 B

View File

@@ -0,0 +1,2 @@
filter=false
gen_mipmaps=false

View File

@@ -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]

View File

Before

Width:  |  Height:  |  Size: 357 B

After

Width:  |  Height:  |  Size: 357 B

View File

@@ -0,0 +1,2 @@
filter=false
gen_mipmaps=false

View File

@@ -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()

View File

@@ -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 )

View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -0,0 +1,2 @@
filter=false
gen_mipmaps=false

View File

@@ -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()

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -0,0 +1,2 @@
filter=false
gen_mipmaps=false

View File

@@ -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="."]

View File

@@ -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()

View File

Before

Width:  |  Height:  |  Size: 330 B

After

Width:  |  Height:  |  Size: 330 B

View File

@@ -0,0 +1,2 @@
filter=false
gen_mipmaps=false

View File

@@ -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"]

View File

@@ -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()

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -0,0 +1,2 @@
filter=false
gen_mipmaps=false

View File

@@ -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"]

View File

@@ -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)

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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 )

View File

@@ -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()

View File

@@ -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",
]]

View File

@@ -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)

View File

@@ -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="."]

View File

Before

Width:  |  Height:  |  Size: 810 B

After

Width:  |  Height:  |  Size: 810 B

View File

@@ -0,0 +1,2 @@
filter=false
gen_mipmaps=false

File diff suppressed because one or more lines are too long

View File

@@ -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

View File

@@ -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()

File diff suppressed because one or more lines are too long

View File

@@ -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")

View File

@@ -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"]

View File

@@ -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()

View File

@@ -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"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -0,0 +1,2 @@
filter=false
gen_mipmaps=false

View File

@@ -0,0 +1,176 @@
[gd_scene load_steps=12 format=1]
[ext_resource path="res://player/ship.gd" type="Script" id=1]
[ext_resource path="res://player/ship.png" type="Texture" id=2]
[ext_resource path="res://effects/particles/fire.png" type="Texture" id=3]
[ext_resource path="res://effects/particles/explosion.tscn" type="PackedScene" id=4]
[ext_resource path="res://effects/sounds/sound_shoot.wav" type="Sample" id=5]
[ext_resource path="res://effects/sounds/sound_explode.wav" type="Sample" id=6]
[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/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("thruster:config/emitting")
tracks/1/interp = 1
tracks/1/imported = false
tracks/1/keys = {
"times": FloatArray( 0 ),
"transitions": FloatArray( 1 ),
"update": 1,
"values": [ false ]
}
tracks/2/type = "value"
tracks/2/path = NodePath("explosion:config/emitting")
tracks/2/interp = 1
tracks/2/imported = false
tracks/2/keys = {
"times": FloatArray( 0, 0.1 ),
"transitions": FloatArray( 1, 1 ),
"update": 1,
"values": [ true, false ]
}
[sub_resource type="ColorRamp" id=4]
offsets = FloatArray( 0, 0.364725, 0.77494, 1 )
colors = ColorArray( 1, 1, 1, 1, 1, 0, 0, 1, 0.184473, 0.181601, 0.181345, 1, 1, 1, 1, 0 )
[sub_resource type="SampleLibrary" id=5]
samples/shoot = {
"db": 0.0,
"pitch": 1.0,
"priority": 0,
"sample": ExtResource( 5 )
}
samples/sound_explode = {
"db": 0.0,
"pitch": 1.0,
"priority": 0,
"sample": ExtResource( 6 )
}
[node name="ship" type="Area2D" groups=[
"player",
]]
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( 1 )
[node name="sprite" type="Sprite" parent="."]
texture = ExtResource( 2 )
[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="."]
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="."]
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/process_mode = 1
config/local_space = false
config/texture = ExtResource( 3 )
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="."]
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="shoot_from" type="Position2D" parent="."]
transform/pos = Vector2( 35.3307, 0.875969 )
[node name="explosion" parent="." instance=ExtResource( 4 )]
config/process_mode = 1
color/color_ramp = SubResource( 4 )
[node name="sfx" type="SamplePlayer" parent="."]
config/polyphony = 1
config/samples = SubResource( 5 )
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
[connection signal="area_enter" from="." to="." method="_on_ship_area_enter"]
[connection signal="body_enter" from="." to="." method="_on_ship_body_enter"]

View File

@@ -0,0 +1,32 @@
[gd_scene load_steps=3 format=1]
[ext_resource path="res://player/rail.gd" type="Script" id=1]
[ext_resource path="res://player/player_ship.tscn" type="PackedScene" id=2]
[node name="rail" type="Node2D"]
script/script = ExtResource( 1 )
[node name="ship" parent="." instance=ExtResource( 2 )]
[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

View File

@@ -0,0 +1,27 @@
extends Node2D
# Member variables
# on-rails movement speed of the game area
const SPEED = 200
# current offset of the game area
var offset = 0
var motion = Vector2()
# reference to the actual ship that is controlled by the player
onready var player_ship = get_node("ship")
# the rail is moved during _fixed_process and should stop on player death
func stop():
set_fixed_process(false)
func _fixed_process(delta):
# move the rail at SPEED pixels per second
translate(motion * delta)
func _ready():
motion = Vector2(SPEED, 0)
# connect the stop method to the player_ship's player_died signal
player_ship.connect("player_died", self, "stop")
# start processing
set_fixed_process(true)

View File

@@ -0,0 +1,105 @@
extends Area2D
# Member variables
const SPEED = 200
const SHOT_COOLDOWN = 0.16
const Shot = preload("res://player/shot.tscn")
var screen_size
var killed = false
var can_shoot = true
var shot_timer = 0
var projectile_container
onready var shot_anchor = get_node("shoot_from")
signal player_died
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()
# normally you would normalize the motion vector using motion.normalized(), so diagonal movement isn't faster
# in this case, the base speed would make dodging the tilemap impossible in some places
# additionally, it could be explained as the ship using both horizontal and vertical thrusters at once
# the better solution in the long run would be to playtest the level and make sure that every passage is playable
# pos += motion.normalized() * delta * SPEED
pos += motion * delta * SPEED
# limit the resulting position to the screen's dimensions, so the player can't fly off screen
pos.x = clamp(pos.x, 0, screen_size.x)
pos.y = clamp(pos.y, 0, screen_size.y)
set_pos(pos)
# tick down the shot cooldown
if shot_timer > 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()

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,2 @@
filter=false
gen_mipmaps=false

View File

Before

Width:  |  Height:  |  Size: 222 B

After

Width:  |  Height:  |  Size: 222 B

View File

@@ -0,0 +1,2 @@
filter=false
gen_mipmaps=false

View File

@@ -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()

View File

@@ -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"]

View File

@@ -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)

View File

@@ -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")

View File

@@ -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"]