zoukankan      html  css  js  c++  java
  • 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 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('');
        FString tryToString(reinterpret_cast<const char*>(ReceivedData.GetData()));
        ReceivedData.RemoveSingle('');
        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 YourProjectConfigDefaultGame.ini or
        * YourPackagedProjectSavedConfigWindowsNoEditorGame.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;
    }
  • 相关阅读:
    Android studio 快捷键记录
    Android开发 判断目标Fragment是否在前台
    Android开发 build.gradle的使用记录
    Android开发 在Application用于初始化的工具类
    Android开发 因为模块化导致findViewById返回为空null
    Android开发 本地广播
    Android开发 报错: xxx does not have a NavController set on xxx
    Android开发 GridLayout网格布局
    Android开发 Fragment启动Activity在使用onActivityResult的一些问题
    Html 创建自定义标签并且被jQuery获取到
  • 原文地址:https://www.cnblogs.com/coolbear/p/7159554.html
Copyright © 2011-2022 走看看