[이득우의 언리얼 C++ 게임 개발의 정석] Chapter 13. 프로젝트의 설정과 무한 맵의 제작

수민·2023년 4월 24일
0
post-thumbnail

이득우의 언리얼 C++ 게임 개발의 정석을 읽고 개인 공부 목적으로 요약 정리한 글입니다!


👀 프로젝트 정리와 모듈의 추가

프로젝트 정리

헤더파일은 public으로,
cpp 파일은 private으로 관리하도록 한다.

이렇게 정리해주면 깔 - 꼼하다

vs에서 보면 이렇게 잘 정리되어있고

에디터에서 보면 이렇게 잘 정리되어 있다.

모듈 추가

언리얼에서는 주 게임 모듈을 사용해서 게임 프로젝트의 로직을 관리한다.
모듈을 활용해서 프로젝트의 로직을 관리해주는 거였는데,
별도의 모듈을 추가해서 프로젝트의 확장성을 높일 수 있다.

언리얼 에디터는 주 게임 모듈을 생성해주고,
추가 모듈은 생성해주지 않는다.
그래서 내가 직 접 ! 추가해줘야 한다.

모듈 추가 시 필요한 요소

  • 모듈 폴더 : 모듈 폴더 (모듈명으로 된 폴더)
  • 빌드 설정 파일 : 모듈명.Build.cs 파일
  • 모듈의 정의 파일 : 모듈명.cpp 파일

직접 모듈 추가해 보기

책에서 제공해준 예제 코드를 주 게임 모듈과 같은 디렉토리에 배치해놓고,
주게임모듈명.Target.cs (게임 빌드 설정)
주게임모듈명Editer.Target.cs (에디터 빌드 설정)

파일을 수정하도록 하자

Target.cs

// Copyright Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;
using System.Collections.Generic;

public class Hunt_PrototypeTarget : TargetRules
{
	public Hunt_PrototypeTarget( TargetInfo Target) : base(Target)
	{
		Type = TargetType.Game;
		DefaultBuildSettings = BuildSettingsVersion.V2;
		ExtraModuleNames.AddRange( new string[] { "Hunt_Prototype", "ArenaBattleSetting" } );
	}
}

Editer.Target.cs

// Copyright Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;
using System.Collections.Generic;

public class Hunt_PrototypeEditorTarget : TargetRules
{
	public Hunt_PrototypeEditorTarget( TargetInfo Target) : base(Target)
	{
		Type = TargetType.Editor;
		DefaultBuildSettings = BuildSettingsVersion.V2;
		ExtraModuleNames.AddRange( new string[] { "Hunt_Prototype", "ArenaBattleSetting" } );
	}
}

이렇게 설정해주면 Binary 폴더에 새로운 파일이 생성된다.

이런식..
중간에 patch 파일이 많아서 2개로 잘라서 올림..

그러면
언리얼 에디터가 이 DLL 파일을 로딩할 수 있도록
uproject 파일에 새 모듈에 대한 정보를 넣어주자.

추가할 모듈을 먼저 로딩하도록 LoadingPhase 값을 PreDefault로 설정하자
그러면
새 모듈 - 주 게임 모듈 순으로 언리얼 에디터 프로세스에 올라가게 된당.

uproject

{
	"FileVersion": 3,
	"EngineAssociation": "5.0",
	"Category": "",
	"Description": "",
	"Modules": [
		{
			"Name": "ArenaBattleSetting",
			"Type": "Runtime",
			"LoadingPhase" :  "PreDefault"
		},
		{
			"Name": "Hunt_Prototype",
			"Type": "Runtime",
			"LoadingPhase": "Default",
			"AdditionalDependencies": [
				"Engine",
				"UMG",
				"AIModule",
				"ArenaBattleSetting"
			]
		}
	],
    ...
}

굿뜨.


👀 INI 설정과 에셋의 지연 로딩

INI 파일 이용법

언리얼 엔진은 언리얼 오브젝트의 기본값을 유연하게 관리할 수 있도록
외부 INI 파일에서 기본 속성 값을 지정하는 기능을 제공한다.

FSoftObjectPath : 에셋 경로를 보관하기 위한 클래스

언리얼 오브젝트가 기본 값을 INI 파일에서 불러들이려면 UCLASS 매크로에 config 키워드가 필요하다
config에 불러들일 INI 파일의 이름을 지정하고,
불러들일 PROPERTY 속성에 config 키워드를 설정하면 된다.
그러면
언리얼 오브젝트 초기화 시 해당 속성의 값을 INI 파일에서 읽어와 설정한다.

ABCharacterSetting.h

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

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "ABCharacterSetting.generated.h"

/**
 * 
 */
UCLASS(config=Hunt_Prototype)
class ARENABATTLESETTING_API UABCharacterSetting : public UObject
{
	GENERATED_BODY()
	
public:
	UABCharacterSetting();

	UPROPERTY(config)
	TArray<FSoftObjectPath> CharacterAssets;
};

이렇게 되면
초기화단계에서 Config 폴더의 DefaultHunt_Prototype.ini 파일을 읽어들여서 CharacterAssets 값을 설정한다.

클래스 기본 객체

엔진 초기화 시, 엔진 구동에 필요한 모듈들이 순차적으로 로딩되면서
기본값을 지정당해 생성되는 언리얼 오브젝트들.
엔진이 초기화되면 모든 언리얼 오브젝트 클래스 기본 객체가 메모리에 올라간 상태가 되고,
이 객체들은 GetDefault 함수를 통해 가져올 수 있당.
수명 : 초기화 ~ 엔진 종료까지

참조할 모듈 목록 추가하기

주 게임 모듈에서 새로 추가한 모듈을 사용하기 위해서
빌드 설정을 변경해주자.
Private(구현부)에서만 새 모듈을 사용하면 되니까
PrivateDependencyModule항목에 추가하면 된당!

Build.cs

// Copyright Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class Hunt_Prototype : ModuleRules
{
	public Hunt_Prototype(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
	
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG", "NavigationSystem", "AIModule", "GameplayTasks" });

		PrivateDependencyModuleNames.AddRange(new string[] { "ArenaBattleSetting" });

		// Uncomment if you are using Slate UI
		// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
		
		// Uncomment if you are using online features
		// PrivateDependencyModuleNames.Add("OnlineSubsystem");

		// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
	}
}

비동기 에셋 로딩

게임 진행 중에 비동기 방식으로 에셋을 로딩할 수 있도록 클래스 FStreambaleManager를 제공한다.
매니저는 프로젝트에서 하나만 활성화하는게 좋다.

AsyncLoad 함수를 통해 비동기방식으로 에셋을 로딩할 수 있다.

비동기방식으로 에셋을 로딩하도록 요청하고,
완료되었으면 델리게이트에 연결해놓은 함수를 호출한다.
정말..
서버 공부하면서 비동기 공부하길 잘했다

MyCharacter.cpp

void AMyCharacter::BeginPlay()
{
	Super::BeginPlay();

	...
    
	if (!IsPlayerControlled())
	{
		auto DefaultSetting = GetDefault<UABCharacterSetting>();
		int32 RandIndex = FMath::RandRange(0, DefaultSetting->CharacterAssets.Num() - 1);
		CharacterAssetToLoad = DefaultSetting->CharacterAssets[RandIndex];

		auto MyGameInstance = Cast<UMyGameInstance>(GetGameInstance());
		if (nullptr != MyGameInstance)
		{
			AssetStreamingHandle = MyGameInstance->StreamableManager.RequestAsyncLoad(CharacterAssetToLoad, FStreamableDelegate::CreateUObject(this, &AMyCharacter::OnAssetLoadCompleted));
		}
	}
}

void AMyCharacter::OnAssetLoadCompleted()
{
	USkeletalMesh* AssetLoaded = Cast<USkeletalMesh>(AssetStreamingHandle->GetLoadedAsset());
	AssetStreamingHandle.Reset();
	if (nullptr != AssetLoaded) {
		GetMesh()->SetSkeletalMesh(AssetLoaded);
	}
}

이 부분은 다시 좀 더 공부해서 포스팅할 가치가 있는 것 같당 !!


👀 무한 맵 제작

섹션 단위 레벨 구현 로직

레벨을 섹션(Section) 단위로 나누고
하나의 섹션 클리어 -> 새로운 섹션 등장
이 되도록 해보장.

맵 소켓에 문 객체들을 달고,
박스 콜리전들을 배치해서 충돌 체크를 한다.
그리고 이제 로직을 짜면,

스테이트 머신으로 설계한다

  • READY : 문 열고 대기. 중앙의 박스 트리거로 진입을 감지하면 BATTLE로 이동
  • BATTLE : 문 닫고 일정 시간 후 NPC 소환. 랜덤한 위치에 아이템 박스 생성, NPC 사망 시 COMPLETE로 이동
  • COMPLETE : 닫힌 문을 연다. 각 문의 트리거로 플레이어 감지하면 해당 방향으로 새로운 섹션 소환

OnConstruction 함수

에디터와 연결되는 엑터의 함수.
에디터 작업에서 선택한 액터의 속성이나 트랜스폼 정보가 변경되면 이 함수가 실행된다.
이 함수를 통해 액터, 컴포넌트의 속성을 설정하면 작업중인 레벨에서 결과를 확인할 수 있다 !!

동적 네비매시 생성

게임 실행 중에 동적으로 NavigationMesh를 생성하도록 하려면
프로젝트세팅 > 네비게이션 메시 > Runtime Generation > Dynamic
으로 변경하면 된당.


나머지는 기본 C++ 문법과 언리얼 오브젝트 생성 방법, 구현 로직 등을 생각해서 구현하면 된다!
책에 잘 나와있지만,,

profile
우하하

0개의 댓글