[UE] 활 구현하기
이전에 만든 Attachment을 구조를 활용하여 활 무기를 추가해보자. CAttachment_Bow를 생성하여 활용하여 활은 만든다. BlendSpace와 Curve를 활용하여 활이 구부러지는 것을 구현한다.
목차
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 | ||
활 구현하기
CPlayer.cpp - 키 맵핑 (Bow)
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("Around", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetAroundMode);
PlayerInputComponent->BindAction("Bow", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetBowMode);
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("Bow", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetBowMode);
CAttachment
CAttachment.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CAttachment.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FAttachmentBeginCollision);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FAttachmentEndCollision);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FAttachmentBeginOverlap, class ACharacter*, InAttacker, AActor*, InAttackCauser, class ACharacter*, InOther);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FAttachmentEndOverlap, class ACharacter*, InAttacker, class ACharacter*, InOther);
UCLASS()
class U2212_06_API ACAttachment : public AActor
{
GENERATED_BODY()
protected:
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
class USceneComponent* Root;
public:
ACAttachment();
protected:
virtual void BeginPlay() override;
public:
UFUNCTION(BlueprintNativeEvent)
void OnBeginEquip();
virtual void OnBeginEquip_Implementation() {}
UFUNCTION(BlueprintNativeEvent)
void OnUnequip();
virtual void OnUnequip_Implementation() {}
//위에걸로 수정하였다.
/*UFUNCTION(BlueprintImplementableEvent)
void OnBeginEquip();
UFUNCTION(BlueprintImplementableEvent)
void OnUnequip();*/
public:
void OnCollisions(); //Collision 켜기
void OffCollisions();//Collision 끄기
private:
UFUNCTION()
void OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void OnComponentEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
protected:
UFUNCTION(BlueprintCallable, Category = "Attach")
void AttachTo(FName InSocketName);
UFUNCTION(BlueprintCallable, Category = "Attach")
void AttachToCollision(FName InCollisionName);
public:
FAttachmentBeginCollision OnAttachmentBeginCollision;
FAttachmentEndCollision OnAttachmentEndCollision;
FAttachmentBeginOverlap OnAttachmentBeginOverlap;
FAttachmentEndOverlap OnAttachmentEndOverlap;
protected:
UPROPERTY(BlueprintReadOnly, Category = "Game")
class ACharacter* OwnerCharacter;
//UShapeComponent는 UBox,Capsule,SphereComponent의 상위클래스
UPROPERTY(BlueprintReadOnly, Category = "Game")
TArray<class UShapeComponent*> Collisions;
};
수정 전
- UFUNCTION(BlueprintImplementableEvent)
void OnBeginEquip(); - UFUNCTION(BlueprintImplementableEvent)
void OnUnequip()
수정 후
- UFUNCTION(BlueprintNativeEvent)
void OnBeginEquip();
virtual void OnBeginEquip_Implementation() {} - UFUNCTION(BlueprintNativeEvent)
void OnUnequip();
virtual void OnUnequip_Implementation() {}
CAttachment_Bow 생성
새 C++ 클래스 생성 - CAttachment - CAttachment_Bow 생성
CAttachment_Bow.h
#pragma once
#include "CoreMinimal.h"
#include "Weapons/CAttachment.h"
#include "CAttachment_Bow.generated.h"
UCLASS()
class U2212_06_API ACAttachment_Bow : public ACAttachment
{
GENERATED_BODY()
private:
UPROPERTY(EditDefaultsOnly, Category = "View")
FVector2D ViewPitchRange = FVector2D(-40, +30);//Pitch 제한각 설정
private:
UPROPERTY(VisibleAnywhere)
class USkeletalMeshComponent* SkeletalMesh;
UPROPERTY(VisibleAnywhere)
class UPoseableMeshComponent* PoseableMesh;
public:
ACAttachment_Bow();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
public:
void OnBeginEquip_Implementation() override;
void OnUnequip_Implementation() override;
private:
FVector2D OriginViewPitchRange;
};
CAttachment_Bow.cpp
#include "Weapons/Attachments/CAttachment_Bow.h"
#include "Global.h"
#include "Global.h"
//#include "Characters/CAnimInstance_Bow.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/PoseableMeshComponent.h"
ACAttachment_Bow::ACAttachment_Bow()
{
CHelpers::CreateComponent<USkeletalMeshComponent>(this, &SkeletalMesh, "SkeletalMesh", Root);
CHelpers::CreateComponent<UPoseableMeshComponent>(this, &PoseableMesh, "PoseableMesh", Root);
USkeletalMesh* mesh;
CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character/Weapons/ElvenBow/SK_ElvenBow.SK_ElvenBow'");
SkeletalMesh->SetSkeletalMesh(mesh);
SkeletalMesh->SetCollisionProfileName("NoCollision");
}
void ACAttachment_Bow::BeginPlay()
{
Super::BeginPlay();
AttachTo("Holster_Bow");
SkeletalMesh->SetVisibility(false);
PoseableMesh->SetSkeletalMesh(SkeletalMesh->SkeletalMesh);//SkeletalMesh 내의 SkeletalMesh 사용.
PoseableMesh->CopyPoseFromSkeletalComponent(SkeletalMesh);//포즈를 캡처해둔다.
}
void ACAttachment_Bow::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
PoseableMesh->CopyPoseFromSkeletalComponent(SkeletalMesh);
}
void ACAttachment_Bow::OnBeginEquip_Implementation()
{
Super::OnBeginEquip_Implementation();
AttachTo("Hand_Bow_Left");//Hand_Bow_Left 소켓에 장착.
APlayerController* controller = OwnerCharacter->GetController<APlayerController>();
if (!!controller)
{
OriginViewPitchRange.X = controller->PlayerCameraManager->ViewPitchMin;
OriginViewPitchRange.Y = controller->PlayerCameraManager->ViewPitchMax;
controller->PlayerCameraManager->ViewPitchMin = ViewPitchRange.X;
controller->PlayerCameraManager->ViewPitchMax = ViewPitchRange.Y;
}
}
void ACAttachment_Bow::OnUnequip_Implementation()
{
Super::OnUnequip_Implementation();
AttachTo("Holster_Bow");//Holster_Bow 소켓에 장착.
APlayerController* controller = OwnerCharacter->GetController<APlayerController>();
if (!!controller)
{
controller->PlayerCameraManager->ViewPitchMin = OriginViewPitchRange.X;
controller->PlayerCameraManager->ViewPitchMax = OriginViewPitchRange.Y;
}
}
BP_CAttachment_Bow 생성
ABP_Character - Bow 동작 추가하기
새 애니메이션 레이어 - BowLayer 생성
AnimGraph
블랜드 포즈 (EWeaponType)에 Bow 포즈 추가
- 링크된 애님 레이어 생성 - 방금 전에 만든 BowLayer 설정하고 이어준다.
CSubAction_Bow 생성
새 C++ 클래스 생성 - CSubAction - 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으로 작업한다.
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::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");
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)
{
}
※참고: TimelineComponent.h
BS_Aiming 생성
애니메이션 - 블래드 스페이스 1D - Skeleton_ElvenBow 선택 - BS_Aiming 생성
Axis Settings
- 가로 축
- Name: Blend
- Minimum Axis Value: 0.0
- Maximum Axis Value: 100.0
- Number of Grid Divisions: 1
Curve_Aiming 생성
기타 - 커브 - Curve_Aiming 생성
Y는 활의 휘는 정도를 조절하는 값이다.
DA_Bow 생성
실행화면
클래스 지정자
Minimal API
private vs Minimal
- private은 컴파일러 자체에서 숨겨버린다.
- Minimal은 직렬화만 못하게 막아준다. 외부 노출을 최소화하여 속도를 높인다.
- 형 변환이 가능하지만, (inline 함수를 제외하고) 호출할 수 없다.
- .cpp 다 무시. .h에서 컴파일되는 inline 함수는 컴파일된다.
Minimal API 사용
class A : Character
{
void Add() { }
inline void Add() { }
}
A* ob = new A();
ob->Add(); 가능
Character* a = Cast<ACharacter>(ob);
ob->Add(); 사용 불가능.
단, inline함수로 선언된 경우 사용 가능하다. inline은 헤더 기준으로 컴파일 된다고 봐도 무방하다.
Transient
'⭐ Unreal Engine > UE RPG Skill' 카테고리의 다른 글
[UE] 활 시위 당기기 (0) | 2023.07.07 |
---|---|
[UE] 활 조준 (0) | 2023.07.06 |
[UE] Around 스킬 구현하기 (0) | 2023.07.04 |
[UE] Around 공격 구현하기 (0) | 2023.07.03 |
[UE] 워프 Top View 만들기 (0) | 2023.06.30 |
댓글
이 글 공유하기
다른 글
-
[UE] 활 시위 당기기
[UE] 활 시위 당기기
2023.07.07 -
[UE] 활 조준
[UE] 활 조준
2023.07.06 -
[UE] Around 스킬 구현하기
[UE] Around 스킬 구현하기
2023.07.04 -
[UE] Around 공격 구현하기
[UE] Around 공격 구현하기
2023.07.03