godot-quest-system
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseQuest System
任务系统
Resource-based data, signal-driven updates, and AutoLoad coordination define scalable quest architectures.
基于资源的数据、信号驱动的更新以及AutoLoad协同构成了可扩展的任务系统架构。
Available Scripts
可用脚本
quest_manager.gd
quest_manager.gd
Expert AutoLoad quest tracker with objective progression and reward distribution.
专业的AutoLoad任务追踪器,支持目标进度管理和奖励分发。
quest_graph_manager.gd
quest_graph_manager.gd
Runtime manager for graph-based quests. Tracks objectives and node progression.
基于图结构的任务运行时管理器。追踪目标和节点进度。
NEVER Do in Quest Systems
任务系统中的绝对禁忌
- NEVER store quest data in nodes — Quest progress in variables? Loss on scene reload. Use Resource-based Quests OR AutoLoad singleton.
player.gd - NEVER use strings for quest IDs without registry — typo = silent failure. Use constants OR validate IDs against registry.
update_objective("kil_bandts", ...) - NEVER forget to disconnect signals — Quest completed but signal still connected? Quest completes again on next update = duplicate rewards. Disconnect in .
_on_quest_completed() - NEVER poll for objective updates — Checking every frame = wasteful. Use signals:
if enemy_count == 10.enemy.died.connect(quest_manager.on_enemy_killed) - NEVER skip save/load for quests — Player completes quest 5, game restarts, quest resets? Frustration. MUST persist +
active_questsarrays.completed_quests - NEVER use for objectives without null check —
all()with null objectives? Crash. Validate array contents first.objectives.all(func(obj): return obj.is_complete())
gdscript
undefined- 绝对不要在节点中存储任务数据 — 把任务进度存在的变量里?场景重载时会丢失数据。请使用基于资源的任务或AutoLoad单例。
player.gd - 绝对不要在没有注册表的情况下使用字符串作为任务ID — 拼写错误会导致静默失败。请使用常量或对照注册表验证ID。
update_objective("kil_bandts", ...) - 绝对不要忘记断开信号 — 任务已完成但信号仍处于连接状态?下次更新时任务会再次完成,导致奖励重复。请在中断开信号。
_on_quest_completed() - 绝对不要轮询目标更新 — 每帧检查非常浪费性能。请使用信号:
if enemy_count == 10。enemy.died.connect(quest_manager.on_enemy_killed) - 绝对不要跳过任务的保存/加载逻辑 — 玩家完成了任务5,游戏重启后任务重置?这会让玩家感到挫败。必须持久化和
active_quests数组。completed_quests - 绝对不要在没有空值检查的情况下对目标使用— 当目标数组中存在空值时,
all()会导致崩溃。请先验证数组内容。objectives.all(func(obj): return obj.is_complete())
gdscript
undefinedquest.gd
quest.gd
class_name Quest
extends Resource
signal progress_updated(objective_id: String, progress: int)signal completed
@export var quest_id: String
@export var quest_name: String
@export_multiline var description: String
@export var objectives: Array[QuestObjective] = []
@export var rewards: Array[QuestReward] = []
@export var required_level: int = 1
func is_complete() -> bool:
return objectives.all(func(obj): return obj.is_complete())
func check_completion() -> void:
if is_complete():
completed.emit()
undefinedclass_name Quest
extends Resource
signal progress_updated(objective_id: String, progress: int)signal completed
@export var quest_id: String
@export var quest_name: String
@export_multiline var description: String
@export var objectives: Array[QuestObjective] = []
@export var rewards: Array[QuestReward] = []
@export var required_level: int = 1
func is_complete() -> bool:
return objectives.all(func(obj): return obj.is_complete())
func check_completion() -> void:
if is_complete():
completed.emit()
undefinedQuest Objectives
任务目标
gdscript
undefinedgdscript
undefinedquest_objective.gd
quest_objective.gd
class_name QuestObjective
extends Resource
enum Type { KILL, COLLECT, TALK, REACH }
@export var objective_id: String
@export var type: Type
@export var target: String # Enemy name, item ID, NPC name, location
@export var required_amount: int = 1
@export var current_amount: int = 0
func progress(amount: int = 1) -> void:
current_amount += amount
current_amount = mini(current_amount, required_amount)
func is_complete() -> bool:
return current_amount >= required_amount
undefinedclass_name QuestObjective
extends Resource
enum Type { KILL, COLLECT, TALK, REACH }
@export var objective_id: String
@export var type: Type
@export var target: String # Enemy name, item ID, NPC name, location
@export var required_amount: int = 1
@export var current_amount: int = 0
func progress(amount: int = 1) -> void:
current_amount += amount
current_amount = mini(current_amount, required_amount)
func is_complete() -> bool:
return current_amount >= required_amount
undefinedQuest Manager
任务管理器
gdscript
undefinedgdscript
undefinedquest_manager.gd (AutoLoad)
quest_manager.gd (AutoLoad)
extends Node
signal quest_accepted(quest: Quest)
signal quest_completed(quest: Quest)
signal objective_updated(quest: Quest, objective: QuestObjective)
var active_quests: Array[Quest] = []
var completed_quests: Array[String] = []
func accept_quest(quest: Quest) -> void:
if quest.quest_id in completed_quests:
return
active_quests.append(quest)
quest.completed.connect(func(): _on_quest_completed(quest))
quest_accepted.emit(quest)func _on_quest_completed(quest: Quest) -> void:
active_quests.erase(quest)
completed_quests.append(quest.quest_id)
# Give rewards
for reward in quest.rewards:
reward.grant()
quest_completed.emit(quest)func update_objective(quest_id: String, objective_id: String, amount: int = 1) -> void:
for quest in active_quests:
if quest.quest_id == quest_id:
for obj in quest.objectives:
if obj.objective_id == objective_id:
obj.progress(amount)
objective_updated.emit(quest, obj)
quest.check_completion()
return
func get_active_quest(quest_id: String) -> Quest:
for quest in active_quests:
if quest.quest_id == quest_id:
return quest
return null
undefinedextends Node
signal quest_accepted(quest: Quest)
signal quest_completed(quest: Quest)
signal objective_updated(quest: Quest, objective: QuestObjective)
var active_quests: Array[Quest] = []
var completed_quests: Array[String] = []
func accept_quest(quest: Quest) -> void:
if quest.quest_id in completed_quests:
return
active_quests.append(quest)
quest.completed.connect(func(): _on_quest_completed(quest))
quest_accepted.emit(quest)func _on_quest_completed(quest: Quest) -> void:
active_quests.erase(quest)
completed_quests.append(quest.quest_id)
# Give rewards
for reward in quest.rewards:
reward.grant()
quest_completed.emit(quest)func update_objective(quest_id: String, objective_id: String, amount: int = 1) -> void:
for quest in active_quests:
if quest.quest_id == quest_id:
for obj in quest.objectives:
if obj.objective_id == objective_id:
obj.progress(amount)
objective_updated.emit(quest, obj)
quest.check_completion()
return
func get_active_quest(quest_id: String) -> Quest:
for quest in active_quests:
if quest.quest_id == quest_id:
return quest
return null
undefinedQuest Triggers
任务触发器
gdscript
undefinedgdscript
undefinedExample: Kill quest integration
Example: Kill quest integration
enemy.gd
enemy.gd
func _on_died() -> void:
QuestManager.update_objective("kill_bandits", "kill_bandit", 1)
func _on_died() -> void:
QuestManager.update_objective("kill_bandits", "kill_bandit", 1)
Example: Collection integration
Example: Collection integration
item_pickup.gd
item_pickup.gd
func _on_collected() -> void:
QuestManager.update_objective("gather_herbs", "collect_herb", 1)
func _on_collected() -> void:
QuestManager.update_objective("gather_herbs", "collect_herb", 1)
Example: Talk integration
Example: Talk integration
npc.gd
npc.gd
func interact() -> void:
DialogueManager.start_dialogue(dialogue_id)
QuestManager.update_objective("find_elder", "talk_to_elder", 1)
undefinedfunc interact() -> void:
DialogueManager.start_dialogue(dialogue_id)
QuestManager.update_objective("find_elder", "talk_to_elder", 1)
undefinedQuest UI
任务UI
gdscript
undefinedgdscript
undefinedquest_ui.gd
quest_ui.gd
extends Control
@onready var quest_list := $QuestList
func _ready() -> void:
QuestManager.quest_accepted.connect(_on_quest_accepted)
QuestManager.objective_updated.connect(_on_objective_updated)
refresh_ui()
func refresh_ui() -> void:
for child in quest_list.get_children():
child.queue_free()
for quest in QuestManager.active_quests:
var quest_entry := create_quest_entry(quest)
quest_list.add_child(quest_entry)func create_quest_entry(quest: Quest) -> Control:
var entry := VBoxContainer.new()
var title := Label.new()
title.text = quest.quest_name
entry.add_child(title)
for obj in quest.objectives:
var obj_label := Label.new()
obj_label.text = "%s: %d/%d" % [obj.target, obj.current_amount, obj.required_amount]
entry.add_child(obj_label)
return entryundefinedextends Control
@onready var quest_list := $QuestList
func _ready() -> void:
QuestManager.quest_accepted.connect(_on_quest_accepted)
QuestManager.objective_updated.connect(_on_objective_updated)
refresh_ui()
func refresh_ui() -> void:
for child in quest_list.get_children():
child.queue_free()
for quest in QuestManager.active_quests:
var quest_entry := create_quest_entry(quest)
quest_list.add_child(quest_entry)func create_quest_entry(quest: Quest) -> Control:
var entry := VBoxContainer.new()
var title := Label.new()
title.text = quest.quest_name
entry.add_child(title)
for obj in quest.objectives:
var obj_label := Label.new()
obj_label.text = "%s: %d/%d" % [obj.target, obj.current_amount, obj.required_amount]
entry.add_child(obj_label)
return entryundefinedBest Practices
最佳实践
- Signal-Driven - Emit events, systems listen
- Save Progress - Track completed quests
- Validation - Check prerequisites before accepting
- 信号驱动 - 触发事件,由系统监听
- 保存进度 - 追踪已完成的任务
- 验证 - 接受任务前检查前置条件
Reference
参考
- Related: ,
godot-dialogue-systemgodot-save-load-systems
- 相关:,
godot-dialogue-systemgodot-save-load-systems
Related
相关
- Master Skill: godot-master
- 大师技能:godot-master