공부/Unreal

24.08.19

월러비 2024. 8. 20. 15:46

무기시스템 - 활

  • 날아가는 화살도 ‘배열’로 관리해야한다.
    • 배열에 화살 하나를 넣었다.
    • 발사했으면 화살 하나를 더 생성해서 빈 손에 쥐어야한다.
    • 화살이 어딘가에 부딪히면 없앤다.
    • 화살은 몇개가 생성될지 모르니 TArray를 사용한다.
    • 중간의 값이 삭제되면 뒤에있는 값을이 앞으로 땡겨와진다.
      • 가변형 배열을 반복문으로 삭제하는것은 삭제하고 땡기면 안된다.
      • 땡겨진 빈 공간 다음것을 가리키게 되기 때문이다.
      • 해결법 : 순서를 반대로 샌다.
  • 손에 붙어있는 화살만 찾아서 장착 해제때 제거한다.
    • 이미 날아간것은 삭제하면 안된다.

화살 생성 후 바로 날아가지 못하도록 설정

  • CArrow
    • 발사체 생성 직후 바로 날아가지 않게 UProjectileMovementComponent를 비활성화 시켜야한다.
    • CPP 파일 - ‘발사체’의 ‘활성화 설정’ 함수를 false로 설정하고 호출한다.
    //CPP
    void ACArrow::BeginPlay()
    {
    	Super::BeginPlay();
    
    	//시작하면 투사체를 비활성화한다.
    	Projectile->SetActive(false);
    	
    }
    

화살 장착 해제시 사라지도록 설정

  • 생성한 화살을 저장한 배열에서, ‘손에 붙어있는 화살’만을 찾아 삭제한다.
  • 배열의 번호로 해당 요소를 탐색할 것이니 Num() 함수 호출에서 -1을 해줘야한다.
  • CDoAction_Bow
    • 헤더 파일 - ‘장착 해제 활성’ 함수 재정의 - 생성한 화살을 저장할 ‘화살들’ 가변 배열 변수 선언
    //header
    void OnUnequip() override;
    
    private:
    	TArray<class ACArrow*> Arrows; //생성된 화살을 저장하는 배열
    
    • CPP 파일 - ‘화살 생성’ 함수 확인 - 화살을 메쉬에 부착한 이후 확인 - ‘화살들’의 ‘추가’에 ‘화살’을 넣고 호출하고, 생성한 화살을 배치한다. - ‘장착 해제 활성’ 함수 정의 - 부모 호출 - ‘화살들’의 ‘요소 갯수 반환’ 함수 호출의 -1 부터 하나씩 줄어들며 반복한다. : ‘화살들’의 현재 순번을 ‘화살’ 변수를 선언하고 저장한다. , ‘화살’이 비어있지 않다면 : ‘화살’의 ‘붙은 부모 엑터 가져오기’ 함수 호출이 비어있지 않다면 : ‘화살들’의 ‘제거’ 함수에 ‘화살’을 넣고 호출한다. , ‘화살’의 ‘파괴’ 함수를 호출한다.
    //CPP
    //붙이기 설정
    FAttachmentTransformRules rule = FAttachmentTransformRules(EAttachmentRule::KeepRelative, true); //붙는 규칙
    arrow->AttachToComponent(OwnerCharacter->GetMesh(), rule, "Hand_Bow_Right_Arrow"); 
    
    Arrows.Add(arrow); //생성된 화살을 배열에 저장
    
    //배치 설정 (엑터, 확정된 배치 위치)
    UGameplayStatics::FinishSpawningActor(arrow, transform);
    
    void UCDoAction_Bow::OnUnequip()
    {
    	Super::OnUnequip();
    
    	//활 시위의 좌표를 다시 원래의 위치로 되돌린다.
    	PoseableMesh->SetBoneLocationByName("bow_string_mid", OriginLocation, EBoneSpaces::ComponentSpace);
    
    	for (int32 i = Arrows.Num() - 1; i >= 0; i--)
    	{
    		ACArrow* arrow = Arrows[i];
    
    		//해당 순번에 화살이 있다면
    		if (!!arrow)
    		{
    			//GetAttachParentActor : 엑터가 어딘가에 붙어있다면 Null이 아니다.
    			//즉, 날아간 화살은 삭제가 안되고, 손에 붙어있는 화살만 삭제가 된다.
    			if (!!arrow->GetAttachParentActor())
    			{
    				Arrows.Remove(arrow); //리스트의 화살 삭제
    				arrow->Destroy(); //화살 객체 삭제
    			}
    		}
    	}
    
    }
    

활 시위를 손에 붙이기

  • 장착할때 붙이고, 쏘거나 장착해제할때 손에서 땐다.
    • DoAction에서 설정한다.
  • 활의 캡쳐된 메쉬는 Attachment에 있어서 매개변수로 받아 저장했다.
    • 장착되었는지의 여부확인은 Equipment에 있어서 Equipment를 매개변수로 받아 저장할 것이다.
  • 메쉬를 수정하기 전에는 반드시 PoseableMesh를 이용해서 ‘포즈를 캡처’해야한다.
    • 캡처하지 않으면 메쉬 수정이 불가능하다.
  • 지금은 월드 공간의 값을 사용한다. ⇒ 최초로 활을 붙일때의 컴포넌트 스페이스 좌표다.
    • 수정할때는 월드 공간 좌표이고, 사용할때는 컴포넌트 공간 좌표를 이용한다.
    • ‘소켓 위치 가져오기’ 함수는 월드 좌표를 반환한다.
  • 캐릭터 스켈레톤
    • 오른손에 활 시위를 붙일 소켓 생성
  • CDoAction_Bow
    • CPP 파일 - Tick 함수 확인 - ‘포즈에이블’의 ‘스켈레탈 컴포넌트의 포즈 캡처’ 함수에 ‘스켈레탈’을 넣고 호출한다. - ‘오너 캐릭터’의 ‘메쉬 가져오기’ 함수 호출의 ‘소켓 위치 가져오기’ 함수에 ‘소켓 이름’을 넣고 호출한 결과를 ‘손 위치’ 변수를 선언하고 저장한다. - ‘포즈 에이블 메쉬’의 ‘이름에 의한 본 위치 설정’ 함수에 ‘본 이름, 설정할 위치 : 오른손 소켓 위치, 본 공간 타입 : 소켓 좌표가 월드 좌표이니 월드 공간으로 설정’을 넣고 호출한다.
    //CPP
    void UCDoAction_Bow::Tick(float InDeltaTime)
    {
    	Super::Tick(InDeltaTime);
    
    	//스켈레탈 메쉬 포즈 캡쳐
    	//활의 포즈를 캡쳐한 다음 캡쳐한 포즈의 활 시위를 바꾸기 위해 사용한다.
    	PoseableMesh->CopyPoseFromSkeletalComponent(SkeletalMesh);
    
    	//활 시위를 붙일 소켓의 위치를 가져온다.
    	FVector handLocation = OwnerCharacter->GetMesh()->GetSocketLocation("Hand_Bow_Right"); 
    
    	//활 시위를 정해진 소켓의 위치에 붙인다.
    	//EBoneSpaces::WorldSpace : 소켓의 위치가 월드공간에서의 좌표라는 뜻이다.
    	//애니메이션을 수정할 때만 WorldSpace로 설정한다.
    	PoseableMesh->SetBoneLocationByName("bow_string_mid", handLocation, EBoneSpaces::WorldSpace);
    
    }
    

장착 해제시 활 시위 되돌리기 설정

  • Bone : 캐릭터 디자이너가 설정한 원래의 위치
  • Socket : 개발자가 캐릭터의 본에 임의로 생성한 공간이다.
  • 메쉬를 수정할때만 ‘월드 공간’으로 설정하고 원본으로 되돌릴때는 ‘컴포넌트 공간’으로 설정하는것이 좋다.
  • CDoAction_Bow
    • 헤더 파일 - 활 시위 첫 좌표를 저장할 ‘원본 위치’ 변수 선언
    //header
    private:
    	FVector OriginLocation; //활 시위의 장착 전 좌표를 저장하는 변수
    
    • CPP 파일 - BeginPlay 확인 - ‘포즈 에이블 메쉬’의 ‘이름에 의한 본 위치 가져오기’ 함수에 ‘본 이름, 본 공간 타입’을 넣고 호출한 결과를 ‘원본 위치’ 변수에 저장한다. - ‘장착 해제 활성’ 함수 확인 - ‘포즈 에이블 메쉬’의 ‘이름에 의한 본 위치 설정’ 함수에 ‘본 이름, 설정할 위치 : 원본 위치, 본 공간 타입 : 원본 좌표가 컴포넌트 좌표이니 컴포넌트 공간으로 설정’을 넣고 호출한다.
    //CPP
    //EBoneSpaces::ComponentSpace : 본의 첫 상대적 위치를 반환한다.
    OriginLocation = PoseableMesh->GetBoneLocationByName("bow_string_mid", EBoneSpaces::ComponentSpace);
    
    //활 시위의 좌표를 다시 원래의 위치로 되돌린다.
    PoseableMesh->SetBoneLocationByName("bow_string_mid", OriginLocation, EBoneSpaces::ComponentSpace);
    

화살이 장착 완료될 때만 활 시위가 붙도록 설정하기

  • 장착이 되었는지는 Equipment에 있기 때문에 Equipment를 매개변수로 받아 설정한다.
    • End_Equip이 발생했을때 ‘장착됨’ 변수를 설정하고 이것을 DoAction으로 가져와서 사용하는것이다.
  • DoAction에 EndEquip을 둬서 true, false로 놓아도 되지만 다른 무기들도 장착 확인을 할 수 있도록 이렇게 하는 것이다.
  • Equipment를 소유해야만 해당 클래스의 상황에 맞는 값을 얻을 수 있다.
    • 하지만, 같은 레벨의 클래스는 서로 소유하지 못하도록 해야한다.
    • 해결 방안 : ‘포인터 자료형’ 함수를 선언하고 해당 값의 ‘주소’를 반환한다.
      • 문제점 ; 주소를 넘기면 해당 주소의 값이 외부에서 변경될 위험이 있다.
      • 해결 방안 : 외부에서 사용하도록 설정한 함수를 ‘상수’로 설정한다.
  • CEquipment
    • DoAction에서 사용해야하는 변수가 있지만 Equipment를 소유하지 못하도록 ‘매개 변수’로 넘겨서 사용할 수 있게한다.
    • 헤더 파일 - ‘장착 됨’ 변수 선언 - 외부에서 사용할 수 있도록 ‘포인터 자료형’으로 ‘장착됨 가져오기’ 함수를 선언하고 ‘장착됨’ 변수의 ‘주소’를 반환하도록 정의한다. ⇒ 값이 변경되지 않도록 ‘상수’로 설정한다.
    //header
    public:
    	//소유하지 못하도록 '포인터'를 넘겨서 '주소'를 주지만, 수정은 하지 못하도록 '상수'로 설정한다.
    	FORCEINLINE const bool* GetEquipped() { return &bEquipped; }
    
    private:
    	bool bEquipped; //장착 되었는지 확인
    
    • CPP 파일 - ‘장착 종료’ 함수 확인 - ‘장착 됨’ 변수를 true로 설정한다. - ‘장착 해제’ 함수 확인 - ‘장착 됨’ 변수를 false로 설정한다.
    //CPP
    void UCEquipment::End_Equip_Implementation()
    {
    	bEquipped = true; //장착 되었다.
    
    	if (Data.bCanMove == false)
    		Movement->Move();
    
    	State->SetIdleMode();
    }
    
    void UCEquipment::Unequip_Implementation()
    {
    	bEquipped = false; //장착 해제 되었다.
    
    	if (Data.bUseControlRotation)
    		Movement->DisableControlRotation();
    
    	if (OnEquipmentUnequip.IsBound())
    		OnEquipmentUnequip.Broadcast();
    }
    
  • DoAction , DoAction_Bow , DoAction_Warp , DoAction_Combo
    • BeginPlay 함수에서 Equipment를 매개변수로 받도록 헤더파일과 CPP 파일에 설정한다.
    • DoAction_Bow는 실제로 Equipment의 값을 사용해야 하니 헤더파일을 include 해야한다.
  • CWeaponAsset
    • CPP 파일 - DoActionClass가 비었는지 확인하는 코드 - DoAction의 BeginPlay를 호출하는 부분에 Equipment를 매개변수에 추가해야한다.
  • CDoAction_Bow
    • 헤더 파일 - Equipment에서 가져올 ‘장착됨’ 의 값을 저장할 포인터 자료형 ‘장착됨’ 변수를 ‘상수’로 설정하고 선언한다.
    //header
    private:
    	const bool* bEquipped; //Equipment에 있는 bEquipped다.
    
    • CPP 파일 - 매개변수로 들어온 ‘Equipment’의 ‘장착 됨 가져오기’ 함수를 호출한 결과를 ‘장착 됨’ 변수에 저장한다. - Tick 함수 확인 - ‘장착 됨’ 변수의 ‘포인터 값’을 넣어 False인지 확인한다. ⇒ false라면 무기가 장착되지 않은 상태이니 활 시위의 위치 수정도 하지 않는다.
    //CPP
    bEquipped = InEquipment->GetEquipped(); //Equipment를 소유하지않고 값만 가져온다.
    
    CheckFalse(*bEquipped); //장착이 되었는지 확인한다.
    

조준 밑준비 - 나머지 준비는 다음에 이어서 시작

  • 애니메이션 가져오기
  • ‘기준 애니메이션’에 다른 동작들 한 프레임씩 섞어서 새로운 동작을 만든다.
    • ‘기준 애니메이션’의 프레임과 섞는 동작의 프레임은 같게 잘랐다. (1 프레임으로)
  • 애니메이션 설정
    • 밑의 Additive 이론에 적어놨다.
    • 기준 애니메이션을 제외하고 섞을 애니메이션 전체 선택 - 우클릭 - 에셋 액션 - ‘프로퍼티 매트릭스를 통한 대량 편집’ 클릭 - 디스플레이 창 - AdditiveSettings - ‘애디티브 애님 타입’ : Mesh Space - ‘베이스 포즈 타입’ : Selected animation frame - ‘레퍼런스 프레임’ : 0 - 베이스 포즈 애니메이션(기준 애니메이션) ; 정조준 애니메이션
  • AO_Bow
    • 상하 좌우 모두 움직이는 조준일때는 ‘에임 오프셋’ , 상하만 움직이는 조준일때는 ‘에임 오프셋 1D’로 생성한다.
      • 블렌드 스페이스와 거의 비슷하지만 한가지 차이점이 존재한다.
    • 에디티브 세팅의 ‘베이스 애니메이션’ 지정을 안하면 애니메이션을 섞을 수 없다.
      • 서로 다른 애니메이션이라고 판단되기 때문이다.
    • 콘텐츠 브라우저 - 우클릭 - 애니메이션 - ‘에임 오브셋 1D’ 클릭
    • 에셋 디테일 - 가로축 이름 : Pitch(y회전) - 최소 축 값 ; -90 - 최대 축 값 : 90 - 그리드 분할 : 4
    • 애니메이션 축값에 따라 배치
  • ABP_Player
    • 간단한 포즈 : ‘스테이트 머신 / 복잡한 포즈 : 레이어 생성 후 정의
      • 포즈 : 스테이트 머신에서 나온 결과를 저장하고 사용한다.
      • 레이어 : 애니메이션 자체를 사용한다.
    • 이벤트 그래프를 생성하여 포즈를 만들어도 되지만 ‘애니메이션 레이어’를 사용하는것이 편하다.
      • 애니메이션 레이어 사용 이유 ; ‘최종 애니메이션 노드’를 하나만 놓고 사용하기 위해서다.
    • 애니메이션 레이어 생성 - 이름 지정 - ‘BS_Bow’ 끌어 놓기 - ‘Direction’과 ‘Speed’ 검색해서 연결
    • 애님 그래스 - ‘생성한 레이어 이름’ 검색 : 레이어 이름의 ‘링크된 애님 레이어’ 라고 나온다. - 블렌드 포즈의 Bow 핀에 연결

가변형 배열 요소 삭제시 생기는 문제

  • 가변형 배열 : 최대 요소의 수가 정해지지 않은 배열
    • C : stlVector
    • C++ : TArray<>
    • C# : List
  • 배열의 중간값이 삭제된다면 : 뒤에 있는 값들이 빈 공간을 채우기 위해 당겨진다.
    • 문제점 : 삭제된 공간 다음 값을 읽을때 이미 다음 값이 빈공간을 채우기 위해 한칸씩 앞으로 값이 이동했으므로 실제로 읽는 값은 삭제된 요소 다음 다음 값이 된다.
      • 반복문 내에서 발생하는 문제다.
    • 해결 방법 ; 역순으로 탐색한다.
      • 0 ~ 5의 요소중 3을 없앤다고 했을때, 3을 없애도 다음 탐색값은 2이고, 3의 공간을 이미 탐색한 4의 요소가 채우게 되니 오류가 없어진다.
  • 배열에 저장한 화살의 제거, 무기 시스템 - 피스트 에 사용한 문법이다.

애니메이션 Additive 이해

  • 애니메이션을 2개 이상 섞는것을 ‘Additive’라고 부른다.
  • 애니메이션은 컴포넌트에서 일어난다.
    • 액터는 컴포넌트를 붙여서 애니메이션을 보여주는것이다.
  • UObject
    • UActorComponent
      • USceneComponent
        • UPreimitiveComponent
          • UMeshComponent
            • UStaticMeshComponent
            • USkinnedMeshComponent
              • USkeletalMesh
              • UPoseableMeshComponent
  • 스켈레탈에 애니메이션이 들어가는 것이다.
    • 포즈 에이블은 애니메이션이 들어간 스켈레탈 메쉬를 ‘캡쳐’해서 복사하여 사용하는 것이다.
    • 스킨드부터 애니메이션이 들어갈 수 있다.
  • SkinnedMesh의 3가지 공간
    • 애니메이션을 수정할 수 있느냐를 나눈다.
      • 즉, 포즈가 들어갈 수 있느냐의 여부다.
    • 애니메이션 블루프린트에서는 ‘스킨드 메쉬’에 있는 애니메이션을 플레이 시키는 것이다.
    • Local Space : 애니메이션 수정 불가능, 플레이만 가능
    • Mesh Space : 애니메이션 수정 가능, 단, 언리얼 에디터 옵션으로만 가능하다.
      • 본 별로 레이어로 블렌딩에서 ‘메쉬 스페이스 회전’ 체크에서 사용된다.
      • 체크를 해제하고 공격하면 허리 회전이 안들어가게 되고, 팔만 깔짝이게 된다.
        • 본을 회전시킨다. 즉, 본을 수정한다. 인데, 체크를 해제하면 수정이 불가능하게 되는 것이다.
        • 움직이면서 때릴때 사용하는것은 UpperBody다.
    • Component : 프로그래밍에 의해서만 수정이 가능하다.
      • 활 시위를 프로그래밍으로 애니메이션 수정을 한다.

Additive 관련 이론

  • 애니메이션 프로퍼티 대량 편집 창
    • 디스플레이 - AdditiveSettings
      • 애디티브 애님 타입 : 에디티브 애니메이션 타입을 설정한다.
        • Mesh Space : 언리얼 에디터에서만 애니메이션 본 수정이 가능하도록 설정한다.
      • 베이스 포즈 타입 : 기준이 되는 애니메이션을 섞을 타입을 설정하는 옵션이다.
        • Selected animation frame : 애니메이션의 프레임으로 섞는 옵션이다.
          • 기준 애니메이션과 특정 애니메이션의 ‘레퍼런스’에 지정된 프레임의 동작과 섞을 것이기 때문에 ‘프레임’ 타입으로 설정하는 것이다.
          • 즉, 레퍼런스 : 원본 동작, 프레임 : 원본 동작 중 특정 프레임의 동작
      • 레퍼런스 프레임 : 기준 애니메이션과 섞을 원본 애니메이션의 특정 프레임 순번
        • 기본적으로 ‘0’을 놓는다.
      • 베이스 포즈 애니메이션 : 섞을 애니메이션들의 ‘기준’이 되는 애니메이션을 설정하는 옵션이다.
  • 설정이 끝나면 애니메이션 항목의 ‘에임 오프셋’ 에셋을 생성해서 넣는다.

발사체 조준 이해

  • 조준에 필요한 것 : 동작
  • 원래 조준 동작은 사방 팔방 움직이는 하나의 애니메이션이다.
    • 그것을 최종 위치에 도달한 ‘한 프레임’ 만을 잘라서 다른 자른 동작과 연결하여 블렌딩하는 것이다.
  • 조준할때 동작 3개를 섞어야한다.
    • 아래, 정방향, 위
    • 원래는 상하좌우, 대각선 모든 방향을 사용해야한다.
      • 아니면 조준한 상태의 한 프레임씩 사용한다.
  • 최종적으로, ‘기준 애니메이션’에 다른 동작들 한 프레임씩 섞어서 새로운 동작이 생기는 것이다.

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

24.08.21  (3) 2024.08.23
24.08.20  (1) 2024.08.23
24.08.16  (0) 2024.08.20
24.08.13  (0) 2024.08.16
24.08.12  (0) 2024.08.16