TIL_089 : 캐릭터 시야 시스템

김펭귄·5일 전

Today What I Learned (TIL)

목록 보기
89/93

1. 캐릭터 시야 시스템

  • 좀보이드처럼 캐릭터의 시야범위 안에서는 좀비를 볼 수 있고, 시야범위 밖은 좀비를 볼 수 없으며 어둡게 처리하고 싶었음

  • 캐릭터에만 적용하는 것이 아닌, 자동차에서도 사용할 수 있도록 컴포넌트화하여 공통으로 사용하였음

1.1 Post Process

  • Material을 만들고, Material Domain을 Post Process로 설정

  • Material Graph도 사진처럼 설정

    • Lerp A : 시야 범위 밖의 색깔 (어둡게)
    • Lerp B : 시야 범위 내의 색깔 (밝게)
  • 레벨에 Post Process Volume을 배치하고, Post Process Material에 방금 만든 Material을 등록

1.2 Vision Mesh

  • Modeling Mode로 선택하고, capsule과 cone을 잘 붙이고 수정하여 하나의 mesh로 저장

2. Vision Component

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를 활성화 시켜 차의 시야를 탑승자끼리 공유

3. 결과

참고 Velog
참고 유튜브

profile
반갑습니다

0개의 댓글