# Photon Fusion 2

探索如何使用以下技术在 Edgegap 上托管 Unity 项目： [Fusion 2 小行星 示例](https://doc.photonengine.com/fusion/current/game-samples/fusion-asteroids).

{% embed url="<https://youtu.be/bg41HpPmE3E>" %}

## ✔️ 准备工作

在开始之前，您将需要：

* Unity 6 - [使用 Unity Hub 下载](https://unity.com/releases/unity-6),
* 为 Edgegap 修改的 Fusion 2 小行星 示例项目 - [从 GitHub 下载](https://github.com/edgegap/netcode-sample-photon-fusion-2).

## ⚡ 部署与连接

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

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

☑️ [为您的应用创建一个新的应用版本](https://app.edgegap.com/application-management/applications/fusion-2-asteroids-sample/versions/create)，选择 Fusion 2 小行星 示例。

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

☑️ 导航到 工具 / Edgegap Hosting，然后到第 6 步。 在 Edgegap 上部署服务器。

☑️ 按 部署到云 并且 [打开您新部署的详细信息](https://app.edgegap.com/deployment-management/deployments/list).

☑️ 找到您部署的唯一一次性连接详情：

* **主机 URL** 格式为 `780aa4260e83.pr.edgegap.net` .

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

### 2. 从编辑器连接

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

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

☑️ 将主机 URL（上一步的连接详情）作为房间名输入。

☑️ 按 开始 Edgegap 按钮以连接到您的服务器。

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

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

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

{% hint style="success" %}
参见 [Unity](/zh/unity.md) 以便 Unity **了解如何构建和自定义服务器**.
{% endhint %}

### 在 Photon 上创建应用

{% hint style="info" %}
为了让初始演示更简单，我们使用了由 Edgegap 拥有的免费层 Photon Cloud 帐户。
{% endhint %}

☑️ [创建 Photon 免费账号](https://dashboard.photonengine.com/).

☑️ [在 Photon 上创建一个应用](https://dashboard.photonengine.com/app/create):

* `多人游戏`,
* `Fusion` Photon SDK，
* `Fusion 2` SDK 版本。

☑️ 找到您的应用 ID，格式为 `85314a99-56fc-4ab3-ba26-50efca09f303` .

☑️ 在 工具 / Fusion / Fusion Hub (Alt + F) 下的 Photon 设置中输入您的 ID。

### 将 Fusion 2 项目与 Edgegap 集成

{% hint style="success" %}
参见 `EdgegapServerManager.cs`  用于示例性将 Fusion 2 与 Edgegap 集成的脚本。
{% endhint %}

{% hint style="warning" %}
您的 `NetworkProjectConfig`  **必须使用 `对等模式 = 单一` （专用 服务器）**，而不是 `多重` （客户端-主机）！
{% endhint %}

游戏客户端将通过 [Photon Fusion 2 ](https://doc.photonengine.com/fusion/current/manual/connection-and-matchmaking/matchmaking#creating-and-joining-a-game-session)[会话（房间） ](https://doc.photonengine.com/fusion/current/manual/connection-and-matchmaking/matchmaking#creating-and-joining-a-game-session)[功能 连接到游戏服务器](https://doc.photonengine.com/fusion/current/manual/connection-and-matchmaking/matchmaking#creating-and-joining-a-game-session).

您的游戏服务器需要在调用时用会话名称注册其 IP 地址和外部端口， `_runnerInstance.StartGame(StartGameArgs args)`:

* 使用 `GameMode.Server`  以确保您的连接不会通过 Photon Cloud 中继（会增加延迟），
* 使用 `NetAddress.CreateFromIpPort` 来自 Fusion 的方法，
* 提供服务器的 IP 地址：\
  `Environment.GetEnvironmentVariable("ARBITRIUM_PUBLIC_IP")`
* 提供服务器的外部端口：\
  `Environment.GetEnvironmentVariable("ARBITRIUM_PORT_GAMEPORT_EXTERNAL")`
  * 如果您使用我们的默认端口映射名称，这是默认名称， [将您的服务器打包的 Unity 插件](/zh/unity.md).

使用 Edgegap 部署主机 URL 作为会话名称：

* 在游戏服务器中，通过以下方式获取：\
  `$"{Environment.GetEnvironmentVariable("ARBITRIUM_REQUEST_ID")}.pr.edgegap.net"`
* 在测试游戏客户端时，从仪表板 - 部署详情页面 / 主机 URL 获取
  * 从我们的以下位置为测试创建部署： [快速入门 托管 插件](/zh/unity/developer-tools.md#dedicated-servers-quickstart-plugin),
* 在游戏客户端上线时，从以下位置获取： [匹配 / 工单分配 / FQDN](/zh/learn/pei-dui/matchmaker-in-depth.md#player-api):
  * 另请参见 [入门：匹配服务](/zh/learn/pei-dui.md).

{% tabs %}
{% tab title="客户端集成" %}
{% code title="EdgegapClientManager.cs" %}

```csharp
// 获取 fusion 2 网络运行器
_runnerInstance = FindFirstObjectByType<NetworkRunner>();
_runnerInstance.ProvideInput = true;

// 从 Edgegap 匹配器分配或 Fusion 2 会话 API 获取此值
string sessionName = "<requestId.pr.edgegap.net>";

// 查找 fusion 2 会话（使用会话名称）
var result = await _runnerInstance.StartGame(
    new StartGameArgs() {
        GameMode = GameMode.Client,
        SessionName = sessionName,
        ObjectProvider = _runnerInstance.GetComponent<NetworkObjectPoolDefault>(),
    }
);

// fusion 现在将基于会话名称获取服务器 IP 和端口并进行连接
```

{% endcode %}
{% endtab %}

{% tab title="服务器集成" %}
{% code title="EdgegapServerManager.cs" %}

```csharp
// 读取 edgegap 注入的环境变量
string requestId = Environment.GetEnvironmentVariable("ARBITRIUM_REQUEST_ID");
string listenPort = Environment.GetEnvironmentVariable("ARBITRIUM_PORT_GAMEPORT_INTERNAL");
string connectIP = Environment.GetEnvironmentVariable("ARBITRIUM_PUBLIC_IP");
string connectPort = Environment.GetEnvironmentVariable("ARBITRIUM_PORT_GAMEPORT_EXTERNAL");

// 获取 fusion 2 网络运行器
_runnerInstance = FindFirstObjectByType<NetworkRunner>();
_runnerInstance.ProvideInput = true;

// 注册 fusion 2 会话，以便客户端稍后查找（使用会话名称）
var result = await _runnerInstance.StartGame(
    new StartGameArgs() {
        GameMode = GameMode.Server,
        SessionName = $"{requestId}.pr.edgegap.net",
        ObjectProvider = _runnerInstance.GetComponent<NetworkObjectPoolDefault>(),
        Address = NetAddress.Any(listenPort),
        CustomPublicAddress = NetAddress.CreateFromIpPort(connectIP, connectPort),
    }
);

// 加载初始场景
if (result.Ok && _runnerInstance.IsServer) {
    await _runnerInstance.LoadScene(sceneName);
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### 故障排除

<details>

<summary><code>游戏不存在 (32758)</code></summary>

* Photon 房间要求玩家连接到与部署所在位置相同的 Photon Cloud 区域。 从仪表板部署使用了随机的玩家 IP。
* 您可能需要在地图上找到部署的位置，并在 Assets / Photon / Fusion / Resources / PhotonAppSettings.asset 中将您的游戏客户端配置为匹配的 [Photon Cloud 区域](https://doc.photonengine.com/fusion/current/manual/connection-and-matchmaking/regions#photon-cloud-for-gaming).
* Edgegap [会尽可能靠近玩家进行部署](https://docs.edgegap.com/zh/docs/sample-projects/unity-netcodes/pages/5e7e2169ca3822647d4607dfc1d3487ebcc0836c#id-1.-server-score-strategy-best-practice) 当使用我们的 [插件 时](/zh/unity/developer-tools.md) 或 [API](/zh/docs/api/zhuan-yong-fu-wu-qi.md) 因此在生产环境中，设置 Photon Cloud 区域不是必需的。

</details>


---

# 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/photon-fusion-2-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.
