Screen Adaptation for Scenes and UIOn this pageScreen Adaptation for Scenes and UI In game development, screen adaptation is a crucial feature. No matter what device or window size the player uses, they want to have the best visual experience. This tutorial will guide you step by step on how to implement screen adaptation for game scenes and UI in the Dora SSR engine. 1. Understanding Screen Adaptation On different devices, screen sizes and resolutions vary. Without adaptation, the game may not display properly on certain devices, or the elements may be misaligned. The goal of screen adaptation is to ensure the game content displays correctly across various screens while maintaining a good user experience. 2. Implementing Screen Adaptation for Game Scenes Screen adaptation for game scenes primarily involves adjusting the camera's view based on the window size, ensuring that the scene's content is fully displayed on the screen. 2.1 Define Design Dimensions First, define the design dimensions for the game. This is the reference size on which you base your design and development. LuaTealTypeScriptYueScriptlocal DesignSceneHeight <const> = 1024local DesignSceneHeight <const> = 1024const DesignSceneHeight = 1024;const DesignSceneHeight = 1024 Here, we set the design height to 1024. This means we design the scene with a height of 1024. When adapting, we aim to ensure the vertical scene area is fully displayed, and any horizontal areas exceeding the screen will be cropped or centered if smaller than the screen area. 2.2 Adjust Camera Zoom Next, adjust the camera's zoom based on the current window size. LuaTealTypeScriptYueScriptlocal Director <const> = require("Director")local View <const> = require("View")local function updateViewSize() Director.currentCamera.zoom = View.size.height / DesignSceneHeightendlocal Director <const> = require("Director")local type Camera2D = require("Camera2D")local View <const> = require("View")local function updateViewSize() local camera = Director.currentCamera as Camera2D.Type camera.zoom = View.size.height / DesignSceneHeightendimport { Director, View, TypeName, tolua } from 'Dora';const updateViewSize = () => { const camera = tolua.cast(Director.currentCamera, TypeName.Camera2D); if (camera) { camera.zoom = View.size.height / DesignSceneHeight; }};_ENV = DoraupdateViewSize = -> Director.currentCamera.zoom = View.size.height / DesignSceneHeight Explanation: Director.currentCamera: The current scene's camera object. zoom: The camera's zoom property, which affects the field of view. View.size.height: The actual height of the current window. By calculating View.size.height / DesignSceneHeight, we get the ratio of the actual height to the design height and set it as the camera's zoom value. 2.3 Listen for Window Size Changes To update the camera's zoom whenever the window size changes (e.g., when the user resizes the window or rotates the device), we need to listen for the application's size change events. LuaTealTypeScriptYueScriptupdateViewSize() -- Call once during initializationDirector.entry:onAppChange(function(settingName) if settingName == "Size" then updateViewSize() -- Update whenever a size change occurs endend)updateViewSize() -- Call once during initializationDirector.entry:onAppChange(function(settingName: string) if settingName == "Size" then updateViewSize() -- Update whenever a size change occurs endend)updateViewSize(); // Call once during initializationDirector.entry.onAppChange(settingName => { if (settingName === 'Size') { updateViewSize(); // Update whenever a size change occurs }});updateViewSize! -- Call once during initializationDirector.entry\onAppChange (settingName) -> if settingName == "Size" updateViewSize! -- Update whenever a size change occurs Explanation: Director.entry:onAppChange: Registers a listener that triggers when the application's settings change. settingName: The name of the setting that changed. When settingName is "Size", it indicates that the window size has changed, and we call updateViewSize() to update the camera's zoom. 2.4 Complete Code LuaTealTypeScriptYueScript-- Example of game scene adaptation-- Import moduleslocal DrawNode <const> = require("DrawNode")local Director <const> = require("Director")local View <const> = require("View")local Vec2 <const> = require("Vec2")-- Define design dimensionslocal DesignSceneHeight <const> = 1024-- Create the scenelocal node = DrawNode()node:drawDot(Vec2.zero, DesignSceneHeight / 2)node:addTo(Director.entry)-- Handle window size changeslocal function updateViewSize() Director.currentCamera.zoom = View.size.height / DesignSceneHeightendupdateViewSize()-- Register event callback for window size changesDirector.entry:onAppChange(function(settingName) if settingName == "Size" then updateViewSize() endend)-- Example of game scene adaptation-- Import moduleslocal DrawNode <const> = require("DrawNode")local Director <const> = require("Director")local View <const> = require("View")local Vec2 <const> = require("Vec2")local type Camera2D = require("Camera2D")-- Define design dimensionslocal DesignSceneHeight <const> = 1024-- Create the scenelocal node = DrawNode()node:drawDot(Vec2.zero, DesignSceneHeight / 2)node:addTo(Director.entry)-- Handle window size changeslocal function updateViewSize() local camera = Director.currentCamera as Camera2D.Type camera.zoom = View.size.height / DesignSceneHeightendupdateViewSize()-- Register event callback for window size changesDirector.entry:onAppChange(function(settingName: string) if settingName == "Size" then updateViewSize() endend)// Example of game scene adaptation// Import modulesimport { DrawNode, Director, View, Vec2, TypeName } from 'Dora';// Define design dimensionsconst DesignSceneHeight = 1024;// Create the sceneconst node = DrawNode();node.drawDot(Vec2.zero, DesignSceneHeight / 2);node.addTo(Director.entry);// Handle window size changesconst updateViewSize = () => { const camera = tolua.cast(Director.currentCamera, TypeName.Camera2D); if (camera) { camera.zoom = View.size.height / DesignSceneHeight; }};updateViewSize();// Register event callback for window size changesDirector.entry.onAppChange(settingName => { if (settingName === 'Size') { updateViewSize(); }});-- Example of game scene adaptation-- Import modules_ENV = Dora-- Define design dimensionsconst DesignSceneHeight = 1024-- Create the sceneconst node = DrawNode!node.drawDot Vec2.zero, DesignSceneHeight / 2node.addTo Director.entry-- Handle window size changesupdateViewSize! -- Call once during initializationDirector.entry\onAppChange (settingName) -> if settingName == "Size" updateViewSize! -- Update whenever a size change occurs 3. Implementing Screen Adaptation for Game UI Screen adaptation for game UI involves ensuring that interface elements are properly arranged on screens of different sizes. We will use the Dora SSR integrated Yoga Layout Engine, which defines element layout relationships using CSS-like Flexbox layout syntax. 3.1 Import Yoga Layout Engine Yoga is a cross-platform layout engine that supports Flexbox-based layouts. It allows us to define layouts using familiar CSS syntax. LuaTealTypeScriptYueScriptlocal AlignNode <const> = require("AlignNode")local AlignNode <const> = require("AlignNode")import { AlignNode } from 'Dora';import "AlignNode" AlignNode is the layout node type in Dora SSR that supports layout functions. 3.2 Using CSS Flex Layout A recommended game to learn CSS Flex LayoutTo quickly learn Flex layout, you can try Flexbox Froggy, an online game: https://flexboxfroggy.com/ First, create a root node and set its layout properties. LuaTealTypeScriptYueScriptlocal root = AlignNode(true)root:css("justify-content: center; align-items: center")root:addTo(Director.ui)local root = AlignNode(true)root:css("justify-content: center; align-items: center")root:addTo(Director.ui)const root = AlignNode(true);root.css("justify-content: center; align-items: center");root.addTo(Director.ui);root = with AlignNode true \css "justify-content: center; align-items: center" \addTo Director.ui Explanation: AlignNode(true): Creates a layout-supporting node. true indicates this node is the layout container for the window's root. css(...): Applies CSS layout styles to the node. justify-content: center: Horizontally center the child nodes. align-items: center: Vertically center the child nodes. root:addTo(Director.ui): Adds the root node to the built-in UI layer of the engine. Next, create a child node and set its size relative to the parent node's percentage. LuaTealTypeScriptYueScriptlocal centerNode = AlignNode()centerNode:css("width: 60%; height: 60%")centerNode:addTo(root)local centerNode = AlignNode()centerNode:css("width: 60%; height: 60%")centerNode:addTo(root)const centerNode = AlignNode();centerNode.css("width: 60%; height: 60%");centerNode.addTo(root);centerNode = with AlignNode \css "width: 60%; height: 60%" \addTo root Explanation: width: 60%: Sets the width to 60% of the parent node's width. height: 60%: Sets the height to 60% of the parent node's height. 3.3 Adjust Elements to Fit the Layout Add actual UI elements (e.g., sprite objects displaying images) to the layout node and adjust their properties after the layout is complete. LuaTealTypeScriptYueScriptlocal sprite = Sprite("Image/logo.png")sprite:addTo(centerNode)local sprite = Sprite("Image/logo.png")sprite:addTo(centerNode)const sprite = Sprite("Image/logo.png");sprite.addTo(centerNode);sprite = with Sprite "Image/logo.png" \addTo centerNode To update the sprite's position and size after the layout adjusts for screen adaptation, we need to listen for the layout completion event. LuaTealTypeScriptYueScriptcenterNode:onAlignLayout(function(width, height) -- Adjust sprite display parameters to match the adapted result sprite.position = Vec2(width / 2, height / 2) sprite.size = Size(width, height)end)centerNode:onAlignLayout(function(width: number, height: number) -- Adjust sprite display parameters to match the adapted result sprite.position = Vec2(width / 2, height / 2) sprite.size = Size(width, height)end)centerNode.onAlignLayout((width, height) => { // Adjust sprite display parameters to match the adapted result sprite.position = Vec2(width / 2, height / 2); sprite.size = Size(width, height);});centerNode\onAlignLayout (width, height) -> -- Adjust sprite display parameters to match the adapted result sprite.position = Vec2 width / 2, height / 2 sprite.size = Size width, height Explanation: onAlignLayout: Callback triggered when layout calculation is completed. width and height: The calculated width and height of the layout node. In the callback, we set the sprite's position to the center of the node and its size to match the node's size, ensuring the sprite displays in line with the layout result. 3.3 Complete Code LuaTealTypeScriptTSXYueScript-- Adaptive Game UI-- Import moduleslocal AlignNode <const> = require("AlignNode")local Director <const> = require("Director")local Sprite <const> = require("Sprite")local Vec2 <const> = require("Vec2")local Size <const> = require("Size")-- Create root node for screen adaptationlocal root = AlignNode(true)root:css("justify-content: center; align-items: center")root:addTo(Director.ui)-- Create child node for centered layoutlocal centerNode = AlignNode()centerNode:css("width: 60%; height: 60%")centerNode:addTo(root)-- Create sprite object for display on the child nodelocal sprite = Sprite("Image/logo.png")sprite:addTo(centerNode)-- Register adaptation callback to update the display object's parameters to match the layout resultcenterNode:onAlignLayout(function(width, height) sprite.position = Vec2(width / 2, height / 2) sprite.size = Size(width, height)end)-- Adaptive Game UI-- Import moduleslocal AlignNode <const> = require("AlignNode")local Director <const> = require("Director")local Sprite <const> = require("Sprite")local Vec2 <const> = require("Vec2")local Size <const> = require("Size")-- Create root node for screen adaptationlocal root = AlignNode(true)root:css("justify-content: center; align-items: center")root:addTo(Director.ui)-- Create child node for centered layoutlocal centerNode = AlignNode()centerNode:css("width: 60%; height: 60%")centerNode:addTo(root)-- Create sprite object for display on the child nodelocal sprite = Sprite("Image/logo.png")sprite:addTo(centerNode)-- Register adaptation callback to update the display object's parameters to match the layout resultcenterNode:onAlignLayout(function(width: number, height: number) sprite.position = Vec2(width / 2, height / 2) sprite.size = Size(width, height)end)// Adaptive Game UI// Import modulesimport { AlignNode, Director, Sprite, Vec2, Size } from 'Dora';// Create root node for screen adaptationconst root = AlignNode(true);root.css('justify-content: center; align-items: center');root.addTo(Director.ui);// Create child node for centered layoutconst centerNode = AlignNode();centerNode.css('width: 60%; height: 60%');centerNode.addTo(root);// Create sprite object for display on the child nodeconst sprite = Sprite('Image/logo.png');sprite.addTo(centerNode);// Register adaptation callback to update the display object's parameters to match the layout resultcenterNode.onAlignLayout((width, height) => { sprite.position = Vec2(width / 2, height / 2); sprite.size = Size(width, height);});// Adaptive Game UI// Import modulesimport { React, toNode, useRef } from 'DoraX';import { Size, Sprite, Vec2, Director } from 'Dora';const sprite = useRef<Sprite.Type>();// Create root node for screen adaptationconst root = toNode( <align-node windowRoot style={{ justifyContent: 'center', alignItems: 'center' }}> <align-node style={{ width: '60%', height: '60%' }} onLayout={(width, height) => { const {current} = sprite; if (current) { current.position = Vec2(width / 2, height / 2); current.size = Size(width, height); } }}> <sprite ref={sprite} file='Image/logo.png'/> </align-node> </align-node>);root?.addTo(Director.ui);-- Adaptive Game UI-- Import modules_ENV = Dora-- Create root node for screen adaptationroot = with AlignNode true \css "justify-content: center; align-items: center" \addTo Director.ui-- Create child node for centered layoutcenterNode = with AlignNode \css "width: 60%; height: 60%" \addTo root-- Create sprite object for display on the child nodesprite = with Sprite "Image/logo.png" \addTo centerNode-- Register adaptation callback to update the display object's parameters to match the layout resultcenterNode\onAlignLayout (width, height) -> sprite.position = Vec2 width / 2, height / 2 sprite.size = Size width, height 4. Conclusion In this tutorial, you learned how to implement screen adaptation for game scenes and UI in Dora SSR: Scene adaptation: By setting design dimensions and adjusting the camera's zoom, you ensure the scene content is fully displayed. UI adaptation: Using the Yoga layout engine and CSS Flexbox layout syntax, you define element layout relationships and update their properties after layout adjustments. These methods help you create games that adapt to different screen sizes, providing players with a consistent and enjoyable experience. Next steps, you can try: Delving into more features of the Yoga Layout Engine, such as flex-direction and flex-wrap. Adding adaptive layouts to more UI elements, such as buttons and text boxes. Exploring other modules in Dora SSR to enhance your game development knowledge. I hope this tutorial was helpful and wish you success on your game development journey!