主题
场景视野系统(Scene Vision)
场景视野系统控制单位之间的可见性、战争迷雾和本地视野遮罩表现。它按 GameDataScene 生效,同一个游戏模式里的不同场景可以使用不同视野模式,并可与场景的 SpaceModel 独立组合。
核心分层
| 层级 | API | 作用 |
|---|---|---|
| 场景配置 | GameDataScene.Vision | 选择该场景的视野模式、精确视野观察者策略,以及是否自动使用主控单位绘制本地视野遮罩 |
| 单位默认配置 | GameDataUnit.Vision | 可选配置。单位创建后如果存在该配置,会懒创建 UnitVision 并应用观察者开关和视野形状 |
| 单位能力对象 | unit.Vision / unit.VisionIfCreated | 运行时读写观察者开关、形状和视野范围。Vision 会懒创建;VisionIfCreated 不会创建 |
| 玩家本地表现 | Player.LocalVisionFocus | 客户端本地视野遮罩的渲染焦点。只影响本地表现,不决定服务端权威可见性 |
场景视野模式
SceneVisionConfig.Mode 支持三种模式:
| 模式 | 说明 |
|---|---|
SceneVisionMode.Normal | 默认正常视野。按单位视野范围、阵营关系和地图视野规则计算可见性 |
SceneVisionMode.Open | 视野全开。所有单位默认互相可见,不按视野距离和阻挡限制 |
SceneVisionMode.Precise | 精确视野。按观察者单位的 Sight 距离、视野阻挡和可选扇形角度判定可见性 |
精确视野适合潜行、探索、战争迷雾、扇形视野、墙体遮挡等玩法。没有配置扇形形状时,精确视野仍然是圆形距离 + 阻挡判定。
观察者策略
SceneVisionConfig.ObserverPolicy 只在 SceneVisionMode.Precise 下生效:
| 策略 | 参与精确视野计算的单位 |
|---|---|
VisionObserverPolicy.AllOwnedUnits | 玩家拥有的普通视野单位都参与计算。配置最简单,但单位很多时计算量更高 |
VisionObserverPolicy.Explicit | 只有 unit.Vision.ObserverEnabled == true 的单位参与计算。适合只让英雄、侦察兵、摄像头等少量单位提供视野 |
VisionObserverPolicy.MainControlledUnit | 只有玩家的 MainUnit 参与计算。适合单主控角色、探索类玩法 |
如果使用 Explicit,不要忘记在单位默认配置或运行时代码里启用观察者:
csharp
Vision = new UnitVisionConfig
{
ObserverEnabled = true,
}视野距离
视野距离仍然来自单位默认属性 GSC.GameDataUnitProperty.Sight。UnitVisionConfig 不配置距离,避免出现两套视野半径。
csharp
Properties = new UnitPropertyInitialData
{
{ GSC.GameDataUnitProperty.Sight, 900 },
}运行时 unit.Vision.Range 读写的也是同一个 Sight 属性;服务端写入会改变单位实际视野距离,客户端为只读。Buff、属性公式或其他系统修改 Sight 后,精确视野距离也会使用最终属性值。
UnitVisionConfig.InnerRadius 不是视野距离。它只用于扇形视野:目标落在内半径内时不受角度过滤,外层仍受 Sight 距离限制。
圆形与扇形
单位视野形状通过 UnitVisionConfig.Shape 或运行时 unit.Vision.SetCircle() / unit.Vision.SetCone(...) 配置。
csharp
Vision = new UnitVisionConfig
{
ObserverEnabled = true,
Shape = UnitVisionShape.Cone,
CentralAngle = 90f,
InnerRadius = 120f,
DirectionSource = UnitVisionDirectionSource.Facing,
}| 字段 | 说明 |
|---|---|
Shape = UnitVisionShape.Circle | 圆形视野。半径来自 Sight |
Shape = UnitVisionShape.Cone | 扇形视野。半径仍来自 Sight,额外应用角度过滤 |
CentralAngle | 扇形中心角,单位为度。小于 360 时产生扇形过滤 |
InnerRadius | 扇形内半径。目标在内半径内时不受角度过滤 |
DirectionSource = Facing | 使用单位当前朝向作为扇形方向 |
DirectionSource = Fixed | 使用 FixedDirection 作为固定世界角度 |
扇形只在精确视野模式下参与服务端可见性判定。普通视野模式不会因为配置了 UnitVisionConfig 自动变成扇形。
客户端本地视野遮罩
服务端可见性是权威来源;客户端本地视野遮罩只是表现层,用于把玩家当前视野画成圆形或扇形迷雾。
框架提供一套现成可用的本地视野遮罩渲染,适合“只围绕一个单位显示玩家当前视野”的玩法,例如主控角色探索、潜行或单英雄模式。这个内置渲染同一客户端同一时间只支持一个 Player.LocalVisionFocus,通常就是本地玩家主控单位。
这只是内置客户端表现的限制,不是服务端精确视野或扇形视野的限制。服务端可以通过 AllOwnedUnits 或 Explicit 让多个单位同时提供权威视野,也可以让任意数量单位使用扇形观察形状。客户端如果需要把多个观察者的视野形状同时画出来,可以不启用内置单单位遮罩,改用 Canvas 或 GameGraph 绘制 API 自行绘制圆形、扇形或其他视野提示;这些自绘内容仍然只是表现,不替代服务端可见性判定。
在场景配置中开启 UseMainUnitAsLocalVisionFocus 后,客户端会在精确视野模式下自动把本地玩家的 MainUnit 作为本地视野遮罩焦点,并在没有全局自定义渲染管线时自动使用支持视野遮罩的渲染管线:
csharp
Vision = new SceneVisionConfig
{
Mode = SceneVisionMode.Precise,
ObserverPolicy = VisionObserverPolicy.MainControlledUnit,
UseMainUnitAsLocalVisionFocus = true,
}如果用户主动设置了 Player.LocalVisionFocus,客户端会固定使用这个值,不再自动回退到主控单位;主动设置为 null 表示关闭自动回退和本地遮罩焦点。调用 ClearLocalVisionFocusOverride() 可以清除手动设置,让场景配置重新决定是否回退到本地玩家主控单位。
csharp
#if CLIENT
Player.LocalPlayer.LocalVisionFocus = scoutUnit;
// 需要恢复场景配置驱动时:
Player.LocalPlayer.ClearLocalVisionFocusOverride();
#endif如果游戏模式已经配置了全局自定义渲染管线,框架不会覆盖它;此时需要确保自定义渲染管线支持本地视野遮罩,否则服务端可见性可能正确,但画面上看起来像没有迷雾。
推荐配置流程
- 在
GameDataScene.Vision上选择Normal、Open或Precise。需要墙体遮挡或扇形时使用Precise。 - 在
GameDataUnit.Properties中配置GSC.GameDataUnitProperty.Sight,不要把距离写进UnitVisionConfig。 - 只有需要作为精确视野观察者或需要非默认形状的单位才配置
GameDataUnit.Vision。 - 使用
Explicit策略时,为观察者单位设置ObserverEnabled = true。 - 如果需要客户端显示圆形/扇形迷雾遮罩,开启
UseMainUnitAsLocalVisionFocus,或在客户端设置Player.LocalVisionFocus。 - 单主控角色玩法要确保服务端设置了
player.MainUnit,否则自动本地视野焦点没有可回退的单位。
UnitNodeBinding 绑定节点
SCE 场景中的 replicated GameGraph Node 如果通过 UnitNodeBinding 绑定到 Unit,会随该 Unit 的服务端视野可见性同步。Unit 离开某个客户端视野时,该客户端会收到绑定 Node 的 OnNodeUnreplicated,之后不再收到该 Node 的 transform/delta;Unit 重新进入视野时,会重新收到 OnNodeReplicated 和当前完整 transform。
这条规则覆盖 UnitDriven、NodeDriven 和 Manual 绑定,用于 root、hitbox、hurtbox、fire point、raycast target 或自定义绑定节点。未绑定 Unit 的 replicated Node 不会按自身坐标自动套用单位视野,仍按所属 SCE Scene 同步;如果需要“没有绑定 Unit 的 node 也按所在位置做视野同步”,需要等待独立的 node 级视野同步能力。
性能与 AI 分层建议
精确视野比普通视野模式更贵,因为它不仅看距离,还要考虑视野阻挡,并且扇形观察者还要做角度过滤。实际消耗取决于玩家数、观察者数、单位密度、视野半径、地图阻挡复杂度和当前同步压力;没有一个适合所有项目的固定上限。
粗略建议:
- 优先用
VisionObserverPolicy.Explicit,只把真正需要提供精确视野的单位设为ObserverEnabled = true。 - 一般玩法中,把精确视野观察者控制在几十到一两百个通常更稳;达到几百个时就应该压测和观察服务端帧耗时。
- 不建议让成百上千个小怪全部成为扇形精确视野观察者。大量敌人只需要“能发现玩家并攻击”时,通常不要让它们参与精确视野。
- 视野半径越大、阻挡越复杂、玩家越多,越应该减少观察者数量或降低更新频率相关玩法需求。
- 如果必须让很多单位拥有权威视野,先使用圆形精确视野或普通视野验证性能,再逐步增加扇形观察者数量。
玩家少、怪物多的常见优化
如果游戏中玩家/英雄数量较少,敌方电脑怪物很多,而且只有少数玩家单位、侦察单位或摄像头需要扇形视野,可以把“玩家看见什么”和“电脑如何搜敌”拆开:
- 场景使用
SceneVisionMode.Precise+VisionObserverPolicy.Explicit。 - 只给玩家主控单位、侦察单位等少量单位配置
GameDataUnit.Vision,并设置ObserverEnabled = true。 - 不给普通敌方怪物配置
GameDataUnit.Vision,让它们不参与精确视野计算。 - 给敌方怪物使用自定义
GameDataAIThinkTree.ScanFilters,去掉UnitRelationship.Visible要求,让怪物按自己的扫描范围发现周围敌人。 - 如果玩法需要“潜行”或“不可被普通怪物发现”,优先用自定义扩展
UnitState表示 AI 层潜行,并在敌方 AI 的ScanFilters、技能AcquireSettings.TargetingFilters、以及必要的GameDataEffectSearch.SearchFilters中排除该状态。若复用UnitState.InvisibleToEnemy,要确认去掉Visible后是否还符合项目对侦测、反隐和客户端可见性的设计。
示例:
csharp
ScanFilters = [
new()
{
Required = [UnitRelationship.Enemy, UnitFilter.Unit],
Excluded = [UnitState.Dead, UnitState.Invulnerable, CustomUnitState.Stealth]
},
new()
{
Required = [UnitRelationship.Enemy, UnitFilter.Structure],
Excluded = [UnitState.Invulnerable]
}
]这种做法的含义是:玩家视野仍由少量精确观察者权威计算;电脑怪物的“发现目标”则由 AI 扫描半径和过滤器控制,不再要求目标处于怪物视野内。它只适用于敌方电脑怪物不需要遵守迷雾、墙体遮挡或扇形视野的场合。
如果敌方怪物也必须被墙挡住、必须只能攻击自己扇形视野里的目标,或者玩家可以利用绕后/遮挡躲避怪物,就不要去掉 UnitRelationship.Visible,而应让这些怪物也成为合适的精确视野观察者,并通过 Explicit 控制数量。
常见问题
| 现象 | 优先检查 |
|---|---|
| 服务端看起来像全图可见 | 场景是否配置为 SceneVisionMode.Open;目标单位同步类型是否为 All、Ally 或其他绕过视野的同步范围 |
| 精确视野下看不到应该可见的单位 | 是否同场景;观察者是否参与策略;Sight 是否大于 0;目标是否被墙体/视野阻挡;扇形角度是否朝向目标 |
| 配了扇形但仍是圆形 | 场景是否为 SceneVisionMode.Precise;单位 Shape 是否为 Cone;CentralAngle 是否小于 360 |
| 服务端可见性正常,但客户端没有迷雾遮罩 | 本地玩家是否有 MainUnit 或 LocalVisionFocus;是否开启 UseMainUnitAsLocalVisionFocus;全局自定义渲染管线是否支持本地视野遮罩 |
| 绑定到 Unit 的 hitbox / fire point 节点在客户端一直可见 | 是否真通过 UnitNodeBinding 绑定到该 Unit;未绑定 Unit 的 replicated Node 仍按 SCE Scene 级同步 |
| 想让多个单位一起提供权威视野 | 使用 AllOwnedUnits 或 Explicit。服务端观察者数量不受内置客户端遮罩限制 |
| 想在客户端同时画多个单位的视野形状 | 内置本地视野遮罩只支持单个 LocalVisionFocus;多单位视野表现请用 Canvas 或 GameGraph 绘制 API 自绘 |
| 怪物很多,精确视野开销过高 | 用 Explicit 只让少量单位提供视野;如果怪物不需要遵守迷雾/墙体/扇形,调整 AI ScanFilters 去掉 UnitRelationship.Visible,并用状态过滤处理潜行 |
| 只想做技能扇形范围 | 使用技能/效果系统的搜索或目标过滤,不要把场景视野当作技能范围系统 |