공부/Unreal

24.08.08

월러비 2024. 8. 9. 16:03

타겟팅 시스템

가장 가까운 적 반환

  • 헤더 파일 - ‘정면 각도에서 가장 가까운 적 가져오기’ 함수 선언 : ‘히트 결과들’ 레퍼런스 변수를 넣고, 내부 값이 변하지 않도록 const를 붙인다.
//header
private:
	class ACharacter* GetNearlyFrontAngle(const TArray<FHitResult>& InHitResults); //전방에서 가장 가까운 적 저장
  • CPP 파일 - 이전의 ‘히트 결과들’을 매개변수로 받은 ‘히트 결과들’로 바꾼다. - 이전의 플레이어의 위치를 저장한 변수를 ‘오너 캐릭터’의 ‘액터 위치 가져오기’ 함수 호출로 바꾼다. - 가장 가까운 적은 ‘타겟팅 후보’ 에 저장되어있으니 이것을 반환시킨다. - ‘정면 각도에서 가장 가까운 적 가져오기’ 함수에 ‘히트 결과들’ 을 넣어 호출한다.
//CPP
ACharacter* UCTargetComponent::GetNearlyFrontAngle(const TArray<FHitResult>& InHitResults)
{
	//dot이 나올 수 있는 최저값 : -2 (원래 -1 ~ 1이 맞다.)
	float angle = -2.0f;
	ACharacter* candidate = nullptr; //발견된 적

	for (int i = 0; i < InHitResults.Num(); i++)
	{
		FVector targetLocation = InHitResults[i].GetActor()->GetActorLocation(); //타겟 위치

		FVector direction = targetLocation - OwnerCharacter->GetActorLocation(); //플레이어가 타겟을 바라보는 방향
		direction.Normalize();

		//GetControlRotation : 플레이어에 붙어있는 카메라의 회전값
		FRotator rotator = OwnerCharacter->GetControlRotation(); //플레이어 카메라 회전값
		FVector forward = FQuat(rotator).GetForwardVector(); //회전한 회전자의 전방벡터 값

		//Dot : 변수 하나로 내적할때 사용한다.
		//DotProduct : Dot과 같지만, 이건 변수가 두개일때 사용한다.
		float dot = FVector::DotProduct(direction, forward);

		if (dot >= angle)
		{
			angle = dot;
			candidate = Cast<ACharacter>(InHitResults[i].GetActor());
		}

	}

	//전방에 가까운 적 확인용
	//DrawDebugLine(GetWorld(), location, candidate->GetActorLocation(), FColor::Red, true, 5);

	return candidate;
}

//플레이어 전방에서 가장 가까운 적 하나 반환
ACharacter* candidate = GetNearlyFrontAngle(hitResults);

타겟팅 된 적에 파티클 발생시키기

  • 파티클의 위치 조정은 소켓에서 하고, 파티클은 그냥 띄워주는 역한만 맡긴다.
  • 헤더 파일 - ‘타겟팅 후보’ 변수를 받는 ‘바꾸기’ 함수를 선언한다. - 파티클을 저장할 파티클 시스템 변수 선언
//header
UPROPERTY(EditAnywhere, Category = "Settings")
		class UParticleSystem* ParticleAsset;

private:
	void Change(class ACharacter* InCandidate); 
  • CPP 파일 - ‘타겟팅 후보’에 값이 없다면 : 타겟팅을 종료한다. - ‘에셋 가져오기’ 함수에 ‘파티클 저장할 변수, 파티클 에셋 경로’를 넣어 호출한다. - ‘파티클 붙이기’ 함수에 ‘파티클이 저장된 변수, 파티클일 붙일곳, 소켓 이름, 위치 보정값, 회전 보정값, 붙일 위치 설정’를 넣어 호출한다. - 들어온 적을 ‘타겟’ 에 저장한다.
//CPP
void UCTargetComponent::Change(class ACharacter* InCandidate)
{
	//만약 타겟팅 된 적이 없다면
	if (InCandidate == nullptr)
	{
		//타겟팅을 종료한다.
		End();

		return;
	}
	
	//이전에 타겟팅해서 파티클이 남아있다면
	if (!!Particle)
	{
		Particle->DestroyComponent();
	}

	//타겟된 적에게 파티클 띄우기
	//(파티클, 붙일 캐릭터의 메쉬, 소켓 이름, 파티클 생성 보정값, 파티클 출력 보정값
	Particle = UGameplayStatics::SpawnEmitterAttached(ParticleAsset, InCandidate->GetMesh(), "Targetting", FVector::ZeroVector, FRotator::ZeroRotator, EAttachLocation::KeepRelativeOffset);

	Target = InCandidate; //발견된 적을 타겟에 저장
}
  • 마네킹 스켈레톤 파일
    • root 본에 ‘타겟팅’ 소켓을 추가한다.

타겟팅 된 적 바라보기 - 보간 사용

  • 타겟의 상태가 Dead 모드일 경우 타겟팅이 되면 안된다.
  • 오너 캐릭터와 타겟의 거리를 판단해야한다.
    • 두 엑터의 거리차가 탐지 거리를 벗어나게 된다면 타겟팅을 해제할 것이다.
  • 타겟을 바라볼때 확 회전하지 않고 서서히 회전해야한다. - 보간을 사용한다.
  • 타겟까지의 회전값(피치 야 롤) 설명
    • 피치 : 피치는 몸의 좌우 기울기이다. → 플레이어의 카메라 피치와 맞춰야한다.
    • 요 : 타겟을 바라봐야하기 때문에 타깃을 바라보는 요값이 필요하다.
      • 마우스를 움직여도 요값으로 계속 모이게 된다.
    • 롤 : 롤은 실질적으로 쓸모가 없으므로 따라가게만 조절한다.
    • 즉, 카메라의 피치값, 타겟까지의 요 ‘ 롤값
  • CStateComponent
    • 헤더 파일 - ‘사망 모드 확인’ 함수를 선언하고 ‘현재 상태 타입’이 ‘사망’ 상태인지 확인하는 코드를 반환하는것으로 정의한다.
    //header
    FORCEINLINE bool IsDeadMode() { return Type == EStateType::Dead; }
    
  • CTargetComponent
    • 헤더 파일 - ‘보간 속도’ 변수를 선언한다.
    //header
    UPROPERTY(EditAnywhere, Category = "Settings")
    		float InterpSpeed = 5.0f; //회전 보간 속도
    
    • CPP 파일 - ‘틱 컴포넌트’ 함수 확인 - ‘타겟’이 들어왔는지 확인 - ‘타겟’의 ‘스테이트 컴포넌트’를 가져와 변수를 선언하고 저장한다. - ‘스테이트’가 들어왔는지 확인한다. - ‘스테이트’의 ‘사망 모드 확인’ 함수를 호출하여 확인한다. - ‘오너 캐릭터’의 ‘거리차 가져오기’ 함수에 ‘타겟’을 넣어 호출한 결과를 ‘거리’ 변수를 선언하고 저장한다. - ‘거리’가 ‘탐지 거리’보다 크다면(멀다면) : ‘종료’ 함수를 호출하고 끝낸다. - ‘오너 캐릭터’의 ‘컨트롤러 회전값 가져오기’ 함수를 호출하여 ‘컨트롤러 회전값’ 변수를 선언하고 저장한다. - ‘바라보는 회전값 찾기’ 함수에 ‘오너캐릭터의 엑터 위치 가져오기 함수호출, 타겟의 엑터 위치 가져오기 함수 호출’을 넣고 호출한 결과를 ‘오너에서 타겟’ 변수를 선언하고 저장한다. - ‘컨트롤러 회전값’의 ‘X 회전’ 값을 ‘오너에서 타겟’의 ‘X 회전’에 저장한다. - ‘오너 캐릭터’의 ‘컨트롤러 가져오기’에 ‘플레이어 컨트롤러’를 지정하고 호출하여 플레이어 컨트롤러 자료형의 ‘컨트롤러’ 변수를 선언하고 저장한다. - 회전하기 위한 ‘타겟의 회전값’ 변수를 선언하고 ‘컨트롤러 회전값의 피치값, 오너에서 타겟의 요값, 오너에서 타겟의 롤값’을 넣어 저장한다. - ‘회전 보간’ 함수에 ‘컨트롤 회전값, 타겟 회전값, 델타 타임, 보간 속도’를 넣어 호출한 결과(회전자)를 ‘결과’ 변수를 선언하고 저장한다. - ‘컨트롤러’의 ‘컨트롤 회전값 설정’ 함수를 호출하고 ‘결과’를 넣는다.
    //CPP
    CheckNull(Target); //타겟이 없으면 타겟을 바라보고있을 필요가 없다.
    
    //타겟이 죽은상태인지 확인하기위해 상태 컴포넌트를 가져온다.
    UCStateComponent* state = CHelpers::GetComponent<UCStateComponent>(Target); 
    CheckNull(state);
    CheckTrue(state->IsDeadMode());
    
    //GetDistanceTo : 두 엑터의 거리차를 반환하는 함수
    float distance = OwnerCharacter->GetDistanceTo(Target);
    
    //추적거리보다 탐지거리가 멀다면
    if (distance > TraceDistance)
    {
    	//타겟팅 하지 않는다.
    	End();
    
    	return;
    }
    
    //타겟팅 된 적 바라보게 하기 => 피치 회전은 사용하면 안된다
    FRotator controlRotation = OwnerCharacter->GetControlRotation();
    
    //오너가 타겟을 바라보는 회전값
    FRotator OwnerToTarget = UKismetMathLibrary::FindLookAtRotation(OwnerCharacter->GetActorLocation(), Target->GetActorLocation());
    OwnerToTarget.Pitch = controlRotation.Pitch; //피치값을 고정하기 위해 넣는다.
    //OwnerToTarget.Yaw = yaw; 
    
    APlayerController* controller = OwnerCharacter->GetController<APlayerController>();
    
    //피치와 롤은 카메라와 오너를 사용하면 되고, 요는 오너의 요를 사용한다.
    //RInterpTo : 리니어 회전(SLerp)를 써서 서서히 타겟된 적을 바라보게 회전시킨다.
    FRotator targetRotation = FRotator(controlRotation.Pitch, OwnerToTarget.Yaw, OwnerToTarget.Roll); //타겟의 회전값
    FRotator result = UKismetMathLibrary::RInterpTo(controlRotation, targetRotation, DeltaTime, InterpSpeed);
    
    //보간된 값 확인
    CLog::Print(result, 9999);
    
    //OwnerCharacter->GetController()->SetControlRotation(result);
    
    //보간된 회전값 설정
    controller->SetControlRotation(result);
    

타겟팅의 보간이 계속되지 않고 완료 되게끔 설정하기

  • 보간값까지 천천히 계속 보간하는 특징이 있다.
    • 성능에 문제가 되지 않도록 목표치의 근삿값에 도달하면 보간이 완료되게 설정한다.
  • CTargetComponent
    • 헤더 파일 - ‘도착 각도’ 변수를 설정하고 초기화한다.
    //header
    UPROPERTY(EditAnywhere, Category = "Settings")
    		float FinishAngle = 2.0f; //보간 값 근삿값
    
    • CPP 파일 - 보간 코드 직전 확인 - ‘컨트롤 회전값’의 ‘같다’ 함수에 ‘오너캐릭터, 도착 각도’를 넣어 호출한 결과가 비슷하게 같아지면 : ‘컨트롤러’의 ‘컨트롤 회전값 설정’ 함수에 ‘오너에서 타겟’ 변수를 넣어 호출하고 반환한다.
    //CPP
    //보간되는 회전값이 근사치 안이라면 타겟팅 된거로 친다.
    	if (controlRotation.Equals(OwnerToTarget, FinishAngle))
    	{
    		controller->SetControlRotation(OwnerToTarget);
    
    		return;
    	}
    

타겟팅 목표 변경

  • CTargetComponent
    • 헤더 파일 - ‘이동 왼쪽’ ‘이동 오른쪽’ ‘이동’ 함수 선언
    //header
    void MoveLeft();
    void MoveRight();
    
    void Move(bool bRight); //타겟 변경
    
    • CPP 파일 - ‘이동 왼쪽’ 함수 정의 - ‘이동’함수에 False를 넣어 호출한다. - ‘이동 오른쪽’ 함수 정의 - ‘이동’ 함수에 True를 넣어 호출한다.
  • CPlayer
    • CPP 파일 - ‘이동 왼쪽’과 ‘이동 오른쪽’ 액션을 연결한다.
    //CPP
    PlayerInputComponent->BindAction("Target_Left", EInputEvent::IE_Pressed, Target, &UCTargetComponent::MoveLeft);
    PlayerInputComponent->BindAction("Target_Right", EInputEvent::IE_Pressed, Target, &UCTargetComponent::MoveRight);
    

블루프린트에서 반환 함수 사용하기

  • 블루프린트 파일 - 내 블루프린트 창 - 함수 목록에서 + 버튼 클릭 - ‘반환 노드 추가’ 검색
    • 또는, 디테일의 출력 항목의 + 버튼을 눌러도 된다.
    • 반환 함수 노드 - 디테일 - 퓨어 : 반환 전용 노드로 바뀌게 된다.
      • 이전 실행 핀이 사라지고 순수하게 반환값만 내보내는 노드로 사용할 수 있다.
    • C에서는 메크로 함수를 만들때 BlueprintPure를 체크하면 퓨어 노드로 만들 수 있다.

타겟팅 = 외적

  • 내적 : 두 방향벡터의 내각을 반환받는다.
  • 외적 : 두 방향벡터의 수직 벡터를 반환한다.
    • 방향벡터를 넣을때 순서가 중요하다.
    • 왼쪽에 출발 벡터, 오른쪽에 도착 벡터를 놓는다.
      • 시계방향으로 회전한다 : 위로 수직인 벡터가 생긴다.
      • 반 시계 방향으로 회전한다 : 아래로 수직인 벡터가 생긴다.
    • 이때, 생성되는 수직 벡터는 외적한 두 방향 벡터의 거리와 동일하다.
    • 기준축 : 외적의 시작 벡터의 방향을 의미한다. (지금은 정면벡터를 기준축으로 썼다.)
      • 도착 지점이 되는 타겟의 벡터 중 기준축과 평행한 축의 이동은 외적에 아무 영향도 주지 않는다. ⇒ 지금은 정면(X축)이 기준이므로, 타겟의 X축 이동은 외적에 영향을 주지 않는다.
      • 반대로, 기준축에 수직인 벡터(Y축)의 이동은 수직 벡터의 길이와 1 대 1로 대응한다.
  • 외적의 특징 : 시계방향 회전 : +1, 기준축 시작지점과 동일한 위치 : 0, 반시계방향 회전 : -1이다.
    • 외적의 결과 출력 : X, Y, Z가 나오고, 수직 벡터의 길이와 시작 위치에서 타깃까지의 거리가 1 대 1 대응하므로 타깃의 수직벡터를 이동시키면 Z 값이 변한다. ⇒ 우측에 있을경우 +, 좌측에 있을경우 - 값이 나온다.
    • 외적의 결과와 ‘Vector.Up’(0, 0, 1)을 내적한 결과 출력 : 내적은 각 축을 곱하고 나온 각 결과를 더한 값이 결과인데, X와 Y가 0이므로 Z의 결과만 출력된다. ⇒ Z의 결과만 필요하기 때문에 이렇게 내적해서 결과를 출력하는 것이다.
    • 내적의 결과(Z값)이 -1인지 +1인지만 필요하다. ⇒ Sign 함수를 이용하면 +값일때는 +1이, -값일때는 -1이 출력된다.
  • 타겟팅의 왼쪽 적을 찾는 방법
    • 플레이어가 바라보는 타겟을 기준으로 왼쪽을 찾아야한다.
  • 객체의 왼쪽은 정면 벡터로 보는게 아니라 ‘절대 좌표’에서 ‘객체의 위치’로 확인하는것이다.
    • 즉, 플레이어 객체가 있을때 객체의 좌표를 기준으로 왼쪽 오른쪽을 보는것이다.

'공부 > Unreal' 카테고리의 다른 글

24.08.12  (0) 2024.08.16
24.08.09  (0) 2024.08.10
24.08.07  (0) 2024.08.09
24.08.06  (0) 2024.08.09
24.08.05  (0) 2024.08.07