https://blog.csdn.net/qq_25021249/article/details/84404009
前言
关于Photon引擎的基础教程视频、文章等等网上有一大堆。我们使用该引擎也有一端时间了,主要是unity项目中的多人联机模块,这里主要总结一下学过的知识,若对他人也有所帮助,那更是极好的。
PhotonEngine简介
官方网址:https://www.photonengine.com/en-US/Photon
The world’s #1 independent networking engine and multiplayer platform — Fast, reliable, scalable. Made for anyone: indies, professional studios and AAA productions.
PhotonEngine是世界排名第一的网络开发引擎、多人网络服务平台,他快速,可靠,并可扩展。适用于所有人:独立开发者,专业工作室或是3A级别大作。
Photon SDKs
SDKs网址:https://www.photonengine.com/en-us/sdks#
Photon makes multiplayer game development across platforms easy,Last but not least for the broad support of platforms and APIs to develop for.
Photon可以轻松实现跨平台多人游戏开发,支持多种平台并且提供对应的API接口。
不同功能的SDK
1.Realtime、PUN:基于Photon云平台的服务器
2.Chat、Voice:主要用于通讯
3.SELF-HOSTED(我们主要说这个):自主添加服务器逻辑的
Photon SDKs
SDK:SELF-HOSTED
SELF-HOSTED
下载安装
下载并安装,得到以下文件夹
本地Server
依次打开:…\deploy\bin_Win64
(等会我们编写服务器逻辑的时候需要修改这个路径下的一些文件)
在这里插入图片描述
服务器端逻辑
创建自己的服务器项目(类库)
因为PhotonEngine已经完成了服务器几乎所有通讯逻辑代码,并封装成很简单的接口,我们只需要在此基础上添加自己的游戏逻辑代码。因此创建一个类库项目,最终生成一个dll,丢给Photon去执行即可。
在这里插入图片描述
导入必要的dll文件
实现最基础的通讯和日志输出功能,我们需要导入以下五个dll:
(文件目录:"…\lib")
ExitGamesLibs.dll
Photon.SocketServer.dll
PhotonHostRuntimeInterfaces.dll
ExitGames.Logging.Log4Net.dll
log4net.dll
服务器的入口和出口(继承ApplicationBase类)
using System;
using Photon.SocketServer;
using ExitGames.Logging;
using System.IO;
namespace MyServer
{
public class MyApplication : ApplicationBase
{
///日志文件输出类
static readonly ILogger log = LogManager.GetCurrentClassLogger();
/// <summary>
/// 服务器开启后,自动调用该函数
/// </summary>
protected override void Setup()
{
//这四句是设置日志文件的格式
LogManager.SetLoggerFactory(ExitGames.Logging.Log4Net.Log4NetLoggerFactory.Instance);
log4net.GlobalContext.Properties["Photon:ApplicationLogPath"] = Path.Combine(this.ApplicationRootPath, "log");
//输出日志文件的名字
log4net.GlobalContext.Properties["LogFileName"] = "LenQiy_" + this.ApplicationName;
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(Path.Combine(this.BinaryPath, "log4net.config")));
ShowSysLog("服务器开启。");
}
/// <summary>
/// 服务器关闭后,自动调用该函数
/// </summary>
protected override void TearDown()
{
ShowSysLog("服务器关闭。");
}
/// <summary>
/// 当有 "新客户端" 连接到服务器的时候,自动调用该函数
/// </summary>
protected override PeerBase CreatePeer(InitRequest initRequest)
{
ShowSysLog("新连接一个客户端。");
return new MyPeer(initRequest);
}
/// <summary>
/// 测试日志
/// </summary>
public static void ShowTestLog(string s)
{
log.Debug("【======================Test======================】 " + s);
}
/// <summary>
/// 系统日志
/// </summary>
/// <param name="s"></param>
public static void ShowSysLog(string s)
{
log.Debug("【System】 " + s);
}
/// <summary>
/// 错误日志
/// </summary>
/// <param name="funcName"></param>
/// <param name="exc"></param>
public static void ShowErrorLog(string exc)
{
log.Debug("【>>>>>Error<<<<<】 " + exc);
}
}
}
服务器和客户端交流中心(继承ClientPeer类)
旧版本(我们之前用的 v3-4-31-10808),这里继承PeerBase类
using System.Collections.Generic;
using Photon.SocketServer;
using PhotonHostRuntimeInterfaces;
namespace MyServer
{
public class MyPeer : ClientPeer
{
/// <summary>
/// 新连接的"客户端对象"在服务器端的"一一对应的对象"
/// </summary>
/// <param name="initRequest"></param>
public MyPeer(InitRequest initRequest) : base(initRequest) { }
/// <summary>
/// 客户端断开连接后,自动调用这里
/// </summary>
protected override void OnDisconnect(DisconnectReason reasonCode, string reasonDetail)
{
MyApplication.ShowSysLog("退出一个客户端");
}
/// <summary>
/// 客户端发送消息后,服务器端在这里接受
/// </summary>
protected override void OnOperationRequest(OperationRequest request, SendParameters sendParameters)
{
/* OperationResponse类的成员
* byte OperationCode: 命令代码,和要求时的命令代表相同,代表该要求的回传值,该值自定义
* Dictionary<byte,object>Parameters: 回传的内容,该值自定义
* short ReturnCode: 回传状态的值,该值自定义
* string DebugMessage: 回传的日志,该值自定义
*
* SendParameters类的成员
* byte ChannelId: 频道,通常都用0频道,除非特别需求否则不建议更改
* bool Encrypted: 是否加密,true为加密,默认为false
* bool Flush: 立即传输,会让queue尚未传送的往后延,默认为false
* bool Unreliable: 可靠传输,默认为true
*/
//从客户端往服务器发数据,用OperationRequest
//可以根据判断request.OperationCode值,来处理不同类型的事件消息
byte reqCode = request.OperationCode;
//客户端传来的值
Dictionary<byte, object> reqPara = request.Parameters;
switch (reqCode)
{
case 0:
object value1 = null;
reqPara.TryGetValue(0, out value1);
break;
case 1:
object value2 = null;
reqPara.TryGetValue(0, out value2);
break;
}
//从服务器往客户端发数据,用OperationResponse
OperationResponse response =new OperationResponse();
//回传的命令Code和接收到的一致即可
response.OperationCode = request.OperationCode;
//这里自定义格式,到了客户端按照自定义的格式对应的读取即可
response.Parameters = new Dictionary<byte, object>();
response.Parameters.Add(0, "值");
response.Parameters.Add(1, "值");
response.Parameters.Add(2, "值");
//回传状态的值
response.ReturnCode = 0;
//回传日志
response.DebugMessage = "这是我的回传日志。";
SendOperationResponse(response, sendParameters);
}
}
}
生成dll包,导入Photon服务器软件中
选择 生成 => 生成解决方案
(快捷键Ctrl+Shift+B)
生成的dll文件默认路径为:"…\bin\Debug"
在这里插入图片描述
当服务器端写好后,打开之前下载的Photon SDK文件夹,“…\deploy”,并在此目录下创建自己的服务器端文件夹,把我们生成的MyServer.dll文件复制粘贴到该文件夹中
在这里插入图片描述
在Photon软件中设置自己的服务器
“…\deploy\bin_Win64\PhotonServer.config”
在这里插入图片描述
打开PhotonServer.config文件在"'Configuration"节点内添加
<Configuration>
//在这一层内添加下面一大段
//内容格式和源文件已有设置文件的一样,把部分节点名字改成自己服务器中对应的名字即可
</Configuration>
<MyServer
MaxMessageSize="512000"
MaxQueuedDataPerPeer="512000"
PerPeerMaxReliableDataInTransit="51200"
PerPeerTransmitRateLimitKBSec="256"
PerPeerTransmitRatePeriodMilliseconds="200"
MinimumTimeout="5000"
MaximumTimeout="30000"
DisplayName="MyServer">
<UDPListeners>
<UDPListener
IPAddress="0.0.0.0"
Port="5055">
</UDPListener>
</UDPListeners>
<TCPListeners>
<TCPListener
IPAddress="0.0.0.0"
Port="4530"
PolicyFile="Policy\assets\socket-policy.xml"
InactivityTimeout="10000"
>
</TCPListener>
</TCPListeners>
<PolicyFileListeners>
<PolicyFileListener
IPAddress="0.0.0.0"
Port="843"
PolicyFile="Policy\assets\socket-policy.xml">
</PolicyFileListener>
<PolicyFileListener
IPAddress="0.0.0.0"
Port="943"
PolicyFile="Policy\assets\socket-policy-silverlight.xml">
</PolicyFileListener>
</PolicyFileListeners>
<WebSocketListeners>
<WebSocketListener
IPAddress="0.0.0.0"
Port="9090"
DisableNagle="true"
InactivityTimeout="10000"
OverrideApplication="Master">
</WebSocketListener>
<WebSocketListener
IPAddress="0.0.0.0"
Port="9091"
DisableNagle="true"
InactivityTimeout="10000"
OverrideApplication="Game">
</WebSocketListener>
</WebSocketListeners>
<Runtime
Assembly="PhotonHostRuntime, Culture=neutral"
Type="PhotonHostRuntime.PhotonDomainManager"
UnhandledExceptionPolicy="Ignore">
</Runtime>
<Applications Default="MyServer">
<Application
Name="MyServer"
BaseDirectory="MyServer"
Assembly="MyServer"
Type="MyServer.MyApplication"
ForceAutoRestart="true"
WatchFiles="dll;config"
ExcludeFiles="log4net.config"
>
</Application>
<Application
Name="CounterPublisher"
BaseDirectory="CounterPublisher"
Assembly="CounterPublisher"
Type="Photon.CounterPublisher.Application"
ForceAutoRestart="true"
WatchFiles="dll;config"
ExcludeFiles="log4net.config">
</Application>
</Applications>
</MyServer>
打开Photon服务器软件,并开启自己的服务器逻辑
打开下面的应用程序
在这里插入图片描述
在这里插入图片描述
【application】可以用于Windows 普通系统:
开启:Start as application
停止:Stop application
【service】可以用于Windows 普通系统 和 Windows Server系统
先安装:Install service
开启:Start service
重启:Restart service
停止:Stop service
移除:Stop service
客户端逻辑
Unity中导入dll包
在自己的Unity客户端项目中导入Photon3Unity.dll
目录:"…\lib"
在这里插入图片描述
Unity客户端逻辑(继承IPhotonPeerListener)
using UnityEngine;
using System.Collections.Generic;
using ExitGames.Client.Photon;
using System;
public class PhotonEngine : MonoBehaviour, IPhotonPeerListener
{
PhotonPeer peer;
void Awake()
{
//自己服务器项目的名字
string serverName = "MyServer";
//服务器软件所在的电脑的Ip,局域网、广域网皆可
string ip = "192.168.1.100";
//通讯协议:TCP为4530,Upd为5055
string port = "5055";
//服务器请求地址
string serverAddr= ip + ":" + port;
ConnectionProtocol protocol;
//通讯协议,与端口号(port)必须保持一致
protocol = ConnectionProtocol.Tcp;
peer = new PhotonPeer(this, protocol);
//请求连接服务器
peer.Connect(serverAddr, serverName);
}
/// <summary>
/// 实时与服务器保持通讯
/// </summary>
void FixedUpdate()
{
if (peer != null)
{
peer.Service();
}
}
/// <summary>
/// 给服务器发送消息
/// </summary>
public void SendRequest()
{
//发送至服务器的消息的类型
byte opCode = 0;
//发送至服务器的消息内容
Dictionary<byte, object> para = new Dictionary<byte, object>();
para.Add(0, "值1");
para.Add(1, "值2");
para.Add(2, "值3");
peer.SendOperation((byte)opCode, para, SendOptions.SendUnreliable);
}
/// <summary>
/// 服务器传回来的日志,自定义的
/// </summary>
public void DebugReturn(DebugLevel level, string message)
{
Debug.Log(level + " : " + message);
}
/// <summary>
/// 服务端传过来的事件,自定义的
/// </summary>
public void OnEvent(EventData eventData)
{
}
/// <summary>
/// 服务器端传回来的消息,自定义的
/// </summary>
public void OnOperationResponse(OperationResponse operResq)
{
//服务器发送回来的消息的类型code
//operResq.OperationCode
//服务器发送回来了的消息内容字典,按照服务器发送的格式解析即可获得
//operResq.Parameters
}
//连线状态更改通知
public void OnStatusChanged(StatusCode statusCode)
{
switch (statusCode)
{
case StatusCode.Connect:
Debug.Log("连接服务器成功");
break;
//case StatusCode.Disconnect:
default:
Debug.Log("与服务器断开连接");
break;
}
}
}