Unet网络组件的使用详解

发表于2018-03-22
评论0 2.8k浏览
Unity自带的Unet网络组件可以帮助我们快速开发网络游戏,这里就给大家简单介绍下如何利用Unet 组件搭建了一个Unity官网介绍的小游戏。

游戏截图:
相当于一款简单的TPS游戏,在这里游戏模型很简单,只由几个常见几何体构建完成

一、主角的联机同步

创建一个空对象,命名为Network Manager,在其添加Network Manager和Network Manager HUD组件

接下来搭建一个主角

在主角下添加如下图三个组件:
1. 在Network Controller组件上在Local Player Authorit上打勾,意思上在服务器端和客户端都将创建一个主角;倘若在Server Only上打勾,该模型主角将只在游戏服务器端创建,客户端上将无法生成

2. 添加Network Transform组件,选择Sync Transform,添加该组件主角的移动,旋转,缩放将会在客户端与服务器端同步;倘若选择Sync Rigidy 3D,则主角的物理运动将在客户端与服务器端同步

3. 添加HeroController C#脚本组件,用于控制子弹射击,旋转,移动

代码如下:
HeroController.cs
using UnityEngine;  
using System.Collections;  
using UnityEngine.Networking;  
public class HeroController : NetworkBehaviour   
{  
    ///旋转速度  
    private float rotateSpeed=40.0f;  
    // 平移速度  
    private float translateSpeed=1.5f;  
    //子弹速度  
    private float bulletSpeed=3.0f;  
    public GameObject bulletObj;  
    public Transform bulletTransform;  
    private float KeyJLifeTime=0.3f;  
    private float KeyJTimer=1.0f;  
    // Use this for initialization  
    void Start ()   
    {  
    }  
    // Update is called once per frame  
    void Update ()   
    {  
        //判断该游戏对象是否处于当前网络  
        if (!isLocalPlayer)   
        {  
            return;  
        }  
        ChangeColor ();  
        HeroActionByTransform ();  
    }  
    private void ChangeColor()  
    {  
        //如果是本地角色,将主角的颜色更换  
        this.transform.GetComponent<MeshRenderer> ().material.color = Color.blue;  
    }  
    private void HeroActionByTransform()  
    {  
        //A,D键  
        float h = Input.GetAxis ("Horizontal");  
        if(h<0)  
        {  
            //旋转  
            this.transform.Rotate(Vector3.up*Time.deltaTime*rotateSpeed);  
        }  
        else if(h>0)  
        {  
            this.transform.Rotate(-Vector3.up*Time.deltaTime*rotateSpeed);  
        }  
        //W,S键  
        float v = Input.GetAxis ("Vertical");  
        if(v>0)  
        {  
            //移动  
            this.transform.Translate(Vector3.forward*Time.deltaTime*translateSpeed);  
        }  
        else if(v<0)  
        {  
            this.transform.Translate(-Vector3.forward*Time.deltaTime*translateSpeed);  
        }  
        KeyJTimer += Time.deltaTime;  
        //J键  
        if (Input.GetKey (KeyCode.J))   
        {  
            if(KeyJTimer>KeyJLifeTime)  
            {  
                KeyJTimer=0.0f;  
                CmdFire();  
            }  
        }  
    }  
    [Command]//表示客户端调用,服务器端运行(换句话说是在服务器里执行的代码,客户端上不执行)  
    private void CmdFire()//函数名一定要添加上"Cmd"  
    {  
        //实例化子弹  
        GameObject bullet= Instantiate(bulletObj,bulletTransform.position,bulletObj.transform.rotation) as GameObject;  
        //bullet.transform.GetComponent<Rigidbody> ().velocity = GameObject.FindGameObjectWithTag ("BulletPosition").transform.forward * bulletSpeed;;  
        //如果运行这行代码的话,寻找名BulletPosition这个游戏对象是服务器角色本身  
        //但是按以下写法,主角是客服端  
        bullet.transform.GetComponent<Rigidbody> ().velocity =this.transform.forward* bulletSpeed;  
        Destroy (bullet,5.0f);  
        //将子弹实体同步到客户端显示,客户端上依旧没有创建实体  
        //一下函数就是将服务器端的实体同步到客户端上  
        //当该实例对象在服务器端上被销毁后,客户端将也随着不被显示了  
        //但是该子弹实例的Rigidy组件需要移动,所以该游戏对象需要添加组件----Rigidy Box组件  
        NetworkServer.Spawn (bullet);  
    }  
}  

这里要特别注意是Vector3.forward而不是transform.forward

创建好主角预制体后将其挂载到network manager上,如下图:
之后便可以测试下主角的移动在否可以同步了

二、子弹的联机同步

也想创建主角预制体一样,添加Network Identity和Network Transform两个组件
此时Network Transform的组件选择Sync Rigidy 3D

将创建好的子弹预制体拖动到network manager,如下图:

三、血条的创建与同步

<1>血条的创建
这里使用进度条来模拟血条,将Slider进度条组件按如下图方式改造,将Background改成红色,Fill改成绿色
并将Canvas加入到主角模型内

<2>血条信息的同步
首先申明:如果用子弹则撞击来作为降低血量的信号的话,由于子弹的创建是在服务器端里创建的,前面也说到----当服务器端的子弹杯销毁后客户端的子弹也会随之销毁,这样势必会使得有时客户端的血量没有减少

这时我们需要使用其他的信号来使得血量同步------就是将服务器端的血量同步到客户端上
先贴代码:
using UnityEngine;  
using System.Collections;  
using UnityEngine.UI;  
using UnityEngine.Networking;  
public class Health : NetworkBehaviour {  
    public const int maxHealth = 100;  
    [SyncVar(hook="OnChangeHealth") ]  
    public int currentHealth = maxHealth;  
    public Slider healthSlider;  
    public bool destroyOnDeath = false;  
    private NetworkStartPosition[] spawnPoints;  
    void Start()  
    {  
        if (isLocalPlayer)  
        {  
            spawnPoints = FindObjectsOfType<NetworkStartPosition>();  
        }  
    }  
    public void TakeDamage(int damage)  
    {  
        if (isServer == false) return;// 血量的处理只在服务器端执行  
        currentHealth -= damage;  
        if (currentHealth <= 0)  
        {  
            if (destroyOnDeath)  
            {  
                Destroy(this.gameObject); return;  
            }  
            currentHealth = maxHealth;  
            Debug.Log("Dead");  
            RpcRespawn();  
        }  
    }  
    void OnChangeHealth(int health)  
    {  
        healthSlider.value = health / (float)maxHealth;  
    }  
    [ClientRpc]  
    void RpcRespawn()  
    {  
        if (isLocalPlayer == false) return;  
        Vector3 spawnPosition = Vector3.zero;  
        if (spawnPoints != null && spawnPoints.Length > 0)  
        {  
            spawnPosition = spawnPoints[Random.Range(0, spawnPoints.Length)].transform.position;  
        }  
        transform.position = spawnPosition;  
    }  
}  

1.所以血条只能在服务器端里减少

2.
[SyncVar(hook="OnChangeHealth") ]  
    public int currentHealth = maxHealth;  
[SyncVar]]的意思是定义一个同步的变量------在该服务器里定义的一个变量,客户端里也会同步,在后面加个(hook="OnChangeHealth")意思就是当这个变量的值发生改变后会调用在你自己定义的一个函数名为OnChangeHealth的函数

注意:
子弹的创建是在服务器里创建的,在客户端里掉用(参考HeroController.cs脚本注释)
NetworkServer.Spawn (bullet);
该函数将服务器端创建的子弹同步到客户端上。如果没有该函数,子弹只会在服务器端里显示,客户端里将不会显示,因为没有同步到客户端里。
但是加上该函数还是会有一个情况,客户端里显示了子弹,但是没有速度。

这是因为在子弹预制体里没有添加network transform组件,所以该组件的作用就是为了使得子弹的物理速度得到同步。
至于为什么服务器端的子弹被销毁后,相应客户端的子弹也将不再显示了的原因是因为子弹只是在服务器端里创建的,客户端并没有创建子弹,一旦服务器端的子弹对象被销毁,客户端一将不再显示了。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引