지난 시간에 이어 활 조준을 구현해보자. 활의 BlendSpace 1D를 만든 것처럼 플레이어 애니메이션도 BlendSpace 1D로 만들어 활 조준을 자연스럽게 구현할 것이다. 

 

 

 

 


 

 

 

 
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
CRotate_Object.h .cpp
CThornObject.h .cpp
CAttachment_Bow.h .cpp
CDoAction_Around.h .cpp
CDoAction_Combo.h .cpp
CDoAction_Warp.h .cpp
CSubAction_Around.h .cpp
CSubAction_Bow.h .cpp
CSubAction_Fist.h .cpp
CSubAction_Hammer.h .cpp
CSubAction_Sword.h .cpp
CDoAction_Warp.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
 

 

 

 

 

 

활 조준 Zoom & Out

 

 


 

CSubAction_Bow

 

 

CSubAction_Bow.h

더보기
#pragma once
#include "CoreMinimal.h"
#include "Weapons/CSubAction.h"
#include "Components/TimelineComponent.h"
#include "CSubAction_Bow.generated.h"
USTRUCT()
struct FAimData
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
float TargetArmLength = 100;
UPROPERTY(EditAnywhere)
FVector SocketOffset = FVector(0, 30, 10);
UPROPERTY(EditAnywhere)
bool bEnableCameraLag;
UPROPERTY(EditAnywhere)
FVector CameraLocation;
};
UCLASS(Blueprintable)
class U2212_06_API UCSubAction_Bow : public UCSubAction
{
GENERATED_BODY()
private:
UPROPERTY(EditAnywhere, Category = "Aiming")
class UCurveVector* Curve;
UPROPERTY(EditAnywhere, Category = "Aiming")
FAimData AimData;
UPROPERTY(EditAnywhere, Category = "Aiming")
float AimingSpeed = 200;
public:
UCSubAction_Bow();
public:
virtual void Pressed() override;
virtual void Released() override;
public:
void BeginPlay(class ACharacter* InOwner, class ACAttachment* InAttachment, class UCDoAction* InDoAction) override;
public:
void Tick_Implementation(float InDeltaTime) override;
private:
UFUNCTION()
void onAiming(FVector Output);
private:
class USpringArmComponent* SpringArm;
class UCameraComponent* Camera;
private:
FTimeline Timeline;
//SubAction는 Actor가 아니기 때문에 Component를 가질 수 없다. SubAction은 UObject 상속이다.
//그래서 TimelineComponent가 아닌 Timeline으로 작업한다.
private:
FAimData OriginData;
};

함수 추가 - 우클릭 했을 때 조준되고 때었을 때 조준이 해제되도록 하는 역할

  • virtual void Pressed() override;
  • virtual void Released() override;

 

변수 추가

  • FAimData OriginData;
    • Aim 전의 데이터를 담아주는 변수

 

 

CSubAction_Bow.cpp

더보기
#include "Weapons/SubActions/CSubAction_Bow.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/CStateComponent.h"
#include "Weapons/Attachments/CAttachment_Bow.h"
UCSubAction_Bow::UCSubAction_Bow()
{
CHelpers::GetAsset<UCurveVector>(&Curve, "CurveVector'/Game/Weapons/Bow/Curve_Aiming.Curve_Aiming'");//Editor에서 만든 CurveVector를 할당한다.
}
void UCSubAction_Bow::Pressed()
{
CheckTrue(State->IsSubActionMode());
CheckNull(SpringArm);
CheckNull(Camera);
Super::Pressed();
State->OnSubActionMode();
OriginData.TargetArmLength = SpringArm->TargetArmLength;
OriginData.SocketOffset = SpringArm->SocketOffset;
OriginData.bEnableCameraLag = SpringArm->bEnableCameraLag;
OriginData.CameraLocation = Camera->GetRelativeLocation();
SpringArm->TargetArmLength = AimData.TargetArmLength;
SpringArm->SocketOffset = AimData.SocketOffset;
SpringArm->bEnableCameraLag = AimData.bEnableCameraLag;
Camera->SetRelativeLocation(AimData.CameraLocation);
Timeline.PlayFromStart();//Timeline 동작 시작.
}
void UCSubAction_Bow::Released()
{
CheckFalse(State->IsSubActionMode());
CheckNull(SpringArm);
CheckNull(Camera);
Super::Released();
State->OffSubActionMode();
SpringArm->TargetArmLength = OriginData.TargetArmLength;
SpringArm->SocketOffset = OriginData.SocketOffset;
SpringArm->bEnableCameraLag = OriginData.bEnableCameraLag;
Camera->SetRelativeLocation(OriginData.CameraLocation);
Timeline.ReverseFromEnd();//Timeline 뒤집기
}
void UCSubAction_Bow::BeginPlay(ACharacter* InOwner, ACAttachment* InAttachment, UCDoAction* InDoAction)
{
Super::BeginPlay(InOwner, InAttachment, InDoAction);
SpringArm = CHelpers::GetComponent<USpringArmComponent>(InOwner);
Camera = CHelpers::GetComponent<UCameraComponent>(InOwner);
FOnTimelineVector timeline;
timeline.BindUFunction(this, "OnAiming");//OnAiming함수를 묶어서 콜한다.
Timeline.AddInterpVector(Curve, timeline);
Timeline.SetPlayRate(AimingSpeed);
}
void UCSubAction_Bow::Tick_Implementation(float InDeltaTime)
{
Super::Tick_Implementation(InDeltaTime);
//TimelineComponent로 사용하는 경우 자기자신이 Tick이 있기 때문에 아래 작업을 안 해도 상관없으나 CSubAction_Bow의 경우 TimelineComponent가 없기 때문에 Tick을 반드시 넣어주어야 한다.
Timeline.TickTimeline(InDeltaTime);
}
void UCSubAction_Bow::onAiming(FVector Output)
{
Camera->FieldOfView = Output.X;//조준 활성화와 해제에 앞뒤 ZoomIn&Out에 Output.X값이 쓰인다.
}

함수 정의

  • void UCSubAction_Bow::Pressed()
  • void UCSubAction_Bow::Released()

 

 


 

 

실행화면

 

void UCSubAction_Bow::onAiming(FVector Output)
{
CLog::Printf(Output, 1);
}

 

Roll과 Pitch값 변화 (90, 0) ↔ (45, 100)

 

 

void UCSubAction_Bow::onAiming(FVector Output)
{
Camera->FieldOfView = Output.X;//조준 활성화와 해제에 앞뒤 ZoomIn&Out에 Output.X값이 쓰인다.
}

 


 

 

 

 

활 조준

 


 

 

AR_Aim2 생성  +  애니메이션 5개의 Base Pose 애니메이션으로 설정

 

 

AR_ShootAndDraw 복제하여 AR_Aim2 생성 - 0프레임 제외하고 제거(=프레임 1에서 프레임 38까지 제거)

 

 

애니메이션 편집

  • AR_Aim_C, AR_Aim_D, AR_Aim_90, AR_Aim_U, AR_Aim_U90 선택
  • 애셋 액션 - 프로퍼티 매트릭스를 통한 대량 편집

 

  1. 다섯개의 애니메이션 선택
  2. Base Pose Animation
    1. 애셋 선택 - AR_Aim2 선택

 


 

 

AO_Aim 생성

 

애니메이션 - 에임 오프셋 1D - Skel_Mannequin - AO_Aim 생성

 

 


 

 

CAnimInstance

 

 

CAnimInstance.h

더보기
#pragma once
#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"
#include "Components/CWeaponComponent.h"
#include "CAnimInstance.generated.h"
UCLASS()
class U2212_06_API UCAnimInstance : public UAnimInstance
{
GENERATED_BODY()
protected:
UPROPERTY(BlueprintReadOnly, Category = "Animation")
float Speed;
UPROPERTY(BlueprintReadOnly, Category = "Animation")
float Pitch;
UPROPERTY(BlueprintReadOnly, Category = "Animation")
float Direction;
UPROPERTY(BlueprintReadOnly, Category = "Animation")
bool bBow_Aiming;
protected:
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Animation")
EWeaponType WeaponType = EWeaponType::Max;
public:
void NativeBeginPlay() override;
void NativeUpdateAnimation(float DeltaSeconds) override;
private:
UFUNCTION()
void OnWeaponTypeChanged(EWeaponType InPrevType, EWeaponType InNewType);
private:
class ACharacter* OwnerCharacter;
class UCWeaponComponent* Weapon;
private:
FRotator PrevRotation;
};

변수 추가

  • UPROPERTY(BlueprintReadOnly, Category = "Animation")
     bool bBow_Aiming;

 

 

 

CAnimInstance.cpp

더보기
#include "Characters/CAnimInstance.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "Weapons/CSubAction.h"
void UCAnimInstance::NativeBeginPlay()
{
Super::NativeBeginPlay();
OwnerCharacter = Cast<ACharacter>(TryGetPawnOwner());
CheckNull(OwnerCharacter);
Weapon = CHelpers::GetComponent<UCWeaponComponent>(OwnerCharacter);
if (!!Weapon)
Weapon->OnWeaponTypeChange.AddDynamic(this, &UCAnimInstance::OnWeaponTypeChanged);
}
void UCAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
Super::NativeUpdateAnimation(DeltaSeconds);
CheckNull(OwnerCharacter);
Speed = OwnerCharacter->GetVelocity().Size2D();
FRotator rotator = OwnerCharacter->GetVelocity().ToOrientationRotator();
FRotator rotator2 = OwnerCharacter->GetControlRotation();
FRotator delta = UKismetMathLibrary::NormalizedDeltaRotator(rotator, rotator2);
PrevRotation = UKismetMathLibrary::RInterpTo(PrevRotation, delta, DeltaSeconds, 25);
Direction = PrevRotation.Yaw;
Pitch = UKismetMathLibrary::FInterpTo(Pitch, OwnerCharacter->GetBaseAimRotation().Pitch, DeltaSeconds, 25);
CheckNull(Weapon);//무기가 있는지 확인
if (!!Weapon->GetSubAction())
{
bBow_Aiming = true;
bBow_Aiming &= WeaponType == EWeaponType::Bow;
bBow_Aiming &= Weapon->GetSubAction()->GetInAction();
}
}
void UCAnimInstance::OnWeaponTypeChanged(EWeaponType InPrevType, EWeaponType InNewType)
{
WeaponType = InNewType;
}

헤더 추가

  • #include "Weapons/CSubAction.h"

 

함수 정의

  • void UCAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
    • CheckNull(Weapon);
      • 무기가 있는지 확인
    • if (!!Weapon->GetSubAction())
      {
      bBow_Aiming = true;
      bBow_Aiming &= WeaponType == EWeaponType::Bow;
      bBow_Aiming &= Weapon->GetSubAction()->GetInAction();
      }
      • SubAction이 true라면(=SubAction 실행중이라면) bBow_Aiming을 true로, WeaponType을 Bow로, GetInAction()을 true로 만들어준다.

 

 


 

 

ABP_Player

 

BowLayer

 

 

 


 

 

 

CSubAction

 

 

CSubAction.h

더보기
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "CSubAction.generated.h"
UCLASS(Abstract)//객체화하지 않는 경우, Abstract 명시
class U2212_06_API UCSubAction : public UObject
{
GENERATED_BODY()
public:
FORCEINLINE bool GetInAction() { return bInAction; }
public:
UCSubAction();
public:
virtual void BeginPlay(class ACharacter* InOwner, class ACAttachment* InAttachment, class UCDoAction* InDoAction);
public:
virtual void Pressed();
virtual void Released();
public:
//NativeEvent는 가상화. 정의할테니 원하면 가져다가 써라는 의미.
//Implementation는 추상화. 함수 호출 해줄테니 필요하면 재정의해서 써라.
UFUNCTION(BlueprintNativeEvent)
void Begin_SubAction();
virtual void Begin_SubAction_Implementation() {}
UFUNCTION(BlueprintNativeEvent)
void End_SubAction();
virtual void End_SubAction_Implementation() {}
UFUNCTION(BlueprintNativeEvent)
void Tick(float InDeltaTime);
virtual void Tick_Implementation(float InDeltaTime) {}
protected:
bool bInAction;
class ACharacter* Owner;
class ACAttachment* Attachment;
class UCDoAction* DoAction;
class UCStateComponent* State;
class UCMovementComponent* Movement;
};

변수 추가

  •  bool bInAction;

 

함수 변형

  • virtual void Pressed();
  • virtual void Released();

 

함수 추가

  • FORCEINLINE bool GetInAction() { return bInAction; }
    • bInAction이 true인지 false인지 리턴해주는 함수.

 

 

 

CSubAction.cpp

더보기
#include "Weapons/CSubAction.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "Components/CStateComponent.h"
#include "Components/CMovementComponent.h"
#include "Components/CapsuleComponent.h"
UCSubAction::UCSubAction()
{
}
void UCSubAction::BeginPlay(ACharacter* InOwner, ACAttachment* InAttachment, UCDoAction* InDoAction)
{
Owner = InOwner;
Attachment = InAttachment;
DoAction = InDoAction;
State = CHelpers::GetComponent<UCStateComponent>(Owner);
Movement = CHelpers::GetComponent<UCMovementComponent>(Owner);
}
void UCSubAction::Pressed()
{
bInAction = true;
}
void UCSubAction::Released()
{
bInAction = false;
}

함수 정의

  • void UCSubAction::Pressed()  { bInAction = true; }
  • void UCSubAction::Released() { bInAction = false; }

 


 

 

 

실행화면

 

 


 

 

 

 

 

'⭐ Unreal Engine > UE RPG Skill' 카테고리의 다른 글

[UE] 활 시위에 손 맞추기  (0) 2023.07.10
[UE] 활 시위 당기기  (0) 2023.07.07
[UE] 활 구현하기  (0) 2023.07.05
[UE] Around 스킬 구현하기  (0) 2023.07.04
[UE] Around 공격 구현하기  (0) 2023.07.03