🎊 Unity使用Photon和腾讯云服务器实现联机对战

Unity使用Photon和腾讯云服务器实现联机对战

记录一下学习联机的过程

更新一下:这个方案太麻烦了,团结的UOS做的比unity好太多,如果可以的话换团结吧,我用得很爽

先看效果:一个unity一个打包程序联机,运行起来挺流畅的这里gif帧率低

使用的是Photon的fusion的server模式

以下AI分析:

联机方案(团结引擎好像有一种后面研究一下):

产品对比:

联机模式:

我觉得fusion的server更满足我的需求就选这个了(想要延迟低一点,响应快,竞技游戏)

先介绍一下,photon他们是自己有服务器的,他们有photon cloud和photon server(服务器模式),因为他们服务器毕竟在国外,如果国内连的话延迟会有点高,所以我想着能不能使用国内的云端服务器来降低时延(理论上是降低的,不过没试过,国内应该好一点),这个方法需要支付腾讯云服务器的费用和使用photon的费用(人数和流量),photon官方文档说了即使不使用他们的photon server也要有photon cloud 订阅(图片下面)

正好有腾讯云的cvm服务器的试用和photon的20个人以内免费,所以我就来试试了

思路:

就是把unity项目打包成一个无头模式(不用渲染画面之类的)的exe文件,放在腾讯云终端上面运行(作为服务端),然后unity和正常打包的exe就可以作为客户端连接到一个房间内,如果你按照这样做的话,后面还有两种选择,一个是云端上面运行一个exe同时存在有多个房间(一个进程多房间),或者运行多个exe,每一个房间对应一个exe(多进程多房间),各有优缺点,这个多房间我还没搞,我这里只讲如何连接上

操作:

1:host模式和server模式区别在服务端不能同时作为客户端没有写入能力,所以我们直接下载host模式的案例教程(每个正方体是一个玩家,可以发射子弹)

链接:Fusion 2 - Host Mode Overview | Photon Engine

2:把文件夹导入工程后,(先测试一下host案例模式是否能够正常运行),修改里面的BasicSpawner脚本,将里面的host换成server等(代码末尾给出)

3:点击菜单栏:工具-fusion-fusion hub,然后修改photon app setting配置

你的Appid:Your Photon Cloud | Photon Engine

4:按照官网所说,中国大陆比较特殊,需要发邮件申请(这步我不确定反正我做了,没多久就回了)

官方文档:Fusion 2 - 区域 |光子引擎 --- Fusion 2 - Regions | Photon Engine

邮件地址:hello@photonengine.com

我用网易邮箱发了个:

Based on your documents, I apply to unlock the appid for the Chinese mainland region. Are there any conditions required,This is my current appid:你的appid

然后回了两封,第一封说尽快

第二封说解锁​

5:将修改好的项目,保存场景,将Game场景拖入项目设置当中打包成无头模式​​

6:按照腾讯云的规则把无头模式打包的所有文件拖入云端,然后控制台运行

​​

.\NetTest.exe -batchmode -nographics -port 5055 -logFile server.log

netstat -ano | Select-String "5055"

7:成功监听端口后,在unity,点击播放的join就可以看到生成了一个预制体,如果你再运行一下正常打包程序的exe的join,又能看到一个​

总结:

还是要多看官方文档,一开始没找到关于fusion和服务器对于中国大陆的限制,网络连接卡半天

好了结束了

觉得有用的话点个赞再走呗OvO

代码:

using System;

using System.Collections.Generic;

using Fusion;

using Fusion.Addons.Physics;

using Fusion.Sockets;

using UnityEngine;

using UnityEngine.SceneManagement;

///

/// 注:很多逻辑没有实现,但是可以连接,需要的话自己按官网改

///

public class BasicSpawner : MonoBehaviour, INetworkRunnerCallbacks

{

private NetworkRunner _runner;

private void Start()

{

Debug.Log("BasicSpawner.Start() called - batchmode=" + Application.isBatchMode);

//如果是无图形模式(云端运行的 exe)

if (Application.isBatchMode)

{

Debug.Log("Detected headless mode: starting as Host Server...");

StartGame(GameMode.Server, true);

}

}

private void OnGUI()

{

if (!Application.isBatchMode && _runner == null)

{

if (GUI.Button(new Rect(0, 0, 200, 40), "Host"))

StartGame(GameMode.Server);

if (GUI.Button(new Rect(0, 40, 200, 40), "Join"))

StartGame(GameMode.Client);

}

}

async void StartGame(GameMode mode, bool isDedicatedServer = false)

{

// Create the Fusion runner and let it know that we will be providing user input

_runner = gameObject.AddComponent();

_runner.ProvideInput = !isDedicatedServer;

_runner.AddCallbacks(this);

var runnerSimulatePhysics3D = gameObject.AddComponent();

runnerSimulatePhysics3D.ClientPhysicsSimulation = ClientPhysicsSimulation.SimulateAlways;

// Create the NetworkSceneInfo from the current scene

var scene = SceneRef.FromIndex(SceneManager.GetActiveScene().buildIndex);

var sceneInfo = new NetworkSceneInfo();

if (scene.IsValid)

{

sceneInfo.AddSceneRef(scene, LoadSceneMode.Additive);

}

var args = new StartGameArgs

{

GameMode = mode,

SessionName = "TestRoom",

SceneManager = gameObject.AddComponent(),

};

//如果是服务端,监听所有IP地址的5055端口

if (mode == GameMode.Server && isDedicatedServer)

{

args.Address = NetAddress.Any(5055);

args.Scene = scene;

Debug.Log("Starting Dedicated Server on port 5055...");

}

//客户端

if (mode == GameMode.Client)

{

// 改成你云主机的公网 IP

//args.Address = NetAddress.CreateFromIpPort("43.139.104.215", 5055);

Debug.Log("StartGame: client will connect to Address = " + (args.Address?.ToString() ?? "null"));

}

Debug.Log("StartGame: StartGameArgs: " + args);

await _runner.StartGame(args);

}

[SerializeField]

private NetworkPrefabRef _playerPrefab; // Character to spawn for a joining player

private Dictionary _spawnedCharacters = new Dictionary();

public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)

{

if (runner.IsServer)

{

// Create a unique position for the player

Vector3 spawnPosition = new Vector3((player.RawEncoded % runner.Config.Simulation.PlayerCount) * 3, 1, 0);

NetworkObject networkPlayerObject = runner.Spawn(_playerPrefab, spawnPosition, Quaternion.identity, player);

// Keep track of the player avatars for easy access

_spawnedCharacters.Add(player, networkPlayerObject);

}

}

public void OnPlayerLeft(NetworkRunner runner, PlayerRef player)

{

if (_spawnedCharacters.TryGetValue(player, out NetworkObject networkObject))

{

runner.Despawn(networkObject);

_spawnedCharacters.Remove(player);

}

}

private bool _mouseButton0;

private bool _mouseButton1;

private void Update()

{

_mouseButton0 = _mouseButton0 || Input.GetMouseButton(0);

_mouseButton1 = _mouseButton1 || Input.GetMouseButton(1);

}

public void OnInput(NetworkRunner runner, NetworkInput input)

{

var data = new NetworkInputData();

if (Input.GetKey(KeyCode.W))

data.direction += Vector3.forward;

if (Input.GetKey(KeyCode.S))

data.direction += Vector3.back;

if (Input.GetKey(KeyCode.A))

data.direction += Vector3.left;

if (Input.GetKey(KeyCode.D))

data.direction += Vector3.right;

data.buttons.Set(NetworkInputData.MOUSEBUTTON0, _mouseButton0);

_mouseButton0 = false;

data.buttons.Set(NetworkInputData.MOUSEBUTTON1, _mouseButton1);

_mouseButton1 = false;

input.Set(data);

}

public void OnInputMissing(NetworkRunner runner, PlayerRef player, NetworkInput input){}

public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason){}

public void OnConnectedToServer(NetworkRunner runner){}

public void OnDisconnectedFromServer(NetworkRunner runner, NetDisconnectReason reason){Debug.LogWarning($"Disconnected: {reason}");}

public void OnConnectRequest(NetworkRunner runner, NetworkRunnerCallbackArgs.ConnectRequest request, byte[] token){}

public void OnConnectFailed(NetworkRunner runner, NetAddress remoteAddress, NetConnectFailedReason reason)

{Debug.LogError($"Connect failed to {remoteAddress}: {reason}"); }

public void OnUserSimulationMessage(NetworkRunner runner, SimulationMessagePtr message){}

public void OnSessionListUpdated(NetworkRunner runner, List sessionList){}

public void OnCustomAuthenticationResponse(NetworkRunner runner, Dictionary data){}

public void OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrationToken){}

public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ReliableKey key, ArraySegment data){}

public void OnReliableDataProgress(NetworkRunner runner, PlayerRef player, ReliableKey key, float progress){}

public void OnSceneLoadDone(NetworkRunner runner){}

public void OnSceneLoadStart(NetworkRunner runner){}

public void OnObjectExitAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) {}

public void OnObjectEnterAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) {}

}

🎈 相关推荐

黑铁排王者不是梦,英雄联盟灵活组排取消段位限制
🏷️ beat365登录平台

黑铁排王者不是梦,英雄联盟灵活组排取消段位限制

📅 09-26 👀 421
信用卡cvv2是什么意思?
🏷️ Bet体育365提款验证

信用卡cvv2是什么意思?

📅 08-14 👀 6202
柳永到底是怎麼死的
🏷️ beat365登录平台

柳永到底是怎麼死的

📅 09-16 👀 7952
感染新冠肺炎多久发病
🏷️ beat365登录平台

感染新冠肺炎多久发病

📅 09-27 👀 6042
腿部肌肉僵硬如何变软
🏷️ office365桌面应用

腿部肌肉僵硬如何变软

📅 10-10 👀 325
天龙八部转区多少钱
🏷️ beat365登录平台

天龙八部转区多少钱

📅 07-22 👀 7061