当前位置: 首页 > news >正文

Lyra学习笔记2 GFA_AddComponents与ULyraPlayerSpawningManagerComponent

目录

  • 前言
  • GameFeatureAction_AddComponents
  • ULyraPlayerSpawningManagerComponent
    • 缓存所有PlayerStart位置
    • 选择位置

前言

1.以control模式为例
2.比较散,想单独拿出一篇梳理下Experience的流程

GameFeatureAction_AddComponents

这部分建议看
《InsideUE5》GameFeatures架构(五)AddComponents
这里写的内容相当于是我跟着走了一遍

Lyra中角色基本类ALyraCharacter类似于一个框架,负责转发事件给其组件,组件实现相关功能,所以ALyraCharacter甚至只有几百行(在这个不算小的项目里这真的算短了(我觉得))。

在研究生成角色流程之前,我想先研究一下GameFeatureAction中的AddComponents,帮助之后的理解。

在加载Experience的过程中,执行到ULyraExperienceManagerComponent::OnExperienceLoadComplete时会激活GameFeature,以AddComponents为例,一直到OnGameFeatureActivating的调用栈如图:
在这里插入图片描述
而在OnGameFeatureActivating中都会调用到AddToWorld
前面经过一些World,客户端服务端之类的判断后,执行逻辑在如下的一行:

void UGameFeatureAction_AddComponents::AddToWorld(const FWorldContext& WorldContext, FContextHandles& Handles) {... 
Handles.ComponentRequestHandles.Add(GFCM->AddComponentRequest(Entry.ActorClass, ComponentClass, static_cast<EGameFrameworkAddComponentFlags>(Entry.AdditionFlags))); 
... 
}

GFCM::AddComponentRequest

TSharedPtr<FComponentRequestHandle> UGameFrameworkComponentManager::AddComponentRequest(const TSoftClassPtr<AActor>& ReceiverClass, TSubclassOf<UActorComponent> ComponentClass, const EGameFrameworkAddComponentFlags AdditionFlags)
{// You must have a receiver and component class. The receiver cannot be AActor, that is too broad and would be bad for performance.if (!ensure(!ReceiverClass.IsNull()) || !ensure(ComponentClass) || !ensure(ReceiverClass.ToString() != TEXT("/Script/Engine.Actor"))){return nullptr;}//一些检查FComponentRequestReceiverClassPath ReceiverClassPath(ReceiverClass);UClass* ComponentClassPtr = ComponentClass.Get();FComponentRequest NewRequest;NewRequest.ReceiverClassPath = ReceiverClassPath;NewRequest.ComponentClass = ComponentClassPtr;// Add a request if there is not an already existing one. Note that it will only uses the receiver and component class to check for uniqueness, not the addition flags.int32& RequestCount = RequestTrackingMap.FindOrAdd(NewRequest);RequestCount++;if (RequestCount == 1)//第一次匹配{EGameFrameworkAddComponentResult Result = EGameFrameworkAddComponentResult::Failed;auto& RequestInfoSet = ReceiverClassToComponentClassMap.FindOrAdd(ReceiverClassPath);RequestInfoSet.Add({ ComponentClassPtr, AdditionFlags } );if (UClass* ReceiverClassPtr = ReceiverClass.Get()){UGameInstance* LocalGameInstance = GetGameInstance();if (ensure(LocalGameInstance)){UWorld* LocalWorld = LocalGameInstance->GetWorld();if (ensure(LocalWorld)){for (TActorIterator<AActor> ActorIt(LocalWorld, ReceiverClassPtr); ActorIt; ++ActorIt)//遍历场景中的Actor{if (ActorIt->IsActorInitialized())//调用过BeginPlay{
#if WITH_EDITORif (!ReceiverClassPtr->HasAllClassFlags(CLASS_Abstract)){ensureMsgf(AllReceivers.Contains(*ActorIt), TEXT("You may not add a component request for an actor class that does not call AddReceiver/RemoveReceiver in code! Class:%s"), *GetPathNameSafe(ReceiverClassPtr));}
#endifResult = CreateComponentOnInstance(*ActorIt, ComponentClass, AdditionFlags);//创建组件}}}}}else{// Actor class is not in memory, there will be no actor instances}return MakeShared<FComponentRequestHandle>(this, ReceiverClass, ComponentClass);}return nullptr;
}
EGameFrameworkAddComponentResult UGameFrameworkComponentManager::CreateComponentOnInstance(AActor* ActorInstance, TSubclassOf<UActorComponent> ComponentClass, const EGameFrameworkAddComponentFlags AdditionFlags)
{...UActorComponent* NewComp = NewObject<UActorComponent>(ActorInstance, ComponentClass, NewComponentName);...
}

一些细节原文写的很好《InsideUE5》GameFeatures架构(五)AddComponents

ULyraPlayerSpawningManagerComponent

总结一下目前为止的流程:

读取WordSetting中的Experience->
GameState中的ULyraExperienceManagerComponent加载Experience->
激活Experience中配置的GameFeature->
执行Actions

Control关卡中的B_LyraShooterGame_ControlPoints(Experience)的Actions中,
有添加组件的Action,其中配置了在LyraGameState中添加ULyraPlayerSpawningManagerComponent,这个组件负责了管理生成位置。

缓存所有PlayerStart位置

void ULyraPlayerSpawningManagerComponent::InitializeComponent()
{Super::InitializeComponent();UE_LOG(LogTemp,Warning,TEXT("LAPI: ULyraPlayerSpawningManagerComponent::InitializeComponent"));FWorldDelegates::LevelAddedToWorld.AddUObject(this, &ThisClass::OnLevelAdded);UWorld* World = GetWorld();World->AddOnActorSpawnedHandler(FOnActorSpawned::FDelegate::CreateUObject(this, &ThisClass::HandleOnActorSpawned));for (TActorIterator<ALyraPlayerStart> It(World); It; ++It){if (ALyraPlayerStart* PlayerStart = *It){CachedPlayerStarts.Add(PlayerStart);}}
}

直接看后半部分可知缓存了场景中所有的PlayerStart。
再来看OnLevelAdded这个函数:

void ULyraPlayerSpawningManagerComponent::OnLevelAdded(ULevel* InLevel, UWorld* InWorld)
{if (InWorld == GetWorld()){for (AActor* Actor : InLevel->Actors){if (ALyraPlayerStart* PlayerStart = Cast<ALyraPlayerStart>(Actor)){ensure(!CachedPlayerStarts.Contains(PlayerStart));CachedPlayerStarts.Add(PlayerStart);}}}
}

这里是新的Level被AddToWorld的时候调用的,以实现更新PlayerStart,从L_DefaultEditorOverview到L_Convolution_Blockout并不会触发,因为是加载组件之后才绑定的。
同理HandleOnActorSpawned是负责处理动态生成的PlayerStart的。

选择位置

加载完地图资源后,GameMode会将ChoosePlayerStart转到SpawningManagerComponent的ChoosePlayerStart函数:
在这里插入图片描述
Choose函数比较长

AActor* ULyraPlayerSpawningManagerComponent::ChoosePlayerStart(AController* Player)
{if (Player){
#if WITH_EDITORif (APlayerStart* PlayerStart = FindPlayFromHereStart(Player)){return PlayerStart;}
#endif//这部分处理编辑器中PlayFromHere的PlayerStartPIE的特殊情况TArray<ALyraPlayerStart*> StarterPoints;for (auto StartIt = CachedPlayerStarts.CreateIterator(); StartIt; ++StartIt){if (ALyraPlayerStart* Start = (*StartIt).Get()){StarterPoints.Add(Start);}else{StartIt.RemoveCurrent();}}//处理完后StarterPoints存的是安全的强引用if (APlayerState* PlayerState = Player->GetPlayerState<APlayerState>()){// start dedicated spectators at any random starting location, but they do not claim itif (PlayerState->IsOnlyASpectator()){if (!StarterPoints.IsEmpty()){return StarterPoints[FMath::RandRange(0, StarterPoints.Num() - 1)];}return nullptr;}}//若是Spectators在数组内随机一个位置返回AActor* PlayerStart = OnChoosePlayerStart(Player, StarterPoints);//返回nullptrif (!PlayerStart)//若为nullptr,暂时必然为nullptr{PlayerStart = GetFirstRandomUnoccupiedPlayerStart(Player, StarterPoints);//若有未占用,则在其中随机一个,若没有就在已经占用的随机一个}if (ALyraPlayerStart* LyraStart = Cast<ALyraPlayerStart>(PlayerStart)){LyraStart->TryClaim(Player);//尝试占用}return PlayerStart;}return nullptr;
}

待续…

http://www.xdnf.cn/news/733267.html

相关文章:

  • 玛哈特校平机实战指南:选型、应用痛点
  • 【Unity】模型渐变技术 BlendShapes变形
  • 深入理解复数加法与乘法:MATLAB演示
  • 云原生微服务架构演进之路:理念、挑战与实践
  • Hive的数据倾斜是什么?
  • 杨传辉:构建 Data × AI 能力,打造 AI 时代的一体化数据底座|OceanBase 开发者大会实录
  • Armv7l或树莓派32位RPI 4B编译faiss
  • @Pushgateway自定义脚本推送数据
  • C++继承权限与访问控制详解
  • 解决win自动重启(自用,留链接)
  • Express教程【002】:Express监听GET和POST请求
  • 基于CAPL的DDS子消息解析- Data
  • golang 基于redis实现集群中的主实例选举
  • Nginx网站服务:从入门到LNMP架构实战
  • 生动形象理解CNN
  • 文件雕刻——一种碎片文件的恢复方法
  • 为什么建立 TCP 连接时,初始序列号不固定?
  • 日志技术-LogBack、Logback快速入门、Logback配置文件、Logback日志级别
  • Kubernetes 入门:安装 kubectl 并掌握基础命令
  • RK3568 OH5.1 编译运行程序hellworld
  • (22)大文件流式处理
  • 五星级酒店技能比赛主持稿串词
  • framework之慕课大巴
  • PCL 渲染显示
  • 电子电路:初步认识4013D触发器
  • 【深度剖析】义齿定制行业数字化转型模式创新研究(上篇3:数字化转型动机分析)
  • 实验设计与分析(第6版,Montgomery)第5章析因设计引导5.7节思考题5.13 R语言解题
  • 人工智能编程学习心得:从零基础到独立开发的蜕变之路
  • 副本(Replica)在Elasticsearch中扮演什么角色?
  • 算力租赁革命:弹性模式如何重构数字时代的创新门槛​