The Candy engine was designed and developed by the Xianyu technical team.
This article mainly explains why and how we developed this engine.
Recently, app gamification has emerged as a new trend. It applies some fun and enticing entertainment methods or scenes used in games to other apps to improve user stickiness and increase daily active users (DAU) at a lower cost. In addition, in some scenes that require user guidance, gamification makes it easier for users to accept and complete guidance tasks and encourages users to remain immersed in tasks by means of incentives, forming a virtuous circle.
Apps generally use embedded HTML5 mini-games, but these present some hidden dangers and are not recommended by many app stores. Therefore, we needed to find a new and safe method to develop app-based embedded mini-games. We wanted this method will feature easy development, stable performance, and complete functionality. These three desires informed our search for a new method.
We had three main ideas about app-based embedded mini-games:
Currently, the ecosystem for native game development is not very mature. Moreover, using Native development requires two sets of code on both sides, incurring relatively high development costs and subsequent maintenance costs.
Currently, game engines are very mature. However, they are generally used to develop intensive games. The engine size is relatively large, and therefore introducing a game engine will significantly increase the package size. In addition, due to complexity, it takes a lot of time to start the engines, difficult to achieve opening game pages in seconds. After a game engine is loaded, the memory consumption is very large. Communication and interaction between the game engine and app are relatively complicated. Currently, there is no proper hybrid stack to support it. Game engines have weak UI capabilities and cannot be used for complex app UI logic. If a game engine is used to develop embedded mini-games, it cannot integrate UIs on mini-game pages, such as game scenes and feeds.
Flutter itself is a cross-terminal app solution based on Skia, a 2D rendering engine. It has intrinsic 2D rendering capabilities. Therefore, Flutter offers a promising method for the development of app-based embedded mini-games. Currently, Flutter has some lightweight game engines, such as Flame. Flame supports simple game logic and animation capabilities. At the same time, the entire game is ultimately inserted into an app as a widget. This allows seamless integration of the game part and the UI part on mini-game pages.
Based on the above considerations, we decide to use a Flutter lightweight interaction engine.
The Flame engine is a good mini-game engine in the Flutter ecosystem, but still has the following problems:
Based on these concerns, we decide to design a new Flutter interaction engine.
Among the preceding points, 2 to 4 essentially integrate the rendering system of the interaction engine into the Flutter rendering system. The following introduces our engine design in order based on how it solves the preceding problems.
First, we analyze what capabilities are required for the gamification business. After analyzing our business scenes, we obtained the capabilities shown in Figure 4-1.
After decomposition, the interaction engine needs to have a game system, rendering system, lifecycle system, GUI system, physical system, animation system, resource system, and event system (gesture management).
According to our previous idea, interactive game rendering needs to be integrated into the Flutter rendering system. Based on this idea, we can reuse the Flutter UI system and also need to integrate the gesture management of the game and Flutter. Finally, we obtain a framework as shown in Figure 4-2.
The interaction engine architecture consists of four parts.
This provides externally exposed game interfaces, including interfaces for creating games, creating game objects, and adding game components. It also encapsulates factory interfaces for some commonly used game objects and game components.
The game world management system manages organizational relationships among Games, Scenes, GameObjects, and Components and controls the startup and shutdown of the game subsystems and rendering system.
This supplements gamification capabilities with a lifecycle system, physical system, animation system, and resource system, which are called by the game system.
This is responsible for game rendering. The rendering system of the engine is highly integrated with the Flutter rendering logic. Therefore, it is compatible with the GUI system and event system (gesture management).
Using the Unity design as a benchmark, the game system has the following four elements:
A GameObject possesses different capabilities by combining various Components. Different combinations make GameObjects distinctive. Figure 4-3 shows the organizational relationships of the entire game system.
Based on Unity and Flutter features, we designed a lifecycle with eight callbacks, as listed in Table 4-1. It basically meets the needs of interactive game business development.
Considering the integration with the Flutter rendering system, we cannot use Canvas for comprehensive rendering management of the entire game. Instead, we need to combine GameObject with RenderObject (Flutter render object), as shown in Figure 4-4.
First, the number of game objects must be effectively integrated with the three trees of Flutter. Therefore, each GameObject must correspond to a Widget, Element, and RenderObject.
The integration process helps solve the following problems:
The overall rendering integration is relatively complex and many BadCases need to be eliminated. Subsequent articles will describe the process of integrating the interaction engine rendering with the Flutter rendering system in detail. Therefore, we will not give a detailed description here.
Rendering has been integrated into the Flutter system, and each GameObject corresponds to a Widget. Therefore, we can design a special GameObject that supports the insertion of a Flutter Widget tree. We can reuse the Flutter UI instead of implementing the GUI separately. This logic is basically the same as for rendering integration. The inserted Widget tree operates as the child of the GUI Widget. The layout, paint, and hitTest logic is implemented in GUIRenderObject.
The following provides a segment of GUI sample code. The development process is relatively simple.
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 example',
style: TextStyle(
fontSize: 14.0,
),
),
),
),
behavior: HitTestBehavior.opaque,
onTap: () {
print('UI is clicked');
},
),
position: Position(100, 100),
);
game.scene.addChild(gui);
On the basis of integrating rendering into the Flutter system, we have integrated the event system and added the gesture processing component GestureBoxComponent, as shown in Figure 4-5:
The integration process is as follows:
Currently, we support skeletal animation and particle animation. The supported resources are DragonBones for skeletal animation and EgretFeather for particle animation. Due to space constraints, the specific animation implementation will be discussed in detail in subsequent articles.
Currently, the resource system of the interaction engine is relatively simple. This article gives a brief introduction. The resource system was designed by reusing the app resource system. This ensures that the app has only one resource library, reducing memory overhead and increasing the resource reuse rate. Figure 4-6 shows the resource system architecture. A proxy layer compatible with the app resource system and backup resource system is added between the game system and resource system. The backup resource system is automatically called if the app resource system is not registered.
The backup resource system is currently divided into two parts:
Backup image library: reuses Flutter ImageCache. This part reuses Flutter capabilities for memory management.
Animation JSON resource management: Currently, only the JSON read logic is implemented. Cache management is not implemented due to low JSON reusability.
The resource system does not have remote loading and preloading capabilities, which we plan to implement in the future. Later articles will share the specific design implementation.
This article describes the design of the Candy interaction engine, and also analyzes and introduces the ongoing Flutter interaction engine design from a global perspective. In the future, we will detail some specific system designs, such as the rendering system and animation system, through a series of articles about the Candy engine.
We have encountered many problems during the design and implementation process of the Candy engine. For example, Flutter experiences some memory leakage during the rendering process and memory collection is not prompt. Later articles will detail the troubleshooting and solutions to these problems and the testing and analysis of the Candy engine's performance and stability, so stay tuned.
15 posts | 1 followers
FollowXianYu Tech - August 10, 2021
Alibaba Clouder - December 22, 2020
XianYu Tech - September 10, 2020
XianYu Tech - September 8, 2020
XianYu Tech - September 10, 2020
Alibaba Clouder - May 17, 2021
15 posts | 1 followers
FollowAlibaba Cloud (in partnership with Whale Cloud) helps telcos build an all-in-one telecommunication and digital lifestyle platform based on DingTalk.
Learn MoreBuild superapps and corresponding ecosystems on a full-stack platform
Learn MoreProvides comprehensive quality assurance for the release of your apps.
Learn MoreWeb App Service allows you to deploy, scale, adjust, and monitor applications in an easy, efficient, secure, and flexible manner.
Learn MoreMore Posts by Alibaba Tech