남이 쓴 상호작용 기능 구현 코드이다.
void AHamsterDemoCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
...
PlayerInputComponent->BindAction("Interaction", IE_Pressed, this, &AHamsterDemoCharacter::OnInteract);
...
}
void AHamsterDemoCharacter::OnInteract()
{
}
// 업데이트 문
void AHamsterDemoCharacter::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
FHitResult hitResult;
if (TraceOn(hitResult))
{
auto interactableObj = TraceInteractableObject(hitResult); // 감지한 물체가 상호 작용 가능한 물체인 지 체크
if (interactableObj != nullptr)
{
bool isSuccessInteract = interactableObj->Interact(); // 물체에게 상호 작용 시도
if (isSuccessInteract)
{
// 상호 작용 성공 시 캐릭터가 할 행동
}
}
}
}
AInteractableObject* AHamsterDemoCharacter::TraceInteractableObject(struct FHitResult& inHit)
{
auto actor = inHit.GetActor();
if (actor == nullptr)
return nullptr;
return Cast<AInteractableObject>(actor);
}
bool AHamsterDemoCharacter::TraceOn(struct FHitResult& OutHit)
{
auto startPos = GetSpawnFVector();
auto forwardVector = GetGunRightFVector();
float TraceOffset = 500.0f;
FVector endPos = ((forwardVector * TraceOffset) + startPos);
FCollisionQueryParams CollisionParams;
return GetWorld()->LineTraceSingleByChannel(OutHit, startPos, endPos, ECollisionChannel::ECC_WorldStatic, CollisionParams);
}
상호 작용 가능한 물체/인물에 다가가면 E키를 누르라는 UI가 보이고,
E키를 눌러 상호작용 시 대화, 탑승 등의 추가 이벤트로 넘어가게 된다.
키보드에서 E키를 눌렀을 때 OnInteract() 가 발동됨
Tick 함수에서 매 틱마다 (프레임마다, 즉 1초에 60회정도) 판정하는 내용은 다음과 같다.
TraceOn(HitResult)에 값이 있는지?
TraceOn 에서는
LineTraceSingleByChannel (RayCast 기능) 을 사용해서, WorldStatic 인 콜리전만 찾음 (WorldStatic은 static mesh에만 적용되는 속성이므로, static mesh를 찾는다는 뜻)
TraceOn은 ray를 맞은 애인 OutHit을 리턴
TraceOn(HitResult)가 nullptr가 아니면 if 문 진입 (ray 맞은 애가 있다면)
TraceInteractableObj 에서 Ray를 맞은 애가 액터인지 & 상호작용 가능한지 (interactable인지) 확인하고 리턴
interactableObj가 nullptr가 아니면 if문 진입 (상호작용 가능하다면)
interactableObj에 Interact() (상호작용 시도)
if(isSucessInteract) 전까지는 모두 interact 여부 판단을 위한 코드임
그 이후로는 상호작용에 성공했을 시 취할 행동의 코드를 기술하면 된다.
OnInteract() 에서 (E키가 눌렸을 때)
조건에 맞지 않으면 무시하고
조건에 맞으면 진행한다.
+) InteractableObj 또는 isSuccessInteract 를 (혹은 둘 다를) 지역변수가 아닌 멤버 변수로 선언해서
OnInteract()에서 가져다가 판정할 수 있게 해도 좋을 것 같음.
'E 누르기' UI는 Interact()에서 띄우게 해도 된다
단, 이미 UI가 떠 있으면 띄우지 않게, 다른 사람이 이미 대화 중이라면 띄우지 않게, 퀘스트를 이미 클리어했다면 띄우지 않게 하기 등 예외처리가 필요하다.
Interact() 에서 E누르기가 가능해서 E를 누른 이후의 처리는 OnInteract() 에서 진행한다 (대화창 등)
11줄을 조정하면 구체의 크기가 바뀌고, 31줄을 조정하면 총알이 남아있는 시간이 바뀔 줄 알았는데...
의외로 그렇게 간단히 바뀌지는 않는 것 같다 (확실하지 않음)
bShouldBounce를 false로 하면 공이 맞은 곳에 그대로 붙어 버린다. 찍찍이공처럼!
AddImpulse를 통해 뭔가 뭔가 할 수 있을 것 같다.
총알이 닿았을 때 이펙트가 터지는 건 어떻게 해야 할 지 아직 잘 모르겠다...
https://docs.unrealengine.com/4.27/ko/Basics/Actors/DecalActor/
아이디어: 데칼 사용? remote procedure call 사용?