A comprehensive guide for implementing multiplayer games using ProudNet networking library and Unreal Engine 5. Covers chat systems, character networking, and advanced multiplayer features.
Prepare and configure Unreal Engine 5 project for ProudNet integration
Create a basic local chat system for testing and foundation
Implement network chat functionality using ProudNet
Build a mirage character with enhanced gameplay features
Integrate mirage character with ProudNet networking for multiplayer gameplay
Project finalization, build, and comprehensive testing procedures
This section covers the initial setup and configuration needed to create an Unreal Engine 5 project with ProudNet enabled.
Visual Studio Project Creation
Adding Project Files
Project Structure
C:\Program Files (x86)\Nettention\ProudNet\include
C:\Program Files (x86)\Nettention\ProudNet\lib\$(Platform)\v140\$(Configuration)
ProudNetServer.lib ProudNetClient.lib
xcopy /Y "C:\Program Files (x86)\Nettention\ProudNet\lib\$(Platform)\v140\$(Configuration)\libcrypto-3-x64.dll" "$(OutDir)" xcopy /Y "C:\Program Files (x86)\Nettention\ProudNet\lib\$(Platform)\v140\$(Configuration)\libssl-3-x64.dll" "$(OutDir)"
+ #pragma once + + namespace ProudSetting + { + namespace CHAT + { + extern const ::Proud::Guid version; + extern const int server_port; + } + }
+ #include <ProudNetClient.h> + #include "setting.h" + + namespace ProudSetting + { + namespace CHAT + { + const ::Proud::PNGUID guid = { 0x3ae33249, 0xecc6, 0x4980, { 0xbc, 0x5d, 0x7b, 0xa, 0x99, 0x9c, 0x7, 0x39 } }; + const ::Proud::Guid version = ::Proud::Guid(guid); + const int server_port = 33337; + } + }
+ #include <iostream> + #include <format> + #include <memory> + + #include <ProudNetServer.h> + #include "setting.h" + + std::shared_ptr<Proud::CNetServer> net_server; + + int main() + { + net_server = std::shared_ptr<Proud::CNetServer>(Proud::CNetServer::Create()); + + net_server->OnClientJoin = [](Proud::CNetClientInfo* clientInfo) + { + std::cout << "Client[" << (int)clientInfo->m_HostID << "] connected.\n"; + }; + net_server->OnClientLeave = [](Proud::CNetClientInfo* clientInfo, Proud::ErrorInfo* error, const Proud::ByteArray byte_arr) + { + std::cout << "Client[" << (int)clientInfo->m_HostID << "] disconnected.\n"; + }; + + Proud::CStartServerParameter start_param; + start_param.m_protocolVersion = ProudSetting::CHAT::version; + start_param.m_tcpPorts.Add(ProudSetting::CHAT::server_port); + + try + { + net_server->Start(start_param); + } + catch (Proud::Exception& error) + { + std::cout << "Server start failed: " << error.what() << endl; + return 0; + } + + std::cout << ("Server started. Enterable commands:\n"); + std::cout << ("-q : Quit.\n"); + std::string input; + while (true) + { + std::cin >> input; + if (input[0] == '-') + { + if (input == "-q") + break; + } + } + + std::cout << "Stopping server...\n"; + net_server->Stop(); + net_server = nullptr; + std::cout << "Server stopped.\n"; + return 0; + }
Complete Code
#pragma once namespace ProudSetting { namespace CHAT { extern const ::Proud::Guid version; extern const int server_port; } }
#include <ProudNetClient.h> #include "setting.h" namespace ProudSetting { namespace CHAT { const ::Proud::PNGUID guid = { 0x3ae33249, 0xecc6, 0x4980, { 0xbc, 0x5d, 0x7b, 0xa, 0x99, 0x9c, 0x7, 0x39 } }; const ::Proud::Guid version = ::Proud::Guid(guid); const int server_port = 33337; } }
#include <iostream> #include <format> #include <memory> #include <ProudNetServer.h> #include "setting.h" std::shared_ptr<Proud::CNetServer> net_server; int main() { net_server = std::shared_ptr<Proud::CNetServer>(Proud::CNetServer::Create()); net_server->OnClientJoin = [](Proud::CNetClientInfo* clientInfo) { std::cout << "Client[" << (int)clientInfo->m_HostID << "] connected.\n"; }; net_server->OnClientLeave = [](Proud::CNetClientInfo* clientInfo, Proud::ErrorInfo* error, const Proud::ByteArray byte_arr) { std::cout << "Client[" << (int)clientInfo->m_HostID << "] disconnected.\n"; }; Proud::CStartServerParameter start_param; start_param.m_protocolVersion = ProudSetting::CHAT::version; start_param.m_tcpPorts.Add(ProudSetting::CHAT::server_port); try { net_server->Start(start_param); } catch (Proud::Exception& error) { std::cout << "Server start failed: " << error.what() << endl; return 0; } std::cout << ("Server started. Enterable commands:\n"); std::cout << ("-q : Quit.\n"); std::string input; while (true) { std::cin >> input; if (input[0] == '-') { if (input == "-q") break; } } std::cout << "Stopping server...\n"; net_server->Stop(); net_server = nullptr; std::cout << "Server stopped.\n"; return 0; }
Server Running Screen
Unreal Project Creation
C++ Class Creation Menu
GameInstanceSubsystem Selection
Enter Class Name
Change Startup Project
Shortcut: Epic Games Launcher → My Projects → Right-click project → Show in Folder
Create a new Plugins folder if it doesn't exist.
ProudNet UE5 Lib Linker Plugin.zip
Plugin Folder Structure
Build.cs File Location
// Fill out your copyright notice in the Description page of Project Settings. using UnrealBuildTool; public class PdnUE5ExampleClient : ModuleRules { public PdnUE5ExampleClient(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); + PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "ProudNet" }); PrivateDependencyModuleNames.AddRange(new string[] { }); // Uncomment if you are using Slate UI // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); // Uncomment if you are using online features // PrivateDependencyModuleNames.Add("OnlineSubsystem"); // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true } }
// Fill out your copyright notice in the Description page of Project Settings. using UnrealBuildTool; public class PdnUE5ExampleClient : ModuleRules { public PdnUE5ExampleClient(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "ProudNet" }); PrivateDependencyModuleNames.AddRange(new string[] { }); // Uncomment if you are using Slate UI // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); // Uncomment if you are using online features // PrivateDependencyModuleNames.Add("OnlineSubsystem"); // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true } }
GissChatNet Files
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Subsystems/GameInstanceSubsystem.h" #include "GissChatNet.generated.h" /** * */ UCLASS() class PDNUE5EXAMPLECLIENT_API UGissChatNet : public UGameInstanceSubsystem { GENERATED_BODY() + private: + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; };
// Fill out your copyright notice in the Description page of Project Settings. #include "GissChatNet.h" + #include <format> + #include <functional> + + #include <ProudNetClient.h> + #include "C:/proudnet_ue5_example/PdnUE5ExampleServer/chat_server/setting.h" + #include "C:/proudnet_ue5_example/PdnUE5ExampleServer/chat_server/setting.cpp" + + static void LogPrint(const std::string& str) + { + UE_LOG(LogTemp, Log, TEXT("%s"), UTF8_TO_TCHAR(str.c_str())); + } + + static Proud::CriticalSection global_critical_section; + static std::shared_ptr<Proud::CNetClient> net_client; + static FDelegateHandle update_handle; + + void UGissChatNet::Initialize(FSubsystemCollectionBase& Collection) + { + net_client = std::shared_ptr<Proud::CNetClient>(Proud::CNetClient::Create()); + + bool connected = false; + + net_client->OnJoinServerComplete = [&](Proud::ErrorInfo* info, const Proud::ByteArray& replyFromServer) + { + Proud::CriticalSectionLock lock(global_critical_section, true); + + if (info->m_errorType == Proud::ErrorType::Ok) + { + auto log = std::format("Succeed to connect server. Allocated hostID={}\n", (int)net_client->GetLocalHostID()); + LogPrint(log); + + connected = true; + } + else + { + auto log = "Failed to connect to server.\n"; + LogPrint(log); + } + }; + + net_client->OnLeaveServer = [&](Proud::ErrorInfo* errorInfo) + { + Proud::CriticalSectionLock lock(global_critical_section, true); + + auto log = std::format("OnLeaveServer. {} \n", StringT2A(errorInfo->m_comment).GetString()); + LogPrint(log); + + connected = false; + if (update_handle.IsValid()) + { + FCoreDelegates::OnEndFrame.Remove(update_handle); + update_handle.Reset(); + } + }; + + Proud::CNetConnectionParam connection_param; + connection_param.m_protocolVersion = ProudSetting::CHAT::version; + connection_param.m_serverIP = _PNT("localhost"); + connection_param.m_serverPort = ProudSetting::CHAT::server_port; + connection_param.m_closeNoPingPongTcpConnections = false; + + net_client->Connect(connection_param); + + update_handle = FCoreDelegates::OnEndFrame.AddStatic([]() + { + if (net_client) + net_client->FrameMove(); + } + ); + } + + void UGissChatNet::Deinitialize() + { + net_client->Disconnect(); + net_client = nullptr; + }
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Subsystems/GameInstanceSubsystem.h" #include "GissChatNet.generated.h" /** * */ UCLASS() class PDNUE5EXAMPLECLIENT_API UGissChatNet : public UGameInstanceSubsystem { GENERATED_BODY() private: virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; };
// Fill out your copyright notice in the Description page of Project Settings. #include "GissChatNet.h" #include <format> #include <functional> #include <ProudNetClient.h> #include "C:/proudnet_ue5_example/PdnUE5ExampleServer/chat_server/setting.h" #include "C:/proudnet_ue5_example/PdnUE5ExampleServer/chat_server/setting.cpp" static void LogPrint(const std::string& str) { UE_LOG(LogTemp, Log, TEXT("%s"), UTF8_TO_TCHAR(str.c_str())); } static Proud::CriticalSection global_critical_section; static std::shared_ptr<Proud::CNetClient> net_client; static FDelegateHandle update_handle; void UGissChatNet::Initialize(FSubsystemCollectionBase& Collection) { net_client = std::shared_ptr<Proud::CNetClient>(Proud::CNetClient::Create()); bool connected = false; net_client->OnJoinServerComplete = [&](Proud::ErrorInfo* info, const Proud::ByteArray& replyFromServer) { Proud::CriticalSectionLock lock(global_critical_section, true); if (info->m_errorType == Proud::ErrorType::Ok) { auto log = std::format("Succeed to connect server. Allocated hostID={}\n", (int)net_client->GetLocalHostID()); LogPrint(log); connected = true; } else { auto log = "Failed to connect to server.\n"; LogPrint(log); } }; net_client->OnLeaveServer = [&](Proud::ErrorInfo* errorInfo) { Proud::CriticalSectionLock lock(global_critical_section, true); auto log = std::format("OnLeaveServer. {} \n", StringT2A(errorInfo->m_comment).GetString()); LogPrint(log); connected = false; if (update_handle.IsValid()) { FCoreDelegates::OnEndFrame.Remove(update_handle); update_handle.Reset(); } }; Proud::CNetConnectionParam connection_param; connection_param.m_protocolVersion = ProudSetting::CHAT::version; connection_param.m_serverIP = _PNT("localhost"); connection_param.m_serverPort = ProudSetting::CHAT::server_port; connection_param.m_closeNoPingPongTcpConnections = false; net_client->Connect(connection_param); update_handle = FCoreDelegates::OnEndFrame.AddStatic([]() { if (net_client) net_client->FrameMove(); } ); } void UGissChatNet::Deinitialize() { net_client->Disconnect(); net_client = nullptr; }
chat_server Execution
Unreal Editor Execution
Server Connection Success
Client Connection Success
This section covers creating a local chat window and implementing basic chat functionality in Unreal Engine 5.
Widget Blueprint Creation
Widget Name Change
>> Canvas Panel >> Canvas Panel - Anchor: Bottom Left - Position: [x: 100, y: -200] - Size: [x: 640, y: 360] - Alignment: [x: 0, y: 1] >> Scroll Box - Anchor: Top - Position: [x: 0, y: 0] - Size: [x: 640, y: 300] - Alignment: [x: 0.5, y: 0] >> Canvas Panel - Anchor: Bottom - Position: [x: 0, y: 0] - Size: [x: 640, y: 48] - Alignment: [x: 0.5, y: 1] >> Text Box - Anchor: Left - Position: [x: 0, y: 0] - Size: [x: 520, y: 48] - Alignment: [x: 0, y: 0.5] >> Button - Anchor: Right - Position: [x: 0, y: 0] - Size: [x: 100, y: 48] - Alignment: [x: 1, y: 0.5] >> Text - Text: "Send"
The appearance upon completion of configuration is as follows.
Chat Widget Configuration
Opening Level Blueprint Editor
Adding Create Widget Node
Class Setting
Adding Add to Viewport Node
Node Connection
Screen with Chat Window Added
>> Canvas Panel >> Border - Anchor: Top Left - Position: [x: 0, y: 0] - Size: [x: 640, y: 40] - Alignment: [x: 0, y: 0] - Brush Color: [R: 1, G: 1, B: 1, A: 0.5] >> Text > Name: ChatText > Check Variable - Horizontal Alignment: Left - Vertical Alignment: Center - Font-Size: 20
The appearance upon completion of configuration is as follows.
BP_ChatBlock Configuration
Blueprint Graph Mode
Function Creation
Function Parameter Setting
Adding SetText Node
Node Connection Complete
-> Canvas Panel -> Canvas Panel -> Scroll Box > Name: ChatBlockArea > Check Variable -> Canvas Panel -> Button > Name: SendButton > Check Variable -> Text Box > Name: ChatTextBox > Check Variable
The appearance after modification is as follows.
BP_ChatWidget Modification Complete
Adding PrintChat Function
PrintChat Function Configuration
Adding Button Click Event
Chat Send Event Complete
Additional Feature Implementation 1
Additional Feature Implementation 2
Chat Function Verification
This section covers implementing network chat functionality using ProudNet.
C:\"Program Files (x86)"\Nettention\ProudNet\util\PIDL.exe "%(FullPath)" -cpp
%(Filename).PIDL Compiling...
%(RootDir)%(Directory)\%(Filename)_common.cpp %(RootDir)%(Directory)\%(Filename)_common.h %(RootDir)%(Directory)\%(Filename)_proxy.cpp %(RootDir)%(Directory)\%(Filename)_proxy.h %(RootDir)%(Directory)\%(Filename)_stub.cpp %(RootDir)%(Directory)\%(Filename)_stub.h
[access=public] global CHAT_C2S 3000 { Chat([in] Proud::String message); }
[access=public] global CHAT_S2C 4000 { SystemChat([in] Proud::String message); BroadcastChat([in] int sender_id, [in] Proud::String message); }
PIDL Build Results
#include <iostream> #include <format> #include <memory> #include <ProudNetServer.h> #include "setting.h" + #include "../chat_pidl/S2C_common.h" + #include "../chat_pidl/S2C_common.cpp" + #include "../chat_pidl/S2C_proxy.h" + #include "../chat_pidl/S2C_proxy.cpp" + + #include "../chat_pidl/C2S_common.h" + #include "../chat_pidl/C2S_common.cpp" + #include "../chat_pidl/C2S_stub.h" + #include "../chat_pidl/C2S_stub.cpp" + + + Proud::HostID group_host_id = Proud::HostID_None; + CHAT_S2C::Proxy s2c_proxy; + + struct CHAT_C2S_Stub : public CHAT_C2S::Stub + { + public: + DECRMI_CHAT_C2S_Chat; + }; + + DEFRMI_CHAT_C2S_Chat(CHAT_C2S_Stub) + { + Proud::RmiContext rmi_context; + rmi_context.m_enableLoopback = false; + s2c_proxy.BroadcastChat(group_host_id, rmi_context, (int)remote, message); + std::cout << std::format("Player[{}] say {}\n", (int)remote, StringT2A(message).GetString()); + return true; + } + + CHAT_C2S_Stub c2s_stub; std::shared_ptr<Proud::CNetServer> net_server; int main() { net_server = std::shared_ptr<Proud::CNetServer>(Proud::CNetServer::Create()); net_server->OnClientJoin = [](Proud::CNetClientInfo* clientInfo) { std::cout << "Client[" << (int)clientInfo->m_HostID << "] connected.\n"; + + Proud::HostID list[100]; + int listCount = net_server->GetClientHostIDs(list, 100); + group_host_id = net_server->CreateP2PGroup(list, listCount, Proud::ByteArray()); + + auto message = std::format("Player[{}] has joined.", (int)clientInfo->m_HostID); + Proud::RmiContext rmi_context; + s2c_proxy.SystemChat(group_host_id, rmi_context, message); }; net_server->OnClientLeave = [](Proud::CNetClientInfo* clientInfo, Proud::ErrorInfo* error, const Proud::ByteArray byte_arr) { std::cout << "Client[" << (int)clientInfo->m_HostID << "] disconnected.\n"; + + auto message = std::format("Player[{}] has left the game.", (int)clientInfo->m_HostID); + Proud::RmiContext rmi_context; + s2c_proxy.SystemChat(group_host_id, rmi_context, message); }; + net_server->AttachProxy(&s2c_proxy); + net_server->AttachStub(&c2s_stub); + Proud::CStartServerParameter start_param; start_param.m_protocolVersion = ProudSetting::CHAT::version; start_param.m_tcpPorts.Add(ProudSetting::CHAT::server_port); try { net_server->Start(start_param); } catch (Proud::Exception& error) { std::cout << "Server start failed: " << error.what() << endl; return 0; } std::cout << ("Server started. Enterable commands:\n"); std::cout << ("-q : Quit.\n"); std::string input; while (true) { std::cin >> input; if (input[0] == '-') { if (input == "-q") break; } + else + { + Proud::RmiContext rmi_context; + s2c_proxy.SystemChat(group_host_id, rmi_context, input); + } } std::cout << "Stopping server...\n"; net_server->Stop(); net_server = nullptr; std::cout << "Server stopped.\n"; return 0; }
#include <iostream> #include <format> #include <memory> #include <ProudNetServer.h> #include "setting.h" #include "../chat_pidl/S2C_common.h" #include "../chat_pidl/S2C_common.cpp" #include "../chat_pidl/S2C_proxy.h" #include "../chat_pidl/S2C_proxy.cpp" #include "../chat_pidl/C2S_common.h" #include "../chat_pidl/C2S_common.cpp" #include "../chat_pidl/C2S_stub.h" #include "../chat_pidl/C2S_stub.cpp" Proud::HostID group_host_id = Proud::HostID_None; CHAT_S2C::Proxy s2c_proxy; struct CHAT_C2S_Stub : public CHAT_C2S::Stub { public: DECRMI_CHAT_C2S_Chat; }; DEFRMI_CHAT_C2S_Chat(CHAT_C2S_Stub) { Proud::RmiContext rmi_context; rmi_context.m_enableLoopback = false; s2c_proxy.BroadcastChat(group_host_id, rmi_context, (int)remote, message); std::cout << std::format("Player[{}] say {}\n", (int)remote, StringT2A(message).GetString()); return true; } CHAT_C2S_Stub c2s_stub; std::shared_ptr<Proud::CNetServer> net_server; int main() { net_server = std::shared_ptr<Proud::CNetServer>(Proud::CNetServer::Create()); net_server->OnClientJoin = [](Proud::CNetClientInfo* clientInfo) { std::cout << "Client[" << (int)clientInfo->m_HostID << "] connected.\n"; Proud::HostID list[100]; int listCount = net_server->GetClientHostIDs(list, 100); group_host_id = net_server->CreateP2PGroup(list, listCount, Proud::ByteArray()); auto message = std::format("Player[{}] has joined.", (int)clientInfo->m_HostID); Proud::RmiContext rmi_context; s2c_proxy.SystemChat(group_host_id, rmi_context, message); }; net_server->OnClientLeave = [](Proud::CNetClientInfo* clientInfo, Proud::ErrorInfo* error, const Proud::ByteArray byte_arr) { std::cout << "Client[" << (int)clientInfo->m_HostID << "] disconnected.\n"; auto message = std::format("Player[{}] has left the game.", (int)clientInfo->m_HostID); Proud::RmiContext rmi_context; s2c_proxy.SystemChat(group_host_id, rmi_context, message); }; net_server->AttachProxy(&s2c_proxy); net_server->AttachStub(&c2s_stub); Proud::CStartServerParameter start_param; start_param.m_protocolVersion = ProudSetting::CHAT::version; start_param.m_tcpPorts.Add(ProudSetting::CHAT::server_port); try { net_server->Start(start_param); } catch (Proud::Exception& error) { std::cout << "Server start failed: " << error.what() << endl; return 0; } std::cout << ("Server started. Enterable commands:\n"); std::cout << ("-q : Quit.\n"); std::string input; while (true) { std::cin >> input; if (input[0] == '-') { if (input == "-q") break; } else { Proud::RmiContext rmi_context; s2c_proxy.SystemChat(group_host_id, rmi_context, input); } } std::cout << "Stopping server...\n"; net_server->Stop(); net_server = nullptr; std::cout << "Server stopped.\n"; return 0; }
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Blueprint/UserWidget.h" #include "BaseChatWidget.generated.h" /** * */ UCLASS() class PDNUE5EXAMPLECLIENT_API UBaseChatWidget : public UUserWidget { GENERATED_BODY() + public: + UFUNCTION(BlueprintImplementableEvent) + void PrintChat(const FText& message); };
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Blueprint/UserWidget.h" #include "BaseChatWidget.generated.h" /** * */ UCLASS() class PDNUE5EXAMPLECLIENT_API UBaseChatWidget : public UUserWidget { GENERATED_BODY() public: UFUNCTION(BlueprintImplementableEvent) void PrintChat(const FText& message); };
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Subsystems/GameInstanceSubsystem.h" #include "GissChatNet.generated.h" /** * */ UCLASS() class PDNUE5EXAMPLECLIENT_API UGissChatNet : public UGameInstanceSubsystem { GENERATED_BODY() + public: + UFUNCTION(BlueprintCallable) + void SendChat(const FText& message); private: virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; };
+ #include "C:/proudnet_ue5_example/PdnUE5ExampleServer/chat_pidl/C2S_common.h" + #include "C:/proudnet_ue5_example/PdnUE5ExampleServer/chat_pidl/C2S_common.cpp" + #include "C:/proudnet_ue5_example/PdnUE5ExampleServer/chat_pidl/C2S_proxy.h" + #include "C:/proudnet_ue5_example/PdnUE5ExampleServer/chat_pidl/C2S_proxy.cpp" + + #include "C:/proudnet_ue5_example/PdnUE5ExampleServer/chat_pidl/S2C_common.h" + #include "C:/proudnet_ue5_example/PdnUE5ExampleServer/chat_pidl/S2C_common.cpp" + #include "C:/proudnet_ue5_example/PdnUE5ExampleServer/chat_pidl/S2C_stub.h" + #include "C:/proudnet_ue5_example/PdnUE5ExampleServer/chat_pidl/S2C_stub.cpp" + + static Proud::HostID pop_group_host_id = Proud::HostID_None; + static CHAT_C2S::Proxy c2s_proxy; + + void UGissChatNet::SendChat(const FText& message) + { + Proud::String message_pstr(*message.ToString()); + Proud::RmiContext context; + context.m_enableLoopback = true; + c2s_proxy.Chat(Proud::HostID_Server, context, message_pstr); + }
PrintChat Event Conversion
Parent Class Change
Adding GissChatNet Node
BP_ChatWidget Final Appearance
Chat Function Test 1
Chat Function Test 2
This section covers building a Mirage character with enhanced gameplay features.
BP_MirageCharacter Configuration
If placed as shown below, it is normal based on the current point in time.
Mirage Character Placement Verification
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "BaseMirageCharacter.generated.h" UCLASS() class PDNUE5EXAMPLECLIENT_API ABaseMirageCharacter : public ACharacter { GENERATED_BODY() public: // Sets default values for this character's properties ABaseMirageCharacter(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: // Called every frame virtual void Tick(float DeltaTime) override; // Called to bind functionality to input virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; + UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) + void UpdateAnimationParameter(bool aiming, bool closeToWall, bool moving, bool running, float jumpVelocity, FVector3f mouseSwayLocation, FVector3f moveSwayLocation); };
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "BaseMirageCharacter.generated.h" UCLASS() class PDNUE5EXAMPLECLIENT_API ABaseMirageCharacter : public ACharacter { GENERATED_BODY() public: // Sets default values for this character's properties ABaseMirageCharacter(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: // Called every frame virtual void Tick(float DeltaTime) override; // Called to bind functionality to input virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) void UpdateAnimationParameter(bool aiming, bool closeToWall, bool moving, bool running, float jumpVelocity, FVector3f mouseSwayLocation, FVector3f moveSwayLocation); };
BlueprintUpdateAnimation Event
Update Animation Parameter Node
Variable Connection Complete
Add Variables
Parameter Storage
Event Tick Disconnection
Animation Variable Connection
You can observe the mirage character mimicking motions during actions like running, jumping, and aiming.
Animation Duplication Verification
Invert.fbx (File download required)
Skeleton Assignment
Animation Deletion
Additive Settings
Apply Additive Node Connection
All Animation Node Modifications
You can confirm that the arm skeleton positions have been corrected to normal ranges.
Animation Correction Results
Layered Blend Per Bone Settings
Animation Loop Activation
A_FP_AssaultRifle_Run_Loop Final Appearance
Complete Graph 1
Complete Graph 2
You can confirm that walking and running animations are being played.
Leg Animation Verification
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "BaseMirageCharacter.generated.h" UCLASS() class PDNUE5EXAMPLECLIENT_API ABaseMirageCharacter : public ACharacter { GENERATED_BODY() public: // Sets default values for this character's properties ABaseMirageCharacter(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: // Called every frame virtual void Tick(float DeltaTime) override; // Called to bind functionality to input virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) void UpdateAnimationParameter(bool aiming, bool closeToWall, bool moving, bool running, float jumpVelocity, FVector3f mouseSwayLocation, FVector3f moveSwayLocation); + UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) + void OnFired(); + + UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) + void OnReloaded(); };
Reload Related Area
On Reloaded Node Addition
Reload Event Connection
Shoot Related Area
Shoot Event Connection
On Reloaded Event Connection
On Fired Event Connection
Action Animation Test
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "BaseMirageCharacter.generated.h" UCLASS() class PDNUE5EXAMPLECLIENT_API ABaseMirageCharacter : public ACharacter { GENERATED_BODY() public: // Sets default values for this character's properties ABaseMirageCharacter(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: // Called every frame virtual void Tick(float DeltaTime) override; // Called to bind functionality to input virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; + UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) + void UpdateTransform(FVector3f position, FQuat4f orientation, FVector3f linearVelocity, FVector3f angularVelocity, float aimPitch); UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) void UpdateAnimationParameter(bool aiming, bool closeToWall, bool moving, bool running, float jumpVelocity, FVector3f mouseSwayLocation, FVector3f moveSwayLocation); UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) void OnFired(); UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) void OnReloaded(); };
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "BaseMirageCharacter.generated.h" UCLASS() class PDNUE5EXAMPLECLIENT_API ABaseMirageCharacter : public ACharacter { GENERATED_BODY() public: // Sets default values for this character's properties ABaseMirageCharacter(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: // Called every frame virtual void Tick(float DeltaTime) override; // Called to bind functionality to input virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) void UpdateTransform(FVector3f position, FQuat4f orientation, FVector3f linearVelocity, FVector3f angularVelocity, float aimPitch); UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) void UpdateAnimationParameter(bool aiming, bool closeToWall, bool moving, bool running, float jumpVelocity, FVector3f mouseSwayLocation, FVector3f moveSwayLocation); UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) void OnFired(); UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) void OnReloaded(); };
AimPitch Variable Addition
Route Node 1
Route Node 2
Route Node Movement
Update Transform Graph Complete
BP_MirageCharacter UpdateTransform Implementation
Position Make Vector Connection
Shoot Related Graph Configuration
ABP_MirageCharacter AimPitch Variable
Mouse Sway Related Section
Transform Bone Node Application
Position Rotation Duplication Test
This section covers integrating mirage character with ProudNet networking for multiplayer gameplay.
C:\Program Files (x86)\Nettention\ProudNet\include
C:\Program Files (x86)\Nettention\ProudNet\lib\$(Platform)\v140\$(Configuration)
ProudNetServer.lib ProudNetClient.lib
xcopy /Y "C:\Program Files (x86)\Nettention\ProudNet\lib\$(Platform)\v140\$(Configuration)\libcrypto-3-x64.dll" "$(OutDir)" xcopy /Y "C:\Program Files (x86)\Nettention\ProudNet\lib\$(Platform)\v140\$(Configuration)\libssl-3-x64.dll" "$(OutDir)"
#pragma once namespace ProudSetting { namespace GAME { extern const ::Proud::Guid version; extern const int server_port; } }
#include <ProudNetClient.h> #include "setting.h" namespace ProudSetting { namespace GAME { const ::Proud::PNGUID guid = { 0x3ae33249, 0xecc6, 0x4980, { 0xbc, 0x5d, 0x7b, 0xa, 0x99, 0x9c, 0x7, 0x39 } }; const ::Proud::Guid version = ::Proud::Guid(guid); const int server_port = 33338; } }
#include <iostream> #include <format> #include <memory> #include <ProudNetServer.h> #include "setting.h" std::shared_ptr<Proud::CNetServer> net_server; int main() { net_server = std::shared_ptr<Proud::CNetServer>(Proud::CNetServer::Create()); net_server->OnClientJoin = [](Proud::CNetClientInfo* clientInfo) { std::cout << "Client[" << (int)clientInfo->m_HostID << "] connected.\n"; }; net_server->OnClientLeave = [](Proud::CNetClientInfo* clientInfo, Proud::ErrorInfo* error, const Proud::ByteArray byte_arr) { std::cout << "Client[" << (int)clientInfo->m_HostID << "] disconnected.\n"; }; Proud::CStartServerParameter start_param; start_param.m_protocolVersion = ProudSetting::GAME::version; start_param.m_tcpPorts.Add(ProudSetting::GAME::server_port); try { net_server->Start(start_param); } catch (Proud::Exception& error) { std::cout << "Server start failed: " << error.what() << endl; return 0; } std::cout << ("Server started. Enterable commands:\n"); std::cout << ("-q : Quit.\n"); std::string input; while (true) { std::cin >> input; if (input[0] == '-') { if (input == "-q") break; } } std::cout << "Stopping server...\n"; net_server->Stop(); net_server = nullptr; std::cout << "Server stopped.\n"; return 0; }
C:\"Program Files (x86)"\Nettention\ProudNet\util\PIDL.exe "%(FullPath)" -cpp
%(Filename).PIDL Compiling...
%(RootDir)%(Directory)\%(Filename)_common.cpp %(RootDir)%(Directory)\%(Filename)_common.h %(RootDir)%(Directory)\%(Filename)_proxy.cpp %(RootDir)%(Directory)\%(Filename)_proxy.h %(RootDir)%(Directory)\%(Filename)_stub.cpp %(RootDir)%(Directory)\%(Filename)_stub.h
[access=public] global GAME_P2P 3000 { Transform([in] Proud::CharacterTransformData transformData); AnimationParams([in] Proud::CharacterAnimationParams animationParams); Action([in] Proud::CharacterAction actionId); }
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Subsystems/GameInstanceSubsystem.h" #include "GissGameNet.generated.h" /** * */ UCLASS() class PDNUE5EXAMPLECLIENT_API UGissGameNet : public UGameInstanceSubsystem { GENERATED_BODY() public: UFUNCTION(BlueprintCallable) void UpdateCharacterTransform(FVector3f position, FQuat4f rotation, FVector3f linearVelocity, FVector3f angularVelocity, float aimPitch); UFUNCTION(BlueprintCallable) void UpdateCharacterAnimationParameter(bool aiming, bool closeToWall, bool moving, bool running, float jumpVelocity, FVector3f mouseSwayLocation, FVector3f moveSwayLocation); UFUNCTION(BlueprintCallable) void SendCharacterFired(); UFUNCTION(BlueprintCallable) void SendCharacterReloaded(); private: virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; };
Update Character Transform Change
Send Character Fired Change
Send Character Reloaded Change
Update Character Animation Parameter Change
This section covers project finalization, build, and comprehensive testing procedures.
Position Direct Connection
MirageCharacter Instance Removal
Build Configuration Change
Built Executable Files
Unreal Project Build
Built Client Files
Final Test Results