# Unity NGO

探索如何使用来自 [NGO Boss 房间示例](https://unity.com/demos/small-scale-coop-sample).

{% hint style="info" %}
此示例 **不需要任何 Unity Gaming Services (UGS)**、Multiplay 或 Relays 即可运行。
{% endhint %}

{% embed url="<https://youtu.be/8Qi-wXswjkw>" %}

## ✔️ 准备工作

在开始之前，您需要：

* Unity 6 - [使用 Unity Hub 下载](https://unity.com/releases/unity-6),
* Unity NGO \[Netcode for Game Objects] Boss 房间示例项目（已为 Edgegap 修改）：
  * [从 Github 下载](https://github.com/edgegap/netcode-sample-unity-ngo-bossroom).

## ⚡ 部署并连接

### 1. 在 Edgegap 上部署服务器

☑️ 要开始，您需要 [创建一个 Edgegap 免费帐户](https://app.edgegap.com/auth/register)。无需信用卡。

☑️ [为您的应用创建一个新的应用版本](https://app.edgegap.com/application-management/applications/unity-ngo-bossroom-sample/versions/create)，选择 NGO Boss 房间示例。

☑️ [使用您的 NGO Boss 房间示例应用版本部署服务器](https://app.edgegap.com/deployment-management/deployments/list).

☑️ [打开您新部署的详细信息](https://app.edgegap.com/deployment-management/deployments/list) 并查找您的唯一一次性连接详情：

* **外部端口** 格式为 `30854`  （5 位数字）。

☑️ 导航到 日志 选项卡并查找（CTRL+F）您的唯一一次性连接详情：

* `ARBITRIUM_PUBLIC_IP` 格式为 `172.234.244.38` .

✅ 现在您可以继续下一步。

### 2. 从编辑器连接

☑️ 在 Unity 中打开您的新项目。

☑️ 验证您已打开场景： `Assets/Scenes/MainMenu.unity`.

☑️ 按 ▶️ 播放 按钮以启动您的游戏客户端：

* 按下 **使用直接 IP 启动** 按钮，
* 选择选项卡 **使用 IP 加入。**

☑️ 输入来自上一步的连接详情。

☑️ 按下 **加入** 按钮以连接到您的服务器。

☑️ 使用第二个虚拟玩家连接， [多人游戏播放模式](https://docs-multiplayer.unity3d.com/mppm/current/about/) 或 [ParrelSync](https://github.com/VeriorPies/ParrelSync).

🙌 恭喜您完成在 Edgegap 上的首次部署！

## ✏️ 自定义服务器构建

{% hint style="success" %}
参见 [unity](https://docs.edgegap.com/zh/unity "mention") 用于 Unity 以 **了解如何构建和自定义服务器**.
{% endhint %}

### 以专用服务器运行

为了使此示例以专用服务器运行，我们做了以下更改：

{% tabs %}
{% tab title="服务器初始化" %}
新脚本（添加到您的 `MainMenu` 场景中的一个新的空 `游戏对象`):

<pre class="language-csharp" data-title="Assets/Scripts/EdgegapServerStarter.cs" data-line-numbers><code class="lang-csharp">下面是该通用未修改脚本（取自 Bolt 文档）的样子：
using System.Collections;
using System.Collections.Generic;
using Unity.BossRoom.ConnectionManagement;
using UnityEngine;

namespace Unity.Multiplayer.Samples.BossRoom
{
    public class EdgegapServerStarter : MonoBehaviour
    {
<strong>        public string portMapName = <a data-footnote-ref href="#user-content-fn-1">"gameport"</a>;
</strong>
        // Start 在第一帧更新前被调用
        // 用于初始化
        {
            if (Application.isBatchMode)
            {
                ConnectionManager connectionManager = GameObject.Find("ConnectionManager").GetComponent&#x3C;ConnectionManager>();
<strong>                string internalPortAsStr = Environment.GetEnvironmentVariable($"ARBITRIUM_PORT_{portMapName.ToUpper()}_INTERNAL");
</strong>
                if (internalPortAsStr == null || !ushort.TryParse(internalPortAsStr, out ushort port))
                {
                    throw new Exception($"找不到端口映射，确保您的应用版本端口名称与 \"{portMapName}\" 匹配");
                }

<strong>                connectionManager.StartHostIp("<a data-footnote-ref href="#user-content-fn-2">服务器</a>", "0.0.0.0", port);
</strong>            }
        }
    }
}
</code></pre>

{% endtab %}

{% tab title="连接管理" %}
已修改的文件：

<pre class="language-csharp" data-title="Assets/Scripts/ConnectionManagement/ConnectionMethod.cs"><code class="lang-csharp"><strong>116        public override async Task SetupHostConnectionAsync()
</strong>117        {
<strong>118            <a data-footnote-ref href="#user-content-fn-3">//SetConnectionPayload(GetPlayerId(), m_PlayerName); // 也需要为主机设置连接负载，因为主机也是一个客户端</a>
</strong>119            var utp = (UnityTransport)m_ConnectionManager.NetworkManager.NetworkConfig.NetworkTransport;
120            utp.SetConnectionData(m_Ipaddress, m_Port);
121        }
</code></pre>

<pre class="language-csharp" data-title="Assets/Scripts/ConnectionManagement/ConnectionState/StartingHostState.cs"><code class="lang-csharp"><strong>66        async void StartHost()
</strong>67        {
              ...

72                // NGO 的 StartHost 启动所有内容
<strong>--                <a data-footnote-ref href="#user-content-fn-3">//if (!m_ConnectionManager.NetworkManager.StartHost())</a>
</strong><strong>73                <a data-footnote-ref href="#user-content-fn-4">if (!m_ConnectionManager.NetworkManager.StartServer())</a>
</strong>74                {
75                    StartHostFailed();
76                }

              ...
</code></pre>

<pre class="language-csharp" data-title="Assets/Scripts/Gameplay/GameplayObjects/Character/ClientCharacter.cs"><code class="lang-csharp"><strong>112        public override void OnNetworkSpawn()
</strong>113        {
               ...

125            m_ServerCharacter.IsStealthy.OnValueChanged += OnStealthyChanged;
126            m_ServerCharacter.MovementStatus.OnValueChanged += OnMovementStatusChanged;
<strong>127            <a data-footnote-ref href="#user-content-fn-3">//OnMovementStatusChanged(MovementStatus.Normal, m_ServerCharacter.MovementStatus.Value);</a>
</strong> 
               ...
</code></pre>

{% endtab %}

{% tab title="编辑器/服务器 兼容性" %}
修改文件：

<pre class="language-csharp" data-title="Assets/Scripts/ConnectionManagement/ConnectionState/HostingState.cs"><code class="lang-csharp"><strong>148        ConnectStatus GetConnectStatus(ConnectionPayload connectionPayload)
</strong>149        {
               ...
                
<strong>155            <a data-footnote-ref href="#user-content-fn-5">//if (connectionPayload.isDebug != Debug.isDebugBuild)</a>
</strong><strong>156            //{
</strong><strong>157            //    return ConnectStatus.IncompatibleBuildType;
</strong><strong>158            //}
</strong>
               ...
</code></pre>

{% endtab %}
{% endtabs %}

[^1]: Edgegap 上的默认端口名称

[^2]: 服务器的玩家名可以是任意值

[^3]: 仅 p2p ⇒ 注释掉此行

[^4]: 改为以专用服务器启动

[^5]: 编辑器兼容性 ⇒ 注释掉此行
