Unity3d相关(M)

Unity新网络Multiplayer

2016-06-14  本文已影响2049人  欣羽馨予

前言

随着Unity版本的更新,新版的网络系统Multiplayer也渐渐地越来越被重视,5.3.4版本测试很好用。使用这套网络系统可以轻松开发联机网络游戏,而且其中封装的API也针对于开发者的层次作了区分。HighLevelAPI(简称HLAPI)针对于简单的网络系统搭建,封装的比较严重,只需轻松几部即可完成网络环境的搭建;LowLevelAPI(简称LLAPI)偏向底层,网络环境的搭建,需要依靠底层类层层搭建,但较为灵活。根据不同的需求,可以选择不同的API,当然通常HLAPI和LLAPI是混合起来一起用的。本文会简单讲解Multiplayer的API架构,主要通过项目将所有内容串联。

当然是用NetworkManager/NetworkLobbyManager组件同样可以搭建网络环境,且更为方便,这里不再赘述,详见项目。

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;

public class MyLobbyManager : NetworkLobbyManager
{
    //单例
    public static MyLobbyManager instance;
    //是否开启切换场景标志位
    public bool beginChange = false;
    //背景音乐
    public GameObject backAud;
    //玩家位置编号
    private int playerPositionIndex = 0;

    void Awake ()
    {
        instance = this;
    }

    void Start ()
    {
        DontDestroyOnLoad (backAud);
    }

    /// <summary>
    /// 当所有玩家都已准备完毕
    /// </summary>
    public override void OnLobbyServerPlayersReady ()
    {
        //启动协程等待动画播放完毕
        StartCoroutine (PlayProgress ());
        //遍历所有客户端发送播放指令
        foreach (NetworkLobbyPlayer item in lobbySlots) {
            if (item) {
                (item as MyLobbyPlayer).RpcBeginPlay ();
            }
        }
    }

    IEnumerator PlayProgress ()
    {
        //如果还没有开始切换场景,继续播放动画,保持等待
        while (!beginChange) {
            yield return null;
        }
        base.OnLobbyServerPlayersReady ();
    }

    //当服务器添加玩家对象时调用
    public override void OnServerAddPlayer (NetworkConnection conn, short playerControllerId)
    {
        base.OnServerAddPlayer (conn, playerControllerId);
        //判断是否在游戏场景而非游戏大厅
        if (beginChange) {
            //创建坦克
            GameObject player = Instantiate (gamePlayerPrefab) as GameObject;
            //通过新场景的NetworkStartPosition确定坦克的创建位置
            player.transform.position = startPositions [playerPositionIndex++].position;
            //设置坦克脚本中的网络变量--坦克编号
            player.GetComponent<MyPlayer> ().tankNum = playerPositionIndex - 1;
            //给予客户端该坦克的使用权限
            NetworkServer.AddPlayerForConnection (conn, player, playerControllerId);
            //卵生坦克
            NetworkServer.Spawn (player);
        }
    }
}

2.大厅玩家

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using UnityEngine.UI;

public class MyLobbyPlayer : NetworkLobbyPlayer
{
    //玩家大厅名称显示
    private Transform content;
    //玩家大厅准备按钮
    private Button readyButton;
    //单例LobbyManager
    private MyLobbyManager manager;
    //倒计时进度条
    private GameObject progress;

    void Awake ()
    {
        manager = MyLobbyManager.instance;
        content = GameController.instance.content.transform;
        readyButton = transform.GetChild (0).GetComponent<Button> ();
    }

    void Start ()
    {
        //设置当前对象到UI中显示
        GameController.instance.SetParent (this.transform);
        //获取进度条
        progress = transform.parent.parent.parent.GetChild (3).gameObject;
        //重置缩放
        transform.localScale = Vector3.one;
        //非主机客户端更新玩家名称
        if (!isServer) {
            CmdUpdateItemName ();
        }
    }

    [Command]
    public void CmdUpdateItemName ()
    {
        //服务器开始下发指令
        RpcUpdateItemName ();
    }

    [ClientRpc]
    public void RpcUpdateItemName ()
    {
        //非主机客户端设置字体颜色
        content.GetChild (1).GetComponent<Image> ().color = Color.red;
        //非主机客户端设置玩家名称
        content.GetChild (1).GetChild (1).GetComponent<Text> ().text = "Player2";
    }

    /// <summary>
    /// 本地玩家执行
    /// </summary>
    public override void OnStartLocalPlayer ()
    {
        base.OnStartLocalPlayer ();
        //设置准备按钮可用
        readyButton.interactable = true;
        //移除所有监听
        readyButton.onClick.RemoveAllListeners ();
        //设置准备按钮事件监听
        readyButton.onClick.AddListener (OnReadyButtonClick);
    }

    /// <summary>
    /// 玩家准备按钮点击事件
    /// </summary>
    public void OnReadyButtonClick ()
    {
        //向服务器发送准备指令
        SendReadyToBeginMessage ();
        //移除该按钮所有事件监听
        readyButton.onClick.RemoveAllListeners ();
        //设置该按钮取消准备的事件监听
        readyButton.onClick.AddListener (OnNotReadyButtonClick);
    }

    /// <summary>
    /// 玩家取消准备按钮点击事件
    /// </summary>
    public void OnNotReadyButtonClick ()
    {
        //向服务器发送取消准备的指令
        SendNotReadyToBeginMessage ();
        //取消该按钮的所有事件监听
        readyButton.onClick.RemoveAllListeners ();
        //添加该按钮准备的事件监听
        readyButton.onClick.AddListener (OnReadyButtonClick);
    }

    /// <summary>
    /// 当客户端主播完毕后调用
    /// </summary>
    /// <param name="readyState">If set to <c>true</c> ready state.</param>
    public override void OnClientReady (bool readyState)
    {
        base.OnClientReady (readyState);
        //如果准备好了
        if (readyState) {
            //按钮文字显示为Done
            readyButton.GetComponentInChildren<Text> ().text = "Done";
        } else {
            //否则显示Ready
            readyButton.GetComponentInChildren<Text> ().text = "Ready";
        }
    }

    [ClientRpc]
    /// <summary>
    /// 客户端开始播放切换场景动画
    /// </summary>
    public void RpcBeginPlay ()
    {
        progress.SetActive (true);
    }
}

3.主场景玩家(坦克)

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using UnityEngine.UI;

public class MyPlayer : NetworkBehaviour
{
    [SyncVar]
    //坦克编号
    public int tankNum = 0;

    [SyncVar]
    //坦克血量
    public int health = 100;
    //坦克移动速度
    public float tankMoveSpeed = 3f;
    //坦克旋转速度
    public float tankTurnSpeed = 10f;
    //坦克发射的炮弹飞行速度
    public float fireSpeed = 20f;
    //声音片段
    public AudioClip idle;
    public AudioClip run;

    private Rigidbody rig;
    //观察点
    private Transform targetPoint;
    //坦克炮头
    private Transform gun;
    //发射点
    private Transform firePoint;
    //操纵轴
    private float hor, ver, gunDir;
    //坦克血条颜色
    private Color[] colors = new Color[]{ Color.red, Color.green };
    //坦克血条背景图片
    private Image healthColor;
    //坦克血条
    private Slider healthSlider;
    //结果UI
    private GameObject resultUI;
    //声音片段
    private AudioSource aud;

    void Awake ()
    {
        rig = GetComponent<Rigidbody> ();
        aud = GetComponent<AudioSource> ();
        targetPoint = transform.Find ("TargetPoint");
        gun = transform.Find ("TankTurret");
        firePoint = transform.Find ("TankTurret/FirePoint");
        healthColor = transform.Find ("HealthCanvas/Slider/Fill Area/Fill").GetComponent<Image> ();
        healthSlider = transform.Find ("HealthCanvas/Slider").GetComponent<Slider> ();
        resultUI = GameObject.FindWithTag ("UI");
    }

    /// <summary>
    /// 本地玩家Start触发
    /// </summary>
    public override void OnStartLocalPlayer ()
    {
        //如果是本地玩家
        if (isLocalPlayer) {
            //设置摄像机跟踪点
            Camera.main.GetComponent<MyCameraFollow> ().SetTarget (targetPoint);
        }
    }

    [ClientCallback]
    void Update ()
    {
        //设置血条背景颜色
        healthColor.color = colors [tankNum];
        //设置血条值
        healthSlider.value = health;
        //如果是本地玩家
        if (isLocalPlayer) {
            //操纵坦克
            hor = Input.GetAxis ("Horizontal");
            ver = Input.GetAxis ("Vertical");
            gunDir = Input.GetAxis ("GunDirection");
            rig.MovePosition (transform.position + transform.forward * ver * Time.deltaTime * tankMoveSpeed);
            transform.eulerAngles += Vector3.up * hor * tankTurnSpeed;
            gun.transform.eulerAngles += Vector3.up * gunDir * tankTurnSpeed;
            //如果坦克移动
            if (hor != 0 || ver != 0) {
                if (aud.clip == idle) {
                    aud.Stop ();
                    aud.clip = run;
                } else {
                    if (!aud.isPlaying) {
                        aud.Play ();
                    }
                }
            } else {
                if (aud.clip == run) {
                    aud.Stop ();
                    aud.clip = idle;
                } else {
                    if (!aud.isPlaying) {
                        aud.Play ();
                    }
                }
            }
            //发射炮弹
            if (Input.GetKeyDown (KeyCode.Space)) {
                CmdFire ();
            }
        }
        //如果血量见底
        if (health <= 0) {
            //本地玩家失败
            if (isLocalPlayer) {
                resultUI.transform.GetChild (0).gameObject.SetActive (true);
                resultUI.transform.GetChild (0).GetChild (0).GetComponent<Text> ().text = "GameOver";
                resultUI.transform.GetChild (0).GetChild (1).GetComponent<Text> ().text = "GameOver";
            }
            //非本地玩家胜利
            else {
                resultUI.transform.GetChild (0).gameObject.SetActive (true);
                resultUI.transform.GetChild (0).GetChild (0).GetComponent<Text> ().text = "Victory";
                resultUI.transform.GetChild (0).GetChild (1).GetComponent<Text> ().text = "Victory";
            }
        }
    }

    [Command]
    /// <summary>
    /// 发射炮弹
    /// </summary>
    void CmdFire ()
    {
        GameObject bullet = (GameObject)Instantiate (MyLobbyManager.instance.spawnPrefabs [0],
                                firePoint.position, firePoint.rotation);
        bullet.GetComponent<Rigidbody> ().velocity = bullet.transform.forward * 20;
        NetworkServer.Spawn (bullet);
    }
}
玩家准备界面
双方玩家都已准备完毕倒计时
主场景开炮射击

结束语

相比从前的老网络系统,新版网络解决了很多Bug,也进一步做了优化,没有出现老网络的尴尬问题,只是新网络同步帧速率有些低,有时候会出现延迟较大的情况,这方面还有待改进。新网络类多内容也多,感兴趣的同学还需要多去看API,关于新网络今后还会有续集喔,敬请期待。本次项目链接: http://pan.baidu.com/s/1jHMN9zS 密码: wh3n

上一篇下一篇

猜你喜欢

热点阅读