Photon Unity Networking基礎(chǔ)教程 7 修改Player的聯(lián)網(wǎng)版本
本節(jié)將指導(dǎo)您修改Player Prefab。我們首先創(chuàng)建一個(gè)可以工作的player,但現(xiàn)在我們將修改它,以便它在PUN環(huán)境中使用時(shí)能正常工作牡属。修改很少的代碼,但概念是至關(guān)重要的扼睬。所以這一節(jié)非常重要。
主要內(nèi)容
- PhotonView 組件
- Transform 同步
- Animator 同步
- 用戶輸入管理
- Camera 控制
- Beams 開火控制
- Health 同步
PhotonView 組件
首先悴势,我們需要在Prefab上添加一個(gè)PhotonView組件窗宇。PhotonView將每個(gè)計(jì)算機(jī)上的各種實(shí)例連接在一起,并定義要觀察的組件以及如何觀察這些組件特纤。
- 添加PhotonView組件到My Robot Kyle
- 將Observe Option設(shè)置為Unreliable On Change
- 注意军俊,PhotonView警告你,要起作用的話你需要觀察一些東西
讓我們?cè)O(shè)置我們要觀察的對(duì)象捧存,然后我們將回到這個(gè)PhotonView組件粪躬,并完成它的設(shè)置。
Transform 同步
我們想要同步的明顯特征是角色的位置和旋轉(zhuǎn)昔穴,使得當(dāng)Player移動(dòng)時(shí)镰官,其他計(jì)算機(jī)上的Player也以類似的方式移動(dòng)和旋轉(zhuǎn)。
你可以直接觀察自己腳本中的Transform組件吗货,但是由于網(wǎng)絡(luò)延遲和數(shù)據(jù)同步的有效性泳唠,你會(huì)遇到很多麻煩。幸運(yùn)的是宙搬,為了使這個(gè)常見的任務(wù)更容易笨腥,我們將使用[Photon Transform View]組件,作為變換組件和PhotonView之間的中間人(middleMan)勇垛〔蹦福基本上,這個(gè)組件已經(jīng)為你做了所有的工作闲孤。
- 給'My Robot Kyle'Prefab添加PhotonTransformView
- 拖拽PhotonTransformView的標(biāo)題欄到PhotonView組件的第一個(gè)Observable組件上
- 在PhotonTransformView組件上勾選Synchronize Position
- 在Synchronize Position里面谆级,Interpolation Option選擇“Lerp”
- 把Lerp Speed設(shè)置為10(數(shù)值越大插值越快)
- 勾選SynchronizeRotation
提示:注意右邊的藍(lán)色書本幫助鏈接,點(diǎn)擊它能獲取信息崭放,學(xué)習(xí)不同的設(shè)置和效果哨苛。
Animator 同步
PhotonAnimatorView也使得網(wǎng)絡(luò)設(shè)置變得輕而易舉,將為您節(jié)省大量的時(shí)間和麻煩币砂。它允許您定義哪些層權(quán)重和要同步的參數(shù)建峭。層權(quán)重只有在游戲過程中改變了才需要同步,并且可以不同步它們决摧。參數(shù)也是如此亿蒸。有時(shí)可以從其他因素導(dǎo)出動(dòng)畫值凑兰。速度值是一個(gè)很好的例子,你不一定需要讓這個(gè)值完全同步边锁,但你可以使用同步的位置更新來估計(jì)它的值姑食。如果可能,嘗試同步盡可能少的參數(shù)茅坛。
- 給My Robot Kyle Prefab添加一個(gè)PhotonAnimatorView
- 拖拽PhotonAnimatorView的標(biāo)題欄到PhotonView組件的Observable組件上
- 同步參數(shù)中設(shè)置Speed為Discrete
- 設(shè)置Direction為Discrete
- 設(shè)置Jump為Discrete
- 設(shè)置Hi為Disabled
每個(gè)值都可以是disabled音半,或者以離散(discretely)或連續(xù)(continuously)的方式同步(synchronized)。在我們的例子中贡蓖,由于我們不使用Hi參數(shù)曹鸠,我們將禁用它,從而節(jié)省帶寬斥铺。
離散同步(Discrete synchronization)意味著值每秒發(fā)送10次(在OnPhotonSerializeView中)彻桃。 接收客戶端將值傳遞到他們的本地Animator。
連續(xù)同步(Continuous synchronization)意味著PhotonAnimatorView每幀都要運(yùn)行晾蜘。當(dāng)調(diào)用OnPhotonSerializeView(每秒10次)時(shí)邻眷,自上次調(diào)用以來記錄的值一起發(fā)送。接收客戶端然后按順序應(yīng)用值以保持平滑過渡剔交。雖然此模式更平滑肆饶,但它為了實(shí)現(xiàn)此效果發(fā)送了更多數(shù)據(jù)。
用戶輸入管理
用戶控制網(wǎng)絡(luò)的一個(gè)關(guān)鍵方面是省容,相同的Prefab將為所有玩家實(shí)例化抖拴,但是其中只有一個(gè)是實(shí)際在計(jì)算機(jī)前的用戶控制的,所有其他實(shí)例代表的是在其他計(jì)算機(jī)上的其他用戶腥椒。因此阿宅,這一點(diǎn)的第一個(gè)障礙是輸入管理。我們?nèi)绾文軌蛟谝粋€(gè)實(shí)例上而不是在其他實(shí)例上啟用輸入笼蛛,以及如何知道哪個(gè)是正確的洒放?這就需要isMine的概念。
讓我們編輯我們之前創(chuàng)建的PlayerAnimatorManager腳本滨砍。在目前的形式中往湿,這個(gè)腳本不知道這個(gè)區(qū)別,讓我們實(shí)現(xiàn)它惋戏。
打開腳本PlayerAnimatorManager
把PlayerAnimatorManager的基類改為Photon.MonoBehaviour领追,這個(gè)類很方便的暴露photonView的組件
-
在Update函數(shù)的開頭加入下面代碼
if( photonView.isMine == false && PhotonNetwork.connected == true ) { return; }
保存腳本
Ok,如果實(shí)例由“客戶端”應(yīng)用程序控制响逢,PhotonView.isMine將為true绒窑,意味著此實(shí)例表示在此應(yīng)用程序中在此計(jì)算機(jī)上正在play的玩家。因此舔亭,如果它是假的些膨,我們不想做任何事情蟀俊,只依靠PhotonView組件來同步我們之前設(shè)置的變換和動(dòng)畫組件。
但是订雾,為什么在我們的if語句中強(qiáng)制執(zhí)行PhotonNetwork.connected == true肢预?因?yàn)樵陂_發(fā)期間,我們可能想要測試這個(gè)prefab洼哎,而不連接烫映。在虛擬場景中,例如噩峦,只是創(chuàng)建和驗(yàn)證與網(wǎng)絡(luò)功能無關(guān)的代碼窑邦。因此,使用這個(gè)附加表達(dá)式壕探,如果我們沒有連接,我們將允許使用輸入郊丛。這是一個(gè)非常簡單的伎倆李请,并將大大改善您的開發(fā)過程中的工作流。
Camera 控制
它和輸入一樣厉熟,Player只有一個(gè)游戲視圖导盅,所以我們需要CameraWork腳本只跟隨本地Player,而不是其他Player揍瑟。這就是為什么CameraWork腳本有這個(gè)能力來定義什么時(shí)候跟隨白翻。
讓我們修改PlayerManager腳本來控制CameraWork組件。
打開PlayerManager腳本
-
在Awake()和Update()函數(shù)之間插入下面的代碼
/// <summary> /// MonoBehaviour method called on GameObject by Unity during initialization phase. /// </summary> 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); } }
保存腳本
首先绢片,它獲取CameraWork組件滤馍,我們期望這樣,所以如果我們沒有找到它底循,就記錄一個(gè)錯(cuò)誤巢株。然后,如果photonView.isMine為true熙涤,這意味著我們需要跟隨這個(gè)實(shí)例阁苞,因此我們調(diào)用_cameraWork.OnStartFollowing(),它有效地使相機(jī)跟隨場景中的那個(gè)實(shí)例祠挫。
所有其他Player實(shí)例的photonView.isMine將設(shè)置為false那槽,因此它們各自的_cameraWork將不會(huì)做任何事情。
下面的一個(gè)改變可以使這點(diǎn)生效
- 在Robot animator prefab上等舔,CameraWork組件中禁用Follow on Start屬性
PlayerManager腳本將會(huì)像上面描述的那樣調(diào)用_cameraWork.OnStartFollowing()骚灸,現(xiàn)在這有效的處理了跟隨Player的邏輯。
Beams 開火控制
開火也同樣遵循上面的輸入原則软瞎,它只在photonView.isMine是true的情況下工作逢唤。
打開PlayerManager腳本
-
用一個(gè)if語句包住輸入處理
if (photonView.isMine) { ProcessInputs (); }
保存腳本
然而拉讯,當(dāng)測試這個(gè)的時(shí)候,我們只看到本地Player開火鳖藕。我們需要看看其他實(shí)例何時(shí)開火魔慷!我們需要一種用于在網(wǎng)絡(luò)上同步開火的機(jī)制。為此著恩,我們將手動(dòng)同步IsFiring布爾值院尔,直到現(xiàn)在,我們離開了PhotonTransformView和PhotonAnimatorView來為我們進(jìn)行變量的所有內(nèi)部同步喉誊,我們只需要調(diào)整通過Unity Inspector方便地暴露給我們的參數(shù)邀摆,但在這里我們需要的是,針對(duì)你的具體游戲伍茄,所以我們需要手動(dòng)這樣做栋盹。
- 打開PlayerManager腳本
- 實(shí)現(xiàn)IPunObservable接口
-
在IPunObservable.OnPhotonSerializeView函數(shù)中添加下面代碼
if (stream.isWriting) { // We own this player: send the others our data stream.SendNext(IsFiring); }else{ // Network player, receive data this.IsFiring = (bool)stream.ReceiveNext(); }
保存腳本
回到Unity編輯器,在assets中選擇Robot Animator prefab敷矫,在PhotonView組件中添加一條監(jiān)視記錄例获,然后把PlayerManager組件拖拽到上面
沒有上一步的話,IPunObservable.OnPhotonSerializeView永遠(yuǎn)不會(huì)調(diào)用曹仗,因?yàn)樗鼪]有被PhotonView監(jiān)視榨汤。
在這個(gè)IPunObservable.OnPhotonSerializeView方法中,我們傳遞了一個(gè)變量stream怎茫,這是將通過網(wǎng)絡(luò)發(fā)送的收壕,并且這個(gè)調(diào)用是我們讀寫數(shù)據(jù)的機(jī)會(huì)。當(dāng)我們是本地Player的時(shí)候(PhotonView.isMine == true)才能寫入數(shù)據(jù)轨蛤,否則是讀數(shù)據(jù)蜜宪。
由于stream類自己知道該怎么處理數(shù)據(jù),所以我們只需要簡單的利用stream.isWriting祥山,就可以知道當(dāng)前實(shí)例情況下要做什么端壳。
如果我們期望寫入數(shù)據(jù),我們使用stream.SendNext()附加到數(shù)據(jù)流的IsFiring值枪蘑,這是一個(gè)非常方便的方法损谦,隱藏了數(shù)據(jù)序列化的所有辛苦工作。如果我們希望讀數(shù)據(jù)岳颇,那就使用stream.ReceiveNext()照捡。
Health 同步
好的,為了完成更新Player的功能话侧,我們將同步Health值栗精,以便Player的每個(gè)實(shí)例都有正確的Health值。這與我們剛剛介紹的IsFiring值使用完全相同的原則。
打開腳本PlayerManager
-
在IPunObservable.OnPhotonSerializeView中悲立,SendNext和ReceiveNext處理IsFiring變量之后鹿寨,同樣的處理一下Health
if (stream.isWriting) { // We own this player: send the others our data stream.SendNext(IsFiring); stream.SendNext(Health); }else{ // Network player, receive data this.IsFiring = (bool)stream.ReceiveNext(); this.Health = (float)stream.ReceiveNext(); }
保存PlayerManager
這樣就完成了同步Health變量。
原文
http://doc.photonengine.com/en-us/pun/current/tutorials/pun-basics-tutorial/player-networking