'분류 전체보기'에 해당되는 글 99건

  1. 2018.12.20 Unreal HTTP Request
  2. 2018.12.18 Advanced Sessions Plugin Setup
  3. 2018.12.08 Mini-Map
  4. 2018.12.07 언리얼 네트워킹 학습자료
  5. 2018.11.22 C++ 액터 클래스 응용
  6. 2018.11.02 비헤이비어 트리 예제
  7. 2018.10.31 Moving Firing example
  8. 2018.10.31 Custom Projectile example
  9. 2018.10.31 Behavior Tree example
  10. 2018.10.30 Find Look at Rotation
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
Networking/Intro(Videos)2018. 12. 7. 11:47

Listen Server

별도의 서버머신이 필요없다

서버에서도 한개의 클라이언트가 실행된다

프로그래밍이 쉽다

소수의 클라이언트가 접속할 경우에 적당


Dedicated Server

전용서버로 클라이언트가 포함되 않음

클라이언트 머신 외에 독립적인 서버머신이 필요하다

해당 게임에 접속자가 많을 경우에 적합하다


리슨서버와 클라이언트를 에디터 외부에서 실행하는 방법

리슨서버 실행

contentExamples.uproject Network_Features?listen -game

conetntExamples.uproject :  프로젝트 이름

Network_Features : 실행할 맵(레벨) 이름

? : 추가적인 옵션

listen : 리슨서버로 실행

-game : 에디터에서 실행하는 것이 아니라 게임을 실행한다


클라이언트 실행

contentExamples.uproject 127.0.0.1 -game

contentExamples.uproject : 프로젝트 이름

127.0.0.1 (IP) : 클라이언트가 접속할 서버의 주소



언리얼 게임 프로젝트를 윈도우 기반으로 패키징하여 실행파일(*.exe)을 콘솔에서 실행하는 방법


네트워크 프로그램을 패키징하여 실행파일(예, Net_TPS.exe)을 실행하는 콘솔명령


1. 완성된 언리얼 프로젝트를 패키징한다

  - 파일 > 프로젝트 패키지 > 윈도우 > 윈도우(64비트) > 실행파일 저장 경로 지정


2. 위와같은 방법으로 프로젝트를 패키징하면 'WindowsNoEditor' 폴더 안에 몇개의 폴더와 실행파일(프로젝트명exe)이 생성된다. 예를들어, 프로젝트명이 Net_TPS 였다면 실행파일은 Net_TPS.exe 이 생성된다


3. CMD를 열고 실행파일(Net_TPS.exe)이 있는 곳으로 이동하여 다음과 같이 서버와 클라이언트를 각각 실행한다


 - Listen 서버 실행 : D:\MyGame>Net_TPS ThirdPersonExampleMap?listen -game<enter>

 - 위의 서버에 연결되는 클라이언트 실행 : D:\MyGame>Net_TPS 127.0.0.1(서버 IP) -game<enter>


위와 같이 게임을 실행하면 방화벽에 접근을 허용할 것인지 묻는 창이 뜰 때 허용하면 된다



Networking

 - 개념


Server/Client

 - Listen Server

 - Dedicated Server

 - 에디터에서 네트워크 프로그램 실행방법(1명, 2명, 화면크기 설정, Simulated Dedicated Server)

 - 에디터 밖에서 Listen 서버/클라이언트 실행 방법

 - 서버실행 : 프로젝트명.uproject 맵이름?listen -game

 - 클라이언트 실행 : 프로젝트명.uproject 127.0.0.1 -game

 - 서버측/클라이언트측 캐릭터 스폰하는 방법

 - 서버/클라이언트 동기화 실험(클라이언트 측에서 키보드를 이용하여 3인칭 캐릭터를 움직여본다)

 - 클라이언트측에서 값이 변경되는 조작은 서버에게 요청하면 서버에서 값을 변경하고 그 값을 클라이언트에게 복제한다


서버에서만 값을 변경해야 하는 이유

 - 클라이언트에서 값을 변경할 수 있다면 클라이언트 머신을 해킹하여 점수나 승패와 관련한 중용한 값을 조작할 수 있다

 - 그러므로 값이 변경되는 부분의 스크립트는 Switch Has Authority 노드를 사용해야 한다

 - 노드의 우측상단 모서리에 서버 모양을 가진 노드(예를 들면, Apply Damage노드)는 서버에서만 호출되어야 하고 클라이언트가 호출할 시에는 아무런 작용을 하지 않는다

 - Receive Draw HUD처럼 노드 우측 상단에 모니터 아이콘을 가진 노드는 서버가 별다른 주의를 기울이지 않는다


Replication(아래 항목 각 실험)

 - Actor : 서버에서 스폰된 액터가 클라이언트에게도 복제됨(액터 클래스의 변수는 개별적으로 Replicate 설정가능)

 - Variable(RepNotify포함) : 서버에서 변경된 변수의 값이 클라이언트에게 복제됨

 - RepNotify 로 설정된 변수가 변경되면 자동생성된 함수(OnRep_변수명)가 자동으로 실행됨(C++에서는 수동으로 호출해야 함)

 - RepNotify : 변수의 값이 변경되면 비주얼 이펙트를 한번 보여주려는 경우에 유용함(Replication Notify)

 - Function : 서버에서 실행된 함수가 클라이언트에서도 실행됨(일회성 이벤트)

 - 변수복제(변경된 값을 유지할 필요가 있을 때 사용), 함수복제(일회성 효과에 사용)

 - Replicate 변수는 변경하는 Set 노드의 우측 상단에는 2개의 볼이 겹쳐진 아이콘이 표시된다

 - 변수복제는 신뢰성(Reliable) 있음: 클라이언트에 반드시 복제됨

 - 함수복제는 신뢰성을 선택할 수 있음 : 장식성 효과에는 Reliable 설정하지 않는 것이 바람직함


변수복제의 용도

 - 예를 들면, 조명이 켜진 상태로 한동안 유지되어야 할 때

 - RepNotify : 변경된 상태가 한동안 지속되아야 할 필요가 있고 비주얼 이펙트를 출력할 필요가 있을 때


함수복제의 용도

 - 예를 들면, 캐릭터가 특정 영역에 들어가면 폭발하는 비주얼 이펙트는 일회성 효과이므로 함수복제를 이용함


Switch Has Authority(아래 항목 실험)

 - Replicate 설정된 변수를 클라이언트가 직접 변경하면 어떤 현상 발생?

 - Replicate 설정된 변수를 서버에서만 변경하려면?

 - Switch Has Authority는 꼭 필요한가?

 

Function Replication

 - Not Replicated : 요청한 클라이언트를 제외한 모든 클라이언트에게는 복제되지 않음

 - Replicated : 모든 클라이언트에게 복제됨

 - Multicast : 서버에서만 호출되어 모든 클라이언트에게 복제됨

 - Reliable : 선택하면 클라이어트에서도 반드시 실행됨

 - 순수한 장식성 효과라면 Reliable 을 설정할 필요는 없다(성능을 고려)

 - Multicast 는 한 프레임 내에서 여러변 호출되더라도 거절됨

 - Run on Server : 클라이언트가 호출하여 서버에서 함수 실행

 - Run on Owning Client : 서버가 호출하여 액터를 소유한 클라이언트에서 함수 실행

`

Network Relevancy(연관성)

 - 액터가 특정 시간, 특정 머신에 연관성이 있는가 결정하는 것

 - 연관성이 있는 클라이언트에만 정보를 전달하여 통신량을 줄이고 성능 저하를 막기위한 것

 - 보이지 않거나 다른 공간에 있는 상대방에 대해서 현재 당장 알아야 할 필요는 없다

 - 나중에는 결국 알아야 할 필요가 있을 수 있다

 - 멀리 있는 플레이어에게 잘 보이지도 않는 고비용 이펙트를 굳이 서버로부터 복제하여 보게 할 필요가 없다

 - 일정한 거리 안으로 들어온 플레이어에게만 현재 상황에 해당하는 장면을 볼 수 있게 한다

Posted by cwisky
Unreal C++/C++ Actor Example2018. 11. 22. 00:45

C++ 액터 클래스를 이용하여 복잡한 로직을 작성하고 BP를 파생하여 사용하는 예


레벨 위에 TargetPoint 4개를 큰 사각형이 되도록 배치하고 그 위치를 향하여 계속 이동하는 큐브를 구현하고자 한다. 이러한 로직이 그다지 복잡한 것은 아니지만 블루프린트를 이용하기에는 적합하지 않을 정도로 복잡해지므로 C++ 액터 클래스를 생성하고 그 안에 사각형 운동을 하는 로직을 작성한 다음 완성된 C++액터 클래스를 기반으로 블루프린트 액터를 생성하여 사용하면 C++의 장점과 블루프린트의 장점을 동시에 활용할 수 있다


CppActor.h

#pragma once


#include "Runtime/Engine/Classes/Engine/TargetPoint.h"

#include "CoreMinimal.h"

#include "GameFramework/Actor.h"

#include "CppActor.generated.h"


UCLASS(Blueprintable, BlueprintType)

class CPP_TEST_API ACppActor : public AActor

{

GENERATED_BODY()

public:

// Sets default values for this actor's properties

ACppActor();


protected:

// Called when the game starts or when spawned

virtual void BeginPlay() override;


public:

// Called every frame

virtual void Tick(float DeltaTime) override;


UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cpp")

TArray<ATargetPoint *> Arr;


UFUNCTION(BlueprintCallable, Category = "Cpp")

void MoveRect(float DeltaTime);

};




위의 헤더파일에 Arr 배열의 원소가 포인터로 선언된 것은 블루프린트에서 취급되는 모든 UObject와 그 하위 클래스의 변수들은 모두 포인터이다. 언리얼 에디터에서 레퍼런스라고 부르고 있지만 C++의 레퍼런스와는 달리 언리얼의 레퍼런스는 포인터를 의미하는 경우가 많다

https://answers.unrealengine.com/questions/351656/pointer-to-uobject-and-inheritance-in-bpc.html

All UObject (and its descendants) variables are pointers.

Calling it a reference is misleading. It is not a reference (&), it is a pointer.



CppActor.cpp

#include "CppActor.h"

#include "../Public/CppActor.h"



// Sets default values

ACppActor::ACppActor()

{

  // 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;


}


// Called when the game starts or when spawned

void ACppActor::BeginPlay()

{

Super::BeginPlay();

}


// Called every frame

void ACppActor::Tick(float DeltaTime)

{

Super::Tick(DeltaTime);


}


void ACppActor::MoveRect(float DeltaTime)

{

static int idx = 0;

static FVector * SpdVec = new FVector(0.0F, 200.0F*DeltaTime, 0.0F);

FVector ALoc, NextLoc;

ALoc = GetActorLocation();

int next = idx + 1 == 4 ? 0 : idx + 1;

NextLoc = Arr[next]->GetActorLocation();


switch (idx) {

case 0:

if (ALoc.Y >= NextLoc.Y) {

ALoc.Y = NextLoc.Y;

idx++;

SpdVec->X = -200.0F * DeltaTime; SpdVec->Y = 0.0F; SpdVec->Z = 0.0F;

}

break;

case 1:

if (ALoc.X <= NextLoc.X) {

ALoc.X = NextLoc.X;

idx++;

SpdVec->X = 0.0F; SpdVec->Y =-200.0F * DeltaTime; SpdVec->Z = 0.0F;

}

break;

case 2:

if (ALoc.Y <= NextLoc.Y) {

ALoc.Y = NextLoc.Y;

idx++;

SpdVec->X = 200.0F * DeltaTime; SpdVec->Y =0.0F; SpdVec->Z = 0.0F;

}

break;

case 3:

if (ALoc.X >= NextLoc.X) {

ALoc.X = NextLoc.X;

idx=0;

SpdVec->X = 0.0F;  SpdVec->Y = 200.0F*DeltaTime; SpdVec->Z = 0.0F;

}

break;

}

ALoc += (*SpdVec);

SetActorLocation(ALoc);

}



위에 있는 헤더파일에는 아래와 같은 내용이 있다


UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cpp")

TArray<ATargetPoint *> Arr;


ATargetPoint 클래스 객체의 포인터를 원소로 하는 배열이 선언되어 있는데, 이 배열의 원소는 Blueprint에서 초기화하여 Arr 변수에 전달하기 위해 EditAnywhere 를 선언한 것이다. 위의 클래스를 드래그하여 레벨에 배치하거나 위의 클래스를 기반으로 블루프린트 클래스를 생성하여 블루프린트 에디터를 열고 [클래스 디폴트] 를 선택하고 우측 [디테일] 뷰를 보면 Arr변수의 원소를 초기화할 수 있는 항목이 나타난다


블루프린트에서 TargetPoint 배열을 선언할 때 원소의 자료형은 [TargetPoint 오브젝트 레퍼런스]형으로 지정해야 하고 [인스턴스 편집가능]을 선택하면 언리얼 에디터의 레벨 위에서 위의 액터를 선택했을 때 우측 Detail 뷰에서 배열의 원소를 할당할 수가 있으므로 레벨에 배치한 ATargetPoint을 선택하여 할당해 줄 수 있다


TargetPoint 레퍼런스 배열의 선언




위의 블루프린트에서 선언한 TargetPoint 참조배열에 원소를 초기화하는 예



블루프린트에서 생성하고 원소가 초기화된 배열을 C++의 TArray<AtargetPoint *> Arr 배열에 전달하는 블루프린트의 예

Posted by cwisky
Behavior Tree (Moving Fire)2018. 11. 2. 01:29

Idle, Patrol, Escape, Hide, MovingFire 등 상태전환이 포함된 비헤이비어 트리의 예





위의 비헤이비어 트리와 함께 사용된 애니메이션 블루프린트



거의 모든 로직을 비헤이비어 트리가 포함하도록 했으므로 AI Controller 클래스에는 아래의 내용 뿐이다

AIPerception 콤포넌트도 AIController에 설정했다


커스텀 AIController의 내용



애니메이션 블루프린트의 이벤트 그래프 내용

BP_AIController 내에 선언된 멤버변수인 Game State 값을 애니메이션 블루프린트에 연결하는 부분



AI 캐릭터가 플레이어를 응시하면서 좌에서 우로 총을 쏘면서 이동하게 하기

총을 연발로 쏘면서 좌에서 우로 이동할 때 시선은 플레이어를 보도록 하는 한가지 방법은 Service를 사용하는 것이다

아래와 같은 서비스를 생성하여 위의 비헤이비어의 내용 중에서 Simple Parallel 노드에 추가해주면 된다





위의 서비스를 Simple Parallel 노드에 적용한 예



Posted by cwisky

AI 캐릭터가 옆으로 빠르게 이동하면서 사격을 하는 내용의 블루프린트


총알은 First Person Projectile을 사용함

총알이 발사되는 시작 위치를 참조하기 위해 아래 그림처럼 캐릭터 앞에 Arrow 콤포넌트를 두고 콤포넌트 계층구조상 Mesh콤포넌트 하위에 배치했기 때문에 블루프린트에서 총알을 발사할 때는 언제든지 화살표의 위치를 참조하여 총알이 스폰되는 위치로 삼을 수 있다



AI Character의 이벤트 그래프






Enum_Game_State



BP_EnemyAnim  (애니메이션 블루프린트의 이벤트 그래프)


BP_EnemyAnim  (애니메이션 블루프린트의 애님 그래프)


Posted by cwisky
Custom Projectile2018. 10. 31. 15:47

총알로 사용할 커스텀 Projectile 만들기


레벨 위에서 마우스 우측을 누르면 커스텀 발사체가 발사되도록 한 예이다


발사체를 만들 때 핵심적인 컴포넌트는 다음과 같다

Projectile Movement : 발사속도, 반동을 설정

Sphere Collision : Collision Preset을 BlockAllDynamics, Physics Actor 등으로 설정



Actor 기반의 블루프린트 생성 > Projectile Movement, Sphere Collision, Sphere 를 콤포넌트로 추가


주의 : 컴포넌트 패널에 기본적으로 포함된 DefaultSceneRoot 컴포넌트는 Sphere Collision을 드래그하여 대치해야 설정한대로 작동하게 된다




발사속도 및 반동 설정

Projectile Movement 컴포넌트를 선택하고 디테일 뷰에서 아래와 같이 설정한다. 발사된 총알이 벽등에서 튕기는 현상을 연출하려면 아래처럼 Should Bounce 항목을 선택해야 한다



콜리전 컴포넌트의 충돌설정

콜리전 컴포넌트(여기서는 Sphere Collision)를 선택하고 Collision Presets 항목에서 Physics Actor 혹은 BlockAllDynamics 등으로 선택한다. 이벤트 그래프에서 충돌 이벤트를 구현할 생각이라면 아래처럼 Simulation Generates Hit Events 항목을 선택한다



Static Mesh 자체는 No Collision으로 설정

Sphere 컴포넌트는 총알 자체를 나타내는데 Collision Presets 항목에 NoCollision을 선택한다



Projectile Movement 컴포넌트의 이벤트를 이용하여 발사체 제거

발사체가 멈추었을 때 레벨에서 발사체를 제거하려면 Projectile Movement 컴포넌트의 On Projectile Stop 이벤트를 구현하면 된다



충돌 대상에 따라 Hit 혹은 Overlap 설정하기

발사체가 캐릭터에 명중했을 때 튕겨 나오지 않도록 하려면 아래처럼 Sphere Collision 설정시 Collision Presets을 Custom으로 하고 Pawn 항목에서 Overlap(겹침)에 선택하면 발사체가 캐릭터에 명중했을 때 그냥 관통하게 된다



마우스 우측을 누르면 발사

레벨에서 마우스 우측을 누르면 커스텀 발사체(BP_MyBullet)가 총구에서 정면으로 발사되도록 한 예

총구 앞에 화살표(Arrow)컴포넌트를 배치하고 그 위치에 발사체가 생성되도록 하면 된다


Posted by cwisky

Animation Starter Pack에 포함된 Skeletal Mesh와 애니메이션 시퀀스를 사용한 Behavior Tree예제


아래의 예제는 가급적 비헤이비어 트리에 대부분의 스크립트를 포함하는 것을 목적으로 작성한 것이므로 특정 패턴과는 견해가 다를 수 있다. 비헤이비어 트리의 기본 개념을 이해하기 위한 예제이다. 

아래와 같이 작성하고 게임을 실행하면 AI 캐릭터가 비헤이비어 트리의 로직에 따라 Idle, Patrol 상태를 반복하며 지정된 애니메이션이 실행되는 것을 확인할 수 있다


비헤이비어 트리의 내용

ASP에 포함된 캐릭터가 Patrol 상태에서는 임의의 이동가능한 장소를 정해서 이동하고 이동을 마친 후에는 Idle 상태로 전환하고 2초 동안 쉰다. 그리고 다시 Patrol 상태로 전환되어 같은 내용이 무한히 반복된다


작업절차

  1. 일인칭 템플릿을 사용한 프로젝트를 생성하고 레벨에 Nav Mesh Bound Volume 설정
  2. 마켓 플레이스에서 Animation Starter Pack을 검색하여 프로젝트에 포함한다
  3. ASP에 포함된 Ue4ASP_Character(블루프린트)를 복사하여 BP_AIEnemy 라는 이름으로 저장하고 더블클릭하여 캐릭터 뒤에 배치된 카메라와 카메라붐을 삭제하고 저장한 후 CB에서 드래그하여 레벨에 배치한다
  4. AIController를 기반으로 BP_AIController 클래스를 생성한다
  5. CB에서 마우스 우측 > 인공지능 > 비헤이비어 트리 > 이름을 BT_EnemyAI 으로 저장한다
  6. CB에서 마우스 우측 > 인공지능 > 블랙보드 > 이름을 BT_EnemyBlackboard 으로 저장한다
  7. Ue4ASP_Character 더블클릭 > 디테일뷰 > AIController 항목 : BP_AIController 설정
  8. BT_EnemyAI 더블클릭 > 블랙보드 항목 : BT_EnemyBlackboard 설정
  9. Enum_GameState : 게임상태를 저장할 열거형 블루프린트 클래스
  10. BP_EnemyAnim : 애니메이션 블루프린트(스테이트 머신을 이용한 애니메이션 실행)
  11. 비헤이비어 트리의 툴바를 통해 생성되는 3개의 Task
  • Task_GetRandomPos : AI캐릭터가 이동가능한 임의의 위치벡터 생성
  • Task_Set_Idle : 게임상태 변수를 STATE_IDLE로 설정(AIController, Blackboard)
  • Task_Set_Patrol : 게임상태 변수를 STATE_PATROL로 설정(AIController, Blackboard)



AI 캐릭터 클래스인 Ue4ASP_Character에는 아무런 스크립트도 작성하지 않았다


BP_AIController 의 이벤트 그래프에는 다음과 같이 작성한다



BT_EnemyAI 는 다음과 같이 작성



BT_EnemyBlackboard



Enum_GameState



BP_EnemyAnim(애니메이션 블루프린트, 이벤트 그래프)



BP_EnemyAnim(애니메이션 블루프린트, 애님 그래프)



Task_GetRandomPos



Task_Set_Idle



Task_Set_Patrol


Posted by cwisky
Vector/Find Look At Rotation2018. 10. 30. 16:26

Find Look at Rotation 노드를 사용하여 AI 캐릭터가 타겟 오브젝트를 향하여 몸을 돌리는 예


Find Look at Rotation 노드는 2개의 위치 벡터를 입력 받아서 Target 방향을 바라볼 수 있는 방향정보(Rotator)를 리턴한다


start : 현재 액터가 있는 위치벡터

Target : 바라 볼 오브젝트의 위치벡터


Posted by cwisky