码迷,mamicode.com
首页 > Web开发 > 详细

多人游戏——02Network View

时间:2015-11-11 16:11:39      阅读:313      评论:0      收藏:0      [点我收藏+]

标签:

学习笔记适合新手,如有错误请指正。?号处也请各位指点下,谢谢。

Network View是Unity封装的一套快速实现多人联机游戏的功能

接口介绍

Network View的接口主要包括属性和函数两个部分,属性主要用于获取当前的状态信息,函数主要用于通信

1属性

isMine:Network View是否是由“我”即本机创建的

observed:指定被观察及同步的组件或脚本

stateSynchronization:Network View类型Off为没有数据将被同步,ReliableDeltaCompressed为当数据发生变化时才发送,Unreliable为强制发送数据

viewID:NetworkViewID类型的ID

2函数

RPc(Remote Procedure Call,远程过程调用),在Network系统中可以理解为向其他机器发送消息,如果有同名函数将被调用

networkView.RPC(string funName,RPCMode mode,params object[] args);

参数funName为函数名,参数mode指定发送的范围,可选范围包括全部连接的机器包括本机,例如,All是发送给所有机器,Server是发送给服务器

args是传递的参数,可传可不传,支持类型包括int、float、string、NetworkPlayer、NetworkViewID、Vector3、Quaternion

只要有Network View,那么如何游戏对象上的脚本的函数体前用【RPC】标注,就可以通过RPC来通知调用,但是函数名必须唯一,否则其中只有一个会被调用

[RPC]

void funName()

{

........

}

实例——聊天室

新建场景新建空游戏对象并命名为Manager,并为其添加代码

代码中STATE枚举,idle是为创建或连接服务器时的空闲状态,server是作为服务器的状态,client是作为客户端的状态

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class C_16_2_2 : MonoBehaviour {
    private STATE state = STATE.idle;
    private string IP = "10.234.41.123";
    private int Port = 1000;
    
    private NetworkView networkView;
    private string myName="";
    //聊天消息记录
    private string textRecord="";
    //将要发送的消息
    private string textToSend="";
    //显示聊天记录的ScrollView的滚动位置
    private Vector2 scrollPos;
    //显示聊天室当前人员的ScrollView的滚动位置
    private Vector2 scrollPos_manlist;
    //储存NetworkViewID以及名字的字典
    private Dictionary<string,string> manList = new Dictionary<string, string>();
    //整个区域的宽度
    private float thewidth = 500f;
    //整个区域的高度
    private float theheight = 500f;
    //聊天室当前人员名字的链表
    private List<string> names = new List<string>();
    void Awake()
    {
        networkView = GetComponent<NetworkView>();
    }
    void OnGUI()
    {
        GUILayout.BeginVertical(GUILayout.Width(thewidth),GUILayout.Height(theheight));
        switch(state)
        {
        case STATE.idle:
            OnIdle();
            break;
        case STATE.server:
            Format_CutOffLine();
            Title();
            Format_CutOffLine();
            OnChat();
            Format_CutOffLine();
            OnServer();
            break;
        case STATE.client:
            Format_CutOffLine();
            Title();
            Format_CutOffLine();
            OnChat();
            Format_CutOffLine();
            OnClient();
            break;
        }
        GUILayout.EndVertical();
    }
    //显示标题
    void Title()
    {
        GUILayout.BeginHorizontal();
        GUILayout.Space(thewidth*0.5f -10f);
        GUILayout.Label("聊天室");
        GUILayout.EndHorizontal();
    }
    //idle状态
    void OnIdle()
    {
        Format_CutOffLine();
        GUILayout.BeginHorizontal();
        GUILayout.Label("我的昵称:",GUILayout.Width(100));
        myName = GUILayout.TextField(myName,GUILayout.Width(thewidth-100));
        GUILayout.EndHorizontal();
        Format_CutOffLine();

        Format_Space();

        Format_CutOffLine();
        if(GUILayout.Button("建立聊天室",GUILayout.Width(thewidth)))
        {
            NetworkConnectionError error  = Network.InitializeServer(10,Port,false);
            if(error == NetworkConnectionError.NoError)
            {
                manList.Add(Network.player.ToString(),myName);
                Server_UpdateNameList();
                state = STATE.server;
            }
        }
        Format_CutOffLine();

        Format_Space();

        Format_CutOffLine();
        GUILayout.BeginHorizontal();
        GUILayout.Label("服务器IP地址:");
        IP = GUILayout.TextField(IP,GUILayout.Width(thewidth-100));
        GUILayout.EndHorizontal();
        if(GUILayout.Button("连接服务器",GUILayout.Width(thewidth)))
        {
            NetworkConnectionError error  = Network.Connect(IP,Port);
            if(error == NetworkConnectionError.NoError)
            {
                state = STATE.client;
            }
        }
        Format_CutOffLine();
    }
    //连接服务器
    void OnConnectedToServer() {
        Debug.Log("Connected to server");
        networkView.RPC("Server_Join", RPCMode.Server,myName);
    }
    //当有客户端断开服务器时被调用的函数
    void OnPlayerDisconnected(NetworkPlayer player) {
        string id = player.ToString();
        Debug.Log("Leaved:"+id);
        if(manList.ContainsKey(id))
        {
            networkView.RPC("SendText", RPCMode.All,manList[id]+"离开了聊天室");
            manList.Remove(id);
        }
        Server_UpdateNameList();
    }
    //server状态
    void OnServer()
    {
        GUILayout.Label("当前连接数量:"+ Network.connections.Length);
        if(GUILayout.Button("关闭聊天室",GUILayout.Width(thewidth)))
        {
            Network.Disconnect();
            state = STATE.idle;
        }
    }
    //client状态
    void OnClient()
    {
        if(GUILayout.Button("离开聊天室",GUILayout.Width(thewidth)))
        {
            Network.Disconnect();
            state = STATE.idle;
        }
    }
    //共用的处理聊天的函数
    void OnChat()
    {
        GUILayout.BeginHorizontal();

            GUILayout.BeginVertical();
            scrollPos = GUILayout.BeginScrollView(scrollPos,GUILayout.Width(thewidth-100),GUILayout.Height(300));
            GUILayout.Label(textRecord);
            GUILayout.EndScrollView();
            GUILayout.EndVertical();

            GUILayout.BeginVertical();
            GUILayout.Label("~聊天室成员~");
            scrollPos_manlist = GUILayout.BeginScrollView(scrollPos_manlist,GUILayout.Width(100),GUILayout.Height(300));
            string txt ="";
            foreach(var s in names)
            {
                txt += s +"\n";
            }
            GUILayout.Label(txt);
            GUILayout.EndScrollView();
            GUILayout.EndVertical();

        GUILayout.EndHorizontal();

        GUILayout.BeginHorizontal();
            textToSend = GUILayout.TextField(textToSend,GUILayout.Width(thewidth-50));
            if(GUILayout.Button("发送",GUILayout.Width(50)))
            {
                networkView.RPC("SendText", RPCMode.All,myName+":"+textToSend);
                textToSend ="";
            }
        GUILayout.EndHorizontal();
    }
    //服务器向客户端发送人员名单及聊天记录,供客户端更新显示
    void Server_UpdateNameList()
    {
        string textToSend="";
        foreach(var item in manList)
        {
            textToSend += item.Value+"|";
        }
        networkView.RPC("RPC_GetNameList", RPCMode.All,textToSend);
        networkView.RPC("RPC_ChatRecordUpdate", RPCMode.All,textRecord);
    }
    //不同栏间的填充
    void Format_CutOffLine()
    {
        GUILayout.Label("===============================================================");
    }
    //标准间隔
    void Format_Space()
    {
        GUILayout.Space(50);
    }
    //当客户端连接成功时向服务器发送的消息
    [RPC]
    public void Server_Join(string client_name, NetworkMessageInfo info)
    {
        string id = info.sender.ToString();
        Debug.Log("Joined:"+id);
        networkView.RPC("SendText", RPCMode.All,client_name+"加入了聊天室");
        manList.Add(id,client_name);
        Server_UpdateNameList();
    }
    //发送文本
    [RPC]
    public void SendText(string content, NetworkMessageInfo info)
    {
        textRecord += content+"\n";
    }
    //人员名单
    [RPC]
    public void RPC_GetNameList(string content, NetworkMessageInfo info)
    {
        string[] ss = content.Split(|);
        names.Clear();
        names.AddRange(ss);
    }
    //聊天记录
    [RPC]
    public void RPC_ChatRecordUpdate(string content)
    {
        textRecord = content;
    }
}

实例——碰碰乐

本例中僬侥实现的是一个小游戏,多个玩家在平台上格子控制一个球体,球体相互碰撞,球体被撞落下平台的玩家为输

新建一个Plane作为平台,调整摄像机视角,新建球体,创建物理材质并赋予sphere Collider,添加刚体组件和Network View组件

最后添加脚本,该代码主要让Network View监听刚体,并使其在玩家之间同步数据

using UnityEngine;
using System.Collections;

public class SphereSerializer : MonoBehaviour {
    private Rigidbody rigidbody;
    private NetworkView networkView;
    void Awake()
    {
        rigidbody = GetComponent<Rigidbody>();
        networkView = GetComponent<NetworkView>();
        //设置networkView的监听对象为刚体
        networkView.observed = rigidbody;
    }
}

再创建控游戏对象并命名Manager,为其添加脚本

using UnityEngine;
using System.Collections;

public enum BallGameState
{
    lobby,
    idle,
    gaming,
    lose
}
public class C_16_2_3 : MonoBehaviour {
    public GameObject prefab_ball;
    private BallGameState state = BallGameState.lobby;
    private string IP = "10.234.41.123";
    private int Port = 1000;
    private Rigidbody myBall;
    
    void OnGUI()
    {
        switch(state)
        {
        case BallGameState.lobby:
            OnLobby();
            break;
        case BallGameState.idle:
            OnIdle();
            break;
        case BallGameState.gaming:
            OnGaming();
            break;
        case BallGameState.lose:
            OnLose();
            break;
        }
    }
    //大厅状态逻辑
    void OnLobby()
    {
        if(GUILayout.Button("建立服务器"))
        {
            NetworkConnectionError error  = Network.InitializeServer(10,Port,false);
            if(error == NetworkConnectionError.NoError)
                state = BallGameState.idle;
        }
        GUILayout.Label("======================================");
        GUILayout.BeginHorizontal();
        GUILayout.Label("服务器IP地址:");
        IP = GUILayout.TextField(IP);
        GUILayout.EndHorizontal();
        if(GUILayout.Button("连接服务器"))
        {
            NetworkConnectionError error  = Network.Connect(IP,Port);
            if(error == NetworkConnectionError.NoError)
                state = BallGameState.idle;
        }
    }    
    //空闲状态逻辑
    void OnIdle()
    {
        if(GUILayout.Button("Start!"))
        {
            StartGaming();
        }
    }
    //游戏状态逻辑
    void OnGaming()
    {
        //控制自己的球体
        float x = Input.GetAxis("Horizontal");
        float y = Input.GetAxis("Vertical");
        myBall.AddForce(new Vector3(x,0,y));

        //出界则失败
        if(myBall.transform.position.y <-5)
        {
            Network.Destroy(myBall.GetComponent<NetworkView>().viewID);
            state = BallGameState.lose;
        }
    }
    //失败状态逻辑
    void OnLose()
    {
        GUILayout.Label("You Lose!");
        if(GUILayout.Button("Start Again!"))
        {
            StartGaming();
        }
    }
    //开始游戏
    void StartGaming()
    {
        state = BallGameState.gaming;

        GameObject obj = Network.Instantiate(prefab_ball,new Vector3(0,1,0),Quaternion.identity,0) as GameObject;
        myBall = obj.GetComponent<Rigidbody>();
    }
}

在Build Settings窗口打包本场景,在编辑器及设备上同时运行

Network View准许指定Transform、rigdbody等组件作为被观察和同步的对象,也准许指定自定义作为被观察和同步的对象,因为脚本继承自MonoBehavior,MonoBehavior继承子Behaviour,Behaviour继承自Component,在脚本中,我们需要实现OnSerializeNetworkView函数同步数据

将脚本上的脚本SphereSerializer替换为SphereSerializer2

using UnityEngine;
using System.Collections;

public class SphereSerializer2 : MonoBehaviour {
    private Rigidbody rigidbody;
    private Renderer renderer;
    private NetworkView networkView;
    
    bool firstSerialize = true;
    void Awake()
    {
        rigidbody = GetComponent<Rigidbody>();
        renderer = GetComponent<Renderer>();
        
        networkView = GetComponent<NetworkView>();
        networkView.observed = this;
    }
    //同步数据
    void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info) {
        if(firstSerialize)
        {
            firstSerialize = false;
            Vector3 color = Vector3.zero;
            if (stream.isWriting) {
                if(networkView.isMine)
                    color = new Vector3(1,0,0);
                else
                    color = new Vector3(0,1,0);
                stream.Serialize(ref color);
            } else {
                stream.Serialize(ref color);
            }
            renderer.material.color = new Color(color.x,color.y,color.z);
        }

        Vector3 pos= Vector3.zero;
        Quaternion rotation= Quaternion.identity;
        Vector3 velocity = Vector3.zero;

        if (stream.isWriting) {
            pos = rigidbody.position;
            stream.Serialize(ref pos);

            rotation = rigidbody.rotation;
            stream.Serialize(ref rotation);

            velocity = rigidbody.velocity; 
            stream.Serialize(ref velocity);
        } else {
            stream.Serialize(ref pos);
            transform.position = pos;

            stream.Serialize(ref rotation);
            transform.rotation = rotation;

            stream.Serialize(ref velocity);
            rigidbody.velocity = velocity;
        }
    }
}

多人游戏——02Network View

标签:

原文地址:http://www.cnblogs.com/kubll/p/4956168.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!