언리얼 C++로 기본적인 캐릭터의 조작을 구현한다.
필요한 헤더의 include가 필요하고, Class와 Object의 구분적인 활용이 요구된다.
입력액션 제작
언리얼 5 에서는 입력액션이 객체로 존재하고, 관리하기 때문에, 해당 입력액션을 컨트롤하고 관리할 객체를 따로 선언하고 만들어준다.
IMC_Movement
기본 입력벨류에 대한 값 처리이다.
Move와 View는 Vector2D를 받아와 사용한다.
IA_Jump는 Bool값을 받아와 사용하게 된다.
내부의 구조데이터는 부정과 XY축 변환을 기준으로 돌아가는데 이는 다음과 같다.
모디파이어로 입력축에 대한 값처리를 등록한다. W는 스위즐 입력 축 값. S는 입력축값의 부정.
D는 기본에,
A는 기본값에 부정한다.
스위즐 입력 축 값을 통해 X와 Y축의 반전을 사용해서, 첫 값에 float을 기입하게 되는걸 X축과 Y축에 바꾸어 입력하고.(기본은 X축이나, 스위줄 입력 축 값을 사용하였기 때문에 해당 위치는 Y값이 들어간다.)
D와 A는 기본에 부정을 넣었기 때문에, X축에 값이 기입된다.
View
View의 경우, XY2D축을 받아와 사용하며, 해당 값 중 Y축은 Screen좌표 기준, Y축은 0에 가깝기 때문에 Y축 반전을 막고자, Y축만 부정으로 처리한다.
Jump
점프의 경우, 이벤트 입력값을 두개 받아와, 눌렸을 때. 해제되었을 때 를 두개 받아서, 점프 중에 스페이스바를 때면 바로 중력의 영향을 받도록 처리해준다.
C++ Character
Character를 상속받는 C++객체를 생성한다. 임의로 이름을 C_Hero라 지었다.
C_Hero.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "C_Hero.generated.h"
class USpringArmComponent;
class UCameraComponent;
struct FInputActionValue;
class UInputMappingContext;
class UInputAction;
UCLASS()
class SUBPROJECT_API AC_Hero : public ACharacter
{
GENERATED_BODY()
UPROPERTY(EditDefaultsOnly, meta = (AllowPreserveRatio = "true"))
TObjectPtr<UCameraComponent> Camera;
UPROPERTY(EditDefaultsOnly, meta = (AllowPrivateAccess = "true"))
TObjectPtr<USpringArmComponent> SpringArm;
UPROPERTY(EditDefaultsOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
TObjectPtr<UInputMappingContext> Imc_Movement;
UPROPERTY(EditDefaultsOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
TObjectPtr<UInputAction> MovementInput;
UPROPERTY(EditDefaultsOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
TObjectPtr<UInputAction> ViewInput;
UPROPERTY(EditDefaultsOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
TObjectPtr<UInputAction> JumpInput;
public:
// Sets default values for this character's properties
AC_Hero();
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;
private:
void Movement(const FInputActionValue& value);
void View(const FInputActionValue& value);
}
Hero의 기본 입력값이다.
헤더에서 Movement와 View를 함수로 처리하고, 캐릭터의 기본기입값인 카메라, 스프링암 을 넣는다.
나머지는 컨트롤을 받기위해, UInputAction과 UInputMappingContext를 생성한다.
생성한 기초값에 데이터를 기입하기 위해 외부에서 블루프린트로 허가설정을 넣을 수 있게 UPROPERTY를 열어준다.
FindActionValue는 Struct임으로, Class로 전방선언 하지 않도록 주의한다.
C_Hero.Cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "Character/C_Hero.h"
#include "Utillites/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"
// Sets default values
AC_Hero::AC_Hero()
{
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
SpringArm = Helper::CreateSceneComponent<USpringArmComponent>(this, "Spring Arm", GetCapsuleComponent());
Camera = Helper::CreateSceneComponent<UCameraComponent>(this, "Camera", SpringArm);
SpringArm->SetRelativeLocation(FVector(0, 20, 60));
SpringArm->bEnableCameraLag = true;
SpringArm->bUsePawnControlRotation = true;
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
GetCharacterMovement()->bOrientRotationToMovement = true;
GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f); // ...at this rotation rate
}
// Mesh
{
USkeletalMesh* SkeletalMesh = Helper::GetAsset<USkeletalMesh>(L"/Script/Engine.SkeletalMesh'/Game/Characters/Mannequins/Meshes/SKM_Manny.SKM_Manny'");
GetMesh()->SetSkeletalMesh(SkeletalMesh);
GetMesh()->SetRelativeLocation(FVector(0, 0, -90));
GetMesh()->SetRelativeRotation(FRotator(0, -90, 0));
UClass* AnimInstanceClass = Helper::GetClass<UAnimInstance>(L"/Script/Engine.AnimBlueprint'/Game/Characters/Mannequins/Animations/ABP_Manny.ABP_Manny'");
GetMesh()->AnimClass = AnimInstanceClass;
}
}
// Called when the game starts or when spawned
void AC_Hero::BeginPlay()
{
Super::BeginPlay();
{
if (APlayerController* PlayerController = Cast<APlayerController>(GetController()))
{
if (UEnhancedInputLocalPlayerSubsystem* SubSystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer())) {
SubSystem->AddMappingContext(Imc_Movement, 0);
}
}
}
}
// Called to bind functionality to input
void AC_Hero::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent)) {
EnhancedInputComponent->BindAction(MovementInput, ETriggerEvent::Triggered, this, &AC_Hero::Movement);
EnhancedInputComponent->BindAction(ViewInput, ETriggerEvent::Triggered, this, &AC_Hero::View);
EnhancedInputComponent->BindAction(JumpInput, ETriggerEvent::Started, this, &ACharacter::Jump);
EnhancedInputComponent->BindAction(JumpInput, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
}
}
#pragma region Input Mapping
void AC_Hero::Movement(const FInputActionValue& value)
{
FVector2D Input = value.Get<FVector2D>();
if (Controller)
{
const FRotator Rotator = Controller->GetControlRotation();
const FRotator YawRotator = FRotator(0, Rotator.Yaw, 0);
// 항상 회전값 기준은 카메라를 기준으로, 2D평면으로 만든 전후좌우 측정기준.
const FVector RightVector = FRotationMatrix(YawRotator).GetUnitAxis(EAxis::Y);
const FVector ForwardVector = FRotationMatrix(YawRotator).GetUnitAxis(EAxis::X);
AddMovementInput(RightVector, Input.X);
AddMovementInput(ForwardVector, Input.Y);
}
}
void AC_Hero::View(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);
}
}
#pragma endregion
생성자에서 카메라와 스프링 암을 설정, 정의해준다.
메시의 경우. 메시경로를 집어넣어서 생성해준다.
에니메이션 경로의 경우, 상수로 집어넣어도 정상설정이 이루어지지 않음으로, ACharacteor에서 UPROPERTY로 열어놓아진 경로에 수동으로 넣어주자.
SetupPlayerInputComponent에서 키 입력 바인딩을 넣어준다.
입력값에 대해 어떤 입력값을 인풋으로 받고, 어떤 이벤트가 바인딩될 것인지 정의해주면 된다.
회전의 기준점은 Contorller를 기준으로, 전방과 우측벡터를 산정하고, Yaw를 기준으로, 객체의 입력값에 따라 메트릭스로 회전을 돌려주면 된다.
Contoller의 회전기준값은 변하지 않음으로, 갱신되는 회전값이 새로운 회전값에 영향을 주지 않게되어 A나 D를 누르고 있을 때 객체가 계속 회전하지 않고, 일정 값에 도달하면 회전을 멈추게 된다.
View의 경우 컨트롤의 Yaw와 Pitch를 현재 받아온 컨트롤러의 Axis값을 기준으로 회전에 넣어준다.
원래라면, 좌표평면상의 Direction을 카메라 뷰 기준으로 Vector로 변환한 다음 해당 Vector에 UpVector와 내적하여, 방향을 확인한 다음, 그 내적의 값에 따랴 카메라를 돌려주어야 하는데, 언리얼에서는 기능이 다 존재한다.
아무튼 위 방법을 사용해서 무브컨트롤과 뷰 컨트롤을 완성한다.
점프의 경우 ACharacter에 이미 기능이 구현되어있음으로, 이벤트만 바인딩해주면 된다.
테스트
'서울게임아카데미 교육과정 6개월 국비과정' 카테고리의 다른 글
20240422 130일차 언리얼 C++ 데이터 레이블 활용 케릭터 생성 (0) | 2024.04.22 |
---|---|
20240419 129일차 언리얼 C++ 데이터 테이블 (1) | 2024.04.19 |
20240409 123일차 언리얼 블루프린트 AI EQS (0) | 2024.04.09 |
20240408 122일차 언리얼 블루프린트 AI (0) | 2024.04.08 |
20240402 120일차 언리얼 C++ 함수 오버라이드 (0) | 2024.04.03 |