[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
댓글을 사용할 수 없습니다.