기본값을 상속받아서 내가 정하고 싶다면 해도 되고
기본 값을 굳이 정하지 않아도 될 때. 블프로
UI도 블프로 해서. C로 가져올 수도 있지만. C로 만으로도 만들 수 있다.(UI디자이너들을 위해서 만드는 방식)
MatineeCameraShake 를 사용
Legacy가 들어가는 것은 빠진다는 것( 언리얼5 에서 빠짐)
Sequence Camera 사용
CRifle.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CRifle.generated.h"
UCLASS()
class U2110_03_API ACRifle : public AActor
{
GENERATED_BODY()
private:
UPROPERTY(EditDefaultsOnly, Category = "Socket")
FName HolsterSocket = "Holster_Rifle";
UPROPERTY(EditDefaultsOnly, Category = "Socket")
FName HandSocket = "Hand_Rifle";
UPROPERTY(EditDefaultsOnly, Category = "Montage")
class UAnimMontage* GrabMontage;
UPROPERTY(EditDefaultsOnly, Category = "Montage")
class UAnimMontage* UngrabMontage;
UPROPERTY(EditDefaultsOnly, Category = "Montage_Play")
float GrabMontageSpeed = 3;
UPROPERTY(EditDefaultsOnly, Category = "Montage_Play")
float UngrabMontageSpeed = 3;
UPROPERTY(EditDefaultsOnly, Category = "Fire")
class UAnimMontage* FireMontage;
UPROPERTY(EditDefaultsOnly, Category = "Fire")
TSubclassOf<class ACBullet> BulletClass;
UPROPERTY(EditDefaultsOnly, Category = "Fire")
float PitchAngle = 0.25f;
UPROPERTY(EditDefaultsOnly, Category = "Fire")
TSubclassOf<class UMatineeCameraShake> CameraShakeClass;
UPROPERTY(EditDefaultsOnly, Category = "Fire")
class USoundWave* Sound;
UPROPERTY(EditDefaultsOnly, Category = "Fire")
class UParticleSystem* FlashParticle;
UPROPERTY(EditDefaultsOnly, Category = "Fire")
class UParticleSystem* EjectParticle;
//위젯 스폰
UPROPERTY(EditDefaultsOnly, Category = "Fire")
TSubclassOf<class UCUserWidget_AutoFire> AutoFireWidgetClass;
//플레이 위젯 하면, 자료형이 생긴다. 밑에 멤버에 저장
UPROPERTY(EditDefaultsOnly, Category = "Trace")
float AimDistance = 3000.0f;
protected:
UPROPERTY(BlueprintReadOnly, VisibleDefaultsOnly)
class USkeletalMeshComponent* Mesh;
public:
FORCEINLINE bool IsEquipped() { return bEquipped; }
FORCEINLINE bool IsAiming() { return bAiming; }
public:
static ACRifle* Spawn(TSubclassOf<ACRifle> RifleClass, class ACharacter* InOwner);
public:
ACRifle();
public:
void Equip();
void Begin_Equip();
void End_Equip();
private:
void Unequip();
public:
void Begin_Unequip();
void End_Unequip();
public:
void Fire();
void Begin_Fire();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
private:
class ACharacter* OwnerCharacter;
private:
bool bEquipping;
bool bEquipped;
bool bAiming;
private: //멤버에 저장 하기
class UCUserWidget_AutoFire* AutoFireWidget;
};
CRifle.cpp
#include "CRifle.h"
#include "Global.h"
#include "CBullet.h"
#include "CUserWidget_AutoFire.h"
#include "Animation/AnimMontage.h"
#include "Components/SkeletalMeshComponent.h"
#include "GameFramework/Character.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/PlayerController.h" //CreatWidget의 Controller가 인식을 못하게 된다.
#include "Camera/CameraComponent.h" // 이 헤더에 카메라 쉐이크 있음
#include "Sound/SoundWave.h"
#include "Particles/ParticleSystem.h"
ACRifle::ACRifle()
{
PrimaryActorTick.bCanEverTick = true;
CHelpers::CreateComponent<USkeletalMeshComponent>(this, &Mesh, "Mesh");
USkeletalMesh* mesh;
CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Weapons/Meshes/SK_AR4.SK_AR4'");
Mesh->SetSkeletalMesh(mesh);
CHelpers::GetAsset<UAnimMontage>(&GrabMontage, "AnimMontage'/Game/Character/Montages/Rifle/Rifle_Equip_Montage.Rifle_Equip_Montage'");
CHelpers::GetAsset<UAnimMontage>(&UngrabMontage, "AnimMontage'/Game/Character/Montages/Rifle/Rifle_Unequip_Montage.Rifle_Unequip_Montage'");
CHelpers::GetAsset<UAnimMontage>(&FireMontage, "AnimMontage'/Game/Character/Montages/Rifle/Rifle_Equip_Fire_Montage.Rifle_Equip_Fire_Montage'");
CHelpers::GetClass<ACBullet>(&BulletClass, "Blueprint'/Game/05_TPS/BP_CBullet.BP_CBullet_C'");
CHelpers::GetClass<UMatineeCameraShake>(&CameraShakeClass, "Blueprint'/Game/05_TPS/BP_CameraShake.BP_CameraShake_C'");
CHelpers::GetAsset<USoundWave>(&Sound, "SoundWave'/Game/Weapons/S_RifleShoot.S_RifleShoot'");
CHelpers::GetAsset<UParticleSystem>(&FlashParticle, "ParticleSystem'/Game/Weapons/Particle/VFX_Muzzleflash.VFX_Muzzleflash'");
CHelpers::GetAsset<UParticleSystem>(&EjectParticle, "ParticleSystem'/Game/Weapons/Particle/VFX_Eject_bullet.VFX_Eject_bullet'");
CHelpers::GetClass<UCUserWidget_AutoFire>(&AutoFireWidgetClass, "WidgetBlueprint'/Game/05_TPS/WB_CAutoFire.WB_CAutoFire_C'");
}
ACRifle* ACRifle::Spawn(TSubclassOf<ACRifle> RifleClass, class ACharacter* InOwner)
{
FActorSpawnParameters params;
params.Owner = InOwner;
return InOwner->GetWorld()->SpawnActor<ACRifle>(RifleClass, params);
}
void ACRifle::BeginPlay()
{
Super::BeginPlay();
OwnerCharacter = Cast<ACharacter>(GetOwner());
AttachToComponent(OwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), HolsterSocket);
//위젯은 BeginPlay에서 실행
if (!!AutoFireWidgetClass)
{
AutoFireWidget = CreateWidget<UCUserWidget_AutoFire, APlayerController>
(
OwnerCharacter->GetController<APlayerController>(),
AutoFireWidgetClass
);
AutoFireWidget->AddToViewport();
AutoFireWidget->SetVisibility(ESlateVisibility::Hidden);
}
}
void ACRifle::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ACRifle::Equip()
{
CheckTrue(bEquipping);
bEquipping = true;
if (bEquipped)
{
Unequip();
return;
}
OwnerCharacter->PlayAnimMontage(GrabMontage, GrabMontageSpeed);
}
void ACRifle::Begin_Equip()
{
AttachToComponent(OwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), HandSocket);
bEquipped = true;
OwnerCharacter->bUseControllerRotationYaw = true;
OwnerCharacter->GetCharacterMovement()->bOrientRotationToMovement = false;
//equip 될 때
AutoFireWidget->SetVisibility(ESlateVisibility::Visible);
}
void ACRifle::End_Equip()
{
bEquipping = false;
}
void ACRifle::Unequip()
{
OwnerCharacter->PlayAnimMontage(UngrabMontage, UngrabMontageSpeed);
}
void ACRifle::Begin_Unequip()
{
AttachToComponent(OwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), HolsterSocket);
bEquipped = false;
OwnerCharacter->bUseControllerRotationYaw = false;
OwnerCharacter->GetCharacterMovement()->bOrientRotationToMovement = true;
//Unequip될 때
AutoFireWidget->SetVisibility(ESlateVisibility::Hidden);
}
void ACRifle::End_Unequip()
{
bEquipping = false;
}
void ACRifle::Fire()
{
CheckTrue(bEquipping);
CheckFalse(bEquipped);
UCameraComponent* camera = CHelpers::GetComponent<UCameraComponent>(OwnerCharacter);
FVector direction = camera->GetForwardVector();
direction = UKismetMathLibrary::RandomUnitVectorInConeInDegrees(direction, PitchAngle);
FTransform transform = camera->GetComponentToWorld();
FVector start = transform.GetLocation() + direction;
FVector end = transform.GetLocation() + direction * AimDistance;
//DrawDebugLine(GetWorld(), start, end, FColor::Green, true, 5);
FVector muzzleLocation = Mesh->GetSocketLocation("MuzzleFlash");
if(!!CameraShakeClass) //적에게 줄 때는 따로 분리해줘야 한다.
OwnerCharacter->GetController<APlayerController>()->PlayerCameraManager->StartCameraShake(CameraShakeClass);
if (!!Sound)
UGameplayStatics::PlaySoundAtLocation(GetWorld(), Sound, muzzleLocation);
if (!!FlashParticle)
UGameplayStatics::SpawnEmitterAttached(FlashParticle, Mesh, "MuzzleFlash");
if (!!EjectParticle)
UGameplayStatics::SpawnEmitterAttached(EjectParticle, Mesh, "EjectBullet");
//Spawn Bullet
{
FVector spawnLocation = muzzleLocation + direction * 50;
FActorSpawnParameters params;
params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
ACBullet* bullet = GetWorld()->SpawnActor<ACBullet>(BulletClass, spawnLocation, direction.Rotation());
bullet->Shoot(direction);
}
}
void ACRifle::Begin_Fire()
{
}
CBullet.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CBullet.generated.h"
UCLASS()
class U2110_03_API ACBullet : public AActor
{
GENERATED_BODY()
private:
UPROPERTY(VisibleDefaultsOnly)
class UCapsuleComponent* Capsule;
UPROPERTY(VisibleDefaultsOnly)
class UStaticMeshComponent* Mesh;
UPROPERTY(VisibleDefaultsOnly)
class UProjectileMovementComponent* Projectile;
public:
ACBullet();
void Shoot(const FVector& InDirection);
protected:
virtual void BeginPlay() override;
private:
UFUNCTION()
void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
};
위젯 클래스 를 C로 먼저 생성하고. 추후 블프로 위젯을 만들어줘서 부모를 할당하는 방식으로 진행할 수 있다.
CUserWidget.h
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "CUserWidget_AutoFire.generated.h"
UCLASS()
class U2110_03_API UCUserWidget_AutoFire : public UUserWidget
{
GENERATED_BODY()
//디자이너가 On / Off 에따라 디자인 할 것
protected: // 블루프린트에서 사용하기 위함
UFUNCTION(BlueprintImplementableEvent) //UI디자이너에게 재정의 하게끔 할려고, 알아서 하세요 해서 이걸 넣어준다.
void On_AutoFire();
UFUNCTION(BlueprintImplementableEvent)
void Off_AutoFire();
public:
bool Toggle(); //toggle등을 만들어서 사용하면 간단
private:
bool bOn;
};