看板 GameDesign 關於我們 聯絡資訊
因為需求關係, 小弟用UE4的ObjectDeliverer,還有FMermoryReader/Writer, 憑藉微薄的經驗寫了簡單的socket message, 方便在裝置之間溝通 業界應該有很多類似的作法,不知道以前有沒有其他人貼過, 自己用unreal刻了一個,分享給大家看看 ObjectDeliverer是UE marketplace上的一套插件, 可以在裝置之間傳送TCP/IP的訊息, 使用方式可以參考他的頁面 https://github.com/ayumax/ObjectDeliverer/ 首先寫一個存訊息資料的class UCLASS() class UNetMessage : public UObject { GENERATED_BODY() public: UNetMessage() {} void Initialize(int32 InParameter) { Data = InParameter; } UPROPERTY() int32 IntData; } IntData是我想要傳送出去的資料, 接著還需要一個將資料轉換成byte array的function -- SerializeMsg(), 以及儲存轉換後的buffer UPROPERTY() TArray<uint8> MessageBuffer; void SerializeMsg(FArchiveProxy& Archive) { Archive << IntData; } 用法是New出一個物件之後用ObjectDeliverer送出 // 建立訊息及初始化資料 UNetMessage* NetMsg = NewObject<UNetmessage>(); NetMsg->Initialize(IntegerPara); // 轉換成bitarry FMemoryWriter MemoryWriter = FMemoryWriter(NetMsg->MessageBuffer, true, true); NetMsg->SerializeMsg(MemoryWriter); // 使用ObjectDeliverer送出訊息 Deliverer->Send(NetMsg->MessageBuffer); 先將MessageBuffer丟給MemoryWriter,再餵給SerializeMsg, 裡面的Archive << IntData會被轉換成raw data存進MessageBuffer內。 但是這樣不是很夠,因為一定會有很多不同的message要送 所以可以再寫一個enum以及Message的base class來區分不同的message UENUM() enum class ENetMessageIndex { Index_1, Index_2, // ... } // Base class UCLASS() class UNetMessageBase : public UObject { GENERATED_BODY() public: UNetMessageBase() {} UNetMessageBase(ENetMessageIndex InNetMessageIndex) : NetMessageIndex((int32)InNetMessageIndex) { } void Initialize() {}; virtual void SerializeMsg(FArchiveProxy& Archive) {} UPROPERTY() int32 NetMessageIndex; UPROPERTY() TArray<uint8> MessageBuffer; }; UCLASS() class UNetMessage : public UNetMessageBase { GENERATED_BODY() public: UNetMessage() : UNetMessageBase(ENetMessageIndex::Index_1) {} void Initialize(int32 InParameter) { Data = InParameter; } void SerializeMsg(FArchiveProxy& Archive) { Archive << Data; } UPROPERTY() int32 Data; }; SerializeMsg的部分則變成 // 先Serialize NetMessageIndex MemoryWriter << NetMessage->NetMessageIndex; NetMsg->SerializeMsg(MemoryWriter); 傳送的部分大致是這樣,接收的部分則是反過來, 先讀取出MessageIndex,再讀取MessageBuffer內的資料 void OnReceive(const UObjectDelivererProtocol* Socket, const TArray<uint8>& Buffer) { FMemoryReader MemoryReader = FMemoryReader(Buffer, true); uint32 Index; // 先讀取MessageIndex FMemoryReader << Index; Msg->SerializeMsg(FMemoryReader); switch ((ENetMessageIndex)Index) { case ENetMessageIndex::Index_1: UNetMessageObject* Msg = NewObject(this); // 依照Message做其他事情... break; } } OnReceive()是一個綁在ObjectDeliverer上的callback event, 當Deliverer收到資料的時候會呼叫他 相較FMemoryWriter,由於這邊是使用FMemoryReader, SerializeMsg()會變成從byte array內依序撈資料出來, 然後依照不同Index做不同事情,這樣就完成了。 最後是用macro簡化傳送部分的code #define NET_MSG(NetMessageClass, ...) \ NetMessageClass* NetMessage = NewObject<NetMessageClass>(); \ NetMessage->Initialize(__VA_ARGS__); \ FMemoryWriter MemoryWriter = FMemoryWriter(NetMessage->MessageBuffer, true, true); \ FNetMessageArchive NetMessageArchive = FNetMessageArchive(MemoryWriter);\ NetMessageArchive << NetMessage->NetMessageIndex; \ NetMessage->SerializeMsg(NetMessageArchive); \ 最後的結果: NET_MSG(UNetMessage, Parameter); Deliverer->Send(NetMessage->MessageBuffer); 這樣就可以在不同裝置之間傳送一個UNetMessage物件了~ 要寫不同message的話可以直接繼承UNetMessageBase, 宣告想要傳送的資料,定義好enum還有Initialize就可以囉 第一次分享程式,沒想到用ptt意外的難打 還請多多指教了 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 220.141.86.66 (臺灣) ※ 文章網址: https://www.ptt.cc/bbs/GameDesign/M.1582498593.A.005.html ※ 編輯: Roronia (220.141.86.66 臺灣), 02/24/2020 06:58:28
gonzdevour: 推 02/24 13:40
※ 編輯: Roronia (220.141.86.66 臺灣), 02/24/2020 17:53:19
oopFoo: 不太懂這要做什麼?flatbuffers不好用嗎? 02/25 10:14
小弟沒有用過flatbuffers的經驗, 所以好不好用我也回答不出來 不過用途看起來是差不多的,都是傳資料 以前待過的公司都用類似的作法 寫一個class或struct,塞東西進去 然後底層再用socket丟出去 自己就也寫了一個UE的版本XD ※ 編輯: Roronia (220.141.86.66 臺灣), 02/25/2020 12:58:21
oopFoo: 遊戲界是蠻喜歡重寫輪子,但有時真的不必要。 02/25 14:44
oopFoo: 現在傳輸資料,基本上就是flatbuffers, protobuf, json 02/25 14:45
oopFoo: 選一個。flatbuffers是為遊戲界設計的,有效率稍難用。 02/25 14:47
oopFoo: 感謝你分享。 02/25 14:54