Files
bl/docs/fight-input-controller-binding.md
xinian f473c54880
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
feat: 支持多站位战斗控制绑定模式
2026-04-05 00:03:32 +08:00

5.1 KiB
Raw Blame History

Fight Input 控制绑定说明

日期2026-04-04

1. 背景

当前战斗模型中,一个 Input 对应一个战斗站位(actorIndex)。
每个 Input 通过 Input.Player 绑定操作者。

当前建战主路径已收敛为:WithFightInputs(ourInputs, oppInputs)。 即:先由调用方创建并组装双方 Input,再传给战斗模块。

为了同时支持以下两种玩法,新增了可配置绑定策略:

  1. 双打:一个玩家控制多个站位(单人多 Input
  2. 组队:一个玩家控制一个站位(每人一个 Input

2. 绑定策略

文件:logic/service/fight/new_options.go

  • InputControllerBindingKeep

    • 含义:保持输入中已有 Input.Player 绑定,不覆盖
    • 适用:调用方已手动构造 Input 绑定
  • InputControllerBindingSingle

    • 含义:单侧全部站位统一绑定为 players[0]
    • 适用:双打中一个人控制多个站位
  • InputControllerBindingPerSlot

    • 含义:按站位顺序绑定为 players[i]
    • 适用:组队中一人一个站位
    • 说明:当 players 数量不足时,回退绑定 players[0]

3. 选项接口

文件:logic/service/fight/new_options.go

新增选项:

WithInputControllerBinding(mode int)

4. 生效时机

文件:logic/service/fight/new.go

buildFight 中,构建完 Our/Opp 输入后,先执行控制绑定,再执行上下文绑定:

  1. bindInputControllers(f.Our, f.OurPlayers, opts.controllerBinding)
  2. bindInputControllers(f.Opp, f.OppPlayers, opts.controllerBinding)
  3. bindInputFightContext(...)
  4. linkTeamViews()
  5. linkOppInputs()

5. 使用示例

5.1 双打(单人控多站位)

fight.NewFightWithOptions(
    fight.WithFightPlayersOnSide(
        []common.PlayerI{ourPlayer},
        []common.PlayerI{oppPlayer},
    ),
    fight.WithFightInputs(ourInputs, oppInputs),
    fight.WithInputControllerBinding(fight.InputControllerBindingSingle),
)

5.2 组队(一人一个站位)

fight.NewFightWithOptions(
    fight.WithFightPlayersOnSide(
        []common.PlayerI{ourP1, ourP2},
        []common.PlayerI{oppP1, oppP2},
    ),
    fight.WithFightInputs(ourInputs, oppInputs),
    fight.WithInputControllerBinding(fight.InputControllerBindingPerSlot),
)

5.3 仅传已绑定 Input推荐灵活接入

ourInputs := []*input.Input{
    input.NewInput(nil, ourP1), // 站位0
    input.NewInput(nil, ourP2), // 站位1
}
oppInputs := []*input.Input{
    input.NewInput(nil, oppP1), // 站位0
    input.NewInput(nil, oppP2), // 站位1
}

fc, err := fight.NewFightWithOptions(
    fight.WithFightInputs(ourInputs, oppInputs),
    // 不传 WithFightPlayersOnSide 也可
    // owner/opponent 与 side players 会从 inputs 自动提取
)
_ = fc
_ = err

说明:InputControllerBindingSingle/PerSlot 会覆盖 ourInputs/oppInputs 中原有的 Input.Player 绑定;Keep 不覆盖。

6. 新模式绑定实例(逐模式)

以下示例假设我方有两个站位:ourInputs[0]ourInputs[1]

6.1 Keep保持输入原绑定

调用:

fight.NewFightWithOptions(
    fight.WithFightInputs(ourInputs, oppInputs),
    fight.WithInputControllerBinding(fight.InputControllerBindingKeep),
)

输入(调用前):

  • ourInputs[0].Player = ourP1
  • ourInputs[1].Player = ourP2

结果(调用后):

  • ourInputs[0].Player = ourP1
  • ourInputs[1].Player = ourP2

适用:调用方已提前把每个站位绑定好,不希望框架覆盖。

6.2 Single单人控制全部站位

调用:

fight.NewFightWithOptions(
    fight.WithFightPlayersOnSide(
        []common.PlayerI{ourCaptain},
        []common.PlayerI{oppCaptain},
    ),
    fight.WithFightInputs(ourInputs, oppInputs),
    fight.WithInputControllerBinding(fight.InputControllerBindingSingle),
)

输入(调用前):

  • ourInputs[0].Player = ourP1
  • ourInputs[1].Player = ourP2

结果(调用后):

  • ourInputs[0].Player = ourCaptain
  • ourInputs[1].Player = ourCaptain

适用:双打或多站位由同一玩家操作。

6.3 PerSlot按站位顺序绑定玩家

调用:

fight.NewFightWithOptions(
    fight.WithFightPlayersOnSide(
        []common.PlayerI{ourP1, ourP2},
        []common.PlayerI{oppP1, oppP2},
    ),
    fight.WithFightInputs(ourInputs, oppInputs),
    fight.WithInputControllerBinding(fight.InputControllerBindingPerSlot),
)

输入(调用前):

  • ourInputs[0].Player = anyA
  • ourInputs[1].Player = anyB

结果(调用后):

  • ourInputs[0].Player = ourP1
  • ourInputs[1].Player = ourP2

补位规则:若 players 数量不足(例如只传一个 ourP1),剩余站位回退绑定 players[0]

7. 注意事项

  1. 默认模式是 InputControllerBindingKeep,不影响现有调用。
  2. 若传入 WithFightInputs(...) 且每个 Input.Player 已预先绑定,可继续用默认模式。
  3. 仅传 WithFightInputs(...) 也可工作:框架会从 ourInputs/oppInputs 自动提取 ourPlayers/oppPlayers,并以各侧首位玩家作为 owner/opponent。
  4. 推荐在新组队逻辑中显式传 WithInputControllerBinding(...),避免调用方歧义。