일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- MLFQ
- 유니티
- 언리얼엔진
- MAC
- STCF
- DSP
- RBAC
- sampling theory
- 운영체제
- AINCAA
- 유스케이스
- dtft
- 메카님
- ret2libc
- Unity #Indie Game
- DP
- Security
- 게임 개발
- Rr
- TSet
- stride
- Race condition
- dirty cow
- linear difference equation
- Double free
- CTF
- pdlc
- frequency-domain spectrum analysis
- 배경 그림
- 게임개발
- Today
- Total
다양한 기록
[UE Game Framework] #2 Character & Input System 본문
플레이어의 입력과 폰의 기초
액터의 구조
- 월드에 속한 컨텐츠의 기본 단위 => 액터
- 액터는 트랜스폼을 가지며, 월드로부터 틱과 시간 서비스를 제공 받음
- 근데 액터는 논리적 개념이고, 컴포넌트를 감싸는 포장
- 실질적인 구현은 컴포넌트가 진행, 액터는 다수의 컴포넌트를 소유
- 다수의 컴포넌트 중 대표 : 루트 컴포넌트 (Root Component, 컴포넌트 1)
- 액터는 루트 컴포넌트를 가져야 하며, 루트 컴포넌트의 트랜스폼 == 액터의 트랜스폼
1. 블루프린트로 액터 만들기
위와 같이 구성 가능
맨 위의 바디가 루트 컴포넌트고, 그 아래 애들까지 컴포넌트 감싸는게 분수 액터
잘 만들어짐
2. C++ 로 액터 만들기
액터 상속하여 ABFountain 생성
C++ 액터에서 컴포넌트 생성
- 컴포넌트는 언리얼 오브젝트임, UPROPERTY 설정하고 TObjectPtr로 포인터 선언
- 컴포넌트의 등록
- CDO에서 생성한 컴포넌트는 자동으로 월드에 등록
- NewObejct로 생성한 컴포넌트는 반드시 등록 절차를 거쳐야 함 (ex. RegisterComponent)
- 등록된 컴포넌트는 월드의 기능을 사용할 수 있으며 물리와 렌더링 처리에 합류
- 컴포넌트의 확장 설계
- 에디터 편집 및 블루프린트로의 승계를 위한 설정
- UPROPERTY에 지정자(specifier) 설정
- 컴포넌트 지정자
- Visible / Edit : 크게 객체 타입 / 값타입으로 사용
- Anywhere / DefaultsOnly / InstanceOnly : 에디터에서 편집 가능 영역 (보통 Anywhere 많이 사용, ex. VisibleAnywhere, EditAnywhere .. 컴포넌트는 객체니까 VisibleAnywhere 사용)
- BlueprintReadOnly / BlueprintReadWrite : 블루프린트로 확장 시 읽기 쓰기 권한
- Category : 에디터 편집 영역(Detail)에서의 카테고리 지정
=> 이걸로 두 개의 StaticMesh 컴포넌트 추가
헤더에서 선언 후
// Fill out your copyright notice in the Description page of Project Settings.
#include "Prop/ABFountain.h"
#include "Components/StaticMeshComponent.h"
// Sets default values
AABFountain::AABFountain()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
Body = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Body"));
Water = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Water"));
RootComponent = Body;
Water->SetupAttachment(Body);
Water->SetRelativeLocation(FVector(0.0f, 0.0f, 132.0f));
static ConstructorHelpers::FObjectFinder<UStaticMesh> BodyMeshRef(TEXT("/Script/Engine.StaticMesh'/Game/ArenaBattle/Environment/Props/SM_Plains_Castle_Fountain_01.SM_Plains_Castle_Fountain_01'"));
if ( BodyMeshRef.Object )
{
Body->SetStaticMesh(BodyMeshRef.Object);
}
static ConstructorHelpers::FObjectFinder<UStaticMesh> WaterMeshRef(TEXT("/Script/Engine.StaticMesh'/Game/ArenaBattle/Environment/Props/SM_Plains_Fountain_02.SM_Plains_Fountain_02'"));
if (WaterMeshRef.Object)
{
Water->SetStaticMesh(WaterMeshRef.Object);
}
}
// Called when the game starts or when spawned
void AABFountain::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AABFountain::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
생성자 코드에서 컴포넌트 설정 및 static mesh 연결
액터 만들어짐
잘 추가됨
이걸 상속해서 별도의 블루프린트로 액터 만들기 가능
BP_Fountain 더블 클릭하고 클래스 세팅
부모 클래스 변경하니까
바디랑 워터가 또 생김
있던 거 제거해도 똑같이 작동함
캐릭터의 제작
폰의 기능과 설계
폰: 액터를 상속, 플레이어가 빙의해 입출력을 처리
폰은 길찾기를 사용 가능하고, 세가지 주요 컴포넌트로 구성
- 기믹과 상호작용을 담당하는 충돌 컴포넌트 (루트 컴포넌트)
- 시각적인 비주얼을 담당하는 메시 컴포넌트
- 움직임을 담당하는 무브먼트 컴포넌트
컴포넌트 중 트랜스폼 없이 기능만 제공하는 컴포넌트 -> 액터 컴포넌트 (ex. 움직임)
트랜스폼이 있는 컴포넌트는 씬 컴포넌트
캐릭터의 기본 구조
캐릭터는 인간형 폰을 구성하도록 언리얼이 제공하는 전문 폰 클래스를 의미
캐릭터는 세 가지 주요 컴포넌트로 구성
- 기믹과 상호작용을 담당하는 캡슐 컴포넌트 (루트 컴포넌트)
- 애니메이션 캐릭터를 표현하는 스켈레탈 메시 컴포넌트
- 캐릭터의 움직임을 담당하는 캐릭터무브먼트 컴포넌트
마네킹 캐릭터 제작하기
// Fill out your copyright notice in the Description page of Project Settings.
#include "Character/ABCharacterBase.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
// Sets default values
AABCharacterBase::AABCharacterBase()
{
// Pawn rotation
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
// Capsule
GetCapsuleComponent()->InitCapsuleSize(42.0f, 96.0f);
GetCapsuleComponent()->SetCollisionProfileName(TEXT("Pawn"));
// Movement
GetCharacterMovement()->bOrientRotationToMovement = true;
GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f);
GetCharacterMovement()->JumpZVelocity = 700.0f;
GetCharacterMovement()->AirControl = 0.35f;
GetCharacterMovement()->MaxWalkSpeed = 500.0f;
GetCharacterMovement()->MinAnalogWalkSpeed = 20.0f;
GetCharacterMovement()->BrakingDecelerationWalking = 2000.0f;
// Mesh
GetMesh()->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, -100.0f), FRotator(0.0f, -90.0f, 0.0f));
GetMesh()->SetAnimationMode(EAnimationMode::AnimationBlueprint);
GetMesh()->SetCollisionProfileName(TEXT("CharacterMesh"));
static ConstructorHelpers::FObjectFinder<USkeletalMesh> CharacterMeshRef(TEXT("/Script/Engine.SkeletalMesh'/Game/Characters/Mannequins/Meshes/SKM_Quinn_Simple.SKM_Quinn_Simple'"));
if (CharacterMeshRef.Object)
{
GetMesh()->SetSkeletalMesh(CharacterMeshRef.Object);
}
static ConstructorHelpers::FClassFinder<UAnimInstance> AnimInstanceClassRef(TEXT("/Game/Characters/Mannequins/Animations/ABP_Quinn.ABP_Quinn_C"));
if (AnimInstanceClassRef.Class)
{
GetMesh()->SetAnimInstanceClass(AnimInstanceClassRef.Class);
}
}
캐릭터 베이스에 로테이션, 캡슐, 메쉬 설정 등을 해줌
// Fill out your copyright notice in the Description page of Project Settings.
#include "Game/ABGameMode.h"
//#include "Player/ABPlayerController.h"
AABGameMode::AABGameMode()
{
/*static ConstructorHelpers::FClassFinder<APawn> ThirdPersonClassRef(TEXT("/Game/ThirdPerson/Blueprints/BP_ThirdPersonCharacter.BP_ThirdPersonCharacter_C"));
if (ThirdPersonClassRef.Class != nullptr )
{
DefaultPawnClass = ThirdPersonClassRef.Class;
}*/
static ConstructorHelpers::FClassFinder<APawn> DefaultPawnClassRef(TEXT("/Script/ArenaBattle.ABCharacterPlayer"));
if (DefaultPawnClassRef.Class != nullptr)
{
DefaultPawnClass = DefaultPawnClassRef.Class;
}
static ConstructorHelpers::FClassFinder<APlayerController> PlayerControllerClassRef(TEXT("/Script/ArenaBattle.ABPlayerController"));
if (PlayerControllerClassRef.Class != nullptr)
{
PlayerControllerClass = PlayerControllerClassRef.Class;
}
//PlayerControllerClass = AABPlayerController::StaticClass();
}
그 후 게임 모드에서 디폴트 폰 클래스 변경
Detach 후 확인하면 마네킹 생성 확인 가능
(카메라는 아직 없음)
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Character/ABCharacterBase.h"
#include "ABCharacterPlayer.generated.h"
/**
*
*/
UCLASS()
class ARENABATTLE_API AABCharacterPlayer : public AABCharacterBase
{
GENERATED_BODY()
public:
AABCharacterPlayer();
// Camera Section
protected:
// Meta 설정 .. Private로 설정된 객체들을 블루프린트에서 접근할 수 있도록 함
// 스프링 암 == 카메라 지지대
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class USpringArmComponent> CameraBoom;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UCameraComponent> FollowCamera;
};
베이스 상속한 플레이어 클래스 헤더 파일
// Fill out your copyright notice in the Description page of Project Settings.
#include "Character/ABCharacterPlayer.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
AABCharacterPlayer::AABCharacterPlayer()
{
// Camera
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);
CameraBoom->TargetArmLength = 400.0f;
CameraBoom->bUsePawnControlRotation = true;
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
// 소켓 지정자 사용하면 스프링 암 끝에 알아서 붙음
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
FollowCamera->bUsePawnControlRotation = false;
}
구현 파일
카메라 지지대랑 카메라 설정
이제 입력 시스템으로 캐릭터 조종 필요
입력 시스템의 동작 방식
플레이어의 입력은 컨트롤러를 통해 폰으로 전달
입력을 컨트롤러가 처리할 수도, 폰이 처리할 수도 있는데 일반적으로는 폰이 처리
향상된 입력 시스템 (Enhanced Input System)
언리얼 5.1 도입 (이전 인풋은 나중에 사용자의 입력 설정 변경에 유연하게 대처 불가)
사용자 입력 처리를 네 단계로 세분화, 각 설정을 독립적인 애셋으로 대체
플레이어의 최종 입력을 게임 로직에서 실행하도록 시스템의 구조가 변경된 것
동작 구성
- 사용자의 입력 데이터를 최종 함수에 매핑하는 과정을 체계적으로 구성
- 플랫폼에 따른 다양한 입력 장치 설정
- 입력 값의 변경
- 이벤트 발생 조건의 상세 설정
사용자 -> 입력 매핑 컨텍스트 -> 액션(입력값 변조, 이벤트 활성화(트리거)) -> 게임 로직(함수)
예전엔 이걸 다 게임 로직에서 처리했음
매핑 컨텍스트 / 액션 / 함수
그리고 추가로 중요한 함수들
BeginPlay()같은 경우는 매핑 컨텍스트 할당
SetupPlayerInputComponent()같은 경우는 언리얼엔진 인풋 시스템에서 액션, 함수를 매핑시켜줌
그리고 실질적으로 키보드를 쓰는지, 게임 패드를 쓰는지 결정하는 것은 BeginPlay의 DefaultMappingContext에서 진행
위에서 선언한 거 할당해주고
액션이랑 함수 바인딩
이때 점프는 기존 ACharacter 거 사용
그리고 무브랑 룩 구현
인풋액션 밸류에서 XY값을 가져온 후에 이걸 무브먼트 컴포넌트랑 연결해서 이동시키거나
컨트롤러의 회전을 설정하여 스프링 암이 해당 컨트롤러를 바라보게 만듦
비긴 플레이에서 매핑 컨텍스트 추가
컨트롤러가 플레이어를 대상으로 하기 때문에 CastChecked
SubSystem이라는 인풋 시스템을 추가해서 매핑 컨텍스트 애셋을 추가
입력이 겹칠 때 우선순위도 설정 가능함
런타임에 RemoveMappingContext를 통해 뺄 수도 있음
무브의 모디파이어 변경
AD를 X축 이동으로 설정, WS를 Y축 이동으로 설정
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Character/ABCharacterBase.h"
#include "InputActionValue.h"
#include "ABCharacterPlayer.generated.h"
/**
*
*/
UCLASS()
class ARENABATTLE_API AABCharacterPlayer : public AABCharacterBase
{
GENERATED_BODY()
public:
AABCharacterPlayer();
protected:
virtual void BeginPlay() override;
public:
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// Camera Section
protected:
// Meta 설정 .. Private로 설정된 객체들을 블루프린트에서 접근할 수 있도록 함
// 스프링 암 == 카메라 지지대
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class USpringArmComponent> CameraBoom;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UCameraComponent> FollowCamera;
// Input Section
protected:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UInputMappingContext> DefaultMappingContext;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UInputAction> JumpAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UInputAction> MoveAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UInputAction> LookAction;
void Move(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
};
헤더
// Fill out your copyright notice in the Description page of Project Settings.
#include "Character/ABCharacterPlayer.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "InputMappingContext.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
AABCharacterPlayer::AABCharacterPlayer()
{
// Camera
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);
CameraBoom->TargetArmLength = 400.0f;
CameraBoom->bUsePawnControlRotation = true;
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
// 소켓 지정자 사용하면 스프링 암 끝에 알아서 붙음
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
FollowCamera->bUsePawnControlRotation = false;
// Input
static ConstructorHelpers::FObjectFinder<UInputMappingContext> InputMappingContextRef(TEXT("/Script/EnhancedInput.InputMappingContext'/Game/ArenaBattle/Input/IMC_Default.IMC_Default'"));
if (nullptr != InputMappingContextRef.Object)
{
DefaultMappingContext = InputMappingContextRef.Object;
}
else
{
UE_LOG(LogTemp, Log, TEXT("Error : InputMappingContextRef"));
}
static ConstructorHelpers::FObjectFinder<UInputAction> InputActionMoveRef(TEXT("/Script/EnhancedInput.InputAction'/Game/ArenaBattle/Input/Actions/IA_Move.IA_Move'"));
if (nullptr != InputActionMoveRef.Object)
{
MoveAction = InputActionMoveRef.Object;
}
else
{
UE_LOG(LogTemp, Log, TEXT("Error : InputActionMoveRef"));
}
static ConstructorHelpers::FObjectFinder<UInputAction> InputActionJumpRef(TEXT("/Script/EnhancedInput.InputAction'/Game/ArenaBattle/Input/Actions/IA_Jump.IA_Jump'"));
if (nullptr != InputActionJumpRef.Object)
{
JumpAction = InputActionJumpRef.Object;
}
else
{
UE_LOG(LogTemp, Log, TEXT("Error : InputActionJumpRef"));
}
static ConstructorHelpers::FObjectFinder<UInputAction> InputActionLookRef(TEXT("/Script/EnhancedInput.InputAction'/Game/ArenaBattle/Input/Actions/IA_Look.IA_Look'"));
if (nullptr != InputActionLookRef.Object)
{
LookAction = InputActionLookRef.Object;
}
else
{
UE_LOG(LogTemp, Log, TEXT("Error : InputActionLookRef"));
}
}
void AABCharacterPlayer::BeginPlay()
{
Super::BeginPlay();
APlayerController* PlayerController = CastChecked<APlayerController>(GetController());
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
//Subsystem->RemoveMappingContext(DefaultMappingContext);
}
else
{
UE_LOG(LogTemp, Log, TEXT("Error : Subsystem"));
}
}
void AABCharacterPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// EnhancedInputComponent를 사용하지 않은 경우가 있을 수 있어 CastChecked
UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACharacter::Jump);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AABCharacterPlayer::Move);
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AABCharacterPlayer::Look);
}
void AABCharacterPlayer::Move(const FInputActionValue& Value)
{
FVector2D MovementVector = Value.Get<FVector2D>();
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
AddMovementInput(ForwardDirection, MovementVector.X);
AddMovementInput(RightDirection, MovementVector.Y);
}
void AABCharacterPlayer::Look(const FInputActionValue& Value)
{
FVector2D LookAxisVector = Value.Get<FVector2D>();
AddControllerYawInput(LookAxisVector.X);
AddControllerPitchInput(LookAxisVector.Y);
}
구현
입력도 잘 받음
'언리얼 엔진 > Unreal Game Framework' 카테고리의 다른 글
[UE Game Framework] #6 Character Attack Hit Check (0) | 2025.01.05 |
---|---|
[UE Game Framework] #5 Character Combo Action (0) | 2025.01.04 |
[UE Game Framework] #4 Character Animation Setting (0) | 2025.01.04 |
[UE Game Framework] #3 Character Control Setting (0) | 2025.01.03 |
[UE Game Framework] #1 기초 (0) | 2025.01.02 |