Front-end students usually use media query or REM to do multi-end adaptation, but CSS does not exist on Cocos. Do you know how to do it on Cocos? This article starts from the demand background, take you to appreciate the multi-terminal adaptation of Cocos ~

background

One day, I received a new demand. After reading the design draft from my design classmates, I felt my head was big. I analyzed the main difficulties as follows:

  1. The background of the topic should be the same background image, and different areas of the background image should be displayed on different ends
  2. The distance between the edges of the countdown, topic and minimize buttons on the title bar varies at each end
  3. The option background image should be automatically stretched according to the length of the option, while ensuring that the rounded corners on both sides are not stretched

If this adaptation scheme using CSS implementation, certainly not a lot of media queries, as front-end students CSS implementation is certainly more familiar, but it will also lead to lengthy style code, is a torture for developers. The Cocos game engine has been introduced in the business for several years to implement new types of problems. CSS that we used to be so familiar with will no longer exist in Cocos. At this time, how can we achieve such multi-terminal adaptation in Cocos?

Analysis of the

In view of this demand, we divide the adaptation process into the following points:

  1. Multi – end adaptation background image
  2. Multi – end ADAPTS the edge node
  3. Option background image nine grid cutting

Multi – end adaptation background image

  1. What is design resolution and screen resolution?

    Multi-endpoint adaptation on Cocos requires understanding design resolution and screen resolution first. According to the official Cocos documentation, the design resolution is a blueprint for the resolution content producers use when creating scenes, and the screen resolution is the actual screen resolution when the game is running on the device. In practical development, the design resolution is actually the size most used by design students in the design draft, which is generally 667*375 of iPhone 6. Almost all design drafts are drawn at this size, and then individually adapted design draft for different terminals (PC, iPad, iPhoneX, etc.). Therefore, the canvas size in Cocos is usually set to a design resolution of 667 in width and 375 in height, and basic function development is completed at this resolution.

  2. Design resolution versus screen resolution?

    Assuming that our design resolution and screen resolution are 667 x 375, the Canvas will fit the screen perfectly without scaling. Let’s say our design resolution is 667 x 375 and the actual screen resolution is 1334 x 750, the Canvas needs to be twice as large to fit the screen perfectly. When canvas is scaled, all nodes in the scene can enjoy intelligent scaling based on design resolution.

  3. Fit Height and Fit Width

    In the example above, if the design resolution is 667 x 375 and the screen resolution is 1334 x 750, the scene needs to be twice as large to fit the screen perfectly, but only if the design resolution matches the aspect ratio of the screen resolution. Suppose that the aspect ratio of the design resolution is inconsistent with the aspect ratio of the screen resolution (a common problem with multi-endpoint adaptation). What should I do? Canvas component provides Fit Height and Fit Width modes to help us solve the problem. How do you understand these two modes of adaptation?

    We use the following scene as the base scene, with the purple box as our design resolution and the blue box as the actual scene:

    Take a look at the case where the screen resolution aspect ratio is smaller than the design resolution aspect ratio (the iPad case).

    First, we set Fit Height mode to see the effect. We will find that the Height of the designed resolution automatically covers the Height of the screen. Since the width and Height ratio of the screen resolution is smaller than that of the designed resolution, part of the background image will be cut out on both sides of the screen.

    Then we set Fit Width mode to see the effect. We will find that the Width of the designed resolution automatically covers the Width of the screen. Since the Width and height ratio of the screen resolution is smaller than that of the designed resolution, more background images will be displayed at the top and bottom of the screen.

    Let’s look at the situation where the screen resolution aspect ratio is larger than the design resolution aspect ratio (iPhoneX case)

    First, we set Fit Height mode to see the effect. We can see that the Height of the designed resolution automatically covers the Height of the screen. Since the width to Height ratio of the screen resolution is larger than that of the designed resolution, more background images will be displayed on both sides of the screen.

    If we set Fit Width mode to see the effect, we will find that the Width of the designed resolution automatically covers the Width of the screen. However, since the Width ratio of the screen resolution is larger than that of the designed resolution, part of the background image will be cut from the top and bottom of the screen.

  4. What mode does background multiterminal fit into?

    After explaining Fit Height and Fit Width above, can you now decide which mode to use for each end of the first diagram of this article? When the screen resolution ratio is smaller than the design resolution aspect ratio (in the case of the iPad), we want to show more background area on both sides of the screen with the same Width, so we need to use Fit Width. Fit Height is used when the screen resolution ratio is larger than the design resolution aspect ratio (in the case of the iPhoneX) and we want to show more background area on the left and right with the same Height.

    In the code, we can get the aspect ratio of the actual screen resolution by getting the current view size, and use Fit Height or Fit Width mode based on the aspect ratio.

    export function setCanvasScaleMode(canvas: cc.Canvas) {
      const standardRadio = 16 / 9; // standard aspect ratio, almost the same as iPhone6 aspect ratio (landscape screen), the general design draft as the standard
      const screenSize = cc.view.getFrameSize();
      const currentRadio = screenSize.width / screenSize.height; / / aspect ratio
      if (currentRadio <= standardRadio) {
        // Square screen, iPad or something like that.
        canvas.fitHeight = false;
        canvas.fitWidth = true;
      } else {
        // Long screen, ipX. Too long, the height looks small, so the height is preferred
        canvas.fitWidth = false;
        canvas.fitHeight = true; }}Copy the code

    We also need to make sure that there are no black edges in the background, because when using Fit Width mode on the iPad, there is more background area on the top and bottom, and if the background image is not that high, there are black edges on the top and bottom. Similarly, when using Fit Height on the iPhoneX, there is more background area on the left and right sides, and black edges on the left and right sides if the background image is not as wide. At this time, we need to design the background picture provided by the students to cover the height of iPad and the width of iPhoneX, the background picture should be larger than the design resolution, and reserve a certain length in the four directions of the upper, lower, left and right to ensure that there will be no black edge in the background adaptation.

  5. A special case

    Careful students may have found that the width and height ratio of PC terminal and iPhone7 terminal is actually the same. According to our idea above, the two ends should display the same background area. Meanwhile, since the width and height of PC terminal is larger than that of iPhone7, all nodes in the scene can enjoy intelligent scaling based on the design resolution. So the node on the PC should be bigger than the node on the iPhone7. But in the first draft design, design the classmate ask PC to occupy more background region, at the same time, the size of the node and iPhone7 the size of the nodes in the same, to ensure that the PC title shows beautiful, this time we need to do in case of the PC adapter separately, the background and nodes are down scaling.

Multi – end ADAPTS the edge node

  1. What is a Widget component?

    The Widget component is a UI layout component in Cocos that aligns the current node to any location of the parent node. By setting various values for the Widget component, we can align the node to the upper, lower, left, right, horizontal, and vertical boundaries. The Widget component is the best choice when there are nodes in the scenario that need to be edged.

  1. Which node is the parent of the aligned edge node?

    When there is a node that needs to be edged, we want it to always be in a fixed position on the screen no matter how the screen resolution changes. For example, the countdown node in the first draft of the design, we want it to be fixed in the upper left corner of the screen at different screen resolutions and not move to the upper right corner as the screen resolution changes. So the size of the parent node specified by the edge node should match the size of the screen. Which node is most appropriate?

    Yes, the answer is canvas node! When we use Fit Height and Fit Width modes, the Canvas node will occupy the size of the screen. In this case, setting the edge distance relative to the canvas node is actually setting the edge distance relative to the screen.

  2. Multi-end welt distance setting

    According to the requirements of the design students, the distance of the edge fitting node (such as the countdown node) is different on PC, iPad, iPhoneX and iPhone7. At this time, how to set the edge fitting distance according to different ends?

    First of all, setting the edge fitting distance of the multi-end node is actually to change the alignment value of the node Widget components in all directions. For example, for the countdown component, we first determine which end is currently, and then change the top and left values of the Widget components based on that end.

    Because the setup logic of the Widget needs to be executed not only on different edge nodes, but also on different ends of each edge node, there are many scenarios, we can extract this logic as a generic script component and separately add it to the desired nodes. Here is a UIAdaptor script component we pulled out:

    @ccclass
    export default class UIAdaptor extends cc.Component {
      @property({
        type: Number.tooltip: `app: ${PlatformTypes.APP}, iPad: ${PlatformTypes.iPad},  PC : ${PlatformTypes. PC }, iPhoneX: ${PlatformTypes.iPhoneX}, AndroidiPad: ${PlatformTypes.AndroidiPad}`,})platform: PlatformTypes = -1;
    
      @property({
        type: [Number].tooltip: 'left, top, right, bottom, horizontalCenter, verticalCenter',})widgetOptions: Array<number> = [
        Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER,
        Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER,
      ];
    
      @property({
        type: Number.tooltip: `ONCE: ${WidgetAlignModes.ONCE}, ON_WINDOW_RESIZE: ${WidgetAlignModes.ON_WINDOW_RESIZE}
          ALWAYS: ${WidgetAlignModes.ALWAYS}
        `,
      })
      widgetAlignModes = WidgetAlignModes.ON_WINDOW_RESIZE;
    
      @property({
        type: [Number].tooltip: 'width, height',})size: Array<number> = [];
    
      @property({
        type: [Number].tooltip: 'x, y',})scale: Array<number> = [];
    
      @property({
        type: Number,
      })
      fontSize = -1;
    	// Adapt the Widget component of the node
      private fitWidget() {
        const widget = this.getComponent(cc.Widget);
        if(! widget) {return;
        }
        enum WidgetOptions {
          left,
          top,
          right,
          bottom,
          horizontalCenter,
          verticalCenter,
        }
        this.widgetOptions.forEach((value: number, index: number) = > {
          if (typeofvalue ! = ='number' || value >= Number.MAX_SAFE_INTEGER - 1) {
            return;
          }
          const optionType = WidgetOptions[index];
          widget[optionType] = value;
          widget[`isAlign${optionType.replace(optionType[0], optionType[0].toUpperCase())}`] = true;
        });
    
        widget.alignMode = this.widgetAlignModes;
      }
    	// Adjust the size of the node
      private fitSize() {
        const [width, height] = this.size;
        this.node.setContentSize(
          width,
          height
        );
      }
    	// Adjust the font size of the node
      private fitFontSize() {
        const label = this.getComponent(cc.Label);
        label.fontSize = this.fontSize;
      }
    	// Adjust the scaling of nodes
      private fitScale() {
        const { scaleX: originalScaleX, scaleY: originalScaleY } = this.node;
        const [x, y = x] = this.scale;
        this.node.setScale(originalScaleX * x, originalScaleY * y);
      }
    
      private fit() {
        if (this.size.length) {
          this.fitSize();
        }
    
        if (this.fontSize >= 0) {
          this.fitFontSize();
        }
    
        if (this.scale.length) {
          this.fitScale();
        }
    
        this.fitWidget();
    
    
        const widget = this.getComponent(cc.Widget);
        if(widget) { widget.updateAlignment(); }}start() {
        // Get the end where the current scenario resides
        const platform = getPlatformType();
        if (platform === this.platform) {
          this.fit(); }}}Copy the code

    This script component can not only set the Widget component of the node, but also set the node’s size, scale, fontSize, etc. Using this script component, you can set the distance, size, scale, etc. Let’s go back to the countdown node. What does it look like using the UIAdaptor script component?

    We added a UIAdaptor script component to the countdown node for each end, since the distance of the countdown node was different on each end. Fill in different Platform (PC, iPhoneX, iPad, Android iPad) and Widget Options (left, Top, Right, Bottom, horizontalCenter, verticalCenter). When the scene is loaded and executed to these scripts, it will judge whether the end of the current scene is consistent with the end selected by UIAdaptor one by one. If not, it will skip, if it is, it will be set according to the value filled in. In this way we can achieve multi-end adaptation of edge nodes without code.

Option background image nine grid cutting

  1. Why do I need to manipulate the background image?

    The design students will present a cut of the options in the first design draft, which looks like this:

    What if we didn’t do anything with the image, just dropped it into the options and let it automatically stretch according to the length of the options?

    It can be seen that, in the case of the large length of the option, the background picture of the option presents a very strange shape, with the four rounded corners stretched out incongruously. If the design students see it, they will inevitably make fun of it. We wanted the four rounded corners to remain pristine no matter how long the options were, and this raw image was clearly not what we wanted.

  2. What is nine grid cut?

    To enable developers to create stretchable UI images, Cocos Creator provides a nine-grid cutting method for image resources. If we select the image resource in Cocos Creator for editing, a popup window for editing the image will appear:

    Here we can move the green line to cut the image resource into nine parts. The stretching rules for each part are as follows:

    We cut the four rounded corners of the option button into the four corners of the nine grid, so that no matter how the option is stretched, the four rounded corners remain pristine and do not scale or stretch as the length of the option changes. Let’s have a look at the image resource after nine grid cutting effect:

    So, does it look a lot more coordinated?

  3. Notes for nine grid cutting

    Generally speaking, the double, double and triple of the cut diagram will be provided when the design students provide the cut diagram. When selecting the option button to cut the diagram, it is best to select the double diagram whose size is similar to that of the button under the design resolution. Assuming that the height of the button cut graph is 44, 88, and 132, and the height of the button is 88 at the design resolution, we will select the double of the button cut graph.

    If you choose one-fold diagram for nine-grid cutting, because the size of one-fold diagram is too small, the four rounded corners will also become very small, as shown below:

    If the triple graph is selected to do the nine-grid cutting, the four rounded corners will also become large due to the large size of the triple graph, as shown below:

Refer to the article

  1. Multi-resolution adaptation scheme
  2. Make stretchable UI images