PART 2 - 강의 3/3

커스텀 노드 구현

팀 기반 노드, Dormancy 노드 등 게임 특화 노드 개발

01

커스텀 노드 기초

UReplicationGraphNode 상속

C++ UCLASS() class UMyCustomReplicationNode : public UReplicationGraphNode { GENERATED_BODY() public: // Actor 추가 알림 virtual void NotifyAddNetworkActor( const FNewReplicatedActorInfo& ActorInfo) override; // Actor 제거 알림 virtual bool NotifyRemoveNetworkActor( const FNewReplicatedActorInfo& ActorInfo, bool bWarnIfNotFound) override; // 연결별 Actor 목록 수집 (핵심!) virtual void GatherActorListsForConnection( const FConnectionGatherActorListParameters& Params) override; protected: // 노드가 관리하는 Actor 목록 FActorRepListRefView ReplicationActorList; };
02

팀 기반 노드 구현

같은 팀에게만 복제하는 노드

C++ UCLASS() class UReplicationGraphNode_TeamBased : public UReplicationGraphNode { GENERATED_BODY() // 팀별 Actor 목록 TMap<int32, FActorRepListRefView> TeamActorLists; public: virtual void NotifyAddNetworkActor( const FNewReplicatedActorInfo& ActorInfo) override { int32 TeamId = GetActorTeamId(ActorInfo.Actor); TeamActorLists.FindOrAdd(TeamId).Add(ActorInfo.Actor); } virtual void GatherActorListsForConnection( const FConnectionGatherActorListParameters& Params) override { // 뷰어의 팀 ID 가져오기 int32 ViewerTeamId = GetViewerTeamId(Params.Viewer); // 같은 팀의 Actor만 복제 목록에 추가 if (FActorRepListRefView* TeamList = TeamActorLists.Find(ViewerTeamId)) { Params.OutGatheredReplicationLists.AddReplicationActorList(*TeamList); } } private: int32 GetActorTeamId(AActor* Actor) { if (ITeamInterface* TeamActor = Cast<ITeamInterface>(Actor)) { return TeamActor->GetTeamId(); } return -1; // 팀 없음 } };
활용 예시

팀 전용 마커, 아군 HP 바, 팀 채팅 등 같은 팀에게만 보여야 하는 정보에 유용합니다.

03

Fortnite 스타일 노드 구성

대규모 Battle Royale 최적화 패턴

C++ class UFortniteReplicationGraph : public UReplicationGraph { // 1. 항상 관련있는 Actor (GameState, GameMode 등) UReplicationGraphNode_AlwaysRelevant* AlwaysRelevantNode; // 2. 연결별 항상 관련있는 Actor (PlayerState, PlayerController) UReplicationGraphNode_AlwaysRelevant_ForConnection* ForConnectionNode; // 3. 공간 분할 (동적 Actor) UReplicationGraphNode_GridSpatialization2D* GridSpatialization; // 4. Dormant Actor (상호작용 전까지 휴면) UReplicationGraphNode_DormancyNode* DormancyNode; // 5. 팀 기반 노드 (같은 팀만 복제) UReplicationGraphNode_TeamBased* TeamNode; };
노드 우선순위
  • AlwaysRelevant - 최우선, 모든 연결에 복제
  • ForConnection - 특정 연결에만 항상 복제
  • GridSpatialization - 거리 기반 필터링
  • Dormancy - 휴면 Actor 효율적 관리
04

Dormancy 노드 구현

휴면 Actor의 효율적 관리

C++ UCLASS() class URepGraphNode_DynamicDormancy : public UReplicationGraphNode { GENERATED_BODY() FActorRepListRefView DormantActors; FActorRepListRefView ActiveActors; public: virtual void GatherActorListsForConnection( const FConnectionGatherActorListParameters& Params) override { // 활성 Actor만 복제 목록에 추가 Params.OutGatheredReplicationLists .AddReplicationActorList(ActiveActors); // 휴면 Actor는 뷰어 근접 시에만 FVector ViewerLoc = Params.Viewer.ViewLocation; for (AActor* Actor : DormantActors) { float Dist = FVector::Dist( ViewerLoc, Actor->GetActorLocation()); if (Dist < WakeUpDistance) { WakeActor(Actor); } } } void WakeActor(AActor* Actor) { DormantActors.Remove(Actor); ActiveActors.Add(Actor); Actor->FlushNetDormancy(); } };
FlushNetDormancy 비용

FlushNetDormancy()는 해당 Actor의 모든 프로퍼티를 다시 비교해야 하므로 비용이 큽니다. 한 프레임에 대량의 Actor를 깨우지 마세요. 프레임당 최대 깨우기 수를 제한하는 로직을 추가하세요.

SUMMARY

핵심 요약

  • GatherActorListsForConnection - 연결별 복제 Actor 목록 결정의 핵심
  • 팀 기반 노드 - 같은 팀에게만 복제, 적 위치 정보 숨김에 유용
  • Dormancy 노드 - 상호작용 전 Actor의 효율적 관리
  • 노드 조합 - 여러 노드를 조합하여 게임 특화 최적화
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: 팀 기반 커스텀 노드 구현

UReplicationGraphNode를 상속하여 IGenericTeamAgentInterface 기반 팀 필터링 노드를 구현하세요. GatherActorListsForConnection에서 뷰어의 TeamId를 확인하고 같은 팀 Actor만 복제 목록에 추가하세요.

실습 2: 거리 기반 Dormancy 노드 구현

WakeUpDistance를 설정 가능한 Dormancy 노드를 구현하세요. NotifyAddNetworkActor에서 Actor의 초기 Dormancy 상태를 확인하고, GatherActorListsForConnection에서 거리 체크 후 FlushNetDormancy를 호출하는 로직을 구현해보세요.

심화 과제

AlwaysRelevant + GridSpatialization + TeamBased + Dormancy 4개 커스텀 노드를 조합한 완전한 UReplicationGraph을 구현하고, RouteAddNetworkActorToNodes에서 Actor 클래스별로 적절한 노드에 분배하는 전체 시스템을 완성해보세요.