【unity游戏开发——网络】使用Unity+PurrNet+Heathens+Steam,在 Unity 中通过 Steam与你的朋友建立联系

前言

如果你想要制作Steam多人游戏,并且想要开始使用PurrNet,那么你来对地方了!

开始之前你可能需要先去了解:

额外说明: 作为独立开发者,测试Steam连接通常很有挑战性,因为它需要两个Steam账户。有很多不同的方法可以单独测试,但为了本指南的目的,我假设你有一个朋友或队友可以帮助你测试。

开始之前

本指南使用以下环境编写:

  • Unity 6002.2.6f1
  • PurrNet 1.15.0
  • Heathen's Toolkit for Steamworks 2025
  • Steam已安装并运行中

如果你不熟悉Heathen's Toolkit for Steamworks SDK,我强烈建议你了解一下,如果你认真想用Steam的话------它会让你的生活更轻松。如果你不想使用Heathen's,那么我展示的脚本示例可能无法直接使用,但本指南的大部分内容仍然对你有帮助。我们将在本指南的最后安装Heathen's Toolkit,确保每个人都能跟上进度。

步骤1:创建场景

这是一个全新的项目,所以我将从创建一个新场景开始,并将其放在Assets/Scenes文件夹中。我将这个场景命名为"NetworkedSteamScene",并删除文件夹中已有的"SampleScene":

打开场景:

步骤2:创建网络管理器

在层级窗口任意位置右键点击,选择PurrNet → NetworkManager:

步骤3:配置游戏对象

点击新创建的PurrNet游戏对象,可以随意重命名;我更喜欢叫它"NetworkManager":

删除UDP传输组件并添加Steam传输组件:

如果你想了解更多关于Steam传输的信息,请参考Steam传输文档

现在点击Steam传输组件上的"Add SteamWorks.Net to Package Manager"------这会将Steamworks.NET添加到你的项目中。如果出现错误,请确保你的机器上安装了git。

如果一切正常,你的设置应该看起来像这样:

让我们通过点击目标图标将网络规则添加到网络管理器组件,并添加默认规则:

如果你不知道这些规则是什么,只想让Steam正常工作,现在请选择"Unsafe"规则。这样做后,网络管理器会变成这样:

让我们将网络管理器配置成这样:

步骤4:脚本设置

这一步我们将使用Heathen's Toolkit for Steamworks来简化工作。如果你没有这个包,下面的代码对你仍然有用。我会注释哪些是Heathen's的API调用,这样你就知道哪些部分需要使用你正在使用的任何Steamworks API包装器来解决。现在让我们安装Heathen's Toolkit for Steamworks。下面是我的包管理器截图:

首先,我们需要创建一个Steam设置对象。如果你没有Heathen's,可以跳过这部分。以下是Heathen's网站上关于如何做到这一点的复制粘贴:

打开项目设置,选择Player > Steamworks

当你第一次这样做时,它会在你的项目设置文件夹中创建一个SteamMain Steam设置对象。

你可以选择添加演示设置和任意多个游戏测试设置。它们都会被添加到设置文件夹中自己的Steam设置对象中。

接下来,让我们添加一个画布,上面有"主机"和"加入"按钮,以及一个文本字段来显示主机ID(只有主机能看到)和一个客户端可以输入文本的输入框。这提示我安装TextMesh Pro,所以我安装了它:

接下来,让我们创建一个脚本来处理与Steam的连接。创建一个名为"ConnectionManager"的新脚本,并将其放在你的脚本文件夹中。

下面是一种松散的代码结构方式,但应该能让你对如何调整以适应自己的游戏有一些见解和直觉。看起来很长,但我保证实际上只有两个方法------我只是尽量让它尽可能清晰。

csharp 复制代码
using Heathen.SteamworksIntegration;
using PurrNet;
using PurrNet.Steam;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

namespace SteamExample {
    /// <summary>
    /// 这个类的目的是管理主机和客户端与Steam服务器的连接。它必须存在于
    /// 连接到网络化场景的场景中的一个游戏对象上。如果你只有一个场景,那么
    /// 它就存在于那个场景中。
    /// </summary>
    /// <remarks>
    /// 我将StartHost()和StartClient()方法分开,试图让它们尽可能清晰。你可以
    /// 自由地将它们重构到同一个方法中,或者按照你喜欢的任何方式组织它们。
    /// 这绝不是一个万能的解决方案;而是你如何利用PurrNet和
    /// Heathen.SteamworksIntegration一起与你的朋友在Steam上连接的示例。
    /// </remarks>
    public sealed class ConnectionManager : NetworkIdentity {
        // 如果你直接将此复制到项目中,请确保拖放每个元素。
        [SerializeField] private SteamSettings steamSettings;
        [SerializeField] private Button hostButton;
        [SerializeField] private Button clientButton;
        [SerializeField] private TMP_Text hostTextField;
        [SerializeField] private TMP_InputField clientInputField;
        
        private void Awake() {
            InstanceHandler.RegisterInstance(this);
            DontDestroyOnLoad(this);
            InitializeSteam();
        }

        private void OnEnable() {
            hostButton?.onClick.AddListener(HandleHostClicked);
            clientButton?.onClick.AddListener(HandleClientClicked);
        }

        private void OnDisable() {
            hostButton?.onClick.RemoveListener(HandleHostClicked);
            clientButton?.onClick.RemoveListener(HandleClientClicked);
        }

        protected override void OnDestroy() {
            base.OnDestroy();
            InstanceHandler.UnregisterInstance<ConnectionManager>();
        }

        /// <summary>
        /// 这个方法的目的是初始化Steam。
        /// </summary>
        /// <remarks>
        /// 我只是包装了一个方法吗?是的。但如果你正在制作Steam游戏,你可能
        /// 出于某种原因想要在这里做更多的事情!所以再次强调,这只是一个示例,
        /// 请随意重构以满足你的需求。
        /// </remarks>
        public void InitializeSteam() {
            steamSettings.Initialize();
        }
        
        /// <summary>
        /// 这个方法负责调用PurrNet的StartHost()方法。它还获取并配置
        /// SteamTransport。它还利用Heathens的UserData结构来存储主机的Steam账户ID。
        /// </summary>
        /// <remarks>
        /// 根据你的需要,可以自由地将这个方法设为public或private。
        /// 在调用此方法之前,必须初始化Steam。
        /// </remarks>
        public void StartHost() {
            var steamTransport = NetworkManager.main.transport as SteamTransport;
            if (steamTransport == null) {
                Debug.LogError("NetworkManager上缺少SteamTransport", this);
                return;
            }

            // 这是一个Heathens API调用。
            var user = UserData.Get();

            steamTransport.peerToPeer = true;
            steamTransport.dedicatedServer = false;
            steamTransport.address = user.id.ToString();
            
            NetworkManager.main.StartHost();
        }

        /// <summary>
        /// 这个方法负责调用PurrNet的StartClient()方法。它还获取并配置
        /// SteamTransport。它还利用Heathens的UserData结构来存储主机的Steam账户ID。
        /// </summary>
        /// <remarks>
        /// 根据你的需要,可以自由地将这个方法设为public或private。
        /// 在调用此方法之前,必须初始化Steam。
        /// </remarks>
        public void StartClient(string steamCode) {
            var steamTransport = NetworkManager.main.transport as SteamTransport;
            if (steamTransport == null) {
                Debug.LogError("NetworkManager上缺少SteamTransport", this);
                return;
            }

            if (string.IsNullOrEmpty(steamCode)) {
                Debug.LogError("连接地址为空。", this);
                return;
            }

            // 这是一个Heathens API调用。
            var hostAddress = UserData.Get(steamCode);

            if (!hostAddress.IsValid) {
                Debug.LogError($"{hostAddress}是一个无效的连接地址。", this);
                return;
            }
            
            steamTransport.peerToPeer = true;
            steamTransport.dedicatedServer = false;
            steamTransport.address = hostAddress.id.ToString();
            
            NetworkManager.main.StartClient();
        }

        /// <summary>
        /// 这个方法负责调用StartHost()并更改UI视觉效果。
        /// </summary>
        private void HandleHostClicked() {
            if (hostButton == null || hostTextField == null) {
                Debug.LogError("主机按钮或主机文本字段为空。请将它们拖放到ConnectionManager中。", this);
                return;
            }
            
            StartHost();

            if (NetworkManager.main.isOffline) {
                hostTextField.text = "服务器离线";
                hostButton.image.color = Color.red;
                return;
            }
            
            hostTextField.text = UserData.Get().HexId;
            hostButton.image.color = Color.green;
            
            hostButton?.onClick.RemoveListener(HandleHostClicked);
            clientButton?.onClick.RemoveListener(HandleClientClicked);
        }

        /// <summary>
        /// 这个方法负责调用StartClient()并更改UI视觉效果。
        /// </summary>
        private void HandleClientClicked() {
            if (string.IsNullOrEmpty(clientInputField.text)) {
                clientInputField.text = "请输入主机的Steam十六进制ID。";
                clientButton.image.color = Color.red;
                return;
            }
            
            StartClient(clientInputField.text);

            clientInputField.text = $"已连接到 {clientInputField.text}";
            clientButton.image.color = Color.green;
            
            clientButton?.onClick.RemoveListener(HandleHostClicked);
            clientButton?.onClick.RemoveListener(HandleClientClicked);
        }
    }
}

让我们将这个脚本作为一个组件添加到一个空的游戏对象上,然后将我们的Steam设置、按钮和TMP对象拖放到它们的序列化字段中。

步骤5:应用程序ID

现在我们有了Steam传输设置,我们需要获取我们的应用程序ID。你可以通过访问Steamworks开发者仪表板来获取你的应用程序ID,或者如果你没有,可以使用Steam提供的"480"应用程序ID(Spacewar)。打开项目所在的文件夹,在根目录创建或修改"steam_appid.txt"文件,将你的ID放在里面并保存。

应用程序ID说明: 480应用程序ID是Steam用于开发和测试的免费应用程序ID。当你在Unity中启动游戏时,Steam会自动显示你正在玩"Spacewar"游戏。这是故意的,应该在你向Steam注册游戏并获得自己的应用程序ID之前使用。你可以在此处了解更多信息:Steam API示例

步骤6:测试

现在我们已经设置好了一切,终于可以测试我们的Steam连接了。按下播放按钮,你应该在控制台中看到一系列初始化消息:

按下主机按钮,如果PurrNet服务器正在运行,你应该在文本字段中看到你的主机ID。你也可以点击网络管理器,确认玩家数量为1并且你已连接:

如果你使用朋友或队友进行测试,请让他们在Unity编辑器中或通过构建版本连接,并提供你的主机ID。如果他们连接成功,他们的屏幕将如下所示:

恭喜!你已成功连接到Steam,并准备好继续你的多人游戏之旅!


专栏推荐

地址
【unity游戏开发入门到精通------C#篇】
【unity游戏开发入门到精通------unity通用篇】
【unity游戏开发入门到精通------unity3D篇】
【unity游戏开发入门到精通------unity2D篇】
【unity实战】
【制作100个Unity游戏】
【推荐100个unity插件】
【实现100个unity特效】
【unity框架/工具集开发】
【unity游戏开发------模型篇】
【unity游戏开发------InputSystem】
【unity游戏开发------Animator动画】
【unity游戏开发------UGUI】
【unity游戏开发------联网篇】
【unity游戏开发------优化篇】
【unity游戏开发------shader篇】
【unity游戏开发------编辑器扩展】

完结

好了,我是向宇,博客地址:https://xiangyu.blog.csdn.net,如果学习过程中遇到任何问题,也欢迎你评论私信找我。

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

相关推荐
鬼臾区5 小时前
Chapter 9 USB Device Framework 摘要1
网络
ikkkkkkkl6 小时前
计算机网络:传输层
网络·计算机网络·udp·tcp·传输层
nnsix6 小时前
Unity 反编译dll(Windows平台)
unity
WebRuntime6 小时前
问世间,exe是何物?直教AI沉默、Web寡言(1)
javascript·c#·.net·web
稀饭过霍6 小时前
【.NET 10.0】使用FluentValidation
c#·mvc·.net
G_H_S_3_6 小时前
【网络运维】容器、容器架构与docker部署
运维·网络·docker·架构
fantasy_arch6 小时前
SVT-AV1帧类型决策-场景切换检测
前端·网络·av1
HUST6 小时前
C 语言 第七讲:数组和函数实践:扫雷游戏
c语言·开发语言·数据结构·vscode·算法·游戏·c#
开开心心_Every6 小时前
无广告干扰:简单好用文字LOGO设计工具
xml·java·网络·数据库·华为od·华为云·excel