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 + 特殊攻击
相关推荐
郝学胜-神的一滴13 分钟前
游戏设计:原型与开发 11:用概率与电子表格搞定骰子系统
程序人生·游戏·游戏策划·1024程序员节·游戏设计
解决小子2 小时前
【虚幻引擎】【C++】【GAS框架】构建角色基类、属性集、数值钳制
c++·笔记·游戏引擎·虚幻·gas·能力系统
treesforest2 小时前
游戏账号安全风险分层方案:IP信号分层与人机复核实践
tcp/ip·安全·游戏·1024程序员节
上班摸鱼下班游戏3 小时前
Unity 仿生智能算法 从蚁群算法 开始
算法·unity·游戏引擎
坐吃山猪3 小时前
Godot01-初体验
godot·1024程序员节
Raspberry_Pi_官方账号4 小时前
隆重推出《Raspberry Pi 复古游戏指南》
游戏·树莓派·diy·raspberry pi
Oiiouui5 小时前
针对Godot-GridGame(网格游戏)的DI注入+RefCounted数据驱动信号流架构设计的研究(全程开源)
游戏·游戏引擎·godot·1024程序员节
Oiiouui9 小时前
Godot-GridGame架构数据层篇
架构·godot·1024程序员节
上班摸鱼下班游戏19 小时前
从零开始玩转 DOTween
unity·游戏引擎
阿松爱学习1 天前
【Unity开发】Unity长截图功能实现
unity·c#·游戏引擎