
哈勃! 在本出版物中,我想分享开发具有大城市和大流量的大型手机游戏的经验。 该出版物中描述的示例和技术并不声称被称为参考和理想。 我不是认证专家,也不希望重复自己的经验。 游戏的目标是获得有趣的体验,并获得具有开放世界的优化游戏。 在开发过程中,我试图尽可能简化代码。 不幸的是,我没有使用ECS,但是犯了单身罪。
游戏
以黑手党为主题的游戏。 在游戏中,我尝试重新创建30-40。 本质上,游戏是第一人称经济策略。 玩家抓住了业务,并试图使其保持生计。
实施:汽车交通(交通信号灯,避撞灯),人流,酒吧,赌场,俱乐部,球员的公寓,购买套装,更换套装,购买/涂漆/加油,警察,安全/黑帮,经济学,出售/购买资源。
建筑学

很遗憾我没有使用ECS,而是尝试骑自行车。 最后,一切都变得繁琐且过于依赖。 该应用程序具有一个入口点-应用程序(go)游戏对象,同名的Application类挂在该对象上。 他负责预加载数据库,填充池和初始设置。 另外,其他几个单例管理器组件类也落在应用程序的肩膀上(转到)。
我狂热地尝试创建这样一种体系结构,在该体系结构中我可以从管理器管理各种组件。 例如,AudioManager管理所有声音,UIManager包含所有UI元素和管理方法。 所有输入都使用事件和委托通过InputManager处理。
简化的AudioManager。 它允许您向游戏对象添加尽可能多的音频组件,并在必要时播放声音:
public class AudioManager : MonoBehaviour { public static AudioManager instance = null;
在启动时,AddAudio方法会添加一个组件,然后我们可以从任何地方播放所需的声音:
AudioManager.instance.isMetalHit = true;
在此示例中,将oneshot回放放到该方法中会更明智。
简化的InputManager如下所示:
public class InputManager : MonoBehaviour { public static InputManager instance = null; public float horizontal, vertical; public delegate void ClickAction(); public static event ClickAction OnAimKeyClicked;
我
将AimKeyDown方法放在
按钮上 ,并
在OnAimKeyClicked上签名武器控制脚本:
InputManager.instance.OnAimKeyClicked += GunShot;
我的整个输入系统以类似的方式实现。 我没有发现速度方面的任何问题。 这使我们可以将所有单击处理程序收集在一个位置-InputManager。
最佳化
让我们继续进行最有趣的事情。 对于初学者而言,Unity中的优化主题是痛苦的,并充满许多陷阱。 我将分享我正在处理的事情。
1.组件缓存(从简单的基础开始)通常,在Toster上,何时在Update中使用GetComponent时,您会遇到一些带有示例的问题。 您无法执行此操作,GetComponent正在对象上寻找组件。 此操作很慢,并且在Update中导致它,您可能会失去宝贵的FPS。 这是
组件缓存的很好解释。
2.使用SendMessage使用SendMessage()比GetComponent()慢。 SendMessage使用字符串比较来遍历每个脚本以查找具有所需名称的方法。 GetComponent通过类型比较查找脚本,然后直接调用该方法。
3.对象标签比较使用CompareTag方法而不是obj.tag ==“ string”。 在Unity中,从游戏对象中提取字符串会创建一个重复的字符串,这会将工作添加到垃圾收集器中。 最好避免获取游戏对象的名称。 您不能在Update中调用CompareTag以及读取繁重的操作。
4.材料材料越少越好。 尽可能减少材料量。 为了达到这个目的,请帮助缎纹。 例如,在我的游戏中,几乎整个城市都由2-3个地图集组成。 应该注意的是,并不是所有的移动设备都能够使用大型地图集。 因此,如果您要支持11-13岁的设备,则值得考虑。 我决定拒绝支持5.1以下的android系统,因为这些设备大多数都是旧设备。 此外,由于线性渲染,该游戏可在OpenGL 3.x上运行。
5.物理将FPS降低到10很容易。事实证明,即使静态对象也会相互作用并参与计算。 我错误地认为静态物理对象(具有RigidBody组件的对象)在需要时完全是被动的。 我被旧教程误导了,旧教程说,只要有对撞机,就应该有RigidBody。 现在我所有的静态对象都是Static + BoxCollider。 在需要物理的地方,例如可以拆下来的灯柱,我认为如有必要,可以减少RigidBody组件。
图层是优化的生命线。 使用图层禁用不必要的交互。 重铸时,请使用图层蒙版。 为什么我们需要额外的错误计算? 请记住,如果您的对象具有复杂的对撞机网格,并且用射线对其进行射击,最好创建一个简单的父对撞机来“捕捉”射线。 对撞机越复杂,计算错误就越多。
6.遮挡剔除+ Lod对于较大的场景,遮挡剔除是必不可少的。 为了远距离禁用对象(树木,电线杆等),我使用Lod。

7.对象池我发现的对象池的所有现成实现都使用实例化。 他们还删除和创建对象。 我害怕实例化所有形式。 缓慢的操作会使游戏停滞不前,或多或少都有大物体。 我决定走一条简单而快捷的道路-我的整个筹码池都以物理游戏对象的形式存在,我可以在需要时将其关闭然后再打开。 它达到了RAM,但是更好。 适用于现代设备的RAM从1GB起,游戏消耗300-500 MB。
用于管理战斗机器人的简单池:
public List<Enemy> enemyPool = new List<Enemy>(); private void Start() {
资料库
我将sqlite用作数据库-方便快捷。 数据以表格的形式呈现,您可以进行复杂的查询。 在用于数据库的类中,当800行时。 我无法想象XML / JSON中的外观。
未来的问题和计划
为了从城市转移到“房间”,我选择了“远程办公”。 播放器靠近门,场景室已装满,播放器被传送。 这使您不必在城市中保留房间。 如果您在城市中安装了15个房间,并且有填充空间,那么内存消耗将至少增加1GB。 我不喜欢这种实现方式,它不现实,并且施加了很多限制。 Unity最近展示了其
Megacity的演示,令人印象深刻。 我想逐步将游戏转移到esc,并使用Megacity的技术来加载建筑物和房屋。 这是一次有趣而有趣的经历,我认为它将变成一个真正充满活力的城市。 为什么不使用
异步加载场景 ? 很简单,它不起作用,在2018.3版本中没有开箱即用的异步加载场景。 最初,我希望在规划城市时使用异步加载场景,但事实证明,在大型场景中,它会像常规加载场景一样冻结游戏。 这已在Unity论坛上得到确认,您可以绕开,但是需要拐杖。
一些统计:
纹理:304 / 374.3 MB
网格:295 / 304.0 MB
资料:101 / 148.0 KB(此处可能有差异)
动画剪辑:24 / 2.8 MB
音频剪辑:22 / 30.3 MB
资产:21761
场景中的GameObjects:29450
场景中的对象总数:111645
对象总数:133406
每帧的GC分配:70 / 2.0 KB总共4800行C#代码。
有人告诉我,这样的游戏可以在一周内完成。 也许我效率不高,也许这个人很有才华,但对我自己来说我明白一件事-很难独自制作这样的游戏。 我想在随意的“手指”的背景下创造一些有趣的东西,在我看来,我实现了自己的梦想。
您可以运行一个公开的Beta测试,并在此处进行测试:
play.google.com/store/apps/details?id=com.ag.mafiaProject01 (如果该程序集无法正常工作,则需要稍加欣赏,每晚都有更新。) 我希望这不会被视为广告链接,因为此测试版和下载不会给我带来评分和分红。 另外,我不认为habr是我游戏的目标受众。
屏幕截图:

