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