文章目录
前言
本文通过自定义继承自NetworkManager的脚本,来实现相关功能。同时自定义启动界面(即实现上一篇中hub功能)。
1.步骤
1.1 自定义NetworkManager
创建脚本CustomerUnetManger,并继承自NetworkManger,并对相关虚函数进行重载,并简单写了几个外部调用方法。完整脚本在文末,并且在关键部分做了详细注释,其中其中LogInfo为debug用,可以删除,完整脚本在文末部分截图如下:
1.2 UI脚本CustomerUnetMainMenu
编写脚本,实现自定义新建Server client 以及host,再次着重说一下原NetworkMangerHUD脚本中的第四个方法,即NetworkManger中的matchmaker。matchmaker通过unity提供的网络服务,建立游戏,然后通过你自己生成的exe就可以获取到建立的游戏列表,然后可以加入游戏.
1.2.1 开启matchmaker
/// <summary>
/// UI调用,MatchMaker
/// </summary>
public void StartMatchMaker_()
{
if (!IsClientConnected() && !NetworkServer.active && matchMaker == null)
{
StartMatchMaker();
matchMode = true;
}
}
1.2.2 建立游戏
/// <summary>
/// UI调用,创建比赛
/// </summary>
public void CreateMatch_()
{
if (matchMaker != null)
{
matchMaker.CreateMatch("LLMatch", 3, true, "", "", "", 0, 0, OnMatchCreate);
}
}
1.2.3 获取游戏列表
matchMaker.ListMatches(0, 5, "", true, 0, 0, OnMatchList);
1.2.4 加入游戏
/// <summary>
/// 加入指定的比赛
/// 对于建立比赛的player,本身已经在游戏中,所以不能再次加入(已经生成host)
/// 对于建立比赛的player,在加入其他游戏时先取消比赛(DestroyMatch)
/// 对于加入比赛的player,只能加入一次,不能重复加入(已经生成client)
/// 对于加入比赛的Player,在重复加入或者加入其他游戏之前先断开连接(DropConnection)
/// 本程序为demo,上述问题不做处理
/// </summary>
/// <param name="jointMatch"></param>
public void JoinMatch(MatchInfoSnapshot jointMatch)
{
if (matchMaker == null) StartMatchMaker();
matchMaker.JoinMatch(jointMatch.networkId, "", "", "", 0, 0, OnMatchJoined);//调用此方法时生成client
}
1.2.5 其他
此部分到此基本结束没有太大问题,一些注意事项已经在代码中注释,至于如何退出比赛等会再下一篇LobbyManager中进行解释,下边时此工程的所有脚本文件。再次说明一下,玩家预制体等均采用上一篇。此工程为demo,所以UI简陋,可能会有不友好的操作或者逻辑bug
2. 完整代码
2.1 自定义NetworkManager
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Networking.Match;
public class CustomerUnetManger : NetworkManager
{
public Action<List<MatchInfoSnapshot>> ListMatchAction;
private float refreshTime = 0;
private bool matchMode = false;
#region SERVER CALLBACKS
public override void OnServerReady(NetworkConnection conn)
{
LogInfo.theLogger.Log("Server ready");
base.OnServerReady(conn);
}
public override void OnStartServer()
{
LogInfo.theLogger.Log("Server start");
base.OnStartServer();
}
public override void OnServerConnect(NetworkConnection conn)
{
LogInfo.theLogger.Log("Server connect");
base.OnServerConnect(conn);
}
public override void OnServerDisconnect(NetworkConnection conn)
{
LogInfo.theLogger.Log("Server disconnect");
base.OnServerDisconnect(conn);
}
public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
{
LogInfo.theLogger.Log("Server add player");
base.OnServerAddPlayer(conn, playerControllerId);
}
public override void OnServerRemovePlayer(NetworkConnection conn, PlayerController player)
{
LogInfo.theLogger.Log("Server remove player");
base.OnServerRemovePlayer(conn, player);
}
public override void OnServerError(NetworkConnection conn, int errorCode)
{
LogInfo.theLogger.Log("Server error");
base.OnServerError(conn, errorCode);
}
public override void OnStartHost()
{
LogInfo.theLogger.Log("Host start");
base.OnStartHost();
}
public override void OnStopHost()
{
LogInfo.theLogger.Log("Host stop");
base.OnStopHost();
}
public override void OnStopServer()
{
LogInfo.theLogger.Log("Server stop");
base.OnStopServer();
}
#endregion
#region CLIENT CALLBACKS
public override void OnClientConnect(NetworkConnection conn)
{
LogInfo.theLogger.Log("Client connect");
base.OnClientConnect(conn);
}
public override void OnClientDisconnect(NetworkConnection conn)
{
LogInfo.theLogger.Log("Client disconnect");
base.OnClientDisconnect(conn);
}
public override void OnClientError(NetworkConnection conn, int errorCode)
{
LogInfo.theLogger.Log("Client error");
base.OnClientError(conn, errorCode);
}
public override void OnStartClient(NetworkClient client)
{
LogInfo.theLogger.Log("Client start");
base.OnStartClient(client);
}
public override void OnStopClient()
{
LogInfo.theLogger.Log("Client stop");
base.OnStopClient();
}
#endregion
#region MATCHMAKER CALLBACKS
public override void OnMatchCreate(bool success, string extendedInfo, MatchInfo matchInfo)
{
LogInfo.theLogger.Log("Match create");
//MatchCreate结束后进行OnMatchCreate回调,而在base中OnMatchCreate时会
//立马创建host(即创建一个server和client),所以当重复创建match时报错
//为避免此类问题可以在OnMatchCreate中不调用base的OnMatchCreate,通过自行创建List<MatchInfoSnapshot>变量自己存储比赛(matches)
//然后在需要创建host时,再startHost,但是一般游戏时只允许玩家创建一个游戏,创建另一个游戏时需退出当前游戏
base.OnMatchCreate(success, extendedInfo, matchInfo);
matchMaker.ListMatches(0, 5, "", true, 0, 0, OnMatchList);
}
public override void OnMatchJoined(bool success, string extendedInfo, MatchInfo matchInfo)
{
LogInfo.theLogger.Log("Match joint");
base.OnMatchJoined(success, extendedInfo, matchInfo);
}
public override void OnMatchList(bool success, string extendedInfo, List<MatchInfoSnapshot> matchList)
{
LogInfo.theLogger.Log("Match list:"+ matchList.Count);//基函数的OnMatchList把matchList赋值给matches,此处matches仍为初始值
base.OnMatchList(success, extendedInfo, matchList);
if(ListMatchAction!=null)
{
ListMatchAction(matchList);
}
//LogInfo.theLogger.Log("Match list:" + matches.Count);
}
#endregion
#region CONTROLL METHODS
/// <summary>
/// UI调用,开启server
/// </summary>
public void StartServer_()
{
if (!IsClientConnected() && !NetworkServer.active && matchMaker == null)
{
StartServer();
}
}
/// <summary>
/// UI调用,开启Host
/// </summary>
public void StartHost_()
{
if (!IsClientConnected() && !NetworkServer.active && matchMaker == null)
{
StartHost();
}
}
/// <summary>
/// UI调用,开启client
/// </summary>
public void StartClient_()
{
if (!IsClientConnected() && !NetworkServer.active && matchMaker == null)
{
StartClient();
}
}
/// <summary>
/// UI调用,MatchMaker
/// </summary>
public void StartMatchMaker_()
{
if (!IsClientConnected() && !NetworkServer.active && matchMaker == null)
{
StartMatchMaker();
matchMode = true;
}
}
/// <summary>
/// UI调用,停止服务
/// </summary>
public void StopAllService()
{
if (NetworkServer.active || IsClientConnected())
{
StopHost();
}
if (client != null && NetworkClient.active)
{
StopClient();
}
if (matchMaker != null)
{
StopMatchMaker();
matchMode = false;
}
}
/// <summary>
/// UI调用,创建比赛
/// </summary>
public void CreateMatch_()
{
if (matchMaker != null)
{
matchMaker.CreateMatch("LLMatch", 3, true, "", "", "", 0, 0, OnMatchCreate);
}
}
/// <summary>
/// 加入指定的比赛
/// 对于建立比赛的player,本身已经在游戏中,所以不能再次加入(已经生成host)
/// 对于建立比赛的player,在加入其他游戏时先取消比赛(DestroyMatch)
/// 对于加入比赛的player,只能加入一次,不能重复加入(已经生成client)
/// 对于加入比赛的Player,在重复加入或者加入其他游戏之前先断开连接(DropConnection)
/// 本程序为demo,上述问题不做处理
/// </summary>
/// <param name="jointMatch"></param>
public void JoinMatch(MatchInfoSnapshot jointMatch)
{
if (matchMaker == null) StartMatchMaker();
matchMaker.JoinMatch(jointMatch.networkId, "", "", "", 0, 0, OnMatchJoined);//调用此方法时生成client
}
#endregion
private void Update()
{
//取消了实时更新列表,可以测试使用
//if(Time.time>refreshTime && matchMode)
//{
// if (matchMaker == null) StartMatchMaker();
// matchMaker.ListMatches(0, 5, "", true, 0, 0, OnMatchList);
// refreshTime += 4;
//}
}
}
2.2 自定义启动界面
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Networking.Match;
using UnityEngine.UI;
public class CustomerUnetMainMenu : MonoBehaviour
{
public Button serverButton;
public Button hostButton;
public Button clinetButton;
public Button matchMakerButton;
public Button stopButton;
public Button createMatchButton;
public GameObject starts;
public GameObject matches;
public CustomerUnetManger theManager;
public MatchListRefresher refresher;
private bool matchCreated = false;
#region BUTTON METHODS
private void StartServer()
{
theManager.StartServer_();
}
private void StartHost()
{
theManager.StartHost_();
}
private void StartClient()
{
theManager.StartClient_();
}
private void StartMatchMaker()
{
theManager.StartMatchMaker_();
matches.SetActive(true);
starts.SetActive(false);
}
private void Stop()
{
theManager.StopAllService();
matches.SetActive(false);
starts.SetActive(true);
matchCreated = false;
}
private void CreateMatch()
{
if(!matchCreated)
{
theManager.CreateMatch_();
matchCreated = true;
}
}
#endregion
private void Start()
{
serverButton.onClick.AddListener(StartServer);
hostButton.onClick.AddListener(StartHost);
clinetButton.onClick.AddListener(StartClient);
matchMakerButton.onClick.AddListener(StartMatchMaker);
stopButton.onClick.AddListener(Stop);
createMatchButton.onClick.AddListener(CreateMatch);
theManager.ListMatchAction += OnListMatch;
}
private void OnListMatch(List<MatchInfoSnapshot> list)
{
refresher.RefreshList(list);
}
}
2.3 游戏列表类
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking.Match;
public class MatchListRefresher : MonoBehaviour
{
[SerializeField]
private GameObject matchButton;
//直接清除列表的刷新的方式不好,可通过一下两种方法解决:
//1、增加本地存储matches,针对每次反馈的matches比对,只实例化增加或者销毁不存在的
//2、或者每次增加或者减少比赛,可以尝试让服务端调用每个客户端执行
public void RefreshList(List<MatchInfoSnapshot> matches)
{
ClearList();
foreach(var match in matches)
{
var button = Instantiate(matchButton, transform);
button.GetComponent<MatchButton>().Initialize(match);
}
}
private void ClearList()
{
foreach(Transform t in transform)
{
Destroy(t.gameObject);
}
}
}
2.4 游戏按钮类
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Networking.Match;
using UnityEngine.UI;
public class MatchButton : MonoBehaviour
{
private MatchInfoSnapshot match;
//private NetworkManager manager;
public void Initialize(MatchInfoSnapshot match)
{
this.match = match;
GetComponentInChildren<Text>().text = match.name;
GetComponent<Button>().onClick.AddListener(() =>
{
//manager.matchMaker.JoinMatch(match);
FindObjectOfType<CustomerUnetManger>().JoinMatch(match);
});
}
}
2.5 Log
using UnityEngine;
using UnityEngine.UI;
public class LogInfo : MonoBehaviour
{
public bool isDebug = true;
private Text log;
public static LogInfo theLogger;
public void Log(string info)
{
if(isDebug)
log.text += "\r\n" + info;
}
private void Clear()
{
log.text = "Log:";
}
void Start ()
{
theLogger = this;
log = GetComponent<Text>();
log.text = "Log:";
GetComponentInChildren<Button>().onClick.AddListener(Clear);
}
}