UE4修改自Rama的UDP通信蓝图插件
UE4.15没有提供蓝图UDP的组件,可以在网上找到一个ID叫Rama写的源代码,我把它封装成插件了(MyUdpPlugin),方便在各个UE4版本工程中使用UDP通信。
使用方式:
1、在自己的工程根目录下新建一个Plugins文件夹,将MyUdpPlugin拷贝进去
2、一般情况下需要重新编译一下插件,最终你会得到一个插件的dll
3、新建一个蓝图类。接收UDP信息就选择MyUDPReceiver,发送UDP就选择MyUDPSender,这两个类都继承自Actor
4、将新建的蓝图类拖拽一个实例到关卡蓝图中
5、编辑蓝图。可以使用网络调试助手调试通信是否成功。
关于ListenPort,RemotePort和RemoteIP,如果在工程打包以后想改变,可以手动添加配置文件,在配置文件中修改
以下是插件源代码
MyUdpPlugin.uplugin
{ "FileVersion": 3, "Version": 1, "VersionName": "1.0", "FriendlyName": "MyUdpPlugin", "Description": "", "Category": "Other", "CreatedBy": "", "CreatedByURL": "", "DocsURL": "", "MarketplaceURL": "", "SupportURL": "", "CanContainContent": true, "IsBetaVersion": false, "Installed": false, "Modules": [ { "Name": "MyUdpPlugin", "Type": "Runtime", "LoadingPhase": "PreLoadingScreen" } ] }
MyUdpPlugin.Build.cs
// Some copyright should be here... using UnrealBuildTool; public class MyUdpPlugin : ModuleRules { public MyUdpPlugin(TargetInfo Target) { PublicIncludePaths.AddRange( new string[] { "MyUdpPlugin/Public" // ... add public include paths required here ... } ); PrivateIncludePaths.AddRange( new string[] { "MyUdpPlugin/Private", // ... add other private include paths required here ... } ); PublicDependencyModuleNames.AddRange( new string[] { "Core", // ... add other public dependencies that you statically link with here ... } ); PrivateDependencyModuleNames.AddRange( new string[] { "CoreUObject", "Engine", "Slate", "SlateCore", "Sockets", "Networking" // ... add private dependencies that you statically link with here ... } ); DynamicallyLoadedModuleNames.AddRange( new string[] { // ... add any modules that your module loads dynamically here ... } ); } }
MyUdpPlugin.h
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. #pragma once #include "ModuleManager.h" class FMyUdpPluginModule : public IModuleInterface { public: /** IModuleInterface implementation */ virtual void StartupModule() override; virtual void ShutdownModule() override; };
MyUdpPlugin.cpp
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. #include "MyUdpPlugin.h" #define LOCTEXT_NAMESPACE "FMyUdpPluginModule" void FMyUdpPluginModule::StartupModule() { // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module } void FMyUdpPluginModule::ShutdownModule() { // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, // we call this function before unloading the module. } #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FMyUdpPluginModule, MyUdpPlugin)
MyUDPReceiver.h
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "Networking.h" //#include "AnyCustomData.h"//如果发送、接收方都是UE4,可以使用FArchive对传输数据进行序列化与反序列化 #include "GameFramework/Actor.h" #include "Engine.h" #include "MyUDPReceiver.generated.h" UCLASS() class AMyUDPReceiver : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties AMyUDPReceiver(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: // Called every frame virtual void Tick(float DeltaTime) override; /** Data has been received!! */ UFUNCTION(BlueprintImplementableEvent, Category = "MyUDPSender") void DataReceived(const TArray<uint8>& rawData, const FString& tryToString); FSocket* ListenSocket; FUdpSocketReceiver* UDPReceiver = nullptr; void Recv(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt); /* * Trying read ListenPort parameter from GameIni config, if ListenPort!=0, * the parameter ListenPort will be overridden. * You can config them in YourProject\Config\DefaultGame.ini or * YourPackagedProject\Saved\Config\WindowsNoEditor\Game.ini. * [MyUDP] * ListenPort=2016 */ UFUNCTION(BlueprintCallable, Category = "MyUDPSender") bool StartUDPReceiver(const FString& YourChosenSocketName, int32 ListenPort = 2017); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyUDPSender") bool ShowOnScreenDebugMessages; //ScreenMsg FORCEINLINE void ScreenMsg(const FString& Msg) { if (!ShowOnScreenDebugMessages) return; GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, *Msg); } FORCEINLINE void ScreenMsg(const FString& Msg, const int32 Value) { if (!ShowOnScreenDebugMessages) return; GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %d"), *Msg, Value)); } FORCEINLINE void ScreenMsg(const FString& Msg, const int32 Value, const FString& Msg2) { if (!ShowOnScreenDebugMessages) return; GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %d %s"), *Msg, Value, *Msg2)); } /** Called whenever this actor is being removed from a level */ virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; };
MyUDPReceiver.cpp
#include "MyUdpPlugin.h" #include "MyUDPReceiver.h" #include <string> // Sets default values AMyUDPReceiver::AMyUDPReceiver() { // 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; ShowOnScreenDebugMessages = true; } // Called when the game starts or when spawned void AMyUDPReceiver::BeginPlay() { Super::BeginPlay(); } // Called every frame void AMyUDPReceiver::Tick(float DeltaTime) { Super::Tick(DeltaTime); } void AMyUDPReceiver::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); delete UDPReceiver; UDPReceiver = nullptr; //Clear all sockets! //makes sure repeat plays in Editor don't hold on to old sockets! if (ListenSocket) { ListenSocket->Close(); ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ListenSocket); } } //Start UDP Receiver bool AMyUDPReceiver::StartUDPReceiver(const FString& YourChosenSocketName, int32 ListenPort) { int32 listenPortFromGameIni = 0; GConfig->GetInt(TEXT("MyUDP"), TEXT("ListenPort"), listenPortFromGameIni, GGameIni); //ScreenMsg(GGameIni); ScreenMsg("ListenPort From Game.ini: [MyUDP] ListenPort=", listenPortFromGameIni); if (listenPortFromGameIni!=0) { ListenPort = listenPortFromGameIni; } //Create Socket FIPv4Endpoint Endpoint(FIPv4Address::Any, ListenPort); //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, &AMyUDPReceiver::Recv); UDPReceiver->Start(); ScreenMsg("UDP Receiver Initialized Successfully!!!"); return true; } void AMyUDPReceiver::Recv(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt) { //FAnyCustomData Data; //*ArrayReaderPtr << Data; //Now de-serializing! See AnyCustomData.h //BPEvent_DataReceived(Data); //BP Event int32 dataByteNum = ArrayReaderPtr->Num(); TArray<uint8> ReceivedData; for (int i = 0; i < dataByteNum; i++) { uint8 tmp; *ArrayReaderPtr << tmp; ReceivedData.Add(tmp); } ReceivedData.Add('\0'); FString tryToString(reinterpret_cast<const char*>(ReceivedData.GetData())); ReceivedData.RemoveSingle('\0'); ScreenMsg("Received from " + EndPt.ToString() + ", Received bytes = ", dataByteNum, ", Received String =" + tryToString); DataReceived(ReceivedData, tryToString); }
MyUDPSender.h
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "Networking.h" #include "GameFramework/Actor.h" #include "MyUDPSender.generated.h" UCLASS() class AMyUDPSender : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties AMyUDPSender(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: // Called every frame virtual void Tick(float DeltaTime) override; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyUDPSender") bool ShowOnScreenDebugMessages; TSharedPtr<FInternetAddr> RemoteAddr; FSocket* SenderSocket; UFUNCTION(BlueprintCallable, Category = "MyUDPSender") bool SendString(FString ToSend); /* * Trying read parameters from GameIni config, if RemoteIP!="" or RemotePort!=0, * the two parameters(RemoteIP and RemotePort) will be overridden. * You can config them in YourProject\Config\DefaultGame.ini or * YourPackagedProject\Saved\Config\WindowsNoEditor\Game.ini. * [MyUDP] * RemoteIP=192.168.1.117 * RemoterPort=2016 */ UFUNCTION(BlueprintCallable, Category = "MyUDPSender") bool StartUDPSender(const FString& YourChosenSocketName, FString RemoteIP="127.0.0.1",int32 RemotePort=2017); //ScreenMsg FORCEINLINE void ScreenMsg(const FString& Msg) { if (!ShowOnScreenDebugMessages) return; GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, *Msg); } FORCEINLINE void ScreenMsg(const FString& Msg, const int32 Value) { if (!ShowOnScreenDebugMessages) return; GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %d"), *Msg, Value)); } FORCEINLINE void ScreenMsg(const FString& Msg, const int32 Value, const FString& Msg2) { if (!ShowOnScreenDebugMessages) return; GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %d %s"), *Msg, Value, *Msg2)); } /** Called whenever this actor is being removed from a level */ virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; };
MyUDPSender.cpp
#include "MyUdpPlugin.h" //#include "AnyCustomData.h"//如果发送、接收方都是UE4,可以使用FArchive对传输数据进行序列化与反序列化 #include "MyUDPSender.h" #include <string> #include "StringConv.h" // Sets default values AMyUDPSender::AMyUDPSender() { // 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; ShowOnScreenDebugMessages = true; } // Called when the game starts or when spawned void AMyUDPSender::BeginPlay() { Super::BeginPlay(); } // Called every frame void AMyUDPSender::Tick(float DeltaTime) { Super::Tick(DeltaTime); } void AMyUDPSender::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); if (SenderSocket) { SenderSocket->Close(); ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(SenderSocket); } } bool AMyUDPSender::StartUDPSender( const FString& YourChosenSocketName, FString RemoteIP /*= "127.0.0.1"*/, int32 RemotePort /*= 2017*/ ) { FString remoteIPFromGameIni = TEXT(""); int32 remotePortFromGameIni = 0; GConfig->GetString(TEXT("MyUDP"), TEXT("RemoteIP"), remoteIPFromGameIni, GGameIni); GConfig->GetInt(TEXT("MyUDP"), TEXT("RemotePort"), remotePortFromGameIni, GGameIni); //ScreenMsg(GGameIni); ScreenMsg("RemoteAddress From Game.ini: [MyUDP] RemoteIP="+ remoteIPFromGameIni +", RemotePort=", remotePortFromGameIni); if (!remoteIPFromGameIni.IsEmpty()) { RemoteIP = remoteIPFromGameIni; } if (remotePortFromGameIni!=0) { RemotePort = remotePortFromGameIni; } //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() ; //check(SenderSocket->GetSocketType() == SOCKTYPE_Datagram); //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 AMyUDPSender::SendString(FString ToSend) { if (!SenderSocket) { ScreenMsg("No sender socket"); return false; } int32 BytesSent = 0; //FAnyCustomData NewData; //NewData.Scale = FMath::FRandRange(0, 1000); //NewData.Count = FMath::RandRange(0, 100); //NewData.Color = FLinearColor(FMath::FRandRange(0, 1), FMath::FRandRange(0, 1), FMath::FRandRange(0, 1), 1); //FArrayWriter Writer; //Writer << NewData; //Serializing our custom data, thank you UE4! //SenderSocket->SendTo(Writer.GetData(), Writer.Num(), BytesSent, *RemoteAddr); TCHAR* pSendData = ToSend.GetCharArray().GetData(); int32 nDataLen = FCString::Strlen(pSendData); uint8* dst = (uint8*)TCHAR_TO_UTF8(pSendData); SenderSocket->SendTo(dst, nDataLen, 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 Success! Bytes Sent = ", BytesSent, ", ToSend String =" + ToSend+", To "+ RemoteAddr->ToString(true)); return true; }
AnyCustomData.h
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "AnyCustomData.generated.h" USTRUCT(BlueprintType) struct FAnyCustomData { GENERATED_USTRUCT_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Joy Color") FString Name = "Victory!"; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Joy Color") int32 Count = 1; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Joy Color") float Scale = 1.f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Joy Color") FLinearColor Color = FLinearColor::Red; FAnyCustomData() {}; ~FAnyCustomData() {}; }; FORCEINLINE FArchive& operator<<(FArchive &Ar, FAnyCustomData& TheStruct) { Ar << TheStruct.Name; Ar << TheStruct.Count; Ar << TheStruct.Scale; Ar << TheStruct.Color; return Ar; }