인생언리얼교과서

총평

게임엔진에 생초보인 나에게 적당한 포지션의 책
게임개발에 프로그래밍이 없을수 없다 → 어느정도 프로그래밍에 익숙한 사람일수록 읽기 편하다
C++ 기본이라도 하고 와야 한다
많은양을 다루지만, 많은양을 스킵함 → 핵심내용은 혼자서 탐구하거나 다른 책을 찾아봐야 함
그러나 진짜 생기초 라면 이것도 모를것이므로 게임개발에 여러분야를 맛본다는 생각으로 임해야함. 이정도는 다 기본으로 알아야 뭐라도 만든다!
책을 구매당시 UE5 버전이 이미 상용출시 되었다. → UI, API 의 변화가 생겼지만 찾아보면 극복할만한 난이도이다.
24.02.16

1장..

스킵해도 됨
별 내용없음 → 의미 없음

2장. 슈팅게임 만들기

2-1. 블루프린트로 제작하기

이부분은 스킵
대부분 CPP 코드를 베이스로 이용할 것이므로 2-2 다음으로 2-1 보는것이 유리하다

2-2. CPP로 제작하기

Tip 알아두기
언리얼 엔진에서 사용하는 매크로는 반드시 UE_ 접두어를 가진다!!

시작하기전에 알아두기

CPP 코드 생성할 때, 상단의 Public, Private 선택하면 자동으로 헤더와 소스를 각각의 폴더로 생성해준다 → 임의 생성시 인식불가되는 현상이 있음
C++ 스크립트 작성 후 엔진의 Content drawer 에서 인식 안될때 다음과 같이 하면 해결될수도 있다.
1.
프로젝트를 닫는다. IDE 포함
2.
해당프로젝트 디렉토리로 이동
a.
Windows 의 경우 ***.uproject 파일에서 오른쪽 클릭 후 다음을 클릭하여 프로젝트 파일을 재생성 한다!
b.
Windows 11 의 경우 오른쪽 클릭 후 → 가장 아래에 추가옵션보기를 누르면 메뉴가 표시된

매크로

// UE_LOG(로그카테고리, 로그수준, 출력문자열) UE_LOG(LogTemp, Warning, TEXT("Hello world!"));
C++
복사
// TEXT, FString // string 의 경우 FString 구현체를 사용한다 // 문자열 초기화시 TEXT 매크로 필수!! // TEXT 매크로는 formatting 가능 // ex) TEXT("Hello %s!", TEXT("world")); FString Greeting = TEXT("Hello world");
C++
복사
// UPROPERTY(권한1, 권한2, ...) [데이터형] [변수명] ... // Unreal reflection 에서 지원하는 데이터 선언 매크로 // https://docs.unrealengine.com/5.3/ko/unreal-engine-uproperties/ UPROPERTY(EditAnywhere) int32 Numb = 1;
C++
복사
수정 권한
Option
Descript
EditAnywhere
어디서든 수정 가능
EditInstanceOnly
월드에 배치한 인스턴스만 수정 가능
EditDefaultsOnly
블루프린트 설정창에서만 수정 가능
보기권한
VisibleAnywhere
어디서든 보기
VisibleInstanceOnly
월드에 배치한 인스턴스에서만 보기
VisibleDefaultsOnly
블루프린트 설정창에서만 보기
내용 더 보기

Actor

Directional light
Cast shadow 옵션 → 그림자 계산하지 않음
Projection Mode
Perspective: 원근법, 관점
Orthographic: 직교투영
Pawn
Actor 를 상속한 언리얼 기본 클래스 APawn
Actor 에게 Component 추가 할땐 CreateDefaultsSubobject() 사용
UPROPERTY(EditAnywhere) UBoxComponent* BoxComp = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxComp"));
C++
복사
Actor 기본함수
// SetRootComponent(Component); // 선언한 컴포넌트를 루트 컴포넌트로 지정한다 // 액터 클래스 내부에서 지정 SetRootComponent(Component);
C++
복사
Component 에게 ParentComponent 설정하기
→ Child Component 에 부모정보 입력 (트리 추가 하듯)
Component->SetAttachment(Component);
C++
복사
24.02.23 - Input

Project settings - input

Project Input axis → 책에서 나온 Input 맵핑

1.
Project settings → Input 에서 Axis Action 추가
a.
여기서 추가하면 나중에 코드에서 사용할때 자동으로 코드가 생성된다
b.
자동완성에 ActionName 이 사용되므로 기억해두기
i.
Vertical → W = 1.0 , D = -1.0
ii.
Horizontal → A = 1.0, S = -1.0
2.
APawn::SetupPlayerInputComponent 함수에서 액션과 Actor 바인딩 한다
3.
캐릭터의 이동 (APawn 함수에 미리 정의된 함수)
GetActorLocation(); // 현재 Actor 의 위치 SetActorLocation(FVector); // 지정한 위치로 Actor 이동
C++
복사
4.
Key binding
void AMainPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent); // Possess 되면 키 바인딩 하기 PlayerInputComponent->BindAxis("MoveHorizontal", this, &AMainPlayer::MoveHorizontal); PlayerInputComponent->BindAxis("MoveVertical", this, &AMainPlayer::MoveVertical); PlayerInputComponent->BindAction("PopupMainMenu", IE_Pressed, this, &AMainPlayer::PressMainMenu); }
JavaScript
복사
stateDiagram-v2


state APawn {
	[*] --> Mesh_Component
	[*] --> Collider_Component
}
Mermaid
복사

기본단축키

F12 - New C++ Class
Ctrl + Alt + Shift + P - Recompile game code
p.499 - 24.02.25

1. Bullet 만들기

사용자의 컨트롤(possession) 필요하지 않다면 APwan 이 아닌 AActor 를 상속하면 된다
움직임있다 → AActor!!
BoxComponent→SetWorldScale3D(FVector()) 월드사이즈에 맞게 크기 줄이기
→ Scale 과 실제 크기(오브젝트) 서로 다른 값이다. 오브젝트의 크기는 WorldScale 에 따라서 결정된다. Object 의 크기가 절대값이 아님!! → 벡터이미지니까 축소확대 자유로움! 이라고 이해해두기
GetActorForwardVector() : AActor 의 내장 메소드, 엑터의 전방 벡터를 가저온다
TSubclassOf<class NM> : 블루프린트, 또는 외부에서 입력받는 오브젝트를 지정하기 위한 필드 선언 → 여기서는 총알의 클래스를 가저온다. ( 또는 Subclass 이므로 ABullet 을 상속한 것이 나와도 된다)
GetWorld() : 내장 함수, 월드의 정보를 가저온다
GetWorld()→SpawnActor<T>() 월드에 T 오브젝트를 인스턴스화 한다
EInputEvent
UENUM( BlueprintType, meta=(ScriptName="InputEventType")) enum EInputEvent : int { IE_Pressed =0, IE_Released =1, IE_Repeat =2, IE_DoubleClick =3, IE_Axis =4, IE_MAX =5, };
C++
복사

2. EnemyActor 만들기

전체 World 에서 특정 Actor 찾기
#include "EngineUtils.h" for (TActorIterator<APlayerPawn> Player(GetWorld()); Player; ++Player) { ... }
C++
복사

3. Collision

Project settings → Collision 에서 Object Channel 추가한다
추가한 순서대로 이름표를 가지게 된다. 순서대로 인덱스 번호를 받으므로 중요!!
DefaultEngine.ini 에도 채널 정보가 입력된다.
ex) ECC_GameTraceChannel1 등으로 이름이 지어진다.
Collision preset 사용하기
Project settings → Collision 탭에서, 맨 아랫부분 preset 에서 Collision set 생성 후 저장
Component 에 Collision 프로파일 이름을 입력하면 된다
BoxComp->SetCollisionProfileName(TEXT("지정한 이름"));
C++
복사
프리셋 적용후, 컴파일 후, 해당하는 블루프린트 객체로 들어가 리셋 콜리젼 탭에서 리셋하고 블루프린트를 컴파일 해준다.
Collision Delegate 선언 및 적용하기
Delegate 선언 파라미터 정보 가저오기
BoxComp→OnComponentBeginOverlap 을 코드에 입력하고 메소드 정의로 이동.
메소드 정의에서 FComponentBeginOverlapSignature 의 정의로 이동
여기서 one → six param 까지 다양하게 파라미터 정의가 존재
앞에 3개의 타입명 제외하고, UPrimitiveComponent* 부터 파라미터 정의
타입, 이름, 타입, 이름 방식으로 적혀있어서 일일이 , 를 삭제하여 맞춰준다
예)
// six params 예시 void ABullet::OnBulletOverlap( UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) { // 지정한 Actor 가 맞는지 타입 케스팅 해본다 AEnemyActor *Enemy = Cast<AEnemyActor>(OtherActor); // 캐스팅에 실패하면 nullptr 결과값이 나온다 if (Enemy == nullptr) return; // 지정한 타입이 맞다면 OtherActor 삭제 Enemy->Destory(); // 충돌했으므로 자신(총알)도 삭제 this->Destory(); }
C++
복사
BoxComp→OnComponentBeginOverLap.AddDynamic(this, &ABullet::OnBulletOverlap); 으로 연결 해준다
헤더에서 UFUNTION() 으로 선언해주어야 작동한다

Rider 단축키

Cmd + Shift + I - 헤더에서 메소드 커서위에 있을때 입력하면, 작은 화면 팝업으로 바로 정의 내릴수 있다.
F10 - 헤더, 소스에서 입력하면 에디터창을 헤더 - 소스, 소스 - 헤더로 각각으로 스왑한다.
p.516 - 24.02.25

2.6-6 UI 만들기

GameModeBase

점수 등 게임 전반적인 데이터를 관리하는 클래스
다른 오브젝트에서 GameBaseMode 접근하기
// 현재 월드의 게임모드베이스를 가저온다 AGameModeBase* CurrentMode = GetWorld()->GetAuthGameMode(); // 2. 원하는 게임모드인지 타입 케스팅 해본다 AShootingGameModeBase* ShootingGameModeBase = Cast<AShootingGameModeBase>(CurrentMode); // 3. nullptr 체크 후 사용 if (ShootingGameModeBase == nullptr) return; // 4. GameModeBase 사용 ShootingGameModeBase->AddScore(1);
C++
복사

UMG (Unreal motion graphics) 모듈 추가하기

UI 를 사용하기 위해서 UMG 모듈을 추가해주어야 한다
[프로젝트명].build.cs 파일 찾기
PublicDependencyModuleNames.AddRange(new string[] { "...", "UMG", // 추가 });
C++
복사
UUserWidget 클래스 상속하여 C++ 를 생성한다
UPROPERTY(EditAnywhere, meta = (BindWidget)) class UTextBlock* ScoreText;
C++
복사
meta = (BindWidget) 추후 Blueprint 에서 위젯 작성시 변수명 = 오브젝트명 동일하게 작성하면 자동 맵핑이 된다
Blueprint 화 한다
블루프린트화 하고 UI 의 기본 사이즈 지정 → 뷰포트의 오른쪽 하단 화살표 잡아 당기면 비율선택할수 있도록 변경된다.
Canvas 팔렛트 찾아서 추가 하면 화면에 맞는 4각형 점선이 생긴다.
Text 팔랫트 생성후 이름을 변수 이름과 동일하게 지어준다
ScoreData, ScoreText
GameModeBase 에서 생성한 Widget 연결하기
1.
에디터에서 위젯 설정할수 있게 UPROPERTY 추가
2.
에디터에서 설정한 프로퍼티를 화면에 추가
3.
점수 연동
// header public: // 에디에터에서 설정할수 있는 변수 지정 UPROPERTY(EditAnywhere) TSubclassOf<class UUserWidget> MainWidget; private: // UPROPERTY 에서 불러온 UI 를 메모리에 로딩한 값 저장할 변수 class UMainWidget* MainUI;
C++
복사
// cpp // 1. 위젯 지정 확인 if (MainWidget == nullptr) return; // 2. 위젯 메모리에 로딩 MainUI = CreateWidget<UMainWidget>(GetWorld(), MainWidget); if (MainWidget == nullptr) return; // 3. 화면에 추가하기 MainUI->AddToViewport();
C++
복사
특정이벤트 발생시 GameModeBase 에서 MainUI 불러와서 UI 갱신
void AShootingGameModeBase::AddScore(int32 Score) { this->CurrentScore += Score; this->PrintScore(); } void AShootingGameModeBase::PrintScore() { if (MainUI == nullptr) return; MainUI->ScoreData->SetText(FText::AsNumber(CurrentScore)); }
C++
복사
게임 일시 정지하기
GameModeBase 에서 실행하기
UGameplayStatics::SEtGamePaused(GetWorld(), true);
C++
복사
Mouse cursor 보이게 하기
GetWorld()->GetFirstPlayerController()->SetShowMouseCursor(true);
C++
복사
메인메뉴 기능
Map[Level] 불러오기
UGameplayStatics::OpenLevel(GetWorld(), "레벨이름");
C++
복사
게임종료하기
자세한 내용은 공식 도큐먼트 찾아보기
UKismetSystemLibrary::QuitGame( GetWorld(), GetWorld()->GetFirstPlayerController(), EQuitPreference::Quit, false );
C++
복사
위젯의 코드와 캔버스팔랫들과 연결해주기
Constructor 대신해주는 메소드
// header private: virtual void NativeConstruct() override;
C++
복사
// cpp void UMenuWidget::NativeConstruct() { Super::NativeConstruct(); // UPROPERTY 에서 meta = (BindWidget) 으로 변수이름 = Pallet 자동연결 된다 // OnClicked 이벤트를 코드와 연결시켜준다. ButtonRestart->OnClicked.AddDynamic(this, &UMenuWidget::Restart); ButtonQuit->OnClicked.AddDynamic(this, &UMenuWidget::Quit); }
C++
복사
23.03.03

3. TPS 제작하기

3-1. 프로토타입 만들기

3-1-1. 로그 매크로 작성

1.
프로젝트 루트 파일에 작성
// TPS.h // 1. 로그 카테고리 설정 DECLARE_LOG_CATEGORY_EXTERN(TPS, Log, All); // 2. Source 로그 라인 가저오는 매크로 생성 #define CALLINFO (FString(__FUNCTION__) + TEXT("(") + FString::FromInt(__LINE__) + TEXT(")")) #define PRINT_CALLINFO UE_LOG(TPS, Warning, TEXT("%s"), *CALLINFO) // 3. PRINT_LOG 매크로 생성 #define PRINT_LOG(fmt, ...) UE_LOG(TPS, Warning, TEXT("%s %s"), *CALLINFO, *FString::Printf(fmt, ##__VA_ARGS__))
C++
복사
// TPS.cpp // 카테고리 생성 DEFINE_LOG_CATEGORY(TPS);
C++
복사

3-1-2. 프로젝트 시작하기

1.
GameModeBase 생성
2.
Project settings 에 GameModeBase 입력
3.
GameModeBase 에 PlayerPawn 설정하기

3-1-3. 플레이어 생성하기

1.
Constructor 은 오브젝트 만들떄 1번만 실행 CDO 생성 때 1번 실행
→ 객체가 인스턴스화 할때 실행하지 않는다.
→ 인스턴스됬을때 초기화 함수는 BeginPlay() 메소드에 선언한다
2.
생성자에서 할일
a.
스켈레탈 메시 초기화
b.
Path 는 Content drawer 에서 컨텐츠선택후 오른쪽 클릭 → Copy reference 하면 된다
const ConstructorHelpers::FObjectFinder<USkeletalMesh> SkeletalMesh(TEXT("/Script/Engine.SkeletalMesh'/Game/Characters/Mannequin_UE4/Meshes/SK_Mannequin.SK_Mannequin'")); if (!SkeletalMesh.Succeeded()) return; this->GetMesh()->SetSkeletalMesh(SkeletalMesh.Object);
C++
복사
c.
메시의 위치와 회원값 초기화