타겟팅 시스템 - 전에 이어서
타겟팅 - 타겟 재선정 중 전방벡터 기준으로 수직거리와 객체 저장
- 절댓값을 사용한다는것 : 왼쪽이든 오른쪽이든 가장 작은 값을 가까운 적이라고 판단한다.
- 문제점 : 빠르게 타겟을 이동하면 원하는 타겟이 아니라 다른 적을 찾는 문제가 생긴다.
- GetControlRotation() : 회전자(Rotator)를 반환한다.
- Rotator는 벡터를 반환하지 못한다.
- 해결법 : FQuat 를 사용한다. ⇒ 회전자를 쿼터니온으로 바꾸고, 쿼터니온의 전방벡터를 가져온다.
- 두 방향 벡터의 외적 결과인 수직 벡터의 길이 = 평행한 벡터끼리의 거리와 일 대 일 관계이다.
- 언리얼은 개발자가 풀지 않는 이상 ‘캐릭터가 겹치지 않으므로’ 같은 위치에 있을 수 없다.
- CTargetComponent
- CPP 파일 -’타겟 이동’ 함수 정의 - ‘오너 캐릭터’의 ‘액터 위치 가져오기’ 함수를 ‘위치’ 함수를 선언하고 저장한다. - 액터 배열 변수 ‘무시하는 객체들’ 변수 선언 - ‘무시하는 객체들’의 ‘추가’ 함수에 ‘오너 캐릭터’를 넣어서 오너 캐릭터 탐색을 무시하게 한다. - ‘무시하는 객체들’의 추자’ 함수’에 ‘타겟’ 변수를 넣고 호출한다. - ‘히트 결과들’ 배열 변수 선언 - ‘구 멀티 탐색’ 함수에 ‘요소들’을 넣고 호출한다. - ‘수직간 거리, 탐색된 적’을 저장할 TMap 클래스의 ‘맵’ 변수를 선언한다. - ‘히트 결과들’ 배열의 요소를 하나씩 꺼내서 ‘히트 결과’에 넣고 반복한다. : 히트된 결과가 캐릭터로 형변환 되는지 확인하기 위해 캐릭터 자료형을 넣은 ‘Cast’ 함수에 ‘히트 결과’의 ‘액터 가져오기’ 함수를 넣어 호출하고 ‘캐릭터’ 변수에 저장한다. - ‘캐릭터’ 변수가 비었다면 : 반복을 무시하고 벗어난다. - 플레이어에서 타겟을 바라보는 방향을 구하기 위해 ‘캐릭터 : 히트 탐색된 적’의 ‘액터 위치 가져오기’ 함수를 호출하고 저장하는 변수를 ‘위치 2’로 선언한다. - ‘오너 캐릭터’의 ‘컨트롤러 회전값 가져오기’함수 호출을 ‘FQuat’함수에 넣은 결과에서 ‘전방 벡터 가져오기’ 함수를 호출한 결과를 ‘전방’ 변수를 선언하고 저장한다. - ‘위치 2 : 적 위치’ - ‘위치 1 ; 플레이어 위치’의 결과를 저장할 ‘방향’ 변수를 선언하고 저장한다. - ‘방향’의 ‘일반화’ 함수를 호출한다. - ‘외적’ 함수에 ‘기준축인 전방 벡터, 뱡향’ 을 넣고 호출한 결과를 ‘외적’ 변수를 선언하고 저장한다. - ‘내적’ 함수에 ‘내적 벡터, 외적’을 넣고 호출한 결과를 ‘내적’ 변수를 선언하고 저장한다. - ‘맵’의 ‘추가’ 함수에 ‘내적, 캐릭터 : 탐색된 적’을 넣고 호출한다.
//CPP //구 추적에서 탐지된 객체를 플레이어 전방벡터를 기준으로 거리와 적 객체를 저장하는 코드 //KeyValue TMap<float, ACharacter*> map; for (const FHitResult& hitResult : hitResults) { ACharacter* character = Cast<ACharacter>(hitResult.GetActor()); if (character == nullptr) { //캐릭터가 아니라면 건너뛴다. continue; } //회전된 오너 캐릭터의 카메라 회전값의 전방벡터를 가져온다. => 타겟팅의 기준이 된다. FVector forward = FQuat(OwnerCharacter->GetControlRotation()).GetForwardVector(); //히트된 캐릭터 위치 FVector location2 = character->GetActorLocation(); //오너가 적을 바라보는 방향 FVector direction = location2 - location; direction.Normalize(); //외적 FVector cross = FVector::CrossProduct(forward, direction); //전방벡터가 기준이되니 매우 중요하다. //수직간 거리는 '내적'을 이용해서 구할 수 있다. float dot = FVector::DotProduct(FVector::UpVector, cross); map.Add(dot, character); //수직간거리(좌측, 우측 판단)와 캐릭터 저장 }
타겟팅 - 타겟 재선정 중 좌 우 타겟 변경
- 0은 현재 설정된 타겟이니 0을 제외한 -와 +의 탐지된 적만 찾는다.
- 왼쪽이든 오른쪽이든 ‘절댓값’을 씌우면 기준축에서 가까운 거리는 가장 작은 값이된다.
- CTargetComponent
- CPP 파일 - ‘타겟 이동’ 함수 정의 - ‘최솟값’ 변수를 선언하고 ‘float 최대값 메크로’를 저장한다. - 변경할 타겟을 저장할 ‘타겟 후보자’ 변수를 선언하고 Null로 초기화한다. - ‘맵’의 요소를 하나씩 꺼내 TPair 자료형의 ‘페어’ 변수에 저장한다. : ‘에어’의 ‘키 : 수직간 거리’를 ‘키’ 변수를 선언하고 저장한다. - ‘오른쪽 확인’ 변수가 True 라면 : ‘키’ 변수가 0.0f보다 작다면(왼쪽 객체들을 의미한다.) : 반복을 건너 뛴다. - 오른쪽이 아니라면 : ‘키’ 변수가 0.0f 보다 크다면(오른쪽 객체들을 의미한다.) : 반복을 건너뛴다. - ‘키’를 ‘절댓값’ 함수에 넣은 결과가 ‘최솟값’ 보다 작다면 : ‘최솟값’에 ‘키’를 ‘절댓값’ 함수에 넣은 결과를 저장하고, ‘페어’의 ‘결과 : 탐지된 객체’를 ‘타겟 후보자’ 변수에 저장한다. - ‘타겟 바꾸기’ 함수에 ‘타겟 후보자’ 변수를 넣고 호출한다.
//CPP //타겟 이동 왼쪽, 오른쪽 구분 //FLT_MAX : 매크로, float 의 가장 큰 값 float minimum = FLT_MAX; ACharacter* candidate = nullptr; for (TPair<float, ACharacter*> pair : map) { float Key = pair.Key; //키에 외적간 거리가 있다. //오른쪽이라면 : 즉, 매개값이 1이라면 if (bRight) { //오른쪽이면 0보다 작은 왼쪽을 배제한다. if (Key < 0.0f) { continue; } } else { //왼쪽이라면 0보다 큰 오른쪽을 배제한다. if (Key > 0.0f) { continue; } } //최솟값보다 작다면 : 그 값이 다음 타겟이 된다. if (FMath::Abs(Key) < minimum) { minimum = FMath::Abs(Key); candidate = pair.Value; } } //Change에서 null값이 들어간다면 : 타겟팅이 해제된다. Change(candidate);
2교시
- 타겟팅 끝
- 워프 시작
타겟팅 - 타겟 재선정 중 또 타깃 변경 안되게 막기
- 타깃 변경중 또 타깃 변경을 누르게 된다면 원하는 타겟이 아닌 다른 적을 타겟팅 하는 경우가 생긴다.
- CTargetComponent
- 헤더 파일 - ‘타겟 토커스 움직임 확인’ 변수 선언
//header private: bool bMovingFocus; //타겟 변경중인지 확인- CPP 파일 - ‘틱 컴포넌트’ 함수 확인 - 타겟 근삿값 같은지 확인하는 코드 확인 - 회전값 설정 전 ‘타겟 토커스 움직임 확인’ 변수 false로 설정해서 타겟 변경 못하게 설정 - ‘타겟 이동’ 함수 확인 - ‘타겟 토커스 움직임 확인’ 변수가 True인지 확인하고 True라면 타겟 이동을 실행하지 못하게 한다. - ‘타겟 변경’ 함수 호출 전에 ‘타겟 토커스 움직임 확인’ 변수 True로 설정한다.
//CPP bMovingFocus = false; //타겟 포커스 이동중이라면 실행하지 않는다. CheckTrue(bMovingFocus);
무기 시스템 - 워프
워프 액션 입력 및 생성 및준비
- 크기를 바꿀때 오른쪽에 자물쇠 모양 : 세 축의 크기중 하나만 바꿔도 첫 비율에 맞게 자동으로 다른 크기들을 보정해주는 기능이다.
- 워프는 무기 장착과는 다른점 : 캐릭터가 액션 입력 방향으로 회전하는것을 허용해야한다.
- 액션에서 ‘데칼’을 처리한다.
- Attachment에서 처리해도 되지만, 위치를 구해서 액션을 할것이기 때문에 DoAction의 Tick에서 그것을 처리할 것이다.
- 파일 생성
- 콘텐츠 브라우저 - 우클릭 - 기타 - 워프 ‘데이터 에셋’ 생성
- 콘텐츠 브라우저 - 우클릭 - 블루프린트 - 블루프린트 CAttachment_Warp 생성
- 데칼용 메테리얼 가져오기 - 메테리얼 인스턴스 생성
- CDoAction - 우클릭 - 파생 C++ 클래스 생성 - CDoAction_Warp 생성
- BP_Attachment_Warp
- 컴포넌트 - 추가 클릭 - ‘데칼 컴포넌트’ 검색 - 디테일 - 지면에 비출것이니 y축 -90도 입력 - 데칼 메테리얼 : 데칼 메테리얼 인스턴스 할당
- ‘Set Visibility 검색 - ‘데칼 컴포넌트’ 끌어다가 연결 - ‘On Begin Equip’에 보이도록 연결하고, ‘OnUnequip’에는 이전의 Set Visibility 노드 순서 핀에 연결한다.
- BP_CPlayer
- WeaponComponent - 디테일 - 데이터 에셋 - 워프 데이터 에셋 할당
- DA_Warp
- Attachment Class 할당
- Equipment Data - ‘컨트롤 회전 사용’ 체크 해제 : 캐릭터의 회전이 마우스 뿐만 아니라 액션 입력 방향으로도 회전할 수 있게 한다.
- CWeaponComponent
- 헤더 파일 - ‘워프 모드 설정’ 함수 선언
//header void SetWarpMode();- CPP 파일 - 기존의 설정 함수를 복붙해서 워프로 바꾼다.
//CPP void UCWeaponComponent::SetWarpMode() { CheckFalse(IsIdleMode()); SetMode(EWeaponType::Warp); } - CPlayer
- CPP 파일 - 워프 액션 연결
//CPP PlayerInputComponent->BindAction("Warp", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetWarpMode); - CDoAction_Warp
- 컴포넌트를 가져오는것은 BeginPlay 함수에서 처리한다.
- 파생 클래스에서 부모의 함수를 사용하기 위해서 그 함수를 ‘가상화’ 시켜야한다.
- 헤더 파일 - 생성자 선언 - 데칼 컴포넌트 자료형으로 ‘데칼’ 변수 선언 - 부모에서 가져온 BeginPlay 함수를 오버라이드 한다. : 오너 캐릭터, DoActionData 배열, HitData 배열을 받는다.
//header public: UCDoAction_Warp(); public: void BeginPlay ( class ACharacter* InOwner, const TArray<FDoActionData>& InDoActionDatas, const TArray<FHitData>& InHitDatas ) override; private: class UDecalComponent* Decal;- CPP 파일 - BeginPlay 정의 - 부모 BeginPlay 호출 -
데칼
- 마법진같은거 그릴때 쓰는 이펙트
기능 설계 팁
- 하나의 클래스의 이름을 정한다. ⇒ 만들 기능을 하나로 합칠 이름으로 한다.
- 어떤 기능을 가지고 있는지 쓴다.
- 이벤트나 델리게이트가 있을 경우 화살표로 어느 것을 연결했는지 연결한다.