본문 바로가기
포트폴리오 제작/Project_P

Project_P 몬스터 HP Bar 네트워크 동기화

by k99812 2025. 12. 22.

개요

 

AI 컨트롤러는 서버에서 동작하기 때문에 몬스터가 플레이어를 감지하면

몬스터의 HP Bar를 켜는 로직이 서버에서 작동하여 리슨서버 클라이언트는

감지되지 않았는데 몬스터 HP Bar가 보이고 감지된 클라이언트는

HP Bar가 보이지 않는 문제가 있었다

 

이를 서버에서 몬스터가 플레이어를 감지하면 감지한 플레이어의

클라이언트에 RPC를 보내는 로직을 추가하여 해결하였다

 

 

코드 살펴보기

 

  1. AIPerception 이벤트로 Delegate가  Broadcast 된다
    •      기존 로직에선 Hp Bar를 조절할 bool 변수만 넘겼지만
           개선한 로직에선 인식한 플레이어를 넘겨 플레이어를 통해 RPC를 전송한다
  2. 콜백함수가 실행되면 인터페이스를 통해 간접 참조하여 플레이어 캐릭터의 함수를 실행한다
    •      이때 넘기는 매개변수는 플레이어가 아닌 액터는 몬스터(this)이다
         이는 RPC를 호출할 액터가 HPBar를 컨트롤할 액터를 넘겨주는 것
  3. 플레이어 캐릭터에서 Client RPC함수를 호출한다
  4. Client RPC 함수에서 다시 인터페이스를 통해 Monster의 HPBar 조절 함수를 호출한다

 

IPPAIControllerInterface

DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FFindTargetDelegate, bool, bFindTarget, AActor*, TargetActor);

class PROJECT_P_API IPPAIControllerInterface
{
	GENERATED_BODY()

	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
	virtual void RunAI() = 0;
	virtual void StopAI() = 0;
	virtual FFindTargetDelegate& GetFindTargetDelegate() = 0;
};

 

인터페이스에 델리게이트를 정의하여 상속받은 객체, 인터페이스를 사용하여

간접참조하는 객체 모두 델리게이트를 사용할 수 있다.

 

간접참조하는 객체에서도 델리게이트를 알 수 있어 델리게이트를 직접 반환하는

함수를 정의하여 사용한다

 

델리게이트를 사용하여 AIPerception으로 인식한 플레이어 액터와

타겟을 인식했는지 bool 변수를 델리게이트를 통하여 전달한다

 

APPAIController

//헤더파일
FORCEINLINE virtual FFindTargetDelegate& GetFindTargetDelegate() override { return FindTargetDelegate; }

FFindTargetDelegate FindTargetDelegate;

void APPAIController::BlackboardTargetUpdate(APawn* Target)
{
	//생략
    FindTargetDelegate.Broadcast(true, Target);
}

void APPAIController::ResetTarget()
{
	//생략
	FindTargetDelegate.Broadcast(false, Target);
}

 

인터페이스 함수를 통해 델리게이트 참조를 반환한다

  •  virtual FFindTargetDelegate& GetFindTargetDelegate() override 

 

플레이어 액터를 넘겨주는 이유는 RPC는 플레이어 컨트롤러 또는

플레이어 컨트롤러가 빙의한 액터 그리고 그 액터의 하위 컴포넌트들만 전송할 수있다

 

즉 몬스터는 플레이어 컨트롤러가 소유(Possess)한 액터가 아니므로 직접

Client RPC를 호출할 수 없다. 따라서 플레이어가 소유권을 가진 PlayerCharacter를

중계(Bridge) 역할로 활용하여 RPC를 전송하였다

 

APPGASCharacterGrunt

void APPGASCharacterGrunt::PossessedBy(AController* NewController)
{
	생략

	// HPBar 델리게이트 연결
	IPPAIControllerInterface* AIController = Cast<IPPAIControllerInterface>(GetController());
	if (AIController)
	{
		AIController->GetFindTargetDelegate().AddDynamic(this, &APPGASCharacterGrunt::FoundTargetCallback);
	}
}

void APPGASCharacterGrunt::FoundTargetCallback(bool bFoundTarget, AActor* TargetActor)
{
	if (!HasAuthority()) return;

	IPPPlayerCharacterInterface* PCInterface = Cast<IPPPlayerCharacterInterface>(TargetActor);
	if (PCInterface)
	{
		PCInterface->RequestSetMonsterHpBar(bFoundTarget, this);
	}
}

 

인터페이스 함수(GetFindTargetDelegate)로 델리게이트를 가져와 콜백함수를 바인드한다

 

인터페이스 함수(RequestSetMonsterHpBar)를 통해 RPC를 전송할 캐릭터를 간접참조한다

 

  • PCInterface->RequestSetMonsterHpBar(bFoundTarget, this);

이때 넘기는 액터는 플레이어 캐릭터가 아닌 몬스터 액터이다

인터페이스 함수를 실행시켜 자기자신(HPBar를 컨트롤할 몬스터 액터)를 넘겨준다

 

void APPGASCharacterGrunt::SetMonstHpBarVisibility(bool bVisible)
{
	HpBar->SetVisibility(bVisible);
}

 

 

플레이어 컨트롤러에서 간접참조하여 실행할 인터페이스 함수

HpBar를 컨트롤한다

 

APPGASCharacterPlayer

void APPGASCharacterPlayer::RequestSetMonsterHpBar(bool bIsFound, AActor* TargetMonster)
{
	if (!HasAuthority()) return;

	ClientRPC_SetMonsterHpBar(bIsFound, TargetMonster);
}

 

몬스터 클래스에서 간접참조하여 실행한 인터페이스 함수

 

  • ClientRPC_SetMonsterHpBar(bIsFound, TargetMonster);

ClientRPC를 호출하여 정보를 넘겨준다

 

void APPGASCharacterPlayer::ClientRPC_SetMonsterHpBar_Implementation(bool bIsFound, AActor* TargetMonster)
{
	IPPMonsterInterface* Target = Cast<IPPMonsterInterface>(TargetMonster);
	if (Target)
	{
		Target->SetMonstHpBarVisibility(bIsFound);
	}
}

 

인터페이스를 통하여 몬스터를 간접참조하여

 

  • Target->SetMonstHpBarVisibility(bIsFound);

HPBar를 컨트롤하는 함수를 실행시켜준다