Replicated

[ChronoSpace] Interaction 구현 본문

언리얼 엔진/ChronoSpace

[ChronoSpace] Interaction 구현

라구넹 2025. 2. 19. 14:21

전에 만들었던 Key와 Altar는  Player가 OnInteract 델리게이트를 가지는데,

상호작용하는 오브젝트들이 충돌을 감지해서 해당 델리게이트를 구독하는 방식이다

 

그런데 인터렉션 인터페이스를 만들어두고 플레이어가 층돌 감지해서 인터렉션 인터페이스를 빼와서 그걸 작동시키는게 나을 것 같다

 

ICSInteractionInterface => 상호작용 오브젝트에 부착

UPlayerInteractionComponent => 플레이어에 부착

뭐가 됐든 상호작용 오브젝트도 감지용 트리거는 붙여놔야 함

 

ICSInteractionInterface 

- BeginInteraction -> 상호작용 오브젝트 감지 시

- EndInteraction -> 상호작용 오브젝트 멀어질 시

- Interact -> 상호작용(F키 누름)

 

이렇게 만들어짐

 

플레이어는 컴포넌트 만들고 트리거 설정

 

그리고 실행 시 해당 컴포넌트의 ExecInteraction 사용

 

void UCSPlayerInteractionComponent::OnComponentBeginOverlapCallback(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepHitResult)
{
	ICSInteractionInterface* Interaction = Cast<ICSInteractionInterface>(OtherActor);

	if ( Interaction )
	{
		Interaction->BeginInteraction();
		CurrentInteractionActor = OtherActor;
		InteractionActors.Emplace(CurrentInteractionActor.GetFName(), CurrentInteractionActor);
	}
}

여러 오브젝트가 동시에 들어올 수 있어서 캐싱해 둬야 함

 

void UCSPlayerInteractionComponent::OnComponentEndOverlapCallback(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	ICSInteractionInterface* Interaction = Cast<ICSInteractionInterface>(OtherActor);

	if (Interaction)
	{
		Interaction->EndInteraction();
		InteractionActors.Remove(OtherActor->GetFName());

		ClearCachedActors();

		if ( InteractionActors.Num() > 0 )
		{
			auto Actor = InteractionActors.CreateIterator();
			CurrentInteractionActor = Actor.Value();
		}
		else
		{
			CurrentInteractionActor = nullptr;
		}
	}
}

트리거 나갈 때 캐시해둔 거 한 번 정리 (Destroy된 거 고려)하고, 남은 객체가 있으면 들고 와서 인터랙션 대상으로 삼음

 

* 굳이 이렇게 캐싱해서 관리하는 이유?

상호작용 오브젝트가 겹치는 상황이 올 수도 있으니까? 유저 경험적으로 유지해두는게 더 나음

Map인 이유는 Stack 쓰면 안되는게 트리거 범위가 다 다르니까 뭐가 먼저 빠져나갈 지 알 수 없음

 

void UCSPlayerInteractionComponent::ClearCachedActors()
{
	for ( auto  Actor = InteractionActors.CreateIterator(); Actor; ++Actor)
	{
		if ( !IsValid( Actor.Value() ) || Actor.Value()->IsPendingKillPending() ) {
			InteractionActors.Remove(Actor.Key());
		}
	}
}

제거되지 않은 객체인지 확실하게 체크하고, 제거된 객체인 경우 맵에서 제거

 

void UCSPlayerInteractionComponent::ExecInteraction()
{
	if ( CurrentInteractionActor )
	{
		ICSInteractionInterface* Interaction = Cast<ICSInteractionInterface>(CurrentInteractionActor);
		Interaction->Interact();
	}
}

인터렉션


사용법은

상호작용 오브젝트에 인터페이스 추가하고

 

이거 세 개 구현

 

Begin, End는 가까이 갔을 때, 멀어졌을 때 발동하는 함수

기본적으로는 위젯 가시화 관련, 다른 거 필요하면 구현해서 넣어도 될 듯

 

Interact는 F 누르면 발동되는 거 내용 구현

트리거도 만들어줘야 하는데 콜리전 프로필 저걸로 해줘야 함

이것만 해주면 나머진 PlayerInteractionComponent에서 알아서 처리함

 

일단 상호작용 표시는 다 뜨고

 

 

다른 오브젝트가 삭제되었을 때, 이전에 캐싱해뒀던 오브젝트로 인터렉션 대상이 잘 옮겨지는 걸 확인 가능하다

 

하나씩 사라지는 건 추가로 설정해뒀다

 

첫 구상 때는 발행 구독 패턴 형태였는데, 지금은 오히려 스트라티지 패턴에 인풋 이벤트를 엮은 형태가 되었다