项目接口¶
Modular Building System 提供以下项目扩展接口:
| 接口 | 用途 | 推荐实现位置 |
|---|---|---|
IBuildingOwnerProvider |
返回建筑操作发起者的稳定所有者 ID。 | PlayerState |
IBuildingWorldSupportProvider |
自定义 Actor 是否可以作为永久世界支撑。 | 提供支撑的 Actor |
Building Owner Provider¶
IBuildingOwnerProvider 用于将插件的建筑所有权系统对接到项目自己的玩家、阵营或 NPC 标识系统。
插件通常从发起建造操作的 Controller 获取 PlayerState,然后通过该接口读取 FBuildingOwnerId。
GetBuildingOwnerId¶
UFUNCTION(
BlueprintNativeEvent,
BlueprintCallable,
Category = "Building|Owner"
)
FBuildingOwnerId GetBuildingOwnerId();
| 类型 | 名称 | 数据类型 | 说明 |
|---|---|---|---|
| Return | OwnerId |
FBuildingOwnerId |
当前对象对应的稳定建筑所有者标识。 |
返回值应在存档和网络同步期间保持稳定。
推荐实现位置¶
推荐在项目的 PlayerState 中实现该接口。
| 实现位置 | 建议 |
|---|---|
PlayerState |
推荐。适合保存稳定玩家身份,并由服务器复制。 |
PlayerController |
不推荐作为主要所有权来源。切换 Controller 后不够稳定。 |
Pawn |
不推荐。Pawn 可能死亡、重生或被替换。 |
| 其他对象 | 可用于阵营、NPC 或系统级所有者。 |
蓝图实现¶
在项目的 PlayerState 蓝图中:
根据当前玩家返回对应的 FBuildingOwnerId。
C++ 实现¶
// Copyright 2026 Zhiying Li. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerState.h"
#include "Interfaces/BuildingOwnerProvider.h"
#include "MyPlayerState.generated.h"
UCLASS()
class AMyPlayerState
: public APlayerState
, public IBuildingOwnerProvider
{
GENERATED_BODY()
public:
virtual FBuildingOwnerId
GetBuildingOwnerId_Implementation() override;
};
// Copyright 2026 Zhiying Li. All Rights Reserved.
#include "MyPlayerState.h"
FBuildingOwnerId
AMyPlayerState::GetBuildingOwnerId_Implementation()
{
return FBuildingOwnerId::Player(
GetUniqueId().IsValid()
? GetUniqueId()->ToString()
: FString::FromInt(GetPlayerId())
);
}
项目应根据自己的存档和在线身份系统生成稳定 ID。
C++ 调用方式¶
接口函数属于 Unreal Engine Interface Event,必须通过 Execute_ 调用。
Warning
不要直接调用接口事件函数:
应始终通过:
所有者类型¶
FBuildingOwnerId 可以表示不同类型的所有者。
| 类型 | 说明 |
|---|---|
None |
没有有效所有者。 |
Player |
玩家所有者。 |
Faction |
阵营或队伍所有者。 |
NPC |
NPC 所有者。 |
System |
游戏系统或世界所有者。 |
所有权数据的详细说明请参阅 建筑实体。
示列¶
FString ATopiaPlayerState::GetStablePlayerSaveId() const
{
#if WITH_EDITOR
// PIE Test
if (!EditorStableSaveId.IsEmpty())
{
return EditorStableSaveId;
}
return FString::Printf(TEXT("PIE_Player_%d"), GetPlayerId());
#else
const FUniqueNetIdRepl PlayerUniqueId = GetUniqueId();
if (PlayerUniqueId.IsValid())
{
return PlayerUniqueId->ToString();
}
return TEXT("UnknownPlayer");
#endif
}
FBuildingOwnerId ATopiaPlayerState::GetBuildingOwnerId_Implementation()
{
FBuildingOwnerId OwnerInfo;
OwnerInfo.Id = GetStablePlayerSaveId();
OwnerInfo.Type = EBuildingOwnerType::Player;
return OwnerInfo;
}
Building World Support Provider¶
IBuildingWorldSupportProvider 用于自定义某个 Actor 是否能够作为建筑结构的永久世界支撑。
该接口是可选的。普通地形或静态物体也可以通过碰撞通道和标签配置为世界支撑。
CanProvideBuildingWorldSupport¶
UFUNCTION(
BlueprintNativeEvent,
BlueprintCallable,
Category = "Building|World Support"
)
bool CanProvideBuildingWorldSupport(
UPrimitiveComponent* HitComponent,
const FHitResult& HitResult
) const;
| 类型 | 名称 | 数据类型 | 说明 |
|---|---|---|---|
| Param | HitComponent |
UPrimitiveComponent* |
本次支撑检测命中的组件。 |
| Param | HitResult |
const FHitResult& |
本次碰撞检测的完整命中信息。 |
| Return | bCanProvideSupport |
bool |
当前命中是否可以作为永久世界支撑。 |
接口实现可以根据以下数据决定结果:
- 命中的组件
- HISM Instance Index
- 命中位置
- 表面法线
- Actor 当前状态
- 项目自定义权限或玩法规则
检查顺序¶
世界支撑检测按照以下顺序执行:
检查 World Support Object Channel
→ 检查 World Support Excluded Tag
→ 调用 Building World Support Provider
→ 检查可选的 World Support Tag
| 检查 | 说明 |
|---|---|
| Object Channel | 命中组件必须使用配置的世界支撑 Object Channel。 |
| Excluded Tag | Actor 或组件具有排除标签时,始终不能提供支撑。 |
| Interface | Actor 实现接口时,由接口返回最终结果。 |
| Support Tag | 未实现接口且启用严格模式时,需要具有支撑标签。 |
Warning
World Support Excluded Tag 的优先级高于接口。
即使接口返回 true,具有排除标签的 Actor 或组件仍然不能提供支撑。
蓝图实现¶
在需要自定义支撑规则的 Actor 蓝图中:
Class Settings
→ Implemented Interfaces
→ 添加 Building World Support Provider
→ 实现 Can Provide Building World Support
例如,可以根据命中的组件返回不同结果:
| 命中内容 | 返回值 |
|---|---|
| 主体地面组件 | true |
| 装饰组件 | false |
| 已损坏的平台 | false |
| 可承重的指定 HISM Instance | true |
C++ 实现¶
// Copyright 2026 Zhiying Li. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Interfaces/BuildingWorldSupportProvider.h"
#include "MyWorldSupportActor.generated.h"
UCLASS()
class AMyWorldSupportActor
: public AActor
, public IBuildingWorldSupportProvider
{
GENERATED_BODY()
public:
virtual bool
CanProvideBuildingWorldSupport_Implementation(
UPrimitiveComponent* HitComponent,
const FHitResult& HitResult
) const override;
};
// Copyright 2026 Zhiying Li. All Rights Reserved.
#include "MyWorldSupportActor.h"
bool AMyWorldSupportActor::
CanProvideBuildingWorldSupport_Implementation(
UPrimitiveComponent* HitComponent,
const FHitResult& HitResult
) const
{
return IsValid(HitComponent) &&
HitComponent->ComponentHasTag(
TEXT("CanSupportBuilding")
);
}
HISM Instance¶
命中 HISM Component 时,可以通过命中结果获取 Instance Index:
| 值 | 说明 |
|---|---|
INDEX_NONE |
没有命中有效 Instance。 |
0 或更大 |
命中的 HISM Instance Index。 |
接口可以根据不同 Instance 返回不同的支撑结果。
C++ 调用方式¶
const bool bCanProvideSupport =
IBuildingWorldSupportProvider::
Execute_CanProvideBuildingWorldSupport(
HitActor,
HitComponent,
HitResult
);
调用前应检查 Actor 是否实现接口:
if (
IsValid(HitActor) &&
HitActor->Implements<UBuildingWorldSupportProvider>()
)
{
const bool bCanProvideSupport =
IBuildingWorldSupportProvider::
Execute_CanProvideBuildingWorldSupport(
HitActor,
HitComponent,
HitResult
);
}
接口与标签的选择¶
| 需求 | 推荐方式 |
|---|---|
| 所有同类物体都可以提供支撑 | 配置碰撞通道。 |
| 简单指定某个 Actor 或组件可以提供支撑 | 使用 World Support Tag。 |
| 明确禁止某个 Actor 或组件提供支撑 | 使用 World Support Excluded Tag。 |
| 根据组件、Instance 或运行时状态动态判断 | 实现 IBuildingWorldSupportProvider。 |
不需要动态判断时,优先使用碰撞通道和标签,避免为简单对象增加接口实现。
注意事项¶
| 问题 | 说明 |
|---|---|
| 接口没有被调用 | 检查命中组件是否使用正确的 World Support Object Channel。 |
接口返回 true 但仍不提供支撑 |
检查 Actor 或组件是否具有排除标签。 |
| 蓝图中找不到接口事件 | 检查接口是否已添加到 Class Settings。 |
| C++ 调用触发错误 | 使用对应的 Execute_ 函数调用。 |
| HISM 判断错误 | 检查 HitResult.Item 是否为有效 Instance Index。 |
| PlayerState 返回无效 Owner ID | 检查所有者类型和稳定 ID 是否正确填写。 |
结构支撑配置请参阅 结构支撑与坍塌。