CRifle 생성

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"; //socket

	UPROPERTY(EditDefaultsOnly, Category = "Socket")
		FName RightHandSocket = "Right_Hand_Rifle"; //소켓 네임 

//장착
	UPROPERTY(EditDefaultsOnly, Category = "Montage")
		class UAnimMontage* GrabMontage;

	UPROPERTY(EditDefaultsOnly, Category = "Montage")
		float GrabMontageSpeed = 3;

//비장착 
	UPROPERTY(EditDefaultsOnly, Category = "Montage") 
		class UAnimMontage* UngrabMontage;

	UPROPERTY(EditDefaultsOnly, Category = "Montage")
		float UngrabMontageSpeed = 3;

private:
	UPROPERTY(VisibleDefaultsOnly)
		class USkeletalMeshComponent* Mesh;

public:
//자기자신 이것은 멤버함수 니까 객체가 있어야 사용 할 수 있다. 누가 만들지 모르니까 static 붙여준다. 
//Rifle 클래스가 블프를 사용하는데 다른 총을 사용할 수 도 있기 때문에 ,TSubclassOf이 타입을 받는다. 
	static ACRifle* Spawn(TSubclassOf<ACRifle> RifleClass, class ACharacter* InOwner);

public:
	FORCEINLINE bool GetEquipped() { return bEquipped; }

public:	
	ACRifle();

	void Equip();

protected:
	virtual void BeginPlay() override;

public:	
	virtual void Tick(float DeltaTime) override;

private:
	class ACharacter* OwnerCharacter; //캐릭터에 

private:
	bool bEquipped;
	bool bEquipping;

};

CRifle.cpp

#include "CRifle.h"
#include "Global.h"
#include "Animation/AnimMontage.h" //몽타쥬 헤더 
#include "Components/SkeletalMeshComponent.h" //스켈레탈메쉬 
#include "GameFramework/Character.h"

**ACRifle::ACRifle()**
{
 	PrimaryActorTick.bCanEverTick = true;

	CHelpers::CreateComponent<USkeletalMeshComponent>(this, &Mesh, "Mesh"); //root가 될 것

	//기본 값 총 경로 가져옴 
	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_Grab_Montage.Rifle_Grab_Montage'");
	CHelpers::GetAsset<UAnimMontage>(&UngrabMontage, "AnimMontage'/Game/Character/Montages/Rifle/Rifle_UnGrab_Montage.Rifle_UnGrab_Montage'");

}

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();
//character의 Owner를 가져온다. 
	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)
	{
		
		return;
	}

	OwnerCharacter->PlayAnimMontage(GrabMontage, GrabMontageSpeed);
}

애니메이션은 Rifle_Grab애니메이션 2개를 불러와서 하나는 Ungrab 으로 사용(grab 똑같은 애니메이션)

Untitled

Untitled

장착을 할 때 플레이어 입장에서 Spawn을 시켜야하는데. spawn은 Rifle이 해주는 것이다. spawn될 자기 자신이 해당 character에 되어 주는 것이다. (character가 spawn될 것을 생성시키는게 아니다.) 즉 무기들이 스스로 자기자신을 생성해서 리턴해 주는 개념이다.

팩토리(Factory) : 자기 스스로가 생성해서 리턴해주는 것(자기를 찍어서 주겠다) = 외부에서 자기자신을 콜 해야한다.

public:
//자기자신 이것은 멤버함수 니까 객체가 있어야 사용 할 수 있다. 누가 만들지 모르니까 static 붙여준다. 
//Rifle 클래스가 블프를 사용하는데 다른 총을 사용할 수 도 있기 때문에 ,TSubclassOf이 타입을 받는다. 
	static ACRifle* Spawn(TSubclassOf<ACRifle> RifleClass, class ACharacter* InOwner);

beginplay이전에 생성

//CRifle.cpp
ACRifle* ACRifle::Spawn(TSubclassOf<ACRifle> RifleClass, class ACharacter* InOwner)
{
	FActorSpawnParameters params;
	params.Owner = InOwner; //해당 Owner

//onwer가 있었으면 그 해당 Owner의 world에 어딘가에 Spawn을 시켜준다.
	return InOwner->GetWorld()->SpawnActor<ACRifle>(RifleClass, params);
 }
//spawnactor가 ACRifle 타입을 생성 시켜서 InOwner에 준다. 이거 자체를 Return 시켜준것
//자기 자신을 생성시켜서 준 것  

Player 기본으로 생성하게 할 것 이다.

CPlayer.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "CPlayer.generated.h"

UCLASS()
class U2110_03_API ACPlayer : public ACharacter
{
	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;

private:
	void OnMoveForward(float InAxisValue);
	void OnMoveRight(float InAxisValue);
	void OnVerticalLook(float InAxisValue);
	void OnHorizontalLook(float InAxisValue);

	void OnRun();
	void OffRun();

	void OnRifle();

public:
	UFUNCTION(BlueprintCallable, Category = "Color")
		void ChangeColor(FLinearColor InColor);

private:
	class UMaterialInstanceDynamic* Materials[2];

//이 타입으로 만들어지고, 어떤 클래스 타입 부모 생성 
private:
	class ACRifle* Rifle;

Untitled

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;

	CHelpers::GetClass<ACRifle>(&RifleClass, "Blueprint'/Game/05_TPS/BPCRifle.BPCRifle_C'");
}

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

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

헤더 넣어주기

Untitled

블프 먼저 생성

Untitled

붙일려면 소켓 필요

Holster_Rifle → Spine03→ Holster_Rifle

Untitled

Hand_R 에 소켓 생성 → Right_Hand_Rifle

Untitled

Attach 시키는것은 누구의 역할 인가?

객체지향 속성

상속에 관련된

  1. 상속성
  2. 추상성
  3. 다형성

외부에 노출 (캡슐화를 정보은닉성과 동일한 개념으로 설명하지만 다른 개념 )

  1. 정보은닉성 : private | public | protected 외부에 공개할 것만 공개 하자
  2. 캡슐화 : (블랙박스화?) 입력 줄 수 있고, 그것으로 부터 결과를 리턴 받을 수 있다. 하지만 내부는 볼 수 없는 것
    1. ex) 윈도우 클릭할 때도 보면 프로그램이 어떻게 열리는 지는 모르겠지만. 우리 자신 한테는 그 프로그램이 보여지는 것 그 내부는 알 수 없는 것

생각해보자 : player가 RIfle에 명령을 내렸고 Rifle을 생성했다. RIfle을 장착 할 것이다. 그럼 Holster에 장착을 할 것이다. 그럼 이것의 주체는 누가 되는 것인가?

Player의 입장에서는 Rifle에게 장착해라 라고 명령을 내리는 것이다. 그럼 장착 처리는 Rifle에 있는 것이다. (캡슐화)

  1. spawn도 마찬가지로. player가 Rifle에게 spawn 시켜라 라고 명령을 내렸고.플레이어 입장이 Rifle 안에서는 어떻게 처리 되고 있는지 모른다. 그런데 spawn 해서 Return 받은 것이다. 그러니까 장착하라는 것도 Rifle에 있다는 것이 된다.
  2. Holster는 시작하자 마자 장착이 될 것이다(등에) beginplay에서 하면 된다.

[클린코드 : 객체지향 설계의 5가지 원칙 : 모든 원칙은 이 5가지로 부터 시작한다?](https://ppatabox.notion.site/5-5-aaba4c11b5c540acb7b8ea074a4f9fef)

CRifle

//CRilfe.cpp

void ACRifle::BeginPlay()
{
	Super::BeginPlay();

	OwnerCharacter = Cast<ACharacter>(GetOwner());
	AttachToComponent(OwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), HolsterSocket);

}

Untitled

Untitled

장착 명령

keyboard 1번 Rifle 장착

CPlayer

CPlyer.cpp Player에서 OnRifle이 눌렸다면. CRifle에서 처리해주어야 한다.

PlayerInputComponent->BindAction("Rifle", EInputEvent::IE_Pressed, this, &ACPlayer::OnRifle);

Untitled

CPlayer.h

void OnRifle();

Untitled

장착 처리 는 CRifle에서

//CRifle.h
public:	
	ACRifle();

	void Equip();

//==
//원래 장착되어 있던 것 
private:
	bool bEquipped;
	bool bEquipping; //장착 중인지. 

Untitled

CRifle.cpp

void ACRifle::Equip()
{

	CheckTrue(bEquipping); //true일때 리턴

	bEquipping = true;
	if (bEquipped) //장착 되었다면. 해제로 
	{
		

		return;
	}
//장착되어있는게 아니면 장착 모션에 들어가야한다. 
	OwnerCharacter->PlayAnimMontage(GrabMontage, GrabMontageSpeed);
}

Rifle 콜

//CPlayer.cpp
void ACPlayer::OnRifle()
{
	Rifle->Equip(); //장착 명령 
}

ABP_Character 애니메이션 블루프린트 수정

Untitled

Untitled

BS_Rifle 만약 애니메이션이 없을 경우? 애니메이션이 없을 경우 사용하는 방법?(다음에)

블프에선느 Private 뭐 Public 등 사용할 수 있게 열어놨었는데. C에서는 그렇게 할 수 없다.

CRifle.h

Equipped를 리턴해 줄 것  만들어준다.

Equipped를 리턴해 줄 것 만들어준다.

선언과 동시에 정의 된 것이 실제로어떻게 정의 되는 가 정의 위치로 가면

템플릿도 inline 처럼 동작을 한다.

이런 부류들이 Inline화가 되어야 좋은가?

inline 템플릿의 단점.