多人游戏——02Network View

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

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;
        }
    }
}
时间: 2024-08-28 08:05:18

多人游戏——02Network View的相关文章

好吧就让我们结束这一切吧

http://www.tudou.com/programs/view/K7lbU7LsiJI/HGN13.htmlhttp://www.tudou.com/programs/view/SfcF7r7DsCk/XCWU7.htmlhttp://www.tudou.com/programs/view/BbljqbN52ZY/ieGvM.htmlhttp://www.tudou.com/programs/view/R4thdEitDik/17gGr.htmlhttp://www.tudou.com/p

法涉法而尴尬的收入法国

http://www.tudou.com/programs/view/pQOO07vn4Sc/l99Fi.htmlhttp://www.tudou.com/programs/view/EDJkM6ojYkc/r7RA6.htmlhttp://www.tudou.com/programs/view/Gf0zVZygPtU/EmvXY.htmlhttp://www.tudou.com/programs/view/UhqFbgbyQ6k/Z23Lx.htmlhttp://www.tudou.com/p

我不要说谎好吗

http://www.tudou.com/programs/view/KhjSmQKHEzM/McEy9.htmlhttp://www.tudou.com/programs/view/5FNDTTRKi6Y/9719g.htmlhttp://www.tudou.com/programs/view/UWFf0dz2DEk/3xPj2.htmlhttp://www.tudou.com/programs/view/b2zsYr4dCZg/gv6FP.htmlhttp://www.tudou.com/p

我不要说谎

http://www.tudou.com/programs/view/KhjSmQKHEzM/McEy9.htmlhttp://www.tudou.com/programs/view/5FNDTTRKi6Y/9719g.htmlhttp://www.tudou.com/programs/view/UWFf0dz2DEk/3xPj2.htmlhttp://www.tudou.com/programs/view/b2zsYr4dCZg/gv6FP.htmlhttp://www.tudou.com/p

Node.js 打造实时多人游戏框架

在 Node.js 如火如荼发展的今天,我们已经可以用它来做各种各样的事情.前段时间UP主参加了极客松活动,在这次活动中我们意在做出一款让“低头族”能够更多交流的游戏,核心功能便是 Lan Party 概念的实时多人互动.极客松比赛只有短得可怜的36个小时,要求一切都敏捷迅速.在这样的前提下初期的准备显得有些“水到渠成”.跨平台应用的 solution 我们选择了node-webkit,它足够简单且符合我们的要求. 按照需求,我们的开发可以按照模块分开进行.本文具体讲述了开发 Spaceroom

Photon多人游戏开发教程

http://gad.qq.com/article/detail/26112 PUN介绍 入门 Photon Unity Networking(首字母缩写PUN)是一个Unity多人游戏插件包.它提供了身份验证选项.匹配,以及快速.可靠的通过我们的Photon后端实现的游戏内通信. PUN输出几乎所有Unity支持的平台,且有两种选项: 注意:对于Unity 5,两个PUN插件包都含相同的文件.你可以买PUN+ 来获得60个月的100 CCU,但客户端上仍使用PUN Free. PUN.PUN+

如何解决IOS 动画中 Autolayout 与View Transforms的冲突

IOS 的动画放大与缩小,并非按照找它的中心点放大和缩小,而是左上角 .我分析了下原来是Autolayout 与View Transforms的冲突造成的. - (void) addSubviewWithZoomInAnimation:(UIView*)view duration:(float)secs option:(UIViewAnimationOptions)option { // first reduce the view to 1/100th of its original dimen

能够在子线程绘画的View SurfaceView

转载请注明出处:王亟亟的大牛之路 近期两天都没有写文章,一方面是自己在看书.一方面不知道写什么,本来昨天想写Glide或者RxAndroid的东西结果公司的"狗屎"网怎么都刷不好Gradle我也是无语了(FQ也没用).准备今天背着笔记本 回家搞.真是服了.. 抱怨的话不说了,来看下这一篇要讲的主角 SurfaceView,关于SurfaceView的文章事实上在别的一些网站上也有,由于我之前没写过,所以也就一直没整这部分的内容(别人写的好坏反正找好的点自己吸收吧,嘿嘿) 问题:Surf

自定义View 篇三 《手动打造ViewPage》

有了之前自定义View的理论基础,有了ViewPage.事件分发机制.滑动冲突.Scroller使用等相关知识的铺垫,今天纯手动打造一款ViewPage. 1.完成基本的显示: 在MainActivity中: public class MainActivity extends AppCompatActivity { private MyViewPage mViewPage; int[] imageIds = new int[]{ R.drawable.pic_0, R.drawable.pic_