频道栏目
首页 > 程序开发 > 移动开发 > 其他 > 正文
Unity3D游戏架构设计之对象管理【一】
2017-03-08 09:13:46         来源:wuming2016的博客  
收藏   我要投稿

1概述

先用一张简要的模块图来帮助大家了解游戏架构设计,了解对象管理在架构中所处的位置和他的作用是。实际的模块图会比本图的内容详细,并且对模块的分割有其他的方式,这张图只是为了帮助大家了解对象管理,做了很多精简。

\

要想讲清楚对象管理,还得从下层的“资源管理器”说起。

1.1资源管理

资源管理主要有两个重要的功能:资源加载和资源释放。

资源加载指的是把需要的资源(如prefab,材质,贴图等,从磁盘加载到内存中),供上层模块调用。这些资源通常以某种形式进行打包。Unity3D通常是用AssetBundle打包。举例来说,想在游戏中显示一个角色需要的资源就是穿过多个模块后,最后达到资源管理模块,从这里获得。

资源释放是与其对应的,就是把不用的资源从内存中卸载。那问题来了,什么时候资源什么时候不用?通常有两种情形。场景切换和角色不在屏幕时。难道此时我们就应该把资源从内存中释放?这样做也不是不可以,但这样就意味着做出来的游戏是低效的。脑补下策划,制作人的表情:) 道理很简单,角色是非常常见的,如果每次不用了就从内存释放,那就意味着每次用的时候都要从磁盘加载。这样就引出了对象管理这个概念。

1.2 对象管理

对象管理是资源管理和上层模块的一个中间层,缓存。他主要为那些暂时不用的资源提供内存缓存作用。角色首次需要的时候确实从资源管理从获取,并且缓存在对象管理中,暂时不用时不从资源管理器释放,而是由对象管理来接管。当下次再用的时候,就从对象管理来取角色。都是在内存中,速度自然比从资源管理中快。

2 对象管理模块设计

既然作为缓存,那当然对速度有要求。比如场景中需要10个相同的NPC,那么我们缓存一个NPC显然是不够的,而是需要多个。不然对象管理的世界意义就会大打折扣。多个重复的对象(NPC),这种情形下,“对象池”出场了。对象池就是用于存储重复对象的。

NPC可能有多种,“对象池”就会有多个。既然是多个对象池,就需一个“对象池管理器”来管理这些“对象池”。比如NPC分为NPC1....NPCn,那么就会有n个对象池。我们不太可能遍列这些对象池。而是需要提供O(1)的查找效率,找到对应的对象池。在C#里采用Dictionary这个数据结构是最合适的了。他的key就是“对象池”的名字(或者是类型),value就是对象池的指针。而持有这个Dictionary的就是对象池管理器。

对象从哪个“对象池”取的问题貌似就解决了。那找到了对应的“对象池”,那怎么以O(1)的效率从“对象池”中找到对象呢?我们还是采用Dictionary这个数据结构。因为在客户端显示的NPC都是在服务器中有唯一编号的,EntityID(事实上Player也是)。他是一个唯一的编号,通常是个int或者无符号的int类型的值。是服务器和客户端双方都认可的,而且双方还约定了类型。也就是还有一个EntityType.这个EntityType就是拿来对应对象池名字的。通过EntityType,找到对应的对象池,然后通过EntityID找到对应对象。

\

无论是本图和上图都一个叫“缓存”的模块。这究竟是怎么回事?事实上是,整个对象管理除了“对象池管理器”和“对象池”之外,还有“缓存”。"对象池"和“缓存”都是存储重复对象的,但他们两点本质的区别。

第一,“缓存”中的对象是已经被用过的(分配了EntityID),用于相同对象(EntityID)相同的对象可以重复使用。而“对象池”中的对象可能是被用过的,也可能没被用过。当上层逻辑暂时不用时,是把对象还给“对象池”,而避免下次使用同类型的对象从资管管理器中从磁盘读取。当对象被还给“对象池”时,对象上虽然有EntityID,但他是无效的。举例来说,当一个“坦克1”对象第一次被服务器发出指令出现在屏幕上,做出一个“攻击”动作。那么“坦克1”是从“对象池”取到“缓存”中,然后取出,执行“攻击”。如果服务器又发出指令要求“坦克1”做出一个“受击”动作。这时“坦克1”是直接从“缓存”中取出,做出受击动作。如果“坦克1”过一会儿,移动出屏幕了,这时需要把“坦克1”从“缓存”移除,并还回“对象池”。然后这时服务器要求“坦克2”做出执行“攻击”。这个“坦克2”除了EnityID和之前的“坦克1”不同之外,其他都一样。那么这时从“对象池”中取出的对象,很有可能是之前用过的“坦克1”。没关系,我们把他的EntityID重新赋值即可。“缓存”和“对象池”两者配合,就实现真正的对象重复使用。也可以简单理解,缓存中的对象是游戏中正在活动的对象,有身份证的(EntityID),而对象池中的对象是预先从资源管理中读取的对象,要么没有身份证,要么身份证已经过期。

第二,两者的数据结构不同。“缓存”中的对象是存在以EntityID为key存储的Dictionary中。而“对象池”不能用Dictionary存储。因为“对象池”种对象必须是当前没有使用的。相当于有个状态。而且这个状态只有两个取值,要么正在被用,要么没有用。那他到底采用什么样的数据结构呢?先买个关子,下篇再讲。下面是对象数据的时序图,比上面的流程图更容易看懂整个过程。重要的是先访问“缓存”再访问“对象池”。

\
点击复制链接 与好友分享!回本站首页
上一篇:SwitchCompat在menu中使用时出现的问题
下一篇:Struts功能详解——ActionForm
相关文章
图文推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站