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
39
2d/space_shooter/README.md
Normal 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
|
||||
@@ -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]
|
||||
|
||||
@@ -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()
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,2 @@
|
||||
filter=false
|
||||
gen_mipmaps=false
|
||||
|
Before Width: | Height: | Size: 275 B After Width: | Height: | Size: 275 B |
2
2d/space_shooter/effects/background/big_star.png.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
filter=false
|
||||
gen_mipmaps=false
|
||||
@@ -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"]
|
||||
|
Before Width: | Height: | Size: 90 B After Width: | Height: | Size: 90 B |
2
2d/space_shooter/effects/background/small_star.png.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
filter=false
|
||||
gen_mipmaps=false
|
||||
@@ -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]
|
||||
|
||||
|
Before Width: | Height: | Size: 357 B After Width: | Height: | Size: 357 B |
2
2d/space_shooter/effects/particles/fire.png.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
filter=false
|
||||
gen_mipmaps=false
|
||||
59
2d/space_shooter/enemies/asteroid/asteroid.gd
Normal 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()
|
||||
@@ -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 )
|
||||
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
2
2d/space_shooter/enemies/asteroid/meteorite.png.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
filter=false
|
||||
gen_mipmaps=false
|
||||
77
2d/space_shooter/enemies/shooter/enemy2.gd
Normal 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()
|
||||
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
2
2d/space_shooter/enemies/shooter/enemy2.png.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
filter=false
|
||||
gen_mipmaps=false
|
||||
@@ -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="."]
|
||||
|
||||
@@ -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()
|
||||
|
Before Width: | Height: | Size: 330 B After Width: | Height: | Size: 330 B |
2
2d/space_shooter/enemies/shooter/enemy_shot.png.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
filter=false
|
||||
gen_mipmaps=false
|
||||
@@ -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"]
|
||||
|
||||
|
||||
41
2d/space_shooter/enemies/ufo/enemy1.gd
Normal 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()
|
||||
|
||||
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
2
2d/space_shooter/enemies/ufo/enemy1.png.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
filter=false
|
||||
gen_mipmaps=false
|
||||
@@ -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"]
|
||||
|
||||
|
||||
33
2d/space_shooter/enemies/ufo/enemy1_rail.gd
Normal 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)
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
|
||||
25
2d/space_shooter/game_screen/hud/game_hud.gd
Normal 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()
|
||||
77
2d/space_shooter/game_screen/hud/game_hud.tscn
Normal 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
|
||||
|
||||
|
||||
284
2d/space_shooter/game_screen/level/enemies.tscn
Normal 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 )
|
||||
|
||||
|
||||
33
2d/space_shooter/game_screen/level/level.gd
Normal 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()
|
||||
29
2d/space_shooter/game_screen/level/level.tscn
Normal 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",
|
||||
]]
|
||||
|
||||
|
||||
12
2d/space_shooter/game_screen/level/level_map.gd
Normal 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)
|
||||
18
2d/space_shooter/game_screen/level/level_map.tscn
Normal 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="."]
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 810 B After Width: | Height: | Size: 810 B |
2
2d/space_shooter/game_screen/level/tile.png.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
filter=false
|
||||
gen_mipmaps=false
|
||||
24
2d/space_shooter/game_screen/level/tilemap_level.tscn
Normal 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
|
||||
|
||||
@@ -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()
|
||||
@@ -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")
|
||||
@@ -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"]
|
||||
|
||||
|
||||
18
2d/space_shooter/main_menu/main_menu.gd
Normal 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()
|
||||
106
2d/space_shooter/main_menu/main_menu.tscn
Normal 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"]
|
||||
|
||||
|
||||
BIN
2d/space_shooter/main_menu/space.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
2
2d/space_shooter/main_menu/space.png.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
filter=false
|
||||
gen_mipmaps=false
|
||||
176
2d/space_shooter/player/player_ship.tscn
Normal 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"]
|
||||
|
||||
|
||||
32
2d/space_shooter/player/player_ship_on_rail.tscn
Normal 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
|
||||
|
||||
|
||||
27
2d/space_shooter/player/rail.gd
Normal 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)
|
||||
105
2d/space_shooter/player/ship.gd
Normal 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()
|
||||
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
2
2d/space_shooter/player/ship.png.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
filter=false
|
||||
gen_mipmaps=false
|
||||
|
Before Width: | Height: | Size: 222 B After Width: | Height: | Size: 222 B |
2
2d/space_shooter/player/shoot.png.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
filter=false
|
||||
gen_mipmaps=false
|
||||
@@ -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()
|
||||
@@ -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"]
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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")
|
||||
@@ -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"]
|
||||
|
||||
|
||||