
좀보이드처럼 캐릭터의 시야범위 안에서는 좀비를 볼 수 있고, 시야범위 밖은 좀비를 볼 수 없으며 어둡게 처리하고 싶었음
캐릭터에만 적용하는 것이 아닌, 자동차에서도 사용할 수 있도록 컴포넌트화하여 공통으로 사용하였음
![]() | ![]() |
|---|
Material을 만들고, Material Domain을 Post Process로 설정
Material Graph도 사진처럼 설정
![]() ![]() |
|---|
![]() | ![]() |
|---|
UVisionComponent::UVisionComponent()
{
// 만들었던 Vision Mesh
VisionMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisionMesh"));
InitSetting(); // 초기 설정
}
void UVisionComponent::InitSetting()
{
VisionMesh->bRenderInMainPass = false;
VisionMesh->bRenderInDepthPass = false;
VisionMesh->bRenderCustomDepth = true;
VisionMesh->CastShadow = false;
VisionMesh->bCastDynamicShadow = false;
Render In Main Pass
오브젝트를 렌더링 할지 말지 여부. 켜져있으면 렌더링 해주고, 꺼져있으면 렌더링은 안 되지만, 다른 Pass를 위해 사용되는 보조용으로 사용됨
Render In Depth Pass
일반 Depth Pass에 이 오브젝트를 포함할지 여부. 켜져 있으면 이 오브젝트의 깊이가 깊이 버퍼에 기록되어 깊이 정도로 렌더링을 결정. 끄면 화면에는 보이더라도 깊이 기반 효과에는 영향 안 줌.
Render CustomDepth
Custom Depth Pass에 이 오브젝트를 추가로 렌더할지 결정. 키면 화면 밖의 별도 텍스처에 이 오브젝트의 깊이가 한 번 더 기록되고, 포스트 프로세스 머티리얼에서 이 값을 읽어 아웃라인, 실루엣 하이라이트, 특정 오브젝트만 마스크 처리 같은 효과를 만들 수 있음.
Shadow
메시로 생기는 그림자는 필요 없으니 제거
void UVisionComponent::BeginPlay()
{
Super::BeginPlay();
if (VisionMesh)
{
VisionMesh->AttachToComponent(this,
FAttachmentTransformRules::KeepRelativeTransform);
VisionMesh->OnComponentBeginOverlap.AddDynamic(this,
&UVisionComponent::OnVisionMeshBeginOverlap);
VisionMesh->OnComponentEndOverlap.AddDynamic(this,
&UVisionComponent::OnVisionMeshEndOverlap);
}
if (IsValid(Character))
{
// 내 캐릭터면 Vision Mesh 활성화
// 다른 플레이어 캐릭터면 비활성화
}
else if (IsValid(Car))
{
// 차에 달린 Vision Mesh는 비활성화
}
}
각 플레이어는 자신의 캐릭터의 시야로만 볼 수 있어야하고 영향을 받아야 함
따라서 서버가 아닌 각자의 클라이언트에서 Owner가 LocallyControlled여야 활성화시킴
void UVisionComponent::OnVisionMeshBeginOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult)
{
if (GetOwner()->HasAuthority())
{
return;
}
if (OtherActor->ActorHasTag("InVisible") == false)
{
return;
}
ACharacter* Character = Cast<ACharacter>(OtherActor);
if (IsValid(Character))
{
OverlappedCharacters.Add(Character);
}
}
void UVisionComponent::OnVisionMeshEndOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex)
{
if (GetOwner()->HasAuthority())
{
return;
}
if (OtherActor->ActorHasTag("InVisible") == false)
{
return;
}
ACharacter* Character = Cast<ACharacter>(OtherActor);
if (IsValid(Character) && OverlappedCharacters.Contains(Character))
{
OverlappedCharacters.Remove(Character);
if (IsVisible())
{
OtherActor->SetActorHiddenInGame(true);
}
}
}
"InVisible"이라는 태그를 통해 이 태그를 가진 대상만 시야 범위에 따라 보이게/안보이게 함
OverlappedCharacters라는 TSet을 만들어서, 현재 시야 범위 내에 있는 캐릭터를 저장
타이머를 통해 0.1초마다 아래의 함수를 불러, 시야 범위 내에 있는 캐릭터를 렌더링 할지말지 정함
void UVisionComponent::CheckVisibilityAll()
{
if (IsVisible() && VisionMesh->IsVisible())
{
for (ACharacter* OverlappedActor : OverlappedCharacters)
{
FVector MyLocation = GetOwner()->GetActorLocation();
FVector OtherLocation = OverlappedActor->GetActorLocation();
FHitResult Hit;
FCollisionQueryParams Params;
Params.AddIgnoredActor(GetOwner());
Params.AddIgnoredActor(OverlappedActor);
Params.bTraceComplex = true;
bool bHitWall = GetWorld()->LineTraceSingleByChannel(
Hit,
MyLocation,
OtherLocation,
ECC_Visibility,
Params
);
if (bHitWall)
{
OverlappedActor->SetActorHiddenInGame(true);
}
else
{
OverlappedActor->SetActorHiddenInGame(false);
}
}
}
}
0.1초마다 다른 캐릭터를 렌더링 할지 말지 정함
라인트레이스를 통해, 캐릭터가 벽 뒤에 있으면 렌더링 안 함
플레이어가 차에 탑승할 경우, 플레이어 캐릭터의 vision component는 비활성화하고, 탑승한 차의 vision component를 활성화 시켜 차의 시야를 탑승자끼리 공유