[UE4] 製作UDP Plugin

產生一個UE4 Plugin

  1. 開啟UE4專案
  2. Edit -> Plugins -> New Plugin
  3. 選擇 "Blank",建立一個plugin空殼
編輯 [PluginName].Build.cs,新增UDP所需Module

// 新增Json是因為提供了一個可以外部載入檔案來初始化UDP Sender的方法 (StartUDPSenderViaJson)

以下是插件原始碼

// Fill out your copyright notice in the Description page of Project Settings.
#include "UDPReceiver.h"
// Sets default values
AUDPReceiver::AUDPReceiver(const 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;
ListenSocket = NULL;
}
// Called when the game starts or when spawned
void AUDPReceiver::BeginPlay()
{
Super::BeginPlay();
StartUDPReceiver(TEXT("SocketName"), "127.0.0.1", 8890);
}
// Called every frame
void AUDPReceiver::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AUDPReceiver::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
UDPReceiver->Stop();
delete UDPReceiver;
UDPReceiver = nullptr;
// Clear all sockets!
// Makes sure repeat plays in Editor dont hold on to old sockets!
if (ListenSocket)
{
ListenSocket->Close();
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ListenSocket);
}
}
bool AUDPReceiver::StartUDPReceiver(
const FString& YourChosenSocketName,
const FString& TheIP,
const int32 ThePort)
{
ScreenMsg("RECEIVER INIT");
FIPv4Address Addr;
FIPv4Address::Parse(TheIP, Addr);
//Create Socket
FIPv4Endpoint Endpoint(Addr, ThePort);
//BUFFER SIZE
int32 BufferSize = 2 * 1024 * 1024;
ListenSocket = FUdpSocketBuilder(*YourChosenSocketName)
.AsNonBlocking()
.AsReusable()
.BoundToEndpoint(Endpoint)
.WithReceiveBufferSize(BufferSize);
FTimespan ThreadWaitTime = FTimespan::FromMilliseconds(100);
UDPReceiver = new FUdpSocketReceiver(ListenSocket, ThreadWaitTime, TEXT("UDP RECEIVER"));
UDPReceiver->OnDataReceived().BindUObject(this, &AUDPReceiver::Recv);
UDPReceiver->Start();
return true;
}
void AUDPReceiver::Recv(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt)
{
ScreenMsg("Received bytes", ArrayReaderPtr->Num());
TArray<FString> Data;
*ArrayReaderPtr << Data;
BPEvent_DataReceived(Data);
}
float AUDPReceiver::UDPSender_GetFloatData(UPARAM(ref) TArray<FString>& packet)
{
if (packet.Num() == 0) return -1;
FString s = packet[0];
packet.RemoveAt(0);
float result = FCString::Atof(*s);
return result;
}
view raw UDPReceiver.cpp hosted with ❤ by GitHub
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Networking.h"
#include "Engine.h"
#include "GameFramework/Actor.h"
#include "UDPReceiver.generated.h"
UCLASS()
class UDPPLUGIN_API AUDPReceiver : public AActor
{
GENERATED_BODY()
public:
/** Data has been received!! */
UFUNCTION(BlueprintImplementableEvent)
void BPEvent_DataReceived(const TArray<FString>& ReceivedData);
UFUNCTION(BlueprintCallable, Category = "UDReceiver")
float UDPSender_GetFloatData(UPARAM(ref) TArray<FString>& packet);
public:
FSocket* ListenSocket;
FUdpSocketReceiver* UDPReceiver = nullptr;
virtual void Recv(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt);
bool StartUDPReceiver(
const FString& YourChosenSocketName,
const FString& TheIP,
const int32 ThePort
);
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UDPReceiver")
bool ShowOnScreenDebugMessages;
//ScreenMsg
FORCEINLINE void ScreenMsg(const FString& Msg)
{
UE_LOG(LogTemp, Log, TEXT("%s"), *Msg);
if (!ShowOnScreenDebugMessages) return;
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, *Msg);
}
FORCEINLINE void ScreenMsg(const FString& Msg, const float Value)
{
UE_LOG(LogTemp, Log, TEXT("%s %f"), *Msg, Value);
if (!ShowOnScreenDebugMessages) return;
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %f"), *Msg, Value));
}
FORCEINLINE void ScreenMsg(const FString& Msg, const FString& Msg2)
{
UE_LOG(LogTemp, Log, TEXT("%s %s"), *Msg, *Msg2);
if (!ShowOnScreenDebugMessages) return;
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %s"), *Msg, *Msg2));
}
public:
// Sets default values for this actor's properties
AUDPReceiver(const 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;
/** Called whenever this actor is being removed from a level */
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
};
view raw UDPReceiver.h hosted with ❤ by GitHub
// Fill out your copyright notice in the Description page of Project Settings.
#include "UDPSender.h"
// Sets default values
AUDPSender::AUDPSender(const 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;
SenderSocket = NULL;
}
// Called when the game starts or when spawned
void AUDPSender::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AUDPSender::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called whenever this actor is being removed from a level
void AUDPSender::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
if (SenderSocket)
{
SenderSocket->Close();
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(SenderSocket);
}
}
bool AUDPSender::StartUDPSender(const FString& YourChosenSocketName, FString RemoteIP /*= "127.0.0.1"*/, int32 RemotePort /*= 8890*/)
{
//Create Remote Address.
RemoteAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
bool bIsValid;
RemoteAddr->SetIp(*RemoteIP, bIsValid);
RemoteAddr->SetPort(RemotePort);
if (!bIsValid)
{
ScreenMsg("IP address was not valid!" + RemoteIP);
return false;
}
SenderSocket = FUdpSocketBuilder(*YourChosenSocketName)
.AsReusable()
.WithBroadcast()
;
//Set Send Buffer Size
int32 SendSize = 2 * 1024 * 1024;
SenderSocket->SetSendBufferSize(SendSize, SendSize);
SenderSocket->SetReceiveBufferSize(SendSize, SendSize);
ScreenMsg("UDP Sender Initialized Successfully!!!");
return true;
}
bool AUDPSender::StartUDPSenderViaJson(FString filePath)
{
// Load JSON
FString loadPath = FPaths::ProjectContentDir() + filePath;
if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*loadPath))
{
UE_LOG(LogTemp, Warning, TEXT("FILE NOT FOUND %s"), *loadPath);
return false;
}
FString fileData;
FFileHelper::LoadFileToString(fileData, *loadPath);
TSharedPtr<FJsonObject> JsonParsed;
TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(fileData);
FString IPFromJSON = TEXT("");
int32 PortFromJSON = 0;
if (FJsonSerializer::Deserialize(JsonReader, JsonParsed))
{
TSharedPtr<FJsonObject> section = JsonParsed->GetObjectField("General");
IPFromJSON = section->GetStringField("IP");
PortFromJSON = section->GetIntegerField("Port");
ScreenMsg("RemoteAddress From JSON: [MyUDP] IP=" + IPFromJSON + ", Port=", PortFromJSON);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("JSON deserialize failed"));
return false;
}
// Load JSON END
if (!IPFromJSON.IsEmpty() && PortFromJSON != 0)
{
}
else
{
UE_LOG(LogTemp, Warning, TEXT("JSON parsing failed"));
return false;
}
//Create Remote Address.
RemoteAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
bool bIsValid;
RemoteAddr->SetIp(*IPFromJSON, bIsValid);
RemoteAddr->SetPort(PortFromJSON);
if (!bIsValid)
{
ScreenMsg("IP address was not valid!" + IPFromJSON);
return false;
}
SenderSocket = FUdpSocketBuilder(TEXT("") /* YourChosenSocketName */)
.AsReusable()
.WithBroadcast()
;
//Set Send Buffer Size
int32 SendSize = 2 * 1024 * 1024;
SenderSocket->SetSendBufferSize(SendSize, SendSize);
SenderSocket->SetReceiveBufferSize(SendSize, SendSize);
ScreenMsg("UDP Sender Initialized Successfully!!!");
return true;
}
TArray<FString> AUDPSender::UDPSender_AddFloatData(UPARAM(ref) TArray<FString>& packet, float data)
{
packet.Add(FString::SanitizeFloat(data));
return packet;
}
bool AUDPSender::UDPSender_SendPacket(TArray<FString> packet)
{
if (!SenderSocket)
{
ScreenMsg("No sender socket");
return false;
}
int32 BytesSent = 0;
FArrayWriter Writer;
Writer << packet;
SenderSocket->SendTo(Writer.GetData(), Writer.Num(), BytesSent, *RemoteAddr);
if (BytesSent <= 0)
{
const FString Str = "Socket is valid but the sender sent 0 bytes.";
UE_LOG(LogTemp, Error, TEXT("%s"), *Str);
ScreenMsg(Str);
return false;
}
ScreenMsg("UDP~ Send Succcess! Bytes Sent = ", BytesSent);
return true;
}
view raw UDPSender.cpp hosted with ❤ by GitHub
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Networking.h"
#include "Engine.h"
#include "GameFramework/Actor.h"
#include "UDPSender.generated.h"
UCLASS()
class UDPPLUGIN_API AUDPSender : public AActor
{
GENERATED_BODY()
public:
TSharedPtr<FInternetAddr> RemoteAddr;
FSocket* SenderSocket;
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UDPSender")
bool ShowOnScreenDebugMessages;
UFUNCTION(BlueprintCallable, Category = "UDPSender")
bool StartUDPSender(const FString& YourChosenSocketName, FString RemoteIP = "127.0.0.1", int32 RemotePort = 8890);
/** ProjectContentDir + filePath */
UFUNCTION(BlueprintCallable, Category = "UDPSender")
bool StartUDPSenderViaJson(FString filePath);
UFUNCTION(BlueprintCallable, Category = "UDPSender")
TArray<FString> UDPSender_AddFloatData(UPARAM(ref) TArray<FString>& packet, float data);
UFUNCTION(BlueprintCallable, Category = "UDPSender")
bool UDPSender_SendPacket(TArray<FString> packet);
//ScreenMsg
FORCEINLINE void ScreenMsg(const FString& Msg)
{
UE_LOG(LogTemp, Log, TEXT("%s"), *Msg);
if (!ShowOnScreenDebugMessages) return;
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, *Msg);
}
FORCEINLINE void ScreenMsg(const FString& Msg, const float Value)
{
UE_LOG(LogTemp, Log, TEXT("%s %f"), *Msg, Value);
if (!ShowOnScreenDebugMessages) return;
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %f"), *Msg, Value));
}
FORCEINLINE void ScreenMsg(const FString& Msg, const FString& Msg2)
{
UE_LOG(LogTemp, Log, TEXT("%s %s"), *Msg, *Msg2);
if (!ShowOnScreenDebugMessages) return;
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %s"), *Msg, *Msg2));
}
public:
// Sets default values for this actor's properties
AUDPSender(const 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;
// Called whenever this actor is being removed from a level
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
};
view raw UDPSender.h hosted with ❤ by GitHub

參考資料

這個網誌中的熱門文章

Windows10 版本1607後可啟用支援長路徑檔名 (Maximum Path Length Limitation)

標準使用者如何執行需系統管理者權限的程式