Unreal C++/VS 2017 Setup2017. 11. 23. 14:02

언리얼 엔진에서 C++를 사용하기 위한 VS 2017 Community Edition 설치 및 테스트


언리얼 엔진에서 C++ BlueprintFunctionLibrary 라는 클래스를 상속하여 하위 클래스를 생성하고 static 함수를 선언 및 정의하고 언리얼에서 컴파일만 해주면 그 외의 아무런 설정 없이 블루프린트에서 그 함수를 호출하는 노드를 바로 사용할 수 있다. 

여기서는 Visual Studio 2017 Community Edition을 설치하고 작동하는지 테스트하기 위해서 언리얼 엔진에서 BlueprintFunctionLibrary 기반 클래스를 작성하고 실행해보는 절차까지 안내한다


Unreal Engine 4.15 이상의 버전에 Visual Studio 2017 Community Edition을 설치하고 설정하는 방법

https://docs.microsoft.com/en-us/cpp/build/vscpp-step-0-installation

https://docs.unrealengine.com/en-us/Programming/Development/VisualStudioSetup



테스트 환경

  • Visual Studio 2017 Community Edition
  • Unreal Engine 4.17
  • Windows 8.1, 10




Visual Studio 2017 Community 설치시 확인할 사항

설치 옵션을 선택하는 창에서 아래처럼 Unreal Engine 항목에 선택하면 코딩시에 Code Assistance가 제대로 지원된다




Visual Studio와 Unreal Engine 4를 설치했다 하더라도 Windows SDK가 설치되지 않으면 Unreal Engine에서 C++ 클래스를 생성하더라도 오류가 발생한다. 오류 메시지를 잘 살펴보면 해당 OS의 SDK가 설치되지 않아서 컴파일에 실패한 경우에는 해당 SDK를 다운로드하여 설치해주면 된다.

오류 메시지에는 Windows 8.1 SDK Not installed 와 같은 직접적인 원인이 표시되므로 SDK 버전에 대해서는 고민할 필요가 없다



Visual Studio 설정시 권장되는 설정 사항


VS 2017 Community Edition Recommended Settings

툴바에서 마우스 우측 > 하위항목 맨 아래에서 [사용자 지정] 선택 > 명령 > 도구모음 > 우측의 콤보박스에서 [표준] 선택 > 미리보기 패널에서 [솔루션 구성] 선택 > 우측에서 [선택사항 수정] 선택 > [너비] 항목에 200 입력 > [확인] > 닫기


Add the Solution Platforms Dropdown

툴바에서 거의 끝에 있는 [표준 도구모음 옵션] 을 선택한다

[단추 추가/제거]에 마우스를 올리고 [솔루션 플랫폼]에 체크한다


Turn Off the Error List Window

도구 > 옵션 > 프로젝트 및 솔루션 > [오류로 인해 빌드가 종료될 때 항상 오류 목록표시] 체크해제 > 확인


Show Inactive Blocks 선택해제

도구 > 옵션 > 텍스트 편집기 > C/C++ > 뷰 > 비활성 블록 표시:False > 확인


Disable External Dependencies Folders 항목을 True 설정

도구 > 옵션 > 텍스트 편집기 > C/C++ > 고급 > 외부 종속성 폴더 사용 안함:True 설정 > 확인


Edit & Continue 기능해제

도구 > 옵션 > 디버깅 > 편집하며 계속하기:해제 > 확인


Intellisense, Live Errors, and Squiggles

도두 > 옵션 > 텍스트 편집기 > C/C++ > [고급] 선택 후 아래처럼 설정 > 확인

  • 자동 요약 정보 : True
  • IntelliSense 사용 안 함 : False
  • 자동 업데이트 사용 안 함 : False
  • 오류 보고 사용 안 함 : False
  • 물결선 사용 안 함 : False


Visual Studio 설치 및 설정 후 UE4와 연동 테스트


Unreal Engine 을 실행하고 임의의 템플릿(블루프린트)을 이용하여 프로젝트를 생성한다

Content Browser 영역에서 마우스 우측 > 새 C++ 클래스 > 모든 클래스에 체크 > 검색란에 BlueprintFunctionLibrary를 입력하여 부모  클래스를 선택한다


하단의 [선택] 버튼을 누르면 헤더파일, 소스파일의 파일경로를 묻는데 디폴트로 두고 [클래스 생성] 버튼을 누른다.


Content Browser에 [C++클래스] 노드가 생성되고 그 아래에 C++ 클래스가 생성된다



Conent Browser 에서 C++ 클래스가 보이지 않는 경우

Content Browser 우측 하단의 눈 아이콘(뷰옵션) > [C++ 클래스 표시]



자동으로 Visual Studio가 실행되면서 C++ 프로젝트에 헤더파일과 소스파일 등이 생성된 것이 확인된다. 보이지 않는다면 Visual Studio의 솔루션 탐색기에서 해당파일을 더블클릭하면 에디터에 보이게 된다



헤더파일(*.h)에 다음과 같이 입력한다(적색 부분이 추가된 내용이다)


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


#pragma once


#include "CoreMinimal.h"

#include "Kismet/BlueprintFunctionLibrary.h"

#include "MyBlueprintFunctionLibrary.generated.h"


/**

 * 

 */

UCLASS()

class CONTAINERTESTMAP_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary

{

GENERATED_BODY()


UFUNCTION(BlueprintCallable, Category="MyTest")

static FString greetings();

};



소스파일(*.cpp)에 다음과 같이 입력한다(적색 부분이 추가된 내용이다)


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


#include "MyBlueprintFunctionLibrary.h"


FString UMyBlueprintFunctionLibrary::greetings()

{

return FString(TEXT("Hello World"));

}


Visual Studio에서 위의 내용을 편집한 후에 저장하고 Unreal 레벨 에디터의 툴바에서 [컴파일] 버튼을 누르면 실패, 성공여부가 보여진다


위의 코드가 언리얼에서 컴파일에 성공하면 블루프린트에서 다음과 같이 함수노드를 생성하고 호출할 수 있다



Posted by cwisky

언리얼 Blueprint Function Library를 사용하여 정수배열을 정렬하는 예


테스트 환경

  • Visual Studio C++
  • Unreal Engine 4



블루프린트 프로젝트를 생성한다


Content Browser에서 마우스 우측 > 새 C++ 클래스 > 부모클래스 선택 창에서 [모든 클래스] 항목에 체크하고 Blueprint Function Library 클래스를 검색하여 선택한다. 파일이름을 디폴트로 두고 [선택] 버튼을 누르면 잠시 후 Visual Studio 가 실행되고 헤더파일과 소스파일이 생성된다.

헤더파일과 소스파일에 아래의 내용을 추가하여 저장하고 빌드할 필요는 없다. 언리얼 에디터의 툴바에서 [컴파일] 버튼을 누르면 C++ 컴파일이 시작되고 그 결과 완료 혹은 실패 메시지가 나타난다. 문법적인 오류가 없다면 컴파일에 성공할 것이다.


헤더파일에 다음과 같이 선언한다(적색 부분은 디폴트 내용에 새로 추가된 내용)

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


#pragma once


#include "CoreMinimal.h"

#include "Kismet/BlueprintFunctionLibrary.h"

#include "MyBlueprintFunctionLibrary.generated.h"


UCLASS()

class BP_CPP_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary

{

GENERATED_BODY()


UFUNCTION(BlueprintCallable, Category="TestFunction")

static TArray<int32> sort(TArray<int32> arr);

};



소스파일에 다음과 같이 선언한다(적색 부분은 디폴트 내용에 새로 추가된 내용)

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


#include "MyBlueprintFunctionLibrary.h"


TArray<int32> UMyBlueprintFunctionLibrary::sort(TArray<int32> arr)

{

int tmp = 0;

for (int i = 0; i < arr.Num() - 1; i++) {

for (int j = i + 1; j < arr.Num(); j++) {

if (arr[i] > arr[j]) {

tmp = arr[i];

arr[i] = arr[j];

arr[j] = tmp;

}

}

}

return arr;

}



위에서 생성한 C++ 함수는 언리얼 엔진의 블루프린트 그래프에서 함수노드를 이용하여 사용할 수 있다. 





C++의 Object 파생클래스를 블루프린트에서 사용하는 예


블루프린트 프로젝트에서 C++ Object 파생클래스를 작성하는 절차는 위와 동일하지만 부모 클래스를 Object 로 설정하며 코드 상에서 UCLASS() 매크로를 추가하고 블루프린트에서 Construct Object from Class 노드를 사용하여 파생 클래스의 인스턴스를 생성한다는 점이 다르다


BlueprintFunctionLibrary 파생클래스를 사용하지 않고 Object 파생클래스를 사용해도 된다. BlueprintFunctionLibrary 도 아니고 Actor도 아닌 Object 파생클래스를 C++에서 작성하고 블루프린트에서 사용하려면 클래스 선언문 위에 UCLASS(Blueprintable, BlueprintType) 매크로를 추가해야 하며, 블루프린트에서 호출하려면 Construct Object from Class 노드를 사용하면 된다


Object 파생클래스의 헤더파일 (적색 부분은 Visual Studio에서 추가한 내용)

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


#pragma once


#include "CoreMinimal.h"

#include "UObject/NoExportTypes.h"

#include "VarTest.generated.h"


/**

 * 

 */

UCLASS(Blueprintable, BlueprintType)

class CONTAINERTESTMAP_API UVarTest : public UObject

{

GENERATED_BODY()


UFUNCTION(BlueprintCallable, Category = "VarTest")

TArray<int> sort(TArray<int> arr);

};



Object 파생클래스의 소스파일 (적색 부분은 Visual Studio에서 추가한 내용)

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


#include "VarTest.h"


TArray<int> UVarTest::sort(TArray<int> arr)

{

for (int i = 0; i < arr.Num()-1; i++)

{

for (int j = i + 1; j < arr.Num(); j++)

{

if (arr[i] > arr[j])

{

int tmp = arr[i];

arr[i] = arr[j];

arr[j] = tmp;

}

}

}

return arr;

}



Visual Studio에서 위처럼 작성했다면 저장하고 언리얼 에디터의 툴바에서 [컴파일]을 눌러 컴파일을 마치면 블루프린트에서 다음과 같이 Construct Object from Class 노드를 사용하여 인스턴스를 생성하고 그 멤버함수를 호출할 수가 있다



Posted by cwisky
Unreal C++/Custom BP Nodes2017. 11. 21. 21:20

언리얼 엔진에서 C++을 이용하여 블루프린트에서 사용할 함수노드 만들기


원문참조 : https://wiki.unrealengine.com/Blueprints,_Creating_C%2B%2B_Functions_as_new_Blueprint_Nodes


언리얼 엔진에서는 블루프린트에서 함수노드로 사용할 수 있도록 C++ 클래스가 준비되어 있다. Blueprint Function Library 클래스의 파생 클래스에 static 함수를 선언하면 다른 블루프린트 그래프에서 해당 함수를 바로 검색하여 노드를 추가할 수 있게 된다


블루프린트 프로젝트를 생성한다


Content Browser에서 마우스 우측 > 새 C++ 클래스 > 부모클래스 선택 : [모든 클래스]에 체크 > Blueprint Function Library 검색하여 선택 > 저장경로를 지정하거나 디폴트로 두고 진행하면 Visual Studio가 실행되면서 에디터에 헤더파일(*.h)과 소스파일(*.cpp)이 생성된다.


생성된 파일의 내용에 아래처럼 적색으로 표시한 내용을 추가하고 저장한 후에 다시 언리얼 메인 에디터의 툴바에서 [컴파일]을 누르고 컴파일이 완료되었다는 메시지가 나타나면 다른 블루프린트에서 이 함수를 호출할 수 있다. 만약 문법적인 오류가 있다면 컴파일 실패 메시지가 뜬다. Visual Studio에서는 컴파일이나 빌드를 실행할 필요가 없다. Visual Studio에서는 코드에 오류가 없는데도 불구하고 오류가 있는 것처럼 코드에 적색라인으로  표시되는 경우가 있는데 이 때는 신경쓰지 않고 저장 후에 언리얼 툴바에서 [컴파일] 버튼을 눌러 컴파일해보면 오류 여부를 알 수 있다. 언리얼 툴바에서 컴파일 후에는 다른 블루프린트 그래프에서 라이브러리 함수를 노드로 추가하여 사용할 수가 있다


헤더파일의 내용

#include "CoreMinimal.h"

#include "Kismet/BlueprintFunctionLibrary.h"

#include "MyBlueprintFunctionLibrary.generated.h"


UCLASS()

class BP_CPP_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary

{

GENERATED_BODY()


UFUNCTION(BlueprintCallable,Category="TestFunction")

static FString MySampleFunction(FString str);

};



소스파일의 내용

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


#include "MyBlueprintFunctionLibrary.h"


FString UMyBlueprintFunctionLibrary::MySampleFunction(FString str)

{

return str;

}



언리얼 메인 에디터에서 C++코드를 컴파일한다




다른 블루프린트의 그래프에서 라이브러리 함수를 호출(함수 노드 추가)한다


Posted by cwisky

언리얼 엔진에서 커스텀 구조체 선언, 초기화, 사용의 예


원문참조 : https://docs.unrealengine.com/latest/INT/Engine/Blueprints/UserGuide/Variables/Structs/


구조체 생성 및 정의

Content Browser 안에서 마우스 우측 > 블루프린트 > 구조체

이름을 BP_PlayerStruct 등으로 적당히 입력한다




구조체 멤버변수 선언(필요한 만큼 변수를 선언하고 자료형을 선택하고 [저장]을 누른다)




구조체 변수 선언(임의의 블루프린트 이벤트 그래프에서 다음과 같이 구조체 변수를 선언한다)



구조체 멤버변수 초기화 및 사용(블루프린트 그래프에서 아래처럼 구조체 멤버변수를 초기화하고 사용한다)



구조체 특정 멈버변수만 선택적으로 설정하기 ( Set Members in Struct 노드를 추가하고 선택한 후 [디테일] 뷰에서 초기화할 변수를 선택하고 노드에서 값을 입력하면 된다)


구조체가 아닌 Object 를 기반으로 한 클래스 블루프린트를 생성하여 위와 같은 일을 할 수도 있다. 이런 경우에는 구조체보다 더 많은 메모리를 차지할 것이므로 많은 오브젝트를 사용해야 하는 경우라면 먼저 구조체의 사용을 고려해야 할 것이다


아래는 Object를 기반으로 클래스 블루프린트를 생성하고  사용하는 예이다



위와 같이 생성된 블루프린트의 이름을 BP_PlayerInfo 라고 선언 했다면 아래와 같이 다른 블루프린트에서 BP_PlayerInfo 형의 변수를 선언하고 사용할 수 있다.



Posted by cwisky
Packaging2017. 11. 19. 13:13

원문참조 https://docs.unrealengine.com/latest/INT/Engine/Basics/Projects/Packaging/


Edit > Project Settings > 프로젝트 / 패키징 / 프로젝트 / Build Cinfiguration 항목에서 DevelopmentShipping 선택

 - Development : 커맨드라인에서 실행할 수 있다


Edit > Project Settings > Maps & Modes

 - Editor StartupMap : 해당 프로젝트를 에디터로 열때 보여줄 맵의 이름 지정

 - Game Default Map : 게임이 시작될 때 사용될 첫번째 맵의 이름을 지정


File > Package Project > [PlatformName]

 - 패키징하여 저장할 디렉토리를 선택한다

 - 패키징 진행 윈도우의 [출력로그표시] 링크를 누르면 패키징 상태를 확인할 수 있다

 - 패키징이 완료되면 해당 디렉토리에 실행파일이 생성된다


작은 패키징 안내 화면에서 [출력로그표시]를 누르면 아래와 같이 패키징 상황을 확인할 수 있는 화면을 볼 수 있다



패키징 후에 게임 실행하기

네트워크 게임이 아니라면 실행파일을 실행하면 된다

네트워크 게임이며 Listen Server로 실행하려면 먼저 서버를 실행한 후에 접속할 클라이언트를 실행하면 된다


Listen Server 실행

 - 커맨드라인 실행명령 형식 : 실행파일명 맵이름?listen -game

 - 커맨드라인 실행명령의 예 : MyGame.exe MyMap?listen -game


Listen Server에 접속할 Client 실행

 - 커맨드라인 실행명령 형식 : 실행파일명 리슨서버IP주소 -game

 - 커맨드라인 실행명령의 예 : MyGame.exe 127.0.0.1 -game


Posted by cwisky
Networking/PlayerState2017. 11. 13. 16:47

언리얼 엔진에서 커스텀 PlayerState 사용하는 예


PlayerState 는 네트워크 게임에서 매우 유용한 클래스이다. 이름에서 그 용도를 짐작할 수 있듯이 게임 플레이어의 모든 상태를 저장하고 다른 클라이언트에게 까지 전달할 수 있는 특징을 가지고 있다


PlayerState 객체는 모든 클라이언트 머신과 서버 머신에서 공히 접근이 가능하며 플레이어 하나에 한개의 PlayerState 가 할당되고 레벨이나 플레이어 폰(캐릭터)가 메모리에서 제거(Destroyed)되더라도 PlayerState 는 계속 존재하며 플레이어의 정보를 유지할 수 있다.


PlayerState 기반의 클래스 블루프린트 생성

커스텀 PlayerState 에 임의의 변수 선언

커스텀 GameMode 에 위에서 생성한 PlayerState 등록

커스텀 GameMode를 월드세팅 / GameMode에 등록

커스텀 GameMode를 Edit > 프로젝트 세팅 > 맵 & 모드 패널의 Default Modes / Default GameMode에 설정

이벤트 그래프에서 GetPlayerState 노드와 형변환 노드를 이용하여 커스텀 PlayerState 참조를 구한다

PlayerState 참조를 이용하여 그 안에서 선언한 변수에 접근하여 값을 변경한다


다음은 마우스로 큐브를 클릭하면 점수를 1점씩 올리며 5점에 도달하면 'You WIN!' 메시지를 화면에 출력하는 예이다



Posted by cwisky
Networking/Summary2017. 11. 12. 17:10

Network Conpendium의 요약정리


자료를 참고하여 정리하는 과정에서 작성자의 오해나 이해가 부족한 점이 있을 수 있음을 알려드립니다


Network Multiplayers 게임관련 주요 클래스


GameInstance

GameMode

GameState

Pawn (and Character, which inherits from Pawn)

PlayerController

PlayerState


게임 데이터를 저장할 때 고려해야 할 사항

 - Pawn은 게임 내에서 죽거나 제거되는 경우가 많으며 그때마다 폰 클래스에 저장한 데이터도 사라진다

 - PlayerController, PlayerState는 새 레벨이 로드되지 않는 한 폰이 제거되어도 계속 존재한다


GameInstance

 - 게임인스턴스는 게임엔진이 시작될 때부터 종료할 때까지 존재한다

 - 서버와 클라이언트에 각각 하나의 게임인스턴스가 있고 서로 통신하지는 않는다

 - 현재 게임세션의 밖에 존재하며 레벨로드에 영향을 받지 않은 상태로 게임을 구성한다

 - 영구적인 정보를 저장할 수 있는 적합한 장소이다


서버에만 존재하는 객체

 - GameMode


서버와 모든 클라이언트에 존재하는 객체

 - GameState, PlayerState, Pawn


서버와 소유한 클라이언트에 존재하는 객체

 - PlayerController 는 클라이언트와 서버에 존재하지만 다른 클라이언트끼리는 공유하지 않는다

 - PlayerController는 클라이언트 서버간의 통신에 관련한 작업을 주로 한다


클라이언트에만 존재하는 객체

 - HUD, UMG Widgets


GameMode 클래스의 오버라드 가능한 함수

 - 게임의 룰을 결정한다(플레이어 수, 최고점수 등)

 - Ready to start Match : 클라이언트 수가 채워지면 true를 리턴하고 아니면 false리턴

 - Event On Post Login : 클라이언트의 PlayerController를 파라미터를 통해 가져온다. PlayerController를 배열에 저장해 놓으면 나중에 유용하다

 - Choose Player Start : 다수개의 Player Start 액터가 레벨상에 존하는 경우에 그 중 아직 점유되지 않은 Player Start를 찾아 리턴한다

 - Start Match, Restart Game, End Match, Has Match Started, Abord Match, Has Match Ended, Is Match in Progress, Event OnSetMatchState, Get Match State 등의 함수 및 이벤트를 오버라이드할 수 있고 수동으로 호출도 가능하다

 - 이 함수들은 Ready to start Match 함수가 true 를 리턴한 후에 대부분 자동으로 호출되지만 수동으로 호출도 가능하다



GameState

 - 클라이언트/서버간의 게임의 현재 상태에 대한 정보교환을 위한 중요한 클래스

 - 멀티 플레이어어 게임에서 중요한 정보인 접속된 플레이어 리스트(PlayerState의 리스트)를 포함한다

 - GameState는 모든 클라이언트에게 Replicated 되므로 모든 클라이언트가 이 객체에 접근할 수 있다

 - 멀티플레이어 게임에서 가장 핵심적인 클래스 중에 하나이다

 - GameMode가 승리를 위한 점수를 가지고 있는 반면, GameState는 현재까지 취득한 점수를 가지고 있다

 - GameState에는 개발자가 임의의 정보(배열이나 구조체 등)를 저장할 수 있다

 - GameMode에 비해 개발자가 다루어야 할 작업은 적은 편이지만 이벤트 그래프에는 모든 클라이언트가 알아야 할 로직을 작성할 수 있다

 - PlayerArray MatchState, ElapsedTime은 replicated 설정되어 있으므로 모든 클라이언트에서 접근할 수 있다

 - GameState에 선언한 변수를 Replicated 설정하고 Switch has Authority 를 사용하여 서버측에서 값을 변경하면 모든 클라이언트에서 확인할 수 있다


PlayerState

 - 모든 접속된 클라이언트는 현재 클라이언트의 정보를 포함하고 있는 한개의 PlayerState 객체를 갖는다

 - PlayerState 객체는 모든 클라이언트에게 Replicated 되므로 어떤 클라이언트에서 다른 클라이언트의 정보를 접할 수가 있다

 - 현재 클라이언트에서 다른 클라이언트의 PlayerState 객체에 접근하는 쉬운 방법은 GameState::getPlayerArray 를 이용하는 것이다

 - PlayerName, Score 등 다른 클라이언트에게 제공해야 하는 다양한 정보(커스텀 변수)를 이 객체에 저장하여 다른 클라이언트에게 전달할 수 있다

 - PlayerPawn이 Destroy 되더라도 PlayerState는 유지된다


Pawn

 - Actor를 기반으로 파생된 클래스이며 플레이어에 의해 조종될 수 있는 액터이다

 - 플레이어는 한번에 한개의 폰을 소유할 수 있으며 쉽게 다른 폰으로 전환할 수 있다(un-possessing, re-possessing)

 - Pawn은 대부분 모든 클라이언트에게 Replicated 된다. Actor Replicated 설정된 상태

 - 폰은 대부분의 경우 사람 형태이지만 동물이나 자동차, 비행기, 배, 블럭 등도 될 수 있다

 - Pawn의 하위 클래스인 Character클래스는 네트워크에서 지원되는 MovementComponent를 포함하고 있어서 위치, 회전 등의 정보가 Replicate 된다


Actor Replication

- 맵에 배치된 액터를 선택하고 [디테일] 뷰 / Replication / 안에 있는 체크박스이다 

- Net Load on Client : 클라이언트에 맵이 로드되면서 동시에 이 항목이 체크된 해당 액터도 맵과 함께 클라이언트에게 보여지게 된다

 - 서버 측에는 위의 항목과 무관하게 보여진다

 - Replicate : 서버에 스폰될 때 클라이언트에게도 Replicate 된다. 

 - 서버측에 스폰하는 것이 아니라 클라이언트에 스폰될 때는 위의 항목설정과 무관하게 액터가 보여진다

 - 클라이언트에서 스폰하는 경우에는 서버측에 Replicate 되지는 않는다

 - 게임의 승패에 영향을 주는 중요한 액터를 생성할 때는 반드시 서버에 스폰하고 클라이언트에게 Replicate하는 것이 보안상 최선책이다

 - 게임의 승패에 영향이 없는 장식성 액터의 출력도 서버에 스폰하여 클라이언트에 Replicate해도 된다

 - 서버에서 Replicate설정된 액터를 삭제하면 모든 Replicate 한 클라이언트에서도 해당 액터가 삭제된다

 - 네트워크 상에서는 클라이언트가 Character를 움직이는 것이 아니라 클라이언트의 입력을 받아서 서버에서 Character를 움직이고 클라이언트에 Replicate 된다



Event replication

https://docs.unrealengine.com/latest/INT/Gameplay/Networking/Blueprints/index.html

 - 서버측에서 발생하는 이벤트만 유효하게 사용될 수 있도록 이벤트 함수 안에서 Switch Has Authority 노드를 사용하여 현재 인스턴스가 서버측에서 실행되는 것인지 확인 할 수 있다


Switch has Authority

 - 현재의 스크립트가 서버측에서 실행되는 클라이언트 로직인지 리모트 클라이언트에서 실행되는 로직인지 확인하여 로직을 분기한다

 - 현재 스크립트를 실행하는 머신이 서버인지 클라인지를 식별하여 실행내용을 다르게 작성할 수 있다

 - 실행 중인 이벤트 함수가 서버측에서 실행 중인지 확인하여 그 결과를 다른 클라이언트에게 복제할 때도 사용할 수 있다

 - 네트워크 게임은 모든 클라이언트의 화면의 내용이 같고 보는 방향만 다르므로 맵은 모두 동일하다고 할 수 있다

 - 현재 실행되고 있는 머신이 서버인지 클라이언트인지를 구분하는 이유는 게임의 승패와 관련한 중요한 변수의 변경은 서버측 머신에서만 하려고 하기 때문이다


Function Replication (Remote Procedure Calls or RPCs for short)

https://docs.unrealengine.com/latest/INT/Gameplay/HowTo/Networking/ReplicateFunction/Blueprints/index.html

https://docs.unrealengine.com/latest/INT/Gameplay/Networking/Blueprints/index.html

 - 로컬 머신에서 호출되어 다른 머신에서 실행된다

 - 클라이언트, 서버간의 데이터 전송방법으로 유용하게 사용된다

 - Reliable : 네트워크 상황과 무관하게 데이터가 반드시 전달된다

 - Unreliable : 네트워크 상황에 따라 전달하려는 데이터가 손실될 수도 있다

 - Multicast : 서버측에서 호출되어 서버에서 실행되고 모든 클라이언트들에게도 포워드된다

 - Run on Server : 클라이언트 측에서 호출하고 서버에서만 실행된다

 - Run on owning Client : 서버측에서 호출하고 소유한(owning client)클라이언트에서만 실행된다

 - PlayerController 에 의해 소유된 폰은 위의 2가지 타입(Run on Server, Run on owning Client)의 함수를 가질 수 있다

 - Run on Server, Run on owning Client 함수의 호출과 선언은 클라이언트의 PlayerController, PlayerState, PlayerController가 소유한 Pawn에서만 해야한다



Multicast Function 테스트

 - Multicast 로 설정된 함수를 서버 윈도우에서 실행하면 서버 화면 뿐만 아니라 다른 모든 클라이언트 화면에서도 그대로 재현된다. 그러나 클라이언트 화면에서 Multicast 함수를 실행하면 현재 클라이언트에서만 효과가 나타난다

 - Multicast 로 설정된 함수는 서버에서 호출되고 서버에서 실행되어 모든 클라이언트에게 포워드되어 실행되기 때문이다


Server Function 테스트

 - 클라이언트가 서버로 데이터를 전송하기 위한 주요 방법이다

 - Ron on Server 로 설정된 함수를 클라이언트에서 호출하면 서버측 화면에서만 효과가 나타나고 현재 클라이언트 화면에는 효과가 나타나지 않는다

 - Run on Server로 설정된 함수는 클라이언트에서 호출되어 서버에서 실행되기 때문이다

 - Run on Server로 설정된 함수를 호출하여 서버에서 특정액터의 변수의 값을 변경하고 그 액터가 Replicates 로 설정된 경우에는 클라이언트가 호출한 로직이 서버에서 실행되고 다른 클라이언트에게 Replicated된다

 - 클라이언트 측에서 호출할 수 있으므로 Run on Server로 설정된 함수 안에서 다시 Multicast 함수를 호출하면 클라이언트가 호출한 로직은 서버에서 실행되고 모든 클라이언트에게 포워드되어 실행되므로 모든 클라이언트에서 효과가 나타난다


Run On Owning Client Function 테스트

 - 서버에서 호출할 수 있고 특정 클라이언트만 선택하여 해당 클라이언트에서만 실행되도록 할 때 사용한다

 - 예를 들어 서버측 스크립트 실행 중 특정 클라이언트의 폰이 트리거에 들어가는 경우 해당 폰을 소유한 클라이언트에게만 이벤트 실행결과를 보여주려는 경우에 사용할 수 있다



Replicating Variables

https://docs.unrealengine.com/latest/INT/Gameplay/HowTo/Networking/ReplicateVariable/index.html

액터 클래스에 선언된 변수가 Replicated 로 설정된 경우, 서버에서 해당 변수의 값을 변경하면 모든 클라이언트 머신에도 변경된 값이 복제된다. 이 때 반드시 서버측에서 값이 변경된 경우에만 클라이언트에 복제가 된다


Actor Replication, Event Replication

https://docs.unrealengine.com/latest/INT/Gameplay/HowTo/Networking/ReplicateActor/Blueprints/index.html

https://docs.unrealengine.com/latest/INT/Gameplay/Networking/Blueprints/index.html

https://docs.unrealengine.com/latest/INT/Gameplay/Networking/Actors/index.html


Testing Multiplayers

https://docs.unrealengine.com/latest/INT/Gameplay/HowTo/Networking/TestMultiplayer/index.html



변수복제 VS 함수복제

변수복제와 함수복제를 위해서는 서버측에서 조작되는 객체가 클라이언트 측에도 있어야 한다. 그러한 이유로 변수복제와 함수복제에 사용할 수 있는 객체는 PlayerController 이거나 Pawn 일 경우가 많다

모든 클라이언트에 존재하는 PlayerController 객체는 서버측에도 생성된다. 그러므로 서버측에서 특정 PlayerController의 변수를 변경하고 그 변수의 값이 클라이언트 측에도 변경되도록 하려면 해당 변수를 Replication 설정해야 한다. 이렇게 하면 특정 PlayerController 의 속성을 변경하면 자동으로 해당 PlayerController 가 존재하는 클라이언트 측에서도 그 변수의 값이 변경된다. 

서버측에서는 모든 클라이언트의 PlayerController를 가지므로 변수복제를 이용하여 모든 클라이언트의 화면에 다른 값을 표시할 수가 있다.

변수복제는 서버측에서 변경된 변수의 값이 클라이언트에서도 변경되도록 할 때 사용한다. 그 반대는 안된다

서버측에서 모든 클라이언트의 PlayerController를 구할 때는 Get All Actors of Class() 함수노드를 사용하면 된다


레벨에 올려진 캐릭터는 모든 클라이언트에서 서로를 볼 수가 있기 때문에 모든 클라이언트의 컴퓨터에는 모든 캐릭터가 기능을 하고 있다고 할 수 있다. 그런데 게임 중에서 특정 캐릭터가 점프를 한다면 모든 클라이언트 화면에서 해당 캐릭터가 점프하는 동작이 보여져야 한다. 이 때는 서버측에서 점프 동작을 수행하고 그 동작이 모든 클라이언트에 존재하는 해당 캐릭터에 그대로 재현되어야 한다. 이러한 일은 함수복제 중에서도 Multicast 설정으로 할 수 있다

서버측에서 특정 클라이언트의 캐릭터에 접근할 때는 PlayerController:: Get Controlled Pawn() 함수노드를 사용하면 된다


Posted by cwisky

Function Replication 을 활용하여 [서버에서 실행], [멀티캐스트] 를 연동하여 사용하는 예


클라이언트에서 함수를 호출하여 서버에서 실행되도록 하고 서버에서 실행 중에 멀티캐스트 함수를 호출하면 클라이언트에서 호출하여 서버를 거쳐 모든 클라이언트에 포워드되는 기능을 구현할 수 있다


아래의 예제는 플로어에 놓인 큐브를 양측 상대방 화면에서 클릭하면 상대방이 있는 방향으로 큐브가 이동하게 하는 예이다. 커스텀 이벤트를 2개 생성하여 Function Replication를 설정할 때 각각 [서버에서 실행], [멀티캐스트]로 설정하였다


[서버에서 실행] 으로 설정하면 클라이언트 측에서 호출하여 서버에서 함수가 실행되도록 할 수 있고, [멀티캐스트]로 설정하면 서버에서 호출하여 서버에서 실행되고 접속된 모든 클라이언트에게로 포워드되어 실행된다


[서버에서 실행] 으로 설정된 함수(커스텀 이벤트) 가 [멀티캐스트]로 설정된 함수를 호출하므로 결과적으로 클라이언트가 호출한 함수는 서버에서 실행되고 멀티캐스트 함수가 실행되어 포워드되므로 모든 클라이언트에서도 실행된다



Posted by cwisky

Function Replication을 설정하여 모든 클라이언트에게 특정 기능을 실행하도록 하는 예


Dedicated Server를 통해 게임을 호스팅하려면 별도의 언리얼 엔진 소스를 컴파일하여 사용해야 하지만 Listen Server는 다른 준비가 필요 없고 언리얼 엔진 설치 버전만 있다면 바로 개발이 가능하다. 여기서는 Listen Server에서 실행될 간단한 네트워크 기능을 테스트하려고 한다.


플레이어1, 플레이어2가 한개의 큐브 액터를 클릭할 때마다 큐브 액터가 튕겨져 나가도록 하고 양쪽 플레이어의 윈도우에서 큐브의 움직임이 동기화되어 움직이도록 한다


Default Pawn을 기반으로 커스텀 폰 클래스 블루프린트 생성

Game Mode 를 기반으로 커스텀 게임모드 클래스 블루프린트 생성

커스텀 게임모드에 커스텀 폰 클래스 등록

언리얼 레벨 에디터의 월드세팅 / Game Mode 에 커스텀 게임모드 등록


뷰포트에 PlayerStart 2개가 플로어의 양단에 오도록 배치한다


Actor Replication 설정

클라이언트들이 공유할 액터를 생성한다(StaticMeshActor를 기반으로 한 클래스 블루프린트 생성)


아래의 그림과 같이 액터에 Replication을 설정한다. 서버측에서 큐브의 위치가 변경되면 클라이언트에게도 반영되도록 설정한 것이다.


생성한 큐브 액터를 뷰포트에 배치한다

큐브 액터가 Static이 아닌 Movable 으로 설정하고 Simulate Physics 도 체크한다


커스텀 폰 클래스의 [이벤트 그래프]에 다음과 같은 내용을 작성한다


Function Replication 설정

위의 그림에서 하단에 위치한 커스텀 이벤트를 선택하고 다음과 같이 설정한다. [서버에서 실행] 타입의 함수는 클라이언트에서 호출하고 서버에서 실행되는 함수를 말한다. 내부에서는 RPC(Remote Procedure Call)방식이 이용된다. 서버측에서 큐브의 위치가 변경되면 큐브액터는 Replicates 설정되어 있으므로 클라이언트에게도 반영된다.


게임을 실행할 때 Dedicated Server를 시뮬레이션하기 위해 아래처럼 실행한다


다음과 같이 2개의 윈도우에 게임이 실행되고 각 윈도우에서 마우스로 큐브를 클릭하면 양쪽 윈도우에서 큐브가 동기화되어 튕겨 나가는 것을 확인할 수 있다


폰 클래스를 Default Pawn을 사용하지 않고 ThirdPersonCharacter를 복사해서 사용하면 2명의 플레이어에 의해 제어되는 2개의 캐릭터를 뷰포트에서 확인할 수 있다


Posted by cwisky
Billiard/TextRender2017. 10. 31. 17:08

언리얼 엔진의 TextRender를 이용할 때 한글이 깨지는 문제 해결


블루프린트 액터에 TextRender 컴포넌트를 추가해서 영어를 출력하면 다음과 같이 아무런 문제가 없는 것을 확인할 수 있다. 그러나 한글을 출력하기 위해 입력하면 제대로 한글이 출력되지 않는 문제가 발생한다. 


TextRender 에 영어를 출력하면 문제가 없다



TextRender를 통해 출력한 한글이 깨지는 현상



언리얼 엔진(4.16.3)에서 위의 문제를 해결하기 위한 방법은 로컬 시스템에서 지원하는 폰트를 등록하고 사용하면 된다. 

좀더 구체적으로 설명하자면 언리얼 컨텐트 뷰에서 폰트를 새로 생성하여 현재 시스템에서 지원하는 폰트를 찾아 등록하고 생성된 폰트를 TextRender에 설정하여 사용하면 된다.


새로운 폰트 생성

컨텐트 브라우저에서 마우스 우측을 누르고 [User Interface > 폰트] 를 선택하고 이름을 Font_KO 등으로 지정한다



새로 생성된 Font_KO를 더블클릭하여 에디터를 열고 [디테일]뷰 / Font / Font Chche Type 항목에서 Runtime 대신 Offline 으로 설정하면 다음과 같이 묻는 창이 뜬다



글꼴 선택 창에서 지원되는 한글 글꼴과 스타일, 크기를 선택하고 [확인] 버튼을 누른다



아래의 그림에서 [디테일]뷰/Font/Font Cache Type 항목이 Offline으로 변경된 것을 확인할 수 있다. 

아래의 창에서 [디테일/Import Options/Chars] 항목에 표준한글 문자를 복사해 넣으면 된다. 한글 표준문자는 아래에 첨부된 텍스트파일에 저장되어 있으므로 다운로드하여 메모장으로 열고 모두 복사하여 Chars항목에 붙여 넣으면 된다

KS-1001.txt

아울러 Texture Page Width, Texture Page Max Height 항목도 1024로 변경한다



폰트 에디터의 툴바에서 [애셋> Font_KO 리임포트] 항목을 선택하고 [저장] 을 누른다



TextRender 컴포넌트에 새로운 폰트 설정

폰트 에디터를 닫고 언리얼 레벨 에디터로 이동하여 TextRender 를 선택하고 [디테일]뷰 / Text / Font 항목에서 Font_KO를 선택한다



텍스트 머티리얼도 변경해야 하므로

위의 화면에서 Font 항목 위의 Text Material 항목에 있는 돋보기 아이콘을 클릭하고 컨텐뷰에 있는 텍스트 머티리얼을 찾아 더블클릭하여 머티리얼 에디터를 연다.



위의 화면에서 Font 노드의 R 핀에 Lerp노드의 Alpha 핀이 연결되어 있는데 아래의 그림과 같이 Font 노드의 Alpha 핀이 Lerp의 Alpha 핀에 연결되도록 변경하고 Font 노드가 선택된 상태에서 [디테일]뷰 / Material Expression Font Sample / Font] 항목에서 앞서 생성된 Font_KO를 선택하고 툴바에서 저장 버튼을 누른다



TextRender 에 한글출력 테스트

머티리얼 에디터를 닫고 언리얼 레벨 에디터로 돌아가서 TextRender 컴포넌트에 한글이 제대로 출력되는지 확인한다



게임을 실행했을 때도 제대로 한글이 출력되는지 확인한다



보다 실용적인 예제로, 마우스로 액터를 클릭할 때 클릭된 횟수를 TextRender에 출력하는 블루프린트를 작성해본다. 아울러 TextRender가 카메라를 향하여 항상 정면을 보이도록 회전하게 한 내용이다



Posted by cwisky