×
Community Blog Candy for You: Design of Xianyu Flutter Interaction Engine Series

Candy for You: Design of Xianyu Flutter Interaction Engine Series

This article introduces the Candy engine and explains why and how the Xianyu technical team developed this engine.

What Is Candy?

The Candy engine was designed and developed by the Xianyu technical team.

  • It is an app-embedded, lightweight, easy-to-develop, and stable interaction engine.
  • The game series complies with industry standards.
  • The rendering system is highly integrated with the Flutter system. Game scenes can be seamlessly mixed with the Flutter UI.
  • The animation system provides strong support for mainstream formats and features easy extensibility.

This article mainly explains why and how we developed this engine.

Background

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.

Thinking

We had three main ideas about app-based embedded mini-games:

  1. Using the native game capabilities

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.

  1. Using game engines, such as Cocos2d-x and Unity

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.

  1. Using the Flutter lightweight interaction engine

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.

Flame or Independent Design

The Flame engine is a good mini-game engine in the Flutter ecosystem, but still has the following problems:

  1. Incomplete game system: The engine provides only Game and Component. Scene and GameObject concepts do not exist. As a result, the nesting of game objects is complex and not well-suited to multiple scenes.
  2. The engine is entirely implemented using Canvas. Partial refresh is not supported in game scenes, posing performance risks.
  3. Lack of a GUI system: It is difficult to nest UIs in a scene.
  4. The engine lacks a gesture event system.
  5. Support for non-mainstream animation formats: Skeletal animation is supported through Flare. DragonBones is not supported. Particle animation was only implemented recently, and it does support mainstream formats well.
  6. Memory problems exist in resource management. Resources are not released after they are loaded.
  7. The engine lacks device model adaptability.

Based on these concerns, we decide to design a new Flutter interaction engine.

  1. We improved the game system based on the EVA engine of the Alibaba Group and the Unity engine in the industry.
  2. We reused Flutter's partial refresh.
  3. We reused the Flutter UI as the GUI.
  4. We reused Flutter gesture management.
  5. We implemented skeletal animation and particle animation that support mainstream formats.
  6. We reused the app resource library (image library).
  7. We implemented global 750 adaptation.

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.

Candy Engine Design

Framework Design

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.

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.

2

The interaction engine architecture consists of four parts.

  1. Interface layer

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.

  1. Game system

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.

  1. Game subsystem

This supplements gamification capabilities with a lifecycle system, physical system, animation system, and resource system, which are called by the game system.

  1. Rendering 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).

Game System

Using the Unity design as a benchmark, the game system has the following four elements:

  1. Game: game class. It is responsible for overall game management, Scene loading management, and subsystem management and scheduling.
  2. Scene: game scene class. It is responsible for the management of each game object in game scenes.
  3. GameObject: game object class. It is the smallest unit of game objects in the game world. Any object in the game world is a GameObject.
  4. Component: game component class. These indicate the capability attributes of a game object. For example, SpriteComponent, the sprite component, indicates the sprite rendering capability.

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.

3

Lifecycle

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.

4_1

Rendering System

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.

5

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:

  1. Conversion and integration between the coordinate system of the Flutter layout
  2. Processing for dynamically adding and deleting game objects
  3. Processing for dynamically modifying the game rendering depth
  4. Support for game objects by Flutter Inspector

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.

GUI System

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);

Event System

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:

6

The integration process is as follows:

  1. GestureBoxComponent registers gesture callback methods registered by the developers with the gesture detector.
  2. The RenderObject corresponding to the GameObject duplicates the hitTest logic and processes the click hitTest according to Flutter specifications. The GestureBoxComponent determines whether a click can be consumed.
  3. The GameObject duplicates handEvent to send a click event to the GestureBoxComponent for consumption.
  4. After receiving the click event, GestureBoxComponent sends it to the gesture detector.
  5. The gesture detector transmits the click to the gesture arena for gesture competition. The winning gesture is returned to the gesture detector and finally is returned for the gesture event response. Naturally, this step belongs to the Flutter logic.

Other Subsystems

Animation System

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.

Resource System

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.

7

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.

Conclusion

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.

0 0 0
Share on

Alibaba Tech

14 posts | 1 followers

You may also like

Comments