21、游戏架构设计模式

第21章 游戏架构设计模式

21.1 组合模式

gdscript 复制代码
# 通过组合节点构建复杂对象,而非深层继承

# HealthComponent.gd
extends Node

signal health_changed(current: int, maximum: int)
signal died

@export var max_health: int = 100
var health: int

func _ready() -> void:
    health = max_health

func take_damage(amount: int) -> void:
    health = clampi(health - amount, 0, max_health)
    health_changed.emit(health, max_health)
    if health <= 0:
        died.emit()

func heal(amount: int) -> void:
    health = clampi(health + amount, 0, max_health)
    health_changed.emit(health, max_health)
gdscript 复制代码
# HitboxComponent.gd
extends Area2D

@export var damage: int = 10
@export var knockback: float = 300.0
gdscript 复制代码
# HurtboxComponent.gd
extends Area2D

signal hurt(damage: int, knockback_dir: Vector2)

func _ready() -> void:
    area_entered.connect(_on_area_entered)

func _on_area_entered(area: Area2D) -> void:
    if area.get("damage") != null:
        var dir := (global_position - area.global_position).normalized()
        hurt.emit(area.damage, dir * area.knockback)
gdscript 复制代码
# 使用组合构建角色
# Player (CharacterBody2D)
# ├── Sprite2D
# ├── CollisionShape2D
# ├── HealthComponent
# ├── HitboxComponent (攻击判定)
# ├── HurtboxComponent (受击区域)
# └── InteractionComponent (交互)

# Player.gd - 只负责移动和状态,不关心具体系统
extends CharacterBody2D

func _ready() -> void:
    $HealthComponent.died.connect(_on_died)
    $HurtboxComponent.hurt.connect(_on_hurt)

func _on_hurt(damage: int, knockback: Vector2) -> void:
    $HealthComponent.take_damage(damage)
    velocity += knockback

21.2 状态模式

gdscript 复制代码
# 脚本化状态方案:每个状态是一个独立脚本
class_name State
extends Node

signal finished(next_state: String)

var state_machine: Node
var actor: CharacterBody2D

func enter() -> void:
    pass

func exit() -> void:
    pass

func update(delta: float) -> void:
    pass

func physics_update(delta: float) -> void:
    pass
gdscript 复制代码
# StateMachine.gd
extends Node

@export var initial_state: State
var current_state: State
var states: Dictionary = {}

func _ready() -> void:
    for child in get_children():
        if child is State:
            states[child.name.to_lower()] = child
            child.state_machine = self
            child.actor = get_parent()
    
    if initial_state:
        initial_state.enter()
        current_state = initial_state

func _process(delta: float) -> void:
    if current_state:
        current_state.update(delta)

func _physics_process(delta: float) -> void:
    if current_state:
        current_state.physics_update(delta)

func transition_to(state_name: String) -> void:
    var new_state = states.get(state_name.to_lower())
    if new_state == null or new_state == current_state:
        return
    
    current_state.exit()
    current_state = new_state
    current_state.enter()
gdscript 复制代码
# IdleState.gd
extends State

func enter() -> void:
    actor.velocity.x = 0
    actor.get_node("AnimatedSprite2D").play("idle")

func physics_update(delta: float) -> void:
    var input_dir := Input.get_axis("move_left", "move_right")
    if absf(input_dir) > 0.1:
        finished.emit("run")
    elif Input.is_action_just_pressed("jump"):
        finished.emit("jump")

21.3 观察者模式(信号解耦)

gdscript 复制代码
# 使用信号实现松耦合通信
# 发送者不需要知道接收者是谁

# 敌人死亡时发送信号
# Enemy.gd
signal died(enemy_type: String, position: Vector2)

func die() -> void:
    died.emit("slime", global_position)
    queue_free()

# 任何系统都可以监听
# ScoreSystem.gd
func _ready() -> void:
    # 连接到场景中所有敌人的 died 信号
    get_tree().node_added.connect(_on_node_added)

func _on_node_added(node: Node) -> void:
    if node.has_signal("died"):
        node.died.connect(_on_enemy_died)

func _on_enemy_died(type: String, pos: Vector2) -> void:
    GameManager.add_score(get_score_for_type(type))

21.4 事件总线

gdscript 复制代码
# event_bus.gd - Autoload
extends Node

# 游戏事件
signal enemy_killed(enemy_type: String, position: Vector2)
signal item_collected(item_type: String, amount: int)
signal player_damaged(damage: int)
signal boss_defeated(boss_name: String)
signal level_up(new_level: int)

# 任何地方都可以发送和监听
# 发送:EventBus.enemy_killed.emit("slime", position)
# 监听:EventBus.enemy_killed.connect(_on_enemy_killed)

21.5 依赖注入

gdscript 复制代码
# 避免硬编码引用,通过注入依赖解耦

# ❌ 硬耦合
extends Node2D
func _ready() -> void:
    var player = get_node("/root/Main/Player")  # 路径写死
    player.health_changed.connect(_on_health_changed)

# ✅ 依赖注入
extends Node2D
@export var player: CharacterBody2D  # 在编辑器中拖入

# ✅ 使用组查找
extends Node2D
func _ready() -> void:
    var players := get_tree().get_nodes_in_group("player")
    if not players.is_empty():
        players[0].health_changed.connect(_on_health_changed)

# ✅ 使用 Autoload
extends Node2D
func _ready() -> void:
    EventBus.player_damaged.connect(_on_player_damaged)

实操练习

练习:组件化敌人系统

gdscript 复制代码
# 创建以下组件:
# 1. HealthComponent - 生命值管理
# 2. HitboxComponent - 攻击判定
# 3. HurtboxComponent - 受击区域
# 4. PatrolComponent - 巡逻行为
# 5. ChaseComponent - 追踪行为

# 使用组件组合不同类型的敌人:
# Slime = Health + Hurtbox + Patrol
# Skeleton = Health + Hitbox + Hurtbox + Patrol + Chase
# Boss = Health + Hitbox + Hurtbox + Chase + 特殊攻击
相关推荐
勤劳X码农1 天前
2026年AI配音做教育视频怎么选?
人工智能·游戏·音视频
bryant_meng2 天前
【Game】Powerful——Pets(4.5)145
游戏·幻唐志·暗夜龙姬·撼山·银角·金角
淡海水2 天前
02-02-原理篇-分代GC
算法·unity·c#·游戏引擎·.net·gc
郝学胜-神的一滴2 天前
AI 编程智能体 03:拆解当下主流智能体能力
开发语言·c++·人工智能·python·程序人生·游戏
-dzk-2 天前
【贪心算法】LC 55.跳跃游戏
算法·游戏·贪心算法
liangshanbo12152 天前
面试题:游戏、直播为什么常用 UDP?KCP 是什么?为什么比 TCP 更适合低延迟场景?
tcp/ip·游戏·udp
勤劳X码农2 天前
2026年游戏解说AI配音软件怎么选?
人工智能·游戏
神奇的小猴程序员2 天前
游戏像素资产生成工具
游戏
淡海水2 天前
02-03-原理篇-大对象堆
unity·c#·游戏引擎·.net·gc
bryant_meng3 天前
【Game】Powerful——Pets(4.4)125~135
游戏·幻唐志·墨虎·贪狼·蚩尤武魂