[UE] 화살
화살을 생성하여 발사하여 데미지를 전달한다. 활을 장착(=Equip) 시에 화살을 생성하고 장착 해제(=Unequp) 시에 화살을 소멸시켜준다. 조준 후 화살을 발사하면 Projectile 값이 1000.0으로 적용되어 날라가고 충돌이 켜진다. 발사 후에는 Owner(여기서는 플레이어)의 손에 화살을 생성하여 붙여준다.
목차
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 CAnimNotifyState_BowString.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 | ||
CArrow.h .cpp 생성 CAura.h .cpp CCamerModifier.h .cpp CGhostTrail.h .cpp CRotate_Object.h .cpp CThornObject.h .cpp CAnimInstance_Bow.h .cpp CAttachment_Bow.h .cpp CDoAction_Around.h .cpp CDoAction_Bow.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 | ||
조준 - 위아래 조준하기
ABP_Character
AO_Aim를 추가한다.
실행화면
화살 구현하기
CArrow 생성
새 C++ 클래스 - AActor - CArrow 생성
CArrow.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CArrow.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FProjectileHit, class AActor*, InCauser, class ACharacter*, InOtherCharacter);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FProjectileEndPlay, class ACArrow*, InDestroyer);
UCLASS()
class U2212_06_API ACArrow : public AActor
{
GENERATED_BODY()
private:
UPROPERTY(EditDefaultsOnly, Category = "LifeSpan")
float LifeSpanAfterCollision = 1;//충돌 후 몇 초 후에 사라지는지
UPROPERTY(VisibleDefaultsOnly)
class UCapsuleComponent* Capsule;//충돌체
UPROPERTY(VisibleDefaultsOnly)
class UProjectileMovementComponent* Projectile;//Projectile
public:
FORCEINLINE void AddIgnoreActor(AActor* InActor) { Ignores.Add(InActor); }
public:
ACArrow();
protected:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;//공격될 때 화살을 소유하고 있는 목록에서 제거해준다.
public:
void Shoot(const FVector& InForward);
private:
UFUNCTION()
void OnComponentHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
public:
FProjectileHit OnHit;
FProjectileEndPlay OnEndPlay;
private:
TArray<AActor*> Ignores;
};
CArrow.cpp
#include "Weapons/AddOns/CArrow.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Components/CapsuleComponent.h"
ACArrow::ACArrow()
{
CHelpers::CreateComponent<UCapsuleComponent>(this, &Capsule, "Capsule");
CHelpers::CreateActorComponent<UProjectileMovementComponent>(this, &Projectile, "Projectile");
Projectile->ProjectileGravityScale = 0;
Capsule->BodyInstance.bNotifyRigidBodyCollision = true;//BP와는 달리 UnrealC++에서는 bNotifyRigidBodyCollision를 true로 켜줘야 Block 연산이 일어난다.
Capsule->SetCollisionProfileName("BlockAll");//Overlap과는 달리 Block의 경우 충돌이 일어나면 멈춘다. 화살은 적에게 박히면 멈춰야하기 때문에 Block으로 설정한다.
}
void ACArrow::BeginPlay()
{
Super::BeginPlay();
//화살이 Player에게 붙어있어야 하므로 충돌을 끄고 시작한다.
Capsule->SetCollisionEnabled(ECollisionEnabled::NoCollision);
//추후에 화살 충돌이 일어나야 하므로 Delegate 연결을 해준다.
Capsule->OnComponentHit.AddDynamic(this, &ACArrow::OnComponentHit);
Projectile->Deactivate();//포물선 움직임이 필요없는 상태이므로 꺼준다.
}
void ACArrow::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
//소유자 이벤트를 콜해 자기 자신을 지워준다. FProjectileEndPlay OnEndPlay 사용하여 Delegate 이벤트 콜.
if (OnEndPlay.IsBound())
OnEndPlay.Broadcast(this);
}
void ACArrow::Shoot(const FVector& InForward)
{
Projectile->Velocity = InForward * Projectile->InitialSpeed;
Projectile->Activate();
Capsule->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);//Collision을 켜준다.
}
void ACArrow::OnComponentHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
SetLifeSpan(LifeSpanAfterCollision);
for (AActor* actor : Ignores)
CheckTrue(actor == OtherActor);
Capsule->SetCollisionEnabled(ECollisionEnabled::NoCollision);
ACharacter* character = Cast<ACharacter>(OtherActor);
if (!!character && OnHit.IsBound())
OnHit.Broadcast(this, character);
}
BP_CArrow
CArrow 기반 블루프린트 생성 - BP_CArrow 생성
Capsule 사이즈 설정
- Capsule Half Height: 8.0
- Capsule Radius: 8.0
컴포넌트 추가 - StaticMesh
- 위치: -75.0, 0.0, 0.0 설정.
- Static Mesh: SM_ElvenArrow 설정.
StaticMesh
- 콜리전
- 콜리전 프리셋: No Collision
Projectile
- Initial Speed: 1000.0으로 설정.
CDoAction_Bow
CDoAction_Bow.h
#pragma once
#include "CoreMinimal.h"
#include "Weapons/CDoAction.h"
#include "CDoAction_Bow.generated.h"
UCLASS(Blueprintable)
class U2212_06_API UCDoAction_Bow : public UCDoAction
{
GENERATED_BODY()
private:
UPROPERTY(EditDefaultsOnly, Category = "Arrow")
TSubclassOf<class ACArrow> ArrowClass;
public:
UCDoAction_Bow();
void BeginPlay
(
class ACAttachment* InAttachment,
class UCEquipment* InEquipment,
class ACharacter* InOwner,
const TArray<FDoActionData>& InDoActionData,
const TArray<FHitData>& InHitData
) override;
void DoAction() override;
void Begin_DoAction() override;
void End_DoAction() override;
void OnBeginEquip() override;
void OnUnequip() override;
void Tick(float InDeltaTime) override;
public:
void End_BowString();//AnimNotify_BowString의 Notify에서 콜하여 사용한다.
/** 화살 */
private:
void CreateArrow();//화살 생성
class ACArrow* GetAttachedArrow();//활에 붙은 화살을 콜하는 함수.
private:
UFUNCTION()
void OnArrowHit(class AActor* InCauser, class ACharacter* InOtherCharacter);
UFUNCTION()
void OnArrowEndPlay(class ACArrow* InDestroyer);
/** 화살 */
private:
class USkeletalMeshComponent* SkeletalMesh;
class UPoseableMeshComponent* PoseableMesh;
private:
float* Bending;
private:
FVector OriginLocation;//처음 위치
bool bAttachedString = true;//true면 활 시위가 손에 붙는다.
private:
const bool* bEquipped;
private:
TArray<class ACArrow*> Arrows;//화살들(=화살 목록)
};
변수 추가
- UPROPERTY(EditDefaultsOnly, Category = "Arrow")
TSubclassOf<class ACArrow> ArrowClass;
- 화살 클래스인 ACArrow를 받아오는 변수
- TArray<class ACArrow*> Arrows;
- 화살들(=화살 목록)
함수 추가
- void CreateArrow();
- 화살를 생성하는 함수
- UFUNCTION()
void OnArrowHit(class AActor* InCauser, class ACharacter* InOtherCharacter);- 화살 충돌 처리 함수
- UFUNCTION()
void OnArrowEndPlay(class ACArrow* InDestroyer);- 화살 발사 후 함수
CDoAction_Bow.cpp
#include "Weapons/DoActions/CDoAction_Bow.h"
#include "Global.h"
#include "Weapons/CEquipment.h"
#include "Weapons/Attachments/CAttachment_Bow.h"
#include "Weapons/AddOns/CArrow.h"
#include "GameFramework/Character.h"
#include "Components/PoseableMeshComponent.h"
#include "Components/CStateComponent.h"
#include "Components/CMovementComponent.h"
UCDoAction_Bow::UCDoAction_Bow()
{
}
void UCDoAction_Bow::BeginPlay(ACAttachment* InAttachment, UCEquipment* InEquipment, ACharacter* InOwner, const TArray<FDoActionData>& InDoActionData, const TArray<FHitData>& InHitData)
{
Super::BeginPlay(InAttachment, InEquipment, InOwner, InDoActionData, InHitData);
SkeletalMesh = CHelpers::GetComponent<USkeletalMeshComponent>(InAttachment);
PoseableMesh = CHelpers::GetComponent<UPoseableMeshComponent>(InAttachment);
//CAnimInstance.h의 Bend변수를 CAttachment_Bow의 GetBend()함수를 거쳐 여기로 가져와서 사용한다.
ACAttachment_Bow* bow = Cast<ACAttachment_Bow>(InAttachment);
Bending = bow->GetBend();
OriginLocation = PoseableMesh->GetBoneLocationByName("bow_string_mid", EBoneSpaces::ComponentSpace);
bEquipped = InEquipment->GetEquipped();//헤더에 만든 bEquipped변수에 장착 상태를 받아와서 넣어준다.
}
void UCDoAction_Bow::DoAction()
{
CheckFalse(State->IsIdleMode());
CheckFalse(State->IsSubActionMode());
Super::DoAction();
DoActionDatas[0].DoAction(OwnerCharacter);
}
void UCDoAction_Bow::Begin_DoAction()//발사
{
Super::Begin_DoAction();
bAttachedString = false;//발사하면 활 시위가 손에서 떨어지도록 false로 만들어준다.
*Bending = 0;//활 구부림 0으로 만들어준다.
PoseableMesh->SetBoneLocationByName("bow_string_mid", OriginLocation, EBoneSpaces::ComponentSpace);//활 시위를 처음 위치로 돌아가게 만들어준다.
CheckNull(ArrowClass);//ArrowClass 있는지 확인.
ACArrow* arrow = GetAttachedArrow();//Attach되어 있는 화살을 가져온다.
arrow->DetachFromActor(FDetachmentTransformRules(EDetachmentRule::KeepWorld,true));//발사하기 위해 화살을 떼어준다. World 상 기준으로 떼어준다. bInCallModify를 true로 설정.
//이벤트를 연결해준다.
arrow->OnHit.AddDynamic(this, &UCDoAction_Bow::OnArrowHit);
arrow->OnEndPlay.AddDynamic(this, &UCDoAction_Bow::OnArrowEndPlay);
FVector forward = FQuat(OwnerCharacter->GetControlRotation()).GetForwardVector();//방향을 설정한다.
arrow->Shoot(forward);//설정한 방향으로 화살을 발사한다.
}
void UCDoAction_Bow::End_DoAction()
{
Super::End_DoAction();
CreateArrow();//Arrow 생성. 화살을 발사한 후 새로운 화살을 생성한다. 새로 생성된 화살은 손에 붙어있다.
}
void UCDoAction_Bow::OnBeginEquip()
{
Super::OnBeginEquip();
OwnerCharacter->GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision);//충돌을 꺼준다.
CreateArrow();//처음에 화살이 하나 붙어있도록 화살을 생성한다.
}
void UCDoAction_Bow::OnUnequip()
{
Super::OnUnequip();
*Bending = 0;//활 시위 휘어지는 Bending을 0으로 만든다.
OwnerCharacter->GetMesh()->SetCollisionEnabled(ECollisionEnabled::QueryOnly);//충돌을 켜준다.
PoseableMesh->SetBoneLocationByName("bow_string_mid", OriginLocation, EBoneSpaces::ComponentSpace);//활 시위를 처음 상태로 돌려준다.
//활 장착을 해제하면 화살들을 삭제한다.
for (int32 i = Arrows.Num() - 1; i >= 0; i--)
{
if (!!Arrows[i]->GetAttachParentActor())
Arrows[i]->Destroy();
}
}
void UCDoAction_Bow::Tick(float InDeltaTime)
{
Super::Tick(InDeltaTime);
PoseableMesh->CopyPoseFromSkeletalComponent(SkeletalMesh);
bool bCheck = true;
bCheck &= (*bEquipped == true);//장착 상태여야 한다.
bCheck &= (bBeginAction == false);//BeginAction이면(활 발사) 활 시위가 붙으면 안되므로 false여야 한다.
bCheck &= (bAttachedString == true);//활 시위 붙는게 true여야 한다.
CheckFalse(bCheck);
FVector handLocation = OwnerCharacter->GetMesh()->GetSocketLocation("Hand_Bow_Right");
PoseableMesh->SetBoneLocationByName("bow_string_mid", handLocation, EBoneSpaces::WorldSpace);
}
void UCDoAction_Bow::End_BowString()
{
*Bending = 100;//활 시위를 땡겨 다시 휘어지게 만들어진다.
bAttachedString = true;//활 시위가 손에 붙는다.
}
void UCDoAction_Bow::CreateArrow()
{
if (World->bIsTearingDown == true)//World->bIsTearingDown는 World가 종료되었다는 의미
return;
FTransform transform;
ACArrow* arrow = World->SpawnActorDeferred<ACArrow>(ArrowClass, transform, NULL, NULL, ESpawnActorCollisionHandlingMethod::AlwaysSpawn);
CheckNull(arrow);
arrow->AddIgnoreActor(OwnerCharacter);
FAttachmentTransformRules rule = FAttachmentTransformRules(EAttachmentRule::KeepRelative, true);
arrow->AttachToComponent(OwnerCharacter->GetMesh(), rule, "Hand_Bow_Right_Arrow");
Arrows.Add(arrow);
UGameplayStatics::FinishSpawningActor(arrow, transform);
}
ACArrow* UCDoAction_Bow::GetAttachedArrow()
{
for (ACArrow* projectile : Arrows)
{
if (!!projectile->GetAttachParentActor())
return projectile;
}
return nullptr;
}
void UCDoAction_Bow::OnArrowHit(AActor* InCauser, ACharacter* InOtherCharacter)
{
CheckFalse(HitDatas.Num() > 0);
HitDatas[0].SendDamage(OwnerCharacter, InCauser, InOtherCharacter);
}
void UCDoAction_Bow::OnArrowEndPlay(ACArrow* InDestroyer)
{
Arrows.Remove(InDestroyer);
}
헤더 추가
- #include "Weapons/AddOns/CArrow.h"
void UCDoAction_Bow::Begin_DoAction()
- CheckNull(ArrowClass);//ArrowClass 있는지 확인.
ACArrow* arrow = GetAttachedArrow();
arrow->DetachFromActor(FDetachmentTransformRules(EDetachmentRule::KeepWorld,true));
arrow->OnHit.AddDynamic(this, &UCDoAction_Bow::OnArrowHit);
arrow->OnEndPlay.AddDynamic(this, &UCDoAction_Bow::OnArrowEndPlay);
FVector forward = FQuat(OwnerCharacter->GetControlRotation()).GetForwardVector();
arrow->Shoot(forward);
void UCDoAction_Bow::End_DoAction()
- CreateArrow();
- 화살을 발사한 후에 새로운 화살을 생성한다.
void UCDoAction_Bow::OnBeginEquip()
- CreateArrow();
- Bow 장착 시, 처음에 화살이 하나 붙어있도록 화살을 생성한다.
void UCDoAction_Bow::OnUnequip()
- for (int32 i = Arrows.Num() - 1; i >= 0; i--)
{
if (!!Arrows[i]->GetAttachParentActor())
Arrows[i]->Destroy();
}- 무기 장착을 해제하면 화살들을 제거해준다.
void UCDoAction_Bow::CreateArrow()
- if (World->bIsTearingDown == true)
return;
- World->bIsTearingDown는 World가 종료되었다는 의미
- FTransform transform;
ACArrow* arrow = World->SpawnActorDeferred<ACArrow>(ArrowClass, transform, NULL, NULL, ESpawnActorCollisionHandlingMethod::AlwaysSpawn);
CheckNull(arrow);
arrow->AddIgnoreActor(OwnerCharacter);
FAttachmentTransformRules rule = FAttachmentTransformRules(EAttachmentRule::KeepRelative, true);
arrow->AttachToComponent(OwnerCharacter->GetMesh(), rule, "Hand_Bow_Right_Arrow");
Arrows.Add(arrow);
UGameplayStatics::FinishSpawningActor(arrow, transform);
ACArrow* UCDoAction_Bow::GetAttachedArrow()
- for (ACArrow* projectile : Arrows)
if (!!projectile->GetAttachParentActor())
return projectile;
void UCDoAction_Bow::OnArrowHit(AActor* InCauser, ACharacter* InOtherCharacter)
- CheckFalse(HitDatas.Num() > 0);
- HitDatas[0].SendDamage(OwnerCharacter, InCauser, InOtherCharacter);
void UCDoAction_Bow::OnArrowEndPlay(ACArrow* InDestroyer)
- Arrows.Remove(InDestroyer);
※ World가 종료되는 상황은 뭐가 있을까?
- 게임이 종료되었을 때(=에디터가 꺼졌을 때)
- 맵 이동
BP_CDoAction_Bow
CDoAction_Bow기반 블루프린트 생성 - BP_CDoAction_Bow생성
Arrow
- Arrow Class: BP_CArrow 할당
DA_Bow - BP_CDoAction_Bow와 Hit Data 할당
Skel_Mannequin - 소켓 수정
Relative Location: -88,9, 23.0, -11.0
Relative Rotation: -79.3, -9.9, 166.7
Relative Scale: 1.1, 1.0, 1.0
실행화면
'⭐ Unreal Engine > UE RPG Skill' 카테고리의 다른 글
[UE] 파쿠르: 벽 오르기 (0) | 2023.07.19 |
---|---|
[UE] 파쿠르 구현하기 (0) | 2023.07.18 |
[UE] 활 시위에 손 맞추기 (0) | 2023.07.10 |
[UE] 활 시위 당기기 (0) | 2023.07.07 |
[UE] 활 조준 (0) | 2023.07.06 |
댓글
이 글 공유하기
다른 글
-
[UE] 파쿠르: 벽 오르기
[UE] 파쿠르: 벽 오르기
2023.07.19 -
[UE] 파쿠르 구현하기
[UE] 파쿠르 구현하기
2023.07.18 -
[UE] 활 시위에 손 맞추기
[UE] 활 시위에 손 맞추기
2023.07.10 -
[UE] 활 시위 당기기
[UE] 활 시위 당기기
2023.07.07