프로그래밍 공부
작성일
2024. 4. 22. 12:57
작성자
WDmil
728x90

케릭터가 분리되어있을 때, 구분적으로 케릭터를 생성하고 관리하는 방법을 안다.

 

일정 동작사항에 케릭터의 메쉬를 바꿀 수 있다.


EnumType

#pragma once

#include "CoreMinimal.h"

#pragma region Enum

UENUM(BlueprintType)
enum class ECharacterMoudlePart
{
	Body UMETA(DisplayName = "Body"),
	Head UMETA(DisplayName = "Head"),
	LeftGranade UMETA(DisplayName = "LeftGranade"),
	RightGranade UMETA(DisplayName = "RightGranade"),
	Tablet UMETA(DisplayName = "Tablet"),
	LeftBag UMETA(DisplayName = "LeftBag"),
	RightBag UMETA(DisplayName = "RightBag"),
	VestBag UMETA(DisplayName = "VestBag"),
	BackBag UMETA(DisplayName = "BackBag"),
	ExoLegs UMETA(DisplayName = "ExoLegs"),
};

#pragma endregion

사용할 캐릭터의 Enum을 생성하고 정의해준다.

이 Enum을 사용해서 Base_Character의 메시데이터를 정의해줄것이다.


Base_Character

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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

#define SKELETAL_MESH_COMP(NAME) \
UPROPERTY(VisibleDefaultsOnly) \
TObjectPtr<USkeletalMeshComponent> NAME;

UCLASS()
class FTPSGAME_API ABaseCharacter : public ACharacter
{
	GENERATED_BODY()
	
protected:
	SKELETAL_MESH_COMP(Body)
	SKELETAL_MESH_COMP(Head)
	SKELETAL_MESH_COMP(LeftGranade)
	SKELETAL_MESH_COMP(RightGranade)
	SKELETAL_MESH_COMP(Tablet)
	SKELETAL_MESH_COMP(LeftBag)
	SKELETAL_MESH_COMP(RightBag)
	SKELETAL_MESH_COMP(VestBag)
	SKELETAL_MESH_COMP(BackBag)
	SKELETAL_MESH_COMP(ExoLegs)

public:
	// Sets default values for this character's properties
	ABaseCharacter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

};

 

스켈레톤 메시를 여러개 가지는 케릭터 H와 CPP를 생성해준다.

// Fill out your copyright notice in the Description page of Project Settings.


#include "Actors/BaseCharacter.h"
#include "Utility/Helper.h"

#define CREATE_EMSH_COMP(Name) \
	Name = Helper::CreateSceneComponent \
	<USkeletalMeshComponent>(this, #Name, GetMesh());

// Sets default values
ABaseCharacter::ABaseCharacter()
{
	// Create Skeletal Mesh Comp
	{
		CREATE_EMSH_COMP(Body)
		CREATE_EMSH_COMP(Head)
		CREATE_EMSH_COMP(LeftGranade)
		CREATE_EMSH_COMP(RightGranade)
		CREATE_EMSH_COMP(Tablet)
		CREATE_EMSH_COMP(LeftBag)
		CREATE_EMSH_COMP(RightBag)
		CREATE_EMSH_COMP(VestBag)
		CREATE_EMSH_COMP(BackBag)
		CREATE_EMSH_COMP(ExoLegs)
	}

	// Mesh Load
	{
		USkeletalMesh* BodyMesh =
			Helper::GetAsset<USkeletalMesh>("/Script/Engine.SkeletalMesh'/Game/Characters/Elite_Solders/Body/SKM_UE5__Elite_Soldier_Body.SKM_UE5__Elite_Soldier_Body'");
		GetMesh()->SetSkeletalMesh(BodyMesh);

		USkeletalMesh* HeadMesh = Helper::GetAsset<USkeletalMesh>("/Script/Engine.SkeletalMesh'/Game/Characters/Elite_Solders/Heads/SKM_UE5__Elite_Soldier_Head_01.SKM_UE5__Elite_Soldier_Head_01'");
		Head->SetSkeletalMesh(HeadMesh);
	}
	// Set Leader
	{
		Head->SetLeaderPoseComponent(GetMesh());
		LeftGranade->SetLeaderPoseComponent(GetMesh());
		RightGranade->SetLeaderPoseComponent(GetMesh());
		Tablet->SetLeaderPoseComponent(GetMesh());
		LeftBag->SetLeaderPoseComponent(GetMesh());
		RightBag->SetLeaderPoseComponent(GetMesh());
		VestBag->SetLeaderPoseComponent(GetMesh());
		BackBag->SetLeaderPoseComponent(GetMesh());
		ExoLegs->SetLeaderPoseComponent(GetMesh());
	}
	
	// Set Mesh Location & Rocation
	{
		GetMesh()->SetRelativeLocation(FVector(0, 0, -90));
		GetMesh()->SetRelativeRotation(FRotator(0, -90, 0));
	}
}

// Called when the game starts or when spawned
void ABaseCharacter::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called to bind functionality to input
void ABaseCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}

각 메시는, PosComponent의 mesh에서 값을 읽어오기 때문에, 지정된 본에 대한 이동움직임. 즉, ABP의 동작사항을 이어받아 각각 움직이게 된다.

 

LeaderPosComponent의 mesh에 상속받기 때문에, Mesh의 이동에 상속받는다는 의미.

 

머리와 몸체는 따로 설정이 들어갈 수 있지만, 기본값을 지정해주어, 값이 없어도 기본몸과 머리가 존재하도록 해준다.

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Actors/BaseCharacter.h"
#include "Hero.generated.h"

class USpringArmComponent;
class UCameraComponent;

struct FInputActionValue;
class UInputMappingContext;
class UInputAction;

UCLASS()
class FTPSGAME_API AHero : public ABaseCharacter
{
	GENERATED_BODY()
	// Components

	UPROPERTY(VisibleDefaultsOnly, Category = "Camera")
	TObjectPtr<UCameraComponent> Camera;

	UPROPERTY(VisibleDefaultsOnly, Category = "Camera")
	TObjectPtr<USpringArmComponent> SpringArm;


	UPROPERTY(EditDefaultsOnly, CateGory = Input, meta=(AllowPrivateAccess="true"))
	TObjectPtr<UInputAction> MoveAction;

	UPROPERTY(EditDefaultsOnly, CateGory = Input, meta = (AllowPrivateAccess = "true"))
	TObjectPtr<UInputAction> ViewAction;

	UPROPERTY(EditDefaultsOnly, CateGory = Input, meta = (AllowPrivateAccess = "true"))
	TObjectPtr<UInputAction> JumpAction;

	UPROPERTY(EditDefaultsOnly, CateGory = Input, meta = (AllowPrivateAccess = "true"))
	TObjectPtr<UInputMappingContext> InputMappingContext;

public:

	AHero();

	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	void Move(const FInputActionValue& Value);
	void Look(const FInputActionValue& Value);
};

 

기본 이동에 대한 Character를 Base_Character를 부모로 두고 생성한다.

 

// Fill out your copyright notice in the Description page of Project Settings.


#include "Actors/Heros/Hero.h"
#include "Utility/Helper.h"

#include "Engine/LocalPlayer.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"

#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/Controller.h"

#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputActionValue.h"

AHero::AHero()
{

	{
		SpringArm = Helper::CreateSceneComponent<USpringArmComponent>(this, "Spring Arm", GetCapsuleComponent());
		Camera = Helper::CreateSceneComponent<UCameraComponent>(this, "Camera", SpringArm);
	}
}

void AHero::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	// Set up action bindings
	if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent)) {

		// Jumping
		EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
		EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);

		// Moving
		EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AHero::Move);

		// Looking
		EnhancedInputComponent->BindAction(ViewAction, ETriggerEvent::Triggered, this, &AHero::Look);
	}
}

void AHero::Look(const FInputActionValue& Value)
{
	// input is a Vector2D
	FVector2D LookAxisVector = Value.Get<FVector2D>();

	if (Controller != nullptr)
	{
		// add yaw and pitch input to controller
		AddControllerYawInput(LookAxisVector.X);
		AddControllerPitchInput(LookAxisVector.Y);
	}
}

void AHero::Move(const FInputActionValue& Value)
{
	// input is a Vector2D
	FVector2D MovementVector = Value.Get<FVector2D>();

	if (Controller != nullptr)
	{
		// find out which way is forward
		const FRotator Rotation = Controller->GetControlRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);

		// get forward vector
		const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);

		// get right vector 
		const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

		// add movement 
		AddMovementInput(ForwardDirection, MovementVector.Y);
		AddMovementInput(RightDirection, MovementVector.X);
	}
}

 

기본 움직임을 구현하였다.

 

메시가 잘 나타나는지 확인한다. 메시데이터가 잘 붙어서 생성되어있으면 성공.

728x90