기본값을 상속받아서 내가 정하고 싶다면 해도 되고

기본 값을 굳이 정하지 않아도 될 때. 블프로

UI도 블프로 해서. C로 가져올 수도 있지만. C로 만으로도 만들 수 있다.(UI디자이너들을 위해서 만드는 방식)

MatineeCameraShake 를 사용

Legacy가 들어가는 것은 빠진다는 것( 언리얼5 에서 빠짐)

Sequence Camera 사용

Untitled

Untitled

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로 먼저 생성하고. 추후 블프로 위젯을 만들어줘서 부모를 할당하는 방식으로 진행할 수 있다.

Untitled

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;
};