총평
•
게임엔진에 생초보인 나에게 적당한 포지션의 책
•
게임개발에 프로그래밍이 없을수 없다 → 어느정도 프로그래밍에 익숙한 사람일수록 읽기 편하다
•
C++ 기본이라도 하고 와야 한다
•
많은양을 다루지만, 많은양을 스킵함 → 핵심내용은 혼자서 탐구하거나 다른 책을 찾아봐야 함
•
그러나 진짜 생기초 라면 이것도 모를것이므로 게임개발에 여러분야를 맛본다는 생각으로 임해야함. 이정도는 다 기본으로 알아야 뭐라도 만든다!
•
책을 구매당시 UE5 버전이 이미 상용출시 되었다. → UI, API 의 변화가 생겼지만 찾아보면 극복할만한 난이도이다.
24.02.16
1장..
•
스킵해도 됨
•
별 내용없음 → 의미 없음
2장. 슈팅게임 만들기
2-1. 블루프린트로 제작하기
•
이부분은 스킵
•
대부분 CPP 코드를 베이스로 이용할 것이므로 2-2 다음으로 2-1 보는것이 유리하다
2-2. CPP로 제작하기
시작하기전에 알아두기
•
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.
메시의 위치와 회원값 초기화