HOME / 第 7 部分 · 完整游戏项目实战 / 第 84 课
MODULE 7 · 完整游戏项目实战

第 84 课 · 塔防游戏实战(二):炮塔与攻击

⏱ 预计时长 50 分钟📊 难度 进阶📚 模块 第 7 部分 · 完整游戏项目实战

🎯 本课学习目标

塔防第二课:玩家建炮塔,炮塔自动锁定并攻击路径上的敌人。

📖 详细教学步骤

炮塔场景搭建

  1. 新建 turret.tscn,根 Node2D
  2. 子节点:Sprite2D(炮塔底座)+ Sprite2D(炮管,可旋转)
  3. 子节点 Area2D(攻击范围,圆形)
  4. 挂脚本 turret.gd

炮塔放置代码

点击空地放置炮塔:

GDSCRIPT# main.gd
const TURRET = preload("res://turret.tscn")

func _unhandled_input(event):
    if event is InputEventMouseButton and event.pressed:
        if event.button_index == MOUSE_BUTTON_LEFT:
            if can_afford(TURRET_COST):
                spend_gold(TURRET_COST)
                var t = TURRET.instantiate()
                t.position = get_global_mouse_position()
                add_child(t)

炮塔搜索敌人代码

在攻击范围内找最近的敌人:

GDSCRIPTextends Node2D

@onready var range_area: Area2D = $RangeArea
@onready var barrel: Sprite2D = $Barrel

@export var fire_rate: float = 1.0   # 每秒射击次数
@export var damage: int = 10
var cooldown: float = 0.0

func _physics_process(delta):
    cooldown -= delta
    var target = find_target()
    if target:
        # 炮管对准目标
        barrel.rotation = (target.global_position - global_position).angle()
        if cooldown <= 0:
            cooldown = 1.0 / fire_rate
            shoot(target)

func find_target():
    var best = null
    var best_progress = -1.0
    for body in range_area.get_overlapping_bodies():
        if body.has_method("take_damage"):
            # 选「路径进度最大」的敌人(最靠前)
            if body.progress > best_progress:
                best = body
                best_progress = body.progress
    return best

射击代码

发射子弹:

GDSCRIPTconst BULLET = preload("res://bullet.tscn")

func shoot(target):
    var bullet = BULLET.instantiate()
    bullet.global_position = global_position
    bullet.target = target
    bullet.damage = damage
    get_parent().add_child(bullet)

子弹追踪敌人代码

子弹朝目标飞去:

GDSCRIPTextends Area2D

var target
var damage: int = 10
@export var speed: float = 300.0

func _physics_process(delta):
    if target == null or not is_instance_valid(target):
        queue_free()   # 目标没了,子弹消失
        return
    # 飞向目标
    global_position = global_position.move_toward(
        target.global_position, speed * delta)

敌人掉血死亡代码

敌人脚本补充:

GDSCRIPTvar hp: int = 100

func take_damage(amount: int):
    hp -= amount
    if hp <= 0:
        die()

func die():
    Globals.gold += 10   # 击杀奖励
    queue_free()

测试测试

  1. 放几只敌人沿路径走
  2. 点击空地放炮塔
  3. 炮塔自动转向最近的敌人并射击
  4. 敌人掉血死亡加金币

🎉 塔防核心战斗完成!

⚠️
炮塔不攻击?

确认:①RangeArea 有 CollisionShape;②敌人是 Area2D/物理体且加入范围;③敌人有 take_damage 方法。

⚠️
子弹不追踪?

子弹 target 引用可能失效(敌人已删)。用 is_instance_valid 判断并销毁子弹。

🧪 动手试一试

🏋️ 课后练习

让炮塔发射「火球」(带拖尾粒子),击中敌人时播放爆炸小特效并造成范围伤害。

子弹 Area2D + 命中时实例化粒子。

💡

选「最靠前」敌人(progress 最大)是塔防常用策略。

💡

子弹用 Area2D 检测命中比物理碰撞更轻量。

🎓 本课小结

塔防战斗:点击放炮塔 → 攻击范围找最近/最靠前敌人 → 炮管瞄准 → 发射追踪子弹 → 敌人掉血死亡给金币。