Init commit
This commit is contained in:
commit
a4452b69c4
59 changed files with 1259 additions and 0 deletions
7
Scripts/explosion.gd
Normal file
7
Scripts/explosion.gd
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
extends Area2D
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
# Free self after 1.5 seconds
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
queue_free()
|
||||
1
Scripts/explosion.gd.uid
Normal file
1
Scripts/explosion.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://lawrmxvtimw1
|
||||
88
Scripts/game.gd
Normal file
88
Scripts/game.gd
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
extends Node2D
|
||||
|
||||
var score: int = 0
|
||||
var lives: int = 0
|
||||
var added_lives: int = 0
|
||||
var score_label: Label
|
||||
var lives_box: HBoxContainer
|
||||
var spawner: Node2D
|
||||
var player: CharacterBody2D
|
||||
var ui: CanvasLayer
|
||||
var game_over_ui: Control
|
||||
var menu_ui: Control
|
||||
var life_icon = preload("res://Scenes/life.tscn")
|
||||
var menu_scn = preload("res://Scenes/main_menu.tscn")
|
||||
var game_over_scn = preload("res://Scenes/game_over.tscn")
|
||||
var ship = preload("res://Scenes/ship.tscn")
|
||||
var life = preload("res://Sounds/extra_life.wav")
|
||||
|
||||
func _ready() -> void:
|
||||
menu_ui = menu_scn.instantiate()
|
||||
menu_ui.new_game.connect(_new_game)
|
||||
menu_ui.quit_game.connect(_quit_game)
|
||||
|
||||
await get_tree().process_frame
|
||||
ui.add_child(menu_ui)
|
||||
|
||||
func set_score(amount: int):
|
||||
score = amount
|
||||
score_label.text = "Score: " + str(score)
|
||||
|
||||
if floor(score / 10000.0) > added_lives:
|
||||
added_lives += 1
|
||||
set_lives(lives + 1)
|
||||
|
||||
var snd = AudioStreamPlayer.new()
|
||||
add_child(snd)
|
||||
snd.stream = life
|
||||
snd.play()
|
||||
snd.finished.connect(func(): snd.queue_free())
|
||||
|
||||
func set_lives(amount: int):
|
||||
lives = amount
|
||||
|
||||
var icons = lives_box.get_children()
|
||||
|
||||
var diff = lives - len(icons);
|
||||
if diff > 0:
|
||||
for i in range(diff):
|
||||
lives_box.add_child(life_icon.instantiate())
|
||||
else:
|
||||
if lives >= 0:
|
||||
for i in range(abs(diff)):
|
||||
lives_box.remove_child(icons[len(icons) - 1])
|
||||
icons.remove_at(len(icons) - 1)
|
||||
|
||||
if lives == 0:
|
||||
spawner.stop()
|
||||
for node in get_tree().get_nodes_in_group("enemies"):
|
||||
node.queue_free()
|
||||
|
||||
player.queue_free()
|
||||
|
||||
score_label.visible = false
|
||||
game_over_ui = game_over_scn.instantiate()
|
||||
game_over_ui.new_game.connect(_new_game)
|
||||
game_over_ui.set_score(score)
|
||||
ui.add_child(game_over_ui)
|
||||
|
||||
func _new_game():
|
||||
if score_label:
|
||||
score_label.visible = true
|
||||
set_lives(3)
|
||||
spawner.start()
|
||||
set_score(0)
|
||||
|
||||
if game_over_ui:
|
||||
game_over_ui.queue_free()
|
||||
|
||||
if menu_ui:
|
||||
menu_ui.queue_free()
|
||||
|
||||
player = ship.instantiate()
|
||||
player.position = get_viewport_rect().size / 2
|
||||
|
||||
get_tree().current_scene.add_child(player)
|
||||
|
||||
func _quit_game():
|
||||
get_tree().quit()
|
||||
1
Scripts/game.gd.uid
Normal file
1
Scripts/game.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bs4d2x1ylus1m
|
||||
12
Scripts/game_over.gd
Normal file
12
Scripts/game_over.gd
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
extends Control
|
||||
|
||||
signal new_game
|
||||
|
||||
func set_score(amount: int):
|
||||
$Container/Score.text = "Score: " + str(amount)
|
||||
|
||||
func _ready():
|
||||
$"Container/New Game".pressed.connect(_emit_new)
|
||||
|
||||
func _emit_new():
|
||||
emit_signal("new_game")
|
||||
1
Scripts/game_over.gd.uid
Normal file
1
Scripts/game_over.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://cf1vwutwpi4rf
|
||||
37
Scripts/laser.gd
Normal file
37
Scripts/laser.gd
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
extends Area2D
|
||||
|
||||
var explosion = preload('res://Scenes/explosion.tscn')
|
||||
var pop = preload("res://Sounds/Explosion.wav")
|
||||
var speed = 750
|
||||
|
||||
var half_size: Vector2 # half of sprite dimensions (used for wrapping)
|
||||
|
||||
func _ready() -> void:
|
||||
connect("area_entered", _on_body_entered)
|
||||
|
||||
half_size = Utils.get_half_size(self)
|
||||
|
||||
func _physics_process(delta):
|
||||
position += -transform.y * speed * delta
|
||||
|
||||
var rect = get_viewport_rect()
|
||||
|
||||
if position.x < -half_size.x or position.x > rect.size.x + half_size.x or position.y < -half_size.y or position.y > rect.size.y + half_size.y:
|
||||
#print_debug("FREED")
|
||||
queue_free()
|
||||
|
||||
func _on_body_entered(body):
|
||||
if body.is_in_group("enemies"):
|
||||
body.queue_free()
|
||||
|
||||
Utils.play_sound(pop, get_parent())
|
||||
|
||||
var boom = explosion.instantiate()
|
||||
boom.position = body.global_position
|
||||
boom.scale = body.scale
|
||||
get_parent().add_child(boom)
|
||||
|
||||
Game.set_score(Game.score + 100)
|
||||
|
||||
queue_free()
|
||||
#print_debug("KILLED")
|
||||
1
Scripts/laser.gd.uid
Normal file
1
Scripts/laser.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://csldj2x1axso1
|
||||
6
Scripts/lives.gd
Normal file
6
Scripts/lives.gd
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
extends HBoxContainer
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
Game.lives_box = self
|
||||
1
Scripts/lives.gd.uid
Normal file
1
Scripts/lives.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://miwfeu4n6uh7
|
||||
14
Scripts/main_menu.gd
Normal file
14
Scripts/main_menu.gd
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
extends Control
|
||||
|
||||
signal new_game
|
||||
signal quit_game
|
||||
|
||||
func _ready():
|
||||
$"Container/New Game".pressed.connect(_emit_new)
|
||||
$Container/Quit.pressed.connect(_emit_quit)
|
||||
|
||||
func _emit_new():
|
||||
emit_signal("new_game")
|
||||
|
||||
func _emit_quit():
|
||||
emit_signal("quit_game")
|
||||
1
Scripts/main_menu.gd.uid
Normal file
1
Scripts/main_menu.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dxbdiq4y3mqtd
|
||||
6
Scripts/score.gd
Normal file
6
Scripts/score.gd
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
extends Label
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
Game.score_label = self
|
||||
1
Scripts/score.gd.uid
Normal file
1
Scripts/score.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://efye4dftj3u6
|
||||
63
Scripts/ship.gd
Normal file
63
Scripts/ship.gd
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
extends CharacterBody2D
|
||||
|
||||
const ROT_SPEED = 3.0
|
||||
const MOVE_ACC = 100
|
||||
const FRICTION = 0.995
|
||||
const FIRE_RATE = 0.2
|
||||
|
||||
@export var Laser: PackedScene
|
||||
@export var pew: Resource
|
||||
|
||||
var speed = Vector2(0, 0)
|
||||
var last_shot = 0.0
|
||||
|
||||
func _ready() -> void:
|
||||
Game.player = self
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
var forward = Input.is_action_pressed("ui_up")
|
||||
var left = Input.is_action_pressed("ui_left")
|
||||
var right = Input.is_action_pressed("ui_right")
|
||||
|
||||
if left:
|
||||
rotation -= ROT_SPEED * delta
|
||||
|
||||
if $AnimatedSprite2D.animation != "left":
|
||||
$AnimatedSprite2D.play("left")
|
||||
if right:
|
||||
rotation += ROT_SPEED * delta
|
||||
|
||||
if $AnimatedSprite2D.animation != "right":
|
||||
$AnimatedSprite2D.play("right")
|
||||
if forward:
|
||||
speed += Vector2.UP.rotated(rotation) * MOVE_ACC * delta
|
||||
|
||||
if $AnimatedSprite2D.animation != "fly":
|
||||
$AnimatedSprite2D.play("fly")
|
||||
if Input.is_action_pressed("ui_down"):
|
||||
speed -= Vector2.UP.rotated(rotation) * MOVE_ACC * delta
|
||||
|
||||
if !(forward || left || right) and $AnimatedSprite2D.animation != "idle":
|
||||
$AnimatedSprite2D.play("idle")
|
||||
|
||||
speed *= FRICTION
|
||||
velocity = speed
|
||||
|
||||
move_and_slide()
|
||||
|
||||
Utils.wrap_position(self)
|
||||
|
||||
# --- Fire projectile on space ---
|
||||
last_shot += delta
|
||||
if Input.is_action_pressed("ui_accept") and last_shot >= FIRE_RATE: # default = Space/Enter
|
||||
var bullet = Laser.instantiate()
|
||||
|
||||
Utils.play_sound(pew, self)
|
||||
|
||||
get_parent().add_child(bullet)
|
||||
bullet.transform = $Muzzle.global_transform
|
||||
last_shot = 0.0
|
||||
|
||||
func reset():
|
||||
position = get_viewport_rect().size / 2
|
||||
speed = Vector2(0, 0)
|
||||
1
Scripts/ship.gd.uid
Normal file
1
Scripts/ship.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://p443pc5fxxg0
|
||||
84
Scripts/spawner.gd
Normal file
84
Scripts/spawner.gd
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
extends Node2D
|
||||
|
||||
var timer: Timer
|
||||
var asteroids := [
|
||||
preload('res://Enemies/Scenes/asteroidTiny.tscn'),
|
||||
preload('res://Enemies/Scenes/asteroidSm.tscn'),
|
||||
preload('res://Enemies/Scenes/asteroid.tscn'),
|
||||
preload('res://Enemies/Scenes/asteroidBig.tscn')
|
||||
]
|
||||
var screen_size: Vector2
|
||||
|
||||
func _ready():
|
||||
Game.spawner = self
|
||||
|
||||
screen_size = get_viewport_rect().size
|
||||
# Call spawn_enemy() every spawn_interval seconds
|
||||
timer = Timer.new()
|
||||
timer.wait_time = spawn_time_from_score(Game.score)
|
||||
timer.timeout.connect(spawn_enemy)
|
||||
add_child(timer)
|
||||
|
||||
func spawn_time_from_score(score: int) -> float:
|
||||
var min_time = 0.3
|
||||
var max_time = 2.0
|
||||
var max_score = 50000.0
|
||||
|
||||
# Clamp score so it doesn’t overshoot
|
||||
var clamped_score = clamp(score, 0, max_score)
|
||||
|
||||
# Normalized value [0..1]
|
||||
var t = float(clamped_score) / max_score
|
||||
|
||||
# Logarithmic easing (slows down progression)
|
||||
# Use log10 but you can tweak the base for different curves
|
||||
var curved_t = log(1.0 + 9.0 * t) / log(10.0) # maps 0..1 to 0..1, but with log shape
|
||||
|
||||
# Interpolate between max_time and min_time
|
||||
return lerp(max_time, min_time, curved_t)
|
||||
|
||||
func spawn_enemy():
|
||||
var enemy = asteroids[randi_range(0, 3)].instantiate()
|
||||
var pos = get_random_edge_position()
|
||||
enemy.position = pos
|
||||
|
||||
# Direction vector toward center
|
||||
var center = screen_size * 0.5
|
||||
var dir = (center - pos).normalized()
|
||||
|
||||
# Add some randomness so it's not always perfect
|
||||
var angle_variation = randf_range(-0.5, 0.5) # radians (~±14 degrees)
|
||||
dir = dir.rotated(angle_variation)
|
||||
|
||||
# Apply starting velocity
|
||||
enemy.rot_velocity = randf_range(0, PI)
|
||||
enemy.velocity = dir * randf_range(50, 200)
|
||||
enemy.rotation = randf_range(0, PI)
|
||||
|
||||
var scl = randi_range(1, 5)
|
||||
enemy.scale = Vector2(scl, scl)
|
||||
|
||||
enemy.add_to_group("enemies")
|
||||
add_child(enemy)
|
||||
|
||||
timer.wait_time = spawn_time_from_score(Game.score)
|
||||
|
||||
func get_random_edge_position() -> Vector2:
|
||||
# Pick which edge (0=top,1=bottom,2=left,3=right)
|
||||
var edge = randi() % 4
|
||||
match edge:
|
||||
0: # top
|
||||
return Vector2(randf() * screen_size.x, 0)
|
||||
1: # bottom
|
||||
return Vector2(randf() * screen_size.x, screen_size.y)
|
||||
2: # left
|
||||
return Vector2(0, randf() * screen_size.y)
|
||||
3: # right
|
||||
return Vector2(screen_size.x, randf() * screen_size.y)
|
||||
return Vector2.ZERO
|
||||
|
||||
func stop():
|
||||
timer.stop()
|
||||
|
||||
func start():
|
||||
timer.start()
|
||||
1
Scripts/spawner.gd.uid
Normal file
1
Scripts/spawner.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://mfgrqlsgsyrx
|
||||
6
Scripts/ui.gd
Normal file
6
Scripts/ui.gd
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
extends CanvasLayer
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
Game.ui = self
|
||||
1
Scripts/ui.gd.uid
Normal file
1
Scripts/ui.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dpa4j6me328ak
|
||||
43
Scripts/utils.gd
Normal file
43
Scripts/utils.gd
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
extends Node2D
|
||||
|
||||
func get_half_size(sprt: Node2D):
|
||||
var half_size: Vector2
|
||||
|
||||
if sprt is Sprite2D:
|
||||
half_size = (sprt.texture.get_size() * sprt.scale) / 2.0
|
||||
elif sprt is AnimatedSprite2D:
|
||||
var tex: Texture2D = sprt.sprite_frames.get_frame_texture(sprt.animation, 0)
|
||||
if tex:
|
||||
half_size = (tex.get_size() * sprt.scale) / 2.0
|
||||
else:
|
||||
half_size = Vector2.ZERO
|
||||
else:
|
||||
half_size = Vector2.ZERO
|
||||
|
||||
return half_size
|
||||
|
||||
func wrap_position(node: Node2D) -> void:
|
||||
var rect = node.get_viewport_rect()
|
||||
var pos = node.position
|
||||
var half_size = get_half_size(node)
|
||||
|
||||
# Horizontal wrap
|
||||
if pos.x < -half_size.x:
|
||||
pos.x = rect.size.x + half_size.x
|
||||
elif pos.x > rect.size.x + half_size.x:
|
||||
pos.x = -half_size.x
|
||||
|
||||
# Vertical wrap
|
||||
if pos.y < -half_size.y:
|
||||
pos.y = rect.size.y + half_size.y
|
||||
elif pos.y > rect.size.y + half_size.y:
|
||||
pos.y = -half_size.y
|
||||
|
||||
node.position = pos
|
||||
|
||||
func play_sound(sound: Resource, parent: Node2D):
|
||||
var player = AudioStreamPlayer.new()
|
||||
parent.add_child(player)
|
||||
player.stream = sound
|
||||
player.play()
|
||||
player.finished.connect(func(): player.queue_free())
|
||||
1
Scripts/utils.gd.uid
Normal file
1
Scripts/utils.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://buu1netks6ojb
|
||||
Loading…
Add table
Add a link
Reference in a new issue