作者Roronia (RRR)
看板GameDesign
标题[程式] 使用ObjectDeliverer传送讯息
时间Mon Feb 24 06:56:30 2020
因为需求关系,
小弟用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://webptt.com/cn.aspx?n=bbs/GameDesign/M.1582498593.A.005.html
※ 编辑: Roronia (220.141.86.66 台湾), 02/24/2020 06:58:28
1F:推 gonzdevour: 推 02/24 13:40
※ 编辑: Roronia (220.141.86.66 台湾), 02/24/2020 17:53:19
2F:推 oopFoo: 不太懂这要做什麽?flatbuffers不好用吗? 02/25 10:14
小弟没有用过flatbuffers的经验,
所以好不好用我也回答不出来
不过用途看起来是差不多的,都是传资料
以前待过的公司都用类似的作法
写一个class或struct,塞东西进去
然後底层再用socket丢出去
自己就也写了一个UE的版本XD
※ 编辑: Roronia (220.141.86.66 台湾), 02/25/2020 12:58:21
3F:推 oopFoo: 游戏界是蛮喜欢重写轮子,但有时真的不必要。 02/25 14:44
4F:→ oopFoo: 现在传输资料,基本上就是flatbuffers, protobuf, json 02/25 14:45
5F:→ oopFoo: 选一个。flatbuffers是为游戏界设计的,有效率稍难用。 02/25 14:47
6F:推 oopFoo: 感谢你分享。 02/25 14:54