[UE4] 製作UDP Plugin
產生一個UE4 Plugin
以下是插件原始碼
參考資料
- 開啟UE4專案
- Edit -> Plugins -> New Plugin
- 選擇 "Blank",建立一個plugin空殼
編輯 [PluginName].Build.cs,新增UDP所需Module
// 新增Json是因為提供了一個可以外部載入檔案來初始化UDP Sender的方法 (StartUDPSenderViaJson)
以下是插件原始碼
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
}; |
參考資料