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 YourProjectConfigDefaultGame.ini or * YourPackagedProjectSavedConfigWindowsNoEditorGame.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('