此篇中,我们会介绍在PUN里面,很重要的二个网路元件: Photon View以及Photon Transform View ,并用它来同步网路中各种物件的位置/旋转/缩放或其他资料的移位。
坦克! 多人游戏(Unity教程)
让我们先来看一下在Unity官方教学中,很著名的游戏范例: Tanks!
它是一个单机多人的坦克射击游戏,可以二个人用同一个键盘在一台主机上一起玩游戏,如果以前没看过这个教学的朋友,可以到下面的网页中,看到Unity官方详细的教学唷〜
Unity – Tanks教程
您需要一个Unity帐户才能在在线商店和资产商店购物,加入Unity社区并管理您的… unity3d.com
建立游戏场景(Game Scene)
我们的Unity专案TanksPUN ,现在要汇入下面这个资产,用它来学着建立玩家角色以及多人连线的游戏场景刚好非常合适。 坦克! 教程的资产商店连结如下:
坦克! Unity Technologies的教程
2015年统一培训日带给您坦克! 这款2人1键盘沙发战将带您建造一个完整的坦克…… www.assetstore.unity3d.com
在Asset Store找到坦克! 并且汇入完成后,接下来建立一个新的场景, 就叫它GameRoomScene吧!
接下来在Assets→Complete→Scenes中找到一个CompleteLevelArt的Prefab,直接把它拖拉到我们的GameRoomScene里。

再把原本GameRoomScene里面预设的Directional Light移除,无需得这个场景的颜色会被直接光源影响到; 接着应该会看到像下面的画面,看起来是不是很有成就感呢呢? 😁👏🏻

呵,上面的成就得来太容易了,所以接下来看点程式码吧〜
下面的程序,就是如此,我们所建立的PhotonManager.cs的内容。
使用UnityEngine;
使用System.Collections;
使用System.Collections.Generic;
公共类PhotonManager:Photon.PunBehaviour {
公共静态PhotonManager实例;
公共静态GameObject localPlayer;
无效Awake()
{
if(instance!= null)
{
DestroyImmediate(gameObject);
返回;
}
DontDestroyOnLoad(gameObject);
实例=这个;
PhotonNetwork.automaticallySyncScene = true;
}
无效Start()
{
PhotonNetwork.ConnectUsingSettings(“ TanksPUN_v1.0”);
}
公共无效JoinGame()
{
RoomOptions options =新的RoomOptions();
options.MaxPlayers = 4;
PhotonNetwork.JoinOrCreateRoom(“ Fighting Room”,options,TypedLobby.Default);
}
公共重写void OnJoinedRoom()
{
Debug.Log(“您已进入游戏室”);
如果(PhotonNetwork.isMasterClient)
{
PhotonNetwork.LoadLevel(“ GameRoomScene”);
}
}
void OnLevelWasLoaded(int levelNumber)
{
//如果不在Photon的游戏室内,则网路有问题..
如果(!PhotonNetwork.inRoom)
返回;
Debug.Log(“我们已进入游戏场景了,耶〜”);
}
}
在上一篇文章(连结:Part2)的PhotonManager中,我们已经可以跟Photon Server沟通了,之前有提到当已经进入游戏室内,PunBehaviour会通知我们的OnJoinedRoom ,此时可以使用PhotonNetwork.LoadLevel来载入游戏场景;
场景载入完成后, OnLevelWasLoaded会被叫起来,这时就可以在场景内部建立一些初始值或资料设定,现在我们简单地建立一个玩家角色(Player Character)来试试〜
加入玩家角色(The Player)
在OnLevelWasLoaded里,我们可以用PhotonNetwork。 实例化在网路上建立一个同步的物件,这样在所有连到这个Game Room的人都可以看到此物件。
void OnLevelWasLoaded(int levelNumber)
{
如果(!PhotonNetwork.inRoom)返回;
Debug.Log(“我们已进入游戏场景了,耶〜”);
localPlayer = PhotonNetwork.Instantiate(
“玩家”,
新的Vector3(0,0.5f,0),
Quaternion.identity,0);
}
}
所以一进入场景,我们就可以在指定的位置,动态的生出个 关于Player的程式码,我们很简单的先设定如下:
公共类玩家:Photon.PunBehaviour
{
相机播放器
无效唤醒(){
DontDestroyOnLoad(gameObject);
playerCam = GetComponentInChildren ();
if(!photonView.isMine)
{
playerCam.gameObject.SetActive(false);
}
}
}
接下来来介绍重要的Photon Network元件。 很重要!!
每个需要在网路上同步的物件都得要有这个元件才有可能资讯同步唷!
光子视图元件介绍
此元件与Unity Networking内部的NetworkView元件的功能蛮相似的,主要是用作同步游戏中的某些单个元件的值,它内部会有个ViewID ,光子网络会使其它来识别场景中不同的被观客户端做同步,而其同步规则还可以在观察选项中设置。 这里我们先设定不可靠的更改即可。
光子变换视图元件介绍
此元件为Photon特制化的Transform View元件,PUN会遵循我们选择的方式来同步网路上有移动的值,像是我们移动某物件时,网路上其他客户端类的。 可同步的值可以是位置,旋转,刻度的单选或是多选。 目前我们先设定Position + Rotation即可。
在Project里,我们找到CompleteTank的Prefab,选它让它显示在Inspector中,加入Photon View以及Photon Transform View这二个元件;
接下来就可以把光子变换视图拉到光子视图中,在它的被观察组件其下,就会有一个被观查的元件。 如下图所示。

以及玩家的移动处理的程式码如下:
使用UnityEngine;
命名空间TanksPUN
{
公共课程PlayerMovement:Photon.PunBehaviour
{
//坦克前进和后退的速度。
公共浮标m_Speed = 12f;
//坦克每秒旋转多少度。
公共浮标m_TurnSpeed = 180f;
//用于移动水箱的参考。
私人刚体m_Rigidbody;
//运动输入的当前值。
私有浮点数m_MovementInputValue;
//转向输入的当前值。
私有浮点数m_TurnInputValue;
私人虚空唤醒()
{
m_Rigidbody = GetComponent ();
if(!photonView.isMine)
{
enabled = false;
}
}
私人无效OnEnable()
{
m_Rigidbody.isKinematic = false;
m_MovementInputValue = 0f;
m_TurnInputValue = 0f;
}
私人无效OnDisable()
{
m_Rigidbody.isKinematic = true;
}
私人无效更新()
{
m_MovementInputValue = Input.GetAxis(“ Vertical”);
m_TurnInputValue = Input.GetAxis(“ Horizontal”);
}
私人void FixedUpdate()
{
移动();
转();
m_Rigidbody.velocity = Vector3.zero;
m_Rigidbody.angularVelocity = Vector3.zero;
}
私人空白Move()
{
Vector3运动= transform.forward * m_MovementInputValue * m_Speed * Time.deltaTime;
m_Rigidbody.MovePosition(m_Rigidbody.position +运动);
}
私人空白转()
{
浮动转弯= m_TurnInputValue * m_TurnSpeed * Time.deltaTime;
四元数turnRotation =四元数.Euler(0f,turn,0f);
m_Rigidbody.MoveRotation(m_Rigidbody.rotation * turnRotation);
}
}
}
下一篇,我们会针对不同的玩家的进入点(重生点)做个管理,解释玩家移动处理的方式以及强大的坦克的弹药射击与碰撞!
下次见啰〜😁😘🤖👏🏻🎮