마우스가 지정한 위치에 플레이어를 이동하는 워프 스킬을 구현해볼 것이다. 마우스가 가리키는 곳의 위치에 데칼을 표시해준다. 데칼 위치를 받아온 후 마우스 좌클릭을 하면 그 곳으로 이동하는 워프 스킬을 만들어보자. 

 

 

목차

     

     


     

     

     
    Plugins
     
      Weapon
     
        Resource
     
          Icon128.png
    weapon_thumbnail_icon.png
     
        Source
     
          Weapon  
          SWeaponCheckBoxes.h .cpp
    SWeaponDetailsView.h .cpp

    SWeaponDoActionData.h .cpp
    SWeaponEquipmentData.h .cpp
    SWeaponHitData.h .cpp

    SWeaponLeftArea.h .cpp
    Weapon.Build.cs
    WeaponAssetEditor.h .cpp
    WeaponAssetFactory.h .cpp
    WeaponCommand.h .cpp 
    WeaponContextMenu.h .cpp
    WeaponModule.h .cpp
    WeaponStyle.h .cpp 
     
       

     

     
    Source
      U2212_06
        Characters
        CAnimInstance.h .cpp
    CEnemy.h .cpp 
    CPlayer.h .cpp
    ICharacter.h .cpp
        Components
        CMontagesComponent.h .cpp 
    CMovementComponent.h .cpp 
    CStateComponent.h .cpp
    CStatusComponent.h .cpp  
    CWeaponComponent.h .cpp 
        Notifies
        CAnimNotifyState_BeginAction.h .cpp 
    CAnimNotify_CameraShake.h .cpp 
    CAnimNotifyState_EndAction.h .cpp
    CAnimNotify_EndState.h .cpp
    CAnimNotifyState.h .cpp
    CAnimNotifyState_CameraAnim.h .cpp
    CAnimNotifyState_Collision.h .cpp 
    CAnimNotifyState_Combo.h .cpp
    CAnimNotifyState_Equip.h .cpp
    CAnimNotifyState_SubAction.h .cpp
        Utilities
        CHelper.h
    CLog.h .cpp
        Weapons
        CAura.h .cpp
    CCamerModifier.h .cpp
    CGhostTrail.h .cpp
    CDoAction_Combo.h .cpp
    CDoAction_Warp.h .cpp 생성
    CSubAction_Fist.h .cpp
    CSubAction_Hammer.h .cpp
    CSubAction_Sword.h .cpp

    CAttachment.h .cpp
    CDoAction.h .cpp
    CEquipment.h .cpp
    CSubAction.h .cpp
    CWeaponAsset.h .cpp
    CWeaponStructures.h .cpp
        Global.h
    CGameMode.h .cpp
    U2212_06.Build.cs
        U2212_06.uproject
     

     

     

     

     

    워프 스킬 구현하기

     

     


     

    Warp_Montage 생성

     

    Begin_DoAction과 End_DoAction을 설정해준다.

     


     

     

     

    BP_CAttachment_Warp 생성

     

    블루프린트 클래스 - CAttachment - BP_CAttachment_Warp 생성

    Decal를 추가

    • 트랜스폼
      • 회전: 0.0, -90.0, 0.0 설정
    • Decal
      • Decal Material: M_Cusor_Inst 설정
      • 데칼 크기: 10.0, 64.0, 64.0 설정

     

    Event Graph

     


     

     

    CPlayer.cpp  -  키 맵핑 (Warp)

     

    CPlayer.cpp

    더보기
    #include "Characters/CPlayer.h"
    #include "Global.h"
    #include "CAnimInstance.h"
    #include "GameFramework/SpringArmComponent.h"
    #include "GameFramework/CharacterMovementComponent.h"
    #include "Camera/CameraComponent.h"
    #include "Components/SkeletalMeshComponent.h"
    #include "Components/InputComponent.h"
    #include "Components/CMontagesComponent.h"
    #include "Components/CMovementComponent.h"
    #include "Components/CWeaponComponent.h"
    
    ACPlayer::ACPlayer()
    {
    	CHelpers::CreateComponent<USpringArmComponent>(this, &SpringArm, "SpringArm", GetMesh());
    	CHelpers::CreateComponent<UCameraComponent>(this, &Camera, "Camera", SpringArm);
    
    	CHelpers::CreateActorComponent<UCWeaponComponent>(this, &Weapon, "Weapon");
    	CHelpers::CreateActorComponent<UCMontagesComponent>(this, &Montages, "Montages");
    	CHelpers::CreateActorComponent<UCMovementComponent>(this, &Movement, "Movement");
    	CHelpers::CreateActorComponent<UCStateComponent>(this, &State, "State");
    
    	GetMesh()->SetRelativeLocation(FVector(0, 0, -90));
    	GetMesh()->SetRelativeRotation(FRotator(0, -90, 0));
    
    	USkeletalMesh* mesh;
    	CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character/Mesh/SK_Mannequin.SK_Mannequin'");
    	GetMesh()->SetSkeletalMesh(mesh);
    
    	TSubclassOf<UCAnimInstance> animInstance;
    	CHelpers::GetClass<UCAnimInstance>(&animInstance, "AnimBlueprint'/Game/ABP_Character.ABP_Character_C'");
    	GetMesh()->SetAnimClass(animInstance);
    
    	SpringArm->SetRelativeLocation(FVector(0, 0, 140));
    	SpringArm->SetRelativeRotation(FRotator(0, 90, 0));
    	SpringArm->TargetArmLength = 200;
    	SpringArm->bDoCollisionTest = false;
    	SpringArm->bUsePawnControlRotation = true;
    	SpringArm->bEnableCameraLag = true;
    
    	GetCharacterMovement()->RotationRate = FRotator(0, 720, 0);
    
    }
    
    void ACPlayer::BeginPlay()
    {
    	Super::BeginPlay();
    
    	Movement->OnRun(); //Movement의 기본을 Run으로 설정
    	Movement->DisableControlRotation();//Movement의 기본을 DisableControlRotation으로 설정
    
    	State->OnStateTypeChanged.AddDynamic(this, &ACPlayer::OnStateTypeChanged);
    }
    
    void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
    {
    	Super::SetupPlayerInputComponent(PlayerInputComponent);
    
    	PlayerInputComponent->BindAxis("MoveForward", Movement, &UCMovementComponent::OnMoveForward);
    	PlayerInputComponent->BindAxis("MoveRight", Movement, &UCMovementComponent::OnMoveRight);
    	PlayerInputComponent->BindAxis("VerticalLook", Movement, &UCMovementComponent::OnVerticalLook);
    	PlayerInputComponent->BindAxis("HorizontalLook", Movement, &UCMovementComponent::OnHorizontalLook);
    
    	PlayerInputComponent->BindAction("Sprint", EInputEvent::IE_Pressed, Movement, &UCMovementComponent::OnSprint);
    	PlayerInputComponent->BindAction("Sprint", EInputEvent::IE_Released, Movement, &UCMovementComponent::OnRun);
    
    	PlayerInputComponent->BindAction("Avoid", EInputEvent::IE_Pressed, this, &ACPlayer::OnAvoid);
    
    	PlayerInputComponent->BindAction("Fist", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetFistMode);
    	PlayerInputComponent->BindAction("Sword", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetSwordMode);
    	PlayerInputComponent->BindAction("Hammer", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetHammerMode);
    	PlayerInputComponent->BindAction("Warp", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetWarpMode);
    
    	PlayerInputComponent->BindAction("Action", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::DoAction);
    
    	PlayerInputComponent->BindAction("SubAction", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SubAction_Pressed);
    	PlayerInputComponent->BindAction("SubAction", EInputEvent::IE_Released, Weapon, &UCWeaponComponent::SubAction_Released);
    }
    
    void ACPlayer::OnStateTypeChanged(EStateType InPrevType, EStateType InNewType)
    {
    	switch (InNewType)
    	{
    		case EStateType::BackStep: BackStep(); break;
    	}
    }
    
    void ACPlayer::OnAvoid()
    {
    	CheckFalse(State->IsIdleMode());
    	CheckFalse(Movement->CanMove());
    
    	CheckTrue(InputComponent->GetAxisValue("MoveForward") >= 0.0f);//뒷방향을 입력했다면
    
    	State->SetBackStepMode();//State을 BackStepMode로 변경한다.
    }
    
    void ACPlayer::BackStep()
    {
    	Movement->EnableControlRotation();//정면을 바라본 상태로 뒤로 뛰어야하기 때문에 EnableControlRotation으로 만들어준다.
    
    	Montages->PlayBackStepMode();//PlayBackStepMode()를 통해 몽타주 재생.
    }
    
    void ACPlayer::End_BackStep()
    {
    	Movement->DisableControlRotation();//Backstep이 끝나면 원래대로 돌려준다.
    
    	State->SetIdleMode();//Idle상태로 돌려줌.
    }

    키 맵핑 void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)

    • PlayerInputComponent->BindAction("Warp", EInputEvent::IE_Pressed, Weapon,  &UCWeaponComponent::SetWarpMode);

     

    CDoAction.h

     

    CDoAction.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    #include "UObject/NoExportTypes.h"
    #include "Weapons/CWeaponStructures.h"
    #include "CDoAction.generated.h"
    
    UCLASS(Abstract)//DoAction 그 자체로는 객체화되면 안 되기 때문에 Abstract을 붙여준다.
    class U2212_06_API UCDoAction : public UObject
    {
    	GENERATED_BODY()
    
    public:
    	UCDoAction();
    
    	virtual void BeginPlay //재정의 할 수 있도록 virtual로 만든다.
    	(
    		class ACAttachment* InAttachment,
    		class UCEquipment* InEquipment,
    		class ACharacter* InOwner,
    		const TArray<FDoActionData>& InDoActionDatas,//CWeaponStucture내의 FDoActionData 
    		const TArray<FHitData>& InHitDatas //CWeaponStucture내의 FHitData
    	);
    	virtual void Tick(float InDeltaTime) { }
    
    public:
    	//재정의 할 수 있도록 virtual로 만든다.
    	virtual void DoAction();
    	virtual void Begin_DoAction();
    	virtual void End_DoAction();
    
    public:
    	UFUNCTION()
    		virtual void OnAttachmentBeginCollision() {}
    
    	UFUNCTION()
    		virtual void OnAttachmentEndCollision() {}
    
    	UFUNCTION()
    		virtual void OnAttachmentBeginOverlap(class ACharacter* InAttacker, AActor* InAttackCauser, class ACharacter* InOther) { }
    
    	UFUNCTION()
    		virtual void OnAttachmentEndOverlap(class ACharacter* InAttacker, class ACharacter* InOther) { }
    
    protected:
    	bool bInAction;
    	bool bBeginAction;
    
    	class ACharacter* OwnerCharacter;
    	class UWorld* World;
    
    	class UCMovementComponent* Movement;
    	class UCStateComponent* State;
    
    	TArray<FDoActionData> DoActionDatas;
    	TArray<FHitData> HitDatas;
    };

    CDoAction_Warp에서 재정의 할 수 있도록 상위 클래스인 CDoAction에서 Tick 함수를 만든다.

    • virtual void Tick(float InDeltaTime) { }

     

    변수 추가

    • bool bInAction;
      • Action이 들어왔는지 true, false 체크해주는 변수를 추가한다.

     

     

     

    CDoAction.cpp

    더보기
    #include "Weapons/CDoAction.h"
    #include "Global.h"
    #include "CAttachment.h"
    #include "CEquipment.h"
    #include "GameFramework/Character.h"
    #include "Components/CStateComponent.h"
    #include "Components/CMovementComponent.h"
    
    UCDoAction::UCDoAction()
    {
    }
    
    void UCDoAction::BeginPlay(ACAttachment* InAttachment, UCEquipment* InEquipment, 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;
    	HitDatas = InHitDatas;
    }
    
    void UCDoAction::DoAction()
    {
    	bInAction = true;
    
    	State->SetActionMode();
    }
    
    void UCDoAction::Begin_DoAction()
    {
    	bBeginAction = true;
    }
    
    void UCDoAction::End_DoAction()
    {
    	bInAction = false;
    	bBeginAction = false;
    
    	State->SetIdleMode();
    
    	Movement->Move();
    	Movement->DisableFixedCamera();
    }

    추가한 bInAction 변수를 DoAction()End_DoAction()에 넣어 Action의 시작과 끝을 알려준다.

    • void UCDoAction::DoAction()
      • bInAction = true;
    • void UCDoAction::End_DoAction()
      • bInAction = false;

     


     

     

    CWeaponComponent.cpp  -  Tick 함수 추가

     

    CWeaponComponent.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Components/ActorComponent.h"
    #include "CWeaponComponent.generated.h"
    
    UENUM(BlueprintType)
    enum class EWeaponType : uint8
    {
    	Fist, Sword, Hammer, Warp, Around, Bow, Max,
    };
    DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FWeaponTypeChanged, EWeaponType, InPrevType, EWeaponType, InNewType);
    
    UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
    class U2212_06_API UCWeaponComponent : public UActorComponent
    {
    	GENERATED_BODY()
    
    private://DataAsset을 받아온다.
    	UPROPERTY(EditAnywhere, Category = "DataAsset")
    		class UCWeaponAsset* DataAssets[(int32)EWeaponType::Max];
    
    public: //무기 Type이 맞는지 확인해주는 함수들
    	FORCEINLINE bool IsUnarmedMode() { return Type == EWeaponType::Max; }
    	FORCEINLINE bool IsFistMode() { return Type == EWeaponType::Fist; }
    	FORCEINLINE bool IsSwordMode() { return Type == EWeaponType::Sword; }
    	FORCEINLINE bool IsHammerMode() { return Type == EWeaponType::Hammer; }
    	FORCEINLINE bool IsWarpMode() { return Type == EWeaponType::Warp; }
    	FORCEINLINE bool IsAroundMode() { return Type == EWeaponType::Around; }
    	FORCEINLINE bool IsBowMode() { return Type == EWeaponType::Bow; }
    
    public:
    	UCWeaponComponent();
    
    protected:
    	virtual void BeginPlay() override;
    
    public:
    	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
    
    private:
    	bool IsIdleMode();//StateComponent, WeaponComponent 둘 다 같은 레벨이다. 서로 소유할 수 없는 관계이기 때문에 참조만해서 리턴받기 위해 IsIdleMode를 사용한다.
    
    public:
    	class ACAttachment* GetAttachment();
    	class UCEquipment* GetEquipment();
    	class UCDoAction* GetDoAction();
    	class UCSubAction* GetSubAction();
    
    public: //무기 세팅
    	void SetUnarmedMode();
    	void SetFistMode();
    	void SetSwordMode();
    	void SetHammerMode();
    	void SetWarpMode();
    	void SetAroundMode();
    	void SetBowMode();
    
    	void DoAction();
    	void SubAction_Pressed();
    	void SubAction_Released();
    
    private:
    	void SetMode(EWeaponType InType);
    	void ChangeType(EWeaponType InType);
    
    public: //무기가 바뀌었을때 통보해줄 delegate
    	FWeaponTypeChanged OnWeaponTypeChange;
    
    private:
    	class ACharacter* OwnerCharacter;
    
    	EWeaponType Type = EWeaponType::Max;
    };

    변경사항 없음.

     

     

     

    CWeaponComponent.cpp

    더보기
    #include "Components/CWeaponComponent.h"
    #include "Global.h"
    #include "CStateComponent.h"
    #include "GameFramework/Character.h"
    #include "Weapons/CWeaponAsset.h"
    #include "Weapons/CAttachment.h"
    #include "Weapons/CEquipment.h"
    #include "Weapons/CDoAction.h"
    #include "Weapons/CSubAction.h"
    
    UCWeaponComponent::UCWeaponComponent()
    {
    	//Tick을 실행시켜주는 코드
    	PrimaryComponentTick.bCanEverTick = true;
    }
    
    void UCWeaponComponent::BeginPlay()
    {
    	Super::BeginPlay();
    
    	OwnerCharacter = Cast<ACharacter>(GetOwner());
    	for (int32 i=0; i < (int32)EWeaponType::Max; i++)
    	{
    		if (!!DataAssets[i]) //DataAssets[i]이 있다면(=무기가 할당되어 있다면)
    			DataAssets[i]->BeginPlay(OwnerCharacter);//BeginPla y 시 OwnerCharacter에 Spawn시켜준다.
    	}
    }
    
    void UCWeaponComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
    {
    	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
    
    	if (!!GetDoAction())//DoAction이 있다면
    		GetDoAction()->Tick(DeltaTime);//DoAction의 Tick을 콜 해준다.
    
    	if (!!GetSubAction())//SubAction이 있다면
    		GetSubAction()->Tick(DeltaTime);//SubAction의 Tick을 콜 해준다.
    }
    
    bool UCWeaponComponent::IsIdleMode()
    {
    	return CHelpers::GetComponent<UCStateComponent>(OwnerCharacter)->IsIdleMode();
    }
    
    ACAttachment* UCWeaponComponent::GetAttachment()
    {
    	CheckTrueResult(IsUnarmedMode(), nullptr);
    	CheckFalseResult(!!DataAssets[(int32)Type], nullptr);
    
    	return DataAssets[(int32)Type]->GetAttachment();
    }
    
    UCEquipment* UCWeaponComponent::GetEquipment()
    {
    	CheckTrueResult(IsUnarmedMode(), nullptr);
    	CheckFalseResult(!!DataAssets[(int32)Type], nullptr);
    
    	return DataAssets[(int32)Type]->GetEquipment();
    }
    
    UCDoAction* UCWeaponComponent::GetDoAction()
    {
    	CheckTrueResult(IsUnarmedMode(), nullptr);
    	CheckFalseResult(!!DataAssets[(int32)Type], nullptr);
    
    	return DataAssets[(int32)Type]->GetDoAction();
    }
    
    UCSubAction* UCWeaponComponent::GetSubAction()
    {
    	CheckTrueResult(IsUnarmedMode(), nullptr);
    	CheckFalseResult(!!DataAssets[(int32)Type], nullptr);
    
    	return DataAssets[(int32)Type]->GetSubAction();
    }
    
    void UCWeaponComponent::SetUnarmedMode()
    {
    	GetEquipment()->Unequip();
    
    	ChangeType(EWeaponType::Max);
    }
    
    void UCWeaponComponent::SetFistMode()
    {
    	CheckFalse(IsIdleMode());
    
    	SetMode(EWeaponType::Fist);
    }
    
    void UCWeaponComponent::SetSwordMode()
    {
    	CheckFalse(IsIdleMode());
    
    	SetMode(EWeaponType::Sword);
    }
    
    void UCWeaponComponent::SetHammerMode()
    {
    	CheckFalse(IsIdleMode());
    
    	SetMode(EWeaponType::Hammer);
    }
    
    void UCWeaponComponent::SetWarpMode()
    {
    	CheckFalse(IsIdleMode());
    
    	SetMode(EWeaponType::Warp);
    }
    
    void UCWeaponComponent::SetAroundMode()
    {
    	CheckFalse(IsIdleMode());
    
    	SetMode(EWeaponType::Around);
    }
    
    void UCWeaponComponent::SetBowMode()
    {
    	CheckFalse(IsIdleMode());
    
    	SetMode(EWeaponType::Bow);
    }
    
    void UCWeaponComponent::SetMode(EWeaponType InType)
    {
    	if (Type == InType)
    	{
    		SetUnarmedMode();
    
    		return;
    	}
    	else if (IsUnarmedMode() == false)
    	{
    		GetEquipment()->Unequip();
    	}
    
    	if (!!DataAssets[(int32)InType])
    	{
    		DataAssets[(int32)InType]->GetEquipment()->Equip();
    
    		ChangeType(InType);
    	}
    }
    
    void UCWeaponComponent::ChangeType(EWeaponType InType)
    {
    	EWeaponType prevType = Type;
    	Type = InType;
    
    	if (OnWeaponTypeChange.IsBound())
    		OnWeaponTypeChange.Broadcast(prevType, InType);
    }
    
    void UCWeaponComponent::DoAction()
    {
    	if (!!GetDoAction())
    		GetDoAction()->DoAction();
    }
    
    void UCWeaponComponent::SubAction_Pressed()
    {
    	if (!!GetSubAction())
    		GetSubAction()->Pressed();
    }
    
    void UCWeaponComponent::SubAction_Released()
    {
    	if (!!GetSubAction())
    		GetSubAction()->Released();
    }

    Tick함수에서 DoAction이 있다면 DoAction의 Tick을 콜 해준다.

    • void UCWeaponComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
      • if (!!GetDoAction())
              GetDoAction()->Tick(DeltaTime);

     


     

    CDoAction_Warp 생성

     

     

    CDoAction_Warp.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Weapons/CDoAction.h"
    #include "CDoAction_Warp.generated.h"
    
    UCLASS(Blueprintable)
    class U2212_06_API UCDoAction_Warp : public UCDoAction
    {
    	GENERATED_BODY()
    
    public:
        UCDoAction_Warp();
    
        virtual void BeginPlay
        (
            class ACAttachment* InAttachment,
            class UCEquipment* InEquipment,
            class ACharacter* InOwner,
            const TArray<FDoActionData>& InDoActionData,
            const TArray<FHitData>& InHitData
        );
        void Tick(float InDeltaTime) override;
    
    public:
        void DoAction() override;
        void Begin_DoAction() override;
    
    private:
        bool GetCursorLocationAndRotation(FVector& OutLocation, FRotator& OutRotation);
        
    private:
        class APlayerController* PlayerController;
        class UDecalComponent* Decal;
    
    private:
        FVector MoveToLocation;
    };

    블루프린트로 만들 수 있게 Blueprintable를 적어준다.

    • UCLASS(Blueprintable)

     

     

     

    CDoAction_Warp.cpp

    더보기
    #include "Weapons/DoActions/CDoAction_Warp.h"
    #include "Global.h"
    #include "GameFramework/Character.h"
    #include "GameFramework/PlayerController.h"
    #include "Components/CStateComponent.h"
    #include "Components/DecalComponent.h"
    #include "Components/CapsuleComponent.h"
    #include "Weapons/CAttachment.h"
    
    UCDoAction_Warp::UCDoAction_Warp()
    {
    }
    
    void UCDoAction_Warp::BeginPlay(ACAttachment* InAttachment, UCEquipment* InEquipment, ACharacter* InOwner, const TArray<FDoActionData>& InDoActionData, const TArray<FHitData>& InHitData)
    {
    	Super::BeginPlay(InAttachment, InEquipment, InOwner, InDoActionData, InHitData);
    
    	Decal = CHelpers::GetComponent<UDecalComponent>(InAttachment);
    	PlayerController = OwnerCharacter->GetController<APlayerController>();
    }
    
    void UCDoAction_Warp::Tick(float InDeltaTime)
    {
    	Super::Tick(InDeltaTime);
    
    	FVector location = FVector::ZeroVector;
    	FRotator rotation = FRotator::ZeroRotator;
    
    	//GetCursorLocationAndRotation이 false면 hit가 안 된 것이다.
    	if (GetCursorLocationAndRotation(location, rotation) == false)
    	{
    		Decal->SetVisibility(false);
    
    		return;
    	}
    
    	//Warp 실행 중에는 Cursor가 움직이지 않도록 만든다.
    	if (bInAction)//Action이 실행중이라면 실행을 할 필요가 없으므로
    		return;//리턴
    
    
    	Decal->SetVisibility(true);
    	//Decal이 그려지는 위치와 회전값을 설정해준다.
    	Decal->SetWorldLocation(location);
    	Decal->SetWorldRotation(rotation);
    }
    
    void UCDoAction_Warp::DoAction()
    {
    	CheckFalse(DoActionDatas.Num() > 0);
    	CheckFalse(State->IsIdleMode());
    
    	Super::DoAction();
    
    
    	FRotator rotation;
    	if (GetCursorLocationAndRotation(MoveToLocation, rotation))
    	{
    		//땅에 뭍히는것을 방지하기 위해 CapsuleHalfHeight만큼 높이를 보정해준다.
    		float height = OwnerCharacter->GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
    		MoveToLocation = FVector(MoveToLocation.X, MoveToLocation.Y, MoveToLocation.Z + height);
    
    		float yaw = UKismetMathLibrary::FindLookAtRotation(OwnerCharacter->GetActorLocation(), MoveToLocation).Yaw;
    		OwnerCharacter->SetActorRotation(FRotator(0, yaw, 0));
    	}
    	else
    		return;
    
    	DoActionDatas[0].DoAction(OwnerCharacter);
    }
    
    void UCDoAction_Warp::Begin_DoAction()
    {
    	Super::Begin_DoAction();
    
    	OwnerCharacter->SetActorLocation(MoveToLocation);//DoAction에서 설정한 MoveToLocation 위치로 이동한다.
    	MoveToLocation = FVector::ZeroVector;//이동 후에 MoveToLocation 위치를 ZeroVector로 초기화해준다.
    }
    
    bool UCDoAction_Warp::GetCursorLocationAndRotation(FVector& OutLocation, FRotator& OutRotation)
    {
    	CheckNullResult(PlayerController, false);//PlayerController가 없으면 실행하면 안 되니 체크해준다.
    
    	FHitResult hitResult;
    	PlayerController->GetHitResultUnderCursorByChannel(ETraceTypeQuery::TraceTypeQuery1, false, hitResult);
    
    	CheckFalseResult(hitResult.bBlockingHit, false);//부딪히는게 없다면 false 리턴.
    
    
    	OutLocation = hitResult.Location;//hit된 위치
    	OutRotation = hitResult.ImpactNormal.Rotation();//hit된 Normal의 회전값
    
    	return true;
    }

     

     

     

     

    BP_CDoAction_Warp 

    CDoAction_Warp 기반 블루프린트 클래스 생성 - BP_CDoAction_Warp 생성

     


     

     

    DA_Warp 생성

     

    DA_Warp 생성

     

     


     

     

     

    BP_CPlayer - Weapon에 DA_Warp 할당하기

     

    BP_CPlayer 

     


     

     

    실행화면