공부/Unreal

24.08.13

월러비 2024. 8. 16. 19:02

무기 시스템 - 활 (붙이기만 하고 잠시 대기함)

  • 활은 Attachment 블루프린트가 아니라 다른 방법으로 붙인다.
    • 여러가지 기능들이 들어가야하기 때문에 C에서 스폰부터 기능까지 작업한다.

활 밑준비 - 스폰시키고 홀스터에 부착시키기

  • DA_Bow
    • Attachment 할당
  • BP_Player
    • WeaponComponent에 DA_Bow 할당
  • CAttachment_Bow
    • 생성
    • 좌표값에서 Min값과 Max 값을 사용할떄는 FVector2D로 선언한다.
    • PoseableMesh->SetSkeletalMesh(SkeletalMesh->SkeletalMesh) 설명 : 스켈레탈 메쉬 컴포넌트에 설정한 디테일 - 스켈레탈 메쉬 를 의미하는 것이다.
    • 헤더 파일 - ‘피치 범위 뷰’ 변수를 선언하고 ‘(-40, 30)’으로 초기화한다. - 활 스켈레탈을 가져와서 저장할 ‘스켈레탈 메쉬’ 변수를 선언한다. - 포즈에이블 컴포넌트 클래스의 ‘포즈에이블 메쉬’ 변수를 선언한다. - 생성자 선언 - BeginPlay 재정의 선언 - Tick 재정의 선언
    //header
    private:
    	UPROPERTY(EditDefaultsOnly, Category = "View")
    		FVector2D ViewPitchRange = FVector2D(-40, 30); //
    
    private:
    	UPROPERTY(VisibleAnywhere)
    		class USkeletalMeshComponent* SkeletalMesh;
    
    	UPROPERTY(VisibleAnywhere)
    		class UPoseableMeshComponent* PoseableMesh;
    
    public:
    	ACAttachment_Bow(); //생성자
    
    • CPP 파일 - 생성자에 ‘스켈레탈 메쉬’와 ‘포즈에이블 메쉬’ 컴포넌트 생성 - 스켈레탈 메쉬를 가져와서 저장할 ‘메쉬’ 변수 선언 - 스켈레탈 메쉬 에셋을 가져와 ‘메쉬’에 저장한다. - ‘스켈레탈 메쉬’의 ‘스켈레탈 메쉬 설정’ 함수를 ‘메쉬’를 넣어 호출한다. - BeginPlay 정의 - 부모 함수 호출 - 정의한 ‘AttachTo’ 함수에 소켓 이름을 넣고 호출한다. - ‘스켈레탈 메쉬’의 ‘보여지기 설정’을 false로 넣어 호출한다. - ‘포즈 에이블 메쉬’의 ‘스켈레탈 메쉬 설정’ 함수에 ‘스켈레탈 메쉬의 스켈레탈 메쉬 디테일 옵션’을 넣어 호출한다. - ‘포즈 에이블 메쉬’의 ‘스켈레탈 컴포넌트의 포즈 복사’ 함수에 ‘스켈레탈 메쉬’를 넣어 호출한다.
    //CPP
    ACAttachment_Bow::ACAttachment_Bow()
    {
    	PrimaryActorTick.bCanEverTick = true;
    
    	CHelpers::CreateComponent<USkeletalMeshComponent>(this, &SkeletalMesh, "SkeletalMesh", Root);
    	CHelpers::CreateComponent<UPoseableMeshComponent>(this, &PoseableMesh, "PoseableMesh", Root);
    
    	USkeletalMesh* mesh;
    	CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Characters/Weapons/ElvenBow/SK_ElvenBow.SK_ElvenBow'");
    	SkeletalMesh->SetSkeletalMesh(mesh);
    	
    }
    
    void ACAttachment_Bow::BeginPlay()
    {
    	Super::BeginPlay();
    
    	AttachTo("Holster_Bow");
    
    	SkeletalMesh->SetVisibility(false);
    
    	PoseableMesh->SetSkeletalMesh(SkeletalMesh->SkeletalMesh); //스켈레탈 메쉬 변수에 저장한 mesh를 의미하는거다.
    	PoseableMesh->CopyPoseFromSkeletalComponent(SkeletalMesh);
    }
    

잔상 효과 (GhostTrail)

  • C로 클래스를 만들어 넣고싶은 곳에 붙이고 사용이 끝나면 필요없을때 삭제하는 방식으로 제작한다.
  • 일반적인 잔상을 ‘Trail’이라고 부르고, 캐릭터에 대한 잔상을 ‘GhostTrail’ 이라고 부른다.
  • 캐릭터에도 잔상 효과를 주고 무기에도 잔상효과를 주면 더 그럴싸하다.
    • 아니면, 무기를 휘두를때만 잔상 효과가 나와도 된다.
  • BindLambda(=)
    • [=] : 외부값을 대입해서 쓰는 캡쳐절이다.
    • 함수를 간단하게 쓰기 위해 사용한다.

잔상 효과 밑준비

  • 잔상 : 반사와 굴절이 생겨야 나온다.
    • Fresnel 방정식 : 반사와 굴절이 합쳐진 방정식이다. ⇒ 잔상 효과를 만들때 사용한다.
  • 유니티는 특별한 쉐이더를 선택하지 않는이상 색상 값의 범위는 ‘0 ~ 1’이지만, 언리얼의 색상 범위는 1을 넘어갈 수 있다.
    • 색상값이 1을 넘어가면 ‘번지듯이 밝아지는 효과’가 나타난다.
    • 즉, 번지듯이 밝아지는 효과를 위해 ‘이미시브 컬러’에 1 이상의 값을 넣고, 기본 색상을 위해 ‘베이스 컬러’에 0 ~ 1까지의 값을 넣는다.
  • Exponnet : 밀도, 값이 커질수록 밀도가 커진다.
    • 즉, 번지듯이 퍼지는 범위가 늘어난다.
  • M_GhostTrail
    • 메테리얼 생성 - 인스턴스 메테리얼 생성
    • 결과 : 외곽선만 색이 입혀지고 내부는 굴절이 일어나서 빛이 투과되게 된다.
    • 베이스 컬러 : 색
    • 이미시브 컬러 : 외곽선 바깥의 밝아지는 색
    • 메테리얼 그래프 - 베이스 컬러가 될 ParameterVector 검색 후 이름 변경(Color) - 굴절과 반사 수식 계산에 필요한 ‘Fresnel’ 검색 - 넓이를 조절하기 위해 ‘ScalarParameter’ 검색 후 이름 변경(Exponent) - 잔상의 넓이를 조절하기 위해 Fresnel 결과와 Exponent 값을 ‘곱한다’ - 넓이가 결정된 잔상에 색을 입해기 위해 곱한 값을 ‘베이스 컬러’와 곱한다. - 잔상을 임의로 보정하기 위해 결과에 핀을 끌어 ‘Multiplyer’를 검색하고 B값에 20을 준다. - 나온 결과를 ‘베이스 컬러’ 핀에 연결한다. - Fresnel 노드와 Exponent 값을 곱한 노드의 핀을 ‘오퍼시티’ 핀에 연결한다. - 베이스 컬러에 연결된 결과 노드를 또 ‘이미시브 컬러’ 핀에 연결한다.
  • M_GhostTrail_Inst
    • Exponent와 Color의 체크박스에 체크를 안하면 색이 나타나지 않는다.
    • 컬러 변경

잔상효과 출력

  • 게임 플레이가 종료될때 생성한 타이머가 사라지지 않았으면 게임이 터진다.
  • CGhostTrail
    • 생성
    • 헤더 파일 - ‘시작 딜레이’ 변수 선언 - ‘잔상 캡쳐 간격’ 변수 선언 - ‘잔상 색상’ 변수 선언하고 흰색으로 초기화한다. - ‘밀도 넓이’ 변수 선언 - ‘스케일’ 변수 선언 - ‘스케일 보정’ 변수 선언 - 포즈에이블 메쉬 컴포넌트 클래스의 ‘메쉬’ 변수 선언 - ‘종료 사유’를 받는 ‘플레이 종료’ 함수를 재정의한다. - ‘오너 캐릭터’ 와 ‘메테리얼’ 변수 선언 - 타이머 핸들링 자료형의 ‘타이머 핸들’ 변수를 선언한다.
    //header
    private:
    	UPROPERTY(EditDefaultsOnly, Category = "Capture")
    		float StartDelay = 0; //잔상이 몇초 뒤에 시작되나
    
    	UPROPERTY(EditDefaultsOnly, Category = "Capture")
    		float Interval = 0.25; //잔상 캡쳐 간격 => 짧으면 짧을수록 자연스럽다.
    
    	UPROPERTY(EditDefaultsOnly, Category = "Capture")
    		FLinearColor Color = FLinearColor(1, 1, 1, 1); //잔상색깔
    
    	UPROPERTY(EditDefaultsOnly, Category = "Capture")
    		float Exponent = 1; //밀도 넓이 조절 값
    
    	UPROPERTY(EditDefaultsOnly, Category = "Capture")
    		FVector Scale = FVector::OneVector; //잔상 크기를 캐릭터 대비 몇 비율로 할거냐
    
    	UPROPERTY(EditDefaultsOnly, Category = "Capture")
    		FVector ScaleAmount = FVector::ZeroVector; //스케일에 더 보정할 값
    
    private:
    	UPROPERTY(VisibleAnywhere)
    		class UPoseableMeshComponent* Mesh;
    
    public:	
    	ACGhostTrail();
    
    protected:
    	// Called when the game starts or when spawned
    	virtual void BeginPlay() override;
    	virtual void EndPlay(const EEndPlayReason::Type EEndPlayReason) override; //게임이 종료되거나, 컴포넌트가 삭제될때 호출되는 함수다.
    
    private:
    	class ACharacter* OwnerCharacter;
    	class UMaterialInstanceDynamic* Material;
    
    	FTimerHandle TimerHandle;
    
    • 타이머 핸들 : 타이머의 식별자다.
    • 타이머 델리게이트 : 타이머가 동작할때 실행할 이벤트 내용
    • CPP 파일 - 캡쳐 포즈를 사용할 컴포넌트인 포즈에이블 컴포넌트 쟈료형의 ‘메쉬’ 컴포넌트 생성 - BeginPlay 정의 - ‘오너 캐릭터’를 형변환해서 저장한다. - 메테리얼 인스턴스를 동적 생성하여 저장하기 위해 메테리얼 인스턴스 컨스턴스 클래스로 ‘임시 메테리얼’ 변수 선언 - 메테리얼 인스턴스 컨스턴스 클래스로 ‘메테리얼 동적생성’하여 ‘메테리얼’ 변수에 저장한다. - ‘메테리얼’ 변수에 ‘임시 메테리얼’ 변수에 저장된 메테리얼을 생성하여 저장한다. - ‘메테리얼’의 ‘벡터 파라미터 값 설정’ 함수에 ‘베이스 컬러 이름, 컬러 변수’를 넣어 색상을 설정한다. - ‘메테리얼’의 ‘스케일 파라미터 값 설정’ 함수에 ‘스칼라 파라미터 이름, 밀도 넓이 변수’ 를 넣어 얼마나 번지는지 설정한다. - ‘메쉬’의 ‘보이기 설정’ 함수를 fasle로 넣어 호출한다. - ‘메쉬’의 ‘스켈레탈 메쉬 설정’ 함수에 ‘오너캐릭터의 메쉬 가져오기 함수를 호출하여 그 안의 스켈레탈 메쉬’를 넣어 호출한다. - ‘메쉬’의 ‘스켈레탈 컴포넌트의 포즈 복사’ 함수에 ‘오너캐릭터의 메쉬 가져오기 함수 호출’을 넣어 호출한다. - ‘메쉬’의 ‘3D 스케일 설정’ 함수에 ‘크기’ 변수를 넣어 호출한다. - 오너캐릭터 스켈레탈 메쉬의 메테리얼 갯수만큼 반복한다. : ‘메쉬’의 ‘메테리얼 설정’ 함수에 ‘현재 순번, 메테리얼’을 넣고 호출한다. - 타이머 델리게이트 구조체의 ‘타이머 델리게이트’ 변수 선언 - ‘타이머 델리게이트’의 ‘람다 연결’ 함수에 ‘외부 값 캡쳐해서 대입’하여 호출한다. : ‘메쉬’의 ‘보여지기 확인’ 함수가 false라면 : ‘메쉬’의 ‘보여지기 토글’ 함수를 호출한다. , 현재 포즈를 캡쳐하기 위해 ‘메쉬’의 ‘스켈레탈 컴포넌트의 포즈 복사’ 함수에 ‘오너캐릭터의 메쉬 가져오기 함수 호출’을 넣어 호출한다. - ‘월드 가져오기’ 함수 호출의 ‘타이머 매니터 가져오기’ 함수의 ‘타이머 설정’ 함수에 ‘타이머 핸들, 타이머 델리게이트, 잔상 캡쳐 간격, 반복 여부, 시작 딜레이 값’을 넣어 호출한다. - EndPlay 정의 - 부모 호출 - ‘월드 가져오기’ 함수 호출의 ‘타이머 메니저 가져오기’ 함수 호출의 ‘타이머 초기화’에 ‘타이머 핸들’을 식별자로 넣어 호출한다.
    //CPP
    ACGhostTrail::ACGhostTrail()
    {
    	CHelpers::CreateComponent<UPoseableMeshComponent>(this, &Mesh, "Mesh");
    
    }
    
    void ACGhostTrail::BeginPlay()
    {
    	Super::BeginPlay();
    
    	OwnerCharacter = Cast<ACharacter>(GetOwner());
    
    	UMaterialInstanceConstant* material;
    	CHelpers::GetAssetDynamic<UMaterialInstanceConstant>(&material, "MaterialInstanceConstant'/Game/Materials/M_GhostTrail_Inst.M_GhostTrail_Inst'");
    	
    	Material = UMaterialInstanceDynamic::Create(material, this);
    	Material->SetVectorParameterValue("Color", Color);
    	Material->SetScalarParameterValue("Exponent", Exponent);
    
    	Mesh->SetVisibility(false);
    	Mesh->SetSkeletalMesh(OwnerCharacter->GetMesh()->SkeletalMesh);
    	Mesh->CopyPoseFromSkeletalComponent(OwnerCharacter->GetMesh());
    	Mesh->SetRelativeScale3D(Scale);
    
    	//OwnerCharacter->GetMesh()->SkeletalMesh->GetMaterials().Num();
    
    	for (int32 i = 0; i < OwnerCharacter->GetMesh()->SkeletalMesh->GetMaterials().Num(); i++)
    	{
    		Mesh->SetMaterial(i, Material);
    	}
    
    	FTimerDelegate timerDelegate;
    	timerDelegate.BindLambda([=]()
    		{
    			if (Mesh->IsVisible() == false)
    			{
    				Mesh->ToggleVisibility();
    			}
    			
    			Mesh->CopyPoseFromSkeletalComponent(OwnerCharacter->GetMesh()); //포즈 저장
    		});
    
    	GetWorld()->GetTimerManager().SetTimer(TimerHandle, timerDelegate, Interval, true, StartDelay);
    
    }
    
    void ACGhostTrail::EndPlay(const EEndPlayReason::Type EEndPlayReason)
    {
    	Super::EndPlay(EEndPlayReason);
    
    	GetWorld()->GetTimerManager().ClearTimer(TimerHandle);
    
    }
    
  • CWeaponStructures
    • 액션때 잔상효과를 출력할 것이니 DoActionData에 GhostTrail 파생 블루프린트를 넣을 변수를 넣는다.
    • 헤더 파일 - GhostTrail로 클래스 제한을 걸어 ‘고스트 트레일 클래스’ 변수를 선언한다. - ‘잔상 파괴’ 함수 선언
    //header
    UPROPERTY(EditAnywhere)
    		TSubclassOf<class ACGhostTrail> GhostTrailClass;
    
    • 캡슐의 절반만큼 빼는 이유 : 캡쳐 메쉬가 그려질때 발을 중심으로 그려져야 하기 때문이다.
    • CPP 파일 - DoAction 함수 확인 - ‘고스트 트레일 클래스’ 변수가 비어있지 않다면 : ‘오너 캐릭터’의 ‘엑터 위치 가져오기’ 함수를 호출해 ‘위치’ 변수를 선언하고 저장한다. , ‘위치’의 Z 값에 ‘오너 캐릭터’의 ‘캡슐 컴포넌트 가져오기’ 함수 호출의 ‘캡슐 절반 높이 크기 가져오기’ 함수를 호출한 결과값을 뺀다. , 엑터 스폰 파라미터 구조체의 ‘파라미터들’ 변수 선언 , ‘파라미터들’의 ‘오너’에 매개변수로 들어온 ‘오너 캐릭터’를 저장한다. - ‘파라미터들’의 ‘스폰 충돌 핸들링 재정의’에 ‘스폰 충돌 핸들링 방법 : 항상 스폰’으로 저장한다. - 트랜스폼 구조체의 ‘트랜스폼’ 변수 선언 - ‘스랜스폼’의 ‘이동 설정’ 함수에 ‘위치’를 넣어 호출한다. - ‘오너 캐릭터’의 ‘월드 가져오기’ 함수 호출의 ‘엑터 스폰’을 ‘GhostTrail’ 클래스로 지정하고 ‘고스트 트레일 클래스, 트랜스폼, 파라미터들’ 변수를 넣고 호출한 결과를 ‘고스트 트레일’ 변수에 저장한다. - ‘잔상 파괴’ 함수 정의 - ‘고스트 트레일’일 null이 아니라면 : ‘고스트 트레일’의 ‘파괴’ 함수를 호출한다.
    //CPP
    if (!!GhostTrailClass)
    {
    	FVector location = InOwner->GetActorLocation();
    	location.Z -= InOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
    
    	FActorSpawnParameters params;
    	params.Owner = InOwner;
    	params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
    
    	FTransform transform;
    	transform.SetTranslation(location);
    
    	GhostTrail = InOwner->GetWorld()->SpawnActor<ACGhostTrail>(GhostTrailClass, transform, params);
    }
    	
    void FDoActionData::Destroy_GhostTrail()
    {
    	if (!!GhostTrail)
    	{
    		GhostTrail->Destroy();
    	}
    }
    
  • CDoAction_Combo
    • CPP 파일 - Begin_DoAction과 End_DoAction에 추가 - ‘DoActionDatas’의 ‘콤보 수’의 ‘고스트 트레일 파괴’ 함수를 호출한다.
    //CPP
    DoActionDatas[Index].Destroy_GhostTrail();
    
  • BP_GhostTrail_Hammer / Sword
    • GhostTrail 파생 블루프린트 파일 생성
    • 시작 딜레이, 캡쳐 간격과 잔상 색상을 설정한다.
  • DA_Hammer
    • GhostTrail Class에 해당 무기의 잔상클래스를 할당한다.

잔상 위치 보정

  • 캐릭터의 메쉬 컴포넌트를 가져올때 메쉬의 위치화 회전을 가져오기 때문에 메쉬에 지정된 회전 -90도 같이 포함되어있다.
    • 해결 방법 : 캡쳐된 메쉬를 소환할때 회전을 준다.
  • 캡쳐 메쉬의 소환위치를 발에 맞춰놓았는데 해당 메쉬의 위치를 캐릭터의 메쉬 위치로 놓으니 메쉬의 중심점인 배 위치에 캡쳐메쉬의 발이 맞춰져서 공중에 띄워지는 것이다.
    • 소환할때 위치를 아래로 내린다.
  • ‘엑터 위치 설정’ 함수에 ‘오너캐릭터의 엑터 위치 가져오기 함수 호출’을 넣어 호출한다. , ‘엑터 회전 설정’ 함수에 ‘오너캐릭터의 엑터 회전 가져오기 함수 호출’을 넣어 호출한다.
//CPP
SetActorLocation(OwnerCharacter->GetActorLocation() + FVector(0, 0, -90));
SetActorRotation(OwnerCharacter->GetActorRotation() + FRotator(0, -90, 0));

언리얼 문서 빠르게 가는방법

  • 에디터 - 상단에 도움말 버튼 - C++ API 레퍼런스 클릭

UPoseableMeshComponent

  • UObject
    • UActorCOmponent : 컴포넌트에 붙을 수 있는 ‘기능’만을 제공하는 컴포넌트다.
      • USceneComponent : 어딘가 또는 어느 컴포넌트에 붙일 수 있는 기능을 가진 컴포넌트다.
        • 상대적 간격을 가지고 있다. ⇒ 컴포넌트 창에서 보여지는 부모와 자식의 구분, 다른 상속을 받는 컴포넌트와 구분 등을 확인할 수 있다.
        • UPriemitiveComponent : 그릴 수 있는 최소한의 기능을 가지고 있다.
          • UShapeCompoennt : 충돌체의 모양을 정의하는 기능을 가지고 있다.
            • 캡슐, 구, 박스 등의 충돌체 컴포넌트
          • UMeshComponent :그물망 구조(버텍스, 트라이앵글을 연결할 형태)를 그릴 수 있는 최소한의 기능을 가지고 있다.
            • UStaticMeshComponent : 메쉬 자체가 고정적이어서 움직이지 않고 변형되지 않는다.
            • USkinnedMeshComponent : 변형이 되는 메쉬를 가지고 있다.
              • 변형이 된것들을 자연스럽게 메꾸는 기능을 가지고 있다.
                • ‘스키닝’이라고 부른다.
              • 두 큐브를 붙여놓고 팔을 굽히는것처럼 구부릴때 큐브 사이가 벌어지고 좁혀지는것을 보정해주는 기능이 있다.
              • USkeletalMeshComponent
              • UDistructableMeshComponent : 파괴되고 찢어지는 메쉬의 기능을 가지고 있다.
              • UPoseableMeshComponent : 애니메이션을 캡쳐해서 변형하여 쓸 수 있게하고, 폰을 임의로 변형할 수 있게하는 컴포넌트다.
  • 잔상효과를 내기위해 스켈레탈 메쉬를 복사하면 연산량이 크다.
    • 해결방법 : 애니메이션의 한 ‘프레임’을 캡쳐해서 그 크기를 늘려 사용한다.
      • 그후, 캡쳐한 부분의 메테리얼을 바꿔 잔상처럼 보이게 설정하는 것이다.

메쉬를 이루는 요소 설명

  • 버텍스 : 삼각형(트라이앵글)을 그릴 수있는 최소한의 ‘정점’을 의미한다.
  • 트라이앵글 ; 버텍스를 3개 연결한 삼각형을 의미한다.
  • 버택스를 연결해 트라이앵글을 만들때 각 정점을 ‘번호’로 연결한다.
    • 그 번호를 ‘인덱스 버퍼(버택스 버퍼)’라고 한다.
  • 어떤식으로 트라이앵글을 연결해 갈지의 정보를 ‘프리미티브 토폴로지’ 라고 한다.
    • 즉, 이러한 그릴 수 있는 최소한의 정보를 ‘UPriemitiveComponent’가 가지고 있다.

Goast Trail

  • 잔상 효과

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

24.08.19  (0) 2024.08.20
24.08.16  (0) 2024.08.20
24.08.12  (0) 2024.08.16
24.08.09  (0) 2024.08.10
24.08.08  (0) 2024.08.09