- CWeaponStructures - 여기부터 시작
- 자기 자신의 데이터를 이용하기 위해 ‘구조체’ 안에 함수를 넣을 수 있다.
- 즉, 구조체에 선언한 변수를 직접적으로 이용할 수 있는 함수를 구조체 안에 선언할 수 있다.
- 헤더 파일 - FEquipmentData 요소 전체 복사 후 이름 변경 : FDoActionData - ‘카메라 사용’을 ‘카메라 고정’ 변수로 이름 변경 - ParticleSystem 클래스의 ‘이펙트’ 변수 선언 - FVector 자료형의 ‘이펙트 위치’ 변수를 선언하고 ‘벡터 제로 : (0, 0, 0)’으로 초기화한다. - FVector 자료형의 ‘이펙트 크기’ 변수를 선언하고 ‘벡터 1 : (1, 1, 1)’로 초기화한다. - 구조체 안에 선언된 변수들을 이용한 함수 : ‘DoAction’ 함수를 캐릭터 클래스의 ‘오너 캐릭터’를 넣어 선언한다.
//hewader
USTRUCT()
struct FDoActionData
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
class UAnimMontage* Montage; //공격 모션
UPROPERTY(EditAnywhere)
float PlayRate = 1;
UPROPERTY(EditAnywhere)
bool bCanMove = true;
UPROPERTY(EditAnywhere)
bool bFixedCamera;
UPROPERTY(EditAnywhere)
class UParticleSystem* Effect;
UPROPERTY(EditAnywhere)
FVector EffectLocation = FVector::ZeroVector;
UPROPERTY(EditAnywhere)
FVector EffectScale = FVector::OneVector;
public:
void DoAction(class ACharacter* InOwner);
};
- CPP 파일 - ‘FDoActionData’ 구조체의 ‘DoAction’ 함수 정의 - 무브먼트 컴포넌트 ‘컴포넌트 생성’ 함수 호출해서 변수 선언하고 저장 - ‘무브먼트 컴포넌트’가 있다면 : ‘움직임 가능 확인’ 변수가 False 라면 : ‘무브먼트 컴포넌트’의 ‘정지’ 함수를 호출한다. - ‘몽타주’가 있다면 : ‘오너 캐릭터’의 ‘애님 몽타주 실행’ 함수를 ‘몽타주, 플레이 속도’를 넣어 호출한다.
//CPP
void FDoActionData::DoAction(ACharacter* InOwner)
{
UCMovementComponent* movement = CHelpers::GetComponent<UCMovementComponent>(InOwner);
if (!!movement)
{
if (bCanMove == false)
movement->Stop();
}
if (!!Montage)
InOwner->PlayAnimMontage(Montage, PlayRate);
}
- CWeaponAsset
- 콤보 공격이라면 공격 데이터가 여러개 필요하다.
- FDoActionData 구조체 자료형의 변수를 ‘배열’로 선언해야한다.
- 콤보공격의 콤보수를 정확히 모르니 ‘TArray’로 선언한다.
- 헤더 파일 - FDoActionData 구조체 자료형의 ‘DoActionDatas’ 배열 변수 선언
//header
UPROPERTY(EditAnywhere)
TArray<FDoActionData> DoActionDatas;
- CDoAction
- AActor로부터 상속받지 않는 이상, 항상 ‘생성자’가 있다고 생각해라
- Actor 상속 이외에는 그냥 거의 습관처럼 ‘생성자’를 만들어라
- DoAction : Object로부터 상속받는다.
- FDoActionData 구조체 자료형의 ‘InDoActionDatas’ 배열 변수를 단순하게 그냥 넣으면 호출될때마다 배열의 요소가 복사되어 들어가게 된다.
- 해결 방법 : 매개 변수를 ‘레퍼런스 &’로 받는다.
- 배열 변수의 내부요소가 복사되지 않는것도 좋지만 내부의 요소값이 수정되어서도 안된다.
- 해결 방법 : ‘const 상수’로 선언한다.
- 원래는 구조체 자료형의 배열 변수를 사용할때는 ‘주소 복사’로 클론을 만들어 사용했어야했다.
- 헤더 파일 - 생성자 추가 - ‘임의의 BeginPlay’ 함수 선언 : Character ‘오너 캐릭터’ 변수, FDoActionData 구조체 자료형의 ‘InDoActionDatas’ const 레퍼런스 배열 변수 를 넣어 선언 - 받아온 DoActionData들을 받아서 사용할 수 있도록 FDoActionData 구조체 자료형의 배열 변수를 선언한다. - 매개변수로 받은 오너 캐릭터를 정장해서 사용하기 위해 ‘오너 캐릭터’ 변수를 선언한다. - 공격에는 소환이 많아 월드도 많이 싸용하기 때문에 ‘월드’ 변수도 선언 - ‘무브먼트 컴포넌트’ 변수와 ‘스테이트 컴포넌트’ 변수도 생성한다. - ‘공격 수행’, ‘공격 수행 시작’, ‘공격 수행 종료’ 함수를 선언하고 재정의가 가능하도록 virtual로 선언한다.
//header
public:
UCDoAction();
void BeginPlay
(
class ACharacter* InOwner,
const TArray<FDoActionData>& InDoActionDatas
);
public:
virtual void DoAction();
virtual void Begin_DoAction();
virtual void End_DoAction();
protected:
class ACharacter* OwnerCharacter;
class UWorld* World;
class UCMovementComponent* Movement;
class UCStateComponent* State;
TArray<FDoActionData> DoActionDatas;
- CPP 파일 - ‘임의의 BeginPlay’ 함수 정의 - 매개변수로 받은 오너 캐릭터를 ‘오너 캐릭터’에 저장 - ‘오너 캐릭터’의 ‘월드 가져오기’ 함수를 호출해서 ‘월드’ 변수에 저장 - ‘무브먼트 컴포넌트’와 ‘스테이트 컴포넌트’ 변수에 ‘콤포넌트 가져오기’ 함수를 호출해서 저장한다. - 매개변수로 들어온 DoActionDatas를 ‘DoActionDatas’에 저장한다. - ‘DoAction’ 함수 정의 - ‘스테이트 컴포넌트’의 ‘공격 모드 설정’ 함수 호출 - ‘End_DoAction’ 함수 정의 - ‘스테이트 컴포넌트’의 ‘기본 모드 설정’ 함수 호출 - ‘무브먼트 컴포넌트’의 ‘이동’ 함수 호출
//CPP
void UCDoAction::BeginPlay(ACharacter* InOwner, const TArray<FDoActionData>& InDoActionDatas, const TArray<FHitData>& InHitDatas)
{
OwnerCharacter = InOwner;
World = OwnerCharacter->GetWorld();
State = CHelpers::GetComponent<UCStateComponent>(OwnerCharacter);
Movement = CHelpers::GetComponent<UCMovementComponent>(OwnerCharacter);
DoActionDatas = InDoActionDatas;
}
void UCDoAction::DoAction()
{
State->SetActionMode();
}
void UCDoAction::End_DoAction()
{
State->SetIdleMode();
Movement->Move();
}
- CStateComponent
//header
void SetActionMode();
- CPP 파일 - ‘공격 모드 설정’ 함수 정의 - ‘타입 변경’ 함수에 ‘상태 타입 : 공격’을 넣고 호출한다.
//CPP
void UCStateComponent::SetActionMode()
{
ChangeType(EStateType::Action);
}
- CDoAction_Combo
- C++ 클래스를 상속받는 C++ 클래스를 생성할때는 상속받는 클래스의 이름을 앞에 남겨두고 뒤에 어떤 기능을 추가하는지 써놓는것이 좋다.
- 공격 기능 중 ‘콤보’ 만을 관리하는 클래스다.
- 필요한 요소는 부모인 CDoAction 에서 저장했으므로 정의할 함수들만 정의한다.
- DoActionDatas의 요소 수가 1보다 작다는것은 : 공격할 데이터가 하나도 없다는 것이다.
- 헤더 파일 - ‘콤보 수’, ‘콤보 허용 확인’, ‘콤보 존재 확인’ 변수 선언 - ‘콤보 허용’ 함수를 선언하고 ‘콤보 허용 확인’ 변수에 True를 저장하는것으로 정의한다. - ‘콤보 비허용’ 함수를 선언하고 ‘콤보 허용 확인’ 변수에 False를 저장하는것으로 정의한다. - ‘공격 수행’, ‘공격 수행 시작’, ‘공격 수행 종료’ 함수를 선언하고 override로 선언한다.
//header
private:
int32 Index;
bool bEnable;
bool bExist;
public:
FORCEINLINE void EnableCombo() { bEnable = true; }
FORCEINLINE void DisableCombo() { bEnable = false; }
public:
void DoAction() override;
void Begin_DoAction() override;
void End_DoAction() override;
- CPP 파일 - ‘DoAction’ 함수 정의 - 부모에 있는 ‘DoActionDatas’의 ‘수 반환’ 함수를 호출하고 1보다 작은지 확인한다. - ‘콤보 허용 확인’이 True 라면 : ‘콤보 허용확인’은 False로, ‘콤보 존재 확인’은 True로 저장한다. - 콤보 허용 구간이 아니라면 : ‘스테이트 컴포넌트’의 ‘기본 모드 확인’ 함수를 호출해서 False인지 확인한다. (최초공격을 할 수있는 상태인지 알아봐야한다.) - 부모의 ‘DoAction’을 호출한다. (부모의 DoAction에는 SetActionMode 가 있다.) - ‘DoActionDatas’의 ‘콤보 수’ 순번에서 ‘DoAction’에 ‘오너 캐릭터’를 넣고 호출한다. - Begin_DoAction’ 함수 정의 - 부모의 ‘Begin_DoAction’ 함수 호출 - ‘콤보 존재 확인’ 변수가 False인지 확인한다. - 존재한다면 ‘콤보 존재 확인’ 변수에 False를 저장한다. - ‘DoActionDatas’에서 ‘1 증가된 콤보수’의 순번의 ‘DoAction’ 함수에 ‘오너 캐릭터’를 넣어 호출한다. - ‘End_DoAction’ 함수 정의 - 부모 함수 호출 - ‘콤보 수’ 0으로 초기화
//CPP
void UCDoAction_Combo::DoAction()
{
CheckTrue(DoActionDatas.Num() < 1); //
if (bEnable)
{
bEnable = false;
bExist = true;
return;
}
//아이들 모드 아니면 최초 공격 안나가게 한다.
CheckFalse(State->IsIdleMode());
Super::DoAction();
DoActionDatas[Index].DoAction(OwnerCharacter);
}
void UCDoAction_Combo::Begin_DoAction()
{
Super::Begin_DoAction();
CheckFalse(bExist);
bExist = false;
DoActionDatas[++Index].DoAction(OwnerCharacter);
}
void UCDoAction_Combo::End_DoAction()
{
Super::End_DoAction();
Index = 0;
}
- 즉, ‘DoActionDatas’의 ‘콤보 수’ 순번에서 ‘DoAction’에 ‘오너 캐릭터’를 넣고 호출하면 → WeaponStructures의 ‘DoAction’ 함수를 실행하고 결과적으로는, 움직임이 정지하고 공격 몽타주가 실행된다.
- CWeaponAsset
- 헤더 파일 - DoAction 클래스의 변수를 전언하고 ‘클래스 제한’으로 선언한다.
//header
UPROPERTY(EditAnywhere)
TSubclassOf<class UCDoAction> DoActionClass;
- CPlayer
- 공격 액션 연결
- CPP 파일 - 인풋 컴포넌트의 ‘액션 연결’ 함수에 ‘액션 맵핑 이름, 액션 상태, 액션 연결 함수가 있는 클래스, 연결 함수’를 넣고 호출한다.
//CPP
PlayerInputComponent->BindAction("Action", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::DoAction);
- CWeaponComponent
- 헤더 파일 - DoAction 클래스의 ‘DoAction 가져오기’ 함수 선언
//header
class UCDoAction* GetDoAction();
- CPP 파일 - ‘Equipment 가져오기’ 함수를 복붙하고 이름을 DoAction으로 바꾼다.
//CPP
UCDoAction* UCWeaponComponent::GetDoAction()
{
CheckTrueResult(ISUnarmedMode(), nullptr);
CheckFalseResult(!!DataAssets[(int32)Type], nullptr);
return DataAssets[(int32)Type]->GetDoAction();
}
- CWeaponAsset
- 헤더 파일 - ‘Equipment 가져오기’ 함수를 복붙해서 DoAction으로 바꾼다. - DoAction 클래스 자료형의 변수를 선언한다.
//header
FORCEINLINE class UCDoAction* GetDoAction() { return DoAction; }
class UCDoAction* DoAction; //장착 관리 담당
- CPP 파일 - BeginPlay 함수 확인 - ‘DoAction 클래스’ 변수가 비어있지 않다면 : ‘DoAction’ 변수에 해당 자료형으로 ‘오브젝트 생성’ 함수를 호출하고 저장한다. - ‘DoAction’ 변수의 ‘BeginPlay’ 함수에 ‘오너 캐릭터, DoActionDatas’를 넣고 호출한다.
//CPP
if (!!DoActionClass)
{
DoAction = NewObject<UCDoAction>(this, DoActionClass);
DoAction->BeginPlay(InOwner, DoActionDatas, HitDatas);
}
- CWeaponComponent
//header
public:
void DoAction();
- CPP 파일 - ‘DoAction’ 함수 정의 - ‘DoAction 가져오기’ 함수를 호출하고 비어있지 않다면 : ‘DoAction 가져오기’ 함수 호출의 ‘DoAction’ 함수를 호출한다.
//CPP
void UCWeaponComponent::DoAction()
{
if (!!GetDoAction())
{
GetDoAction()->DoAction();
}
}
- DA_Sword
- Do Action Datas 배열에 콤보 공격 데이터 넣기