游戏模式:ALyraGameMode
路径:/LyraGame/GameModes/LyraGameMode.h/.cpp
学习内容:GameMode定义了游戏的规则。
问题:InitGameState()是如何初始化游戏状态的?
问题:HandleStartingNewPlayer()里当一个新玩家加入时(包括本地分屏和网络连接),游戏模式如何为其生成Pawn和PlayerController?

1. InitGameState() - 游戏状态初始化

void ALyraGameMode::InitGameState()
{
    Super::InitGameState(); // 调用父类

    // 监听经验加载完成    
    ULyraExperienceManagerComponent* ExperienceComponent = GameState->FindComponentByClass<ULyraExperienceManagerComponent>();
    check(ExperienceComponent);
    ExperienceComponent->CallOrRegister_OnExperienceLoaded(FOnLyraExperienceLoaded::FDelegate::CreateUObject(this, &ThisClass::OnExperienceLoaded));
}

初始化流程详解:

  1. 调用父类初始化:首先调用 AModularGameModeBase::InitGameState() 完成基础游戏状态初始化

  2. 获取经验管理器组件

    ULyraExperienceManagerComponent* ExperienceComponent = GameState->FindComponentByClass<ULyraExperienceManagerComponent>();
    
    • 从 GameState 中查找经验管理器组件
    • 这个组件负责管理游戏体验配置的加载和应用
  3. 注册经验加载回调

    ExperienceComponent->CallOrRegister_OnExperienceLoaded(FOnLyraExperienceLoaded::FDelegate::CreateUObject(this, &ThisClass::OnExperienceLoaded));
    
    • 注册 OnExperienceLoaded 回调函数
    • 当经验配置加载完成后,会触发这个回调来生成玩家等后续操作

2. HandleStartingNewPlayer() - 新玩家处理流程

void ALyraGameMode::HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer)
{
    // 延迟开始新玩家直到经验加载完成
    if (IsExperienceLoaded())
    {
        Super::HandleStartingNewPlayer_Implementation(NewPlayer);
    }
}

完整的新玩家生成流程:

阶段1:经验依赖检查

  • 新玩家加入时,首先检查 IsExperienceLoaded()
  • 如果经验尚未加载,玩家生成会被延迟
  • 已登录的玩家会在 OnExperienceLoaded 回调中统一处理

阶段2:实际生成流程(父类调用时)

当调用父类 Super::HandleStartingNewPlayer_Implementation() 时:

  1. PlayerController 创建

    • 游戏模式在构造函数中设置了 PlayerControllerClass = ALyraPlayerController::StaticClass()
    • 引擎会自动创建对应类型的 PlayerController
  2. Pawn 类确定

    UClass* ALyraGameMode::GetDefaultPawnClassForController_Implementation(AController* InController)
    {
        if (const ULyraPawnData* PawnData = GetPawnDataForController(InController))
        {
            if (PawnData->PawnClass)
            {
                return PawnData->PawnClass;
            }
        }
        return Super::GetDefaultPawnClassForController_Implementation(InController);
    }
    
  3. Pawn 数据获取流程

    const ULyraPawnData* ALyraGameMode::GetPawnDataForController(const AController* InController) const
    {
        // 1. 从玩家状态获取
        if (const ALyraPlayerState* LyraPS = InController->GetPlayerState<ALyraPlayerState>())
        {
            if (const ULyraPawnData* PawnData = LyraPS->GetPawnData<ULyraPawnData>())
            {
                return PawnData;
            }
        }
        
        // 2. 从当前经验配置获取
        ULyraExperienceManagerComponent* ExperienceComponent = GameState->FindComponentByClass<ULyraExperienceManagerComponent>();
        if (ExperienceComponent->IsExperienceLoaded())
        {
            const ULyraExperienceDefinition* Experience = ExperienceComponent->GetCurrentExperienceChecked();
            if (Experience->DefaultPawnData != nullptr)
            {
                return Experience->DefaultPawnData;
            }
            return ULyraAssetManager::Get().GetDefaultPawnData();
        }
        
        return nullptr;
    }
    
  4. Pawn 生成和配置

    APawn* ALyraGameMode::SpawnDefaultPawnAtTransform_Implementation(AController* NewPlayer, const FTransform& SpawnTransform)
    {
        if (UClass* PawnClass = GetDefaultPawnClassForController(NewPlayer))
        {
            if (APawn* SpawnedPawn = GetWorld()->SpawnActor<APawn>(PawnClass, SpawnTransform, SpawnInfo))
            {
                // 配置Pawn数据
                if (ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(SpawnedPawn))
                {
                    if (const ULyraPawnData* PawnData = GetPawnDataForController(NewPlayer))
                    {
                        PawnExtComp->SetPawnData(PawnData);
                    }
                }
                SpawnedPawn->FinishSpawning(SpawnTransform);
                return SpawnedPawn;
            }
        }
        return nullptr;
    }
    

阶段3:生成位置选择

AActor* ALyraGameMode::ChoosePlayerStart_Implementation(AController* Player)
{
    if (ULyraPlayerSpawningManagerComponent* PlayerSpawningComponent = GameState->FindComponentByClass<ULyraPlayerSpawningManagerComponent>())
    {
        return PlayerSpawningComponent->ChoosePlayerStart(Player);
    }
    return Super::ChoosePlayerStart_Implementation(Player);
}

阶段4:最终关联

  • 通过 NewPlayer->Possess(SpawnedPawn) 将 PlayerController 与 Pawn 关联
  • 完成玩家状态初始化和其他配置

关键设计特点

  1. 经验驱动的配置:所有玩家配置都通过 Experience 系统管理
  2. 组件化架构:使用专门的组件处理生成、经验管理等
  3. 异步加载支持:支持经验配置的异步加载,避免阻塞
  4. 网络同步:所有流程都考虑了网络环境下的同步问题
  5. 错误恢复:包含完整的错误处理和重试机制

这种设计确保了新玩家加入时的完整且可配置的初始化流程,同时支持复杂的游戏体验需求。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐