godot-turn-system

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Turn System

回合系统

Turn order calculation, action points, phase management, and timeline systems define turn-based combat.
回合顺序计算、行动点数、阶段管理和时间线系统构成了回合制战斗的核心。

Available Scripts

可用脚本

active_time_battle.gd

active_time_battle.gd

Framework for Active Time Battle (ATB) systems with async action support.
支持异步动作的动态时间战斗(ATB)系统框架。

timeline_turn_manager.gd

timeline_turn_manager.gd

Expert timeline-based turn manager with interrupts and simultaneous actions.
专业级基于时间线的回合管理器,支持打断机制和同步动作。

NEVER Do in Turn Systems

回合系统的禁止做法

  • NEVER recalculate turn order every action — Sort 50 combatants after every move? O(n log n) × actions = lag. Calculate once per round, update on stat changes only.
  • NEVER use speed ties without determinism — Two units same speed, random order? Non-reproducible replays. Break ties with secondary stat (ID, position, etc.).
  • NEVER forget to validate action costs — Allow action without checking points? Negative AP = exploits. ALWAYS
    if can_perform_action(cost)
    before deducting.
  • NEVER hardcode phase transitions
    if phase == 0: phase = 1
    for 10 phases? Unmaintainable. Use enum + match OR array of phase handlers.
  • NEVER skip turn timeout for networked games — Wait forever for player input? Griefing exploit. ALWAYS implement turn timer with default action.
  • NEVER emit turn_ended before cleanup — Signal listeners start next turn, previous hasn't cleaned up? State corruption. Cleanup FIRST, then emit.

gdscript
undefined
  • 绝对不要每次行动后都重新计算回合顺序——每次行动后都给50个战斗单位排序?O(n log n)复杂度 × 行动次数 = 卡顿。每轮仅计算一次,仅在属性变化时更新即可。
  • 速度相同的情况下绝对不要使用非确定性排序——两个单位速度相同就随机排序?会导致回放无法复现。使用次要属性(ID、位置等)来打破平局。
  • 绝对不要忘记验证行动消耗——不检查点数就允许行动?负AP会导致漏洞。扣除点数前务必先执行
    if can_perform_action(cost)
    判断。
  • 绝对不要硬编码阶段跳转——10个阶段就写
    if phase == 0: phase = 1
    这种代码?完全无法维护。建议使用枚举+match语法,或者阶段处理函数数组。
  • 网络游戏中绝对不要省略回合超时机制——永远等待玩家输入?会导致恶意挂机漏洞。务必实现带有默认行动的回合计时器。
  • 清理工作完成前绝对不要发送turn_ended信号——信号监听器会启动下一回合,但上一回合还没完成清理?会导致状态损坏。先完成清理,再发送信号。

gdscript
undefined

turn_manager.gd (AutoLoad)

turn_manager.gd (AutoLoad)

extends Node
signal turn_started(combatant: Node) signal turn_ended(combatant: Node) signal round_ended
var combatants: Array[Node] = [] var turn_order: Array[Node] = [] var current_turn_index: int = 0
func start_combat(participants: Array[Node]) -> void: combatants = participants calculate_turn_order() start_next_turn()
func calculate_turn_order() -> void: turn_order = combatants.duplicate() turn_order.sort_custom(func(a, b): return a.speed > b.speed)
func start_next_turn() -> void: if current_turn_index >= turn_order.size(): current_turn_index = 0 round_ended.emit() calculate_turn_order() # Recalculate each round
var current := turn_order[current_turn_index]
turn_started.emit(current)
func end_turn() -> void: var current := turn_order[current_turn_index] turn_ended.emit(current) current_turn_index += 1 start_next_turn()
undefined
extends Node
signal turn_started(combatant: Node) signal turn_ended(combatant: Node) signal round_ended
var combatants: Array[Node] = [] var turn_order: Array[Node] = [] var current_turn_index: int = 0
func start_combat(participants: Array[Node]) -> void: combatants = participants calculate_turn_order() start_next_turn()
func calculate_turn_order() -> void: turn_order = combatants.duplicate() turn_order.sort_custom(func(a, b): return a.speed > b.speed)
func start_next_turn() -> void: if current_turn_index >= turn_order.size(): current_turn_index = 0 round_ended.emit() calculate_turn_order() # Recalculate each round
var current := turn_order[current_turn_index]
turn_started.emit(current)
func end_turn() -> void: var current := turn_order[current_turn_index] turn_ended.emit(current) current_turn_index += 1 start_next_turn()
undefined

Action Point System

行动点数系统

gdscript
undefined
gdscript
undefined

combatant.gd

combatant.gd

extends Node
@export var max_action_points: int = 3 var current_action_points: int = 3
func start_turn() -> void: current_action_points = max_action_points
func can_perform_action(cost: int) -> bool: return current_action_points >= cost
func perform_action(cost: int) -> bool: if not can_perform_action(cost): return false
current_action_points -= cost
return true
undefined
extends Node
@export var max_action_points: int = 3 var current_action_points: int = 3
func start_turn() -> void: current_action_points = max_action_points
func can_perform_action(cost: int) -> bool: return current_action_points >= cost
func perform_action(cost: int) -> bool: if not can_perform_action(cost): return false
current_action_points -= cost
return true
undefined

Turn Phases

回合阶段

gdscript
enum Phase { DRAW, MAIN, END }

var current_phase: Phase = Phase.DRAW

func advance_phase() -> void:
    match current_phase:
        Phase.DRAW:
            current_phase = Phase.MAIN
        Phase.MAIN:
            current_phase = Phase.END
        Phase.END:
            TurnManager.end_turn()
            current_phase = Phase.DRAW
gdscript
enum Phase { DRAW, MAIN, END }

var current_phase: Phase = Phase.DRAW

func advance_phase() -> void:
    match current_phase:
        Phase.DRAW:
            current_phase = Phase.MAIN
        Phase.MAIN:
            current_phase = Phase.END
        Phase.END:
            TurnManager.end_turn()
            current_phase = Phase.DRAW

Best Practices

最佳实践

  1. Speed-Based - Initiative determines order
  2. Action Points - Limit actions per turn
  3. Timeout - Add turn timer for online play
  1. 基于速度——主动权决定回合顺序
  2. 行动点数——限制每回合可执行的动作数量
  3. 超时机制——为在线对局添加回合计时器

Reference

参考

  • Related:
    godot-combat-system
    ,
    godot-rpg-stats
  • 相关内容:
    godot-combat-system
    godot-rpg-stats

Related

相关内容

  • Master Skill: godot-master
  • 核心技能:godot-master