언리얼 엔진 WebBrowser Plugin 다루는 법 정리
이 글은 언리얼 엔진과 웹페이지를 통신하는 기능을 구현하는 방법을 정리했습니다. 여러가지 정보를 조사하고 코드를 정리했습니다.
또한, 공부와 기록을 위해서 쓴 글이기도 해서 틀린 점과 수정해야 하는 부분이 있는 점 양해 부탁드립니다.
개념 정리
- 언리얼에 많은 클래스의 함수들을 JSManager의 SetData()에 등록한다. (예시로 SetResultII, SetResultIF, SetResultSS를 사용)
- 웹에서 언리얼 함수를 접근할 수 있게 WebBrowser에 JSManager를 BindUObject()한다.
- 웹에 SendMsgUnreal()로 WebBrowser에 바인딩된 JSManager의 SearchData()를 호출한다.
- WebBrowser의 ExcuteJavascript()로 웹에 접근해서 UnrealCall()를 호출한다.
Web Browser 플러그인 설치
- Settings -> Plugins를 선택해서 “Web Browser”를 검색해서 활성화하고 프로젝트를 제시작한다.
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Json", "JsonUtilities", "UMG", "WebBrowser", "WebBrowserWidget" });
- 프로젝트 경로 -> Source/ProjectName/ProjectName.Build.cs에 들어가서
- Json”, “JsonUtilities”, “UMG”, “WebBrowser”, “WebBrowerWidget”을 추가한다.
- ProjectName.uproject 파일을 우클릭하고, Generate Visual Studio project Files를 선택해서 Visual Studio 프로젝트 파일을 갱신하거나 Visual Studio를 컴파일한다.
새로운 Webbrower C++ 생성
- 새로운 C++ 클래스를 선택해서 새로운 클래스를 생성한다.
- “WebBrowser” 검색하고 선택한다.
- MyWebBrower라고 클래스 이름을 입력하고 클래스를 생성한다.
#include "CoreMinimal.h"
#include "WebBrowser.h"
#include "MyWebBrowser.generated.h"
/**
*
*/
UCLASS()
class SCROLLGAME1_API UMyWebBrowser : public UWebBrowser
{
GENERATED_BODY()
public:
// 자바스크립트에서 접근할 수 있게 해주는 함수
UFUNCTION(BlueprintCallable, Category = "Web Browser")
void BindUObject(const FString& Name, UObject* Object, bool bIsPermanent);
};
- MyWebBrower.h에 들어가서 BindUObject 함수를 선언한다.
#include "MyWebBrowser.h"
#include "SWebBrowser.h"
void UMyWebBrowser::BindUObject(const FString& Name, UObject* Object, bool bIsPermanent)
{
if (WebBrowserWidget.IsValid())
WebBrowserWidget->BindUObject(Name, Object, bIsPermanent);
}
- MyWebBrowser.cpp에 들어가서 BindUObject 함수를 구현한다.
웹페이지 생성 및 자바스크립트 구현
- 프로젝트에 들어가서 Web 폴더를 생성하고, index.html과 lib.js를 생성한다.
<!DOCTYPE html>
<html>
<head>
<title>Webview 테스트용 웹페이지</title>
<script src ="lib.js"></script>
</head>
<body>
<h1 id="msg">언리얼 메시지를 보여주는 영역</h1>
<hr>
<br>
<form>
Arg1: <input type="text" id="1arg1" value="3">
Arg2: <input type="text" id="1arg2" value="5">
<input type="button" value="Atomic 함수" onclick="ClickButton1()"><br><br>
Arg1: <input type="text" id="2arg1" value="1">
Arg2: <input type="text" id="2arg2" value="3.14">
<input type="button" value="Atom 함수" onclick="ClickButton2()"><br><br>
Arg1: <input type="text" id="3arg1" value="a">
Arg2: <input type="text" id="3arg2" value="b">
<input type="button" value="Atomy 함수" onclick="ClickButton3()">
</form>
</body>
</html>
// 언리얼에 메시지를 전달하는 함수
// 언리얼 함수를 호출하는 함수
function sendMsgUnreal(Argument)
{
var jsonData =JSON.stringify(Argument);
if(window.ue && window.ue.obj){
window.ue.obj.searchdata(jsonData);
}
else
alert('Not Found Message');
}
// 언리얼에 있는 webui_SetResultII_II 함수를 호출하는 함수
// Atomic(int, int);
// 키: webui_SetResultII_II
function ClickButton1()
{
var arg1 = document.getElementById("1arg1");
var arg2 = document.getElementById("1arg2");
sendMsgUnreal({key: "webui_SetResultII_II",values:[arg1.value, arg2.value]});
}
// 언리얼에 있는 webui_SetResultIF_IF 함수를 호출하는 함수
// Atom(int,float);
// 키: webui_SetResultIF_IF
function ClickButton2()
{
var arg1 = document.getElementById("2arg1");
var arg2 = document.getElementById("2arg2");
sendMsgUnreal({key: "webui_SetResultIF_IF",values:[arg1.value, arg2.value]});
}
// 언리얼에 있는 webui_SetResultSS_SS 함수를 호출하는 함수
// Atomy(string,string);
// 키: webui_SetResultSS_SS
function ClickButton3()
{
var arg1 = document.getElementById("3arg1");
var arg2 = document.getElementById("3arg2");
sendMsgUnreal({key: "webui_SetResultSS_SS",values:[arg1.value, arg2.value]});
}
//언리얼에서 호출될 함수
function UnrealCall(arg1, arg2)
{
var msg = "언리얼에서 "+arg1+"와 "+arg2+"를 입력했습니다."
var H1 = document.getElementById("msg");
H1.innerHTML = msg;
}
Webbrower를 사용할 WebUI 생성
- 위젯 블루프린트를 클릭해서 UI 에셋을 생성하고 WebUI라고 한다.
기본적인 UI 배치
- Vertical Box에 Border 위젯을 추가하고 Vertical Alignment와 Brush Color를 수정한다.
- Border에 Text 위젯을 추가하고, ResultText로 이름을 변경한다.
- 변수인자에 체크하고, Color and Opacity, Font, Justification를 수정한다.
- Text 값을 “웹페이지 메시지를 보여주는 영역”으로 변경한다.
- Border에 MyWebBrower 위젯을 추가하고, Padding과 Size를 수정한다.
- Border에 Horizontal Box 위젯을 추가하고 UnrealInput으로 이름을 변경한다.
- Vertical Alignment를 수정한다.
- UnrealInput에 Vertical Box 위젯을 추가하고 InputPanel으로 이름을 변경한다.
- Size를 변경한다.
- InputPanel에 Border 위젯을 추가하고 Input_A으로 이름을 변경한다
- Pading, Size를 수정한다.
- Input_A에 Editable Text 위젯을 추가하고, Text_A로 이름을 변경한다
- Font size와 Color and Opacity를 수정한다.
- Input_A를 복사하고 각각 Input_B, Text_B로 이름을 변경한다
- padding을 수정한다.
- UnrealInput에 Button 위젯을 추가하고 SendBtn으로 이름을 변경한다
- Padding을 수정한다
- Text 위젯을 추가하고 Text, Color and Opacity, Size를 수정한다.
- UI 배치 결과 화면
UI에 사용할 함수 구현
- int형 변수 arg1과 arg2를 더한 값을 ResultText에 반영하는 SetResultII 함수를 구현한다.
- int형 변수 arg1과 float형 변수 arg2를 더한 값을 ResultText에 반영하는 SetResultIF 함수를 구현한다.
- string형 변수 arg1과 arg2를 더한 값을 SetResultSS 함수를 구현한다.
WebUI 화면에 띄우기
- GameMode 블루프린트 생성하고 BP_WebbrowserGM으로 이름을 변경한다.
- BeginPlay 이벤트에 WebUI르 띄우는 기능을 구현한다.
- 추가로 Show Mouse Cursor를 활성화한다.
- 월드세팅 -> 게임모드 오버라이드에 BP_WebbrowserGM으로 변경한다..
C++로 JSManager 생성
- Actor를 상속받은 JSManager 클래스를 생성한다.
JS와 통신할 함수들을 구현
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "JSManager.generated.h"
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnJSCallFunctionDelegate, const TArray<FString>&, args);
UCLASS()
class SCROLLGAME1_API AJSManager : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AJSManager();
private:
TMap<FString, FOnJSCallFunctionDelegate> database; // 딕셔너리 자료구조로 만든 데이터베이스
public:
UFUNCTION(BlueprintCallable)
// 데이터베이스에 키와 이벤트 함수를 등록하는 함수
void setdata(FString key, FOnJSCallFunctionDelegate func);
UFUNCTION(BlueprintCallable)
// 데이터베이스를 검색하는 함수
void SearchData(FString data);
};
- JSManager.h에 들어가서 델리게이트 FOnJSCallFuctionDelegate를 선언한다.
- 딕셔너리 자료구조인 <Fstring, FOnJSCallFunctionDelegate> database를 선언한다.
- 키와 함수를 데이터베이스에 추가하는 setdata를 선언한다.
- 데이터베이스를 검색하는 SearchData 함수를 선언한다.
#include "JSManager.h"
// Sets default values
AJSManager::AJSManager()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
}
void AJSManager::setdata(FString key, FOnJSCallFunctionDelegate func)
{
database.Add(key, func);
}
void AJSManager::SearchData(FString data)
{
TSharedPtr<FJsonObject> JsonParsed;
TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(data);
//문자열로 저장된 Json 데이터를 Json 형식으로 바꾸는 작업
// 작업이 성공하지 못하면 리턴한다
bool checkDes = FJsonSerializer::Deserialize(JsonReader, JsonParsed) && JsonParsed.IsValid();
if (!checkDes)
return;
// Json에 저장된 키와 매개변수 값들을 가져오는 작업
FString key = JsonParsed->GetStringField("key");
TArray<FString> ArrayJson;
JsonParsed->TryGetStringArrayField("values", ArrayJson);
if (database.Contains(key))
database[key].ExecuteIfBound(ArrayJson);
}
- JSManager.cpp에 들어가서 setData함수와 SearchData 함수를 구현한다.
JSManager를 블루프린트로 생성
- JSManager를 상속받은 블루프린트 BP_JSManager를 생성한다.
- DefaultSetting 함수를 선언하고 구현한다.
- BP_WebbrowerGM에 WebUI를 새로운 변수로 승격한다.
UI에 구현된 함수들을 데이터베이스에 등록
- WebUI_SetResultII_II 함수를 선언하고 구현한다.
- WebUI에 SetResultII 함수와 연결한다.
- WebUI_SetResultIF_IF 함수를 선언하고 구현한다.
- WebUI에 SetResultIF 함수와 연결한다.
- WebUI_SetResultSS_SS 함수를 선언하고 구현한다.
- WebUI에 SetResultSS 함수와 연결한다.
- SettingDatabase 함수를 선언하고 구현한다.
- WebUI_SetResultII_II, WebUI_SetResult_IF, WebUI_SetResult_SS를 Setdata 함수에 등록한다.
- DefulatSetting 함수에 들어가서 SettingDatabase 함수를 호출한다.
- BeginPlay 이벤트에 UI 생성 시간을 위해 0.2초 딜레이 후 DefalutSetting 함수를 호출한다.
- 레벨에 BP_JSManager를 배치한다.
Webbrowser와 웹페이지를 연결하기
- string 타입의 FileURL 변수를 선언한다.
- 디폴트 값으로 index.html의 주소를 넣는다.
- Construct 이벤트에 들어가서 레벨에 있는 BP_JSManager를 변수로 승격해서 JSManager를 생성한다.
- 다시 Construct 이벤트로 돌아간다
- MyWebBrowser에 LoadURL함수와 BindUObject 함수를 호출한다.
- BindUObject에 JSManager를 연결하고, Name에 obj를 넣는다.
- 자바스크립트 lib.js에 UnrealCall 함수를 호출하는 GetCallJS 함수를 선언하고 구현한다.
- 퓨어 항목에 체크한다.
- SendBtn에 OnClicked 이벤트를 생성하고 MyWebBrower의 ExcuteJavascript 함수를 호출한다.
- Text_A의 Text와 Text_B의 Text 값을 GetCallJS 함수를 호출해서 ScriptText에 전달한다.