초보 코린이의 성장 일지
UE4 SubAction Around Skill 본문
플레이어 주변을 회전하는 스킬을 만들어 볼 것이다.
1. 먼저 DataAsset를 생성해준다.
2. Attach은 사용하지 않을 것이다.
1. Action을 할 수 있도록 DoAction을 상속받아 생성해준다.
#pragma once
#include "CoreMinimal.h"
#include "Weapons/CDoAction.h"
#include "CDoAction_Around.generated.h"
UCLASS(Blueprintable)
class U2212_06_API UCDoAction_Around : public UCDoAction
{
GENERATED_BODY()
public:
void DoAction() override;
void Begin_DoAction() override;
};
#include "Weapons/DoActions/CDoAction_Around.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "Components/CStateComponent.h"
void UCDoAction_Around::DoAction()
{
// 체크
CheckFalse(DoActionDatas.Num() > 0);
CheckFalse(State->IsIdleMode());
Super::DoAction();
// 데이터 받기.
DoActionDatas[0].DoAction(OwnerCharacter);
}
void UCDoAction_Around::Begin_DoAction()
{
Super::Begin_DoAction();
}
1. 데이터만 받아놓는다.
1. BP로 생성해준다.
1. DoAction 넣어주고, 시점 고정 없이 돌아다닐 수 있도록 Use Control은 꺼준다.
1. 사용할 망타주에 노티파이를 설정해준다.
1. 다시 돌아와서 몽타주를 넣어주고, 각 설정에 알맞은 수치를 넣어준다.
#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/CWeaponComponent.h"
#include "Components/CMontagesComponent.h"
#include "Components/CMovementComponent.h"
void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAction("Around", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetAroundMode);
}
1. Player로가서 Around 버튼을 등록해준다.
1. Player로가서 Weapon에 Around를 넣어준다.
1. 몽타주가 실행되는걸 확인할 수 있다.
1. Around 스킬로 생성하게될 객체를 Actor로 만들어준다.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Weapons/CWeaponStructures.h"
#include "CRotate_Object.generated.h"
UCLASS()
class U2212_06_API ACRotate_Object : public AActor
{
GENERATED_BODY()
private:
UPROPERTY(EditDefaultsOnly, Category = "Damage")
FHitData HitData;
private:
UPROPERTY(EditDefaultsOnly, Category = "Spawn")
float Speed = 300; // 돌아가는 속도
UPROPERTY(EditDefaultsOnly, Category = "Spawn")
float Distance = 150; // 중심으로부터 거리
UPROPERTY(EditDefaultsOnly, Category = "Spawn")
bool bNegative; // 시계방향인지, 반시계방향인지
UPROPERTY(EditDefaultsOnly, Category = "Spawn")
float DamageInteval = 0.1f; // 데미지 들어갈 간격
private:
UPROPERTY(VisibleDefaultsOnly)
class UCapsuleComponent* Capsule;
UPROPERTY(VisibleDefaultsOnly)
class UParticleSystemComponent* Particle;
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);
UFUNCTION()
// 데미지 보내주는 이벤트
void SendDamage();
public:
ACRotate_Object();
protected:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason); // 타이머 제거용
public:
virtual void Tick(float DeltaTime) override;
private:
float Angle; // 현재 돌아갈 각도
TArray<ACharacter*> Hitted; // 데미지 받은 객체
FTimerHandle TimerHandle; // 타이머
};
#include "Weapons/AddOns/CRotate_Object.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "Components/CapsuleComponent.h"
#include "Particles/ParticleSystemComponent.h"
ACRotate_Object::ACRotate_Object()
{
PrimaryActorTick.bCanEverTick = true;
// 생성
CHelpers::CreateComponent<UCapsuleComponent>(this, &Capsule, "Capsule");
CHelpers::CreateComponent<UParticleSystemComponent>(this, &Particle, "Particle", Capsule);
// 모양 : 구형태
Capsule->SetCapsuleHalfHeight(44);
Capsule->SetCapsuleRadius(44);
// 초기값들
InitialLifeSpan = 5;
HitData.Launch = 0;
HitData.Power = 5;
// HitData에 대한 몽타주 초기값 설정,
CHelpers::GetAsset<UAnimMontage>(&HitData.Montage, "AnimMontage'/Game/Character/Montages/HitReaction_Montage.HitReaction_Montage'");
}
void ACRotate_Object::BeginPlay()
{
Super::BeginPlay();
// 맨 앞에서 나타나지 않도록, Angle 랜덤으로 설정
Angle = UKismetMathLibrary::RandomFloatInRange(0, 360);
// 이벤트 연결
Capsule->OnComponentBeginOverlap.AddDynamic(this, &ACRotate_Object::OnComponentBeginOverlap);
Capsule->OnComponentEndOverlap.AddDynamic(this, &ACRotate_Object::OnComponentEndOverlap);
// 타이머 이벤트 연결
GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &ACRotate_Object::SendDamage, DamageInteval, true);
}
void ACRotate_Object::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
// 타이머 제거
GetWorld()->GetTimerManager().ClearTimer(TimerHandle);
}
void ACRotate_Object::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 스킬 나갈 시작지점
FVector location = GetOwner()->GetActorLocation();
Angle += (bNegative ? -Speed : +Speed) * DeltaTime; // 더해질 수치
// 양방향 360 수치에 도달하고 수치들이 중첩되면, 짐벌락이 생길 가능성이 높아지므로, 0으로 초기화 시켜주기
if (FMath::IsNearlyEqual(Angle, bNegative ? -360 : +360))
Angle = 0;
// 거리 만들어주기
FVector distance = FVector(Distance, 0, 0);
// 회전값 만들어주기, UpVector인 이유는 위에 Vector 기준으로 Angle을 돌게 하기 위해.
FVector value = distance.RotateAngleAxis(Angle, FVector::UpVector);
location += value; // 현재위치에 + 해주면 수치만큼 이동한다.
// 세팅값 넣어주기
SetActorLocation(location);
SetActorRotation(FRotator(0, Angle, 0)); // 캐릭터 주변으로 돌것이므로, Yaw만 사용.
}
void ACRotate_Object::OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
CheckTrue(GetOwner() == OtherActor);
ACharacter* character = Cast<ACharacter>(OtherActor);
if (!!character)
Hitted.AddUnique(character); // Hitted되면 리스트에 넣기.
}
void ACRotate_Object::OnComponentEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
CheckTrue(GetOwner() == OtherActor);
ACharacter* character = Cast<ACharacter>(OtherActor);
if (!!character)
Hitted.Remove(character);
}
void ACRotate_Object::SendDamage()
{
// 타이머에 의해서 계속 데미지 누적시키기.
for (int32 i = Hitted.Num() - 1; i >= 0; i--)
HitData.SendDamage(Cast<ACharacter>(GetOwner()), this, Hitted[i]);
}
1. 기본 초기값을 설정해주고, Hit 몽타주 또한 초기값 세팅을 해준다.
2. 플레이어 주변을 돌게 설정할 것이므로, 시작지점을 중심으로 일정 거리만큼 Spawn되서 돌아갈 수 있도록 설정.
#pragma once
#include "CoreMinimal.h"
#include "Weapons/CDoAction.h"
#include "CDoAction_Around.generated.h"
UCLASS(Blueprintable)
class U2212_06_API UCDoAction_Around : public UCDoAction
{
GENERATED_BODY()
private:
// 나갈 Around 스킬이 여러개이므로, 배열
UPROPERTY(EditAnywhere, Category = "SpawnClass")
TArray<TSubclassOf<class ACRotate_Object>> RotateClasses;
public:
void DoAction() override;
void Begin_DoAction() override;
};
#include "Weapons/DoActions/CDoAction_Around.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "Components/CStateComponent.h"
void UCDoAction_Around::DoAction()
{
// 체크
CheckFalse(DoActionDatas.Num() > 0);
CheckFalse(State->IsIdleMode());
Super::DoAction();
// 데이터 받기.
DoActionDatas[0].DoAction(OwnerCharacter);
}
void UCDoAction_Around::Begin_DoAction()
{
Super::Begin_DoAction();
// 랜덤 인덱스
int32 index = UKismetMathLibrary::RandomIntegerInRange(0, RotateClasses.Num() - 1);
// 랜덤 인덱스로 객체 Spawn,
FActorSpawnParameters params;
params.Owner = OwnerCharacter;
params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
OwnerCharacter->GetWorld()->SpawnActor<ACRotate_Object>(RotateClasses[index], params);
}
1. Spawn 스킬이 여러개가 될 것이므로, 배열로 생성해준다.
2. Index 랜덤으로 돌려주면서 Spawn될 수 있도록 설정.
1. Spawn되서 캐릭터 주변을 돌게될 스킬 생성.
2. Particles 설정해주기.
1. 위에서 만들어 준 Rotate 복사해주고, Particles을 Ice 스킬로 바꿔준다.
2. Fire와 다르게 수치를 변경해주고, Inteval을 0.2 간격으로 데미지 들어가도록 설정.
1. 만들어준 스킬 등록해준다.
https://www.youtube.com/watch?v=mBZHX_SLrIw
'언리얼' 카테고리의 다른 글
UE4 Weapon Bow (0) | 2023.07.05 |
---|---|
UE4 SubAction Around Skill (0) | 2023.07.04 |
UE4 SubAction Warp, Top View (0) | 2023.06.30 |
UE4 SubAction Warp (0) | 2023.06.29 |
UE4 SubAction Hammer Skill (0) | 2023.06.27 |