# 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](/zh/unity.md) 用于 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]: 编辑器兼容性 ⇒ 注释掉此行


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.edgegap.com/zh/docs/sample-projects/unity-netcodes/unity-netcode-on-edgegap.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
