UI 에 따른 연발 모드 실행 및 발사
CRifle.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/TimelineComponent.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")
float LimitPitchAngle = 0.25f; //0.25 가 45도
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 = "Fire")
float AutoFireInterval = 0.1f;
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();
private:
UFUNCTION()
void Fire();
public:
void Begin_Fire();
void End_Fire();
void Toggle_AutoFire();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
private:
class ACharacter* OwnerCharacter;
private:
bool bEquipping;
bool bEquipped;
bool bAiming;
bool bFiring;
float CurrPitchAngle; //발사시 반동 재현
private:
class UCUserWidget_AutoFire* AutoFireWidget;
private:
FTimerHandle AutoFirehandle;
};
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"
#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);
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;
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;
AutoFireWidget->SetVisibility(ESlateVisibility::Hidden);
}
void ACRifle::End_Unequip()
{
bEquipping = false;
}
void ACRifle::Fire()
{
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");
//pitchAngle
{
CurrPitchAngle -= PitchAngle * GetWorld()->GetDeltaSeconds();
//마우스가 뒤집어져 있어서, 마이너스 값을 준다.
if (CurrPitchAngle > -LimitPitchAngle)
OwnerCharacter->AddControllerPitchInput(CurrPitchAngle);
}
//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()
{
CheckTrue(bEquipping);
CheckFalse(bEquipped);
CheckTrue(bFiring);
bFiring = true;
CurrPitchAngle = 0;
if (AutoFireWidget->GetOn())
{
GetWorld()->GetTimerManager().SetTimer(AutoFirehandle, this, &ACRifle::Fire, AutoFireInterval, true); //뭔가 델리게이션 모양
return;
}
Fire();
}
void ACRifle::End_Fire()
{
if (AutoFireWidget->GetOn())
{
if (GetWorld()->GetTimerManager().IsTimerActive(AutoFirehandle))
GetWorld()->GetTimerManager().ClearTimer(AutoFirehandle);
// 어디에 들어오든 해지가 되는
}
bFiring = false;
}
//타이머가 끝나고 나서. 없어지는
void ACRifle::Toggle_AutoFire()
{
AutoFireWidget->Toggle();
if (bFiring && AutoFireWidget->GetOn() == false)
End_Fire();
}
CPlayer.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "05_TPS/IRifle.h"
#include "CPlayer.generated.h"
UCLASS()
class U2110_03_API ACPlayer
: public ACharacter
, public IIRifle
{
GENERATED_BODY()
private:
UPROPERTY(EditDefaultsOnly, Category = "Rifle")
TSubclassOf<class ACRifle> RifleClass;
private:
UPROPERTY(VisibleDefaultsOnly)
class USpringArmComponent* SpringArm;
UPROPERTY(VisibleDefaultsOnly)
class UCameraComponent* Camera;
public:
ACPlayer();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
public:
virtual bool IsEquipped_Rifle() override;
virtual bool IsAiming_Rifle() override;
virtual void Begin_Equip_Rifle() override;
virtual void End_Equip_Rifle() override;
virtual void Begin_Unequip_Rifle() override;
virtual void End_Unequip_Rifle() override;
//virtual void Fire_Rifle() override;
private:
void OnMoveForward(float InAxisValue);
void OnMoveRight(float InAxisValue);
void OnVerticalLook(float InAxisValue);
void OnHorizontalLook(float InAxisValue);
public:
void OnRun();
void OffRun();
void OnRifle();
void OnSubAction();
void OffSubAction();
void OnFire();
void OffFire();
void OnAutoFire();
public:
UFUNCTION(BlueprintCallable, Category = "Color")
void ChangeColor(FLinearColor InColor);
private:
class UMaterialInstanceDynamic* Materials[2];
private:
class ACRifle* Rifle;
};
CPlayer.cpp
#include "CPlayer.h"
#include "Global.h"
#include "05_TPS/CRifle.h"
#include "CAnimInstance.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/InputComponent.h"
#include "Materials/MaterialInstanceDynamic.h"
ACPlayer::ACPlayer()
{
PrimaryActorTick.bCanEverTick = true;
CHelpers::CreateComponent<USpringArmComponent>(this, &SpringArm, "SpringArm", GetCapsuleComponent());
CHelpers::CreateComponent<UCameraComponent>(this, &Camera, "Camera", SpringArm);
bUseControllerRotationYaw = false;
GetCharacterMovement()->bOrientRotationToMovement = true;
GetCharacterMovement()->MaxWalkSpeed = 400;
USkeletalMesh* mesh;
CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character/Mesh/SK_Mannequin.SK_Mannequin'");
GetMesh()->SetSkeletalMesh(mesh);
GetMesh()->SetRelativeLocation(FVector(0, 0, -90));
GetMesh()->SetRelativeRotation(FRotator(0, -90, 0));
TSubclassOf<UCAnimInstance> animInstance;
CHelpers::GetClass<UCAnimInstance>(&animInstance, "AnimBlueprint'/Game/ABP_Character.ABP_Character_C'");
GetMesh()->SetAnimClass(animInstance);
SpringArm->SetRelativeLocation(FVector(0, 0, 60));
SpringArm->TargetArmLength = 200;
SpringArm->bDoCollisionTest = false;
SpringArm->bUsePawnControlRotation = true;
SpringArm->SocketOffset = FVector(0, 60, 0);
SpringArm->bEnableCameraLag = true;
RifleClass = ACRifle::StaticClass();
}
void ACPlayer::BeginPlay()
{
Super::BeginPlay();
TArray<UMaterialInterface*> materials = GetMesh()->GetMaterials();
for (int32 i = 0; i < materials.Num(); i++)
{
Materials[i] = UMaterialInstanceDynamic::Create(materials[i], this);
GetMesh()->SetMaterial(i, Materials[i]);
}
Rifle = ACRifle::Spawn(RifleClass, this);
}
void ACPlayer::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis("MoveForward", this, &ACPlayer::OnMoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &ACPlayer::OnMoveRight);
PlayerInputComponent->BindAxis("VerticalLook", this, &ACPlayer::OnVerticalLook);
PlayerInputComponent->BindAxis("HorizontalLook", this, &ACPlayer::OnHorizontalLook);
PlayerInputComponent->BindAction("Run", EInputEvent::IE_Pressed, this, &ACPlayer::OnRun);
PlayerInputComponent->BindAction("Run", EInputEvent::IE_Released, this, &ACPlayer::OffRun);
PlayerInputComponent->BindAction("Rifle", EInputEvent::IE_Pressed, this, &ACPlayer::OnRifle);
PlayerInputComponent->BindAction("SubAction", EInputEvent::IE_Pressed, this, &ACPlayer::OnSubAction);
PlayerInputComponent->BindAction("SubAction", EInputEvent::IE_Released, this, &ACPlayer::OffSubAction);
PlayerInputComponent->BindAction("Fire", EInputEvent::IE_Pressed, this, &ACPlayer::OnFire);
PlayerInputComponent->BindAction("Fire", EInputEvent::IE_Released, this, &ACPlayer::OffFire);
PlayerInputComponent->BindAction("AutoFire", EInputEvent::IE_Pressed, this, &ACPlayer::OnAutoFire);
}
void ACPlayer::OnMoveForward(float InAxisValue)
{
FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
FVector direction = FQuat(rotator).GetForwardVector().GetSafeNormal2D();
AddMovementInput(direction, InAxisValue);
}
void ACPlayer::OnMoveRight(float InAxisValue)
{
FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
FVector direction = FQuat(rotator).GetRightVector().GetSafeNormal2D();
AddMovementInput(direction, InAxisValue);
}
void ACPlayer::OnVerticalLook(float InAxisValue)
{
AddControllerPitchInput(InAxisValue);
}
void ACPlayer::OnHorizontalLook(float InAxisValue)
{
AddControllerYawInput(InAxisValue);
}
void ACPlayer::OnRun()
{
GetCharacterMovement()->MaxWalkSpeed = 600;
}
void ACPlayer::OffRun()
{
GetCharacterMovement()->MaxWalkSpeed = 400;
}
void ACPlayer::OnRifle()
{
Rifle->Equip();
}
void ACPlayer::ChangeColor(FLinearColor InColor)
{
for (UMaterialInstanceDynamic* material : Materials)
material->SetVectorParameterValue("BodyColor", InColor);
}
void ACPlayer::OnSubAction()
{
}
void ACPlayer::OffSubAction()
{
}
void ACPlayer::Begin_Equip_Rifle()
{
Rifle->Begin_Equip();
}
void ACPlayer::End_Equip_Rifle()
{
Rifle->End_Equip();
}
void ACPlayer::Begin_Unequip_Rifle()
{
Rifle->Begin_Unequip();
}
void ACPlayer::End_Unequip_Rifle()
{
Rifle->End_Unequip();
}
bool ACPlayer::IsEquipped_Rifle()
{
return Rifle->IsEquipped();
}
bool ACPlayer::IsAiming_Rifle()
{
return Rifle->IsAiming();
}
//void ACPlayer::Fire_Rifle()
//{
// Rifle->Begin_Fire();
//}
void ACPlayer::OnFire()
{
Rifle->Begin_Fire();
}
//타임델리게이트 이용해서 지난 시간마다. 샷을 만든다?
void ACPlayer::OffFire()
{
Rifle->End_Fire();
}
void ACPlayer::OnAutoFire()
{
Rifle->Toggle_AutoFire();
}
연발 모드가 시간마다 콜해주는 ?
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(EditDefaultsOnly, Category = "Impact")
class UMaterialInstanceConstant* Decal;
UPROPERTY(EditDefaultsOnly, Category = "Impact")
class UParticleSystem* Particle;
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);
//맞았을 때 상황을
};
CBullet.cpp
#include "CBullet.h"
#include "Global.h"
#include "Components/CapsuleComponent.h"
#include "Components/StaticMeshComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Materials/MaterialInstanceConstant.h"
#include "Components/DecalComponent.h"
#include "Particles/ParticleSystem.h"
ACBullet::ACBullet()
{
CHelpers::CreateComponent<UCapsuleComponent>(this, &Capsule, "Capsule");
CHelpers::CreateComponent<UStaticMeshComponent>(this, &Mesh, "Mesh", Capsule);
CHelpers::CreateActorComponent<UProjectileMovementComponent>(this, &Projectile, "Projectile");
Capsule->SetRelativeRotation(FRotator(90, 0, 0));
Capsule->SetCapsuleHalfHeight(50);
Capsule->SetCapsuleRadius(2);
Capsule->SetCollisionProfileName("BlockAllDynamic");
UStaticMesh* mesh;
CHelpers::GetAsset<UStaticMesh>(&mesh, "StaticMesh'/Game/Meshes/Sphere.Sphere'");
Mesh->SetStaticMesh(mesh);
Mesh->SetRelativeScale3D(FVector(1, 0.025f, 0.05f));
Mesh->SetRelativeRotation(FRotator(90, 0, 0));
Projectile->InitialSpeed = 2e+4f; //2 * 10 ^ 4
Projectile->MaxSpeed = 2e+4f; //2 * 10 ^ 4
Projectile->ProjectileGravityScale = 0;
CHelpers::GetAsset<UMaterialInstanceConstant>(&Decal, "MaterialInstanceConstant'/Game/Weapons/M_Decal_Inst.M_Decal_Inst'");
CHelpers::GetAsset<UParticleSystem>(&Particle, "ParticleSystem'/Game/Weapons/Particle/VFX_Impact_Default.VFX_Impact_Default'");
}
void ACBullet::BeginPlay()
{
Super::BeginPlay();
Capsule->OnComponentHit.AddDynamic(this, &ACBullet::OnHit);
}
void ACBullet::Shoot(const FVector& InDirection)
{
Projectile->Velocity = InDirection * Projectile->InitialSpeed;
}
//히트가 되었을 때 decal 생성
void ACBullet::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
FRotator rotation = Hit.ImpactNormal.Rotation(); //회전값을 구해주고
if (!!Decal)
{
UDecalComponent* decal = UGameplayStatics::SpawnDecalAtLocation(GetWorld(), Decal, FVector(5), Hit.Location, rotation, 10);
decal->SetFadeScreenSize(0);
}
if (!!Particle)
{
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), Particle, Hit.Location, rotation);
}
Destroy();
}