Unity 网络通信
2015-07-15 10:57
549 查看
http://blog.csdn.net/chenggong2dm/article/details/7896440
最新消息:在11月2日的开发者大会上,我问了一下unity的人,会不会对网络层进行封装和改进,unity的人表示,最晚5.0,最快4.x版本里,就会加入新的网络通信接口,他们已经在测试阶段了。
翻了半天unity的API,也没有发现可以处理Socket的方法。Unity自己封了个网络处理的类Network,主要用于状态同步。 似乎是要实现MMO级别的网络应用,只能用C#自己去写了。(Unity不像AS,有直接处理数据包、字节级别的底层函数。至少现在没有。如果您要应用于实际,比如做了个网游,那么您需要用unity自带的C#去写一套自定义通信协议来实现。这个我在后面的文章中会写一个示例。)
我们还是先了解一下Unity,看看用Unity自带的组件,如何编写通信,从最简单的开始。
1,新建项目。新建一个项目里面会有一个摄像机。
2,编写服务器端代码cs:
[csharp] view
plaincopy
using UnityEngine;
using System.Collections;
public class server : MonoBehaviour {
int Port = 10000;
//OnGUI方法,所有GUI的绘制都需要在这个方法中实现
void OnGUI(){
//Network.peerType是端类型的状态:
//即disconnected, connecting, server 或 client四种
switch(Network.peerType){
//禁止客户端连接运行, 服务器未初始化
case NetworkPeerType.Disconnected:
StartServer();
break;
//运行于服务器端
case NetworkPeerType.Server:
OnServer();
break;
//运行于客户端
case NetworkPeerType.Client:
break;
//正在尝试连接到服务器
case NetworkPeerType.Connecting:
break;
}
}
void StartServer(){
//当用户点击按钮的时候为true
if (GUILayout.Button("创建服务器")) {
//初始化本机服务器端口,第一个参数就是本机接收多少连接
NetworkConnectionError error = Network.InitializeServer(12,Port,false);
Debug.Log("错误日志"+error);
}
}
void OnServer(){
GUILayout.Label("服务端已经运行,等待客户端连接");
//Network.connections是所有连接的玩家, 数组[]
//取客户端连接数.
int length = Network.connections.Length;
//按数组下标输出每个客户端的IP,Port
for (int i=0; i<length; i++)
{
GUILayout.Label("客户端"+i);
GUILayout.Label("客户端ip"+Network.connections[i].ipAddress);
GUILayout.Label("客户端端口"+Network.connections[i].port);
}
//当用户点击按钮的时候为true
if (GUILayout.Button("断开服务器")){
Network.Disconnect();
}
}
/* 系统提供的方法,该方法只执行一次 */
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
3,把这个代码拖拽到摄像机上。
4,运行程序 File---->Build settings---->Build And Run,选择Web Player
5,服务器端建立完毕,保持服务器端的运行状态。
6,这里可以再新建立个项目写客户端。嫌麻烦也可以像我这样,先把摄像机上的服务器脚本删掉,再把下面的客户端脚本拖拽上去。
客户端代码如下:
[csharp] view
plaincopy
using UnityEngine;
using System.Collections;
public class client : MonoBehaviour {
//要连接的服务器地址
string IP = "127.0.0.1";
//要连接的端口
int Port = 10000;
void OnGUI(){
//端类型的状态
switch(Network.peerType){
//禁止客户端连接运行, 服务器未初始化
case NetworkPeerType.Disconnected:
StartConnect();
break;
//运行于服务器端
case NetworkPeerType.Server:
break;
//运行于客户端
case NetworkPeerType.Client:
break;
//正在尝试连接到服务器
case NetworkPeerType.Connecting:
break;
}
}
void StartConnect(){
if (GUILayout.Button("连接服务器")){
NetworkConnectionError error = Network.Connect(IP,Port);
Debug.Log("连接状态"+error);
}
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
7,运行程序 File---->Build settings---->Build And Run,选择Web Player
8,结果如下,可以通信。
![](http://my.csdn.net/uploads/201208/22/1345628610_2945.jpg)
附注: 本文大部分代码出自《Unity 3D 游戏开发》,作者宣雨松。
在上一个例子基础上,我们构建一个聊天室程序。
1,首先建立一个新项目,文件夹名为chat。
2,给接收请求的脚本所绑定的对象,添加网络视图组件(听起来有点绕口)。我们的服务器脚本是绑定在主摄像机上的,所以点击主摄像机,在菜单上选择component-->miscellaneous-->Net work View。
3,服务器端程序:(C# 脚本)
[csharp] view
plaincopy
using UnityEngine;
using System.Collections;
public class server : MonoBehaviour {
int Port = 10100;
string Message = "";
//声明一个二维向量
Vector2 Sc;
//OnGUI方法,所有GUI的绘制都需要在这个方法中实现
void OnGUI(){
//Network.peerType是端类型的状态:
//即disconnected, connecting, server 或 client四种
switch(Network.peerType){
//禁止客户端连接运行, 服务器未初始化
case NetworkPeerType.Disconnected:
StartServer();
break;
//运行于服务器端
case NetworkPeerType.Server:
OnServer();
break;
//运行于客户端
case NetworkPeerType.Client:
break;
//正在尝试连接到服务器
case NetworkPeerType.Connecting:
break;
}
}
void StartServer(){
//当用户点击按钮的时候为true
if (GUILayout.Button("创建服务器")) {
//初始化本机服务器端口,第一个参数就是本机接收多少连接
NetworkConnectionError error = Network.InitializeServer(12,Port,false);
//连接状态
switch(error){
case NetworkConnectionError.NoError:
break;
default:
Debug.Log("服务端错误"+error);
break;
}
}
}
void OnServer(){
GUILayout.Label("服务端已经运行,等待客户端连接");
//Network.connections是所有连接的玩家, 数组[]
//取客户端连接数.
int length = Network.connections.Length;
//按数组下标输出每个客户端的IP,Port
for (int i=0; i<length; i++)
{
GUILayout.Label("客户端"+i);
GUILayout.Label("客户端ip"+Network.connections[i].ipAddress);
GUILayout.Label("客户端端口"+Network.connections[i].port);
GUILayout.Label("-------------------------------");
}
//当用户点击按钮的时候为true
if (GUILayout.Button("断开服务器")){
Network.Disconnect();
}
//创建开始滚动视图
Sc = GUILayout.BeginScrollView(Sc,GUILayout.Width(280),GUILayout.Height(400));
//绘制纹理, 显示内容
GUILayout.Box(Message);
//结束滚动视图, 注意, 与开始滚动视图成对出现
GUILayout.EndScrollView();
}
//接收请求的方法. 注意要在上面添加[RPC]
[RPC]
void ReciveMessage(string msg, NetworkMessageInfo info){
//刚从网络接收的数据的相关信息,会被保存到NetworkMessageInfo这个结构中
Message = "发送端"+info.sender +"消息"+msg;
//+"时间"+info.timestamp +"网络视图"+info.networkView
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
4,把脚本拖拽到主摄像机上,绑定。
5,运行程序 File---->Build settings---->Build And Run,选择Web Player
6,服务器端建立完毕,保持服务器端的运行状态。
7,简单起见,我们这里不新建项目。先把摄像机上的服务器脚本删掉,再把下面的客户端脚本拖拽上去。
客户端代码:
[csharp] view
plaincopy
using UnityEngine;
using System.Collections;
public class client : MonoBehaviour {
//要连接的服务器地址
string IP = "127.0.0.1";
//要连接的端口
int Port = 10100;
//聊天信息
string Message = "";
//声明一个二维向量
Vector2 Sc;
void OnGUI(){
//端类型的状态
switch(Network.peerType){
//禁止客户端连接运行, 服务器未初始化
case NetworkPeerType.Disconnected:
StartConnect();
break;
//运行于服务器端
case NetworkPeerType.Server:
break;
//运行于客户端
case NetworkPeerType.Client:
OnClient();
break;
//正在尝试连接到服务器
case NetworkPeerType.Connecting:
break;
}
}
void StartConnect(){
if (GUILayout.Button("连接服务器")){
NetworkConnectionError error = Network.Connect(IP,Port);
//连接状态
switch(error){
case NetworkConnectionError.NoError:
break;
default:
Debug.Log("客户端错误"+error);
break;
}
}
}
void OnClient(){
//创建开始滚动视图
Sc = GUILayout.BeginScrollView(Sc,GUILayout.Width(280),GUILayout.Height(400));
//绘制纹理, 显示内容
GUILayout.Box(Message);
//文本框
Message = GUILayout.TextArea(Message);
if (GUILayout.Button("发送")){
//发送给接收的函数, 模式为全部, 参数为信息
networkView.RPC("ReciveMessage", RPCMode.All, Message);
}
//结束滚动视图, 注意, 与开始滚动视图成对出现
GUILayout.EndScrollView();
}
//接收请求的方法. 注意要在上面添加[RPC]
[RPC]
void ReciveMessage(string msg, NetworkMessageInfo info){
//刚从网络接收的数据的相关信息,会被保存到NetworkMessageInfo这个结构中
Message = "发送端"+info.sender +"消息"+msg;
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
服务端运行状态:
![](http://my.csdn.net/uploads/201208/24/1345789506_2844.jpg)
客户端运行状态:
![](http://my.csdn.net/uploads/201208/24/1345789523_5837.jpg)
附注: 本文大部分代码出自《Unity 3D 游戏开发》,作者宣雨松。
在上两篇的基础上,这次我们要做物体同步。使两个物体在两个机器上显示同样的移动效果。这里,使用W、S、A、D四个键位实现前后左右的移动。
注意:如果您复制粘贴代码,千万注意文件编码!最好为ANSI,否则会报出各种各样奇怪的错误!
步骤:
1,首先建立一个新工程。
2,添加两个cube,分别重命名为Cube01,和Cube02。这两个cube就是我们要同步的对象。
3,添加一个平行光源,Directional light。否则你在漆黑的场景中,真的很难找到你的cube。
4,调整摄像机、cube、Directional light 的相对位置,使他们在视野内,可以看着它们运行。
![](http://my.csdn.net/uploads/201208/29/1346205603_9891.jpg)
5,create一个C#脚本cube_move,如下:
[csharp] view
plaincopy
using UnityEngine;
using System.Collections;
public class cube_move : MonoBehaviour {
string IP = "192.168.1.6";
int Port = 10100;
int moveSpeed = 16;
GameObject cube01 = null;
GameObject cube02 = null;
GameObject myself = null;
GameObject another = null;
// Use this for initialization
void Start () {
cube01 = GameObject.Find("Cube01");
cube02 = GameObject.Find("Cube02");
}
//OnGUI方法,所有GUI的绘制都需要在这个方法中实现
void OnGUI(){
//Network.peerType是端类型的状态:
//即disconnected, connecting, server 或 client四种
switch(Network.peerType){
//禁止客户端连接运行, 服务器未初始化
case NetworkPeerType.Disconnected:
StartServer();
break;
//运行于服务器端
case NetworkPeerType.Server:
OnServer();
break;
//运行于客户端
case NetworkPeerType.Client:
break;
//正在尝试连接到服务器
case NetworkPeerType.Connecting:
break;
}
}
void StartServer(){
GUILayout.Label("同步测试:");
//当用户点击按钮的时候为true
if (GUILayout.Button("创建服务器")) {
//初始化本机服务器端口,第一个参数就是本机接收多少连接
NetworkConnectionError error = Network.InitializeServer(12,Port,false);
//连接状态
switch(error){
case NetworkConnectionError.NoError:
break;
default:
Debug.Log("服务端错误"+error);
break;
}
}
if (GUILayout.Button("连接服务器")){
NetworkConnectionError error = Network.Connect(IP,Port);
//连接状态
switch(error){
case NetworkConnectionError.NoError:
break;
default:
Debug.Log("客户端错误"+error);
break;
}
}
}
void OnServer(){
GUILayout.Label("服务端已经运行,等待客户端连接");
//Network.connections是所有连接的玩家, 数组[]
//取客户端连接数.
int length = Network.connections.Length;
//按数组下标输出每个客户端的IP,Port
for (int i=0; i<length; i++)
{
GUILayout.Label("客户端"+i);
GUILayout.Label("客户端ip"+Network.connections[i].ipAddress);
GUILayout.Label("客户端端口"+Network.connections[i].port);
GUILayout.Label("-------------------------------");
}
//当用户点击按钮的时候为true
if (GUILayout.Button("断开服务器")){
Network.Disconnect();
}
}
//接收请求的方法. 注意要在上面添加[RPC]
[RPC]
void ProcessMove(string msg, NetworkMessageInfo info){
//刚从网络接收的数据的相关信息,会被保存到NetworkMessageInfo这个结构中
string sender = info.sender.ToString();
//看脚本运行在什么状态下
NetworkPeerType status = Network.peerType;
if (status == NetworkPeerType.Server)
{
myself = cube01; //假如运行在server状态下, 那么自己就是cube1
another = cube02;
}
else
{
myself = cube02; //假如运行在client状态下, 那么自己就是cube2
another = cube01;
}
//假如是自己发送的信息
if (sender == "-1")
{
if (msg == "W")
{
myself.transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed);
}
if (msg == "S")
{
myself.transform.Translate(Vector3.back * Time.deltaTime * moveSpeed);
}
if (msg == "A")
{
myself.transform.Translate(Vector3.left * Time.deltaTime * moveSpeed);
}
if (msg == "D")
{
myself.transform.Translate(Vector3.right * Time.deltaTime * moveSpeed);
}
}
//假如是别人发送的信息
else
{
if (msg == "W")
{
another.transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed);
}
if (msg == "S")
{
another.transform.Translate(Vector3.back * Time.deltaTime * moveSpeed);
}
if (msg == "A")
{
another.transform.Translate(Vector3.left * Time.deltaTime * moveSpeed);
}
if (msg == "D")
{
another.transform.Translate(Vector3.right * Time.deltaTime * moveSpeed);
}
}
}
// Update is called once per frame
void Update ()
{
//前移
if (Input.GetKeyDown(KeyCode.W))
{
Debug.Log("wwwwwwwww|");
networkView.RPC("ProcessMove", RPCMode.All, "W");
}
if (Input.GetKey(KeyCode.W))
{
networkView.RPC("ProcessMove", RPCMode.All, "W");
}
//后退
if (Input.GetKeyDown(KeyCode.S))
{
Debug.Log("sssssssss!");
networkView.RPC("ProcessMove", RPCMode.All, "S");
}
if (Input.GetKey(KeyCode.S))
{
networkView.RPC("ProcessMove", RPCMode.All, "S");
}
//向左移动
if (Input.GetKeyDown(KeyCode.A))
{
networkView.RPC("ProcessMove", RPCMode.All, "A");
}
if (Input.GetKey(KeyCode.A))
{
networkView.RPC("ProcessMove", RPCMode.All, "A");
}
//向右移动
if (Input.GetKeyDown(KeyCode.D))
{
networkView.RPC("ProcessMove", RPCMode.All, "D");
}
if (Input.GetKey(KeyCode.D))
{
networkView.RPC("ProcessMove", RPCMode.All, "D");
}
}
}
6,给接收请求的脚本所绑定的对象,添加网络视图组件(听起来有点绕口)。我们的这个脚本是绑定在主摄像机上的,所以点击主摄像机,在菜单上选择component-->miscellaneous-->Net work View。
7,绑定脚本,把脚本拖拽到摄像机上。
8,编译发布成web的:File---->Build settings---->Build And Run,选择Web Player
9,拷贝编译出来的文件夹,到另一台机器上。
10,分别在两台机器上运行服务器端和客户端。
你会看到同步的效果了。
附注:在一台机器上,不太容易看到效果。因为浏览器失焦后,似乎无法刷新画面,导致同步效果不明显。
Ps. 这个代码只是unity自带的网络组件示例。在实际应用中,不能直接应用。需要用unity底层支持的C#语言,重新写一套通信框架。我已经写了一套,但是没有时间发博客。改天发出来。
最新消息:在11月2日的开发者大会上,我问了一下unity的人,会不会对网络层进行封装和改进,unity的人表示,最晚5.0,最快4.x版本里,就会加入新的网络通信接口,他们已经在测试阶段了。
翻了半天unity的API,也没有发现可以处理Socket的方法。Unity自己封了个网络处理的类Network,主要用于状态同步。 似乎是要实现MMO级别的网络应用,只能用C#自己去写了。(Unity不像AS,有直接处理数据包、字节级别的底层函数。至少现在没有。如果您要应用于实际,比如做了个网游,那么您需要用unity自带的C#去写一套自定义通信协议来实现。这个我在后面的文章中会写一个示例。)
我们还是先了解一下Unity,看看用Unity自带的组件,如何编写通信,从最简单的开始。
1,新建项目。新建一个项目里面会有一个摄像机。
2,编写服务器端代码cs:
[csharp] view
plaincopy
using UnityEngine;
using System.Collections;
public class server : MonoBehaviour {
int Port = 10000;
//OnGUI方法,所有GUI的绘制都需要在这个方法中实现
void OnGUI(){
//Network.peerType是端类型的状态:
//即disconnected, connecting, server 或 client四种
switch(Network.peerType){
//禁止客户端连接运行, 服务器未初始化
case NetworkPeerType.Disconnected:
StartServer();
break;
//运行于服务器端
case NetworkPeerType.Server:
OnServer();
break;
//运行于客户端
case NetworkPeerType.Client:
break;
//正在尝试连接到服务器
case NetworkPeerType.Connecting:
break;
}
}
void StartServer(){
//当用户点击按钮的时候为true
if (GUILayout.Button("创建服务器")) {
//初始化本机服务器端口,第一个参数就是本机接收多少连接
NetworkConnectionError error = Network.InitializeServer(12,Port,false);
Debug.Log("错误日志"+error);
}
}
void OnServer(){
GUILayout.Label("服务端已经运行,等待客户端连接");
//Network.connections是所有连接的玩家, 数组[]
//取客户端连接数.
int length = Network.connections.Length;
//按数组下标输出每个客户端的IP,Port
for (int i=0; i<length; i++)
{
GUILayout.Label("客户端"+i);
GUILayout.Label("客户端ip"+Network.connections[i].ipAddress);
GUILayout.Label("客户端端口"+Network.connections[i].port);
}
//当用户点击按钮的时候为true
if (GUILayout.Button("断开服务器")){
Network.Disconnect();
}
}
/* 系统提供的方法,该方法只执行一次 */
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
3,把这个代码拖拽到摄像机上。
4,运行程序 File---->Build settings---->Build And Run,选择Web Player
5,服务器端建立完毕,保持服务器端的运行状态。
6,这里可以再新建立个项目写客户端。嫌麻烦也可以像我这样,先把摄像机上的服务器脚本删掉,再把下面的客户端脚本拖拽上去。
客户端代码如下:
[csharp] view
plaincopy
using UnityEngine;
using System.Collections;
public class client : MonoBehaviour {
//要连接的服务器地址
string IP = "127.0.0.1";
//要连接的端口
int Port = 10000;
void OnGUI(){
//端类型的状态
switch(Network.peerType){
//禁止客户端连接运行, 服务器未初始化
case NetworkPeerType.Disconnected:
StartConnect();
break;
//运行于服务器端
case NetworkPeerType.Server:
break;
//运行于客户端
case NetworkPeerType.Client:
break;
//正在尝试连接到服务器
case NetworkPeerType.Connecting:
break;
}
}
void StartConnect(){
if (GUILayout.Button("连接服务器")){
NetworkConnectionError error = Network.Connect(IP,Port);
Debug.Log("连接状态"+error);
}
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
7,运行程序 File---->Build settings---->Build And Run,选择Web Player
8,结果如下,可以通信。
![](http://my.csdn.net/uploads/201208/22/1345628610_2945.jpg)
附注: 本文大部分代码出自《Unity 3D 游戏开发》,作者宣雨松。
在上一个例子基础上,我们构建一个聊天室程序。
1,首先建立一个新项目,文件夹名为chat。
2,给接收请求的脚本所绑定的对象,添加网络视图组件(听起来有点绕口)。我们的服务器脚本是绑定在主摄像机上的,所以点击主摄像机,在菜单上选择component-->miscellaneous-->Net work View。
3,服务器端程序:(C# 脚本)
[csharp] view
plaincopy
using UnityEngine;
using System.Collections;
public class server : MonoBehaviour {
int Port = 10100;
string Message = "";
//声明一个二维向量
Vector2 Sc;
//OnGUI方法,所有GUI的绘制都需要在这个方法中实现
void OnGUI(){
//Network.peerType是端类型的状态:
//即disconnected, connecting, server 或 client四种
switch(Network.peerType){
//禁止客户端连接运行, 服务器未初始化
case NetworkPeerType.Disconnected:
StartServer();
break;
//运行于服务器端
case NetworkPeerType.Server:
OnServer();
break;
//运行于客户端
case NetworkPeerType.Client:
break;
//正在尝试连接到服务器
case NetworkPeerType.Connecting:
break;
}
}
void StartServer(){
//当用户点击按钮的时候为true
if (GUILayout.Button("创建服务器")) {
//初始化本机服务器端口,第一个参数就是本机接收多少连接
NetworkConnectionError error = Network.InitializeServer(12,Port,false);
//连接状态
switch(error){
case NetworkConnectionError.NoError:
break;
default:
Debug.Log("服务端错误"+error);
break;
}
}
}
void OnServer(){
GUILayout.Label("服务端已经运行,等待客户端连接");
//Network.connections是所有连接的玩家, 数组[]
//取客户端连接数.
int length = Network.connections.Length;
//按数组下标输出每个客户端的IP,Port
for (int i=0; i<length; i++)
{
GUILayout.Label("客户端"+i);
GUILayout.Label("客户端ip"+Network.connections[i].ipAddress);
GUILayout.Label("客户端端口"+Network.connections[i].port);
GUILayout.Label("-------------------------------");
}
//当用户点击按钮的时候为true
if (GUILayout.Button("断开服务器")){
Network.Disconnect();
}
//创建开始滚动视图
Sc = GUILayout.BeginScrollView(Sc,GUILayout.Width(280),GUILayout.Height(400));
//绘制纹理, 显示内容
GUILayout.Box(Message);
//结束滚动视图, 注意, 与开始滚动视图成对出现
GUILayout.EndScrollView();
}
//接收请求的方法. 注意要在上面添加[RPC]
[RPC]
void ReciveMessage(string msg, NetworkMessageInfo info){
//刚从网络接收的数据的相关信息,会被保存到NetworkMessageInfo这个结构中
Message = "发送端"+info.sender +"消息"+msg;
//+"时间"+info.timestamp +"网络视图"+info.networkView
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
4,把脚本拖拽到主摄像机上,绑定。
5,运行程序 File---->Build settings---->Build And Run,选择Web Player
6,服务器端建立完毕,保持服务器端的运行状态。
7,简单起见,我们这里不新建项目。先把摄像机上的服务器脚本删掉,再把下面的客户端脚本拖拽上去。
客户端代码:
[csharp] view
plaincopy
using UnityEngine;
using System.Collections;
public class client : MonoBehaviour {
//要连接的服务器地址
string IP = "127.0.0.1";
//要连接的端口
int Port = 10100;
//聊天信息
string Message = "";
//声明一个二维向量
Vector2 Sc;
void OnGUI(){
//端类型的状态
switch(Network.peerType){
//禁止客户端连接运行, 服务器未初始化
case NetworkPeerType.Disconnected:
StartConnect();
break;
//运行于服务器端
case NetworkPeerType.Server:
break;
//运行于客户端
case NetworkPeerType.Client:
OnClient();
break;
//正在尝试连接到服务器
case NetworkPeerType.Connecting:
break;
}
}
void StartConnect(){
if (GUILayout.Button("连接服务器")){
NetworkConnectionError error = Network.Connect(IP,Port);
//连接状态
switch(error){
case NetworkConnectionError.NoError:
break;
default:
Debug.Log("客户端错误"+error);
break;
}
}
}
void OnClient(){
//创建开始滚动视图
Sc = GUILayout.BeginScrollView(Sc,GUILayout.Width(280),GUILayout.Height(400));
//绘制纹理, 显示内容
GUILayout.Box(Message);
//文本框
Message = GUILayout.TextArea(Message);
if (GUILayout.Button("发送")){
//发送给接收的函数, 模式为全部, 参数为信息
networkView.RPC("ReciveMessage", RPCMode.All, Message);
}
//结束滚动视图, 注意, 与开始滚动视图成对出现
GUILayout.EndScrollView();
}
//接收请求的方法. 注意要在上面添加[RPC]
[RPC]
void ReciveMessage(string msg, NetworkMessageInfo info){
//刚从网络接收的数据的相关信息,会被保存到NetworkMessageInfo这个结构中
Message = "发送端"+info.sender +"消息"+msg;
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
服务端运行状态:
![](http://my.csdn.net/uploads/201208/24/1345789506_2844.jpg)
客户端运行状态:
![](http://my.csdn.net/uploads/201208/24/1345789523_5837.jpg)
附注: 本文大部分代码出自《Unity 3D 游戏开发》,作者宣雨松。
在上两篇的基础上,这次我们要做物体同步。使两个物体在两个机器上显示同样的移动效果。这里,使用W、S、A、D四个键位实现前后左右的移动。
注意:如果您复制粘贴代码,千万注意文件编码!最好为ANSI,否则会报出各种各样奇怪的错误!
步骤:
1,首先建立一个新工程。
2,添加两个cube,分别重命名为Cube01,和Cube02。这两个cube就是我们要同步的对象。
3,添加一个平行光源,Directional light。否则你在漆黑的场景中,真的很难找到你的cube。
4,调整摄像机、cube、Directional light 的相对位置,使他们在视野内,可以看着它们运行。
![](http://my.csdn.net/uploads/201208/29/1346205603_9891.jpg)
5,create一个C#脚本cube_move,如下:
[csharp] view
plaincopy
using UnityEngine;
using System.Collections;
public class cube_move : MonoBehaviour {
string IP = "192.168.1.6";
int Port = 10100;
int moveSpeed = 16;
GameObject cube01 = null;
GameObject cube02 = null;
GameObject myself = null;
GameObject another = null;
// Use this for initialization
void Start () {
cube01 = GameObject.Find("Cube01");
cube02 = GameObject.Find("Cube02");
}
//OnGUI方法,所有GUI的绘制都需要在这个方法中实现
void OnGUI(){
//Network.peerType是端类型的状态:
//即disconnected, connecting, server 或 client四种
switch(Network.peerType){
//禁止客户端连接运行, 服务器未初始化
case NetworkPeerType.Disconnected:
StartServer();
break;
//运行于服务器端
case NetworkPeerType.Server:
OnServer();
break;
//运行于客户端
case NetworkPeerType.Client:
break;
//正在尝试连接到服务器
case NetworkPeerType.Connecting:
break;
}
}
void StartServer(){
GUILayout.Label("同步测试:");
//当用户点击按钮的时候为true
if (GUILayout.Button("创建服务器")) {
//初始化本机服务器端口,第一个参数就是本机接收多少连接
NetworkConnectionError error = Network.InitializeServer(12,Port,false);
//连接状态
switch(error){
case NetworkConnectionError.NoError:
break;
default:
Debug.Log("服务端错误"+error);
break;
}
}
if (GUILayout.Button("连接服务器")){
NetworkConnectionError error = Network.Connect(IP,Port);
//连接状态
switch(error){
case NetworkConnectionError.NoError:
break;
default:
Debug.Log("客户端错误"+error);
break;
}
}
}
void OnServer(){
GUILayout.Label("服务端已经运行,等待客户端连接");
//Network.connections是所有连接的玩家, 数组[]
//取客户端连接数.
int length = Network.connections.Length;
//按数组下标输出每个客户端的IP,Port
for (int i=0; i<length; i++)
{
GUILayout.Label("客户端"+i);
GUILayout.Label("客户端ip"+Network.connections[i].ipAddress);
GUILayout.Label("客户端端口"+Network.connections[i].port);
GUILayout.Label("-------------------------------");
}
//当用户点击按钮的时候为true
if (GUILayout.Button("断开服务器")){
Network.Disconnect();
}
}
//接收请求的方法. 注意要在上面添加[RPC]
[RPC]
void ProcessMove(string msg, NetworkMessageInfo info){
//刚从网络接收的数据的相关信息,会被保存到NetworkMessageInfo这个结构中
string sender = info.sender.ToString();
//看脚本运行在什么状态下
NetworkPeerType status = Network.peerType;
if (status == NetworkPeerType.Server)
{
myself = cube01; //假如运行在server状态下, 那么自己就是cube1
another = cube02;
}
else
{
myself = cube02; //假如运行在client状态下, 那么自己就是cube2
another = cube01;
}
//假如是自己发送的信息
if (sender == "-1")
{
if (msg == "W")
{
myself.transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed);
}
if (msg == "S")
{
myself.transform.Translate(Vector3.back * Time.deltaTime * moveSpeed);
}
if (msg == "A")
{
myself.transform.Translate(Vector3.left * Time.deltaTime * moveSpeed);
}
if (msg == "D")
{
myself.transform.Translate(Vector3.right * Time.deltaTime * moveSpeed);
}
}
//假如是别人发送的信息
else
{
if (msg == "W")
{
another.transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed);
}
if (msg == "S")
{
another.transform.Translate(Vector3.back * Time.deltaTime * moveSpeed);
}
if (msg == "A")
{
another.transform.Translate(Vector3.left * Time.deltaTime * moveSpeed);
}
if (msg == "D")
{
another.transform.Translate(Vector3.right * Time.deltaTime * moveSpeed);
}
}
}
// Update is called once per frame
void Update ()
{
//前移
if (Input.GetKeyDown(KeyCode.W))
{
Debug.Log("wwwwwwwww|");
networkView.RPC("ProcessMove", RPCMode.All, "W");
}
if (Input.GetKey(KeyCode.W))
{
networkView.RPC("ProcessMove", RPCMode.All, "W");
}
//后退
if (Input.GetKeyDown(KeyCode.S))
{
Debug.Log("sssssssss!");
networkView.RPC("ProcessMove", RPCMode.All, "S");
}
if (Input.GetKey(KeyCode.S))
{
networkView.RPC("ProcessMove", RPCMode.All, "S");
}
//向左移动
if (Input.GetKeyDown(KeyCode.A))
{
networkView.RPC("ProcessMove", RPCMode.All, "A");
}
if (Input.GetKey(KeyCode.A))
{
networkView.RPC("ProcessMove", RPCMode.All, "A");
}
//向右移动
if (Input.GetKeyDown(KeyCode.D))
{
networkView.RPC("ProcessMove", RPCMode.All, "D");
}
if (Input.GetKey(KeyCode.D))
{
networkView.RPC("ProcessMove", RPCMode.All, "D");
}
}
}
6,给接收请求的脚本所绑定的对象,添加网络视图组件(听起来有点绕口)。我们的这个脚本是绑定在主摄像机上的,所以点击主摄像机,在菜单上选择component-->miscellaneous-->Net work View。
7,绑定脚本,把脚本拖拽到摄像机上。
8,编译发布成web的:File---->Build settings---->Build And Run,选择Web Player
9,拷贝编译出来的文件夹,到另一台机器上。
10,分别在两台机器上运行服务器端和客户端。
你会看到同步的效果了。
附注:在一台机器上,不太容易看到效果。因为浏览器失焦后,似乎无法刷新画面,导致同步效果不明显。
Ps. 这个代码只是unity自带的网络组件示例。在实际应用中,不能直接应用。需要用unity底层支持的C#语言,重新写一套通信框架。我已经写了一套,但是没有时间发博客。改天发出来。
相关文章推荐
- android-async-http AsyncHttpClient介绍
- 使用Volley缓存图片时,缓存无效的原因。
- 网络相关面试题1
- tcp相关知识
- https原理及tomcat配置https方法
- android http://dl-ssl.google.com/android/eclipse/无法访问
- [NOI2007][BZOJ1491] 社交网络
- ios如何获取手机的网络状态和运营商名称
- 社交网络的神营销让“大圣”顺利归来
- Linux 上的基础网络设备详解
- TCP/IP-----------tcp粘包问题
- jsp页面出现Multiple annotations found at this line: - The superclass "javax.servlet.http.HttpServlet" w
- 请求http服务
- RedHat 系统使用yum网络源
- 网络编程释疑之:同步,异步,阻塞,非阻塞
- android https(SSL) 双向验证详解
- Android HTTPS(SSL)双向验证服务器验证的一种思路
- 从TCP协议的原理来谈谈rst复位攻击
- Unity5.1 新的网络引擎UNET(九) UNET 官方推荐视频教程
- (网络层)IP 协议首部格式与其配套使用的四个协议(ARP,RARP,ICMP,IGMP)