195 lines
5.1 KiB
Go
195 lines
5.1 KiB
Go
|
|
# 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`
|
|||
|
|
|
|||
|
|
新增选项:
|
|||
|
|
|
|||
|
|
```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 双打(单人控多站位)
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
fight.NewFightWithOptions(
|
|||
|
|
fight.WithFightPlayersOnSide(
|
|||
|
|
[]common.PlayerI{ourPlayer},
|
|||
|
|
[]common.PlayerI{oppPlayer},
|
|||
|
|
),
|
|||
|
|
fight.WithFightInputs(ourInputs, oppInputs),
|
|||
|
|
fight.WithInputControllerBinding(fight.InputControllerBindingSingle),
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.2 组队(一人一个站位)
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
fight.NewFightWithOptions(
|
|||
|
|
fight.WithFightPlayersOnSide(
|
|||
|
|
[]common.PlayerI{ourP1, ourP2},
|
|||
|
|
[]common.PlayerI{oppP1, oppP2},
|
|||
|
|
),
|
|||
|
|
fight.WithFightInputs(ourInputs, oppInputs),
|
|||
|
|
fight.WithInputControllerBinding(fight.InputControllerBindingPerSlot),
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.3 仅传已绑定 Input(推荐灵活接入)
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
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(保持输入原绑定)
|
|||
|
|
|
|||
|
|
调用:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
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(单人控制全部站位)
|
|||
|
|
|
|||
|
|
调用:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
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(按站位顺序绑定玩家)
|
|||
|
|
|
|||
|
|
调用:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
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(...)`,避免调用方歧义。
|