不用 H5,闲鱼 Flutter 如何玩转小游戏?

2023-05-16

阿里妹导读:最近APP游戏化成为了一个新的风口,把在游戏中一些好玩的、能吸引用户的娱乐方式或场景应用在应用当中,以达到增加用户粘性,提升DAU的效果,成本较低。同时在一些需要对用户有引导性的场景,游戏化还可以使用户更易于接受并完成引导性任务,并通过激励的形式鼓励用户持续沉浸在任务当中,形成良性循环。基于这个思路,闲鱼开发了互动引擎Candy。

什么是Candy引擎?

Candy 是闲鱼技术团队设计开发的一款引擎:

  • APP嵌入式的、轻量级的、易于开发、性能稳定的互动引擎;

  • 绘制系统高度融合Flutter体系,游戏场景和Flutter UI支持无缝混排;

  • 动画系统对主流格式的支持友好且易扩展。

本文讲解我们为什么要做这款引擎以及我们是如何设计这款引擎的。

缘起

目前APP内嵌小游戏一般采用H5小游戏的方式,而这个方式存在一些隐患,并不被很多应用商店推荐。因此我们需要寻找一种新的安全的方式来实现APP内嵌小游戏,并且我们希望这个方式开发友好、性能稳定、功能齐全;所以我们遵循这三点去寻找一种新的方式。

思考

我们主要通过下面三种思路来探讨APP内嵌小游戏:

  • 采用Native的游戏能力

目前Native开发游戏生态并不是特别成熟,而且采用Native开发,就必须面临双端两套代码的问题,开发成本和后续维护成本都会比较高。

  • 采用游戏引擎,比如Cocos-2dx、Unity等

虽然游戏引擎目前非常成熟,但是游戏引擎一般用于开发重度游戏,所以引擎大小一般比较大,引入游戏引擎会导致包大小增幅不小。而且游戏引擎比较复杂,所以引擎启动耗时较多,比较难做到游戏页面秒开;游戏引擎加载进来后内存消耗都会比较大。游戏引擎和APP间的通信互动相对较为麻烦,目前没有比较好的混合栈支持。游戏引擎的UI能力较弱,无法胜任复杂的APP UI逻辑,若采用游戏引擎开发内嵌小游戏,无法融合小游戏页面内游戏场景和Feeds等UI。

  • 采用Flutter的轻量级互动引擎

Flutter本身是基于Skia这个2D绘制引擎实现的跨端APP解决方案,所以它天然具备2D绘制能力,所以采用Flutter来实现App内嵌小游戏存在可能。目前Flutter存在一些轻量级游戏引擎,比如Flame,这款引擎支持简单游戏逻辑和动画能力,同时整个游戏是以一个Widget的形式最终插入到APP中,可以让小游戏页面中游戏部分和UI部分完美融合。

综上考虑,我们决定采用Flutter的轻量级互动引擎。

Flame?还是自主设计?

Flame引擎目前是Flutter生态中比较不错的一款小游戏引擎,但是依然存在很多问题:

  • 游戏系统不完善:引擎只有Game和Componet,没有Scene和GameObject概念,这样会导致游戏对象嵌套复杂,对多场景不友好。

  • 引擎完全采用Canvas来实现,游戏场景中无法实现局部刷新,存在性能隐患。

  • 缺少GUI系统,场景内嵌套UI比较难。

  • 缺少手势事件系统。

  • 动画支持格式不主流:骨骼动画是通过Flare支持的,不支持DragonBones。粒子动画最近才上,对主流格式支持也不太友好。

  • 资源管理存在内存问题,资源加载后一直不会释放。

  • 缺少机型适配能力。

基于这些考虑,我们决定重新设计一款Flutter互动引擎:

  • 对标集团的EVA引擎和业界的Unity引擎,完善游戏系统。

  • 复用Flutter局部刷新。

  • 复用Flutter UI作为GUI。

  • 复用Flutter手势管理。

  • 实现支持主流格式的骨骼动画和粒子动画。

  • 复用APP资源库(图片库)。

  • 实现全局750适配。

其中2-4点本质上是将互动引擎的绘制系统融合入Flutter的绘制体系中,本文下面按解决上面问题的思路依次介绍我们的引擎设计。

Candy引擎设计

框架设计

首先分析游戏化业务需要哪些能力,分析我们的业务场景,得出游戏化业务需要图4-1所示的能力:

图4-1 游戏化业务能力需求

拆解后,互动引擎需要有游戏系统、绘制系统、生命周期系统、GUI系统、物理系统、动画系统、资源系统、事件系统(手势管理)。

根据我们之前的定位,互动游戏绘制融合到Flutter绘制体系中来,基于这个思路,我们可以复用Flutter的UI系统,同时需要融合Flutter和游戏的手势管理。最终我们得出如图4-2所示的框架图:

图4-2 互动引擎架构

整个互动引擎架构共分为四部分:

  • 接口层

对外暴露的游戏接口,主要包含创建游戏、创建游戏对象、添加游戏组件等接口,同时还封装了一些常用游戏对象、常用游戏组件的工厂接口。

  • 游戏系统

游戏世界的管理系统,主要管理Game、Scene、GameObject和Compoent间的组织关系,还控制游戏子系统和绘制系统的启动与关闭。

  • 游戏子系统

游戏化能力补充,主要包含生命周期系统、物理系统、动画系统和资源系统,被游戏系统调用。

  • 绘制系统

负责游戏的绘制,本引擎的绘制系统会高度和Flutter绘制逻辑融合,所以兼容了GUI系统和事件系统(手势管理)。

游戏系统

对标Unity设计,游戏系统有下列四大元素:

  • Game:游戏类,负责整个游戏的管理,Scene的加载管理以及各子系统管理与调度。

  • Scene:游戏场景类,负责游戏场景中各游戏对象的管理。

  • GameObject:游戏对象类,游戏世界中游戏对象的最小单位,游戏世界中的任何物体都是GameObject。

  • Component:游戏组件类,表示游戏对象的能力属性,比如SpriteComponent表示精灵组件,表示绘制精灵的能力。

GameObject通过组合Component的形式来让自己拥有各种能力,不同的组合让GameObject相互之间不一样。整个游戏系统的组织关系如图4-3所示:

图4-3 游戏组织形式

生命周期

对标Unity和Flutter特性,我们设计了如表4-1所示的生命周期,共有八个回调,基本可以满足互动游戏业务开发。

表4-1 生命周期

渲染系统

基于融合Flutter绘制体系思考,我们就不能全盘用Canvas来做整个游戏的绘制管理,我们需要将游戏对象和Flutter的绘制对象RenderObject结合起来,如图4-4所示:

图4-4 渲染映射

首先是Game的对象数和Flutter的三颗树有效融合,所以每一个GameObject必须对应一个Widget、Element和RenderObject。

融合过程主要需要解决以下问题:

  • 游戏的坐标系与Flutter的布局转换融合。

  • 动态添加和删除游戏对象的处理。

  • 动态修改游戏绘制深度的处理。

  • Flutter Inspector对游戏对象的支持。

整个绘制融合相对复杂,需要解决很多BadCase,后续会另撰文详述互动引擎绘制融合Flutter绘制体系的过程,本文不再赘述。

GUI系统

由于绘制已经融合到Flutter体系,GameObject都会对应Widget,所以我们可以设计一个特殊的GameObject,支持插入一段Flutter Widget树,这样我们就不需要另外实现GUI了,复用Flutter UI作为GUI即可。这个逻辑和绘制融合思路比较一致,将插入的Widget树作为GUIWidget的孩子即可,在GUIRenderObject中打通layout、paint和hitTest逻辑即可。

这里给一段我们GUI的示例实例代码,开发过程相对简单:

final GUIObject gui = IdleFishGame.createGUI(
  'gui',
  child: GestureDetector(
    child: Container(
      width: 100.0,
      height: 60.0,
      decoration: BoxDecoration(
        color: const Color(0xFFA9CCE3),
        borderRadius: const BorderRadius.all(
          Radius.circular(10.0),
        ),
      ),
      child: const Center(
        child: Text(
          'Flutter UI示例',
          style: TextStyle(
            fontSize: 14.0,
          ),
        ),
      ),
    ),
    behavior: HitTestBehavior.opaque,
    onTap: () {
      print('UI被点击了');
    },
  ),
  position: Position(100, 100),
);
game.scene.addChild(gui);

事件系统

在绘制融合到Flutter体系的基础上,我们融合了事件系统,增加了手势处理组件GestureBoxComponent,如图4-5所示:

图4-5 手势竞技

整个融合过程分下列几步:

  1. GestureBoxComponent将开发者注册手势回调方法注册到手势识别器中。

  2. GameObject对应的RenderObject复写hitTest逻辑,按Flutter规范来处理点击的hitTest。通过GestureBoxComponent来判断点击是否该被消费。

  3. GameObject复写handEvent来将点击事件传递给GestureBoxComponent消费。

  4. GestureBoxComponent收到点击事件后,传递给手势识别器处理。

  5. 手势识别器在将点击传给手势竞技场开始手势竞技,最终将胜出的手势返回手势识别器,最终返回并做出手势事件响应,当然这一步属于Flutter逻辑了。

 

动画系统

我们目前动画主要支持骨骼动画和粒子动画,骨骼动画资源目前支持DragonBones,粒子动画资源目前支持EgretFeather。

资源系统

目前互动引擎的资源系统相对简单,本文就简单介绍下。资源系统的设计思路是复用APP的资源系统,确保整个APP只有一份资源库,减少内存开销和增大资源复用率。资源系统架构如图4-6所示,在游戏系统和资源系统中间增加了一层代理,兼容APP资源系统和兜底资源系统。若我们没有注册APP的资源系统,系统会自动调用兜底的资源系统。

兜底资源系统目前分两部分:

兜底图片库,复用Flutter的ImageCache,复用Flutter的能力做内存管理。

动画JSON资源管理,目前只实现了JSON读取逻辑,由于JSON复用性不高,所以目前并没有实现缓存管理。

图4-6 资源系统

目前资源系统没有做远程加载和预加载的能力,这部分在我们的后续规划中,后续我们再撰文分享具体设计实现。

说在最后

本文主要讲述了Candy互动引擎的设计,而我们在设计实现过程中遇到了很多问题,如发现了Flutter在绘制过程中存在一定的内存泄露,内存回收不及时等,我们后续会详述这些问题的排查与解决,同时还会对Candy引擎的性能与稳定性方面做详细测试分析。

从入门到精通|最实用Java学习路线

包含:基础阶段篇、Java职业规划篇、Java面试篇,以及871节Java视频课程。识别下方二维码或点击“阅读原文”立刻学习。

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

不用 H5,闲鱼 Flutter 如何玩转小游戏? 的相关文章

  • KEIL 生成bin文件 MDK 51 ram bin 文件

    使用keil做项目开发 xff0c 并且需要做IAP功能时 xff0c 就需生成bin文件 xff0c bin文件相比于hex文件更小 xff0c 一般用 bin文件作为升级文件 keil无法像iar一样通过工程设置直接输出bin文件 xf
  • ADC 采样数据抖动

    MSP430或STM32 xff0c 在使用内部ADC出现的采样数据异常抖动问题 采样设计 xff1a 用于检测供电线路电流及电压 产品运行在两种模式下 xff0c 1 低功耗静态模式 xff08 仓储态 xff09 xff0c 2 全功能
  • CC1310 基于CCS工程建立+Sensor Controller ADC

    基于TIRTOS xff0c PacketTx 43 Sensor Controller ADC 43 CCS工程建立 需要的工具 xff1a 1 simplelink cc13x0 sdk 3 20 00 23 CC1310的SDK xf
  • linux w5500 驱动及使用

    1 驱动 驱动来源 a 内核驱动 xff1b b xff1a 官方驱动 a 内核 xff1a linux内核w5500驱动 xff0c 包含两个源文件w5100 c和w5100 spi c kernel drivers net ethern
  • 扩展 IO pca95555 linux驱动 及使用

    驱动 xff1a 内核驱动 xff1a kernel drivers gpio gpio pca953x c make menuconfig配置编译 或拷贝出来手动编译成模块加载 设备树 xff1a 使用2个芯片 xff0c 在同一i2c总
  • Linux内核崩溃 dump调试

    内核 crash 崩溃 xff0c oops消息 xff0c dump oops xff08 也称 panic xff09 xff0c 称程序运行崩溃 xff0c 程序崩溃后会产生oops消息 应用程序或内核线程的崩溃都会产生oops消息
  • 【路径规划】VFH

    简介 在没有全局地图或者是应对动态障碍物的情况下 xff0c 往往是运用局部规划来进行绕障 xff0c 在局部规划的经典算法中 xff0c 比较常用的是 DWA 与 VFH xff0c VFH 全名向量场直方图算法 xff0c 不同于 DW
  • keil5 编译程序出现错误Error: L6411E: No compatible library exists with a definition of startup symbol __main

    Error L6411E No compatible library exists with a definition of startup symbol main 之前装过ADS xff0c ADS与MDK冲突 xff0c 依据网友提供的
  • LORA 射频自组网 两级中继

    基于sx1276lora模块 xff0c 进行多个模块之间自组网 xff0c 组网形式为1个集中器加多个终端 模块之间距离较远时 xff0c 集中器无法直接与某个终端进行通信 xff0c 其他终端本身可作为中继给该终端作为中继与集中器通信
  • STM32上进行Delay延时的方法

    1 使用SYStick专门的延时 void delay us uint32 t us static uint32 t delay flag 61 0 delay flag 61 1 set reload register HCLK 61 4
  • 内核模块已打开,但开机未加载

    环境 xff1a 系 统 Centos 6 6 内核版本 xff1a linux 2 6 32 1 vmware转qcow2格式 xff0c 内核崩溃 最近做一个kvm的镜像 xff0c 尽力裁小内核大小 xff0c 从VMware格式转q
  • px4飞控校准中常遇到的一些坑

    1 校准中最后给飞控独立供电 xff0c 这样可以避免一些不必要的错误 比如要给飞控解锁 xff0c 就必须是给飞控独立供电 xff0c 不能通过USB线给飞控供电 2 校准时至少给飞控接上数传和GPS xff0c 注意GPS的方向和机头方
  • ros+mavros+gazebo仿真实践要点总结

    1 安装ros时要看好对应的版本 xff0c ubuntu16 4对应的kenitic版本 xff0c Ubuntu18 04对应的melodic版本 2 通过ssh方式clone px4 firmware比http方式快的多 3 参考教程
  • offboard

    GAAS
  • 无人机飞控系统的简单分析

    这里写自定义目录标题 刚上大一的时候的学业报告 xff0c 将就这看吧 无人机控制系统原理的简单分析 摘要 xff1a 无人机 xff08 UAV xff09 是无人驾驶飞机的简称 xff0c 是指利用无线电遥控设备和自备的程序控制装置操纵
  • [网络编程]运用socket实现本地通信

    功能实现 基于socket文件 xff0c 进行数据传递 xff0c 实现服务器与客户端之间进行本地通信 设计思路 服务器 创建socket套接字将socket套接字关联到socket文件 xff0c 实现绑定并创建套接字文件此时 xff0
  • 学东西要先看这个东西能给你提供什么功能,作东西要明白自己要实现什么功能!一定要耐下性子!

    学东西不要急匆匆去学 xff0c 首先要明白你要学的这个东西它能给你提供什么功能 xff0c 然后再去学这些功能怎么应用 xff0c 怎么实现的 xff0c 甚至 xff0c 只去学习自己需要使用的功能 作东西也不要匆匆就去作 xff0c
  • Linux系统 搭建gitlab仓库服务器

    安装依赖工具 安装技术依赖 yum span class token function install span y span class token function curl span policycoreutils python op
  • chmod +x 与chmod +777的区别

    对于chmod 43 x 脚本 来说就是将脚本改为可执行状态 xff0c 在linux因高亮语法 xff0c 会让file文件显示加黑 对于灰色的文件来说 xff0c 没有可执行的权限 xff0c 这是若我们给它chmod 43 x后它将会
  • 多线程编程实例(使用CompletableFuture)

    关键配置 xff1a 代理类对象 YrSyncWrService yrSyncWrServiceProxy 获取代理类的对象 xff0c 调用本类方法时 xff0c 注解才会生效 比如 64 Async多线程 xff0c 64 Transa

随机推荐