前一篇文章已经介绍了弹幕的制作过程,然后服务器和客户端的交互不是很流畅,最近新学习了Unet,Unity自带的NetworkManager,使用这个实现的效果很不错,还可以发送中文啥的,先看下图:
先说下前半段吧,Unet的原理学习,我这里就不介绍了,看此教程钱先看下Unet的教程,不然后面的可能不太明白,具体的官网有教程:官方教程地址
然后再次基础上,我把之前做的弹幕放了进去,就是在此工程上放了一个预制Text,还有前篇的两个脚本,一个TextItem.cs,一个InputControl.cs脚本,稍作修改:
修改如下
最终的Hierachy面板如图所示:
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
它的意思是,把要生成物体的NetId设置为目标父物体的NetId,这个只生效于第一个生成的物体有效,第二个必须在第一个消失后才能生效,猜测原因是统一目录下统一时间只能存在于一个id的物体,不能存在两个相同id的物体,所以此方法失败.
方法二:
直接根据netid找到客户端场景中的父物体,设置父物体
这个方法也是失效的,最后还是生成在根目录。
方法三:直接换了个思维,也是个取巧的方法,直接在TextItem子物体中自己设置自己的父物体,给父物体设置个tag
至于为什么把失败的方法贴出了,一是失败的方法可以改进成有效的方法,另一个有些功能就是用错误的方法用到其它地方就是正确的,这个我深有体会。
问题二:[Command]方法没有执行的问题?
之前实例化text文本的方法是放在InputControl下的,可是明明一样的功能,却没有被调用,如图:
这个[Command]必须要放在Player载体上才会被执行,其它不能执行。
最后整体演示图如下:
局域网内联机测试也是没问题的!