Unreal Engine 4でのFieldモジュールの利用方法

Fieldモジュールは二次元平面上に仮想世界を構築し、距離的に近いクライアントのみ同期を取る性質があります。 これはMMORPGのような広大なフィールドを扱うゲームに最適です。 このエントリではDiarkisのUnreal Engine 4(UE4)用プラグインを使ってフィールドに参加し、実際にキャラクターの位置を同期してみます。

サンプルプログラムの実行方法

こちら よりサンプルプログラムをダウンロードし実行してください。

最初にDiarkis Cloudとの接続画面が表示されるので、必要な情報を入力し接続してください(詳しくは こちらのエントリ を参照してください)。

接続が成功すると Select Feature と書かれた画面が表示されるので、 Field Demos を選択してください。

Field Demos(上記画像参考)ではFieldモジュールに関するデモを試すことができます(今回のデモは一つしかありません)。

1. Sync Character Demo を選択し、次に表示された画面で Join のボタンを押すとフィールドに参加します。

フィールドへの参加が成功すると Joining field と書かれた画面(上記画像参考)が表示されます。

デモでは半径5メートル(UE4での)以内のキャラクターの位置が同期されます。

またフロアが四色(赤、青、緑、黄)に色分けされていますが、異なる色のフロアにいるキャラクターは同期がとれない(姿が見えない)ことがわかります。これはDiarkis Fieldの機能の一つで、フィールドの座標空間(x, y)において、正の座標を持つクライアントと負の座標を持つクライアントは互いに同期をとらないようになっているからです(よって四色の交わるところが原点(0, 0)となります)。

Fieldモジュールの利用方法

ここからはプラグインの使い方を実際のソースコードを交えて解説します。

プラグインの機能にアクセスするには、 DiarkisFieldBase のクラスを継承する必要があります(詳しくは 以前のエントリ を参照してください)。

今回のエントリでは便宜上、以下の名前を派生クラスとして使います。

// 実際は自由なクラス名をつけてください
DiarkisInterfaceBaseを継承したクラス:CustomDiarkisInterface
DiarkisFieldBaseを継承したクラス:CustomDiarkisField

CustomDiarkisInterfaceの実装

DiarkisInterfaceBaseを継承してCustomDiarkisInterfaceを作成します。

コンストラクタの中で、CustomDiarkisFieldを持たせるようにします。

#include "DiarkisInterfaceBase.h"

class CustomDiarkisInterface : public DiarkisInterfaceBase
{
public:
	CustomDiarkisInterface(std::string host, std::string clientKey, uint64_t uid, LogOutType out);

	virtual ~CustomDiarkisInterface() {};
};
#include "CustomDiarkisInterface.h"
#include "CustomDiarkisField.h" // CustomDiarkisFieldのインクルード(後に解説)。

CustomDiarkisInterface::CustomDiarkisInterface(std::string host, std::string clientKey, uint64_t uid, LogOutType out)
	: DiarkisInterfaceBase(host, clientKey, uid, out)
{
	this->fieldBase = make_shared<CustomDiarkisField>(); // Fieldモジュールを上書き
}

最後にDiarkisInterfaceBaseインスタンスの代わりにCustomDiarkisInterfaceインスタンスを作成するようにします(この部分は以前のエントリと同じです)。

+ #include "CustomDiarkisInterface.h"

  bool AMyClass::Connect(const FString& Host, const FString& ClientKey, int64 UID, bool bUseUdp)
  {
  	...
  	...
- 	this->diarkis = MakeShared<DiarkisInterfaceBase>(this, host, clientKey, (uint64_t) UID, LogOutType::FILE_OUT); // 削除
+ 	this->diarkis = MakeShared<CustomDiarkisInterface>(this, host, clientKey, (uint64_t) UID, LogOutType::FILE_OUT);
  	this->diarkis->WaitUntilReady();
  	...
  	...
  }

CustomDiarkisFieldの実装

DiarkisFieldBaseを継承してCustomDiarkisFieldを作成します。この時各種コールバック関数(OnSyncInt ~ OnDisappear)をオーバーライドします。

class CustomDiarkisField : public DiarkisFieldBase
{
public:
	CustomDiarkisField() {};

	virtual ~CustomDiarkisField() {};

protected:
	// 各種コールバック関数をオーバーライド
	void OnSyncInt(const std::vector<std::vector<uint8_t>>& list) override;

	void OnSync(const std::vector<uint8_t>& msg) override;

	void OnDisappear(const std::string& remoteUid) override;
};

フィールドモジュールを初期化する

フィールドに参加するには、まずDiarkisInterfaceBase::SetupFieldを呼び出してFieldモジュールを初期化します。

void AMyClass::SetupField()
{
	this->diarkis->SetupField();
}

自分の位置情報を送信する

DiarkisInterfaceBase::SendFieldSyncを呼び出し、自分の位置情報をサーバに送信します。

void AMyClass::SendFieldSync(int X, int Y, int Z, const TArray<uint8>& Payload, bool bReliable)
{
	std::vector<uint8_t> payload(Payload.Num());
	for (int i = 0; i < Payload.Num(); ++i)
	{
		payload[i] = Payload[i];
	}
	this->diarkis->SendFieldSync(X, Y, Z, payload, bReliable, 0, PT_NONE, 0);
}

SendFieldSyncには以下の引数を渡します。

x:ワールドX座標(単位メートル。Fieldは半径50メートル以内のクライアントと同期する)。
y:ワールドY座標(単位メートル。Fieldは半径50メートル以内のクライアントと同期する)。
z:空間(高さではない)。
payload:送信するデータ。
reliable:RUDPで送るかどうか(トランスポート層にUDP指定時のみ)。

SendFieldSyncで送られたデータは、距離的に近い他のクライアントのOnSyncIntもしくはOnSync関数が呼ばれることで伝わります。OnSyncによって受け取ったデータには先頭にメタ情報が加えられているので、 RemoveHeader を呼び出してpayloadだけを取り出します。

void CustomDiarkisField::OnSyncInt(const std::vector<std::vector<uint8_t>>& list)
{
	DiarkisFieldBase::OnSyncInt(list);
}

void CustomDiarkisField::OnSync(const std::vector<uint8_t>& payload)
{
	DiarkisFieldBase::OnSync(payload);

	uint16_t dataType;
	ProfileType profileType;
	uint32_t createDelta;
	int64_t sendTime;
	int32_t sendDelta;
	uint32_t serverDelta;

	// remove header
	vector<uint8_t>& removeHeaderPayload = (vector<uint8_t>&) payload;
	this->RemoveHeader(removeHeaderPayload, dataType, profileType, createDelta, sendTime, sendDelta, serverDelta);
}

他のクライアントが視界から消えたのを検出する

他のクライアントが視界から消えた場合、OnDisappearが呼ばれます。

void CustomDiarkisField::OnDisappear(const std::string& remoteUid)
{
	DiarkisFieldBase::OnDisappear(remoteUid);

	std::string disappearClientUID = remoteUID;
}

最終更新