HOME / 第 7 部分 · 完整游戏项目实战 / 第 86 课
MODULE 7 · 完整游戏项目实战
第 86 课 · 弹幕射击游戏(一):飞机与子弹
🎯 本课学习目标
- 实现飞机移动与射击
- 实现敌方弹幕发射
- 碰撞与爆炸
经典飞机弹幕游戏:控制战机躲弹幕、打敌机。本课做飞机与子弹系统。
📖 详细教学步骤
游戏设计设计
- 玩家:战机,上下左右移动 + 自动/空格射击
- 敌人:会发射弹幕的敌机
- 目标:躲避弹幕,击落敌机得分
- 难度:弹幕越来越密
玩家战机搭建
- 新建 player_plane.tscn,根 Area2D(用 Area 便于检测子弹)
- Sprite2D 战机图 + CollisionShape2D
- 脚本:WASD/方向键移动,限制在屏幕内
GDSCRIPTextends Area2D
@export var speed: float = 300.0
@export var fire_rate: float = 0.2
var fire_timer: float = 0.0
func _physics_process(delta):
var dir = Input.get_vector("move_left", "move_right", "move_up", "move_down")
global_position += dir * speed * delta
# 限制在屏幕内
var vp = get_viewport_rect().size
global_position.x = clampf(global_position.x, 20, vp.x - 20)
global_position.y = clampf(global_position.y, 20, vp.y - 20)
# 自动射击
fire_timer -= delta
if fire_timer <= 0:
fire_timer = fire_rate
shoot()
func shoot():
var bullet = player_bullet.instantiate()
bullet.global_position = global_position + Vector2(0, -20)
get_parent().add_child(bullet)
玩家子弹代码
子弹向上飞,出屏幕删除:
GDSCRIPTextends Area2D
@export var speed: float = 600.0
func _physics_process(delta):
global_position.y -= speed * delta
# 出屏幕删除
if global_position.y < -20:
queue_free()
敌机与弹幕搭建
敌机发射弹幕(扇形):
GDSCRIPTextends Area2D
@export var fire_interval: float = 1.5
var timer: float = 0.0
func _physics_process(delta):
# 敌机缓慢下移
global_position.y += 60 * delta
timer -= delta
if timer <= 0 and is_visible_in_tree():
timer = fire_interval
fire_barrage()
func fire_barrage():
# 发射 5 发扇形弹幕
for i in range(5):
var b = enemy_bullet.instantiate()
b.global_position = global_position
# 角度:-40° 到 +40°
var angle = deg_to_rad(-40 + i * 20)
b.dir = Vector2(0, 1).rotated(angle)
get_parent().add_child(b)
碰撞与爆炸代码
子弹命中敌机、敌弹命中玩家:
GDSCRIPT# player_plane.gd
func _ready():
body_entered.connect(_on_body_entered)
func _on_body_entered(body):
if body.is_in_group("enemy_bullet"):
body.queue_free()
explode() # 玩家爆炸
get_tree().reload_current_scene()
# enemy.gd
func _ready():
body_entered.connect(_on_body_entered)
func _on_body_entered(body):
if body.is_in_group("player_bullet"):
body.queue_free()
take_damage(1)
生成敌机代码
主场景定时生成敌机:
GDSCRIPTconst ENEMY = preload("res://enemy_plane.tscn")
func _ready():
spawn_timer.timeout.connect(_on_spawn)
spawn_timer.start()
func _on_spawn():
var e = ENEMY.instantiate()
e.position = Vector2(randf_range(30, 610), -30)
add_child(e)
⚠️
子弹打不到敌机?
子弹和敌机都必须是 Area2D 且互相检测;用组(player_bullet/enemy_bullet)判断身份。
⚠️
飞机出屏幕?
用 clampf 限制在屏幕范围内(第 1 步代码里有)。
🧪 动手试一试
🏋️ 课后练习
做出「扇形弹幕」:敌机每 1.5 秒发射 5 发不同角度子弹,玩家子弹能击落敌机。
Vector2(0,1).rotated(angle) 生成方向 + 每帧按 dir 移动。
💡
弹幕方向用向量旋转(rotated)最灵活。
💡
用组区分敌我子弹,碰撞判断清晰。
🎓 本课小结
弹幕游戏基础:玩家移动+射击 + 敌机弹幕(rotated 方向)+ Area 碰撞检测 + 爆炸重开;组区分敌我。