요구사항
Platform SDK를 사용하기 위한 요구 사항은 아래와 같습니다.
Visual Studio 2022 Version 17.9.6
연동 준비
SDK 설치
언리얼 엔진에서 Platform SDK를 사용하기 위해서는 배포된 플러그인을 프로젝트의 Plugins 복사합니다.
UnrealStarter 샘플 프로젝트
플랫폼SDK 플러그인을 적용한 언리얼 샘플 프로젝트를 제공합니다.
UnrealStarter_x.x.x.zip 파일을 압축해제 한 후 UnrealStarter.uproject 파일을 사용하세요.
또는 UnrealStarter.uproject 파일을 이용해서 Visual Studio 프로젝트를 생성 후 visual studio 솔루션 파일을 통해 빌드 후 확인할 수 있습니다.
Visual Studio에서 디버깅하기
프로젝트 속성 > Debugging > Command Arguments 를 아래와 같이 설정 한 후 빌드합니다.
Copy "$(SolutionDir)UnrealStarter.uproject" -game
빌드 설정
ProjectName.Build.cs 파일에 SDK 플러그인 모듈 사용을 추가합니다.
Copy // HybePlatform Plugin 사용설정
PrivateDependencyModuleNames.AddRange(new string[] { "WebBrowser", "HybePlatform" });
스팀 설정
스팀 연동D정보를 DefaultEngine.ini에 SteamAppId를설정한합니.
Copy [HybeGamePlatform]
SteamAppId=스팀앱ID
API 연동
API Flow
플러그인 기본 구조
HybePlatformAgent
Platform의 모든 기능을 통합해서 제공합니다.
HybePlatformAuthAgent
인증(Auth) 연동 이벤트를 제공하는 클래스.
게임 클라이언트에서 상속한 클래스를 제공해야 합니다.
HybePlatformWebviewAgent
인증 과정에서 발생하는 웹뷰 이벤트를 제공하는 클래스
게임 클라이언트에서 상속한 클래스를 제공해야 합니다.
초기화
게임의 초기화 프로세스에서 플러그인 초기화를 수행합니다.
플랫폼 Agent 등록
플랫폼 이벤트 리스너 Agent 구현
HybePlatformAuthAgent 클래스를 상속받은 클래스 구현
웹뷰 이벤트 리스너 Agent 구현
HybePlatformWebviewAgent 클래스를 상속받은 클래스 구현
플랫폼 Agent 구현
HybePlatformAuthAgent
클래스를 상속 받은 클래스 구현
HybePlatformWebviewAgent
클래스를 상속 받은 클래스 구현
Webview 구현
언리얼 Webview 플러그인 UWebBrowser 클래스를 상속 받은 클래스 구현
Copy //1. 게임플랫폼 플러그인에 Agent를 등록한다.
HybePlatformAgent::Instance().SetPlatformAuthAgent(NewObject<UStarterPlatformAuthAgent>());
HybePlatformAgent::Instance().SetPlatformWebviewAgent(NewObject<UStarterPlatformWebviewAgent>());
//2. 설정 정보와 함께 플랫폼 초기화를 시도한다.
std::string configJson = R"({
"BuildEnv": "qa",
"SteamAppID": 2691730,
"ProjectId": "1006",
"SteamIdentity": "ds-qa-10060021"
})";
//3. 클라이언트에서 사용하는언어를 설정한다.
HybePlatformAgent::Instance().SetClientLanguage(TEXT("ko"));
//4. 플랫폼 SDK 초기화를 수행한다.
HybePlatformAgent::Instance().Mount(FString(configJson.c_str()));
게임 클라이언트에서는 HybePlatformAuthAgent
클래스를 상속 받은 이벤트 리스너를 제공해야 합니다.
HybePlatformAuthAgent
클래스를 상속받은 이벤트 리스너를 제공해야 합니다.
Copy class HYBEPLATFORM_API HybePlatformAuthAgent
{
public:
virtual void OnLoginSuccess(const FString& resultJson);
virtual void OnLoginFailure(const FString& resultJson);
virtual void OnLogoutSuccess();
virtual void OnLogoutFailure(const FString& resultJson);
virtual void OnWithDraw();
virtual void OnMount(bool mounted);
virtual void OnRefreshVerifyToken(const FString& token);
};
웹뷰가 필요할 때 호출되는 이벤트를 처리하는 HybePlatformWebviewAgent 를 상속받은 이벤트 리스너를 제공해야한다.
Copy class HYBEPLATFORM_API HybePlatformWebviewAgent
{
public:
virtual void OnCloseWebview();
virtual void OnShowWebview(const FString& url);
void EvalMethod(const FString& actionJson, std::function<void(const FString&)> callback);
};
실행 환경 정보 구성
플랫폼 초기화 함수(HybePlatformAgent::Mount)에 전달하는 실행 환경 JSON 정보는 다음과 같습니다.
플랫폼 초기화 함수(HybePlatformAgent::Mount)에 전달하는 실행 환경 JSON 정보는 다음과 같습니다.
Copy {
"BuildEnv": "qa",
"SteamAppID": 00000000,
"ProjectId": "0000",
"SteamIdentity": "sample-qa-00000000"
}
게임 플랫폼의 서비스를 제공하는 메인 클래스로 게임 플랫폼이 제공하는모든 기능은 HybePlatformAgent를 통해서 제공됩니다.
Agent 등록
플랫폼 인증 과정에서 발생하는 이벤트를 수신하기 위해서는 Agent를 제공해야 합니다.
HybePlatformAuthAgent을 상속받은 Agent 클래스를 등록합니다.
Copy void SetPlatformAuthAgent(HybePlatformAuthAgent* authAgent)
파라미터
authAgent
HybePlatformAuthAgent을 상속 받은 클래스 인스턴스
HybePlatformAuthAgent을 상속받은 Agent 클래스를 등록합니다.
Copy void SetPlatformWebviewAgent(HybePlatformWebviewAgent* webviewAgent)
파라미터
webviewAgent
HybePlatformWebviewAgent을 상속 받은 클래스 인스턴스
Copy HybePlatformAgent::Instance().SetPlatformAuthAgent(new PlatformAuthAgent());
HybePlatformAgent::Instance().SetPlatformWebviewAgent(new UMyGamePlatformWebviewAgent());
1. 플러그인 초기화
플랫폼 SDK 플러그인 초기화를 수행합니다.
Copy void HybePlatformAgent::Mount(const FString& configJson) const
파라미터
configJson 설정 정보를 담고있는 JSON 문자열
초기화 결과는 HybePlatformAuthAgent
를 상속 받은 Agent로 전달됩니다.
초기화 결과는 HybePlatformAuthAgent 상속받은 클래스의 OnMount 이벤트로 전달됩니다.
Copy void UMyGamePlatformAuthAgent::OnMount(bool mounted)
{
UE_LOG(LogTemp, Warning, TEXT("Platform Mounted : %d"), mounted);
}
2. 로그인
플랫폼 로그인을 수행합니다.
Copy void HybePlatformAgent::Login(const FString& signinMethod) const
파라미터
signinMethod 로그인 종류 (STEAM, GOOGLE, APPLE)
스팀 계정을 이용한 플랫폼 로그인 예
Copy HybePlatformAgent::Instance().Login(TEXT("STEAM"));
플랫폼 로그인(Signin)은 백그라운드에서 수행되고 등록한 HybePlatformAuthAgent
로 결과 이벤트가 전달됩니다.
3. 로그인 취소
플랫폼 로그인을 프로세스를 중단합니다.
4. 플랫폼 로그인 이벤트
플랫폼 로그인은 백그라운드에서 수행되며 등록한 HybePlatformAuthAgent로 결과가 전달됩니다.
플랫폼 로그인이 정상적으로 완료된 경우에 호출됩니다.
Copy void OnLoginSuccess(const FString& resultJson)
파라미터
Copy {
"imid": "LUDLJKU29PSYL5PMPJJM",
"loginVerifyToken": "eyJhbGciOiJIUzI1NiJ9.eyJpbUlkIjoiTFVETEpLVTI5"
}
플랫폼 백엔드 인증에 사용되는 로그인 Token
플랫폼 로그인이 실패한 경우에 호출됩니다.
Copy void OnLoginFailure(const FString& resultJson)
파라미터
resultJson 로그인 실패 결과 JSON
Copy {
"code": 2,
"message": "Unknown login error"
}
에러 코드는 다음과 같습니다.
Copy enum class Code
{
UNKNOWN_ERROR = 0,
NO_INITIALIZE = 1,
SIGNIN_FAIL = 2, // Google/Apple Signin Fail
PLATFORM_LOGIN_FAIL = 3, // 플랫폼 로그인 실패
AUTO_LOGIN_FAIL = 4, // 자동 로그인 실패
WITHDRAW_ACCOUNT = 5, // 계정 탈퇴 회원
SIGNUP_FAIL = 6, // 회원가입 실패
};
Copy void OnLogoutSuccess()
플랫폼 로그아웃이 실패한 경우에 호출됩니다.
Copy void OnLogoutFailure(std::string resultJson)
계정 탈퇴 시 호출되는 이벤트입니다.
Copy OnRefreshVerifyToken(const FString& token)
플랫폼 SDK 초기화 완료 시 호출됩니다.
Copy void OnMount(bool mounted)
파라미터
게임 클라이언트는 수신한 인증 토큰을 게임 서버에 전달해야 합니다.
Copy void OnMount(bool mounted)
파라미터
계정 탈퇴 시 호출되는 이벤트입니다.
5. 게임 서버 인증 연동
클라이언트는 게임 서버에 접속하기 위해서는 플랫폼 로그인 후 수신한 로그인 인증 토큰을 게임 서버에 전달하여야 합니다. 게임 서버는 수신한 인증 토큰을 플랫폼 백엔드를 통해 변조 여부를 검증 합니다.
게임 서버 로그인에 필요한 인증 토큰을 요청합니다.
백그라운드에서 수행되면 인증 토큰은 HybePlatformAuthAgent::OnRefreshVerifyToken() 이벤트로 전달 됩니다.
Copy void RefreshVerifyToken()
파라미터
로그인 성공 이벤트(OnLoginSuccess)에 인증 토큰(verifyToken) 과 유저 식별정보(IMID)가전달됩니다.
성공 이벤트에 전달되는 토큰을 게임 서버에 전달하는 것도 가능하지만 인증 토큰은 만료 시간을 가지고 있습니다. (1시간)
플랫폼 로그인 과 게임 서버 접속 이 분리되어 있는 경우 장시간 대기 중 게임서버에 접속하는 경우 인증 실패가 발생할 수 있습니다.
이러한 이유로, 게임 서버에 전달하는 인증 토큰은 반드시 RefreshVerifyToken()
로 갱신한 후 사용해야 합니다.
6. 회원 가입 웹뷰 연동
HybePlatform 플러그인은 UI를 제공하지 않습니다.
UI 가 필요한 경우 해당되는 이벤트를 클라이언트가 수신하도록 제공하며 이벤트를 수신한 클라이언트에 명세에 맞도록 UI를 노출시키고, 결과를 Platform 플러그인에게 제공해야 합니다.
게임 플랫폼 연동 과정에서 다양한 목적으로 유저에게 웹뷰를 팝업합니다 (다국어 지원)
로그인 프로세스에서 신규 회원인 경우 서비스 이용 동의
구글/애플/이메일 로그인 (지원하는 서비스의 경우)
언리얼에서 제공하는 WebBrowser 플러그인을 활용하여 연동하는 방법 다음과 같습니다.
ProjectName.Build.cs 에서 WebBrowser 플러그인 모듈을 추가합니다.
Copy
PrivateDependencyModuleNames.AddRange(new string[] { "WebBrowser" });
PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
UWebBrower를 상속받을 C++ 클래스를 추가합니다.
아래는 플랫폼 연동에 필요한 기능한 구현된 샘플 Webbrower 소스입니다.
아래 메소드는 반드시 구현 되어야 합니다.
- PostMessage : JavaScript → SDK 통신 시 호출되는 이벤트 핸들러
- PrepareJsBridge : 웹뷰의 JavaScript 와 언리얼 통신 설정
Copy //UMyGameWebview.h
UCLASS()
class UMyGameWebview : public UWebBrowser
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
void PostMessage(FString jsonResponse);
void PrepareJsBridge();
void ShowWebview(const FString& url);
void CloseWebview();
protected:
virtual TSharedRef<SWidget> RebuildWidget() override;
};
Copy // UMyGameWebview.cpp
/**
* @brief JavaScript -> Unreal 함수 호출을 처리하는 이벤트 핸들러
* @param jsonMethod JSON 형태의 메소드 호출 포맷
*/
void UMyGameWebview::PostMessage(FString jsonMethod)
{
HybePlatformAgent::Instance().GetWebviewAgent()->EvalMethod(jsonMethod, [this](const FString& resultJson) {
if (resultJson.IsEmpty() == false)
{
FString js = FString::Printf(TEXT("window.unityEventHandler(%s);"), *resultJson);
ExecuteJavascript(js);
}
});
}
/**
* @brief 웹뷰(JavaScript)에서 언리얼 오브젝트를 호출하도록 설정한다.
*/
void UMyGameWebview::PrepareJsBridge()
{
FString linkName = TEXT("webbridge");
WebBrowserWidget->BindUObject(linkName, this, true); //true : URL이 변경되어도 계속 바이딩 유지
}
void UMyGameWebview::ShowWebview(const FString& url)
{
WebBrowserWidget->LoadURL(url);
SetVisibility(ESlateVisibility::Visible);
}
void UMyGameWebview::CloseWebview()
{
SetVisibility(ESlateVisibility::Hidden);
}
HybePlatformWebviewAgent 상속 클래스에서 OnShowWebview(url) 이벤트 수신 시 웹 브라우저를 화면에 띄웁니다.
7. 계정 탈퇴
파라미터
8. 로그 연동
게임 클라이언트는 플레이 과정에서 발생하는 유저의 행동 로그를 수집하여 전달해야 합니다.
전달하는 로그의 명세는 별도로 전달되는 로그 명세서를 참고하세요.
로그 전송 과정은 2 단계로 구분할 수 있습니다.
로그 데이터(JSON) 수집
로그 JSON은 로그 명세서를 참고하여 직접 생성하거나, 플러그인에서 제공하는 기능을 사용할 수 있습니다.
로그 속성 종류
로그 JSON 생성시 현재 시간(UTC)으로 자동 추가(ISO8601)
아래 포맷의 JSON 로그를 생성하는 경우
Copy {
"log_type": "LOGIN_COMP",
"os_type" : 1,
"service_id" : "10060000",
"external_device_type" : 0,
"external_device_id" : "b845158f4d599d791ca22790151cd473",
"cre_time" : "2024-06-13T08:02:27.014000Z"
}
공통 속성 등록
공통 속성은 1회 등록하면 LogneticEvent를 사용하는 경우 자동으로 JSON 생성 시 추가됩니다.
Copy Lognetic().SetCommon(TEXT("os_type"), 1);
Lognetic().SetCommon(TEXT("service_id"), TEXT("10060000"));
Lognetic().SetCommon(TEXT("external_device_type"), 0);
Lognetic().SetCommon(TEXT("external_device_id"), FPlatformMisc::GetLoginId());
개별 속성 등록
Copy LogneticEvent()
.Set(TEXT("log_type"), TEXT("LOGIN_COMP"))
로그 JSON 생성
JSON 생성 시 등록된 공통 속성과 생성 시간(cre_time) 속성이 자동으로 추가 됩니다.
Copy FString logJson = LogneticEvent()
.Set(TEXT("log_type"), TEXT("LOGIN_COMP"))
.ToJson();
로그 JSON 생성 및 전송
Send() 메소드는 JSON을 생성 한 후 로그 서버로 전송을 수행합니다.
1개의 로그를 전송 하는 경우에 사용합니다.
Copy LogneticEvent()
.Set(TEXT("log_type"), TEXT("LOGIN_COMP"))
.Send();
중첩 구조 로그를 생성하는 경우
Copy {
"log_type": "LOGOUT",
"os_type": 1,
"service_id": "10060000",
"external_device_type": 0,
"external_device_id": "b845158f4d599d791ca22790151cd473",
"cre_time": "2024-06-13T08:02:27.015000Z",
"online_play": {
"play_cnts": 5,
"play_details": [
{
"dungeon_type": "10",
"dungeon_id": "1",
"scale": 2
},
{
"dungeon_type": "20",
"dungeon_id": "2",
"scale": 5
}
]
}
}
Copy LogneticEvent logoutLog;
logoutLog.Set(TEXT("log_type"), TEXT("LOGOUT"));
auto& onlinePlay = logoutLog.AddObject(TEXT("online_play"));
onlinePlay.Set(TEXT("play_cnts"), 5);
onlinePlay.AddArray(TEXT("play_details")) // online_play/play_details[0]
.Set(TEXT("dungeon_type"), TEXT("10"))
.Set(TEXT("dungeon_id"), TEXT("1"))
.Set(TEXT("scale"), 2);
onlinePlay.AddArray(TEXT("play_details")) // online_play/play_details[1]
.Set(TEXT("dungeon_type"), TEXT("20"))
.Set(TEXT("dungeon_id"), TEXT("2"))
.Set(TEXT("scale"), 5);
logoutLog.Send();
JSON 로그 전송
1개 이상의 수집한 로그를 전송하는 기능을 제공합니다.
단일 로그를 생성하면서 전송하는 경우
LogneticEvent 사용
Copy LogneticEvent()
.Set(TEXT("log_type"), TEXT("LOGIN_COMP"))
.Send();
LogneticAgent 사용
Copy FString logJson = LogneticEvent()
.Set(TEXT("log_type"), TEXT("LOGIN_COMP"))
.ToJson();
Lognetic().SendEvent(logJson)
1개 이상 로그를 전송하는 경우
Copy FString logJson = LogneticEvent()
.Set(TEXT("log_type"), TEXT("LOGIN_COMP"))
.ToJson();
FString logJson2 = LogneticEvent()
.Set(TEXT("log_type"), TEXT("LOGOUT"))
.ToJson();
TArray<FString> logs = {logJson, logJson2};
Lognetic().SendEvent(logs)
LogneticEvent::Set
속성을 추가합니다.
Copy template<typename T>
LogneticEvent& Set(const FString& key, T value)
파라미터
value 속성 값. int, usingined int, int64_t, uint64_64, float, double, FString 지원
리턴
LogneticEvent::AddObject
하위 로그 이벤트 객체를 생성합니다. 지정된 키(key)에 하위를 등록합니다.
Copy LogneticEvent& AddObject(const FString& key)
파라미터
리턴
Copy LogneticEvent().Set(TEXT("log_type"), TEXT("LOGOUT"))
.AddObject(TEXT("online_play"))
.Set(TEXT("play_cnts"), 5)
.ToJson();
Copy {
"log_type": "LOGOUT",
"cre_time": "2024-06-13T08:02:27.015000Z",
"online_play": {
"play_cnts": 5
}
}
AddObject()는 새로 추가된 이벤트 객체를 리턴합니다.
체이닝(chaning) 형식으로 호출하는 경우 리턴된 이벤트에 속성이 추가됩니다.
2번 호출하는 경우 새로 추가된 이벤트의 하위 속성으로 추가되기 때문에 주의가 필요합니다.
Copy LogneticEvent().Set(TEXT("log_type"), TEXT("LOGOUT"))
.AddObject(TEXT("online_play"))
.Set(TEXT("play_cnts"), 5)
.AddObject(TEXT("item_info")) //online_play 하위에 item_info가생성된다.
.Set(TEXT("type"), "bag")
.ToJson();
Copy {
"log_type": "LOGOUT",
"cre_time": "2024-06-13T08:02:27.015000Z",
"online_play": {
"play_cnts": 5,
"item_info": {
"type": "bag"
}
}
}
동일 경로의 하위 이벤트를 추가하는 경우
Copy LogneticEvent logoutLog;
logoutLog.Set(TEXT("log_type"), TEXT("LOGOUT"));
auto& onlinePlay = logoutLog.AddObject(TEXT("online_play"));
onlinePlay.Set(TEXT("play_cnts"), 5);
auto& itemInfo = logoutLog.AddObject(TEXT("item_info")); //루트에 생성된다.
itemInfo .Set(TEXT("type"), "bag");
logoutLog.ToJson();
Copy {
"log_type": "LOGOUT",
"cre_time": "2024-06-13T08:02:27.015000Z",
"online_play": {
"play_cnts": 5,
},
"item_info": {
"type": "bag"
}
}
LogneticEvent::AddArray
배열에 추가되는 로그 이벤트 객체를 생성합니다. 지정된 키(key)의 하위를 등록되며 지정된 키의 배열의 마지막 아이템으로 등록됩니다.
동일한 키(key) 로 호출할 때마다 배열에 새로운 이벤트 객체가 추가 됩니다.
Copy LogneticEvent& LogneticEvent::AddArray(const FString& key)
파라미터
리턴
Copy LogneticEvent log
log.Set(TEXT("log_type"), TEXT("LOGOUT"))
.AddArray(TEXT("play_details")) // online_play/play_details[0]
.Set(TEXT("dungeon_type"), TEXT("10"))
.Set(TEXT("scale"), 2);
log.ToJson().Send();
Copy {
"log_type": "LOGOUT",
"play_details": [
{
"dungeon_type": "10",
"dungeon_id": "1",
"scale": 2
}
]
}
}
동일한 키(key)로 호출할 때마다 배열에 새로운 이벤트 객체가 추가 됩니다.
Copy LogneticEvent log
log.Set(TEXT("log_type"), TEXT("LOGOUT"))
.AddArray(TEXT("play_details")) // online_play/play_details[0]
.Set(TEXT("dungeon_type"), TEXT("10"))
.Set(TEXT("scale"), 2);
log.AddArray(TEXT("play_details")) // online_play/play_details[1]
.Set(TEXT("dungeon_type"), TEXT("20"))
.Set(TEXT("scale"), 5);
log.ToJson().Send();
Copy {
"log_type": "LOGOUT",
"play_details": [
{
"dungeon_type": "10",
"scale": 2
},
{
"dungeon_type": "20",
"scale": 5
}
]
}
}
LogneticEvent::Send
로그를 서버에 전송합니다. 아래의 추가 작업을 수행합니다.
등록된 속성을 JSON으로 변환 후에 로그 서버에 전송합니다.
파라미터
LogneticAgent::SendEvent
JSON 로그를 서버에 전송합니다.
LogneticEvent를 사용하지 않고, 로그 JSON을 직접 생성하는 경우에 사용할 수 있습니다.
Copy void SendEvent(const FString& json)
void SendEvent(TArray<FString> jsons)
파라미터
Copy FString myLog = BuildCustomLog();
LogneticAgent::Instance().SendEvent(myLog); // 동일 Lognetic().SendEvent(myLog);
Copy FString myLog = BuildCustomLog();
FString myLog2 = BuildCustomLog2();
FArray logs = {myLog, myLog2};
LogneticAgent::Instance().SendEvent(logs); // 동일 Lognetic().SendEvent(logs);
9. 기타
게임 클라이언트에서 사용하는 언어를 SDK에 전달합니다.
지정된 언어 정보를 이용해서 플랫폼 연동 과정에서 출력되는 웹뷰의 다국어가 적용됩니다.
Copy void SetClientLanguage(const FString& language)
파라미터
language ISO 639-1 언어코드( ko, en, ja, zh-tw).
웹뷰에 보여지는 컨텐츠는 지원 국가 외에는 영문(en)으로 출력 됩니다.
현재 사용자의 접속 국가 코드를 반환합니다
Copy FString HybePlatformAgent::GetLocationCode()
리턴
ISO 3166-1 alpha-2 국가 코드, ex) KR, US
연동된 스팀 정보를 조회합니다. Mount 호출이 정상적으로 수행된 후에 사용할 수 있습니다.
Copy HybePlatform::SteamInfo HybePlatformAgent::GetSteamInfo()
리턴
Copy class SteamInfo
{
public:
UINT64 Id; // User ID
FString Language; // (결제)스팀 API 호출시 사용되는 언어코드(ISO 639-1)
FString Currency; // (결제)통화코드 ex) KRW, USD
INT AppBuildId; // 앱빌드ID
};
Troubleshooting
플러그인 초기화 실패가 발생합니다.