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);
};
Projectile쓸려면 맨위에가 충돌체가 나와야한다.
CBullet.cpp
#include "CBullet.h"
#include "Global.h"
#include "Components/CapsuleComponent.h"
#include "Components/StaticMeshComponent.h" //충돌체 같이 들어감
#include "GameFramework/ProjectileMovementComponent.h"
#include "Materials/MaterialInstanceConstant.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;
}
void ACBullet::BeginPlay()
{
Super::BeginPlay();
Capsule->OnComponentHit.AddDynamic(this, &ACBullet::OnHit); // 불러들인 자기 충돌에 대해
}
void ACBullet::Shoot(const FVector& InDirection)
{
Projectile->Velocity = InDirection * Projectile->InitialSpeed; //
}
//자기 충돌 제거
void ACBullet::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
Destroy();
}
CHelpers.h
#pragma once
#include "CoreMinimal.h"
#include "Components/SceneComponent.h"
class U2110_03_API CHelpers
{
public:
template<typename T>
static void CreateComponent(AActor* InActor, T** OutComponent, FName InName, USceneComponent* InParent = nullptr, FName InSocketName = NAME_None)
{
*OutComponent = InActor->CreateDefaultSubobject<T>(InName);
if (!!InParent)
{
(*OutComponent)->SetupAttachment(InParent, InSocketName);
return;
}
InActor->SetRootComponent(*OutComponent);
}
//실행만 시켜주기 부모 자식 관계 없음
template<typename T>
static void CreateActorComponent(AActor* InActor, T** OutComponent, FName InName)
{
*OutComponent = InActor->CreateDefaultSubobject<T>(InName);
}
template<typename T>
static void GetAsset(T** OutObject, FString InPath)
{
ConstructorHelpers::FObjectFinder<T> asset(*InPath);
*OutObject = asset.Object;
}
template<typename T>
static void GetAssetDynamic(T** OutObject, FString InPath)
{
*OutObject = Cast<T>(StaticLoadObject(T::StaticClass(), nullptr, *InPath));
}
template<typename T>
static void GetClass(TSubclassOf<T>* OutClass, FString InPath)
{
ConstructorHelpers::FClassFinder<T> asset(*InPath);
*OutClass = asset.Class;
}
template<typename T>
static T* FindActor(UWorld* World)
{
for (AActor* actor : World->GetCurrentLevel()->Actors)
{
if (!!actor && actor->IsA<T>())
return Cast<T>(actor);
}
return nullptr;
}
template<typename T>
static void FindActors(UWorld* World, TArray<T *>& OutArray)
{
OutArray.Empty();
for (AActor* actor : World->GetCurrentLevel()->Actors)
{
if (!!actor && actor->IsA<T>())
OutArray.Add(Cast<T>(actor));
}
}
template<typename T>
static T* GetComponent(AActor* InActor)
{
//return Cast<USkeletalMeshComponent>(GetComponentByClass(USkeletalMeshComponent::StaticClass()));
return Cast<T>(InActor->GetComponentByClass(T::StaticClass()));
}
template<typename T>
static T* GetComponent(AActor* InActor, const FString& InName)
{
TArray<T *> components;
InActor->GetComponents<T>(components);
for (T* component : components)
{
if (component->GetName() == InName)
return component;
}
return nullptr;
}
};
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 = "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;
};
CRifle.cpp
#include "CRifle.h"
#include "Global.h"
#include "CBullet.h"
#include "Animation/AnimMontage.h"
#include "Components/SkeletalMeshComponent.h"
#include "GameFramework/Character.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Camera/CameraComponent.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'");
//클래스 타입 _C
CHelpers::GetClass<ACBullet>(&BulletClass, "Blueprint'/Game/05_TPS/BP_CBullet.BP_CBullet_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);
}
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;
}
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;
}
void ACRifle::End_Unequip()
{
bEquipping = false;
}
//fire 위치 및 생성
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); //탄착군 형성 보기위한 draw
FVector muzzleLocation = Mesh->GetSocketLocation("MuzzleFlash"); //소켓 가져다가
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()
{
}
알고만 있자) 쿼터니온 하고 Rotator는 1:1 매칭 된다.
FRotator == FQuat == FRotateMatrix (1 : 1 매칭 되는 것들 )
ACBullet* bullet = GetWorld()->SpawnActor<ACBullet>(BulletClass, 생성방향, direction.Rotation());
소켓 위치에 잘 못 주면 콜리전과 걸려가지고, 발사가 안될 수 있다. 소켓을 좀더 앞으로 배치하거나. (이문제는 또 해당 위치에서 전방으로 나가지 않고 아래로 붙어서 날라가는 경우가 발생할 수 있다. )
애니메이션 문제로 인해 아래로 날라가는 현상이 생길 수 있다. 주의하자.