[UE] 활 조준

지난 시간에 이어 활 조준을 구현해보자. 활의 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 선택
- 애셋 액션 - 프로퍼티 매트릭스를 통한 대량 편집

- 다섯개의 애니메이션 선택
- Base Pose Animation
- 애셋 선택 - 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로 만들어준다.
- CheckNull(Weapon);
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 |
댓글
이 글 공유하기
다른 글
-
[UE] 활 시위에 손 맞추기
[UE] 활 시위에 손 맞추기
2023.07.10 -
[UE] 활 시위 당기기
[UE] 활 시위 당기기
2023.07.07 -
[UE] 활 구현하기
[UE] 활 구현하기
2023.07.05 -
[UE] Around 스킬 구현하기
[UE] Around 스킬 구현하기
2023.07.04
댓글을 사용할 수 없습니다.