大乱斗游戏效果

 运行服务器程序并启动两个游戏客户端:
  当玩家客户端连上服务器时服务器窗口打印玩家加入信息
  当某一玩家进行移动时服务器打印玩家移动信息并发送给其他所有客户端程序
  客户端接收到其他玩家的移动信息时更新其信息

  当然这是在同一台电脑上运行两个服务端,但只需要用手机开个热点,便可以将两个游戏场景运行在不同的计算机上。如果将图中控制台程序部署到服务器上,即可实现全世界所有玩家的加入!

(1)引言

【多人网络游戏的数据传输】
 当我们玩LOL或者王者荣耀这类多人网络游戏时,我们往往有一个可以自己操纵的角色,诸如疾风剑豪又或安琪拉。我们可以控制自己的角色进行移动和攻击,也可以控制角色和场景中的其他游戏对象进行交互。那么从游戏开发的角度,你是否想过这样的多人游戏是怎样开发的呢,具体来说它从网络层面的数据传输是怎样的呢?
【客户端与服务端交换的数据】
 我们很容易可以知道的是,每个游戏客户端都与服务端相连,并彼此互相传输数据。以前小时候,我总是想当然的以为:玩游戏只需要将自己屏幕上的画面传给服务器,然后服务器把其他玩家屏幕上的画面传给我,就拼接成了游戏中的完整画面。在学习了游戏引擎之后,我才发现游戏传输的数据不是“画面”。
【玩家客户端即游戏引擎】
 比如在玩英雄联盟时,我们诺手对线卡米尔,当它使用W时我们可以看到她的技能释放过程,这画面数据肯定不是由服务器一帧帧发送过来的,而是你的客户端实现的。我的意思是:每个玩家的客户端就是游戏引擎,不仅负责操控自己的角色,也要操控其他的角色!看到这里可能有人由疑问了,那我也控制不了对面的卡米尔呀!但是我的意思是:从游戏引擎角度,其操控所有角色的移动、攻击、动画切换,动画渲染等过程。

【总结】
 在设计游戏客户端时,游戏客户端应实现场景中所有的对象(不管是否由自己操控)。当玩家操控角色进行交互时,客户端应该通过服务器告诉其他客户端:“我做了什么”。比如当我们使用卡米尔的W技能”战术横扫“时,我们应该告诉服务器我们正在使用W技能”战术横扫“,服务器会告诉其他所有客户端”卡米尔“这个角色正在使用W技能”战术横扫“,由其他客户端对场景中的”卡米尔“角色调用W技能”战术横扫“相关方法和动画。
 具体到图片中的游戏中,当其他角色移动时,发送给客户端的不应该是其他角色的坐标,而应该是告诉客户端”你应该操控这个角色往哪走“。这就是为什么有时我们玩LOL网断了还能看到自己或敌人玩家还在走,应该服务器告诉你客户端的不是玩家现在的位置,而是玩家鼠标点击的目标位置,再由你客户端实现一步步走过去。

(2)代码

 话不多说,直接上代码,有兴趣的可以试试,在不同计算机上互联哦!

服务端程序

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
public class ClientState
{public Socket socket;public byte[] readBuff = new byte[1024];
}class MainClass
{//监听Socket listenfdprivate static Socket listenfd;//连接服务端的客户端列表(使用字典是为了方便查找)private static Dictionary<Socket, ClientState> clients =new Dictionary<Socket, ClientState>();public static void Main(string[] args){//创建监听Socketlistenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//注册BindIPAddress ipAdr = IPAddress.Parse("192.168.43.203");IPEndPoint ipEp = new IPEndPoint(ipAdr, 8888);listenfd.Bind(ipEp);//设置并开始监听listenfd.Listen(0);Console.WriteLine("[服务器]启动成功");//传入回调函数的状态信息是:监听Socketlistenfd.BeginAccept(AcceptCallback, listenfd);Console.ReadLine();}public static void AcceptCallback(IAsyncResult ar){try{//根据传入的监听Socket调用End方法,返回:连接客户端的SocketConsole.WriteLine("[服务端]Accept");Socket listenfd = (Socket)ar.AsyncState;Socket clientfd = listenfd.EndAccept(ar);//根据连接客户端的Socket创建对应的客户端信息对象ClientState state = new ClientState();state.socket = clientfd;//将客户端信息对象加入服务端客户列表(Socket,state)clients.Add(clientfd, state);//对连接的客户端进行监听(接收数组使用对应客户端的读缓冲区readBuff)clientfd.BeginReceive(state.readBuff, 0, 1024, 0,ReceiveCallback, state);//服务器继续监听:客户端连接listenfd.BeginAccept(AcceptCallback, listenfd);}catch (SocketException e){Console.WriteLine("Socket Accept fail" + e.ToString());}}public static void ReceiveCallback(IAsyncResult ar){try{//回调函数的状态参数为客户端信息ClientState state = (ClientState)ar.AsyncState;Socket clientfd = state.socket;//接收客户端发送的信息int count = clientfd.EndReceive(ar);//客户端发送信息为空则移除客户端连接并且return Receive (关闭对其信息的接收)if (count == 0){clientfd.Close();clients.Remove(clientfd);Console.WriteLine("Socket Close");return;}//发送原文回复客户端string receStr = System.Text.Encoding.Default.GetString(state.readBuff, 0, count);Console.WriteLine(receStr);byte[] sendBytes = System.Text.Encoding.Default.GetBytes(receStr);foreach (ClientState s in clients.Values){s.socket.Send(sendBytes);}//继续接收客户端的信息clientfd.BeginReceive(state.readBuff, 0, 1024, 0, ReceiveCallback, state);}catch (SocketException e){Console.WriteLine("Socket Receive fail:" + e.ToString());}}
}

客户端程序

网络模块程序

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using UnityEngine;public static class NetManager
{private static Socket socket;private static byte[] readBuff = new byte[1024];public delegate void MsgListener(string str);private static Dictionary<string, MsgListener> listeners = new Dictionary<string, MsgListener>();private static List<string> msgList = new List<string>();public static void AddListener(string msgName,MsgListener listener){listeners[msgName] = listener;}public static string GetDesc(){if (socket == null) return "";if (!socket.Connected) return "";return socket.LocalEndPoint.ToString();}public static void Connect(string ip,int port){socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);try{socket.Connect(ip,port);}catch (SocketException e){Console.WriteLine("Connect fail"+e.ToString());}socket.BeginReceive(readBuff, 0, 1024, 0,ReceiveCallback, socket);}private static void ReceiveCallback(IAsyncResult ar){try{Socket socket = (Socket)ar.AsyncState;int count = socket.EndReceive(ar);string recvStr = System.Text.Encoding.Default.GetString(readBuff, 0, count);msgList.Add(recvStr);socket.BeginReceive(readBuff, 0, 1024, 0, ReceiveCallback, socket);}catch (SocketException e){Debug.Log("Socket Receive fail" + e.ToString());}}public static void Send(String sendStr){if(socket==null)return;if(!socket.Connected)return;byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);socket.Send(sendBytes);}public static void Update(){if (msgList.Count <= 0)return;String msgStr = msgList[0];msgList.RemoveAt(0);String[] split = msgStr.Split('|');String msgName = split[0];String msgArgs = split[1];if (listeners.ContainsKey(msgName)){listeners[msgName](msgArgs);}}
}

加载网络模块程序

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Main : MonoBehaviour
{public GameObject humanPrefab;public BaseHuman myHuman;public Dictionary<string, BaseHuman> otherHuamns=new Dictionary<string, BaseHuman>();void Start(){NetManager.AddListener("Enter",OnEnter);NetManager.AddListener("Move",OnMove);NetManager.AddListener("Leave",OnEnter);NetManager.Connect("192.168.43.203",8888);GameObject obj = (GameObject)Instantiate(humanPrefab);float x = Random.Range(2, 10);float z = Random.Range(2, 10);obj.transform.position = new Vector3(x, 0, z);myHuman = obj.AddComponent<CtrlHuman>();myHuman.desc = NetManager.GetDesc();Vector3 pos = myHuman.transform.position;Vector3 eul = myHuman.transform.eulerAngles;string sendStr = "Enter|";sendStr += myHuman.desc+",";sendStr += pos.x + ",";sendStr += pos.y + ",";sendStr += pos.z + ",";sendStr += eul.y;NetManager.Send(sendStr);}void OnEnter(string msgArgs){Debug.Log("OnEntet:"+msgArgs);string[] split = msgArgs.Split(',');string desc = split[0];float x = float.Parse(split[1]);float y = float.Parse(split[2]);float z = float.Parse(split[3]);float eulY = float.Parse(split[4]);if(desc == NetManager.GetDesc())return;GameObject obj = (GameObject)Instantiate(humanPrefab);obj.transform.position = new Vector3(x, y, z);obj.transform.eulerAngles = new Vector3(0, eulY, 0);BaseHuman h = obj.AddComponent<SyncHuman>();h.desc = desc;otherHuamns.Add(desc,h);}void OnMove(string msgArgs){Debug.Log("OnMove:"+msgArgs);string[] split = msgArgs.Split(',');string desc = split[0];float x = float.Parse(split[1]);float y = float.Parse(split[2]);float z = float.Parse(split[3]);if(desc == NetManager.GetDesc())return;otherHuamns[desc].MoveTo(new Vector3(x,y,z));}void OnLeave(string msgArgs){Debug.Log("OnLeave:"+msgArgs);}void Update(){NetManager.Update();}
}

玩家角色基类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class BaseHuman : MonoBehaviour
{protected bool isMoving = false;private Vector3 targetPosition;public float speed = 1.2f;private Animator animator;public string desc = "";public void MoveTo(Vector3 pos){targetPosition = pos;isMoving = true;animator.SetBool("isMoving",true);}public void MoveUpdate(){if (!isMoving)return;Vector3 pos = transform.position;transform.position = Vector3.MoveTowards(pos, targetPosition, speed * Time.deltaTime);transform.LookAt(targetPosition);if (Vector3.Distance(transform.position, targetPosition) < 0.05f){isMoving = false;animator.SetBool("isMoving", false);}}protected void Start(){animator = GetComponent<Animator>();}protected void Update(){MoveUpdate();}
}

玩家操控角色类

using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using UnityEngine;public class CtrlHuman : BaseHuman
{// Start is called before the first frame updatenew void Start(){base.Start();}// Update is called once per framenew void Update(){base.Update();if (Input.GetMouseButtonDown(0)){Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hit;Physics.Raycast(ray, out hit);if (hit.collider.tag == "Terrain"){MoveTo(hit.point);NetManager.Send("Move|" + NetManager.GetDesc()+ "," + hit.point.x+ "," + hit.point.y+ "," + hit.point.z);}}}
}

非玩家操控角色类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SyncHuman : BaseHuman
{// Start is called before the first frame updatenew void Start(){base.Start();}// Update is called once per framenew void Update(){base.Update();}
}

(3)使用

 在加载网络模块程序Main类的Start方法中调用的connect方法,第一个字符串参数是服务端的IP地址。可以使用手机开热点,然后电脑连接同一个手机热点,通过在任意一台电脑上使用命令行查找IP地址查找IP地址,随便选了一个,写到Connect方法中,并修改服务端程序的Bind方法的IP参数。
 我会将游戏文件打包上传,有兴趣可以下载使用。

Unity3D网络游戏0.6相关推荐

  1. Unity3D网络游戏0.1

    一.网络游戏的架构 (1)网络游戏:   分为客户端和服务端两个部分,客户端程序运行在用户的电脑或手机上,服务端程序运行在游戏运营商的服务器上.   以下是一些典型的游戏客户端.            ...

  2. Unity3D网络游戏0.2

    一.异步委托 C#委托 异步委托[理解] 异步委托[原理] 异步委托[实验] 注意:Action.BeginInvoke方法是在.NET Framework中引入的,而在.NET Core和.NET ...

  3. Unity3D网络游戏0.3

    异步客户端  上文中的同步服务端程序会一直阻塞等待某个客户端的数据,导致它同一时间只能处理一个客户端的请求,无法响应其他客户端.使用异步方法,可以让服务端同时处理多个客户端的数据,及时响应. (1)管 ...

  4. 【实战】Unity3d实战之Unity3d网络游戏实战篇(11):消息分发

    Unity3d实战之Unity3d网络游戏实战篇(11):消息分发 学习书籍<Unity3d网络游戏实战> 罗培羽著 机械工业出版社 本文是作者在学习过程中遇到的认为值得记录的点,因此引用 ...

  5. 《Unity3D网络游戏实战》第7章

    <Unity3D网络游戏实战>第7章 服务端架构 总体架构 模块划分 游戏流程 Json编码解码 添加协议文件 引用System.web.Extensions 修改MsgBase类 测试 ...

  6. 【实战】Unity3d实战之Unity3d网络游戏实战篇(9):协议

    Unity3d实战之Unity3d网络游戏实战篇(9):协议 学习书籍<Unity3d网络游戏实战> 罗培羽著 机械工业出版社 本文是作者在学习过程中遇到的认为值得记录的点,因此引用的代码 ...

  7. Unity3D 网络游戏框架(一、网络基础)

    1.套接字(Socket)         网络上两个程序通过一个双向的通信连接实现数据交换,这个连接的一端称为一个Socket.一个Socket包含了进行网络通信必须的五种信息:连接使用的协议.本地 ...

  8. Unity3D网络游戏开发——开始网络编程:Echo

    Unity3D网络游戏开发--开始网络编程:Echo(客户端部分) 什么是Echo程序 Echo程序是网络编程中最基础的案例.建立网络连接后,客户端向服务端发送一行文本,服务端收到后将文本送回客户端. ...

  9. unity3d 网络游戏任务系统设计考良(待续)

    unity3d 网络游戏任务系统设计考良   2013-08-23 10:18:49|  分类: unity3d |  标签:unity3d  |举报|字号 订阅 首先,网络游戏是一个大的概念,本文所 ...

最新文章

  1. 【安全漏洞】从补丁追溯漏洞触发路径
  2. new file https 找不到路径_Python3用pathlib模块替代os.path进行文件路径的操作
  3. [Socket网络编程]一个封锁操作被对 WSACancelBlockingCall 的调用中断。
  4. c++ ftp服务端_FTP客户端软件介绍及使用
  5. go 错误处理总结
  6. 深度学习笔记(22) Padding
  7. 红旗linux6.0安装不了,在红旗linux6.0中安装vmware tools遇到的问题
  8. No architectures to compile for VALID_错误解决法案
  9. Linux基础-网络配置
  10. 关于IP地址定位、IP查询和IP地址库 你想了解的历史都在这里
  11. 推荐几款珍藏多年的插件,好用到爆,进来瞅瞅有没有
  12. 树莓派4b自带wifi_树莓派4B(ubuntu)无线网络配置
  13. 【操作系统】CPU是如何执行程序的?
  14. android 应用引导用户去应用市场评论
  15. HashMap的四种同步方式
  16. 天津大学计算机学院博士招生目录,天津大学计算机科学与技术学院考博招生人数和专业.pdf...
  17. 深入剖析Spring架构与设计原理(一)
  18. 【Learncpp中文翻译版】【1.9、1.10、1.11】
  19. 如何快速的将EXCEL表格数据拆分成多个文件
  20. 记录一次上网正常但是【登录客户端应用网络异常问题】

热门文章

  1. 科目二 直角转弯 流程记录 LTS
  2. FANUC机械手应用贴标机实例
  3. 大学面试三分钟自我介绍七篇
  4. C#读写注册表及 WOW6432Node 问题
  5. 【沧海拾昧】Proteus8仿真stm32:ADC转换程序
  6. 刺激战场瞬灭助手 2019稳定版
  7. 在visio中将图片变成黑白,将图片对象进行颜色转换 彩色图转灰度图
  8. ae输出quicktime设置_AE输出设置详解
  9. html粗圆点,HTML
  10. 微信小程序 — 长列表组件 recycle-view 详细教学