NetworkManager網絡通訊_問題匯總(四)


此篇來填坑,有些坑是unet自身問題,而大部分則是理解不准確造成的(或者unity定義太復雜)

問題一: isLocalPlayer 值一直是false

出現場景:NetworkLobbyPlayer中重寫 OnClientEnterLobby()方法時出現(public override void OnClientEnterLobby()),當時想法是當玩家進入lobby后對local Player進行個性化設置,但返回結果一直是false。此問題在其他情況可能也會出現。

解決方案:此問題主要是對isLocalPlayer,isServer以及isClient定義不清楚。isServer:在服務端的活動的player,此值均為true,即不管是server端StartHost時自己建立的client還是Server Spawn產生,只要在服務端顯示,此值均為true。isClient:由Server端spawn產生,並作為client運行的,此值均為true,即只要是Spawn產生的,不管是在服務端還是在客戶端,此值均為true。所以服務端存在的player,isServer和isClient均為true,客戶端isclient均為true,isServer均為false。(isServer與isClient並沒有對立關系)。isLocalPlayer是生成的玩家可以控制的player,不管是服務端(如果服務端也有玩家)還是客戶端均只有一個player的isLocalPlayer值為true。isLocalPayer在執行OnStartLocalPlayer回調時賦值為true,即生成本地的player時賦值,而OnClientEnterLobby回調時只是數據層面已經客戶端連接進來,但是還沒有建立client的gameobject,所以此時isLocalPlayer並未賦值(默認false)。而且只有本地

玩家的player是Create產生的,先回調OnClientEnterLobby,后回調函數OnStartLocalPlayer,而且其他玩家(敵人)則是有服務端Spawn產生的,只調用回調OnClientEnterLobby。

問題二: Server disconnect due to error:timeout

出現場景:當Server運行時,client斷開時出現(第一次斷開不會出現,第二次斷開以及以后均會出現)

解決方案:無法解決,但不影響運行。谷歌很多此,在unity論壇中有unity人員(自己號稱)unet自身bug,此問題類似於tcp或者udp編程時,客戶端主動斷開連接,而服務端雖然接受到了斷開的消息(甚至沒有接收到),但依然接受下一條消息,此時報錯(如果有人看過作者寫過的一個tcpServer,會看到類似問題)。當然對於unet來說此問題可能涉及到的東西比較復雜,號稱已經解決,但是在后續版本中一直存在,筆者為unity2017,仍存在此問題,並未下載補丁進行嘗試,谷歌出來的此問題來源如下,如果不想出現此問題可以在重寫OnServerDisconnect時不調用基函數,只添加NetworkServer.DestroyPlayersForConnection(conn)即可。

public virtual void OnServerDisconnect(NetworkConnection conn)
{
    NetworkServer.DestroyPlayersForConnection(conn);
    if (conn.lastError != NetworkError.Ok)
    {
        if (LogFilter.logError)
        {
             Debug.LogError("ServerDisconnected due to error: " + conn.lastError);
        }
    }
}

問題三: Client disconnect due to error:timeout

出現場景:當在lobby場景中調用DropConnection斷開玩家與Server的連接時出現

解決方案:無法解決,但不影響運行。谷歌很多此,此問題也沒有很好的得到解決,有點類似與問題三。

問題四: A connection has already set as ready....

出現場景:遠程客戶端調用RemovePlayer()退出游戲又重新加入時出現。在OnClientSceneChanged的base中會調用AddPlayer,也會出現此問題(目前選擇直接暴力注釋掉base方法)

解決方案:unity遠程客戶端和服務端建立連接時先建立NetworkConnection,然后分別建立服務端和客戶端的游戲物體,所以調用removePlayer方法時只是在clientscene中removePlayer,並沒有斷開連接,所以在重新加入游戲時會出現此問題(即本身的connection還存在),此問題不影響運行,但是不確定會有其他隱患,多以在上一篇中遠程客戶端斷開連接時采用matchmaker的DropConnection方法(調用此方法時會自動移除服務端和游戲端的游戲物體),但此方法又會引發問題三,可以采用connectionToServer進行解決,具體看問題六。

問題五: Local Connection already exists 以及ClientScene::AddPlayer: playerControllerId of 0 already in use

出現場景:建立游戲的主機銷毀游戲時出現

解決方案:主機建立游戲,如果想要銷毀游戲需要三步,第一步銷毀player(remove Player方法),第二部斷開connection(可以理解為銷毀游戲即destroy Match),第三部StopHost。第一步時為了移除clientScene中的player,如果缺少則會引發ClientScene::AddPlayer: playerControllerId of 0 already in use問題。如果缺少第二部則會引發問題四,而且游戲不會銷毀。如果缺少第三部,怎會引發 Local Connection already exists,因為不管是否銷毀游戲,建立游戲主機的本機player即使服務端player,又是client端player,所以localconnection會一直存在,只有StopHost才會斷開此本地連接。

問題六:connectionToClient以及connectionToServer應用

出現場景:此問題設計到離開游戲的定義。以魔獸爭霸(frozenstone暴露年齡了)為例,局域網建立一個游戲,局域網內其他玩家加入此游戲,那么客戶端(加入游戲的玩家)只能自己離開游戲,所以此時對應的問題四出現的場景即加入游戲的玩家自己離開游戲。建立游戲的玩家可以選擇自己退出游戲(銷毀游戲)也可以選擇將不順眼的玩家踢出,前者自己退出游戲對應問題五場景。那么踢出其他游戲時就是對應此問題。即要想踢出玩家,只要斷開此玩家有服務端的連接即可。

解決方案:connectionToClient定義為服務端與客戶端的連接,但是只在服務端有效(只有在服務端此connectionToClient變量才有效)。所以此情況下只要調用connectionToClient的Disconnect方法並dispose即可。相反,connectionToServer定義為仍然為服務端與客戶端的連接,但是此方法只在local client有效,所以問題四也可以調用connectionToServer的Disconnect方法並dispose(此方法為測試,但從釋義上將可行,並且應該可以避免問題四和問題三)

問題七:drop Connection與destroyconnection時場景重新加載

出現場景以及解決方案:第一次dropconnection時場景會重新加載(並非在dropconnection的回調中調用重新加載,二十直接在dropconnection方法中就重新加載),之后再次調用則不會重新加載,目前看不到內部代碼,所以只能避免使用drop Connection,所以問題四中離開游戲時可以使用connectionToServer進行嘗試。

destroyconnection是每次調用都會加載場景,其實可以理解,每次重新創建游戲時回到最初也是可以接受的,不過要想不重新加載只能查看源碼了。

問題八:DontDestroyOnLoad問題

出現場景以及解決方案:在上一篇中開頭講過不要在自定義的LobbyManager中添加DontDestroyOnLoad,如果反復進行自身場景的加載就會一直不停的添加LobbyManager,即沒加載一次都會生成一個新的LobbyManager,而原來的又不會銷毀掉。而NetworkManager中已經存在,所以不要直接調用了,很多視頻demo中都加了此語句,但是他們那些demo都是一根筋跑到底,沒有出現重新加載的問題,所以此處要慎重,實現像調用可以看http://www.xuanyusong.com/archives/2938。而且如果添加了DontDestroyOnLoad還可能會出現中間

LobbyManager丟失的情況,之前做一個項目時,有一個插件也用到此方法,但是一重新加載就導致DontDestroyOnLoad對應的組件(腳本)獲取不到,但是editor中查看此組件(腳本)時存在的,所以慎重調用。

問題九:超上限加入游戲

出現場景以及解決方案:當游戲設置玩家為2,即使人數已滿,但仍然可以加入此游戲,但是加入后的游戲玩家顯示會有問題,所以只能在加入游戲前自己做判斷,不能靠unet自己處理。

問題十:ready后自動開始游戲

出現場景以及解決方案:當當前游戲中的所有玩家均ready后立馬開始游戲,即當前只有一個玩家,沒有達到游戲玩家數,若此玩家發送了准備(sendreadytobeginMessage),則服務端調用OnLobbyServerPlayersReady,並在其base方法中立馬開啟游戲,所以此處不能調用其base方法。

 

 

PS:到此為止,踩了很多坑,目找時間看看networkmanager源碼,unet的思路很不錯,如果發展好了將會使多人在線游戲開變得更加簡單。他與傳統的客戶端服務端分離不同(即客戶端與服務端是不同的類),他揉合在一起,但又設定好了不同的角色與權限,雖然理解起來比較費解,但是應用起來確實方便。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM