Networking/HTTP2018. 12. 20. 20:56

원문 : 언리얼 HTTP 요청 참조

https://wiki.unrealengine.com/UE4.10_How_To_Make_HTTP_GET_Request_in_C%2B%2B


위의 내용을 참조하여 UE4.20에서 테스트해본 내용이며 설명대로 설정하고 코딩한 것을 테스트한 결과 언리얼에서 웹서버에 접속하고 응답을 수신하여 언리얼 화면에 표시할 수 있었다


C++프로젝트 생성

Visual Studio 의 Games/프로젝트이름/Source/프로젝트이름/프로젝트이름.Build.cs 파일을 더블클릭하여 연다


소스코드에서 아래 내용을 찾는다

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });


위의 코드에 HTTP 관련 모듈이름을 추가한다

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore"

,"Http", "Json", "JsonUtilities" });


/Games/프로젝트명/Config/DefaultEngine.ini 파일을 열고 아래의 내용을 추가한다

[HTTP]

HttpTimeout=300

HttpConnectionTimeout=-1

HttpReceiveTimeout=-1

HttpSendTimeout=-1

HttpMaxConnectionsPerServer=16

bEnableHttp=true

bUseNullHttp=false

HttpDelayTime=0



Actor기반의 C++ 클래스를 생성

언리얼 에디터 Content Browser에서 마우스 우측 > C++ 클래스 생성 > Actor > 액터의 이름을 HttpActor 등으로 입력


Visual Studio에 위에서 생성한 클래스의 *.h, *.cpp 파일의 디폴트 코드가 열리면 헤더파일과 소스파일을 다음과 같이 편집한다. 굵게 표시한 코드는 디폴트 코드에 추가된 것이다


헤더파일 편집

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


#pragma once


#include "CoreMinimal.h"

#include "GameFramework/Actor.h"

#include "Runtime/Online/HTTP/Public/Http.h"

#include "MyHttpActor.generated.h"


UCLASS()

class CPP_HTTP_API AMyHttpActor : public AActor

{

GENERATED_BODY()

public:

FHttpModule* Http;


UFUNCTION()

void MyHttpCall(); // http 요청에 사용할 함수


// http GET 요청 직후 호출되는 콜백함수

void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);

// 생성자 함수를 약간 변경한다

AMyHttpActor(const class FObjectInitializer& ObjectInitializer);


protected:

// Called when the game starts or when spawned

virtual void BeginPlay() override;


public:

// Called every frame

virtual void Tick(float DeltaTime) override;

};



소스파일 편집

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


#include "MyHttpActor.h"



// Sets default values

AMyHttpActor::AMyHttpActor(const class FObjectInitializer& ObjectInitializer)

: Super(ObjectInitializer)

{

  // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.

PrimaryActorTick.bCanEverTick = true;

Http = &FHttpModule::Get();

}


// Called when the game starts or when spawned

void AMyHttpActor::BeginPlay()

{

MyHttpCall();

Super::BeginPlay();

}


void AMyHttpActor::MyHttpCall()

{

TSharedRef<IHttpRequest> Request = Http->CreateRequest();

Request->OnProcessRequestComplete().BindUObject(this, &AMyHttpActor::OnResponseReceived);

//This is the url on which to process the request

Request->SetURL("http://unreal.mywebcommunity.org/getInt.php");

Request->SetVerb("GET");

Request->SetHeader(TEXT("User-Agent"), "X-UnrealEngine-Agent");

Request->SetHeader("Content-Type", TEXT("application/json"));

Request->ProcessRequest();

}


void AMyHttpActor::OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)

{


//Create a pointer to hold the json serialized data

TSharedPtr<FJsonObject> JsonObject;


//Create a reader pointer to read the json data

TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());


//Deserialize the json data given Reader and the actual object to deserialize

if (FJsonSerializer::Deserialize(Reader, JsonObject))

{

//Get the value of the json object by field name

int32 recievedInt = JsonObject->GetIntegerField("customInt");


//Output it to the engine

GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, FString::FromInt(recievedInt));

}

}


// Called every frame

void AMyHttpActor::Tick(float DeltaTime)

{

Super::Tick(DeltaTime);


}



위와 같이 작성된 클래스를 Visual Studio에서 저장하고 언리얼 에디터에서 [컴파일] 버튼을 누른다

컴파일에 성공하면 Content Broswer에서 위의 클래스를 드래그하여 레벨에 배치하면 언리얼 에디터에서 작업은 끝이다


위의 언리얼 프로그램이 접속할 대상은 HTTP 프로토콜을 사용하여 연결되는 웹서버이므로 웹서버에서 위의 접속에 응답할 서버 스크립트가 필요하다. 웹서버에서 실행되는 스크립트는 프로그래밍 언어와 상관없다


getInt.php (위의 언리얼 프로그램에서 접속할 웹서버의 스크립트)

<?php
	//Create a variable to be used in 
	$theVar = array('customInt' => 5);

	//Set the headers
	header('Content-Type: application/json');

	//Encode the variable, and save the encoded string
	$encoded = json_encode($theVar);

	//Output it
	echo $encoded; 

?>



위의 예제를 약간 변경하여 언리얼 프로젝트에서 웹서버에 로그인하도록 연결한 예

로그인 기능을 담당하는 C++ 클래스에서 로그인 결과를 블루프린트에 통보하기 위해 C++에 함수의 원형을 선언하고  블루프린트에서 구현하였다. C++에 선언되어 있기 때문에 C++에서 호출이 가능하며 실행은 블루프린트에 정의된 함수가 실행된다


MyHttpActor.h (블루프린트에서 구현할 함수의 원형을 선언한다)

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


#pragma once


#include "CoreMinimal.h"

#include "GameFramework/Actor.h"

#include "Runtime/Online/HTTP/Public/Http.h"

#include "MyHttpActor.generated.h"


UCLASS(BlueprintType, Blueprintable)

class CPP_HTTP_API AMyHttpActor : public AActor

{

GENERATED_BODY()

public:

FHttpModule* Http;


UFUNCTION(BlueprintCallable, Category="HTTP")

void MyHttpCall(FString url); // http 요청에 사용할 함수


// http GET 요청 직후 호출되는 콜백함수

void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);

//블루프린트에서 구현할 함수 선언

//이 방법을 사용하면 블루프린트에서 구현된 이벤트함수를 C++에서 호출할 수 있다

UFUNCTION(BlueprintImplementableEvent, Category="HTTP")

void PrintOnScreen(bool bLogin); // 정수 파라미터라면 int32형을 사용해야 한다


// 생성자 함수를 약간 변경한다

AMyHttpActor(const class FObjectInitializer& ObjectInitializer);


protected:

// Called when the game starts or when spawned

virtual void BeginPlay() override;


public:

// Called every frame

virtual void Tick(float DeltaTime) override;

};



MyHttpActor.cpp (블루프린트에서 구현한 함수를 C++에서 호출)

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


#include "MyHttpActor.h"

#include "../Public/MyHttpActor.h"


// Sets default values

AMyHttpActor::AMyHttpActor(const class FObjectInitializer& ObjectInitializer)

: Super(ObjectInitializer)

{

  // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.

PrimaryActorTick.bCanEverTick = true;

Http = &FHttpModule::Get();

}


// Called when the game starts or when spawned

void AMyHttpActor::BeginPlay()

{

Super::BeginPlay();

}


void AMyHttpActor::MyHttpCall(FString url)

{

TSharedRef<IHttpRequest> Request = Http->CreateRequest();

Request->OnProcessRequestComplete().BindUObject(this, &AMyHttpActor::OnResponseReceived);

//This is the url on which to process the request


Request->SetURL(url);


Request->SetVerb("GET");

Request->SetHeader(TEXT("User-Agent"), "X-UnrealEngine-Agent");

Request->SetHeader("Content-Type", TEXT("application/json"));

Request->ProcessRequest();

}


void AMyHttpActor::OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)

{

//웹서버로부터 응답된 내용을 화면에 디버그 문자열로 출력해본다

//GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, Response->GetContentAsString());

//Create a pointer to hold the json serialized data

TSharedPtr<FJsonObject> JsonObject;


//Create a reader pointer to read the json data

TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());


//Deserialize the json data given Reader and the actual object to deserialize

if (FJsonSerializer::Deserialize(Reader, JsonObject))

{

bool login = JsonObject->GetBoolField("login");

//이 클래스에서 선언하고 블루프린트에서 구현된 함수 호출

PrintOnScreen(login);

}

}


// Called every frame

void AMyHttpActor::Tick(float DeltaTime)

{

Super::Tick(DeltaTime);

}



위에 정의된 C++ 클래스를 기반으로 블루프린트 액터 클래스를 생성하고 C++에 선언된 PrintOnScreen 함수를 블루프린트에서 구현한다


BP_HttpActor



웹서버에서 로그인을 처리하는 PHP 스크립트

언리얼 프로젝트로부터 id, pwd 파라미터를 받아서 MySQL 데이터베이스를 검색하여 전달된 이용자 정보가 존재하는지 확인한다. 확인된 결과를 JSON 문자열로 표현하여 응답한다


unreal_login.php

<?php

header('Content-type: application/json; charset=UTF-8');

  $id = $_REQUEST["id"];

  $pwd = $_REQUEST["pwd"];


  $result = "";

  

  // DB 코드 시작

  $host='fdb24.awardspace.net';

  $user='2915954_user';

  $pass='2915954Database';

  $db='2915954_user';


  $arr = array();

  

  $conn = mysqli_connect($host,$user,$pass,$db);

  if ($conn->connect_error) {

$arr["error"] = true;

        echo json_encode($arr);

return;

  }

  if($conn) {

    $arr["connection"] = true;

  }

 

  $select_query = "SELECT * FROM Member WHERE memid='$id' AND mempwd='$pwd' ";


  $result_set = mysqli_query($conn, $select_query);


  if (mysqli_num_rows($result_set) > 0) {

$arr["login"] = true;

  } else {

$arr["login"] = false;

  }

  mysqli_close($conn);

  $json = json_encode($arr);

  echo $json;

?>


Posted by cwisky


Advanced Sessions Plugin을 검색하여 포럼 사이트에 접속하면 최시버전을 다운로드할 수 있다

https://forums.unrealengine.com/community/community-content-tools-and-tutorials/41043-advanced-sessions-plugin


압축을 해제하고 AdvancedSessions 폴더를 언리얼 설치폴더의 Engine/Plugins/안에 복사해 넣는다


프로젝트를 새로 생성하거나 기존 프로젝트를 연다


편집 > Plugins 를 선택하고 Advanced Sessions, Advanced Steam Sessions, Online Subsystem Steam 에 각각 체크하고 하단의 [지금 재시작] 버튼을 누른다


언리얼 에디터가 재시작 되면 블루프린트 클래스 안에서 다음과 같은 노드가 사용가능한지 확인한다

Create Advanced Sessions 

Find Sessions Advanced




Create Advanced Session 노드의 사용 예


Find Sessions Advanced 노드의 사용예



Join Session은 일반 Session 의 Join Session과 동일


Posted by cwisky
UMG/Mini-Map2018. 12. 8. 08:15

Minimap 만들기 (고비용 버전)


3인칭 프로젝트 템플릿을 이용하여 프로젝트를 생성한다


3인칭 캐릭터 블루프린트 에디터를 연다


CapsuleComponent 에 SpringArm 컴포넌트 추가

추가된 SpringArm 컴포넌트를 Y축 중심으로 -90도 회전하여 수직으로 세운다


SpringArm에 SceneCaptureComponent2D를 추가하고 이름을 SceneCapture 등으로 변경한다

 - 위와같이 하면 SpringArm이 수직으로 세워져 있는 끝에 SceneCapture가 연결되므로 수직으로 내려다보면서 장면을 캡쳐하게 된다. 매 프레임마다 캡쳐하여 미맵을 그려야 하므로 성능에 좋지 못한 영향을 가진다


미니맵이 그려질 애셋(RenderTarget)을 CB에 생성한다

CB에서 > Add New > Materials & Textures > RenderTarget 선택

 - RenderTarget은 일반적인 Texture2D 와 같은 이미지로 사용할 수 있다

생성된 RenderTarget의 이름을 Minimap_Render_Target 등으로 변경


SceneCapture 에 RenderTarget 를 연결한다

SceneCapture 선택 > 디테일 뷰 > Scene Capture > Texture Target > 위에서 생성한 Minimap_Render_Target 지정

컴파일 > 저장 > 캐릭터 블루프린트 에디터를 닫는다


RenderTarget의 머티리얼 생성

CB에 있는 Minimap_Render_Target > 마우스 우측 > Create Material 

생성된 머티리얼을 더블클릭하여 에디터를 연다

머티리얼 노드 선택 > 왼쪽 속성창에서 > Material > Material Domain > User Interface 로 변경

저장 > 에디터를 닫는다


위젯블루프린트를 생성하여 스크린에 미니맵 이미지를 설정한다

팔레트에서 Image 위젯을 드래그하여 Canvas 위에 놓고 256x256 크기로 설정 > 선택 > 디테일뷰 > Appearance > Brush > Image > 위에서 생성한 머티리얼 지정 > 저장 > 에디터 닫는다


레벨블루프린트에서 위젯을 스크린에 보이도록 설정한다

Create Widget노드에 위에서 생성한 위젯블루프린트를 지정하고 Add To Viewport 노드를 연결한다

저장 > 에디터를 닫는다 > 실행하여 미니맵이 스크린에 나타나는지 확인한다


3인칭 캐릭터 블루프린트를 열고 SpringArm을 선택하고 디테일뷰에서 Camera Settings > Inherit Pitch, Yaw, Roll 모든 항목 체크해제


Scene Capture 컴포넌트 선택 > 디테일뷰 > Projection > Project Type > Orthographic

위와 같은 항목 내에서 Orth Width > 2000  정도로 지정한다

컴파일 > 저장 > 에디터를 닫고 > 게임을 실행하여 오쏘 그래픽 효과를 테스트한다


캐릭터를 대체할 스프라이트 이미지를 설정한다

3인칭 캐릭터 블루프린트를 열고 Scene Capture 컴포넌트 선택 > 디테일 뷰 > Scene Capture 하단 확장> General Show Flags(일반표시플래그) > Skeletal Meshes 선택해제

 - 위와 같이 설정하면 미니맵에서 캐릭터는 보이지 않게 된다. 대신 조그만 스프라이트 이미지를 보여주면 된다


캐릭터 위치를 나타낼 이미지를 CB 안으로 임포트한다

이미지 위에서 마우스 우측 > Sprite Actions > Create Sprite 

3인칭 캐릭터 블루프린트에서 SpringArm 컴포넌트 아래에 Paper Sprite 컴포넌트 추가 > 선택 > 디테일뷰 > Sprite > Source Sprite > 위에서 생성한 스프라이트 이미지를 지정

추가된 스프라이트 컴포넌트를 적당히 회전하여 캐릭터 X와 스프라이트의 X 방향이 일치하도록 조정한다

PaperSprite 컴포넌트가 CapsuleComponent의 하위에 오도록 계층구조를 변경한다

게임을 실행하여 캐릭터의 머리 위에 스프라이트가 보이는지 확인한다

컴파일 > 저장 > 실행 테스트


캐릭터에게는 스프라이트가 보이지 않도록 설정한다

3인칭 캐릭터 블루프린트를 열고 > PaperSprite 컴포넌트 선택 > 디테일뷰 > Rendering 하단 확장 > Owner No See 항목 체크 > 컴파일 > 저장


게임실행 테스트




Minimap 만들기 2 ( 저비용 버전 )


맵의 전체 폭을 구한다(레벨 에디터에서 줌아웃하고 와이어프레임 모드에서 레벨의 좌우 끝까지 길이를 구함, 마우스 중앙버튼을 누르고 드래그하면 선의 크기가 표시됨)

World Outliner에서 Floor 를 선택하고 레벨블루프린트에서 Floor-->GetBounds노드를 사용하여 위치와 크기를 동시에 구해볼 수도 있다

레벨의 중앙에 큐브액터를 추가한다

큐브액터에 SpringArm 컴포넌트를 추가하고 수직으로 세운다

SpringArm 컴포넌트 안에 SceneCaptureCompnent2D 컴포넌트 추가

위에서 생성된 SceneCaptureComponent2D 컴포넌트를 선택 > 디테일뷰 > Projection > Projectin Type : Orthographic, Ortho Width: 레벨의 전체 폭 입력

CB에서 마우스 우측 > 머티리얼&텍스쳐 > Render Target 애셋을 생성

        SceneCaptureComponent2D 컴포넌트를 선택 > Scene Capture / Texture Target 항목에 위에서 생성한 렌더타겟을 설정

컴파일 > 저장

CB에서 Render Target 마우스 우측 > 애셋액션 > 엑스포트 > 저장 (HDR 포맷)

HDR 파일을 포토샵으로 열고 이미지 전체선택 > 복사

그림판 열고 > 붙여넣기 > 다른 이름으로 저장 > PNG 포맷으로 저장

이렇게 작성된 배경 이미지를 CB안으로 임포트한다

큐브액터는 더 이상 필요 없으므로 레벨에서 삭제한다


미니맵을 포함할 최상위 위젯(MainWidget) 생성

- Canvas Panel 이 화면 전체를 채우도록 설정


미니맵을 표현할 MinimapWidget 생성

- Canvas Panel [변수인지] 체크

- Canvas Panel 크기를 커스텀으로 설정(X:300, Y:300)정도

- Canvas Panel 위에 Image 위젯을 드래그하여 놓고 크기를 300, 300 설정

- Image 선택 > Appearance > Brush > Image 항목에 위에서 생성한 배경 이미지를 지정

- 저장 > 에디터를 닫는다

- MainWidget의 팔레트에서 MinimapWidget을 드래그하여 적당한 위치에 놓는다

- MinimapWidget [변수인지]에 체크


플레이어 캐릭터의 아이콘으로 사용할 위젯(IconWidget) 생성

- Canvas Panel을 삭제하고 Image 위젯을 드래그하여 놓는다

- Image 선택 > [변수인지]에 체크

- Image의 크기를 32 x 32 정도의 크기로 설정

- Image 선택 > Appearance > Brush > Image 항목에 플레이어 아이콘으로 사용할 이미지 지정

- 저장 > 에디터를 닫는다


플레이어 캐릭터 클래스에서 MainWidget을 생성하고 변수에 저장한다

- Create Widget 노드 -->(변수에 저장)--> Add to Viewport

- IconWidget 를 생성한다

- 현재 플레이어의 위치를 미니맵의 위치로 환산하여 그 위치에 IconWidget 를 위치시킨다

- 주기적으로 플레이어의 위치를 미니맵의 위치로 환산하여 IconWidget위치를 변경한다


미니맵 안에서의 아이콘 위치를 지정할 때는 일반 이미지처럼 미니맵의 좌상단 모서리가 원점이 된다

언리얼 뷰포트에서는 전방(x증가), 후방(x감소), 좌측(y감소), 우측(y증가) 이므로 좌표변환시 주의해야 한다 


GetActorBounds 함수노드를 사용하여 Floor 의 위치와 전체 크기(폭, 길이)를 구하는 예




3인칭 캐릭터의 아이콘을 미니맵에 표시하는 예


위에서 설정한 타이머에 의해서 호출되는 UpdateFunction 함수



AI 캐릭터에서 아이콘을 미니맵에 표시하는 경우



위에서 설정한 타이머에 의해서 주기적으로 호출되는 UpdateFunction




위에서 사용된 4개의 위젯


WG_MainWidget



WG_Minimap



WG_PlayerIcon



WG_NPC_Icon



미니맵 실행 테스트


사용된 아이콘


Posted by cwisky