3atv精品不卡视频,97人人超碰国产精品最新,中文字幕av一区二区三区人妻少妇,久久久精品波多野结衣,日韩一区二区三区精品

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【持续更新】Pun多人在线游戏开发教程

發布時間:2023/12/18 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【持续更新】Pun多人在线游戏开发教程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、PUN介紹

1.入門

Photon Unity Networking(首字母縮寫 PUN)是一個 Unity 多人游戲插件包。它提供了身份驗證選項、匹配,以及快速、可靠的通過我們的 Photon 后端實現的游戲內通信。

PUN 輸出幾乎所有 Unity 支持的平臺,且有兩種選項:

注意:對于 Unity 5,兩個 PUN 插件包都含相同的文件。你可以買 PUN+ 來獲得 60 個月的 100 CCU 1 ,但客戶端上仍使用 PUN Free。

CCU,即 concurrent user 的縮寫,意為同時在線用戶,100CCU 就是最多容納 100 個同時在線用戶。

2.連接

PhotonNetwork.ConnectUsingSettings("v4.2");

上面的代碼是你需要連接并開始使用 Photon 功能的所有代碼。

ConnectUsingSettings 設置你的客戶端的游戲版本并使用一個由 PUN 設置向導寫入的配置文件,該配置文件保存在 PhotonServerSettings 里面。

3.匹配

//加入名為"someRoom"的房間
PhotonNetwork.JoinRoom("someRoom");
//如果沒有開放的游戲就會失敗。錯誤回調: OnPhotonJoinRoomFailed
//嘗試加入任何隨機游戲:
PhotonNetwork.JoinRandomRoom();
//如果沒有開放的游戲就會失敗。錯誤回調: OnPhotonRandomJoinFailed
//創建名為"MyMatch"的房間。
PhotonNetwork.CreateRoom("MyMatch");
//如果名為"MyMatch"的房間已存在就會失敗并調用:OnPhotonCreateRoomFailed

好朋友常常想要一起玩游戲。如果他們可以交流(例如 使用 Photon Chat,Facebook), 他們可以瞎編一個房間名并使用 JoinOrCreateRoom 方法。因為他們知道房間的名字,他們可以創建為他人不可見,像這樣:

RoomOptions roomOptions = new RoomOptions() { isVisible = false,maxPlayers = 4 };

PhotonNetwork.JoinOrCreateRoom(nameEveryFriendKnows, roomOptions, 5TypedLobby.Default);

?使用 JoinOrCreateRoom 方法,如果房間不存在就會創建該房間。如果房間滿了,OnPhotonJoinRoomFailed 會被調用 (如果你在某個地方實現了這個回調函數)。

4.游戲

GameObjects 可以被實例化為"networked GameObjects"? 。它們會有一個可以被識別的 PhotonView 組件和一個所有者(或控制者)。所有者會更新其他人。持續更新可以通過拖拽一個腳本到一個 PhotonView 的 Observed 字段被發送。需要更新的腳本必須實現 OnPhotonSerializeView 像這樣:

// 在一個"observed"? 腳本里:
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.isWriting)
{
Vector3 pos = transform.localPosition;
stream.Serialize(ref pos);
}

else
{
Vector3 pos = Vector3.zero;
stream.Serialize(ref pos); // pos 被填充。必須在某個地方使用
}

"networked GameObjects",網絡游戲對象,會在網絡上進行同步。
"observed",被觀察的。

客戶端可以為不見用的操作執行 Remote Procedure Calls :

// 定義一個可以被其他客戶端調用的方法:
[PunRPC]
public void OnAwakeRPC(byte myParameter)
{
//Debug.Log("RPC: 'OnAwakeRPC' Parameter: " + myParameter + " PhotonView: " + this.photonView);
}
// [...]
// 在別的某個地方調用該 RPC
photonView.RPC("OnAwakeRPC", PhotonTargets.All, (byte)1);

獨立于 GameObjects, 你也可以發送你自己的事件:

PhotonNetwork.RaiseEvent((byte)eventCode, (object)eventContent, (bool)sendReliable, (RaiseEventOptions)options)

Remote Procedure Calls,首字母縮寫為 RPCs,意為遠程過程調用。

二、初始設置

Photon Unity Networking (PUN)真的很容易設置。把 PUN 導入到一個新的項目中,然后 PUN 設置向導就會彈出來,如圖 0-1 所示。通過輸入一個郵箱地址來注冊一個新的(免費) Photon Cloud 帳號,或者復制粘貼一個已有的AppId 到該字段里。打完收工。如果你想要自己托管一個 Photon 服務器,點擊"skip",然后像如下描述的
那樣編輯 PhotonServerSettings 。

Photon Cloud 在后面的章節會有詳細解釋,你可以理解為云服務。

要連接,你只需在你的代碼中調用 PhotonNetwork.ConnectUsingSettings() 。如果你需要更多的控制,詳見下面的 Connect Manually 。

1.Photon 服務器設置

設置向導會添加一個 PhotonServerSettings 文件到你的項目,用來保存配置。如圖 0-2 所示,這也是去編輯服務器設置的地方。

你可以設置 AppId、Photon Cloud Region 和更多的。你的客戶端的 GameVersion? 是在代碼里被設置的。要選擇的最重要的選項是托管類型。

托管類型:通過 Hosting Type 你選擇處理你游戲的服務器和其他配置。Photon Cloud 和 Best Region 都涉及到我們管理的云服務。您可以選擇特定區域,
也可以讓客戶選擇最佳 ping 區域。如果你想在別的地方運行 Photon 服務器,選擇 Self Hosted 。安裝程序如下。或者,你的客戶可以在脫機模式。

最佳托管區域:最佳區域模式將在應用首次啟動的時候 ping 所有已知區域。由于這需要一點時間,結果被存儲在 PlayerPrefs。這會加快連接時間。你可以設置哪些區域可以忽略。在更少的區域分發客戶端會導致剩余區域的玩家更多。這在游戲流行之前是有益的。使用 PhotonNetwork.OverrideBestCloudServer() 來定義要使用的另一個區域。

自托管:如果你要自己托管一個 Photon 服務器,你應在 PhotonServerSettings 里面設置好它的地址和端口。當這些都被正確設置了,你可以在你的代碼里調用
PhotonNetwork.ConnectUsingSettings() 。確保您的客戶端可以到達輸入的地址。它可以是一個公共的、靜態的 IP 地址、主機名或在你的客戶端也使用的網絡中的任何地址。端口取決于所選協議,所以請確保這兩個字段匹配。清除該字段會將其重置為默認端口。

如果你為 iOS 開發游戲可以考慮閱讀 PUN and IPv6 和 how to setup Photon Server for IPv6。

協議:這里默認是(可靠的)UDP,但 Photon 還支持使用 TCP 以及將允許一個可靠的 HTTP 協議。我們建議你堅持 UDP。PUN+不支持 TCP。WebGL 導出只能使用WebSockets。

客戶端設置:客戶端設置部分包含了每個項目應設置的幾個選項。當你勾選 Auto-Join Lobby 時,PUN 將在連接(或離開房間)時自動加入默認大廳。Photon 的大廳提供當前房間的列表,這樣玩家可以選擇一個加入。這個默認是關閉的,因為更好的選擇是使用隨機匹配,就像所有的演示案例中使用的那樣。啟用 Enable Lobby Stats 來從服務器獲取大廳統計信息。如果游戲使用多個大廳,并且你想要向玩家展示每一個活動,則這個統計信息會很有用。每個大廳,你都可以獲取這些屬性: name、type、room 和 playercount。詳見PhotonNetworking.LobbyStatistics !這些設置在 PUN v1.60 版本引入。

遠程過程調用列表:Remote Procedure Calls 使你可以在一個房間里調用所有客戶端上的方法。PUN將這些方法的列表保存在 PhotonServerSettings。對于最初的設置,這是不相關的。詳見 Remote Procedure Calls。

2.手動連接

作為替代自動連接的 PhotonNetwork.ConnectUsingSettings() 方法你可以通過PhotonNetwork.ConnectToMaster() 方法來手動連接你自己的 Photon 服務器。當你托
管付費 Photon 服務器時這是有用的。對于 ConnectToMaster() ,你需要提供一個 masterServerAddress 和一個 port 參數。地址可以是你的 On-Premises DNS 名稱或一個 IP。它可以包括冒號后的端口(然后傳遞 0 作為端口)或您可以單獨通過端口。ConnectToMaster() 方法有更多的另外兩個參數 : "appID"和"gameVersion"。
兩者都只與 Photon Cloud 有關,并且當你自己托管 Photon 服務器時,可以設置為任何值。對于 Photon Cloud, 使用 ConnectUsingSettings() 方法。它涉及到我們的 Name Server 自動找到一個區域的主服務器。

DNS,即 Domain Name Server 的首字母縮寫,意為域名服務器,這里指你自己架構的服務器。

三、功能概述

1.PUN

PUN 由相當多的文件組成, 然而只有一個是真正重要的: PhotonNetwork 。這個類包含所有需要的函數和變量.。如果您有自定義要求,可以隨時修改源文件。

要從UnityScript中使用PUN,你需要把 "PhotonNetwork"和"UtilityScripts" 文件夾移動到 Assets\Plugins\文件夾。為了告訴你這個 API 如何工作,這里有幾個例子。

2.連接

PhotonNetwork 始終使用主服務器和一個或多個游戲服務器。主服務器管理當前可用的游戲并進行匹配。一旦房間被發現或創建,實際的游戲是在游戲服務器上完成的。所有的服務器都運行在專用的機器上,沒有所謂的玩家托管的服務器。你不必費心記住該服務器組織,PUN 會為你處理它。

PhotonNetwork.ConnectUsingSettings("v1.0");

上面的代碼是你需要連接并開始使用 Photon 功能的所有代碼。ConnectUsingSettings 設置你的客戶端的游戲版本并使用一個由 PUN 設置向導寫入的配置文件,該配置文件保存在 PhotonServerSettings 里面。你也可以修改文件PhotonServerSettings 屬性來連接到你自己的服務器。或者,使用 Connect() 方法來忽略該 PhotonServerSettings 文件。

3.版本控制

Photon 的負載均衡邏輯使用你的 AppId 來區分你的和他人的游戲。玩家也會被游戲版本分開, ConnectUsingSettings 的參數(見上文)。通過這種方式,您可以發布新功能的客戶端,而不破壞舊版本的游戲。由于我們不能保證不同 PUN 的版本之間相互兼容,PUN 把它自己的版本號添加到你的游戲里。更新 PUN 可能會從舊的版本中分離出新的客戶端,但不會打破老客戶端。

4.創建和加入游戲

接下來,你想加入或創建一個房間。下面的代碼展示了一些必要的函數:

//加入一個房間
PhotonNetwork.JoinRoom(roomName);
//創建這個房間。
PhotonNetwork.CreateRoom(roomName);
// 如果該房間已存在則會失敗并調用: OnPhotonCreateGameFailed
//嘗試加入任何隨機游戲:
PhotonNetwork.JoinRandomRoom();

//如果沒有匹配的游戲則會失敗并調用: OnPhotonRandomJoinFailed

在最好的情況下,您的游戲使用隨機配對。 JoinRandomRoom() 將嘗試加入任何房間。如果該方法失敗了(沒有房間接受另一個玩家),只需創建一個新的房間,并等到其他玩家隨機加入它為止。或者,您的客戶端可以獲得當前可用的房間列表。這是通過加入一個大廳來獲得的。大廳自動發送他們的房間列表到客戶端,并在時間間隔內更新(從而減少流量)。玩家不會看到對方,且無法溝通(以防止當您的游戲繁忙時出問題)。PhotonNetwork 插件可以在其連接時自動加入默認大廳。把 PhotonServerSettings 文件里的"Auto-Join Lobby"屬性開啟即可。當你的客戶端在一個大廳里時,房間列表會得到更新, 這些更新會緩存。如果需要的話,你可以通過 GetRoomList 方法來每一幀訪問房間列表。?

foreach (RoomInfo room in PhotonNetwork.GetRoomList())
{
GUILayout.Label(room.name + " " + room.playerCount + "/" + room.maxPlayers);

}

PhotonNetwork 使用多個回調函數來讓你的游戲知道狀態的變化,如“已連接”或“已加入一個游戲”。像往常對 Unity 一樣,回調可在任何腳本里實現。如果你的腳本擴展 Photon.PunBehaviour , 你可以單獨重寫每個回調。在這種情況下,您不必調用基類實現。

public override void OnJoinedRoom()
{
Debug.Log("OnJoinedRoom() called by PUN: " + PhotonNetwork.room.name);
}

你不需要擴展 PunBehaviour 。如果你在其本身身上實現它所有的回調函數也會起作用。它們也在枚舉 PhotonNetworkingMessage 中被列出和描述。這包括建立游戲房間的基礎知識。接下來是游戲中的實際交流。

5.發消息

在一個房間里,你可以發送網絡信息給其他連接的玩家。此外,您還可以發送緩沖消息,也將被發送到未來連接的玩家(以玩家生成為例)。

發送消息可以使用兩種方法。無論是 RPCs,還是通過在一個由 PhotonView 觀察的腳本里實現 OnSerializePhotonView 。

然而有更多的網絡互動。你可以監聽一些網絡事件的回調函數,如 OnPhotonInstantiate或 OnPhotonPlayerConnected ,并且你可以觸發其中一些事件,如 PhotonNetwork.Instantiate 。如果你被最后一段弄糊涂了,不要擔心,下一步我們會為這些主題逐個做解釋。

6.視覺同步組件

PhotonView 是一個用于發送消息(RPCs 和 OnSerializePhotonView )的腳本組件。你需要將 PhotonView 依附到游戲對象或預設上。請注意,PhotonView 和 Unity 的
NetworkView 非常相似。整個過程,你的游戲中需要至少一個 PhotonView,才能發送消息和可選的實例化/分配其他的 PhotonViews。如圖下圖所示,添加一個 PhotonView 到一個游戲對象,只需選擇一個游戲對象并使用: "Components/Miscellaneous/Photon View"。

7.觀察 Transform?

如果你將一個 Transform 綁定到 PhotonView 的觀察屬性上,你可以選擇同步位置、旋轉和尺度或玩家的這些屬性組合。這可以極大的幫助制作原型或小游戲。注意:任何觀察到的值變化將發送所有觀察到的值-而不只是發生變化的那個單一值。此外,更新的值是不平滑的或插值。

8.觀察 MonoBehaviour

PhotonView 可以被設置來觀察 MonoBehaviour。在這種情況下,腳本的OnPhotonSerializeView 方法會被調用。此方法被調用來寫入對象的狀態并讀取它,這取決于
腳本是否由本地玩家控制。下面簡單的代碼展示了如何用幾行代碼來增加角色狀態同步:

void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.isWriting)
{
//我們擁有這個玩家:把我們的數據發送給別的玩家

stream.SendNext((int)controllerScript._characterState);
stream.SendNext(transform.position);
stream.SendNext(transform.rotation);
}
else
{
//網絡玩家,接收數據
controllerScript._characterState = (CharacterState)(int)stream.ReceiveNext();
correctPlayerPos = (Vector3)stream.ReceiveNext();
correctPlayerRot = (Quaternion)stream.ReceiveNext();
}
}

9.觀察選項?

Observe Option 字段讓你選擇更新如何發送以及何時被發送。該字段還會影響到OnPhotonSerializeView 被調用的頻率。

Off 顧名思義,關掉。如果該 PhotonView 被保留為 RPCs 限定時可以很有用。

Unreliable 更新如是被發送,但可能會丟失。這個想法是,下一次更新很快到來,并提供所需的正確的/絕對的值。這對于位置和其他絕對數據來說是有利的,但對于像切換武器這樣觸發器來說是不好的。當用于同步的游戲對象的位置,它會總是發送更新,即使該游戲對象停止運動(這是不好的)。

Unreliable on Change 將檢查每一個更新的更改。如果所有值與之前發送的一樣,該更新將作為可靠的被發送,然后所有者停止發送更新直到事情再次發生變化。這對于那些可能會停止運動的以及暫時不會創建進一步更新的游戲對象來說是有利的。例如那些在找到自己的位置后就不再移動的箱子。

Reliable Delta Compressed 將更新的每個值與它之前的值進行比較。未更改的值將跳過以保持低流量。接收端只需填入先前更新的值。任何你通過 OnPhotonSerializeView 寫入的都會自動進行檢查并以這種方式被壓縮。如果沒有改變, OnPhotonSerializeView 不會再接收客戶端調用。該“可靠的”部分需要一些開銷,所以對于小的更新,應該考慮這些開銷。

現在開始,以另一種方式交流:RPCs。

10.遠程過程調用

Remote Procedure Calls ( RPC )使你可以調用"networked GameObjects"? 上的方法,對由用戶輸入等觸發的不常用動作很有用。
一個 RPC 會被在同房間里的每個玩家在相同的游戲對象上被執行,所以你可以容易地觸發整個場景效果就像你可以修改某些 GameObject 。

作為 RPC 被調用的方法必須在一個帶 PhotonView 組件的游戲對象上。該方法自身必須要被 [PunRPC] 屬性標記。

[PunRPC]
void ChatMessage(string a, string b)
{
Debug.Log("ChatMessage " + a + " " + b);
}

要調用該方法,先訪問到目標對象的 PhotonView 組件。而不是直接調用目標方法,調用 PhotonView.RPC() 并提供想要調用的方法名稱:

PhotonView photonView = PhotonView.Get(this);
photonView.RPC("ChatMessage", PhotonTargets.All, "jup", "and jup!");

你可以發送一系列的參數,但它必須匹配該 RPC 方法的定義。
這些是最基本的。詳情請閱讀 Remote Procedure Calls.?

11.Timing for RPCs and Loading Levels | 時機

RPCs 在指定的 PhotonViews 上被調用,并總是以接收客戶端上的匹配者為目標。如果一個遠程客戶端還沒有加載或創建匹配的 PhotonView,這個 RPC 就會丟失!

因此,丟失 RPCs 一個典型的原因就是當客戶端加載新場景的時候。它只需要一個已經加載有新游戲對象的場景的客戶端,并且其他客戶端不能理解這個 RPC(直到這些客戶端也加載了相同的場景)。

PUN 可以幫你解決此問題。只需在你連接之前設置PhotonNetwork.automaticallySyncScene = true 并在房間的主客戶端上使用PhotonNetwork.LoadLevel() 。這樣,一個客戶端定義了所有客戶端必須在房間/游戲中加載的關卡。

客戶端可以停止執行接收到的消息來防止 RPCs 丟失(這正是 LoadLevel 方法幫你做的)。當你得到一個 RPC 來加載一些場景,立即設置 isMessageQueueRunning = false 直到該內容被初始化。

private IEnumerator MoveToGameScene()
{
// 加載關卡前臨時禁用進一步的網絡信息處理
PhotonNetwork.isMessageQueueRunning = false;
Application.LoadLevel(levelName);
}

?禁用消息隊列將延遲傳入和傳出消息,直到隊列被解鎖。顯然,當你準備好要繼續的時候,打開隊列是非常重要的。

四、簡介

這個 PUN 基本教程是一個基于 Unity3D 的教程。它將教會你如何開發一個你自己的多人在線應用,當然,這是由 Photon Cloud 提供技術支持的。以及
怎樣使用 Animator? 來為角色做動畫。同時我們將一路學習許多重要的功能、提示和技巧,以獲得一個很好的以網絡為基礎的 PUN 開發路線總覽。

1.概述

本教程將從一個空的項目開始,在整個游戲創建過程中一步一步地引導您。一路上,概念將被解釋,以及常見的陷阱和為網絡游戲所做的設計考慮。為了不讓玩家四處走動,什么也不做,我們將實現一個基本的射擊系統,再加上玩家的健康管理,這將有助于我們支持變量通過網絡同步的解釋。

?* Photon Cloud 直譯為光子云,是類似于阿里云、騰訊云那樣的云端服務。本文將保留英文名稱,以方便讀者追本溯源。后面的教程中還有更詳細的解釋。

我們也將使用一個根據在房間里的玩家數量來自定義大小的競技場,競技場的大小正在根據當前游戲的玩家數據被調整。這將展示幾個關于自動同步場景功能、在加載不同場景時如何應對玩家、以及在這種情況下哪些容易出錯的技巧 :)

2.游戲目標

當游戲啟動時,用戶將在 UI 中見證連接協議并了解連接進展情況。當玩家加入或創建一個房間后,玩家進入一個可同時容納多達 4 個玩家的、可調整大小
的競技場。玩家可以跑動、轉彎以及射擊。每一個玩家發射的光束會影響其他被擊中玩家的健康值。當你的健康值是 0 時,那么你就游戲結束了并離開競技場。
然后出現在屏幕上的,是再次讓你開始或加入一個新的游戲,如果想要的話。

3.你需要知道的

本教程假定你只有使用 Unity 編輯器和編程的基礎知識。然而,為了集中介紹 Photon Networking 的新概念,讀者最好有一個良好的知識儲備以及創建
常規的、非網絡游戲的項目經驗。
示例代碼是用 C#寫的,但在 Unity 的腳本中同樣起作用。

* Photon Networking 即 Photon 的網絡架構,包括 Photon 服務器和客戶端。

創建一個新的項目,一般情況這是在使用教程時推薦的操作。然后一旦你同化了概念和設計模式,就能將其應用到自己的項目中。如圖 0-1 所示,該操作是比較簡單的。

4.導入 PUN 與設置

打開 Asset store? 并找到 PUN 插件并下載/安裝它。當你把所有 PUN 插件資源都導入完成時讓 Unity 重新進行編譯。如圖 0-2 所示,按照步驟操作即可。

PUN 設置向導是幫助你完成網絡設置,并提供了一個方便的方式來開始我們的多人游戲開發:The Photon Cloud!Cloud? Yes, Cloud. 這是一組我們可以用來為我們的游戲服務的 Photon服務器。我們將一點點做解釋。使用云端的"Free Plan "是免費且沒有任何義務的,所以現在我們只需輸入我們的郵件地址,向導就會執行它的魔法。如圖 0-3 所示,在 PUN 設置向導里面可以輸入郵件地址,也可以輸入"AppId",然后點擊"Setup Project "即可。

* Free Plan 免費計劃。

* AppId 應用的 Id,可以在 Photon 官網注冊后在儀表盤處獲得。

* Setup Project 設置項目。

新賬戶會立即獲得一個"AppId"。如果你的郵箱地址已經被注冊,你會被要求打開 Dashboard 。登錄即可獲得"AppId",將其復制粘貼到輸入框里面。當"AppId"被保存,我們就完成了這一步的設置。

如圖 0-4 所示,Photon Cloud 就像云團一樣。那么,這個"Photon Cloud" 到底是做什么的呢?!

* Dashboard 儀表盤,即網站的管理后臺,根據權限的不同會有不同的儀表盤界面。這里指用戶儀表盤。

本質上,它是一組運行 Photon 服務器的 PC 機。這個服務器"cloud"是由Exit Games? 維護并為您的多人游戲提供無憂的服務的。服務器會根據需求來進行添加,所以可以勝任處理任何數量的玩家。盡管 Photon Cloud 不是完全免費的,但是成本低,特別是和常規的托管主機相比。

Photon Unity Networking? 將為你處理 Photon Cloud,但這就是內部的情況:首先每個玩家連接到一個"Name Server"? 。服務器會檢查你客戶端使用的是哪個應用(使用 AppId 來識別),以及客戶端想要使用哪個區域的服務器。然后"Name Server"將客戶端轉發到一個主服務器。主服務器是一組區域服務器的集線器。它知道所有現有的游戲。任何時候一個游戲(或房間)被創建或加入,客戶端都會被轉發到其他所謂"Game Server" 的機器。PUN 的設置是非常簡單的,并且你無需擔心托管費用、性能或維護。一次也不需要。

* Exit Games 是國際領先的多平臺網絡游戲引擎供應商,其所開發的高性能 photon 引擎,通過 SDK 的形式,為游戲開發者開發實時多人應用提供了最佳的解決方案。目前該引擎已支持 Unity,iOS,Android,Flash和 HTML5 等多個平臺,它能幫助開發人員輕松實現瀏覽器、PC、Mac 或移動設備,包括 iPad、iPhone 和
Android 的游戲及應用中的多人實時功能。包括 OpenFeint,Bigpoint,Walt Disney,KONAMI 等游戲公司都在使用 Exit Games 的產品。

* Photon Unity Networking 即 PUN,是 Photon 為 Unity 定制的多人游戲網絡解決方案插件。

* "Name Server",即“名稱服務器”。

* "Game Server",即“游戲服務器”。

要記住 Photon Cloud 被構建來做"room-based games"? ,意味著每次匹配有限數量的玩家(譬如說:少于 10),這些玩家是與其他房間的玩家分開的。在一個房間里面(通常情況下)的每一個玩家都會接收到其他玩家發送的任何信息。房間外的玩家則不能溝通,所以我們總是想要他們盡快加入房間。加入一個房間最好的方式就是使用隨機匹配。我們只需請求服務器任何房間或帶特定屬性的房間即可。所有的房間都有一個名稱作為標識符。除非房間已滿或被關閉了,我們可以通過名稱來加入房間。為了方便玩家,主服務器可以為我們的應用程序提供一個房間列表。

* "room-based games" ,即基于房間的游戲,類似于 CF、LOL 之類的。

5.游戲大廳

您的應用的大廳存在于主服務器上,大廳中為您的游戲列出房間。在我們的例子中,我們將不使用大廳,如果有可用的房間就簡單地加入一個隨機房間,如果沒有可用的房間可以加入(房間可以有一個最大容量,所以他們可能是全滿了),則創建一個新的房間。

6.應用 IDs與游戲版本

如果每個玩家都連接到相同的服務器,必須有一個方法來區分你和其他的玩家。每個游戲(同樣適用于應用)在云端有它自己的"AppId"。玩家總是會遇到有同樣"AppId"的其他玩家,僅在客戶端里。也有一個"game version" ,您可以用來將不同客戶端版本的玩家分開。

7.區域

Photon Cloud 被組織在全球不同的區域,以防止玩家因為距離服務器太遠而潛在的不良連接。重要的是要理解這個概念,尤其是當與分散在各地的遠程團隊工作時。與您的隊友測試您的游戲也許不太可能,因為分散在不同地區。所以確保你強制所有想要和彼此互動測試員的區域是相同的。

8.?開發

每個部分涵蓋了項目開發階段的一個非常具體的部分,重要的是要按順序進行工作。腳本和 Photon 知識的假設水平也逐漸增加。

9.小結

所以,我們達成了一個工作系統,用戶可以在互聯網上互相對抗,具備基本的良好體驗要求。我們會學到如何控制 PUN,如何監聽 PUN 狀態和現狀,以及使最直觀的組件來輕易地協同 Animator 工作。我們也了解到 Photon 的一些有趣的功能,如自動場景同步,來創建原創的且強大的游戲。

要創造一個完整的游戲,還有很多事情要做,為上線做準備,但這僅僅是建立在我們所涵蓋的基礎之上。

* API 參考文檔,API,即 Application Programming Interface 的首字母縮寫,意為應用程序編程接口。

五、游戲大廳

1.連接到服務器、房間訪問和創建

讓我們先解決這個教程的核心,能夠連接到 Photon 云服務器,并加入一個房間或如有必要則創建一個。

1. 創建一個新場景,并將其保存為 Launcher.unity ;
2. 創建一個新的 C #腳本 Launcher ;
3. 在 Hierarchy 層次結構中創建一個空的 GameObject 游戲對象,命名為 Launcher;
4. 將 C #腳本 Launcher 添加到 GameObject Launcher 上;
5. 編輯 C #腳本 Launcher 成如下內容的樣子。

using UnityEngine; namespace Com.MyCompany.MyGame { public class Launcher : MonoBehaviour { #region Public Variables //公共變量區域 #endregion #region Private Variables //私有變量區域 /// <summary> /// 此客戶端的版本號。用戶通過 gameversion 彼此分離 (這讓你可以做出突破性 的改變). /// </summary> string _gameVersion = "1"; #endregion #region MonoBehaviour CallBacks //回調函數區域 /// <summary> /// 在早期初始化階段里被 Unity 在游戲對象上調用的 MonoBehaviour 方法。 /// </summary> void Awake() { // #Critical | 極重要 //我們不加入大廳。沒有必要加入一個大廳來獲得房間列表。 PhotonNetwork.autoJoinLobby = false; // #Critical | 極重要 //這樣可以確保我們可以在主客戶端上使用PhotonNetwork.LoadLevel()方法, 并且在相同房間里的所有客戶端都會自動同步它們的關卡。 PhotonNetwork.automaticallySyncScene = true; } /// <summary> /// 在初始化階段里被 Unity 在游戲對象上調用的 MonoBehaviour 方法。 /// </summary> void Start() { Connect(); } #endregion #region Public Methods //公共方法 /// <summary> /// 啟動連接進程。 /// - 如果已經連接,我們試圖加入一個隨機的房間 /// - 如果尚未連接,請將此應用程序實例連接到 Photon 云網絡 /// </summary> public void Connect() { // 我們檢查是否連接,如果我們已連接則加入,否則我們啟動連接到服務器。 if (PhotonNetwork.connected) { // #Critical | 極重要 -我們需要在這個點上企圖加入一個隨機房間。如果失 敗,我們將在 OnPhotonRandomJoinFailed()里面得到通知,這樣我們將創建一個房間。 PhotonNetwork.JoinRandomRoom(); }else{ // #Critical | 極重要 -我們必須首先連接到 Photon 在線服務器。 PhotonNetwork.ConnectUsingSettings(_gameVersion); } } #endregion } }

6. 保存該 C#腳本 Launcher

讓我們回顧一下目前這個腳本中的內容,首先從一般的 Unity 角度來看,然后看看我們制作的 PUN 的具體調用。

命名空間:? 雖然不是強制性的,給予你的腳本適當的 namespace 可以防止與其他資源和開發者發生沖突。萬一另一個開發者也創建了一個 Launcher 類呢?Unity 將報錯,并且您或該開發人員將不得不為 Unity 重命名該類,以允許執行該項目。如果沖突來自您從資源商店下載的資產,這可能是棘手的。現在, Launcher 類實際上是Com.MyCompany.MyGame.launcher,在引擎下不太可能有其他人會使用和我們一樣的命名空間,因為你擁有這個域名,并且使用倒置域名慣例作為命名空間使你的工作安全而又組織良好。Com.MyCompany.MyGame 應該被你自己的反向域名和游戲名替換,這是一個值得遵循的良好約定。

?MonoBehaviour Class: 請注意我們用 MonoBehaviour 派生出我們的類,這從本質上把我們的類轉化成一個Unity Component,從而使我們可以把這些類添加到GameObject或Prefab上作為組件。一個繼承 MonoBehaviour 的類可以訪問許多非常重要的方法和屬
性。在這個案例中我們使用了兩個回調方法,Awake()和 Start()。

PhotonNetwork.autoJoinLobby: 在 Awake()方法中,我們設置 PhotonNetwork.autoJoinLobby 為 false,因為我們不需要 Lobby 功能,我們只需要獲取當前房間的列表。強制設置通常是一個好主意,因為在同一個項目中你可以有另一個確實想要 autoJoin 該 Lobby 的場景,這樣在轉換不同的方法時就不會有問題了。

PhotonNetwork.ConnectUsingSettings(): 在 Start()方法中我們調用了我們的公共函數 connect() 來使用
PhotonNetwork.ConnectUsingSettings()連接 PUN云。請注意 _gameVersion 變量代表你的 gameversion。你應該保持該參數為 "1" 直到你在你的上線項目中產生突破性改變。這里要記住的重要信息是 PhotonNetwork.ConnectUsingSettings()是你的游戲網絡化以及連接 connect 到 Photon 云的起點。

PhotonNetwork.automaticallySyncScene: 我們的游戲將有一個根據玩家的數量調整大小的競技場,并確保每一個已連接的玩家所
加載的場景都是一樣的,我們將使用 Photon 提供的很方便的功能:PhotonNetwork.automaticallySyncScene當該屬性被設置為 true,MasterClient 可以調用 PhotonNetwork.LoadLevel(),這樣所有已連接的玩家將自動加載相同的關卡。

在這一點上,你可以保存 Launch 場景,并打開 PhotonSettings (從 Unity 的菜單窗口 Window/Photon Unity Networking/Highlight Photon Server Settings 中選擇它),如下圖所示,我們需要像這樣設置調試級別為 Full:

然后我們可以點擊Play。你應該在Unity控制臺看到好幾十條日志。特別是“Connected to masterserver.”這條記錄表明我們現在已連接并準備加入一個房間。

總是測試潛在的錯誤是寫代碼的一個好習慣。在這里我們假設計算機已連接到互聯網,但如果計算機沒有連接到互聯網會發生什么?讓我們探尋一下。關閉您計算機的互聯網并運行該場景。你應該看到在 Unity 控制臺出現一個錯誤"Connect() to 'ns.exitgames.com'
failed: System.Net.Sockets.SocketException: No such host is known"。理想情況下,我們的腳本應該意識到這個問題,并優雅地應對這些情況,并提出一個反應性的經驗,無論出現什么情況或問題。讓我們現在處理這兩種情況,并在我們的 Launcher 腳本內通知我們確實已連接或沒有連接到 PUN 服務器。這將是完美的 PUN 回調介紹。

2. PUN 回調

PUN 的回調是非常靈活的,且提供了三種非常不同的實現。讓我們覆蓋所有三種方法的學習,我們會根據情況來選擇一個最適合的。

"magic" methods | “魔術”方法

使用常規 MonoBehaviour 時,你可以簡單地創建私有方法。

void OnConnectedToMaster()
{
Debug.Log("DemoAnimator/Launcher: OnConnectedToMaster() was called by PUN");

}

這是神奇的因為任何 MonoBehaviour 可實現該方法或來自 PUN 的任何消息。它遵循和 Unity 正在發送到 MonoBehaviours 的諸如 Awake()或 Start()這樣的主方法一樣的原則。但是我們不會用這個,因為如果你拼錯了這些“神奇”的方法,也不會告知你的錯誤,所以這是一個非常實用的快速實現,但只有在知道每個方法的確切名稱,并且你很熟悉和擅長調試技術來快速找到這些問題時。

Using IPunCallbacks and IPunObservable Interfaces | 使用接口

PUN 提供了兩種 C#接口 Interfaces ,你可以在你的類里面實現它們。 IPunObservable和 IPunCallbacks 。

這是一個非常安全的方法,以確保一個類符合接口的所有,但會強制開發人員實現接口的所有聲明。大多數好的腳本編輯器會使這個任務很容易,如上圖使用 MonoDevelop 所演示那樣。但是,該腳本可能會有很多什么都不做的方法,但必須全部實現來讓 Unity 編譯器高興。所以這是當你的腳本是要大量使用所有或大部分的 PUN 功能時適用。我們確實要在接下來的數據序列化的教程中使用 IPunObservable 接口。

Using Photon.PunBehaviour | 使用 Photon.PunBehaviour

最后的技術,這是一個我們將經常使用,且是最方便的。與其從 MonoBehaviour 中創建一個派生類,我們將從Photon.PunBehaviour 派生類,如下圖所示,因為它暴露了特定的屬性和虛函數 virtual methods 供我們使用以及方便我們重寫 override。這是很實用的,因為我們可以肯定的是,我們沒有任何的錯別字,并且我們不需要實現所有的方法。

注:在重寫時,大多數腳本編輯器將默認實現基本調用和自動為你填充,但在我們的案例中我們并不需要,所以就Photon.PunBehaviour 一般規則,不要調用基本方法。注意:重寫的另一大好處是,只需懸停在方法的名稱上你就可以得到上下文的幫助。所以讓我們來實踐一下 OnConnectedToMaster()和 OnDisconnectedFromPhoton()PUN 回調。

1. 編輯 C #腳本 Launcher
2. 把基類從 MonoBehaviour 修改成 Photon.PunBehaviour

public class Launcher : Photon.PunBehaviour {

3. 在類的結尾添加以下兩種方法, 在域 region Photon.PunBehaviour CallBacks 內更加明了。

#region Photon.PunBehaviour CallBacks //域可以使代碼結構更加清晰
public override void OnConnectedToMaster()
{
Debug.Log("DemoAnimator/Launcher: OnConnectedToMaster() was called by
PUN");
}

public override void OnDisconnectedFromPhoton()
{
Debug.LogWarning("DemoAnimator/Launcher:
OnDisconnectedFromPhoton() was called by PUN");
}
#endregion

4. 保存 Launcher 腳本。
現在,如果我們玩這個場景,無論有或沒有互聯網,我們都可以采取適當的步驟來通知玩家,并進一步進入邏輯。我們將在下一節開始構建 UI 時處理這個問題。現在我們將處理成功的連接:

所以,我們在 OnConnectedToMaster()方法中追加如下的調用:

// #Critical | 極重要: 我們首先嘗試要做的就是加入一個潛在現有房間。如果有,很好,
否則,我們將調用回調 OnPhotonRandomJoinFailed()
PhotonNetwork.JoinRandomRoom();

并且如注釋所說那樣,如果加入一個隨機房間的企圖失敗了我們需要被通知,在該情況下我們需要創建一個房間,所以我們在腳本里實現 OnPhotonRandomJoinFailed()PUN 回調,并使用 PhotonNetwork.CreateRoom() 來創建一個房間,以及,你已經猜到了,相關
的 Pun 回調 OnJoinedRoom()將在我們實際加入一個房間時通知你的腳本:

public override void OnPhotonRandomJoinFailed (object[] codeAndMsg)
{
Debug.Log("DemoAnimator/Launcher:OnPhotonRandomJoinFailed() was called by PUN. No random room available, so we create one.\nCalling: PhotonNetwork.CreateRoom(null, new RoomOptions() {maxPlayers = 4}, null);");
// #Critical | 極重要: 我們加入一個隨機房間失敗,也許沒有房間存在或房間已滿。別擔心,我們創建一個新的房間即可。
PhotonNetwork.CreateRoom(null, new RoomOptions() { maxPlayers = 4 }, null);
}
public override void OnJoinedRoom()
{
?Debug.Log("DemoAnimator/Launcher: OnJoinedRoom() called by PUN. Now this client is in a room.");
}

現在如果你運行該場景,你應該遵循連接到 PUN 的邏輯序列,試圖加入一個現有的房間,否則創建一個新的房間并加入那個新創建的房間。

在教程的這個點上,因為我們現在已經覆蓋了連接和加入一個房間的關鍵部分,有幾件事情不是很方便,并且這些問題需要盡早解決。這些問題都不是真正關系到學習 PUN 的,但重要的是從整體的角度來看。

Expose variables in Unity Inspector | 暴露變量

你可能已經知道了這一點,但假如你不知道,MonoBehaviours 自動暴露自己的公共屬性到 Unity 的 Inspector 窗口。這在 Unity 里是一個非常重要的概念,在我們的情況下,我們要修改定義 LogLevel 的方式,并制作一個公共變量,這樣我們不接觸代碼本身就可以
完成設置。

/// <summary>
/// PUN 的日志等級。
/// </summary>
public PhotonLogLevel Loglevel = PhotonLogLevel.Informational;

并且在 Awake()方法里面我們將進行接下來的修改:

// #NotImportant | 不重要
//強制為 LogLevel 賦值
PhotonNetwork.logLevel = Loglevel;

所以,現在我們不強制腳本是某種類型的 LogLevel,我們只需要在 Unity 的 Inspector里進行設置,然后運行,不需要打開腳本,編輯,保存,等待 Unity 重新編譯,最后再運行。這是一個更富有成效和靈活的方式。

我們會為每個房間的最大玩家數量做同樣的事情。在代碼里硬編碼并不是最佳實踐,相反,讓它作為一個公共變量,這樣我們就可以決定和調試那個數值而無需重新編譯。在類聲明的開頭,在公共變量 Public Variables 區域我們添加:

/// <summary>
/// 每個房間的最大玩家數。當一個房間滿了,它不能加入新的玩家,所以新的房間將被創建。如圖 11 所示,該公共變量將暴露在 Unity 的 Inspector 窗口面板中。
/// </summary>
[Tooltip("每個房間的最大玩家數。當一個房間滿了,它不能加入新的玩家,所以新的房間將被創建。")]
public byte MaxPlayersPerRoom = 4;

然后我們修改 PhotonNetwork.CreateRoom()調用,并使用這個新的公共變量來取代我們之前使用的硬編碼的數字。

// #Critical | 重要:我們沒能進入一個隨機的房間,也許沒有這樣的房間存在,或者都滿了。不用擔心,我們創造一個新的房間。
PhotonNetwork.CreateRoom(null, new RoomOptions() { maxPlayers =MaxPlayersPerRoom }, null);

六、游戲大廳UI

這部分將著重于為大廳創建用戶界面(UI)。它將保持非常基本的,因為它不是真正關系到網絡。

1. The Play Button | 開始按鈕

目前我們的游戲大廳是自動把我們連接到一個房間,這是很好的早期測試,但我們真的希望讓用戶選擇是否以及何時開始游戲。所以我們將簡單地為此提供一個按鈕即可。

1. 打開上一節創建的 Launcher 場景;
2. 使用 Unity 菜單'GameObject/UI/Button'創建一個 UI 按鈕, 并命名為: Play Button注意該操作在創建按鈕的同時在場景的Hierarchy 層級中創建了一個 Canvas 幕布和一個 EventSystem 事件系統游戲對象,所以我們不必手動創建;
3. 編輯 Play Button 按鈕的子類 Text 的變量為"Play";

4. 選擇 Play Button 并找到 Button 組件里的 On Click () 部分;
5. 點擊'+'小按鈕來添加一個條目;
6. 把 Launcher 游戲對象從 Hierarchy 層級中拖進該字段;
7. 在下拉菜單中選擇 Launcher.connect() ,這樣我們就把該按鈕和 Launcher 腳本連接到了一起,當玩家按下該按鈕的時候就會從 Launcher 腳本中調用"Connect()"方法;
8. 打開 Launcher 腳本;
9. 移除我們在 Start()方法里調用 Connect() 的那一行代碼;
10. 保存 Launcher 腳本和場景;
如果你現在點擊運行,你會發現不會再連接直到你點擊該按鈕。

2. The Player Name | 玩家名稱

典型游戲的另一個重要的最低要求就是讓用戶輸入他們的名字,這樣其他玩家就知道和他們一起玩游戲的是誰了。我們將為這個簡單的任務添加一點曲折,通過使用 PLayerPrefs記住名字的值,這樣當用戶打開游戲,我們可以恢復該名字是什么。這是一個非常方便和非常重要的功能,實現在您的游戲的許多領域可以為用戶帶來非常好的體驗。讓我們首先創建該腳本,將用來管理和記住玩家的名稱,然后創建相關的用戶界面。

創建 PlayerNameInputField:

1. 創建一個新的 C #腳本,命名為 PlayerNameInputField;
2. 這里是它的全部內容。編輯并保存相應的 PlayerNameInputField 腳本;

using UnityEngine; using UnityEngine.UI; using System.Collections; namespace Com.MyCompany.MyGame { /// <summary> /// 玩家姓名輸入字段。讓用戶輸入自己的名字,就會出現在游戲中的玩家頭上。 /// </summary> [RequireComponent(typeof(InputField))] public class PlayerNameInputField : MonoBehaviour { #region Private Variables //私有變量區域 //保存 PlayerPref 鍵來避免錯別字 static string playerNamePrefKey = "PlayerName"; #endregion #region MonoBehaviour CallBacks //回調函數區域 /// <summary> /// 初始化階段被 Unity 在游戲對象上調用的 MonoBehaviour 方法 /// </summary> void Start () { string defaultName = ""; InputField _inputField = this.GetComponent<InputField>(); if (_inputField!=null) { if (PlayerPrefs.HasKey(playerNamePrefKey)) { defaultName = PlayerPrefs.GetString(playerNamePrefKey); _inputField.text = defaultName; } } PhotonNetwork.playerName = defaultName; } #endregion #region Public Methods //公共方法區域 /// <summary> ///設置該玩家的名字,并把它保存在 PlayerPrefs 待用。 /// </summary> /// <param name="value">玩家姓名</param> public void SetPlayerName(string value) { // #Important | 重要 PhotonNetwork.playerName = value + " "; //強制加一個拖尾空格字符串, 以免該值是一個空字符串,否則 playerName 不會被更新。 PlayerPrefs.SetString(playerNamePrefKey,value); } #endregion } }

讓我們來一起分析這個腳本:
RequireComponent(typeof(InputField)):我們首先要確保這個腳本強制執行 InputField,因為我們需要它,這是一個非常
方便快捷的方法,以保證該腳本的使用沒有問題。
PlayerPrefs.HasKey(), PlayerPrefs.GetString()和 PlayerPrefs.SetString():PlayerPrefs 是一個簡單的配對條目查找列表(就像兩列的 Excel 表),一列是 key ,一列是 Value 。鍵 key 是一個字符串,并且是完全任意的,你決定如何命名且您將需要在整個開發中堅持該命名。正因為如此,總是把你的 PlayerPrefs 鍵只保存在一個地方是很有道理的,一個方便的方法是使用一個[Static|變量名,因為在游戲中它不會隨著時間的推移而改變,而且每次都是一樣的。所以,邏輯是非常直接的。如果 PlayerPrefs 有已給定的鍵,我們可以獲取它并在啟動該功能的時候直接為其注入值,在我們的案例中,在我們啟動和編輯過程中用這個來為InputField 填入值,我們用 InputField 的當前值來設置 PlayerPref,然后我們確定它是在用戶設備本地存儲供以后檢索(下一次用戶將打開這個游戲)。

PhotonNetwork.playerName:這是這個腳本的要點,在網絡上設置玩家的名稱。該腳本在兩個地方使用玩家名稱,一次是在 Start()過程中,在檢查完該名稱是否被存儲在 PlayerPrefs 里面之后,以及在公共方法 SetPlayerName() 里面。現在,并沒有調用這個方法,我們需要綁定 InputField 的OnValueChange()來調用 SetPlayerName() ,這樣每次用戶編輯該 InputField 時我們都把它記錄下來。我們只有在用戶按下開始按鈕后才能執行該操作,這取決于你,但是這是一個更明智的腳本,所以讓我們為了清楚起見保持其簡單。這也意味著,無論用戶會做什么,輸入將被記住,這往往是所需的行為。

3. Creating the UI for the Player's Name | 創建 UI

1. 確保你仍然在 Launcher 場景里面,如果不在,打開該場景;
2. 使用 Unity 的菜單'GameObject/UI/InputField'創建一個 UI InputField , 將該游戲對象 GameObject 命名為 Name InputField;
3. 設置 RectTransform 里面的 PosY 值到 35 ,這樣它會在 Play Button 按鈕上面;
4. 找到 Name InputField 的子類 PlaceHolder ,并將它的 Text 值設置為"Enter your Name...";
5. 選擇 Name InputField GameObject;
6. 把我們剛剛創建好的 PlayerNamerInputField 腳本添加到該對象上;
7. 找到 InputField 組件內的 On Value Change (String) 部分;
8. 添加那個'+'號來添加一條綁定事件;

9. 拖拽 Launch 的 PlayerNamerInputField 組件到該字段進行綁定;
10. 在 Dynamic String 部分的下拉菜單中選擇 PlayerNameInputField.SetPlayerName();
11. 保存該場景(Ctrl+S);
現在你可以點擊運行了,輸入你的名字,停止運行,再次運行,你之前輸入的名字就會出現。
我們越來越接近目標了,但是在用戶體驗 User Experience 方面我們缺少對連接進度的反饋,以及在連接和加入房間出錯時的反饋。

4. The Connection Progress | 連接進程

我們將在這里保持簡單,并隱藏名稱字段和開始按鈕,并用一個簡單的文本“Connecting...”在連接期間代替,并在需要時切換回來。
要實現這樣的效果,我們將開始按鈕和名稱字段歸為一組,以便我們簡單地激活和停用該組。后面更多的功能可以添加到該組,且它不會影響到我們的邏輯。
1. 老規矩,確保你仍然在 Launcher 場景;
2. 使 用 Unity 菜 單'GameObject/UI/Panel' 創 建一 個 UI Panel, 將 該游 戲對 象GameObject 命名為 Control Panel;
3. 從 Control Panel 中刪除其 Image 和 Canvas Renderer 組件,我們不需要這個面板的任何視覺效果,我們只關心它的內容;

4. 把 Play Button 和 Name InputField 拖拽到 Control Panel 上;
5. 使用 Unity 菜單'GameObject/UI/Text'創建一個 UI Text, 將其命名為 Progress;Label 不要擔心它會干擾視覺,我們將在運行時激活/關閉他們;
6. 選擇 Progress Label 的 Text 組件;
7. 把 Alignment 設置成 center align 和 middle align;
8. 把 Text 的值設置成"Connecting...";
9. 把 Color 設置成白色或任何可以背景中凸顯出來的顏色;
10. 保存該場景。
在這一點上,想要測試,您可以簡單地啟用/禁用控制面板 Control Panel 和進度標簽Progress Label ,看看事情在不同的連接階段會如何顯示。現在讓我們編輯腳本來控制這些兩個游戲對象的激活。

1. 編輯腳本 Launcher
2. 在公共屬性區域 Public Properties Region 中添加以下兩個屬性

[Tooltip("讓用戶輸入名稱、連接和開始游戲的 UI 面板")]
public GameObject controlPanel;
[Tooltip("通知用戶連接正在進行中的 UI 標簽")]
public GameObject progressLabel;

3. 把以下的代碼添加到 Start()方法

progressLabel.SetActive(false);

controlPanel.SetActive(true);

4. 在 Connect()方法的開始處添加下面的代碼

progressLabel.SetActive(true);
controlPanel.SetActive(false);

5.在 OnDisconnectedFromPhoton()方法開始處添加如下代碼

progressLabel.SetActive(false);
controlPanel.SetActive(true);

6. 保存 Launcher 腳本并等待 Unity 完成編譯
7. 確保你仍然在 Launcher 場景
8. 在 Hierarchy 層級中選擇游戲對象 GameObject Launcher
9. 從 Hierarchy 層級中把 Control Panel 和 Progress Label 拖拽到 Launcher 組件里的對應字段。
10. 保存場景
現在,如果你運行該場景。你只會看到控制面板 Control Panel,只要你點擊開始游戲,進程標簽 Progres Label 就會出現。
現在,我們的游戲大廳部分暫時做好了。為了進一步添加功能到大廳,我們需要切換到游戲本身,并創建各種場景,以便我們終于可以在加入一個房間時加載正確的關卡。我們將在下一節和之后小節中完成,我們將最終完成大廳系統。

七、游戲場景

本節介紹了玩家將要進行游戲的各種場景的創建。每個場景將致力于滿足特定數量的玩家,越來越大,以適應所有玩家,給他們足夠的空間走動。在本教程中,我們將實現基于玩家數量加載正確關卡的邏輯,這將使用一個達到關卡的約定,關卡名稱以這種格式“Room for X”命名,X 代表玩家的數量。

1.First Room Creation | 第一個房間創建

1. 創建一個新場景, 保存并命名為 "Room for 1" ;
2. 創建一個立方體 Cube 并命名為 " floor";

3. 將其放置在 0,0,0。這是重要的,因為我們的邏輯系統將生成的玩家放置在中心之上(0,x,0);
4. 把 floor 放大到 20,1,20。

這就足夠一個可玩的水平了,但一些墻將使玩家在該范圍內。只需創建更多的立方體,
調整其位置、旋轉和縮放來使它們作為墻壁。以下是四墻壁與地板的游戲對象的位置和規模。

做到這里別忘了保存 Room For 1 場景。

2.Game Manager Prefab | 游戲總管預制體

在所有情況下,用戶界面的最低要求是能夠退出房間。為此,我們需要一個 UI Button按鈕,但我們也需要一個調用 Photon 來使本地玩家離開房間的腳本,所以讓我們開始創建我們將調用的 Game Manager 預設,和第一個任務將處理退出本地玩家當前所在的房間。
1. 創建一個新的 C #腳本 GameManager;
2. 在場景中創建一個空游戲對象,命名為 Game Manager;
3. 把 GameManager 腳本拖拽到 Game Manager 游戲對象上;
4. 通過把場景層級 Hierarchy 中的 Game Manager 拖拽到資源瀏覽器 Assets 中將其轉化成預設,而層級中的原型就會變成藍色的;
5. 編輯 GameManager 腳本;
6. 用以下的代碼進行替換:

using System; using System.Collections; using UnityEngine; using UnityEngine.SceneManagement; namespace Com.MyCompany.MyGame { public class GameManager : MonoBehaviour { #region Photon Messages //Photon 消息區域 /// <summary> ///當本地玩家離開房間時被調用。我們需要加載 Launcher 場景。 /// </summary> public void OnLeftRoom() { SceneManager.LoadScene(0); } #endregion #region Public Methods //公共方法區域 public void LeaveRoom() { PhotonNetwork.LeaveRoom(); } #endregion } }

7. 保存 GameManager 腳本。

因此,我們創建一個公共方法 LeaveRoom() 。它的作用是顯式地讓本地玩家離開 Photon網絡房間,我們用我們自己的公共方法來將其包裝使其抽象化。我們可能會想在稍后的階段實現更多的功能,如保存數據,或插入一個用戶將離開游戲等的確認步驟。基于我們的游戲要求,如果我們不在一個房間里我們需要顯示 Launcher 場景,所以我們要監聽 OnLeftRoom() Photon 回調并加載游戲大廳場景 Launcher ,該場景在 Build settings 的場景列表中的索引為 0,而我們將在這一節的 Build Settings Scene List 部分里進行設置。
但為什么要制作成預制體呢?因為我們的游戲需求意味著同一個游戲有幾個不同的場景,所以我們需要重用這個游戲管理器 Game Manager 。在 Unity 中重用對象的最佳方式是把他們變成預設。

接下來,讓我們創建 UI Button 按鈕,用來調用我們的 GameManager 的 LeaveRoom()方法。

3.Quit Room Button Prefab | 退出房間按鈕預設

再次,就像 Game Manager 一樣,長遠來看我們會有許多不同的場景需要這個功能,所以睿智的做法是未雨綢繆并將該按鈕做成一個預設,這樣我們就可以重用它了,并且在將來需要改變它的時候在一個地方做修改即可。
1. 確保你在場景 Room for 1 里面;
2. 使用 Unity 菜單創建'GameObject/UI/Panel'一個 UI Panel,并命名為 Top Panel ;
3. 移除 Image 和 Canvas Renderer 組件清空這個面板。如果你覺得不刪除好看點的話也可以保留,這只是為了審美而已;
4. 同時按住 Shift 和 Alt 設置垂直錨點預置到 top ,設置水平錨點預置到 stretch 。RectTransform 錨點需要一些經驗去適應它,但它是值得的;
5. 把 RectTransform 的高度設置成 50;
6. 右鍵點擊該面板的游戲對象 Top Panel 并添加一個 UI/Button,命名為 Leave button;
7. 選擇 Leave button 的子類 Text, 并將它的文本設置為 Leave Game;
8. 如下圖所示,把 OnClick 按鈕事件連接到 Hierarchy 層級中的 Game Manager 實例來調用 LeaveRoom() 。

1. 通過把 Leave button 從場景 Hierarchy 層級中拖拽到 Assets 資源瀏覽器將其轉變成一個預設,它將在 Hierarchy 層級中變成藍色;
2. 保存該場景及項目。

4.Other Rooms Creation | 其他房間創建

現在,我們已經有一個房間做好了,讓我們復制 3 次,并適當地為其命名(他們應該在被復制的時候被 Unity 命名過了):
? Room for 2
? Room for 3
? Room for 4
查找以下位置、旋轉和規模的變化,以加快這一重復過程。

Room for 2:

Room for 3:

地面大小縮放: 50,1,50

Room for 4:
地面大小縮放: 60,1,60

5.Build Settings Scenes List | 構建設置場景列表

在編輯和發布時,項目的良好運行至關重要,我們需要在 Build Settings 中添加所有這些場景,以便 Unity 在構建應用程序時包含它們。
1. 通過 Unity 菜單"File/Build Settings"打開 Build Settings;
2. 如下圖所示,拖拽所有的場景到列表,Launcher 場景必須保持在第一,因為 Unity默認將為玩家加載和展示列表中的第一個場景。

現在我們有了基本的場景設置,我們終于可以開始把一切串聯起來。讓我們在下一節做這個。

八、Game Manager&Levels

本節介紹了添加功能來處理基于目前在房間里玩的玩家數量來加載不同的關卡。

1.Loading Arena Routine | 加載競技場例行程序

我們已經創建了 4 個不同的房間,我們以一定的規律來它們命名,場景名稱的最后一個字符是玩家的數量,所以現在很容易把房間里的玩家數量和相關場景綁定起來。這是一個非常有效的技術被稱為“convention over configuration”,就是方法匹配玩家數量到對應的房間。然后,我們的腳本將在該列表中查找,并返回一個場景,其中的名稱并不重要。"Configuration"需要更多編寫,這就是為什么我們會在這里使用"Convention",這讓我們得到更快的編寫代碼,而無需在我們的代碼中寫無關的功能。

1. 打開 GameManager 腳本;

2. 讓我們添加一個新的方法到一個新的專用于私有方法的區域。不要忘記保存GameManager 腳本;

#region Private Methods //私有方法區域 void LoadArena() { if ( ! PhotonNetwork.isMasterClient ) { Debug.LogError( "PhotonNetwork : Trying to Load a level but we are not the master Client" ); } Debug.Log( "PhotonNetwork : Loading Level : " + PhotonNetwork.room.PlayerCount ); PhotonNetwork.LoadLevel("Room for "+PhotonNetwork.room.playerCount); } #endregion

3. 保存 GameManager 腳本。
當我們調用這個方法時,我們將根據我們所在房間的 PlayerCount 屬性加載合適的房間。

這里有兩件事值得注意,這是非常重要的。
? 只有在我們是主客戶端的時候才能調用 PhotonNetwork.LoadLevel()。所以我首先使用 PhotonNetwork.isMasterClient 來檢查我們是否是主客戶端。檢查這個是調用者的責任,我們將在這一節的下一部分詳細講。
? 我們使用 PhotonNetwork.LoadLevel()來加載我們想要加載的關卡,我們不會支持使用 Unity,因為我們想要依賴 Photon 來在所有已連接的該房間內的客戶端上加載這個關卡 ,因為我們為這個游戲啟用了
PhotonNetwork.automaticallySyncScene。
現在我們已經有了加載正確關卡的方法,讓我們把這個和玩家的連接和斷開連接綁定起來。

2.Watching Players Connection | 觀察玩家連接

目前,我們的 GameManager 腳本是一個常規的 MonoBehaviour,我們已經在本教程的前面部分研究了得到 Photon 回調的各種方法,現在 GameManager 需要監聽玩家連接與
斷開。讓我們來實現這個。
1. 打開 GameManager 腳本;
2. 把基類從 MonoBehaviour 修改成 Photon.PunBehaviour:

public class GameManager : Photon.PunBehaviour {

3. 讓我們來添加下面的 Photon 回調信息并保存 GameManager 腳本:

#region Photon Messages //Photon 消息區域 public override void OnPhotonPlayerConnected( PhotonPlayer other ) { Debug.Log( "OnPhotonPlayerConnected() " + other.NickName ); //如果你是正在連接的玩家則看不到 if ( PhotonNetwork.isMasterClient ) { Debug.Log( "OnPhotonPlayerConnected isMasterClient " + PhotonNetwork.isMasterClient ); //在 OnPhotonPlayerDisconnected 之前調用LoadArena(); //加載競技場 } } public override void OnPhotonPlayerDisconnected( PhotonPlayer other ) { Debug.Log( "OnPhotonPlayerDisconnected() " + other.NickName ); //當其他客戶端斷開連接時可見 if ( PhotonNetwork.isMasterClient ) { Debug.Log( "OnPhotonPlayerConnected isMasterClient " + PhotonNetwork.isMasterClient ); //在 OnPhotonPlayerDisconnected 之前調用 LoadArena(); } } #endregion

4. 保存 GameManager 腳本
現在,我們有一個完整的設置。每當一個玩家加入或離開房間,我們就被告知,我們會調用我們剛剛在上面創建的 LoadArena() 方法。然而,只有使用PhotonNetwork.isMasterClient 確定我們是主客戶端時才能調用 LoadArena() 。現在我們回到大廳,終于可以在加入房間時加載正確的場景。

3.Loading Arena from the Lobby | 加載競技場

1. 編輯腳本 Launcher;
2. 追加下面的代碼到 OnJoinedRoom() 方法中:

// #Critical | 極重要:只有第一個玩家才加載,否則我們依賴PhotonNetwork.automaticallySyncScene 來同步我們的場景實例 if (PhotonNetwork.room.PlayerCount == 1) { Debug.Log("We load the 'Room for 1' "); // #Critical | 極重要:加載房間關卡 PhotonNetwork.LoadLevel("Room for 1"); }

3. 保存腳本 Launcher .
讓我們來測試下這個腳本,打開場景 Launcher ,運行它。點擊"Play",讓系統連接并加入一個房間。就是這樣,游戲大廳正在工作。但如果你離開房間,當回到大廳時你將注意到,它會自動重新加入…哎呀,讓我們解決這個問題。如果你還不知道為什么,"simply"分析日志。我把簡單地引用起來,因為分析日志需要實踐和經驗來獲得概述問題的能力,并且知道到哪里去找,以及如何調試它。現在你自己嘗試一下,如果你仍然無法找到問題的根源,讓我們一起做這件事。
1. 運行 Launcher 場景;
2. 點擊"Play"按鈕,等待直到你加入了房間,并且"Room for 1"已被加載;
3. 清除 Unity 控制臺;
4. 點擊"Leave Room"按鈕;

5. 研究 Unity 的控制臺 , 注意到 "DemoAnimator/Launcher: OnConnectedToMaster() was called by PUN"被記錄了;
6. 停止 Launcher 場景;
7. 雙擊該日志條目 "DemoAnimator/Launcher: OnConnectedToMaster() was called by PUN",對應的腳本將被加載并指向對應調試調用的代碼;
8. 嗯 … 所以 ,每次我們收到我們已連接的通知 ,我們會自動加入一個JoinRandomRoom方法。但那不是我們想要的;
要解決這個問題,我們需要了解上下文。當用戶點擊"Play"按鈕時,我們應該發起一個標志來知道連接過程源于用戶。然后我們可以檢查這個標志在各種 Photon 回調中相應行為。
1. 編輯腳本 Launcher;
2. 在私有變量區域 Private Variables regions 內創建一個新屬性;

/// <summary> /// 跟蹤當前進程。因為連接是異步的,且是基于來自 Photon 的幾個回調, /// 我們需要跟蹤這一點,以在我們收到 Photon 回調時適當地調整該行為。 /// 這通常是用于 OnConnectedToMaster()回調。 /// </summary> bool isConnecting;

3. 在 connect() 方法前面添加下列代碼:

// 跟蹤玩家加入一個房間的意愿,因為當我們從游戲中回來時,我們會得到一個我們已連接的回調,所以我們需要知道那個時候該怎么做 isConnecting = true;

4. 在 OnConnectedToMaster() 方法中, 用一個如下所示的 if 語句來包裹PhotonNetwork.JoinRandomRoom()

//如果我們不想加入一個房間,我們不想做任何事情。 //這種情況下 isConnecting 是 false,通常是當你掉線或退出游戲時,當這一關卡被加載, OnConnectedToMaster 將被調用,在這種情況下,我們不想做任何事情。 if (isConnecting) { // #Critical | 關鍵: 我們首先要做的是加入一個潛在的現有房間。如果有,很好,否 則,我們將回調 OnPhotonRandomJoinFailed() PhotonNetwork.JoinRandomRoom(); }

5. 保存腳本 Launcher
現在如果我們再次測試并運行 Launcher 場景,大廳和游戲之間來回切換,一切都很好 :)? 為了測試場景的自動同步,你將需要發布該應用(發布為桌面版,這是最快的運行測試的平臺),并在 Unity 中同時運行,所以你實際上有兩個玩家將連接和加入房間。如果 Unity編輯器首先創建房間,它將是主客戶端,并且你可以在 Unity 控制臺得到證明:你將得到"PhotonNetwork : Loading Level : 1"以及接下來在你連接已發布實例應用時的
"PhotonNetwork : Loading Level : 2"。

很好!我們已經談了很多,但這只完成了一半… :) 我們需要處理玩家本身了,讓我們在下一節做吧。別忘了按時離開電腦休息一下,這樣可以更有效地吸收我們解釋的各種概念。
如果你對某個特定的功能有疑問,或者如果你在教程中遇到錯誤,或者遇到了在這里沒有涉及到錯誤或問題,請不要猶豫在論壇上提問,我們將樂意幫助 :)

九、創建玩家Player

本節將引導你從零開始創建將在本教程中使用的玩家預設 Prefab,所以我們涵蓋了創作過程的每一步。嘗試并創建一個無需連接PUN就能工作的玩家預設Prefab常常是一個很好的途徑,這樣很容易快速測試、調試以及確保一切在沒有網絡功能的情況下也能工作。然后,你可以建立和慢慢修改,加入網絡兼容的特性:通常情況下,用戶輸入只能在玩家擁有的實例上才能被激活,不在別人的電腦上。我們將在下面詳細介紹。

1.The Prefab Basics | 預制體基礎

你需要了解的第一且最重要的約定就是預設Prefab需要在網絡上被實例化,它需要被放在 Resources 資源文件夾里,否則就不能被找到。第二重要的,在 Resources 資源文件夾里有預設 Prefabs 的副作用是,你
需要注意這些預設的名字。你不應該在你的資產下的 Resources 資源路徑里有兩個命名相同的預制件 Prefab,因為 Unity 你會選擇找到的第一個,所以一定要確保在你的項目資產里,Resources 資源文件夾路徑下沒有兩個預設 Prefabs的命名是相同的。我們很快就會講到。我們將使用 Unity 提供的免費資源 Kyle 機器人來作為玩家預設。它是一個Fbx 文件,可以通過諸如 3ds Max、 Maya、 cinema4d 之流的 3d 軟件來制作 generated。在這些軟件上制作網格和動畫已經超出了本教程的范圍,但創造自己的人物和動畫是必不可少。如下圖所示,這個機器人 Kyle.fbx 位于/Assets/Photon Unity Networking/Demos/Shared Assets/ 路徑下。

下面是開始使用 Kyle Robot.fbx 來制作玩家預設的一種方法:
1. 在你的項目瀏覽器里,在某個地方創建一個名為"Resources"的文件夾,通常建議你組織你的內容,所以可以像這樣 'DemoAnimator_tutorial/Resources/'的路徑;
2. 創建一個新的空場景,并將其保存在 /PunBasics_tutorial/Scenes/ 路徑下,命名為Kyle Test ;
3. 把 Robot Kyle 資源拖拽到場景 Hierarchy 層級上;
4. 把你剛剛在 Hierarchy 層級中創建的游戲對象重命名為 My Robot Kyle;
5. 再把 My Robot Kyle 拖拽進 /PunBasics_tutorial/Resources/ 文件夾下。現在我們已經創建了一個基于 Kyle Robot Fbx 資產的預設 Prefab,并且我們在你的 Kyle Test 場景 Hierarchy 層級中有它的一個實例。現在我們可以開始完善這個預設了。

2.CharacterController | 角色控制器

一、如下圖右側所示,我們來一起為 Hierarchy 層級中的 My Kyle Robot 實例添加一個 CharacterController Component 組件。你也可以直接在預設 Prefab本身上做這個操作,但我們需要對其進行調整,所以這種方式更快。

這些組件是一個由Unity為我們更快的創建使用Animator的典型人物角色而提供的非常方便的標準資產,所以讓我們來一起利用這些偉大的Unity 功能。

二、雙擊 My Kyle Robot 來使場景視圖 Scene View 聚焦到該對象上。如上圖左側所示,注意到 Capsule Collider 在腳的中心,我們實際上需要 Capsule Collider 適當地匹配角色。

三、如上圖 31 右側所示,在 CharacterController Component 組件里把Center.y 屬性改成 1(是其 Height 屬性的一半)。

四、如下圖所示,點擊 Apply 來應用我們所做的改變到預設上。這是非常重要的一步,因為我們完成 My Kyle Robot 預設實例的編輯,但是我們想要這些改變被應用到每一個實例上,不僅僅是這一個,所以我們點擊 Apply 來應用。

3.Animator Setup | 動畫設置

Assigning an Animator Controller | 指派動畫控制器

Kyle Robot Fbx 資源需要被 Animator Graph 控制,如下圖所示。我們在本教程中將不包括創建此圖表,所以我們提供了一個控制器,位于您的項目資Photon Unity Networking/Demos/PunBasics/Animator/ 路徑下,叫做 Kyle Robot

如下圖所示,要指派這個 Kyle Robot 控制器到我們預設 Prefab,簡單地設置預設上的 Animator 組件的 Controller 屬性指向 Kyle Robot 即可。

別忘了,如果你在 My Kyle Robot 的實例上執行該操作的話,你需要點擊 Apply來讓預設 Prefab 本身合并這些改變。

處理控制器參數 | Controller Parameters

要搞清楚 Animator Controller 的關鍵特性就是 Animation Parameters,通過該參數我們可以在腳本里控制我們的動畫。在我們的例子中,我們有參數如Speed , Direction , Jump , Hi 。動畫組件 Animator Component 的其中一個偉大的特點就是能夠真正基于它的動畫四處移動角色,這個功能被稱為 Root Motion,并且在 Animator Component 上有一個屬性 Apply Root Motion 默認為 true,所以我們保持默認即可。
因此,實際上,要有角色行走,我們只需要將 Animation Parameter 動畫參數 Speed 設置為正數值,它就可以開始行走和向前移動。讓我們就這樣做!

Animator Manager Script | 動畫管理腳本

讓我們創建一個新的腳本,在該腳本中我們將根據用戶的輸入來控制角色。
1. 在 DemoAnimator/tutorial/Scripts/ 路徑下創建一個名為 PlayerAnimatorManager 的 C#新腳本;
2. 將該腳本添加到 Prefab 預設 My Robot Kyle 上;
3. 如下面的代碼所示,為你的腳本添加命名空間 Com.MyCompany.MyGame ;
4. 為了結構清晰起見,用 MONOBEHAVIOUR MESSAGES 域把 Start()和 Update()
包裹起來;

using UnityEngine; using System.Collections; namespace Com.MyCompany.MyGame { public class PlayerAnimatorManager : MonoBehaviour { #region MONOBEHAVIOUR MESSAGES void Start () { } void Update () { } #endregion } }

5. 保存腳本 PlayerAnimatorManager。

Animator Manager: Speed Control | 速度控制

我們需要編碼的第一件事就是獲取 Animator Component 動畫組件,這樣我們才可以控制它。
1. 確保您正在編輯腳本 PlayerAnimatorManager;
2. 創建一個 Animator 類型的私有變量 animator;
3. 在 Start()方法內把 Animator Component 存儲進這個變量。

private Animator animator; void Start () { animator = GetComponent<Animator>(); //獲取動畫組件 if (!animator) //如果沒有獲取到則報錯 { Debug.LogError("PlayerAnimatorManager is Missing Animator Component",this); } }

4. 注意,因為我們必需一個 Animator Component 動畫組件,如果我們沒有獲取到,我們記錄一個錯誤,這樣可以及時發現問題并由開發者直接解決。你應該總是以別人會使用的方式來寫代碼 :) 這是乏味的,但長期來
看是值得的。

5. 現在讓我們來監聽用戶輸入 User Inputs ,并且控制 Animation Parameter 動畫參數 Speed 。然后保存 PlayerAnimatorManager 腳本。

void Update () { if (!animator) //如果沒有獲取到動畫組件,則用 return 中斷 { return; } float h = Input.GetAxis("Horizontal"); float v = Input.GetAxis("Vertical"); if( v < 0 ) //數值校正 { v = 0; } animator.SetFloat( "Speed", h*h+v*v ); //設置速度參數值 }

6. 保存腳本 PlayerAnimatorManager
讓我們來研究這個腳本在做什么:
由于我們的游戲不允許玩家后退,我們確保 v 不小于 0。用戶正在按下‘↓’鍵或“s”鍵,我們不允許使用此值,并強制將值改成 0。你也會注意到,我們把兩個輸入都平方化處理,為什么?這樣的話它總是一個正絕對值,以及增加一些 easing。很好的微妙技巧就在這里。你也可以使用Mathf.Abs() ,那也能正常工作。我們還把兩個輸入相加來控制速度 Speed ,以便當只按下左的右輸入,我們仍然獲得一些速度,因為我們轉向。當然所有這一切對于我們的角色設計都很具體,取決于你的游戲邏輯,你可能想角色轉變,或有往回走的能力,所以游戲的動畫控制參數 Animation Parameters 總是非常具體。

Test, test, 1 2 3... | 測試

讓我們來驗證一下我們迄今所做的。確保你已經打開了 Kyle Test 場景。目前,在這個場景中,我們只有一個相機和 Kyle Robot 實例,我們不在乎場景的照明或任何花哨的效果,我們要驗證我們的角色和腳本是否能正常工作。

1. 選擇相機 Camera,并移動它來得到一個很好的總覽視角。一個好的技巧是在場景視圖 Scene View 中獲取你喜歡的視角,選擇相機,然后到菜單"GameObject/Align With View",這樣像機將匹配場景視圖的視角。
2. 最后一步,把 My Robot Kyle 在 y 軸上向上移 0.1,否則碰撞在開始時就丟失了,角色會直接穿過地面,所以總是在碰撞器之間預留一些物理空間,來模擬接觸。
3. 運行場景,按下‘↑’或“A”鍵,角色正在行走!你可以用所有的鍵來驗證測試。這很好,但仍然有大量的工作在我們面前,相機需要跟隨,而且我們還不能轉彎…如果你現在就像完成攝像機,可以直接跳到像機部分的內容,接下來的頁面將完成動畫控制和實現轉彎。

Animator Manager Script: Direction Control | 方向控制

控制旋轉會稍微復雜一點,我們不希望我們的角色在我們按左鍵和右鍵時突然旋轉,我們希望平緩和平滑的旋轉。幸運的是,一個動畫參數 Animation Parameter 可以設置一些阻尼。
1. 確保您正在編輯腳本 PlayerAnimatorManager;
2. 在腳本的新的域"PUBLIC PROPERTIES"內創建公共浮點數變量DirectionDampTime。

//#region PUBLIC PROPERTIES //公共屬性域 public float DirectionDampTime = .25f; //#endregion

3. 在 Update()函數末尾加上:

animator.SetFloat( "Direction", h, DirectionDampTime, Time.deltaTime );

4. 所以我們馬上注意到 animator.SetFloat()有不同的用法。我們用來控制速度 Speed 的是簡單直接的,但是對于這一個需要多兩個參數,一個阻尼時間,一個是 deltaTime。阻尼時間是有意義的:它是需要多久才能達到期望值,但 deltaTime?它本質上讓你寫的代碼獨立于幀速率,因為Update()是依賴于幀速率的,我們需要通過使用 deltaTime 來計數。盡可能多地閱讀關于這個主題知識,以及那些當你在網上搜索時你會發現的內容。只有在你之后理解這個概念后,當涉及到動畫和隨著時間推移值的一致性控制,你就可以充分利用到 Unity 的許多功能;
5. 保存腳本 PlayerAnimatorManager;
6. 運行您的場景,并使用所有的箭頭鍵來看看你的角色是如何走動和轉身;
7. 測試 DirectionDampTime 的效果:例如將其值改為 1,然后 5,并看看角色需要多長時間來達到最大彎度。你將發現轉彎半徑隨著 DirectionDampTime 增加。

Animator Manager Script: Jumping | 跳躍

對于跳躍,我們需要更多的工作,因為兩個因素。一、我們不希望玩家在沒有奔跑的情況下跳躍;二,我們不想玩家循環跳躍。

1. 確保您正在編輯腳本 PlayerAnimatorManager;
2. 在方法里,在我們捕捉用戶輸入之前插入這些代碼。

// 處理跳躍 AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0); // 只有當我們在奔跑的時候才能跳躍。 if (stateInfo.IsName("Base Layer.Run")) { // 何時使用觸發參數。 if (Input.GetButtonDown("Fire2")) animator.SetTrigger("Jump"); }

3. 保存腳本 PlayerAnimatorManager;
4. 測試。開始運行,按“Alt”鍵,凱爾應該會跳躍。好的,首先要了解我們如何知道動畫是否在運行,我們使用stateInfo.IsName("Base Layer.Run") 來判斷。我們只是想問動畫當前活動狀態是否是Run 。我們必須追加 Base Layer ,因為 Run 狀態位于 Base Layer 中。如果我們在奔跑的狀態中,那么就可以監聽 Fire2 Input 輸入,并且啟用 Jump觸發器是必要的。所以,這是目前的完整 PlayerAnimatorManager 腳本:

using UnityEngine; using System.Collections; namespace Com.MyCompany.MyGame { public class PlayerAnimatorManager : MonoBehaviour { #region PUBLIC PROPERTIES //公共屬性區域 public float DirectionDampTime = .25f; #endregion #region PRIVATE PROPERTIES //私有屬性區域 private Animator animator; #endregion #region MONOBEHAVIOUR MESSAGES //Mono 行為消息區域 void Start () { animator = GetComponent<Animator>(); if (!animator) { Debug.LogError("PlayerAnimatorManager is Missing Animator Component",this); } } void Update () { if (!animator) { return; } //處理跳躍,獲取當前動畫狀態 AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0); //只有我們正在奔跑時才能跳躍 if (stateInfo.IsName("Base Layer.Run")) { //何時使用觸發器參數 if (Input.GetButtonDown("Fire2")) animator.SetTrigger("Jump"); } float h = Input.GetAxis("Horizontal"); float v = Input.GetAxis("Vertical"); if( v < 0 ) { v = 0; } animator.SetFloat( "Speed", h*h+v*v ); animator.SetFloat( "Direction", h, DirectionDampTime, Time.deltaTime ); } #endregion } }

當你考慮到它在場景中實現的功能時,對幾行代碼來說不是太壞。現在,讓我們處理相機的工作,因為我們能夠在我們的世界發展,我們需要一個適當的相機行為來跟隨。

4.Camera Setup | 攝像機設置

在本章中,我們將使用 CameraWork 腳本來完成攝像機對玩家的跟隨。如果你想從零開始寫 CameraWork ,請看下一部分,在完成后再回來看這一部分。
1. 添加 CameraWork 組件到 My Kyle Robot 預設;
2. 開啟 Follow on Start 屬性,這樣可以使攝像機立即跟隨角色。當我們將開始網絡實現的時候我們將把它關掉;
3. 把 Center Offset 屬性設置成 0,4,0 ,這可以使攝像機看得更高,這樣可以給出更好的環境視角,而不是直接盯著玩家,我們將看到太多的地面;
4. 運行 Kyle Test 場景,讓角色四處走走來測試攝像機是否恰當地跟隨角色。

5.Beams Setup | 射線設置

我們的機器人角色仍然缺少它的武器,讓我們創造一些激光束從其眼睛內射出來。

Adding the Beams models | 添加射線模型

為了簡單起見,我們將使用簡單的立方體和將其縮放成非常薄和長。有一些技巧可以將其很快完成:不要直接將一個立方體添加為頭部的子物體,而是單獨創建它移動它,并單獨進行縮放,然后附加到頭部,它將防止猜測適當的旋轉值,該值使您的光束與眼睛對齊。另一個重要的技巧是在兩個光束中只使用一個碰撞器。這是為了讓物理引擎的工作得更好,薄的碰撞器絕不是一個好主意,因為碰撞器太薄是不可靠的,所以我們要做一個大的碰撞器來使我們確定能夠可靠地打擊到目標。
1. 打開 Kyle test 場景;
2. 添加一個立方體到場景中,并命名為 Beam Left;
3. 修改它,使其看起來像一個長的光束,并妥善定位于左眼;
4. 在 Hierarchy 層級內選擇 My Kyle Robot 實例;
5. 如下圖所示,定位到 Head 子類;

6. 添加一個空對象作為 Head 游戲對象的子類,并命名為 Beams;
7. 把 Beam Left 拖拽進 Beams;
8. 如下圖所示,復制 Beams Left , 并命名為 Beams Right;

9. 定位它,以便它與右眼對齊;
10. 從 Beams Right 中移除 Box Collider 組件;
11. 調整 Beams Left 的 Box Collider 中心和大小來封裝兩個光束;
12. 把 Beams Left 的 Box Collider 組件的 IsTrigger 屬性改成 True ,我們只想在射線觸碰到玩家時被通知,并且沒有碰撞;
13. 創建一個新的材質, 命名為 Red Beam , 將其保存在'DemoAnimator_tutorial/Scenes/'路徑下;
14. 把 Red Beam 材質指派給兩個射線;
15. 把更改應用到預設上(點擊實例上的 Apply 即可)。

如下圖所示,你現在應該有這樣的東西了:

Controlling the Beams with User Input | 用戶控制射線

好吧,現在我們有射線了,讓我們插入發射輸入來觸發它們。讓我們創建一個新的 C #腳本,叫做 PlayerManager 。下面是第一個版本的完整代碼,來使射線工作。

using UnityEngine; using UnityEngine.EventSystems; using System.Collections; namespace Com.MyCompany.MyGame { /// <summary> /// 玩家總管,處理發射輸入和射線。 /// </summary> public class PlayerManager : MonoBehaviour { #region Public Variables //公共變量區域 [Tooltip("The Beams GameObject to control")] public GameObject Beams; //要控制的射線 #endregion #region Private Variables //私有變量區域 //當玩家發射的時候為 true bool IsFiring; #endregion #region MonoBehaviour CallBacks //Mono 行為回調 void Awake() { if (Beams==null) //如果射線為空,則報錯 { Debug.LogError("<Color=Red><a>Missing</a></Color> Beams Reference.",this); }else{ Beams.SetActive(false); } } void Update() { ProcessInputs (); //觸發射線的激活狀態 if (Beams!=null && IsFiring != Beams.GetActive ()) { Beams.SetActive(IsFiring); } } #endregion #region Custom //自定義區域 /// <summary> /// 處理輸入。當用戶按下發射時,維護一個標志。 /// </summary> void ProcessInputs() { if (Input.GetButtonDown ("Fire1") ) { if (!IsFiring) { IsFiring = true; } } if (Input.GetButtonUp ("Fire1") ) { if (IsFiring) { IsFiring = false; } } } #endregion } }

在這個階段,這個腳本主要是激活或停用射線。當被激活時,射線將在與其他模型發生碰撞時有效地觸發,所以我們將在觸發后捕捉這些觸發器來影響每個角色的健康值或體力。我們也暴露出公共屬性 Beams ,將讓我們引用在 My Kyle Robot 預設 Prefab 的層級內部的具體對象。讓我們看看要如何辦才能連接 Beams ,因為這在預設內部是棘手的,由于在資產瀏覽器里面,預設只暴露出第一個子類,并非其他子類,并且我們的射線確實是埋在預制體的層次中,所以我們需要做的是從場景中的一個實例上獲取,然后將它應用到預制體本身。
1. 打開 Kyle test 場景;
2. 在場景層級中選擇 My Kyle Robot;
3. 添加 PlayerManager 組件到 My Kyle Robot;
4. 把 My Kyle Robot/Root/Ribs/Neck/Head/Beams 拖拽進 PlayerManager 在 Inspector窗口里的 Beams 屬性里;
5. 把實例上的改變應用到預設上。

如果你點擊運行,并且按下 Fire1 Input 輸入(默認是鼠標左鍵或左 Ctrl 鍵),射線將出現,并在你松開時立即隱藏。

6.Health Setup | 體力設置

讓我們實現一個非常簡單的健康系統,體力值會在射線擊中玩家的時候減少。因為它不是子彈,而是源源不斷的能量,我們需要用兩種方法來計算傷害,當我們被射線擊中,并且射線一直擊中我們。
1. 打開 PlayerManager 腳本;
2. 把 PlayerManager 轉化成Photon.PunBehaviour來暴露PhotonView組件:

public class PlayerManager : Photon.PunBehaviour {

3. 在 Public Variables 區域添加一個 Health 公共屬性:

[Tooltip("The current Health of our player")] //玩家的當前體力值 public float Health = 1f;

4. 在 MonoBehaviour CallBacks 區域添加下面這兩個方法,然后保存 Player Manager 腳本。

/// <summary> /// 當碰撞器'other'進入觸發器時調用的 MonoBehaviour 方法。 /// 如果碰撞器是射線就會影響到玩家的體力值 /// 注:當跳躍的同時射擊,你會發現自己的射線和自身發生交互 /// 你可以把碰撞器移動稍遠一些,以防止這樣的 Bug 或檢查光束是否屬于玩家。 /// </summary> void OnTriggerEnter(Collider other) { if (! photonView.isMine) { return; } //我們只對敵人感興趣,我們可以通過標簽來區分,也可以簡單地檢查名稱 if (!other.name.Contains("Beam")) { return; } Health -= 0.1f; } /// <summary> /// 每一個'other'碰撞器觸摸這個觸發器的時候每幀調用的 MonoBehaviour 方法 /// 當射線持續觸碰玩家時我們將繼續影響體力值。 /// </summary> /// <param name="other">其他碰撞器</param> void OnTriggerStay(Collider other) { //如果不是本地玩家,什么都不做 if (! photonView.isMine) { return; } if (!other.name.Contains("Beam")) { return; } //當射線持續擊中我們的時候我們緩慢地影響體力值,這樣玩家不得不移動來防止被擊 殺。 Health -= 0.1f*Time.deltaTime; }

5. 保存 PlayerManager 腳本。

首先,兩方法幾乎完全相同,唯一不同的是,我們在 TriggerStay 中使用Deltatime 來減少體力值,遞減速度不應與幀率相關。這是一個重要的概念,通常適用于動畫,但在這里,我們也需要這樣,我們希望體力值在所有的設備上以可預見的方式減少,在一個更快的計算機上,你的體力值減少得越快 :) ,這是不公平的,Deltatime 來保證該值減少的一致性。如果你有問題的話可以給我們發郵件,通過搜索 Unity 社區來學習 DeltaTime,直到你完全了解 DeltaTime概念,它是必要的。第二重要的方面,那現在應該明白的是,我們只影響本地玩家的體力值,這就是為什么我們在 PhotonView 不是 Mine 時提前退出方法。
最后,我們只想如果擊中我們的對象是射線時才影響體力值,所以我們使用標簽"beam"來檢查,這是我們如何標簽我們的射線對象的。為了便于調試,我們使體力值作為一個公共浮點數,以便在我們等待 UI 被構建時檢查它的值。

好吧,這看起來全都做對了嗎?呃...健康系統沒有考慮到玩家的游戲結束狀態是不完整的,當體力值達到 0 時觸發該狀態,讓我們現在來完成這個。

Health checking for game over | 檢查體力來結束游戲

為了使事情簡單,當玩家的體力值達到 0,我們就離開房間,如果你記得,我們已經在 GameManager 腳本里創造了離開房間的方法。如果我們可以重復使用這種方法,而不是為相同的功能編碼兩次,這才是最好的做法。為同樣的結果重復代碼是你應該不惜一切代價避免的。這也將是一個很好的介紹時機來引入一個非常方便的編程概念,"Singleton"。雖然這個主題本身可以填補一些教程,我們只會做"Singleton"最小化實現。要了解 Singleton,它們在 Unity 上下文中的變體,以及它們如何幫助創建強大的功能是非常重要的,將為您節省很多麻煩。所以不要猶豫在這個教程之外花更多時間來了解更多關于 Singleton 的知識。

1. 打開 GameManager 腳本
2. 在 Public Properties 區域添加這個變量

static public GameManager Instance;

3. 在 Start()方法中添加這個代碼

Instance = this;

4. 保存 GameManager 腳本

注意我們用[static]關鍵字來修飾實例變量,這意味著這個變量可無需持有一個指向 GameManager 實例的指針就可用,所以你可以簡單地在你的代碼的任何地方使用 GameManager.instance.xxx() 。這確實很實際!讓我們看看單例是如何在邏輯管理上適合我們的游戲。
1. 打開 PlayerManager 腳本
2. 在 Update()方法中,在我們 ProcessInputs 之后,添加下面這個判斷,并保存 PlayerManager 腳本

if ( Health <= 0f) { GameManager.Instance.LeaveRoom(); }

3. 保存 PlayerManager 腳本
注意,我們考慮到體力值可能是負的,因為激光束造成的損害強度不同。注意我們沒有得到任何組件就使用了GameManager實例的LeaveRoom()公共方法,我們只是依賴于這樣的事實:我們認為在當前場景中的一個游戲對象上有一個 GameManager 組件。

好吧,現在我們進入網絡!

十、創建玩家攝像機腳本

本部分將引導您創建 CameraWork 腳本來讓攝像機在你玩游戲時跟隨你。
這一部分與網絡無關,所以它會保持簡短。

創建 CameraWork 腳本

1. 創建一個新的 C #腳本,叫做 CameraWork
2. 用下面的代碼來替換 CameraWork 腳本里面的內容 :

using UnityEngine; using System.Collections; namespace Com.MyCompany.MyGame { /// <summary> /// 相機的工作。跟蹤目標 /// </summary> public class CameraWork : MonoBehaviour { #region Public Properties //公共屬性區域 [Tooltip("在本地 X-Z 平面到目標的距離")] public float distance = 7.0f; [Tooltip("我們希望相機高于目標的高度")] public float height = 3.0f; [Tooltip("相機高度的平滑時間滯后.")] public float heightSmoothLag = 0.3f; [Tooltip("讓相機可以從目標抵消垂直,例如提供更多的視野且更少的地面。")] public Vector3 centerOffset = Vector3.zero; [Tooltip("如果預設的組件正在被 Photon Networ 實例化把這個屬性設置為false,并在需要的時候手動調用 OnStartFollowing()")] public bool followOnStart = false; #endregion #region Private Properties //私有屬性區域 //把目標的 Transform 緩存 Transform cameraTransform; //如果目標丟失或相機被切換,請在內部保持一個標志來重新連接 bool isFollowing; //表示當前的速度,這個值在每次你調用 SmoothDamp()時被修改。 private float heightVelocity = 0.0f; //代表我們試圖使用 SmoothDamp()來達到的位置 private float targetHeight = 100000.0f; #endregion #region MonoBehaviour Messages //Mono 行為消息區域 void Start() { //如果想要的話開始跟隨目標 if (followOnStart) { OnStartFollowing(); } } /// <summary> /// 在所有 Update 方法被調用后才調用的 MonoBehaviour 方法。這對于讓腳本按順序執行是有用的。例如一個跟蹤相機應該總是在 LateUpdate 里實現,因為它的追蹤對象可能已經在 Update 中移動了。 /// </summary> void LateUpdate() { //在關卡加載時目標 Transform 也許沒有被破壞,因此,我們需要覆蓋 //小概率事件,每一次我們加載一個新場主攝像機是不一樣的,在發生時重連 if (cameraTransform == null && isFollowing) { OnStartFollowing(); } //只在被明確聲明時跟隨 if (isFollowing) { Apply (); } } #endregion #region Public Methods //公共方法區域 /// <summary> /// 引發開始跟隨事件。 /// 當你不知道在編輯什么時候跟隨時使用這個,通常實例由 photon 網絡管理。 /// </summary> public void OnStartFollowing() { cameraTransform = Camera.main.transform; isFollowing = true; //我們不平滑任何東西,我們直接找到正確的相機拍攝 Cut(); } #endregion #region Private Methods //私有方法區域 /// <summary> /// 平滑地跟蹤目標 /// </summary> void Apply() { Vector3 targetCenter = transform.position + centerOffset; //計算當前與目標旋轉角度 float originalTargetAngle = transform.eulerAngles.y; float currentAngle = cameraTransform.eulerAngles.y; //當攝像機被鎖定的時候適應真正的目標角度 float targetAngle = originalTargetAngle; currentAngle = targetAngle; targetHeight = targetCenter.y + height; //對高度進行平滑緩沖 float currentHeight = cameraTransform.position.y; currentHeight = Mathf.SmoothDamp( currentHeight, targetHeight, ref heightVelocity, heightSmoothLag ); //把角度轉化成旋轉,這樣我們就可以重置像機的位置了 Quaternion currentRotation = Quaternion.Euler( 0, currentAngle, 0 ); //把攝像機在 x-z 平面上的位置設置成:到目標的距離 cameraTransform.position = targetCenter; cameraTransform.position += currentRotation * Vector3.back * distance; //設置攝像機的高度 cameraTransform.position = new Vector3( cameraTransform.position.x, currentHeight, cameraTransform.position.z ); //總是看向目標 SetUpRotation(targetCenter); } /// <summary> /// 將攝像機直接定位到指定的目標和中心。 /// </summary> void Cut( ) { float oldHeightSmooth = heightSmoothLag; heightSmoothLag = 0.001f; Apply(); heightSmoothLag = oldHeightSmooth; } /// <summary> /// 設置攝像機的旋轉始終在目標后面 /// </summary> /// <param name="centerPos">中心位置.</param> void SetUpRotation( Vector3 centerPos ) { Vector3 cameraPos = cameraTransform.position; Vector3 offsetToCenter = centerPos - cameraPos; //只圍繞 Y 軸生成基礎旋轉 Quaternion yRotation = Quaternion.LookRotation( new Vector3( offsetToCenter.x, 0, offsetToCenter.z ) ); Vector3 relativeOffset = Vector3.forward * distance + Vector3.down * height; cameraTransform.rotation = yRotation * Quaternion.LookRotation( relativeOffset ); } #endregion } }

3. 保存腳本 CameraWork。

如果你剛剛開始實時 3D、向量和四元數為基礎的數學,跟隨玩家背后的數學是棘手的。所以我不會在這個教程內不遺余力地試圖解釋。但是如果你好奇并想學習,不要猶豫與我們聯系,我們會盡全力解釋。然而,這個腳本不只是關于瘋狂的數學,重要的也是設置;當攝像機的行為應該積極地跟隨玩家時有能力去控制。并且理解這一點很重要:為什么我們在攝像機跟隨玩家時想要去控制。通常情況下,如果它總是跟隨玩家,讓我們想象會發生什么。當你連接到一個滿是玩家的房間,在其他玩家上的每一個 CameraWork 腳本都會努力控制主攝像來讓其看到自己的玩家…好吧,我們不想這樣,我們只想跟隨本地玩家。一旦我們定義了這個問題,我們只有一個攝像機,但有幾個玩家的實例,我們可以很容易地找到幾種方法去解決這個問題。
1. 只在本地玩家上依附 CameraWork 腳本。
2. 通過關閉 CameraWork 來控制其行為,并根據是否是本地玩家來進行跟隨。
3. 把 CameraWork 依附到攝像機上并時刻注意場景中的本地玩家實例,然后只跟隨本地玩家。
這 3 個選項并不詳盡,可以找到更多的方法,但從這 3 個,我們將任意選擇第二個。上述選項沒有一個更好或更壞,但這可能是一個需要編碼最少和最靈活… "有趣..." 我聽到你說 :)

? 我們已經暴露了公共屬性 followOnStart ,如果我們想用在非網絡環境上就將其設置為 true,例如在我們的測試場景里,或完全不同的場景里。在我們的網絡游戲運行的時候,當檢測到本地玩家時我們會調用OnStartFollowing() 公共方法。這將在 Player Prefab Networking 章里創建并解釋的 PlayerManager 腳本里完成。

十一、把玩家修改成網絡預設

本部分將引導您修改玩家預制體。我們首先創建了一個玩家,但現在我們要修改它使其在我們在 PUN 環境里使用的時候能夠工作和兼容。非常輕量的修改,但概念是至關重要的。所以這部分確實很重要。

1.PhotonView Component | PhotonView 組件

首先,我們需要在我們的預設上添加一個 PhotonView 組件。一個 PhotonView 就是將不同的實例在每臺電腦上連接起來的組件,并定義觀察什么組件以及如何觀察這些組件。
1. 添加一個 PhotonView 組件到 My Robot Kyle

2. 設置 Observe Option 為 Unreliable On Change
3. 注意 PhotonView 提醒你,你需要觀察的東西對其有任何影響
讓我們一起來設置我們將要觀察的,然后我們將會回到這個 PhotonView 組件并完成它的設置。

2.Transform Synchronization | 同步 Transform

我們想要同步的一個明顯的特征是角色的位置和旋轉,這樣當一個玩家移動時,其他計算機上的其他玩家代表以同樣的方式移動和旋轉。您可以直接觀察到自己腳本中的 Transform 組件,但是由于網絡延遲和數據同步的有效性,您會遇到很多麻煩。幸運的是為了讓這個共同的任務更容易,我們將使用一個[Photon Transform View]組件,作為 Transform 組件和 PhotonView 之間的中間人。基本上,用這個組件把你要做的所有難點都解決了。

1. 添加一個 PhotonTransformView 組件到'My Robot Kyle'預設
2. 如下圖所示,拖拽 PhotonTransformView 組件的標題將其放進 PhotonView 組件的第一個觀察組件條目里(文中的 Gif 圖請參考官網)

3. 現在檢查 PhotonTransformView 里的 Synchronize Position
4. 在 Synchronize Position 里,選擇"Lerp Value for Interpolation Option"
5. 設置 Lerp Speed 為 10 (這個值越大就能越快追上)
6. 如下圖所示,檢查 SynchronizeRotation

提示: 注意如下圖所示的藍皮書幫助鏈接。點擊它揭示信息,了解所有的各種設置和它們的影響。

3.Animator Synchronization | 動畫同步

PhotonAnimatorView 組件也使得網絡設置輕而易舉,并且幫你省去了大把的時間和麻煩。它允許您定義哪些層權重和哪些參數要同步。層權重只需要在游戲中發生改變時同步,有可能逃脫而完全沒有同步。參數也一樣。有時它可能從其他因素中獲得動畫值。一個速度值是一個很好的例子,你不必需要精確地同步這個值,你可以使用同步位置更新來估計它的值。如果可能的話,盡量少同步參數。
1. 添加一個 PhotonAnimatorView 組件到 My Robot Kyle 預設
2. 把 PhotonAnimatorView 拖拽到 PhotonView 組件的新的觀察組件條目上
3. 現在,在同步參數中,設置 Speed 為 Discrete
4. 設置 Direction 為 Discrete
5. 設置 Jump 為 Discrete
6. 設置 Hi 為 Disabled

每個值都可以被禁用,或同步(無論是離散或連續)。在我們的情況下,因為我們不使用 Hi 參數,我們將禁用它,并節省帶寬。Discrete synchronization 離散同步意味著一個值每秒被發送 10 次(在OnPhotonSerializeView 里面)。接收客戶端把值傳遞給本地的動畫。

Continuous 連續同步意味著 PhotonAnimatorView 每幀運行。當 OnPhotonSerializeView被調用(每秒 10 次),該值自從上次被調用就開始記錄,再一起發送。接收客戶端然后按序列應用這些值,以保持平穩過渡。雖然這個模式是平滑的,它也發送更多的數據來實現這種效果。

4.User Input Management | 用戶輸入管理

在網絡用戶控制的一個關鍵方面是相同的預制件將在所有的玩家客戶端上被實例化,但其中只有一個角色是代表在當前電腦前玩游戲的用戶,所有其他實例代表在其他電腦上玩游戲的其他用戶。因此,首先要考慮的就是輸入管理。我們怎樣才能使在一個實例上啟用輸入而不是其他,如何知道哪個是正確的?引入 isMine 概念。
讓我們來一起編輯我們之前創建的 PlayerAnimatorManager 腳本。在當前形態下,該腳本并不知道其中的區別,讓我們來一起實現這個功能。
1. 打開腳本 PlayerAnimatorManager
2. 把 PlayerAnimatorManager 類 的 基 類 從 MonoBehaviour 改 成Photon.MonoBehaviour,從而很方便地暴露 photonView 組件。
3. 在 Update() 調用里,在最開始插入下面的代碼

if( photonView.isMine == false && PhotonNetwork.connected == true ) { return; }

4. 保存腳本 PlayerAnimatorManager
5. 好的,如果實例由'client'應用控制 PhotonView.isMine 將是 true,意味著這個實例代表的就是在這臺電腦上玩這個游戲的本地玩家。所以如果它是 false,我們不想做任何事,并且像我們早先設置好的那樣僅僅依靠 PhotonView 組件來同步Transform 和動畫組件。
6. 但是,為什么又要在if語句里面判斷 PhotonNetwork.connected == true ?呃 嗯 :) 因為在開發過程中,我們也許想要在沒有連接的情況下測試這個預設。例如在虛擬場景中,只是創建和驗證與網絡功能無關的代碼。所以有這個語句我們就可以允許在不聯網的情況下輸入。這是一個非常簡單的技巧,將在開發過程中大大提高您的工作流程。

5.Camera Control | 攝像機控制

和上一部分講的輸入一樣,玩家只有一個游戲視圖,所以我們需要 CameraWork 腳本,來控制攝像機只能跟著本地玩家,而不是其他玩家。這就是為什么 CameraWork 腳本有定義何時跟隨的能力。讓我們一起修改 PlayerManager 腳本來控制 CameraWork 組件。

1. 打開 PlayerManager 腳本.
2. 把下面的代碼插入到 Awake() 和 Update() 方法之間。

void Start() { CameraWork _cameraWork = this.gameObject.GetComponent<CameraWork>(); if (_cameraWork!=null ) { if ( photonView.isMine) { _cameraWork.OnStartFollowing(); } }else{ Debug.LogError("<Color=Red><a>Missing</a></Color> CameraWork Component on playerPrefab.",this); } }

3. 保存腳本 PlayerManager

首先,獲取到 CameraWork 組件,我們想到,如果我們找不到它,我們記錄一個錯誤。然后,如果 photonView.isMine 是 true ,這意味著我們需要跟隨這個實例,所以我們調用_cameraWork.OnStartFollowing() ,該方法有效地讓相機跟隨場景中的對應實例。所有其他玩家實例將把它們的 photonView.isMine 設置為 false ,這樣它們的對應_cameraWork 將什么也不做。使其運行起來的最后一個改動:
1. 如下圖所示,在 My Robot Kyle 預設上的 CameraWork 組件上禁用 Follow on Start

這樣就把跟隨玩家的邏輯都交給了 PlayerManager 腳本,該腳本將按照上面描述的那樣調用 _cameraWork.OnStartFollowing() 。

6.Beams Fire Control | 射線發射控制

發射同樣遵循上述的輸入原則,它只在 photonView.isMine 是 true 的時候工作:
1. 打開腳本 PlayerManager
2. 用 if 語句來包裹輸入調用。

if (photonView.isMine) { ProcessInputs (); }

3. 保存腳本 PlayerManager
然而,在測試時,我們只看到了本地玩家射擊。我們需要查看其他實例什么時候射擊!我們需要一個同步整個網絡發射的機制。要做到這一點,我們要去手動同步 IsFiring 布爾值,直到現在,我們得到了 PhotonTransformView 和 PhotonAnimatorView 來我們做所有變量的內部同步,我們只有調整那些通過 Unity 的 Inspector 方便地暴露出來的值,但在這里我們所需要的對于你的游戲來說是非常具體的,所以我們需要手動來做。
1. 打開腳本 PlayerManager
2. 實現 IPunObservable

3. 在 IPunObservable.OnPhotonSerializeView 里面加入下列代碼

if (stream.isWriting) { // 我們擁有該玩家:把我們的數據發送給其他玩家 stream.SendNext(IsFiring); }else{ //網絡玩家,接收數據 this.IsFiring = (bool)stream.ReceiveNext(); }

4. 保存腳本 PlayerManager
5. 回到 Unity 編輯器,在資源里面選擇 My Robot Kyle 預設,并在 PhotonView 組件上添加一個觀察條目,把 PlayerManager 組件拖拽到該條目上。

沒有這最后一步,IPunObservable.OnPhotonSerializeView 絕不會被調用,因為沒有被 PhotonView 觀察。

在這個 IPunObservable.OnPhotonSerializeView 方法里面,我們傳遞一個 stream 變量,這是將被通過網絡發送的,并且如果我們進行讀寫數據也會調用。我們只有是本地玩家( PhotonView.isMine == true )時才寫入,否則我們就讀取。
由于 stream 類有助手,知道要做什么,我們僅僅依靠 stream.isWriting 來知道在當前實例想要什么。如果我們想要寫入數據,我們把 IsFiring 值追加到數據流,使用 stream.SendNext() ,該方法非常方便地隱藏了所有數據序列化的艱苦工作。如果我們想讀取,使用stream.ReceiveNext()。

Health Synchronization | 同步體力值

好了,要完成為網絡更新玩家的功能,我們將同步的健康值,使每個玩家的實例將有正確的健康值。這和上述講的 IsFiring 值是完全相同的原則。

1. 打開腳本 PlayerManager
2. 在 IPunObservable.OnPhotonSerializeView 里面,在你 SendNext 和 ReceiveNext 變量IsFiring 之后,為 Health 執行一樣的操作

if (stream.isWriting) { // 我們擁有該玩家:把我們的數據發送給其他玩家 stream.SendNext(IsFiring); stream.SendNext(Health); }else{ //網絡玩家,接收數據 this.IsFiring = (bool)stream.ReceiveNext(); this.Health = (float)stream.ReceiveNext(); }

十二、玩家網絡實例

十三、玩家UI預設Prefab

十四、匹配指南

十五、實例化

十六、同步和狀態

十七、RPCs和RaiseEvent

十八、所有權轉移

十九、優化技巧

二十、剔除演示

二十一、PUN案例

二十二、動作演示

二十三、Mecanim案例

二十四、Photon Animator View(觸發器)

二十五、2D跳和跑案例

二十六、回合制游戲案例

總結

以上是生活随笔為你收集整理的【持续更新】Pun多人在线游戏开发教程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

国产又爽又黄又刺激的视频 | 99精品无人区乱码1区2区3区 | 狠狠色噜噜狠狠狠7777奇米 | 久久99精品国产麻豆 | 蜜桃无码一区二区三区 | 欧美国产日韩亚洲中文 | 精品国产av色一区二区深夜久久 | 欧美亚洲日韩国产人成在线播放 | 国产av剧情md精品麻豆 | 美女扒开屁股让男人桶 | 精品国产一区二区三区四区在线看 | 55夜色66夜色国产精品视频 | 东京热无码av男人的天堂 | 成 人 网 站国产免费观看 | 久久久久亚洲精品男人的天堂 | 又粗又大又硬又长又爽 | 国产真实乱对白精彩久久 | 亚洲色www成人永久网址 | 老头边吃奶边弄进去呻吟 | 国产成人精品一区二区在线小狼 | 亚洲 欧美 激情 小说 另类 | 女人被男人爽到呻吟的视频 | 少妇无码一区二区二三区 | 日本大乳高潮视频在线观看 | 国产又爽又猛又粗的视频a片 | 无码午夜成人1000部免费视频 | 无码毛片视频一区二区本码 | 大胆欧美熟妇xx | 日韩精品无码一本二本三本色 | 人人爽人人澡人人高潮 | 亚洲精品一区二区三区四区五区 | 人妻少妇精品久久 | 欧洲vodafone精品性 | 国产色xx群视频射精 | 国内揄拍国内精品少妇国语 | 国产熟妇另类久久久久 | 成人无码精品一区二区三区 | 久久久av男人的天堂 | 精品一二三区久久aaa片 | 日韩 欧美 动漫 国产 制服 | 丝袜人妻一区二区三区 | 精品一区二区三区波多野结衣 | 久久99精品久久久久久动态图 | 国产午夜无码精品免费看 | 国内精品九九久久久精品 | 牲欲强的熟妇农村老妇女视频 | 少妇性俱乐部纵欲狂欢电影 | 无码纯肉视频在线观看 | 免费无码一区二区三区蜜桃大 | 无码av免费一区二区三区试看 | 在线成人www免费观看视频 | 正在播放东北夫妻内射 | 亚洲成av人片在线观看无码不卡 | 偷窥日本少妇撒尿chinese | 国产精品久久久久久无码 | 欧美成人免费全部网站 | 日韩精品无码免费一区二区三区 | 图片区 小说区 区 亚洲五月 | 狠狠色噜噜狠狠狠7777奇米 | 亚洲日本在线电影 | 欧美性生交活xxxxxdddd | 99久久99久久免费精品蜜桃 | 日本在线高清不卡免费播放 | 97久久国产亚洲精品超碰热 | 久久久久亚洲精品男人的天堂 | 国产成人久久精品流白浆 | 超碰97人人做人人爱少妇 | 成熟女人特级毛片www免费 | 动漫av一区二区在线观看 | 国产精品无码mv在线观看 | 波多野结衣av在线观看 | 国产成人无码专区 | 亚洲国精产品一二二线 | 国产亚洲精品久久久久久大师 | 婷婷综合久久中文字幕蜜桃三电影 | 成人性做爰aaa片免费看 | 丰满少妇弄高潮了www | 国产激情综合五月久久 | 色婷婷av一区二区三区之红樱桃 | 亚洲人成影院在线观看 | 波多野结衣高清一区二区三区 | 色综合久久久无码网中文 | 激情综合激情五月俺也去 | 乱人伦人妻中文字幕无码 | 日韩精品无码一本二本三本色 | 未满小14洗澡无码视频网站 | 真人与拘做受免费视频 | 国产精品无码永久免费888 | 亚洲综合久久一区二区 | 人妻人人添人妻人人爱 | 久久www免费人成人片 | 免费无码肉片在线观看 | 亚洲熟妇自偷自拍另类 | 久青草影院在线观看国产 | 亚洲日韩一区二区三区 | 欧美第一黄网免费网站 | 日本一区二区三区免费高清 | 人妻夜夜爽天天爽三区 | 欧美人妻一区二区三区 | 中国大陆精品视频xxxx | 国产熟妇高潮叫床视频播放 | 国产成人无码一二三区视频 | 强开小婷嫩苞又嫩又紧视频 | 国内精品人妻无码久久久影院蜜桃 | 秋霞特色aa大片 | 亚洲中文字幕成人无码 | 一个人看的www免费视频在线观看 | 久久精品人人做人人综合试看 | 亚洲国产综合无码一区 | 国产成人综合色在线观看网站 | 欧美老熟妇乱xxxxx | 久久综合狠狠综合久久综合88 | 丰满少妇人妻久久久久久 | 波多野结衣乳巨码无在线观看 | 图片区 小说区 区 亚洲五月 | 18禁黄网站男男禁片免费观看 | 国产激情无码一区二区 | 国产成人无码专区 | 香蕉久久久久久av成人 | 亚洲人成网站在线播放942 | 成人无码视频在线观看网站 | 人人澡人摸人人添 | 亚洲熟女一区二区三区 | 国产偷自视频区视频 | 精品久久综合1区2区3区激情 | 欧美变态另类xxxx | 人人妻人人澡人人爽人人精品 | 亚洲 高清 成人 动漫 | 一二三四在线观看免费视频 | 色老头在线一区二区三区 | 国产亚洲视频中文字幕97精品 | 国产片av国语在线观看 | 国产精品香蕉在线观看 | 国产激情一区二区三区 | 婷婷六月久久综合丁香 | 初尝人妻少妇中文字幕 | 亚洲伊人久久精品影院 | 精品欧美一区二区三区久久久 | 四虎国产精品免费久久 | 在线观看国产午夜福利片 | 亚洲一区二区三区国产精华液 | 国产成人综合色在线观看网站 | 欧美日韩视频无码一区二区三 | 亚洲一区二区三区香蕉 | 久久伊人色av天堂九九小黄鸭 | 亚洲国产成人a精品不卡在线 | 亚洲国产欧美国产综合一区 | 午夜福利电影 | 国产成人综合美国十次 | 国产熟妇高潮叫床视频播放 | 55夜色66夜色国产精品视频 | 沈阳熟女露脸对白视频 | 野狼第一精品社区 | 国产绳艺sm调教室论坛 | 国产av剧情md精品麻豆 | 国产福利视频一区二区 | 水蜜桃色314在线观看 | 天天躁夜夜躁狠狠是什么心态 | 99er热精品视频 | 日韩人妻无码一区二区三区久久99 | 荡女精品导航 | 日韩亚洲欧美中文高清在线 | 一区二区三区高清视频一 | 国产精品久久久久9999小说 | 色欲人妻aaaaaaa无码 | 东北女人啪啪对白 | 国产精品久久精品三级 | 国产精品久久国产精品99 | 欧美日本精品一区二区三区 | 日本xxxx色视频在线观看免费 | 日本www一道久久久免费榴莲 | 国产日产欧产精品精品app | 久久天天躁夜夜躁狠狠 | 国产黑色丝袜在线播放 | 精品国精品国产自在久国产87 | 亚洲色www成人永久网址 | 国产精品a成v人在线播放 | 性欧美牲交xxxxx视频 | 欧美成人免费全部网站 | 1000部夫妻午夜免费 | 色婷婷av一区二区三区之红樱桃 | 少女韩国电视剧在线观看完整 | 国产在线精品一区二区高清不卡 | 免费国产黄网站在线观看 | 天下第一社区视频www日本 | 中文字幕av无码一区二区三区电影 | 九九综合va免费看 | 全球成人中文在线 | 青春草在线视频免费观看 | 三上悠亚人妻中文字幕在线 | 国产亚洲视频中文字幕97精品 | 国产精品丝袜黑色高跟鞋 | 天干天干啦夜天干天2017 | 爆乳一区二区三区无码 | 人人爽人人爽人人片av亚洲 | 鲁鲁鲁爽爽爽在线视频观看 | 久久婷婷五月综合色国产香蕉 | 精品久久久中文字幕人妻 | 在线a亚洲视频播放在线观看 | 成人欧美一区二区三区黑人 | 极品嫩模高潮叫床 | 在线观看国产一区二区三区 | 欧洲极品少妇 | 亚洲欧洲日本综合aⅴ在线 | 大乳丰满人妻中文字幕日本 | 国产午夜亚洲精品不卡 | 亚洲七七久久桃花影院 | 动漫av网站免费观看 | 狂野欧美激情性xxxx | 国产人妖乱国产精品人妖 | 亚洲精品无码人妻无码 | 久热国产vs视频在线观看 | 熟妇人妻中文av无码 | 国产乱人伦av在线无码 | 特黄特色大片免费播放器图片 | 四虎国产精品一区二区 | 无码免费一区二区三区 | 2020最新国产自产精品 | 国产偷抇久久精品a片69 | 日韩欧美中文字幕在线三区 | 色噜噜亚洲男人的天堂 | 男女猛烈xx00免费视频试看 | 熟女少妇人妻中文字幕 | 日本xxxx色视频在线观看免费 | 在线播放免费人成毛片乱码 | 国内揄拍国内精品少妇国语 | 亚洲大尺度无码无码专区 | 少妇的肉体aa片免费 | 亚洲自偷自偷在线制服 | 亚洲精品综合一区二区三区在线 | 久久国产精品精品国产色婷婷 | 精品偷拍一区二区三区在线看 | 在线观看国产午夜福利片 | www成人国产高清内射 | 亚洲 欧美 激情 小说 另类 | 中国女人内谢69xxxxxa片 | 蜜桃视频插满18在线观看 | 动漫av一区二区在线观看 | 国产免费久久久久久无码 | 亚洲精品一区二区三区四区五区 | 一本大道伊人av久久综合 | 丰满妇女强制高潮18xxxx | 99久久人妻精品免费一区 | 亚洲欧美国产精品专区久久 | 强奷人妻日本中文字幕 | 老司机亚洲精品影院无码 | 中文字幕乱妇无码av在线 | 欧美性生交活xxxxxdddd | 亚洲精品成人av在线 | 人妻少妇精品无码专区二区 | 午夜福利一区二区三区在线观看 | 国产激情艳情在线看视频 | 一本大道久久东京热无码av | 国产精品鲁鲁鲁 | 国产性生大片免费观看性 | 夫妻免费无码v看片 | 伊人久久大香线蕉亚洲 | 无码av岛国片在线播放 | 成人片黄网站色大片免费观看 | 中文字幕+乱码+中文字幕一区 | 久久99精品国产麻豆 | 中文字幕 人妻熟女 | 国产美女极度色诱视频www | 亚洲精品一区二区三区婷婷月 | 国产特级毛片aaaaaa高潮流水 | 午夜无码区在线观看 | 久久综合给久久狠狠97色 | 亚洲日韩av一区二区三区四区 | 一个人看的www免费视频在线观看 | 国产超碰人人爽人人做人人添 | 天天av天天av天天透 | 亚洲熟妇色xxxxx欧美老妇y | 国内丰满熟女出轨videos | 未满小14洗澡无码视频网站 | 啦啦啦www在线观看免费视频 | 欧美一区二区三区视频在线观看 | 国产真实伦对白全集 | 国产精品无码一区二区三区不卡 | 在线看片无码永久免费视频 | 久久久www成人免费毛片 | 好屌草这里只有精品 | 国产高清av在线播放 | 国产9 9在线 | 中文 | 亚洲国产精品无码久久久久高潮 | 少妇久久久久久人妻无码 | 水蜜桃av无码 | 国产成人一区二区三区在线观看 | 麻豆国产97在线 | 欧洲 | 欧美兽交xxxx×视频 | 亚洲欧美日韩成人高清在线一区 | 18禁黄网站男男禁片免费观看 | 男女作爱免费网站 | 日本成熟视频免费视频 | 国产精品久久国产三级国 | 亚洲春色在线视频 | 午夜理论片yy44880影院 | 人妻天天爽夜夜爽一区二区 | 亚洲大尺度无码无码专区 | 丁香啪啪综合成人亚洲 | 亚洲s色大片在线观看 | 久久精品女人的天堂av | 黑人玩弄人妻中文在线 | 麻豆国产人妻欲求不满谁演的 | 亚洲 欧美 激情 小说 另类 | 国产成人综合在线女婷五月99播放 | 18禁黄网站男男禁片免费观看 | 久久久久久av无码免费看大片 | 国产凸凹视频一区二区 | 中文字幕 亚洲精品 第1页 | 1000部啪啪未满十八勿入下载 | 人妻互换免费中文字幕 | 久久人人97超碰a片精品 | 成 人 网 站国产免费观看 | 国产精品久久久久7777 | 在线观看国产午夜福利片 | 强奷人妻日本中文字幕 | 天天av天天av天天透 | 77777熟女视频在线观看 а天堂中文在线官网 | 久久天天躁狠狠躁夜夜免费观看 | 女人被爽到呻吟gif动态图视看 | 亚洲精品久久久久avwww潮水 | 黄网在线观看免费网站 | 波多野结衣高清一区二区三区 | 午夜精品一区二区三区在线观看 | 乱人伦人妻中文字幕无码 | 无码毛片视频一区二区本码 | 高清不卡一区二区三区 | 一二三四在线观看免费视频 | 精品日本一区二区三区在线观看 | 久久 国产 尿 小便 嘘嘘 | 国产成人精品久久亚洲高清不卡 | 亚洲一区av无码专区在线观看 | 日韩精品a片一区二区三区妖精 | 国内综合精品午夜久久资源 | 日本一区二区三区免费高清 | 日韩欧美中文字幕在线三区 | 日韩精品乱码av一区二区 | 在线看片无码永久免费视频 | 99久久精品国产一区二区蜜芽 | 岛国片人妻三上悠亚 | 荫蒂被男人添的好舒服爽免费视频 | 青春草在线视频免费观看 | 成人无码视频免费播放 | 亚洲伊人久久精品影院 | 久久婷婷五月综合色国产香蕉 | 久久综合香蕉国产蜜臀av | 国产激情无码一区二区app | 久久亚洲国产成人精品性色 | 亚洲精品一区二区三区四区五区 | 疯狂三人交性欧美 | 嫩b人妻精品一区二区三区 | 无码精品国产va在线观看dvd | 午夜不卡av免费 一本久久a久久精品vr综合 | 伦伦影院午夜理论片 | 永久免费观看美女裸体的网站 | 国产精品亚洲专区无码不卡 | 日本一区二区更新不卡 | 中文字幕无码日韩专区 | 中文字幕无码免费久久99 | 沈阳熟女露脸对白视频 | 久久久无码中文字幕久... | 国产欧美亚洲精品a | 成人免费视频一区二区 | 99er热精品视频 | 99在线 | 亚洲 | 国产精品亚洲专区无码不卡 | 老司机亚洲精品影院无码 | 精品午夜福利在线观看 | 无码人妻丰满熟妇区毛片18 | 亚洲a无码综合a国产av中文 | 亚洲春色在线视频 | 亚洲国产午夜精品理论片 | 国产综合在线观看 | 欧美肥老太牲交大战 | 人人妻人人澡人人爽人人精品浪潮 | 狠狠cao日日穞夜夜穞av | 在线播放免费人成毛片乱码 | 免费国产黄网站在线观看 | 亚洲国产精品久久久天堂 | 99er热精品视频 | 波多野结衣av一区二区全免费观看 | 色综合视频一区二区三区 | 精品国产精品久久一区免费式 | 久久精品无码一区二区三区 | 伊人色综合久久天天小片 | 久久精品成人欧美大片 | 久久国产劲爆∧v内射 | 又大又紧又粉嫩18p少妇 | 国产精品亚洲lv粉色 | 亚洲欧洲日本综合aⅴ在线 | 装睡被陌生人摸出水好爽 | 日韩欧美中文字幕公布 | 精品人妻中文字幕有码在线 | 国产舌乚八伦偷品w中 | 国产尤物精品视频 | 丰满人妻被黑人猛烈进入 | 国产精品18久久久久久麻辣 | 日韩少妇白浆无码系列 | 黑人大群体交免费视频 | 少妇被黑人到高潮喷出白浆 | 国产熟妇另类久久久久 | 在线欧美精品一区二区三区 | 日本乱偷人妻中文字幕 | 熟妇女人妻丰满少妇中文字幕 | 少妇太爽了在线观看 | 中文字幕无码视频专区 | 久久人人爽人人爽人人片ⅴ | 俺去俺来也在线www色官网 | 99久久久无码国产aaa精品 | 熟妇激情内射com | 日日摸日日碰夜夜爽av | 四虎4hu永久免费 | aⅴ亚洲 日韩 色 图网站 播放 | 性欧美疯狂xxxxbbbb | 精品 日韩 国产 欧美 视频 | 国产女主播喷水视频在线观看 | 伊人色综合久久天天小片 | 亚洲娇小与黑人巨大交 | 国产精品高潮呻吟av久久4虎 | 国产麻豆精品精东影业av网站 | 内射老妇bbwx0c0ck | 理论片87福利理论电影 | 国产亚av手机在线观看 | 久久久久免费精品国产 | 国产成人av免费观看 | 久久久久免费精品国产 | 亚洲精品www久久久 | 国产97色在线 | 免 | 国产成人久久精品流白浆 | 成 人 免费观看网站 | 久久久精品人妻久久影视 | 久久人人爽人人人人片 | 中文字幕乱码人妻无码久久 | 又湿又紧又大又爽a视频国产 | 熟妇人妻中文av无码 | 蜜臀av在线播放 久久综合激激的五月天 | 亚洲熟妇色xxxxx亚洲 | 日本www一道久久久免费榴莲 | 久久久久se色偷偷亚洲精品av | 中文字幕无线码 | 久久久中文久久久无码 | 蜜臀av在线观看 在线欧美精品一区二区三区 | 内射爽无广熟女亚洲 | 夜夜躁日日躁狠狠久久av | 成在人线av无码免观看麻豆 | 成人无码视频免费播放 | 水蜜桃亚洲一二三四在线 | 啦啦啦www在线观看免费视频 | 国产成人综合色在线观看网站 | 亚洲 欧美 激情 小说 另类 | 久久久久久国产精品无码下载 | 国产精品手机免费 | 国产精品igao视频网 | 人人妻人人澡人人爽精品欧美 | 一个人看的视频www在线 | 蜜臀aⅴ国产精品久久久国产老师 | 真人与拘做受免费视频 | 亚洲日本va午夜在线电影 | 久久国产劲爆∧v内射 | 欧洲vodafone精品性 | 丝袜美腿亚洲一区二区 | 少女韩国电视剧在线观看完整 | 亚洲精品国偷拍自产在线麻豆 | 又大又硬又黄的免费视频 | 国产精品久久久久久亚洲毛片 | 午夜精品久久久内射近拍高清 | 无码乱肉视频免费大全合集 | 亚洲精品国产精品乱码视色 | 无码人妻精品一区二区三区不卡 | 婷婷丁香五月天综合东京热 | 久久久久国色av免费观看性色 | 国产精品人人爽人人做我的可爱 | 国产特级毛片aaaaaaa高清 | 国产精品久久久久久久9999 | 国产精品.xx视频.xxtv | 麻豆人妻少妇精品无码专区 | 亚洲 高清 成人 动漫 | √天堂中文官网8在线 | 久久五月精品中文字幕 | 免费观看黄网站 | 无人区乱码一区二区三区 | 成人无码影片精品久久久 | a在线观看免费网站大全 | 天天躁日日躁狠狠躁免费麻豆 | 国产免费久久久久久无码 | www一区二区www免费 | 纯爱无遮挡h肉动漫在线播放 | 内射后入在线观看一区 | 色五月五月丁香亚洲综合网 | 日韩视频 中文字幕 视频一区 | 久久国产精品精品国产色婷婷 | 成人片黄网站色大片免费观看 | 国产麻豆精品一区二区三区v视界 | 国产艳妇av在线观看果冻传媒 | 性欧美牲交xxxxx视频 | 国产成人无码a区在线观看视频app | 中文字幕无码日韩欧毛 | 欧美 日韩 人妻 高清 中文 | 日本熟妇乱子伦xxxx | 日韩精品成人一区二区三区 | 成人欧美一区二区三区 | 久久久久免费看成人影片 | 粗大的内捧猛烈进出视频 | 99久久99久久免费精品蜜桃 | 18无码粉嫩小泬无套在线观看 | 一区二区三区高清视频一 | 台湾无码一区二区 | 国产精品国产三级国产专播 | 无码国产激情在线观看 | 亚洲色成人中文字幕网站 | 天天摸天天透天天添 | 精品一区二区三区无码免费视频 | 国产成人无码区免费内射一片色欲 | 欧美老人巨大xxxx做受 | 欧美 丝袜 自拍 制服 另类 | 99久久久国产精品无码免费 | 中文字幕人妻无码一区二区三区 | 精品久久久无码中文字幕 | 色噜噜亚洲男人的天堂 | 国产成人亚洲综合无码 | 美女极度色诱视频国产 | 国产卡一卡二卡三 | 国产精品视频免费播放 | 国产婷婷色一区二区三区在线 | 国产特级毛片aaaaaa高潮流水 | 国产精品久久久午夜夜伦鲁鲁 | 婷婷丁香六月激情综合啪 | 亚洲国产一区二区三区在线观看 | 中文字幕乱妇无码av在线 | 久久国产36精品色熟妇 | 18精品久久久无码午夜福利 | 日韩亚洲欧美中文高清在线 | 99久久婷婷国产综合精品青草免费 | 国产猛烈高潮尖叫视频免费 | 国产卡一卡二卡三 | 亚洲一区二区三区香蕉 | 牲交欧美兽交欧美 | 夜夜高潮次次欢爽av女 | 成年美女黄网站色大免费全看 | 国产日产欧产精品精品app | 久久人人爽人人爽人人片ⅴ | 一本加勒比波多野结衣 | 国产超碰人人爽人人做人人添 | 99久久久国产精品无码免费 | 精品亚洲韩国一区二区三区 | 欧美 丝袜 自拍 制服 另类 | 成人精品天堂一区二区三区 | 亚洲精品一区二区三区婷婷月 | 久久精品一区二区三区四区 | 久久精品成人欧美大片 | 搡女人真爽免费视频大全 | 青青草原综合久久大伊人精品 | 久久久久免费看成人影片 | 色窝窝无码一区二区三区色欲 | 午夜丰满少妇性开放视频 | 亚洲无人区一区二区三区 | 2019午夜福利不卡片在线 | 精品无码成人片一区二区98 | 日本大乳高潮视频在线观看 | 欧美亚洲日韩国产人成在线播放 | 欧美人与动性行为视频 | 性啪啪chinese东北女人 | 动漫av网站免费观看 | 亚洲精品一区二区三区在线观看 | 两性色午夜免费视频 | 嫩b人妻精品一区二区三区 | 男女下面进入的视频免费午夜 | 内射后入在线观看一区 | 樱花草在线播放免费中文 | 18无码粉嫩小泬无套在线观看 | 国产精品.xx视频.xxtv | 亚洲一区二区三区 | 色情久久久av熟女人妻网站 | 国产舌乚八伦偷品w中 | 免费无码午夜福利片69 | 免费无码的av片在线观看 | 亚洲一区二区三区国产精华液 | 国产猛烈高潮尖叫视频免费 | 国産精品久久久久久久 | 午夜福利不卡在线视频 | 夜夜夜高潮夜夜爽夜夜爰爰 | 小鲜肉自慰网站xnxx | 国产两女互慰高潮视频在线观看 | 亚洲成a人片在线观看无码3d | 婷婷丁香六月激情综合啪 | 成人无码精品1区2区3区免费看 | 红桃av一区二区三区在线无码av | 少妇性l交大片欧洲热妇乱xxx | 18无码粉嫩小泬无套在线观看 | 久久精品女人的天堂av | 荡女精品导航 | 久青草影院在线观看国产 | 久久精品国产精品国产精品污 | 久久99精品久久久久婷婷 | 麻豆国产丝袜白领秘书在线观看 | 99久久人妻精品免费一区 | 国产午夜福利100集发布 | 国产激情艳情在线看视频 | 国产精品99久久精品爆乳 | 又湿又紧又大又爽a视频国产 | 久久久无码中文字幕久... | 精品国偷自产在线 | 精品无码一区二区三区的天堂 | 俺去俺来也www色官网 | 76少妇精品导航 | 人人爽人人澡人人人妻 | 亚洲日本va中文字幕 | 澳门永久av免费网站 | 无码人妻少妇伦在线电影 | 高潮喷水的毛片 | 亚洲中文字幕久久无码 | 丝袜美腿亚洲一区二区 | 国产精品亚洲lv粉色 | 色 综合 欧美 亚洲 国产 | 亚洲人交乣女bbw | 日韩人妻少妇一区二区三区 | 精品亚洲韩国一区二区三区 | 日本爽爽爽爽爽爽在线观看免 | 午夜精品久久久内射近拍高清 | 国产亚洲精品久久久久久久久动漫 | 日本高清一区免费中文视频 | 国产精品久久久久久亚洲影视内衣 | 东京热无码av男人的天堂 | 九九综合va免费看 | 日欧一片内射va在线影院 | 久久久久免费精品国产 | 亚洲色大成网站www国产 | 成人毛片一区二区 | 激情五月综合色婷婷一区二区 | 国产精品高潮呻吟av久久4虎 | 日本肉体xxxx裸交 | 亚洲精品美女久久久久久久 | 国产激情一区二区三区 | 女人被爽到呻吟gif动态图视看 | 国产人成高清在线视频99最全资源 | 人妻有码中文字幕在线 | 少妇被粗大的猛进出69影院 | 亚洲精品一区三区三区在线观看 | 无码毛片视频一区二区本码 | 久久精品99久久香蕉国产色戒 | 久久久久亚洲精品男人的天堂 | 一本色道久久综合亚洲精品不卡 | 国产精品嫩草久久久久 | 中文字幕+乱码+中文字幕一区 | 狠狠cao日日穞夜夜穞av | 三上悠亚人妻中文字幕在线 | 在教室伦流澡到高潮hnp视频 | 无码人妻精品一区二区三区不卡 | 国产香蕉97碰碰久久人人 | 人人妻在人人 | 强辱丰满人妻hd中文字幕 | 国产特级毛片aaaaaa高潮流水 | 国产精品久久久 | 久久久久成人片免费观看蜜芽 | 精品久久综合1区2区3区激情 | 无码乱肉视频免费大全合集 | 99视频精品全部免费免费观看 | 成熟女人特级毛片www免费 | 国产午夜无码精品免费看 | 55夜色66夜色国产精品视频 | 亚洲综合色区中文字幕 | 人妻夜夜爽天天爽三区 | 人人妻人人澡人人爽人人精品 | 日本熟妇乱子伦xxxx | 亚洲欧洲日本综合aⅴ在线 | 精品无人区无码乱码毛片国产 | 免费国产成人高清在线观看网站 | 国产猛烈高潮尖叫视频免费 | 扒开双腿疯狂进出爽爽爽视频 | 亚洲人成网站免费播放 | 亚洲人成无码网www | 中文字幕 人妻熟女 | 国产 浪潮av性色四虎 | 欧美怡红院免费全部视频 | 日韩少妇内射免费播放 | 一个人看的www免费视频在线观看 | 精品人妻人人做人人爽夜夜爽 | 精品国产一区二区三区av 性色 | 国产免费久久久久久无码 | 国色天香社区在线视频 | 少妇性荡欲午夜性开放视频剧场 | 亚洲一区二区三区在线观看网站 | 永久免费观看国产裸体美女 | 久久久久久国产精品无码下载 | 伊人久久大香线蕉亚洲 | 日本精品人妻无码77777 天堂一区人妻无码 | 99视频精品全部免费免费观看 | av在线亚洲欧洲日产一区二区 | 免费观看激色视频网站 | 亚洲综合在线一区二区三区 | 国语精品一区二区三区 | 久久久久久久人妻无码中文字幕爆 | 荡女精品导航 | 精品少妇爆乳无码av无码专区 | 国产婷婷色一区二区三区在线 | 桃花色综合影院 | 一本久道久久综合狠狠爱 | 老熟妇仑乱视频一区二区 | 国产内射爽爽大片视频社区在线 | 亚洲国产成人a精品不卡在线 | 国产偷自视频区视频 | 国精品人妻无码一区二区三区蜜柚 | 又大又硬又黄的免费视频 | 麻豆av传媒蜜桃天美传媒 | 国产又爽又黄又刺激的视频 | 中文无码精品a∨在线观看不卡 | 欧美日韩精品 | 国产精品高潮呻吟av久久 | 女高中生第一次破苞av | 最新国产乱人伦偷精品免费网站 | 国产肉丝袜在线观看 | 日本在线高清不卡免费播放 | 乱人伦人妻中文字幕无码 | 激情内射日本一区二区三区 | 国产亚洲视频中文字幕97精品 | 亚洲国产一区二区三区在线观看 | 国产亚洲欧美在线专区 | 麻豆精品国产精华精华液好用吗 | 综合激情五月综合激情五月激情1 | 天天爽夜夜爽夜夜爽 | 国产一区二区三区影院 | 蜜桃视频韩日免费播放 | 亚洲男人av天堂午夜在 | 永久免费精品精品永久-夜色 | 丰满少妇人妻久久久久久 | 一本久道久久综合婷婷五月 | 牲欲强的熟妇农村老妇女视频 | 亚洲成色www久久网站 | 人人爽人人澡人人高潮 | 领导边摸边吃奶边做爽在线观看 | 成人精品视频一区二区 | 无套内谢的新婚少妇国语播放 | 亚洲中文字幕无码中字 | 色综合视频一区二区三区 | 欧美激情一区二区三区成人 | 中文字幕乱妇无码av在线 | 在线成人www免费观看视频 | 纯爱无遮挡h肉动漫在线播放 | 亚洲人成网站在线播放942 | 东京热无码av男人的天堂 | 日韩精品久久久肉伦网站 | 精品aⅴ一区二区三区 | 国产激情艳情在线看视频 | 国内老熟妇对白xxxxhd | 亚洲色无码一区二区三区 | 久久精品国产一区二区三区肥胖 | 噜噜噜亚洲色成人网站 | 亚洲精品国偷拍自产在线麻豆 | 高潮喷水的毛片 | 久久久久久国产精品无码下载 | 又色又爽又黄的美女裸体网站 | 国产国语老龄妇女a片 | 久久久国产精品无码免费专区 | 国产莉萝无码av在线播放 | 中文字幕无码乱人伦 | 蜜桃视频插满18在线观看 | 香港三级日本三级妇三级 | 国产深夜福利视频在线 | 国产欧美熟妇另类久久久 | 黑人巨大精品欧美一区二区 | 精品无码国产一区二区三区av | 中文精品无码中文字幕无码专区 | 欧美激情综合亚洲一二区 | 丰满岳乱妇在线观看中字无码 | 激情五月综合色婷婷一区二区 | 成人无码精品一区二区三区 | 日韩av无码一区二区三区 | 亚洲综合无码久久精品综合 | 伊人久久大香线蕉亚洲 | 国产精品久久久久久亚洲毛片 | 国产人妻精品一区二区三区不卡 | 最新国产麻豆aⅴ精品无码 | 成人无码精品1区2区3区免费看 | 欧美自拍另类欧美综合图片区 | a国产一区二区免费入口 | 中文字幕av日韩精品一区二区 | 精品国产一区二区三区四区在线看 | 高清国产亚洲精品自在久久 | 国产精品久久精品三级 | 亚洲自偷自偷在线制服 | 午夜精品久久久内射近拍高清 | 国产人成高清在线视频99最全资源 | 国产精品久久久久久亚洲毛片 | 国产亚洲精品久久久久久国模美 | 亚洲伊人久久精品影院 | 中文字幕久久久久人妻 | 亚洲精品一区二区三区在线 | 亚洲国产av精品一区二区蜜芽 | 无码av免费一区二区三区试看 | 国产内射爽爽大片视频社区在线 | 女高中生第一次破苞av | 午夜无码人妻av大片色欲 | 国产精品无码永久免费888 | 国产激情无码一区二区app | 精品无码成人片一区二区98 | 国产凸凹视频一区二区 | 特大黑人娇小亚洲女 | 国产偷国产偷精品高清尤物 | 永久黄网站色视频免费直播 | 中文字幕乱码中文乱码51精品 | 久久精品人人做人人综合试看 | 无码精品国产va在线观看dvd | 又大又硬又黄的免费视频 | 丝袜 中出 制服 人妻 美腿 | 亚洲综合精品香蕉久久网 | 乱中年女人伦av三区 | 欧洲欧美人成视频在线 | 亚洲熟妇色xxxxx亚洲 | 天天躁夜夜躁狠狠是什么心态 | 亚洲a无码综合a国产av中文 | 国产欧美精品一区二区三区 | 无码播放一区二区三区 | 偷窥日本少妇撒尿chinese | 欧美 亚洲 国产 另类 | 99久久久无码国产aaa精品 | 久久国产精品二国产精品 | 国产精品高潮呻吟av久久4虎 | 3d动漫精品啪啪一区二区中 | 国产无遮挡吃胸膜奶免费看 | 成年美女黄网站色大免费视频 | 日韩无码专区 | 内射白嫩少妇超碰 | 国产亚洲精品久久久久久国模美 | 老太婆性杂交欧美肥老太 | 人妻与老人中文字幕 | 中文字幕色婷婷在线视频 | 国产成人无码av在线影院 | 国产成人精品一区二区在线小狼 | 午夜精品一区二区三区的区别 | 日本熟妇人妻xxxxx人hd | 亚洲熟女一区二区三区 | 亚洲の无码国产の无码影院 | 国产成人无码av一区二区 | 狠狠噜狠狠狠狠丁香五月 | 老熟妇乱子伦牲交视频 | 亚欧洲精品在线视频免费观看 | 亚洲精品国偷拍自产在线观看蜜桃 | 18禁黄网站男男禁片免费观看 | 51国偷自产一区二区三区 | 亚洲大尺度无码无码专区 | 国产乱子伦视频在线播放 | 免费国产成人高清在线观看网站 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 中文字幕久久久久人妻 | 色噜噜亚洲男人的天堂 | 亚拍精品一区二区三区探花 | 久久国语露脸国产精品电影 | 日韩成人一区二区三区在线观看 | 久久亚洲日韩精品一区二区三区 | 国产乱人伦av在线无码 | 色狠狠av一区二区三区 | 波多野结衣av一区二区全免费观看 | 女人被男人躁得好爽免费视频 | 欧洲美熟女乱又伦 | 国产成人综合美国十次 | 国产日产欧产精品精品app | 国产人妻精品午夜福利免费 | 亚洲欧洲日本无在线码 | 国产精品沙发午睡系列 | 国产情侣作爱视频免费观看 | 欧美猛少妇色xxxxx | 高清不卡一区二区三区 | 99国产精品白浆在线观看免费 | 日本欧美一区二区三区乱码 | 中文字幕乱码中文乱码51精品 | 1000部啪啪未满十八勿入下载 | 国产疯狂伦交大片 | 国产精品资源一区二区 | 最新国产乱人伦偷精品免费网站 | 亚洲精品国偷拍自产在线观看蜜桃 | 又大又硬又黄的免费视频 | 妺妺窝人体色www在线小说 | 九九综合va免费看 | 欧美熟妇另类久久久久久不卡 | 国产精品亚洲а∨无码播放麻豆 | 国产精品亚洲а∨无码播放麻豆 | 十八禁视频网站在线观看 | 成人无码影片精品久久久 | 国产精品久久久av久久久 | 日本精品高清一区二区 | 伊人久久大香线焦av综合影院 | 国产精品福利视频导航 | 国产三级精品三级男人的天堂 | 亚洲а∨天堂久久精品2021 | 久久久久久国产精品无码下载 | 亚洲欧美精品aaaaaa片 | 九一九色国产 | 自拍偷自拍亚洲精品被多人伦好爽 | 男女作爱免费网站 | 最近的中文字幕在线看视频 | 98国产精品综合一区二区三区 | 兔费看少妇性l交大片免费 | 亚洲国产午夜精品理论片 | 色综合视频一区二区三区 | 免费网站看v片在线18禁无码 | 免费无码一区二区三区蜜桃大 | 超碰97人人做人人爱少妇 | 丁香啪啪综合成人亚洲 | 在线亚洲高清揄拍自拍一品区 | 久久久久se色偷偷亚洲精品av | 欧美xxxxx精品 | 中国女人内谢69xxxx | 少妇厨房愉情理9仑片视频 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 免费国产成人高清在线观看网站 | 精品国产青草久久久久福利 | 99久久精品国产一区二区蜜芽 | 日韩av激情在线观看 | 亚洲精品一区二区三区大桥未久 | 国产亚洲精品久久久ai换 | 55夜色66夜色国产精品视频 | 国产精品久久精品三级 | 国产亲子乱弄免费视频 | 久久99精品国产麻豆 | 亚洲熟妇自偷自拍另类 | 午夜精品久久久久久久久 | 人人妻人人澡人人爽精品欧美 | 亲嘴扒胸摸屁股激烈网站 | 国产成人无码区免费内射一片色欲 | 国产精品亚洲专区无码不卡 | 久9re热视频这里只有精品 | 在线成人www免费观看视频 | 成 人影片 免费观看 | 欧美老人巨大xxxx做受 | 精品国精品国产自在久国产87 | 国产成人午夜福利在线播放 | 亚洲码国产精品高潮在线 | 国产亚洲精品久久久久久大师 | 无码乱肉视频免费大全合集 | 大乳丰满人妻中文字幕日本 | 内射老妇bbwx0c0ck | 欧美日韩视频无码一区二区三 | 亚洲中文字幕成人无码 | 亚洲欧美日韩综合久久久 | 久久人人97超碰a片精品 | 亚洲人成无码网www | 精品少妇爆乳无码av无码专区 | 国产一区二区不卡老阿姨 | 蜜桃视频韩日免费播放 | 日韩人妻无码中文字幕视频 | 麻花豆传媒剧国产免费mv在线 | 国内精品人妻无码久久久影院 | 欧美人与禽zoz0性伦交 | 亚洲 日韩 欧美 成人 在线观看 | 免费播放一区二区三区 | 国产成人综合美国十次 | 日日夜夜撸啊撸 | 午夜精品一区二区三区的区别 | 无码一区二区三区在线观看 | 成人一区二区免费视频 | 日产精品高潮呻吟av久久 | 图片区 小说区 区 亚洲五月 | 精品无码一区二区三区的天堂 | 99久久人妻精品免费二区 | 精品一区二区三区波多野结衣 | 黑人玩弄人妻中文在线 | 日本www一道久久久免费榴莲 | 红桃av一区二区三区在线无码av | 女人被男人爽到呻吟的视频 | 国内精品久久毛片一区二区 | 99久久人妻精品免费一区 | 欧美激情内射喷水高潮 | 性史性农村dvd毛片 | 亚洲精品欧美二区三区中文字幕 | 欧美亚洲国产一区二区三区 | 国产又爽又黄又刺激的视频 | 黑人巨大精品欧美一区二区 | 精品人妻中文字幕有码在线 | 国内综合精品午夜久久资源 | 98国产精品综合一区二区三区 | 国语精品一区二区三区 | 爆乳一区二区三区无码 | 亚洲大尺度无码无码专区 | 久久综合给久久狠狠97色 | 国产综合色产在线精品 | 亚洲国产精华液网站w | 亚洲色成人中文字幕网站 | 97精品国产97久久久久久免费 | 午夜福利试看120秒体验区 | 真人与拘做受免费视频一 | 国精品人妻无码一区二区三区蜜柚 | 国产精品视频免费播放 | 亚洲精品一区二区三区在线观看 | 一本无码人妻在中文字幕免费 | 色综合久久久无码中文字幕 | 色综合久久88色综合天天 | 国精品人妻无码一区二区三区蜜柚 | 丰满岳乱妇在线观看中字无码 | 2020最新国产自产精品 | 国语自产偷拍精品视频偷 | 亚洲区欧美区综合区自拍区 | 免费看男女做好爽好硬视频 | 日日麻批免费40分钟无码 | 亚洲成a人一区二区三区 | www一区二区www免费 | v一区无码内射国产 | 亚洲人成影院在线无码按摩店 | 国产激情精品一区二区三区 | 乱人伦人妻中文字幕无码久久网 | 国产精品手机免费 | 欧美熟妇另类久久久久久不卡 | 久久久久国色av免费观看性色 | 国产精品久久久久久亚洲毛片 | 成人精品视频一区二区 | 性欧美牲交在线视频 | 一区二区三区乱码在线 | 欧洲 | 亚洲中文字幕乱码av波多ji | 亚洲国产精品一区二区美利坚 | 99久久99久久免费精品蜜桃 | 色诱久久久久综合网ywww | 2020久久香蕉国产线看观看 | 成人免费视频一区二区 | 国产成人精品优优av | 狠狠cao日日穞夜夜穞av | 亚洲毛片av日韩av无码 | 青青久在线视频免费观看 | 国产香蕉97碰碰久久人人 | 暴力强奷在线播放无码 | 国产另类ts人妖一区二区 | 人妻少妇精品无码专区动漫 | 精品夜夜澡人妻无码av蜜桃 | 日本丰满熟妇videos | 丝袜足控一区二区三区 | 国产又粗又硬又大爽黄老大爷视 | 亚洲熟妇自偷自拍另类 | 中文字幕色婷婷在线视频 | 色婷婷av一区二区三区之红樱桃 | 国产成人精品久久亚洲高清不卡 | 美女毛片一区二区三区四区 | 亚洲 激情 小说 另类 欧美 | 国产又爽又猛又粗的视频a片 | 成人免费视频一区二区 | 无套内谢老熟女 | 少妇无码av无码专区在线观看 | 精品aⅴ一区二区三区 | 国产又粗又硬又大爽黄老大爷视 | 国产99久久精品一区二区 | 国产suv精品一区二区五 | 久久久中文字幕日本无吗 | 久久久久久a亚洲欧洲av冫 | 中文字幕人妻丝袜二区 | 久久久成人毛片无码 | 在线观看免费人成视频 | 国产精品久久久久无码av色戒 | 黑人玩弄人妻中文在线 | 欧美熟妇另类久久久久久多毛 | 亚洲色www成人永久网址 | 亚洲人成人无码网www国产 | 亚洲 日韩 欧美 成人 在线观看 | 日日躁夜夜躁狠狠躁 | 亚洲成色www久久网站 | 亚洲熟妇色xxxxx欧美老妇y | 日日橹狠狠爱欧美视频 | 国产深夜福利视频在线 | 亚洲国产精品久久人人爱 | 国产精品视频免费播放 | 婷婷五月综合激情中文字幕 | 亚洲人成影院在线无码按摩店 | 中文字幕无码免费久久9一区9 | 亚洲成av人片在线观看无码不卡 | 日韩亚洲欧美精品综合 | 2020最新国产自产精品 | 国产精品va在线观看无码 | 国产一精品一av一免费 | 免费国产黄网站在线观看 | 亚洲欧美中文字幕5发布 | 久久综合九色综合欧美狠狠 | 人人妻人人澡人人爽欧美一区 | 国产精品免费大片 | 无码人妻久久一区二区三区不卡 | 日本va欧美va欧美va精品 | 欧洲vodafone精品性 | 中文无码成人免费视频在线观看 | 日日摸天天摸爽爽狠狠97 | 小sao货水好多真紧h无码视频 | 亚洲色在线无码国产精品不卡 | 亚洲精品国产a久久久久久 | 免费人成在线视频无码 | 国产乱人偷精品人妻a片 | 亚洲色偷偷偷综合网 | 亚洲午夜久久久影院 | 国产欧美熟妇另类久久久 | 成人aaa片一区国产精品 | 色婷婷av一区二区三区之红樱桃 | 久久国语露脸国产精品电影 | 午夜福利试看120秒体验区 | 日本一本二本三区免费 | av人摸人人人澡人人超碰下载 | 日本护士毛茸茸高潮 | 成熟女人特级毛片www免费 | 亚洲国产综合无码一区 | 国产精品永久免费视频 | 无码人妻丰满熟妇区五十路百度 | 高清国产亚洲精品自在久久 | 色欲综合久久中文字幕网 | 成人精品视频一区二区 | 精品少妇爆乳无码av无码专区 | 欧美xxxx黑人又粗又长 | 欧美怡红院免费全部视频 | 在教室伦流澡到高潮hnp视频 | 精品国产一区二区三区四区在线看 | www成人国产高清内射 | 国产一区二区不卡老阿姨 | 高清不卡一区二区三区 | 亚洲国产精品成人久久蜜臀 | 无码毛片视频一区二区本码 | 在线天堂新版最新版在线8 | 亚洲国产精品毛片av不卡在线 | 亚洲综合伊人久久大杳蕉 | 青春草在线视频免费观看 | 中文字幕乱码人妻无码久久 | 日韩欧美中文字幕公布 | 亚洲精品国产精品乱码不卡 | 成人免费视频一区二区 | 亚洲综合久久一区二区 | 亚洲色www成人永久网址 | 久久久亚洲欧洲日产国码αv | 麻豆果冻传媒2021精品传媒一区下载 | 一区二区三区高清视频一 | 亚洲高清偷拍一区二区三区 | 精品成人av一区二区三区 | 日本高清一区免费中文视频 | 无码一区二区三区在线观看 | 成人无码视频免费播放 | 亚洲熟女一区二区三区 | 欧洲熟妇色 欧美 | 亚洲高清偷拍一区二区三区 | 日韩亚洲欧美精品综合 | 一本久道高清无码视频 | 无人区乱码一区二区三区 | 国产又爽又黄又刺激的视频 | 天天综合网天天综合色 | 亚洲熟熟妇xxxx | 女人被男人躁得好爽免费视频 | 久久综合香蕉国产蜜臀av | 日韩欧美中文字幕公布 | 青春草在线视频免费观看 | 欧美变态另类xxxx | 午夜精品久久久久久久 | 免费乱码人妻系列无码专区 | 亚洲精品久久久久久一区二区 | 日韩无码专区 | 亚洲人成无码网www | 免费男性肉肉影院 | 三上悠亚人妻中文字幕在线 | 少妇无码av无码专区在线观看 | 成人aaa片一区国产精品 | 亚洲精品欧美二区三区中文字幕 | 日本乱人伦片中文三区 | 精品国产一区二区三区四区在线看 | 狂野欧美性猛xxxx乱大交 | 丁香啪啪综合成人亚洲 | 欧美高清在线精品一区 | 久久人人爽人人爽人人片ⅴ | 国产精品高潮呻吟av久久4虎 | 国产黄在线观看免费观看不卡 | 性史性农村dvd毛片 | 亚洲精品无码人妻无码 | 99久久久国产精品无码免费 | 无码人妻久久一区二区三区不卡 | 俄罗斯老熟妇色xxxx | 欧美黑人巨大xxxxx | 亚洲精品鲁一鲁一区二区三区 | 中文毛片无遮挡高清免费 | 国产色精品久久人妻 | 成人免费无码大片a毛片 | 荫蒂被男人添的好舒服爽免费视频 | 狂野欧美激情性xxxx | 日韩精品a片一区二区三区妖精 | 青草青草久热国产精品 | 欧美丰满熟妇xxxx | 婷婷丁香六月激情综合啪 | 国产精品-区区久久久狼 | 国产精品美女久久久久av爽李琼 | 日本一区二区三区免费高清 | 无码人妻av免费一区二区三区 | 亚洲日韩av一区二区三区四区 | 婷婷五月综合缴情在线视频 | 国产特级毛片aaaaaaa高清 | 欧美高清在线精品一区 | 国产精品怡红院永久免费 | 美女张开腿让人桶 | 国产精品久久久久影院嫩草 | 成人av无码一区二区三区 | 激情综合激情五月俺也去 | 中国大陆精品视频xxxx | 日韩精品无码一本二本三本色 | 国产成人综合在线女婷五月99播放 | 国产极品美女高潮无套在线观看 | 一本精品99久久精品77 | yw尤物av无码国产在线观看 | 人妻无码αv中文字幕久久琪琪布 | 亚洲va欧美va天堂v国产综合 | 国产乱码精品一品二品 | 精品 日韩 国产 欧美 视频 | 久久 国产 尿 小便 嘘嘘 | 欧美日韩在线亚洲综合国产人 | 日韩精品无码免费一区二区三区 | 亚洲娇小与黑人巨大交 | 欧美放荡的少妇 | 国产精品久久久久影院嫩草 | 好爽又高潮了毛片免费下载 | 中文字幕av日韩精品一区二区 | 欧美精品国产综合久久 | 国产两女互慰高潮视频在线观看 | 精品国产成人一区二区三区 | 影音先锋中文字幕无码 | 99久久精品无码一区二区毛片 | 国产特级毛片aaaaaaa高清 | 无码一区二区三区在线 | 午夜熟女插插xx免费视频 | 奇米影视888欧美在线观看 | 成人免费视频视频在线观看 免费 | 女人高潮内射99精品 | 日韩av无码一区二区三区不卡 | 精品人人妻人人澡人人爽人人 | 波多野结衣乳巨码无在线观看 | 日本护士xxxxhd少妇 | 大胆欧美熟妇xx | 亚洲精品无码人妻无码 | 日欧一片内射va在线影院 | 亚洲中文字幕va福利 | 久久久亚洲欧洲日产国码αv | a片免费视频在线观看 | 国产成人无码区免费内射一片色欲 | 久久久精品456亚洲影院 | 亚洲欧洲日本综合aⅴ在线 | 亚洲色欲色欲天天天www | 领导边摸边吃奶边做爽在线观看 | 国产精品久久久久9999小说 | 色婷婷综合中文久久一本 | 天堂а√在线地址中文在线 | 久久久久亚洲精品男人的天堂 | 国产精品美女久久久久av爽李琼 | 国产乱人伦偷精品视频 | 97精品人妻一区二区三区香蕉 | 好男人社区资源 | 欧美性生交xxxxx久久久 | 狠狠色噜噜狠狠狠7777奇米 | 51国偷自产一区二区三区 | 少妇久久久久久人妻无码 | 久久午夜夜伦鲁鲁片无码免费 | 国产午夜手机精彩视频 | 激情内射日本一区二区三区 | 国产亚洲视频中文字幕97精品 | 精品国产aⅴ无码一区二区 | a在线亚洲男人的天堂 | 波多野结衣乳巨码无在线观看 | 少妇性俱乐部纵欲狂欢电影 | 风流少妇按摩来高潮 | 成人一区二区免费视频 | 日本爽爽爽爽爽爽在线观看免 | 性啪啪chinese东北女人 | 国产乡下妇女做爰 | 国产免费无码一区二区视频 | 亚洲成av人片天堂网无码】 | 欧美变态另类xxxx | 国内精品久久毛片一区二区 | 精品国产av色一区二区深夜久久 | 久久国语露脸国产精品电影 | 久久 国产 尿 小便 嘘嘘 | 国产人妻精品午夜福利免费 | 欧美人与牲动交xxxx | 久久久久成人片免费观看蜜芽 | 久久99精品国产麻豆蜜芽 | 少妇邻居内射在线 | 少妇无套内谢久久久久 | 精品国产精品久久一区免费式 | 麻豆人妻少妇精品无码专区 | 久激情内射婷内射蜜桃人妖 | 日产国产精品亚洲系列 | 骚片av蜜桃精品一区 | 日本一本二本三区免费 | 亚洲人亚洲人成电影网站色 | 2019午夜福利不卡片在线 | 亚洲综合伊人久久大杳蕉 | 欧美日韩视频无码一区二区三 | 桃花色综合影院 | 特黄特色大片免费播放器图片 | 精品无人区无码乱码毛片国产 | 国产激情一区二区三区 | 少妇厨房愉情理9仑片视频 | 捆绑白丝粉色jk震动捧喷白浆 | 日本熟妇乱子伦xxxx | 最近中文2019字幕第二页 | 亚洲精品国产精品乱码不卡 | 久久久久亚洲精品中文字幕 | 色偷偷人人澡人人爽人人模 | 97人妻精品一区二区三区 | 人人澡人人透人人爽 | a片在线免费观看 | 水蜜桃av无码 | 水蜜桃色314在线观看 | 精品无码国产一区二区三区av | 欧美freesex黑人又粗又大 | 捆绑白丝粉色jk震动捧喷白浆 | 一本色道婷婷久久欧美 | 99精品无人区乱码1区2区3区 | 久久aⅴ免费观看 | 性欧美熟妇videofreesex | 5858s亚洲色大成网站www | 久久99国产综合精品 | 亚洲成a人片在线观看无码3d | 东京热无码av男人的天堂 | 自拍偷自拍亚洲精品被多人伦好爽 | 日韩精品乱码av一区二区 | 奇米影视7777久久精品人人爽 | 青青青爽视频在线观看 | 中文字幕无码乱人伦 | 国产成人久久精品流白浆 | 高潮喷水的毛片 | 人人妻人人澡人人爽欧美一区 | 国产高清不卡无码视频 | 亚洲理论电影在线观看 | 在线播放亚洲第一字幕 | 少妇高潮喷潮久久久影院 | 性色欲网站人妻丰满中文久久不卡 | 熟妇人妻无码xxx视频 | 久久精品成人欧美大片 | 日韩av无码中文无码电影 | 97精品国产97久久久久久免费 | 樱花草在线播放免费中文 | 丰满肥臀大屁股熟妇激情视频 | 大地资源网第二页免费观看 | 日韩欧美中文字幕在线三区 | 国产午夜福利亚洲第一 | 亚洲色偷偷偷综合网 | 少妇无码吹潮 | 波多野结衣一区二区三区av免费 | 亚洲国产欧美国产综合一区 | 中文字幕无码免费久久99 | 成年美女黄网站色大免费视频 | 精品无人国产偷自产在线 | 国产人妻久久精品二区三区老狼 | 欧美性猛交内射兽交老熟妇 | 一本久久a久久精品vr综合 | 人妻少妇精品无码专区动漫 | 亚洲男人av香蕉爽爽爽爽 | 久久人妻内射无码一区三区 | 婷婷丁香五月天综合东京热 | 麻豆av传媒蜜桃天美传媒 | 欧美精品在线观看 | 一本加勒比波多野结衣 | 欧美野外疯狂做受xxxx高潮 | 国产精品人人妻人人爽 | 少女韩国电视剧在线观看完整 | 日本熟妇人妻xxxxx人hd | 中文字幕色婷婷在线视频 | 国产内射老熟女aaaa | 久久99精品久久久久婷婷 | 狠狠色色综合网站 | 无遮挡国产高潮视频免费观看 | 无码乱肉视频免费大全合集 | 中文字幕无码人妻少妇免费 | 影音先锋中文字幕无码 | 永久免费精品精品永久-夜色 | www一区二区www免费 | 欧美35页视频在线观看 | √天堂中文官网8在线 | 亚洲色欲色欲天天天www | 午夜不卡av免费 一本久久a久久精品vr综合 | 黑森林福利视频导航 | 少妇邻居内射在线 | 免费国产成人高清在线观看网站 | 99久久久无码国产精品免费 | 亚洲国产精品美女久久久久 | 人妻少妇被猛烈进入中文字幕 | 大肉大捧一进一出视频出来呀 | 一个人免费观看的www视频 | 九九久久精品国产免费看小说 | 日本饥渴人妻欲求不满 | 国精品人妻无码一区二区三区蜜柚 | 国内精品九九久久久精品 | 极品尤物被啪到呻吟喷水 | 性做久久久久久久免费看 | 人妻天天爽夜夜爽一区二区 | 97久久超碰中文字幕 | 亚洲爆乳无码专区 | 欧美 日韩 人妻 高清 中文 | 亚洲精品午夜无码电影网 | 国内丰满熟女出轨videos | 久久综合久久自在自线精品自 | 精品国产一区av天美传媒 | 精品国产aⅴ无码一区二区 | 少妇愉情理伦片bd | 国产午夜亚洲精品不卡下载 | 一本一道久久综合久久 | 久久国内精品自在自线 | 日韩精品成人一区二区三区 | 丰满诱人的人妻3 | 国产电影无码午夜在线播放 | 国产成人精品优优av | 丝袜 中出 制服 人妻 美腿 | 丝袜 中出 制服 人妻 美腿 | 亚洲国精产品一二二线 | 久久99精品久久久久婷婷 | 国产精品丝袜黑色高跟鞋 | 狠狠色噜噜狠狠狠7777奇米 | 亚洲熟妇色xxxxx欧美老妇y | 亚洲中文无码av永久不收费 | 国产电影无码午夜在线播放 | 国产亚洲人成a在线v网站 | 人妻无码久久精品人妻 | 性开放的女人aaa片 | 999久久久国产精品消防器材 | 日韩成人一区二区三区在线观看 | 无码人妻av免费一区二区三区 | 久9re热视频这里只有精品 | 国产精品人人爽人人做我的可爱 | 国产性生交xxxxx无码 | 无遮挡啪啪摇乳动态图 | 亚洲自偷精品视频自拍 | 国产精品国产自线拍免费软件 | 亚洲s码欧洲m码国产av | 欧美国产亚洲日韩在线二区 | 99久久久国产精品无码免费 | 玩弄人妻少妇500系列视频 | 黑人粗大猛烈进出高潮视频 | 国产乱人偷精品人妻a片 | 亚洲精品无码人妻无码 | 97久久超碰中文字幕 | 国产精品内射视频免费 | 黄网在线观看免费网站 | 一本大道久久东京热无码av | 国产做国产爱免费视频 | 久久精品国产精品国产精品污 | 国产在线精品一区二区三区直播 | 无码人妻丰满熟妇区五十路百度 | 国产又爽又猛又粗的视频a片 | 97久久超碰中文字幕 | 久久亚洲中文字幕精品一区 | 乱码午夜-极国产极内射 | 综合激情五月综合激情五月激情1 | 鲁一鲁av2019在线 | 日本xxxx色视频在线观看免费 | 亚洲中文字幕av在天堂 | 性生交大片免费看女人按摩摩 | 99国产精品白浆在线观看免费 | 国产精品无码一区二区桃花视频 | 久久99精品国产.久久久久 | 国产精品久久久 | 四虎4hu永久免费 | 亚洲精品久久久久久一区二区 | 久久综合久久自在自线精品自 | 久久精品国产一区二区三区 | 欧美老妇交乱视频在线观看 | 国产欧美精品一区二区三区 | 久久人人爽人人爽人人片ⅴ | 日韩av无码一区二区三区不卡 | 51国偷自产一区二区三区 | 国产精品igao视频网 | 午夜精品久久久内射近拍高清 | 男女下面进入的视频免费午夜 | 丰满岳乱妇在线观看中字无码 | 特黄特色大片免费播放器图片 | 国语自产偷拍精品视频偷 | 又大又紧又粉嫩18p少妇 | 国产精品.xx视频.xxtv | a片免费视频在线观看 | 亚洲精品国产精品乱码视色 | 妺妺窝人体色www在线小说 | 在线观看免费人成视频 | 国产绳艺sm调教室论坛 | 日日摸夜夜摸狠狠摸婷婷 | 国产9 9在线 | 中文 | 免费无码午夜福利片69 | 丰满少妇弄高潮了www | 岛国片人妻三上悠亚 | 欧美 日韩 亚洲 在线 | 亚洲国精产品一二二线 | 欧美日韩人成综合在线播放 | 久久这里只有精品视频9 | 欧美第一黄网免费网站 | 国产精品亚洲а∨无码播放麻豆 | 亚洲国产精品成人久久蜜臀 | 欧洲熟妇色 欧美 | 麻豆国产人妻欲求不满谁演的 | 国产精品亚洲综合色区韩国 | 亚洲欧美综合区丁香五月小说 | 性开放的女人aaa片 | 精品无码一区二区三区的天堂 | 成人免费视频视频在线观看 免费 | 丝袜人妻一区二区三区 | 成人一区二区免费视频 | 国产成人综合美国十次 | 国产疯狂伦交大片 | 国产片av国语在线观看 | www一区二区www免费 | 初尝人妻少妇中文字幕 | 两性色午夜免费视频 | 国产精品内射视频免费 | 又大又黄又粗又爽的免费视频 | 最近的中文字幕在线看视频 | 波多野42部无码喷潮在线 | 国产免费观看黄av片 | 精品国产国产综合精品 | 2019午夜福利不卡片在线 | 性色av无码免费一区二区三区 | 亚洲欧美日韩国产精品一区二区 | 狠狠色丁香久久婷婷综合五月 | 内射老妇bbwx0c0ck | 亚洲第一无码av无码专区 | 小sao货水好多真紧h无码视频 | 亚洲s码欧洲m码国产av | 免费网站看v片在线18禁无码 | 久在线观看福利视频 | 亚洲a无码综合a国产av中文 | √8天堂资源地址中文在线 | 激情五月综合色婷婷一区二区 | 熟妇人妻无乱码中文字幕 | 丰满人妻翻云覆雨呻吟视频 | 色噜噜亚洲男人的天堂 | 人人爽人人澡人人高潮 | 99riav国产精品视频 | 日本又色又爽又黄的a片18禁 | 人人澡人人妻人人爽人人蜜桃 | 性欧美videos高清精品 | 对白脏话肉麻粗话av | 午夜熟女插插xx免费视频 | 亚洲精品成人av在线 | 中文字幕无码乱人伦 | 精品国精品国产自在久国产87 | 中文字幕中文有码在线 | 妺妺窝人体色www婷婷 | 精品久久久久久亚洲精品 | 欧美自拍另类欧美综合图片区 | 国产 精品 自在自线 | 国产精品无码成人午夜电影 | 国産精品久久久久久久 | 人妻体内射精一区二区三四 | 久久人人爽人人爽人人片av高清 | 国产极品美女高潮无套在线观看 | 纯爱无遮挡h肉动漫在线播放 | 国产两女互慰高潮视频在线观看 | 四虎国产精品一区二区 | 亚洲aⅴ无码成人网站国产app | 亚洲精品久久久久avwww潮水 | 亚洲第一网站男人都懂 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 亚洲日韩一区二区三区 | 亚洲狠狠色丁香婷婷综合 | 免费中文字幕日韩欧美 | 日本熟妇人妻xxxxx人hd | 日产精品高潮呻吟av久久 | 丁香花在线影院观看在线播放 | 亚洲国产精品久久人人爱 | 国产精品人妻一区二区三区四 | 国产三级久久久精品麻豆三级 | 国产精品18久久久久久麻辣 | 好屌草这里只有精品 | 午夜精品一区二区三区在线观看 | 18禁止看的免费污网站 | 国产av久久久久精东av | 水蜜桃亚洲一二三四在线 | 日韩人妻少妇一区二区三区 | 乱人伦人妻中文字幕无码久久网 | 亚洲精品久久久久中文第一幕 | 夜先锋av资源网站 | 久久无码专区国产精品s | 少妇无码av无码专区在线观看 | 亚欧洲精品在线视频免费观看 | √8天堂资源地址中文在线 | 伊人久久大香线焦av综合影院 | 亚洲熟妇色xxxxx欧美老妇y | 18禁黄网站男男禁片免费观看 | 久久精品女人的天堂av | 国产精品第一国产精品 | 最新版天堂资源中文官网 | 免费看男女做好爽好硬视频 | 国产猛烈高潮尖叫视频免费 | 亚洲国产av美女网站 | 亚洲a无码综合a国产av中文 | 久久久中文久久久无码 | 最近中文2019字幕第二页 | 精品国精品国产自在久国产87 | 亚洲色偷偷男人的天堂 | 中文字幕日韩精品一区二区三区 | 无码精品国产va在线观看dvd | 国产黑色丝袜在线播放 | 性色欲网站人妻丰满中文久久不卡 | 欧美亚洲国产一区二区三区 | 久久精品国产一区二区三区肥胖 | 国产电影无码午夜在线播放 | 亚洲综合无码久久精品综合 | 午夜福利一区二区三区在线观看 | 天天燥日日燥 | 欧美成人午夜精品久久久 | 久久亚洲中文字幕精品一区 | 又紧又大又爽精品一区二区 | 97资源共享在线视频 | 性欧美熟妇videofreesex | 婷婷六月久久综合丁香 | 国产绳艺sm调教室论坛 | 高清国产亚洲精品自在久久 | 国产艳妇av在线观看果冻传媒 | 高清国产亚洲精品自在久久 | 青青久在线视频免费观看 | 色老头在线一区二区三区 | 台湾无码一区二区 | 久久无码人妻影院 | 无遮挡国产高潮视频免费观看 | 曰韩无码二三区中文字幕 | 理论片87福利理论电影 | 国产片av国语在线观看 | 激情综合激情五月俺也去 | 亚洲精品国产精品乱码视色 | 国产午夜福利亚洲第一 | a在线观看免费网站大全 | 国产在热线精品视频 | 一本久久a久久精品vr综合 | 国产精品亚洲专区无码不卡 | 国产亚洲精品久久久久久国模美 | 精品国产精品久久一区免费式 | 精品国产一区二区三区四区在线看 | 玩弄中年熟妇正在播放 | 国产亚洲精品久久久ai换 | 亚洲性无码av中文字幕 | 亚洲乱码国产乱码精品精 | 亚洲gv猛男gv无码男同 | 香港三级日本三级妇三级 | 国产在线一区二区三区四区五区 | 狠狠噜狠狠狠狠丁香五月 | 日日噜噜噜噜夜夜爽亚洲精品 | 久久久精品人妻久久影视 | 久久精品人人做人人综合试看 | 久久亚洲中文字幕精品一区 | 97夜夜澡人人爽人人喊中国片 | 国产亚av手机在线观看 | 九月婷婷人人澡人人添人人爽 | 国产精品高潮呻吟av久久4虎 | 黑人巨大精品欧美一区二区 | 亚洲欧美精品伊人久久 | 亚洲成a人片在线观看日本 | 国产精品久久久av久久久 | 久久午夜无码鲁丝片 | 久久国产36精品色熟妇 | 少妇高潮喷潮久久久影院 | 日韩av无码一区二区三区 | 国产色视频一区二区三区 | 爽爽影院免费观看 | 色诱久久久久综合网ywww | 欧美大屁股xxxxhd黑色 | 精品久久久久香蕉网 | 5858s亚洲色大成网站www | 国产色在线 | 国产 | 亚洲一区二区观看播放 | 大屁股大乳丰满人妻 | 久激情内射婷内射蜜桃人妖 | 粉嫩少妇内射浓精videos | 国产成人精品久久亚洲高清不卡 | 日韩人妻无码中文字幕视频 | 国产三级精品三级男人的天堂 | 在线天堂新版最新版在线8 | 极品尤物被啪到呻吟喷水 | 2020久久超碰国产精品最新 | 少妇无码一区二区二三区 | 超碰97人人做人人爱少妇 | 天天做天天爱天天爽综合网 | 真人与拘做受免费视频 | 人妻少妇精品久久 | 国产精品怡红院永久免费 | 国产莉萝无码av在线播放 | 天堂久久天堂av色综合 | 天天摸天天碰天天添 | 色婷婷综合激情综在线播放 | 国产精品第一区揄拍无码 | 天堂久久天堂av色综合 | 疯狂三人交性欧美 | 国产香蕉97碰碰久久人人 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 久久久久av无码免费网 | 国产精品久久久久久亚洲毛片 | 2019午夜福利不卡片在线 | 岛国片人妻三上悠亚 | 久久综合九色综合欧美狠狠 | 亚洲自偷自偷在线制服 |