跳转至

项目接口

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 蓝图中:

Class Settings
→ Implemented Interfaces
→ 添加 Building Owner Provider
→ 实现 Get Building Owner ID

根据当前玩家返回对应的 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_ 调用。

FBuildingOwnerId OwnerId =
    IBuildingOwnerProvider::Execute_GetBuildingOwnerId(
        PlayerState
    );

Warning

不要直接调用接口事件函数:

Provider->GetBuildingOwnerId();

应始终通过:

IBuildingOwnerProvider::Execute_GetBuildingOwnerId(Object);

所有者类型

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:

const int32 InstanceIndex = HitResult.Item;
说明
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 是否正确填写。

结构支撑配置请参阅 结构支撑与坍塌