• Home
  • About
    • 게임 개발자 유정룡 photo

      게임 개발자 유정룡

      포트폴리오

    • Learn More
    • Email
    • Github
    • Bitbucket
  • Projects
    • All Projects
    • All Tags

ProjectDark - 01 - Create Main Player

20 Aug 2021

Reading time ~4 minutes

며칠동안 다크사이더스3에 있는 모델링 데이터를 뽑아 와서 애니메이션과 모델들을 전부 FBX 확장자로 변경했다.

(Fury는 무슨 이틀이 걸렸다…. ㅠㅠ)

이제 이 데이터들로 한번 다크소울 비슷하게 다크사이더스 비슷하게 만들어보자

MainPlayerCharacter

일단 플레이어 캐릭터를 만들자.

캐릭터를 부모로 갖고있는 MainPlayer를 만들어두자

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

#pragma once


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

USTRUCT(Atomic, BlueprintType)
struct FMainPlayerStatus
{
	GENERATED_USTRUCT_BODY()

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float MaxHealth;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float CurrentHealth;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float Damage;
};

UCLASS()
class MAINPLAYERCODES_API AMainPlayer : public ACharacter
{
	GENERATED_BODY()

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

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

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

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

private:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Statuc", meta = (AllowPrivateAccess = "True"))
	FMainPlayerStatus Status;

private:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "True"))
	class USpringArmComponent* CameraBoom;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "True"))
	class UCameraComponent* FollowCamera;

private:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "True"))
	float BaseTurnRate;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "True"))
	float BaseLookUpRate;


#pragma region TargetingSystem
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Targeting", meta = (AllowPrivateAccess = "True"))
	AActor* TargetingActor;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Targeting", meta = (AllowPrivateAccess = "True"))
	float TargetingAngle = 65.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Targeting", meta = (AllowPrivateAccess = "True"))
	float TargetiingInterpSpeed = 1.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Targeting", meta = (AllowPrivateAccess = "True"))
	float TargetiingInterpDeltaTime = 0.01f;
#pragma endregion

private:
	void MoveForward(float Value);

	void MoveRight(float Value);

	void TurnAtRate(float Rate);

	void LookUpAtRate(float Rate);

public:
	FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }

	FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }

	FORCEINLINE const FMainPlayerStatus& GetMainPlayerStatus() const {return Status;}
};

일단 간단하게 움직이는 것들과 카메라 정도만 만들어두자

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


#include "MainPlayer.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Controller.h"
#include "GameFramework/SpringArmComponent.h"
#include "Components/PrimitiveComponent.h"

AMainPlayer::AMainPlayer()
{
	PrimaryActorTick.bCanEverTick = true;

	BaseTurnRate = 45.0f;
	BaseLookUpRate = 45.0f;

	bUseControllerRotationPitch = false;
	bUseControllerRotationYaw = false;
	bUseControllerRotationRoll = false;

	GetCharacterMovement()->bOrientRotationToMovement = true;
	GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f);
	GetCharacterMovement()->JumpZVelocity = 350.0f;
	GetCharacterMovement()->AirControl = 0.1f;

	CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
	CameraBoom->SetupAttachment(RootComponent);
	CameraBoom->TargetArmLength = 300.0f;
	CameraBoom->bUsePawnControlRotation = true;

	FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
	FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
	FollowCamera->bUsePawnControlRotation = false;
}

void AMainPlayer::BeginPlay()
{
	Super::BeginPlay();
	
}

void AMainPlayer::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	ComboAttackCheck();
}

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

	PlayerInputComponent->BindAxis("MoveForward", this, &AMainPlayer::MoveForward);
	PlayerInputComponent->BindAxis("MoveRight", this, &AMainPlayer::MoveRight);

	PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
	PlayerInputComponent->BindAxis("TurnRate", this, &AMainPlayer::TurnAtRate);
	PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
	PlayerInputComponent->BindAxis("LookUpRate", this, &AMainPlayer::LookUpAtRate);
}

void AMainPlayer::MoveForward(float Value)
{
	if (bIsEvade == true) { return; }
	if (bAttacking == true) { return; }

	if ((Controller != nullptr) && (Value != 0.0f))
	{
		const FRotator Rotation = Controller->GetControlRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);

		const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
		AddMovementInput(Direction, Value);
	}
}

void AMainPlayer::MoveRight(float Value)
{
	if (bIsEvade == true) { return; }
	if (bAttacking == true) { return; }

	if ((Controller != nullptr) && (Value != 0.0f))
	{
		const FRotator Rotation = Controller->GetControlRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);

		const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
		AddMovementInput(Direction, Value);
	}
}

void AMainPlayer::TurnAtRate(float Rate)
{
	AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void AMainPlayer::LookUpAtRate(float Rate)
{
	AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

일단 간단하게 만들어 준다.

그런 뒤, AnimInstance를 만들어준다.

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

#pragma once

#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"
#include "MainPlayerAnimInstance.generated.h"

/**
 * 
 */
UCLASS()
class MAINPLAYERCODES_API UMainPlayerAnimInstance : public UAnimInstance
{
	GENERATED_BODY()

public:
	virtual void NativeInitializeAnimation() override;

	virtual void NativeUpdateAnimation(float DeltaSeconds) override;

private:
	UPROPERTY(EditAnyWhere, BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = true))
	class APawn* Pawn;
	UPROPERTY(EditAnyWhere, BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = true))
	class AMainPlayer* PlayerCharacter;

	UPROPERTY(EditAnyWhere, BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = true))
	float MovementSpeed;

	UPROPERTY(EditAnyWhere, BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = true))
	float CurrentDirection;
	UPROPERTY(EditAnyWhere, BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = true))
	bool bIsInAir;

public:
	FORCEINLINE const float& GetMovementSpeed() { return MovementSpeed; }
	FORCEINLINE void SetMovementSpeed(const float& _MovementSpeed) { MovementSpeed = _MovementSpeed; }

	FORCEINLINE const float& GetCurrentDirrection() { return CurrentDirection; }
	FORCEINLINE void SetCurrentDirrection(const float& _CurrentDirection) { CurrentDirection = _CurrentDirection; }

	FORCEINLINE const bool& GetIsInAir() { return bIsInAir; }
	FORCEINLINE void SetIsInAir(const bool& _bIsInAir) { bIsInAir = _bIsInAir; }

	FORCEINLINE const AMainPlayer* GetPlayerCharacter() { return PlayerCharacter; }
	FORCEINLINE void SetPlayerCharacter(AMainPlayer* Character) { PlayerCharacter = Character; }
};

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


#include "MainPlayerAnimInstance.h"
#include "MainPlayer.h"

void UMainPlayerAnimInstance::NativeInitializeAnimation()
{
	if (Pawn == nullptr)
	{
		Pawn = TryGetPawnOwner();

		if (Pawn)
		{
			PlayerCharacter = Cast<AMainPlayer>(Pawn);
		}
	}
}

void UMainPlayerAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);

	if (Pawn == nullptr)
	{
		Pawn = TryGetPawnOwner();
	}

	if (Pawn)
	{
		FVector Speed = Pawn->GetVelocity();
		MovementSpeed = Speed.Size();
		CurrentDirection = CalculateDirection(Speed, Pawn->GetActorRotation());


		if (PlayerCharacter == nullptr)
		{
			PlayerCharacter = Cast<AMainPlayer>(Pawn);
		}
	}
}

애니메이션을 동기화 해서 만들어준다.

이렇게 한뒤, 두개 전부 블루프린트를 만들어서 적용해주자.

움직이는 BlendState를 만든 뒤, 이 플레이어의 현재 스피드와 방향을 갖고와서 적용시키면

이동이 잘 된다.



Unreal Share Tweet +1