2Dシューティング

エネミーが破壊された時にスコアーを加算する
エネミースクリプトで破壊時にポイントを送るシグナルとポイントを設定して、HPが0になったときにemitする

extends CharacterBody2D

# シグナル
signal enemy_destruction(points)

# 移動スピード
const move_speed = 30.0
# HP
var health = 10
# ポイント
var points = 10

var direction: Vector2

# バレット
var canShoot = true
const ENEMY_BULLET = preload("res://scenes/enemy_bullet.tscn")
@onready var marker_2d: Marker2D = $Marker2D
@onready var shoot_timer: Timer = $shootTimer

func _ready() -> void:
	add_to_group("enemies")

func _physics_process(delta: float) -> void:
	direction.y = 1
	velocity = direction.normalized() * move_speed
	position += velocity * delta

	shoot()
	
	move_and_slide()

func _on_visible_on_screen_notifier_2d_screen_exited() -> void:
	print("outside")
	queue_free()
	
func shoot():
	if canShoot:
		canShoot = false
		shoot_timer.start()

		var bullet = ENEMY_BULLET.instantiate()
		bullet.set_position(marker_2d.global_position)
		get_parent().add_child(bullet)

func _on_shoot_timer_timeout() -> void:
	canShoot = true

func get_damage(damage):
	health -= damage
	print("Enemy Damage")
	if health <= 0:
		enemy_destruction.emit(points)
		queue_free()

メインスクリプトでシグナルを受け取る
エネミーを出現させる際にシグナルに接続しておく。そして破壊時にポイントを受け取り、スコアに加算

extends Node2D

# ランダムエネミー
@export var enemies: Array[PackedScene] = []
@onready var enemy_node: Node2D = $EnemyNode
@onready var enemy_spawn_timer: Timer = $EnemySpawnTimer

# スコアー
var score = 0

func _ready() -> void:
	# マウスカーソルを消す
	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED

func _process(delta: float) -> void:
	# ESCでマウスカーソルを出す
	if Input.is_action_just_pressed("ui_cancel"):
		Input.mouse_mode = Input.MOUSE_MODE_VISIBLE

func _on_enemy_spawn_timer_timeout() -> void:
	var enemy = enemies.pick_random().instantiate()
	enemy.global_position = Vector2(randf_range(40, 440) , 50)
	enemy.enemy_destruction.connect(_on_enemy_destruction)
	enemy_node.add_child(enemy)

func _on_enemy_destruction(points):
	score += points
	prints("Score: ", score)

スコアの表示
CanvasLayerにControlを追加し全面にする

ContorlをHUDにリネームして新規シーンにする
Labelを追加してフォントを設定する(今回は無指定)サイズは60

スコアを表示するスクリプト

extends Control

@onready var score_label: Label = $scoreLabel

func update_score(score):
	score_label.text = "Score : " + str(score)

メインシーンにHUDを追加してスコアを表示させる

extends Node2D

# ランダムエネミー
@export var enemies: Array[PackedScene] = []
@onready var enemy_node: Node2D = $EnemyNode
@onready var enemy_spawn_timer: Timer = $EnemySpawnTimer

# スコアー
var score = 0
@onready var hud: Control = $CanvasLayer/HUD

func _ready() -> void:
	# マウスカーソルを消す
	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED

func _process(delta: float) -> void:
	# ESCでマウスカーソルを出す
	if Input.is_action_just_pressed("ui_cancel"):
		Input.mouse_mode = Input.MOUSE_MODE_VISIBLE

func _on_enemy_spawn_timer_timeout() -> void:
	var enemy = enemies.pick_random().instantiate()
	enemy.global_position = Vector2(randf_range(40, 440) , 50)
	enemy.enemy_destruction.connect(_on_enemy_destruction)
	enemy_node.add_child(enemy)

func _on_enemy_destruction(points):
	score += points
	hud.update_score(score)
	prints("Score: ", score)

2Dシューティング

Controlを親にして背景とラベル、ボタンをつけたシンプルなスタート画面

ボタン押下時のスクリプト

extends Control

func _on_start_button_pressed() -> void:
	get_tree().change_scene_to_file("res://scenes/game.tscn")

プロジェクト設定でメインシーンにスタート画面を設定

2Dシューティング

HUDと同じくControlでGAMEOVER画面を作る

スクリプト
メインシーンから呼ばれるスコア部分とボタンクリック部分
ボタンはそれぞれのノードから接続してクリック時の動作を指定する

extends Control

@onready var hiscore_label: Label = $Panel/HiscoreLabel
@onready var score_label: Label = $Panel/ScoreLabel

func set_score(score):
	score_label.text = "Score : " + str(score)

func set_hi_score(score):
	hiscore_label.text = "Hi-Score : " + str(score)

func _on_quit_button_pressed() -> void:
	get_tree().quit()

func _on_restart_button_pressed() -> void:
	get_tree().reload_current_scene()

プレイヤーにシグナルを追加

extends CharacterBody2D

# シグナル
signal destruction

# 移動スピード
const move_speed = 100.0
# 傾き
var turnTilt = 4
# HP
var health = 10

var direction: Vector2

# バレット
var canShoot = true 
const BULLET = preload("res://scenes/bullet.tscn")
@onready var marker_2d: Marker2D = $Marker2D
@onready var shoot_timer: Timer = $shootTimer

func _ready() -> void:
	add_to_group("player")

func _physics_process(delta: float) -> void:
	direction.x = Input.get_axis("move_left", "move_right")
	direction.y = Input.get_axis("move_up", "move_down")
	
	if direction.x:
		velocity = direction.normalized() * move_speed
		if direction.x < 0:
			rotation = -1 * turnTilt * delta
		elif direction.x > 0:
			rotation = 1 * turnTilt * delta
	elif direction.y:
		velocity = direction.normalized() * move_speed
		rotation = 0 * turnTilt * delta
	else :
		velocity = Vector2.ZERO

	# 攻撃
	if Input.is_action_pressed("shoot"):
		shoot()

	position += velocity * delta

	# 画面の外に行かないように	
	var viewRect = get_viewport_rect()
	var viewOffset = Vector2(50, 50)
	position = position.clamp(viewOffset, viewRect.size - viewOffset)

	move_and_slide()

func shoot():
	if canShoot:
		canShoot = false
		shoot_timer.start()

		var bullet = BULLET.instantiate()
		bullet.set_position(marker_2d.global_position)
		get_parent().add_child(bullet)

func _on_shoot_timer_timeout() -> void:
	canShoot = true
	
func get_damage(damage):
	health -= damage
	print("Play Damage")
	if health <= 0:
		destruction.emit()
		queue_free()

メインシーンにプレーヤーが破壊された時のシグナル受信とゲームオーバー表示を追加

extends Node2D

# ランダムエネミー
@export var enemies: Array[PackedScene] = []
@onready var enemy_node: Node2D = $EnemyNode
@onready var enemy_spawn_timer: Timer = $EnemySpawnTimer
@onready var game_over: Control = $CanvasLayer/GameOver

# スコアー
var score = 0
var hi_score : int
@onready var hud: Control = $CanvasLayer/HUD

func _ready() -> void:
	# マウスカーソルを消す
	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
	var player = get_tree().get_first_node_in_group("player")
	player.destruction.connect(_on_player_destruction)
	hi_score = 0
	hud.update_score(0)

func _process(delta: float) -> void:
	# ESCでマウスカーソルを出す
	if Input.is_action_just_pressed("ui_cancel"):
		Input.mouse_mode = Input.MOUSE_MODE_VISIBLE

func _on_enemy_spawn_timer_timeout() -> void:
	var enemy = enemies.pick_random().instantiate()
	enemy.global_position = Vector2(randf_range(40, 440) , 50)
	enemy.enemy_destruction.connect(_on_enemy_destruction)
	enemy_node.add_child(enemy)

func _on_enemy_destruction(points):
	score += points
	if score > hi_score:
		hi_score = score
	hud.update_score(score)
	prints("Score: ", score)

func _on_player_destruction():
	game_over.visible = true
	game_over.set_score(score)
	game_over.set_hi_score(hi_score)
	Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
	

2Dシューティング

メインシーンにNode2D、Timerを追加してエネミーをランダムに出現させる

Timerは1.5秒。

エネミーは数種類、用意するのでランダムエネミーは配列にしてインスペクターにエネミーシーンをドラッグ

スクリプト
1.5秒タイムアウトごとにエネミーをX:40~480、Y:50に出現させる

extends Node2D

# ランダムエネミー
@export var enemies: Array[PackedScene] = []
@onready var enemy_node: Node2D = $EnemyNode
@onready var enemy_spawn_timer: Timer = $EnemySpawnTimer

func _ready() -> void:
	# マウスカーソルを消す
	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED

func _process(delta: float) -> void:
	# ESCでマウスカーソルを出す
	if Input.is_action_just_pressed("ui_cancel"):
		Input.mouse_mode = Input.MOUSE_MODE_VISIBLE

func _on_enemy_spawn_timer_timeout() -> void:
	var enemy = enemies.pick_random().instantiate()
	enemy.global_position = Vector2(randf_range(40, 440) , 50)
	enemy_node.add_child(enemy)

2Dシューティング

プレイヤーバレット
damageは1、Area2Dのノードでbody_enteredを追加してEnemyに当たったかを検出
enemiesグループのget_damageにdamageを渡す

extends Area2D

# 移動スピード
const move_speed = 300.0
# プレーヤーからの距離
const bullet_range = 500.0
# バレットダメージ
var damage = 1

var direction = -1

func _ready() -> void:
	await get_tree().create_timer(3).timeout
	queue_free()

func _physics_process(delta: float) -> void:
	position.y += direction * move_speed * delta
	
	# プレーヤーから指定距離離れたら消す
	var player = get_tree().get_first_node_in_group("player")
	var distance = global_position.distance_to(player.global_position)
	if distance > bullet_range:
		queue_free()

func _on_body_entered(body: Node2D) -> void:
	if body.is_in_group("enemies"):
		body.get_damage(damage)
		queue_free()

エネミーのバレット
プレイヤーに当たったかを検出
playerグループのget_damageにdamageを渡す

extends Area2D

# 移動スピード
const move_speed = 200.0
# バレットダメージ
var damage = 1

var direction = 1

func _ready() -> void:
	await get_tree().create_timer(3).timeout
	queue_free()

func _physics_process(delta: float) -> void:
	position.y += direction * move_speed * delta
	


func _on_body_entered(body: Node2D) -> void:
	if body.is_in_group("player"):
		body.get_damage(damage)
		queue_free()

プレイヤー
バレットが当たった場合、get_damageでHPを引く処理

extends CharacterBody2D

# 移動スピード
const move_speed = 100.0
# 傾き
var turnTilt = 4
# HP
var health = 10

var direction: Vector2

# バレット
var canShoot = true 
const BULLET = preload("res://scenes/bullet.tscn")
@onready var marker_2d: Marker2D = $Marker2D
@onready var shoot_timer: Timer = $shootTimer

func _ready() -> void:
	add_to_group("player")

func _physics_process(delta: float) -> void:
	direction.x = Input.get_axis("move_left", "move_right")
	direction.y = Input.get_axis("move_up", "move_down")
	
	if direction.x:
		velocity = direction.normalized() * move_speed
		if direction.x < 0:
			rotation = -1 * turnTilt * delta
		elif direction.x > 0:
			rotation = 1 * turnTilt * delta
	elif direction.y:
		velocity = direction.normalized() * move_speed
		rotation = 0 * turnTilt * delta
	else :
		velocity = Vector2.ZERO

	# 攻撃
	if Input.is_action_pressed("shoot"):
		shoot()

	position += velocity * delta

	# 画面の外に行かないように	
	var viewRect = get_viewport_rect()
	var viewOffset = Vector2(50, 50)
	position = position.clamp(viewOffset, viewRect.size - viewOffset)

	move_and_slide()

func shoot():
	if canShoot:
		canShoot = false
		shoot_timer.start()

		var bullet = BULLET.instantiate()
		bullet.set_position(marker_2d.global_position)
		get_parent().add_child(bullet)

func _on_shoot_timer_timeout() -> void:
	canShoot = true
	
func get_damage(damage):
	health -= damage
	print("Play Damage")
	if health <= 0:
		queue_free()

エネミーのget_damage
プレイヤー同様にdamageをHPから引く

extends CharacterBody2D

# 移動スピード
const move_speed = 30.0
# HP
var health = 10

var direction: Vector2

# バレット
var canShoot = true
const ENEMY_BULLET = preload("res://scenes/enemy_bullet.tscn")
@onready var marker_2d: Marker2D = $Marker2D
@onready var shoot_timer: Timer = $shootTimer

func _ready() -> void:
	add_to_group("enemies")

func _physics_process(delta: float) -> void:
	direction.y = 1
	velocity = direction.normalized() * move_speed
	position += velocity * delta

	shoot()
	
	move_and_slide()

func _on_visible_on_screen_notifier_2d_screen_exited() -> void:
	print("outside")
	queue_free()
	
func shoot():
	if canShoot:
		canShoot = false
		shoot_timer.start()

		var bullet = ENEMY_BULLET.instantiate()
		bullet.set_position(marker_2d.global_position)
		get_parent().add_child(bullet)

func _on_shoot_timer_timeout() -> void:
	canShoot = true

func get_damage(damage):
	health -= damage
	print("Enemy Damage")
	if health <= 0:
		queue_free()

2Dシューティング

プレーヤーのバレットと同じくArea2DでSprite、Collisionを追加しLayerを指定

バレットのスクリプト
移動方向がプレイヤーと逆

extends Area2D

# 移動スピード
const move_speed = 200.0

var direction = 1

func _ready() -> void:
	await get_tree().create_timer(3).timeout
	queue_free()

func _physics_process(delta: float) -> void:
	position.y += direction * move_speed * delta
	

エネミーにバレット発射を追加

extends CharacterBody2D

# 移動スピード
const move_speed = 30.0

var direction: Vector2

# バレット
var canShoot = true
const ENEMY_BULLET = preload("res://scenes/enemy_bullet.tscn")
@onready var marker_2d: Marker2D = $Marker2D
@onready var shoot_timer: Timer = $shootTimer

func _ready() -> void:
	add_to_group("enemies")

func _physics_process(delta: float) -> void:
	direction.y = 1
	velocity = direction.normalized() * move_speed
	position += velocity * delta

	shoot()
	
	move_and_slide()

func _on_visible_on_screen_notifier_2d_screen_exited() -> void:
	print("outside")
	queue_free()
	
func shoot():
	if canShoot:
		canShoot = false
		shoot_timer.start()

		var bullet = ENEMY_BULLET.instantiate()
		bullet.set_position(marker_2d.global_position)
		get_parent().add_child(bullet)

func _on_shoot_timer_timeout() -> void:
	canShoot = true