前一篇文章已经介绍了弹幕的制作过程,然后服务器和客户端的交互不是很流畅,最近新学习了Unet,Unity自带的NetworkManager,使用这个实现的效果很不错,还可以发送中文啥的,先看下图:


unity 悬停弹窗 unity 弹幕_服务器


先说下前半段吧,Unet的原理学习,我这里就不介绍了,看此教程钱先看下Unet的教程,不然后面的可能不太明白,具体的官网有教程:官方教程地址


然后再次基础上,我把之前做的弹幕放了进去,就是在此工程上放了一个预制Text,还有前篇的两个脚本,一个TextItem.cs,一个InputControl.cs脚本,稍作修改:


修改如下


最终的Hierachy面板如图所示:


unity 悬停弹窗 unity 弹幕_unity_02


InputField添加InputControl脚本和NetworkIdentity:

InputContol脚本的代码更新为如下:


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

public class InputControl : NetworkBehaviour
{
    /// <summary>
    /// text prefab
    /// </summary>
    public GameObject item;

    /// <summary>
    /// item的父物体
    /// </summary>
    public Transform parent;

    /// <summary>
    /// 输入框
    /// </summary>
    private InputField inputField;


    /// <summary>
    /// InputControl的单列
    /// </summary>
    public static InputControl Instance;

    /// <summary>
    /// 当前文本框的文字内容
    /// </summary>
    public static string currentText;

    // Use this for initialization
    void Start()
    {
        Instance = this;
        inputField = GetComponent<InputField>();
    }

    public void OnSumbit()
    {
        Debug.Log("===========1==============");
        currentText = inputField.text;
        PlayerController.Instance.CmdCreateItem(currentText);
        inputField.text = "";
        
        Debug.Log("===========2===============");
    }

    /// <summary>
    /// 实例化文本
    /// </summary>
    /// <param name="text">文本内容</param>
    /// <returns></returns>
    public GameObject CreateItem(string text)
    {
        Debug.Log("===========3===============");
        GameObject temp = Instantiate(item, parent) as GameObject;
        temp.GetComponent<TextItem>().contentText = text;

        return temp;
    }

}



TextItem脚本更新为:


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

public class TextItem : NetworkBehaviour
{
    public string text="&&&&&";

    private Text textAnchor;

    /// <summary>
    /// 同步文本内容
    /// </summary>
    [SyncVar(hook="OnChangeText")]
    public string contentText = "";

    void Start()
    {
        
        transform.SetParent(GameObject.FindGameObjectWithTag("UGUI").transform);

        textAnchor = GetComponent<Text>();

        textAnchor.text = contentText;
        textAnchor.color = Random.ColorHSV();
        float y = Random.Range(-200f, 220f);
        Debug.Log(y);
        
        transform.localPosition = new Vector3(550f, y, 0);
    }

    public float speed;

    void Update()
    {
        if (speed != 0)
        {
            float x = transform.localPosition.x + speed * Time.deltaTime;
            transform.localPosition = new Vector3(x, transform.localPosition.y, 1);

            //出屏幕销毁
            if (transform.localPosition.x < -550f)
            {
                Destroy(gameObject);
            }
        }
    }

    void OnChangeText(string text)
    {
        textAnchor = GetComponent<Text>();
        textAnchor.text = text;
    }
}



最后PlayerControl也需要更新下,具体原因,下面会具体详解:

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

public class PlayerController : NetworkBehaviour
{
    public GameObject bulletPrefab;
    public Transform bulletSpawn;

    public static PlayerController Instance;

    // Update is called once per frame
    void Update()
    {
        if (isLocalPlayer == false)
            return;



        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");

        transform.Rotate(Vector3.up * h * 120 * Time.deltaTime);
        transform.Translate(Vector3.forward * v * 3 * Time.deltaTime);

        if (Input.GetKeyDown(KeyCode.Space))
        {
            CmdFire();
        }
    }

    public override void OnStartLocalPlayer()
    {
        Instance = this;
        //only worked on local role ,when the role was created
        GetComponent<MeshRenderer>().material.color = Color.blue;
    }

    [Command]//called in client ,run in server
    void CmdFire()//use by server
    {
        Debug.Log("+++++++++++++");
        //bullet should be born on server then Synchronise to other client
        GameObject bullet = Instantiate(bulletPrefab, bulletSpawn.position, bulletSpawn.rotation) as GameObject;
        bullet.GetComponent<Rigidbody>().velocity = bullet.transform.forward * 10;
        Destroy(bullet, 2);

        NetworkServer.Spawn(bullet);
    }

    [Command]
    public void CmdCreateItem(string text)
    {
        GameObject temp = InputControl.Instance.CreateItem(text);

        NetworkServer.Spawn(temp);
    }
}



到这里为止,你就会发现工程的客户端和服务器端可以交互的成功运行起来了,但是做到这个过程中遇到了很多问题,主要是两个问题解决的心得:


问题一:NetworkServer.Spawn(temp);



描述:Network生成物体,会直接生产的根目录下,但是我们的text是ugui必须要在Canvas目录下,即使不是,以后也会遇到类似特效生成在武器目录下的问题?




解决过程:



方法一: http://answers.unity3d.com/questions/1136831/unet-5x-instantiate-as-child-on-both-sides.html


unity 悬停弹窗 unity 弹幕_unity_03


它的意思是,把要生成物体的NetId设置为目标父物体的NetId,这个只生效于第一个生成的物体有效,第二个必须在第一个消失后才能生效,猜测原因是统一目录下统一时间只能存在于一个id的物体,不能存在两个相同id的物体,所以此方法失败.



方法二:


直接根据netid找到客户端场景中的父物体,设置父物体



unity 悬停弹窗 unity 弹幕_Unet_04




这个方法也是失效的,最后还是生成在根目录。


            方法三:直接换了个思维,也是个取巧的方法,直接在TextItem子物体中自己设置自己的父物体,给父物体设置个tag

unity 悬停弹窗 unity 弹幕_c#_05



至于为什么把失败的方法贴出了,一是失败的方法可以改进成有效的方法,另一个有些功能就是用错误的方法用到其它地方就是正确的,这个我深有体会。


问题二:[Command]方法没有执行的问题?



之前实例化text文本的方法是放在InputControl下的,可是明明一样的功能,却没有被调用,如图:





unity 悬停弹窗 unity 弹幕_unity 悬停弹窗_06




这个[Command]必须要放在Player载体上才会被执行,其它不能执行。




最后整体演示图如下:



unity 悬停弹窗 unity 弹幕_unity_07




局域网内联机测试也是没问题的!