《Unity3D网络游戏实战》学习与实践--制作一款大乱斗游戏

角色类

基类Base Human是基础的角色类,它处理“操控角色”和“同步角色”的一些共有功能;CtrlHuman类代表“操控角色”​,它在BaseHuman类的基础上处理鼠标操控功能;SyncHuman类是“同步角色”类,它也继承自BaseHuman,并处理网络同步(如果有必要)​。

BaseHuman

        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);}//移动Updatepublic void MoveUpdate(){if(isMoving == false) {return;}Vector3 pos = transform.position;transform.position = Vector3.MoveTowards(pos, targetPosition, speed*Time.deltaTime);transform.LookAt(targetPosition);if(Vector3.Distance(pos, targetPosition) < 0.05f){isMoving = false;animator.SetBool("isMoving", false);}}// Use this for initializationprotected void Start () {animator = GetComponent<Animator>();}// Update is called once per frameprotected void Update () {MoveUpdate();}}

CtrlHuman

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class CtrlHuman : BaseHuman
{new 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);}}}
}

如何使用网络模块

在实际的网络游戏开发中,网络模块往往是作为一个底层模块用的,它应该和具体的游戏逻辑分开,而不应该把处理逻辑的代码写到 ReceiveCallback 里面去。因为ReceiveCallback应当只处理网络数据,不应该去处理游戏功能

一个可行的做法是,给网络管理类添加回调方法,当收到某种消息时就自动调用某个函数,这样便能够将游戏逻辑和底层模块分开。制作网络管理类前,需要先了解委托、协议和消息队列这三个概念。

通信协议

通信协议是通信双方对数据传送控制的一种约定,通信双方必须共同遵守,方能“知道对方在说什么”和“让对方听懂我的话”​。

使用一种最简单的字符串协议来实现。协议格式如下所示,消息名和消息体用“|”隔开,消息体中各个参数用“, ”隔开。

消息名|参数1, 参数2, 参数3, ...

Move|127.0.0.1:1234, 10, 0, 8,

处理数据:

        string str = "Move|127.0.0.1:1234, 10, 0,8, ";string[] args = str.Split('|');string msgName = args[0]; //协议名:Movestring msgBody = args[1]; //协议体:127.0.0.1:1234, 10, 0,8,string[] bodyArgs = msgBody.Split(', ');string desc = bodyArgs [0];               //玩家描述:127.0.0.1:1234float x = float.Parse(bodyArgs [1]);     //x坐标:10float y = float.Parse(bodyArgs [2]);     //y坐标:0float z = float.Parse(bodyArgs [3]);     //z坐标:8

消息队列

多线程消息处理虽然效率较高,但非主线程不能设置Unity3D组件,而且容易造成各种莫名其妙的混乱。由于单线程消息处理足以满足游戏客户端的需要,因此大部分游戏会使用消息队列让主线程去处理异步Socket接收到的消息。

C#的异步通信由线程池实现,不同的BeginReceive不一定在同一线程中执行。创建一个消息列表,每当收到消息便在列表末端添加数据,这个列表由主线程读取,它可以作为主线程和异步接收线程之间的桥梁。由于MonoBehaviour的Update方法在主线程中执行,可让Update方法每次从消息列表中读取几条信息并处理,处理后便在消息列表中删除它们

NetManager类

网络模块中最核心的地方是一个称为NetManager的静态类,这个类对外提供了三个最主要的接口。

  • Connect方法,调用后发起连接;
  • AddListener方法,消息监听。其他模块可以通过AddListener设置某个消息名对应的处理方法,当网络模块接收到这类消息时,就会回调处理方法;
  • Send方法,发送消息给服务端。

无论内部实现有多么复杂,网络模块对外的接口只有图片展示的这几个:

对内部而言,NetManager使用了异步Socket接收消息,每次接收到一条消息后,NetManager会把消息存入消息队列中​。NetManager有一个供外部调用的Update方法,每当调用它时就会处理消息队列里的第一条消息,然后根据协议名将消息分发给对应的回调函数

    using System.Collections;using System.Collections.Generic;using UnityEngine;using System.Net.Sockets;using UnityEngine.UI;using System;public static class NetManager {//定义套接字static Socket socket;//接收缓冲区static byte[] readBuff = new byte[1024];//委托类型public delegate void MsgListener(String str);//监听列表private static Dictionary<string, MsgListener> listeners =new Dictionary<string, MsgListener>();//消息列表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){//Socketsocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);//Connect(用同步方式简化代码)socket.Connect(ip, port);//BeginReceivesocket.BeginReceive( readBuff, 0, 1024, 0,ReceiveCallback, socket);}//Receive回调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 ex){Debug.Log("Socket Receive fail" + ex.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);}//Updatepublic 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);}}}

漏洞

上述代码没有处理粘包分包、线程冲突等问题

参考书籍:《Unity3D网络游戏实战(第2版)》 (豆瓣) (douban.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/392157.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决电脑缺少.NET组件?手把手教你轻松解决

在日常使用电脑的过程中&#xff0c;很多用户可能会遇到“电脑缺少.NET组件”的提示&#xff0c;这可能导致某些应用程序无法正常运行或安装。那么&#xff0c;.NET组件到底是什么&#xff1f;为何它如此重要&#xff1f;本文将为您详细解答这些问题&#xff0c;并提供有效的解…

[ACM MM 2024] Wave-Mamba:超高清暗光图像增强的小波状态空间模型

Wave-Mamba: Wavelet State Space Model for Ultra-High-Definition Low-Light Image Enhancement (arxiv.org) Wave-Mamba是一种用于增强超高清低光照图像的新模型&#xff0c;它引入了低频状态空间块和高频增强块&#xff0c;并取得了领先水平的性能。该模型即将开源&#x…

用Python插入表格到PowerPoint演示文稿

有效的信息传达是演示文稿中的重点&#xff0c;而PowerPoint演示文稿作为最广泛使用的演示工具之一&#xff0c;提供了丰富的功能来帮助演讲者实现这一目标。其中&#xff0c;在演示文稿中插入表格可以帮助观众更直观地理解数据和比较信息。通过使用Python这样的强大编程语言&a…

【STL】 vector的底层实现

1.vector的模拟代码完整实现&#xff08;后面会拆分开一个一个细讲&#xff09; #pragma once #include<assert.h>// 抓重点namespace bit {/*template<class T>class vector{public:typedef T* iterator;private:T* _a;size_t _size;size_t _capacity;};*/templa…

Python(模块)

模块编写完成就可以被其他模块进行调用并使用被调用模块的功能。 import导入方式的语法结构&#xff1a; import模块名称【as别名】 from……import导入方式的语法结构&#xff1a; from模块名称&#xff0c;import变量/函数/类/*&#xff08;*是通配符&#xff09; impor…

Milvus 向量数据库进阶系列丨构建 RAG 多租户/多用户系统 (上)

本系列文章介绍 在和社区小伙伴们交流的过程中&#xff0c;我们发现大家最关心的问题从来不是某个具体的功能如何使用&#xff0c;而是面对一个具体的实战场景时&#xff0c;如何选择合适的向量数据库解决方案或最优的功能组合。在 “Milvus 向量数据库进阶” 这个系列文章中&…

【生成式AI-二-强大的AI下我们可以做什么】

强大的AI下我们可以做什么 人工智能的厉害之处我们可以作什么评估模型好坏的难度prompt engineering微调fine tune 人工智能的厉害之处 人工智能并不是忽然就爆火的&#xff0c;事实上&#xff0c;很久以前就已经有深度学习、机器学习这些概念了&#xff0c;那现在的人工智能和…

Java之类和对象

目录 static关键字 1修饰属性 2修饰方法 final 构造方法 基本语法 this关键字 代码块 定义 普通代码块 构造代码块 静态代码块 匿名对象 toString 总结 static关键字 1修饰属性 Java的静态属性和类相关, 和具体的实例无关. 换句话说, 同一个类的不同实例共用同一个…

反转链表 II(LeetCode)

题目 给你单链表的头指针 和两个整数 和 &#xff0c;其中 。请你反转从位置 到位置 的链表节点&#xff0c;返回 反转后的链表 。 解题 class ListNode:def __init__(self, value0, nextNone):self.value valueself.next nextdef reverseBetween(head: ListNode, lef…

crm客户管理系统有哪些?盘点大家使用最广泛的15款

将对比的客户管理CRM系统包括&#xff1a;纷享销客、Zoho CRM、销售易、用友CRM、Salesforce、Microsoft Dynamics 365、销帮帮CRM、HubSpot、Oracle CRM、悟空CRM、神州云动CRM、红圈CRM、SAP CRM、Odoo、OroCRM。 一个合适的CRM系统可以极大地提高你的销售效率和客户满意度&a…

SpringMVC执行流程

1 流程对比 1.1 原生servlet开发流程 根据需求编写servlet程序在web.xml 中通过配置&#xff0c;指定servlet所能处理的请求&#xff0c;即建立servlet与请求路径间的映射在Servlet的service方法中对路径进行再判断&#xff0c;从而进行具体的逻辑处理servlet参数从request中…

数据结构-递归

用递归代替循环 假设工作中的你&#xff0c;需要写一个倒数程序。该程序接收一个数字&#xff0c;例如10&#xff0c;然后显示从10到0的数字。现在先暂停一下&#xff0c;选择一门编程语言来实现这个程序&#xff0c;做完以后&#xff0c;再往下阅读。或许你用了JavaScript&am…

数学建模--二分法

目录 二分法的基本原理 应用实例 求解方程根 查找有序数组中的元素 注意事项 Python代码示例 ​编辑 延伸 二分法在数学建模中的具体应用案例有哪些&#xff1f; 如何选择二分法的初始区间以确保收敛速度和精度&#xff1f; 在使用二分法求解方程时&#xff0c;如何…

排序算法2:直接选择排序与快速排序

目录 1.直接选择排序 1.1直接选择排序的优化 2.快速排序 2.1基准值的置位&#xff08;Hoare版&#xff09; 2.2挖坑法 2.3lomuto前后指针 前言 前面我们进入了排序算的讲解。今天我们将继续学习几种重要的排序思想&#xff0c;好&#xff0c;咱们三连上车开始今天的内容。…

ChatTTS文本转语音本地部署结合内网穿透实现远程使用生成AI音频

文章目录 前言1. 下载运行ChatTTS模型2. 安装Cpolar工具3. 实现公网访问4. 配置ChatTTS固定公网地址 前言 本篇文章主要介绍如何快速地在Windows系统电脑中本地部署ChatTTS开源文本转语音项目&#xff0c;并且我们还可以结合Cpolar内网穿透工具创建公网地址&#xff0c;随时随…

动态规划.

目录 &#xff08;一&#xff09;递归到动规的一般转化方法 &#xff08;二&#xff09;动规解题的一般思路 1. 将原问题分解为子问题 2. 确定状态 3. 确定一些初始状态&#xff08;边界状态&#xff09;的值 4. 确定状态转移方程 &#xff08;三&#xff09;能用动规解…

【网络】HTTP协议

目录 概述 URL 结构 urlencode&#xff08;URL编码&#xff09; urldecode&#xff08;URL解码&#xff09; 工具网址 HTTP请求 请求行 请求头 请求体 HTTP响应 状态行 响应头 响应体 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 概述 HTTP协议是应用层协议…

TCP 三次握手建立连接

一开始&#xff0c;客户端和服务端都处于 CLOSE 状态。先是服务端主动监听某个端口&#xff0c;处于 LISTEN 状态 1. 第一次握手 客户端会随机初始化序号&#xff08;client_isn&#xff09;&#xff0c;将此序号置于 TCP 首部的「序号」字段中&#xff0c;同时把 SYN 标志位置…

略读ArrayList源码

ArrayList是Java集合框架中的一部分&#xff0c;底层是通过数组实现的&#xff0c;可以动态增长和缩减。 一、首先看成员变量 序列化ID定义。在Java中&#xff0c;如果一个类实现了Serializable接口&#xff0c;那么它的serialVersionUID就非常重要了。serialVersionUID用于确…

python 图片爬虫记录

感谢大家的点赞。再补充一点。 对于这个 url https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEqB5nighYsMZE7kexaVNJfxy3OkRutNEKatksw9u5f-ckHNROLzFyx2Uty3zYWNEaeOmzsljGr3eARiDWaM9DM8G2hPuPf8uZP0NO3kNUCnM2Cjb3ZKtLhJDBwqeR4ElpJ7ID5_wIHGQ/s200 这个url最…