diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 796cb0fc98..0e218ea198 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -105,19 +105,19 @@ jobs:
browser: chrome
- name: Upload Diff
if: failure()
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: cypress-diff
path: e2e/diff/
- name: Upload Origin
if: failure()
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: cypress-origin
path: e2e/fixtures/originImage
- name: Upload Screenshots
if: failure()
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: cypress-screenshots
path: e2e/downloads/
diff --git a/docs/en/UI/_meta.json b/docs/en/UI/_meta.json
new file mode 100644
index 0000000000..963d63d40e
--- /dev/null
+++ b/docs/en/UI/_meta.json
@@ -0,0 +1,11 @@
+{
+ "overall": {
+ "title": "Overview"
+ },
+ "quickStart": {
+ "title": "QuickStart"
+ },
+ "system": {
+ "title": "Architecture"
+ }
+}
diff --git a/docs/en/UI/overall.md b/docs/en/UI/overall.md
new file mode 100644
index 0000000000..1dc8f574de
--- /dev/null
+++ b/docs/en/UI/overall.md
@@ -0,0 +1,26 @@
+---
+order: 0
+title: Overview
+type: UI
+label: UI
+---
+
+UI is a system used to build user interfaces. It provides a range of tools and components to help developers create interactive interface elements. Here are its key features:
+
+- **Visual Editing**: With the basic nodes and component creation capabilities in the [editor](https://galacean.antgroup.com/editor/projects), combined with the RectTool (shortcut key T), developing interactive interfaces becomes more intuitive and efficient.
+- **Rendering and Interactive Components**: Supports rendering components like Image, Text, etc., as well as basic interactive components such as Button.
+- **Transferable Opacity and Interactivity Attributes**: Through the UIGroup component, properties such as **opacity** and **interactivity** can be inherited or ignored.
+- **Events**: In addition to supporting original Pointer events, UI components also support **event bubbling** for interactions triggered by the UI.
+
+In this section, you will:
+
+- Learn how to quickly develop a UI interface:
+ - Create a [Root Canvas](/en/docs/UI/quickStart/canvas)
+ - Familiarize yourself with [UITransform](/en/docs/UI/quickStart/transform)
+ - Create [Image](/en/docs/UI/quickStart/image)
+ - Create [Text](/en/docs/UI/quickStart/text)
+ - Create [Button](/en/docs/UI/quickStart/button)
+ - Create [UIGroup](/en/docs/UI/quickStart/group)
+- Understand the [Overall Architecture and Module Management](/en/docs/UI/system) of UI
+- Learn about [UI Rendering Order](/en/docs/UI/quickStart/order)
+- Understand the [UI Event Mechanism](/en/docs/UI/quickStart/event)
diff --git a/docs/en/UI/quickStart/button.md b/docs/en/UI/quickStart/button.md
new file mode 100644
index 0000000000..20cb753101
--- /dev/null
+++ b/docs/en/UI/quickStart/button.md
@@ -0,0 +1,44 @@
+---
+order: 4
+title: Button
+type: UI
+label: UI
+---
+
+`Button` can be used to create interactive buttons within a UICanvas.
+
+## Editor Usage
+
+### Add Button Node
+
+Add a `Button` node in the **[Hierarchy Panel](/docs/interface/hierarchy/)**.
+
+
+
+> If the parent or ancestor node does not have a Canvas component, a root Canvas node will be automatically added.
+
+### Set Transition
+
+In the editor, you can easily set the button's transition effects for different states.
+
+
+
+## Properties
+
+| Property Name | Description |
+| :-------------- | :-------------------------------- |
+| `transitions` | All the transition effects of the button |
+| `interactive` | Whether the button is interactive |
+
+## Methods
+
+| Method Name | Description |
+| :------------------ | :------------------------------------ |
+| `addTransition` | Add a transition effect to the button |
+| `removeTransition` | Remove a transition effect from the button |
+| `addClicked` | Add a click callback function |
+| `removeClicked` | Remove a click callback function |
+
+## Script Development
+
+
\ No newline at end of file
diff --git a/docs/en/UI/quickStart/canvas.md b/docs/en/UI/quickStart/canvas.md
new file mode 100644
index 0000000000..7bb4b12c1b
--- /dev/null
+++ b/docs/en/UI/quickStart/canvas.md
@@ -0,0 +1,62 @@
+---
+order: 0
+title: Canvas
+type: UI
+label: UI
+---
+
+The root canvas is the foundation of the UI, but not all `UICanvas` nodes are root canvases. Here’s how to create a root canvas in your scene.
+
+## Editor Usage
+
+### Add UICanvas Node
+
+Add a Canvas node in the **[Hierarchy Panel](/docs/interface/hierarchy/)**.
+
+
+
+### Set Properties
+
+Select the node that has the `Canvas` component and you can set its properties in the **[Inspector Panel](/docs/interface/inspector)**.
+
+
+
+### Root Canvas
+
+If the parent or ancestor node of the newly added canvas node already contains an active `UICanvas` component, this canvas will not have rendering or interaction functionality.
+
+
+
+## Properties
+
+| Property Name | Description |
+| :------------------------------ | :------------------------------------------------------------ |
+| `renderMode` | The rendering mode of the canvas |
+| `renderCamera` | The camera used for rendering when the canvas is in `ScreenSpaceCamera` mode |
+| `distance` | The distance of the canvas relative to the camera when in `ScreenSpaceCamera` mode |
+| `resolutionAdaptationMode` | The adaptation mode of the canvas, such as width adaptation or height adaptation |
+| `referenceResolution` | The reference resolution for size adaptation of the canvas |
+| `referenceResolutionPerUnit` | The ratio of canvas units to world space units |
+| `sortOrder` | The rendering priority of the canvas |
+
+## Script Development
+
+```typescript
+// Add camera
+const cameraEntity = root.createChild("Camera");
+const camera = cameraEntity.addComponent(Camera);
+
+// Add UICanvas
+const canvasEntity = root.createChild("canvas");
+const canvas = canvasEntity.addComponent(UICanvas);
+
+// Set renderMode to `ScreenSpaceOverlay`
+canvas.renderMode = CanvasRenderMode.ScreenSpaceOverlay;
+// Set renderMode to `ScreenSpaceCamera`
+canvas.renderMode = CanvasRenderMode.ScreenSpaceCamera;
+canvas.renderCamera = camera;
+// Set Reference Resolution
+canvas.referenceResolution.set(750, 1624);
+// Set Adaptation Mode
+canvas.resolutionAdaptationMode = ResolutionAdaptationMode.WidthAdaptation;
+```
\ No newline at end of file
diff --git a/docs/en/UI/quickStart/event.md b/docs/en/UI/quickStart/event.md
new file mode 100644
index 0000000000..0d6db41457
--- /dev/null
+++ b/docs/en/UI/quickStart/event.md
@@ -0,0 +1,40 @@
+---
+order: 3
+title: Event
+type: UI
+label: UI
+---
+
+UI events follow the engine's event system, with the additional support for event bubbling in UI components.
+
+## Bubbling
+
+The current version only supports the following bubbling flow:
+
+| Interface | Bubbles |
+| :----------------------------------------------------------- | :--------- |
+| [onPointerEnter](/apis/core/#Script-onPointerEnter) | Does not bubble |
+| [onPointerExit](/apis/core/#Script-onPointerExit) | Does not bubble |
+| [onPointerDown](/apis/core/#Script-onPointerDown) | Bubbles |
+| [onPointerUp](/apis/core/#Script-onPointerUp) | Bubbles |
+| [onPointerClick](/apis/core/#Script-onPointerClick) | Bubbles |
+| [onPointerBeginDrag](/apis/core/#Script-onPointerBeginDrag) | Bubbles |
+| [onPointerDrag](/apis/core/#Script-onPointerDrag) | Bubbles |
+| [onPointerEndDrag](/apis/core/#Script-onPointerEndDrag) | Bubbles |
+| [onPointerDrop](/apis/core/#Script-onPointerDrop) | Bubbles |
+
+As shown in the diagram below, if node C triggers the `pointerup` event, the event will bubble along the path C --> B --> A --> RootCanvas.
+
+```mermaid
+stateDiagram
+ RootCanvas --> A
+ RootCanvas --> F
+ A --> B
+ A --> E
+ B --> C
+ B --> D
+```
+
+## Script Development
+
+
\ No newline at end of file
diff --git a/docs/en/UI/quickStart/group.md b/docs/en/UI/quickStart/group.md
new file mode 100644
index 0000000000..8ec0cae9b5
--- /dev/null
+++ b/docs/en/UI/quickStart/group.md
@@ -0,0 +1,28 @@
+---
+order: 5
+title: UIGroup
+type: UI
+label: UI
+---
+
+The `UIGroup` component allows you to inherit or ignore properties such as **opacity** and **interactivity**.
+
+## Editor Usage
+
+Select the node, then in the **[Inspector Panel](/docs/interface/inspector)**, click **Add Component** and choose **UIGroup**. You can control the opacity of multiple UI elements by modifying the settings.
+
+
+
+## Properties
+
+| Property Name | Description |
+| :------------------ | :--------------------------------- |
+| `alpha` | Opacity |
+| `interactive` | Whether the element is interactive |
+| `ignoreParentGroup` | Whether to ignore the settings of the parent group |
+
+> UIGroup resolves the issue where UI element properties cannot be passed from parent to child.
+
+## Script Development
+
+
\ No newline at end of file
diff --git a/docs/en/UI/quickStart/image.md b/docs/en/UI/quickStart/image.md
new file mode 100644
index 0000000000..f4851aed68
--- /dev/null
+++ b/docs/en/UI/quickStart/image.md
@@ -0,0 +1,48 @@
+---
+order: 2
+title: Image
+type: UI
+label: UI
+---
+
+The `Image` component is used to display images within a `UICanvas`.
+
+## Editor Usage
+
+### Add Image Node
+
+Add an `Image` node in the **[Hierarchy Panel](/docs/interface/hierarchy/)**.
+
+
+
+> If the parent or ancestor node does not have a Canvas component, a root canvas node will be automatically added.
+
+### Set Sprite
+
+The content displayed by the `Image` depends on the selected [Sprite asset](). Select the node with the `Image` component, and in the **[Inspector Panel](/docs/interface/inspector)**, choose the corresponding sprite asset in the Sprite property to change the displayed content.
+
+
+
+### Modify Draw Mode
+
+The `Image` component currently provides three draw modes: Normal, Nine-Patch, and Tiled (the default is Normal). You can visually feel the rendering differences between modes by modifying the width and height in each mode.
+
+
+
+### Adjust Size
+
+For adjusting the size of UI elements, refer to [Quickly Adjust UI Element Size](/docs/UI/quickStart/transform).
+
+## Properties
+
+| Property Name | Description |
+| :----------------- | :-------------------------------------------------- |
+| `sprite` | The sprite to render |
+| `color` | The color of the sprite |
+| `drawMode` | The draw mode, supports Normal, Nine-Patch, and Tiled modes |
+| `raycastEnabled` | Whether the image can be detected by raycasting |
+| `raycastPadding` | Custom padding for raycasting, representing the distance from the edges of the collision area. This is a normalized value, where X, Y, Z, and W represent the distances from the left, bottom, right, and top edges respectively. |
+
+## Script Development
+
+
\ No newline at end of file
diff --git a/docs/en/UI/quickStart/order.md b/docs/en/UI/quickStart/order.md
new file mode 100644
index 0000000000..803b97bc10
--- /dev/null
+++ b/docs/en/UI/quickStart/order.md
@@ -0,0 +1,61 @@
+---
+order: 6
+title: Rendering Order
+type: UI
+label: UI
+---
+
+The rendering order of UI components follows two rules:
+
+- Different `UICanvas` instances follow a specific rendering order based on their `RendererMode` type.
+- `UIRenderer` components under a `UICanvas` are rendered according to a **depth-first** order, from parent to child, and from left to right.
+
+## UICanvas
+
+Assume the current runtime:
+- There is a scene `Scene`
+- The scene `Scene` contains two cameras, `Camera1` and `Camera2`
+- The scene `Scene` contains three canvases:
+ - `Canvas1` with `WorldSpace` render mode
+ - `Canvas2` with `ScreenSpace-Overlay` render mode
+ - `Canvas3` with `ScreenSpace-Camera` render mode, using `Camera1` as the render camera
+
+```mermaid
+journey
+ title Scene Rendering Cycle
+ section Camera1.render
+ Canvas1.render: 5
+ Canvas3.render: 5
+ section Camera2.render
+ Canvas1.render: 5
+ section Ending
+ Canvas2.render: 5
+```
+
+It's important to note:
+- Canvases with `ScreenSpace-Camera` render mode will only render with their corresponding camera, and they follow the general camera clipping rules, just like canvases with `ScreenSpace-Overlay` render mode.
+- Canvases with `ScreenSpace-Overlay` render mode can still be rendered without a camera.
+- Within the same camera, the rendering order of `UICanvas` follows these rules: canvases in the overlay mode have their rendering order determined only by `sortOrder`.
+
+```mermaid
+flowchart TD
+ A[Sort rendering data] --> B{canvas.sortOrder}
+ B -->|Not equal| C[Return comparison result]
+ B -->|Equal| D{Canvas and camera distance}
+ D -->|Not equal| E[Return comparison result]
+ D -->|Equal| F[Return comparison result]
+```
+
+## UIRenderer Rendering Order
+
+```mermaid
+stateDiagram
+ RootCanvas --> A
+ RootCanvas --> F
+ A --> B
+ A --> E
+ B --> C
+ B --> D
+```
+
+As shown in the diagram above, the rendering order under the root canvas follows A -> B -> C -> D -> E -> F. It is important to note that setting `UIRenderer.priority` does not change its rendering order.
\ No newline at end of file
diff --git a/docs/en/UI/quickStart/text.md b/docs/en/UI/quickStart/text.md
new file mode 100644
index 0000000000..8a30a56a37
--- /dev/null
+++ b/docs/en/UI/quickStart/text.md
@@ -0,0 +1,60 @@
+---
+order: 3
+title: Text
+type: UI
+label: UI
+---
+
+The `Text` component is used to display text within a `UICanvas`.
+
+## Editor Usage
+
+### Add Text Node
+
+Add a `Text` node in the **[Hierarchy Panel](/docs/interface/hierarchy/)**.
+
+
+
+> If the parent or ancestor node does not have a Canvas component, a root canvas node will be automatically added.
+
+### Set Text Content
+
+Select the node with the `Text` component, and in the **[Inspector Panel](/docs/interface/inspector)**, modify the `text` property to change the content of the `Text` element.
+
+
+
+### Set Font
+
+Select the node with the `Text` component, and in the **[Inspector Panel](/docs/interface/inspector)**, modify the `font` property to change the font type of the `Text` element.
+
+
+
+### Modify Font Size
+
+The `Text` component can modify the rendering size by adjusting the `FontSize`.
+
+
+
+> Changing the `size` in `UITransform` will not affect the rendering size of `Text`.
+
+## Properties
+
+| Property Name | Description |
+| :------------------ | :------------------------------------------------------------------------------------------------ |
+| `Text` | The text to be displayed |
+| `Color` | The color of the text |
+| `FontSize` | The font size of the text |
+| `Font` | Custom font for the text |
+| `LineSpacing` | The line spacing between text lines |
+| `FontStyle` | Font style settings: whether to apply bold or italic styles |
+| `HorizontalAlignment` | Horizontal alignment of the text, with options: Left/Center/Right |
+| `VerticalAlignment` | Vertical alignment of the text, with options: Top/Center/Bottom |
+| `EnableWrapping` | Whether to enable word wrapping; when enabled, text will wrap based on the set width. If width is set to 0, the text will not render |
+| `OverflowMode` | How to handle overflow when the text exceeds the set height: options are Overflow/Truncate. Overflow means the text will overflow and display; Truncate means only the content within the set height will be displayed, depending on the vertical alignment of the text. |
+| `Mask Interaction` | Mask type for text, specifying whether the text needs masking, and whether the content inside or outside the mask should be shown |
+| `Mask Layer` | The mask layer for the text, used for matching with SpriteMask. Default is Everything, meaning it can interact with any SpriteMask |
+| `priority` | Rendering priority; the lower the value, the higher the priority, meaning it will be rendered first |
+
+## Script Development
+
+
\ No newline at end of file
diff --git a/docs/en/UI/quickStart/transform.md b/docs/en/UI/quickStart/transform.md
new file mode 100644
index 0000000000..95c05f739c
--- /dev/null
+++ b/docs/en/UI/quickStart/transform.md
@@ -0,0 +1,34 @@
+---
+order: 1
+title: UITransform
+type: UI
+label: UI
+---
+
+The `UITransform` component is specifically designed to represent the size and position of UI elements. It inherits from the [Transform](/apis/core/#Transform) component.
+
+## Editor Usage
+
+When a node with a UI component is added, the `UITransform` component will automatically be added (replacing the previous [Transform](/apis/core/#Transform) component). In the editor, you can select the node and use the `RectTool` (shortcut key `T`) to quickly adjust properties, or you can set precise properties in the **[Inspector Panel](/docs/interface/inspector)**.
+
+
+
+> When the main canvas's render mode is `Screen`, the editor will prohibit modifications to its `transform` to avoid screen adaptation issues. Therefore, in scripts, **developers should avoid modifying the `transform` properties of the main canvas in screen space.**
+
+## Properties
+
+| Property Name | Description |
+| :------------ | :--------------------------------------------------------------------------- |
+| `size` | The size of the UI element. `x` represents width, and `y` represents height. The default is `100` for both. |
+| `pivot` | The anchor point of the UI element. It is a normalized 2D vector with the origin at the bottom-left corner, with the default value being the center (0.5, 0.5). |
+
+## Script Usage
+
+```typescript
+// Add UICanvas
+const canvasEntity = root.createChild("canvas");
+const canvas = canvasEntity.addComponent(UICanvas);
+const imageEntity = canvasEntity.create("Image");
+(imageEntity.transform).size.set(200, 200);
+(imageEntity.transform).pivot.set(0, 0);
+```
\ No newline at end of file
diff --git a/docs/en/UI/system.md b/docs/en/UI/system.md
new file mode 100644
index 0000000000..f6fe6df877
--- /dev/null
+++ b/docs/en/UI/system.md
@@ -0,0 +1,108 @@
+---
+order: 2
+title: Architecture
+type: UI
+label: UI
+---
+
+## System Design
+
+```mermaid
+---
+title: UI System
+---
+classDiagram
+ Component <|-- Transform
+ Script <|-- Component
+ Transform <|-- UITransform
+ Component <|-- Renderer
+ Renderer <|-- UIRenderer
+ UIRenderer <|-- Image
+ UIRenderer <|-- Text
+ UICanvas <|-- Component
+ UIInteractive <|-- Script
+ Button <|-- UIInteractive
+ UIGroup <|-- Component
+
+ class Script {
+ +void onUpdate()
+ }
+
+ class Transform{
+ +Vector3 position
+ +Vector3 rotation
+ +Vector3 scale
+ ...
+ }
+
+ class UITransform{
+ <>
+ +Vector2 size
+ +Vector2 pivot
+ }
+
+ class Renderer{
+ +BoundingBox bounds
+ +Material getMaterial()
+ +Material setMaterial()
+ }
+
+ class UIRenderer{
+ <>
+ +Color color
+ +boolean raycastEnabled
+ +vec4 raycastPadding
+ }
+
+ class Image{
+ <>
+ +Sprite sprite
+ +SpriteDrawMode drawMode
+ ...
+ }
+
+ class Text{
+ <>
+ +string text
+ +Font font
+ ...
+ }
+
+ class UICanvas {
+ <>
+ +CanvasRenderMode renderMode
+ +Camera renderCamera
+ ...
+ }
+
+ class UIInteractive {
+ <>
+ +boolean interactive
+ +Transition transitions
+ ...
+ }
+
+ class Button {
+ <>
+ +void addClicked()
+ +void removeClicked()
+ ...
+ }
+
+ class UIGroup {
+ <>
+ +boolean interactive
+ +number alpha
+ +boolean ignoreParentGroup
+ }
+```
+
+## Module Management
+
+| Package | Description | Related Documentation |
+| :--------------------------------------------------------------------- | :------------- | ----------------------- |
+| [@galacean/engine-ui](https://www.npmjs.com/package/@galacean/engine-xr) | Core architecture logic | [API](/apis/galacean) |
+
+> `@galacean/engine-ui` is a dependency that must be included to implement **UI**.
+
+> The [version dependency rules](/docs/basics/version/#version-dependency) must be followed, meaning the version of `@galacean/engine-ui` should match the version of `@galacean/engine`.
\ No newline at end of file
diff --git a/docs/en/_meta.json b/docs/en/_meta.json
index c4284d4e74..6706832105 100644
--- a/docs/en/_meta.json
+++ b/docs/en/_meta.json
@@ -47,6 +47,12 @@
"collapse": true
}
},
+ "UI": {
+ "title": "UI",
+ "theme": {
+ "collapse": true
+ }
+ },
"xr": {
"title": "XR",
"theme": {
diff --git a/docs/en/basics/overview.md b/docs/en/basics/overview.md
index 8e440e88db..b8c02d2db5 100644
--- a/docs/en/basics/overview.md
+++ b/docs/en/basics/overview.md
@@ -35,7 +35,7 @@ Includes the following sub-packages:
| [@galacean/engine](https://www.npmjs.com/package/@galacean/engine) | Core architecture logic and core functionalities | [API](/apis/galacean) |
| [@galacean/engine-physics-lite](https://www.npmjs.com/package/@galacean/engine-physics-lite) | Lightweight physics engine | [Doc](/en/docs/physics/overall) |
| [@galacean/engine-physics-physx](https://www.npmjs.com/package/@galacean/engine-physics-physx) | Full-featured physics engine | [Doc](/en/docs/physics/overall) |
-| [@galacean/engine-shader-lab](https://www.npmjs.com/package/@galacean/engine-shader-lab) | Galacean Shader compiler | [Doc](/en/docs/graphics/shader/lab) |
+| [@galacean/engine-shaderlab](https://www.npmjs.com/package/@galacean/engine-shaderlab) | Galacean Shader compiler | [Doc](/en/docs/graphics/shader/lab) |
| [@galacean/engine-xr](https://www.npmjs.com/package/@galacean/engine-xr) | XR logic package | [Doc](/en/docs/xr/overall) |
| [@galacean/engine-xr-webxr](https://www.npmjs.com/package/@galacean/engine-xr-webxr) | WebXR backend | [Doc](/en/docs/xr/overall) |
diff --git a/docs/en/graphics/2D/lottie.md b/docs/en/graphics/2D/lottie.md
index ea52ea830b..20400c634f 100644
--- a/docs/en/graphics/2D/lottie.md
+++ b/docs/en/graphics/2D/lottie.md
@@ -145,7 +145,8 @@ Thanks to the unified architecture of the Galacean Engine 2D/3D engine, 3D trans
| Engine Version | Lottie Version |
| :--- | :--- |
| 1.2.x | 1.1.0-beta.0 |
-| 1.3.x | 1.1.0-beta.0 |
+| 1.3.x | engine-1.3 |
+| 1.4.x | engine-1.4 |
## Performance Recommendations
diff --git a/docs/zh/UI/_meta.json b/docs/zh/UI/_meta.json
new file mode 100644
index 0000000000..522ed42a57
--- /dev/null
+++ b/docs/zh/UI/_meta.json
@@ -0,0 +1,11 @@
+{
+ "overall": {
+ "title": "总览"
+ },
+ "quickStart": {
+ "title": "快速上手"
+ },
+ "system": {
+ "title": "架构"
+ }
+}
diff --git a/docs/zh/UI/overall.md b/docs/zh/UI/overall.md
new file mode 100644
index 0000000000..763bf8ab82
--- /dev/null
+++ b/docs/zh/UI/overall.md
@@ -0,0 +1,26 @@
+---
+order: 0
+title: 总览
+type: UI
+label: UI
+---
+
+UI 是用于构建用户界面的系统,它提供了一系列工具和组件方便开发者搭建交互界面元素。以下是它的主要能力:
+
+- 可视化编辑:通过[编辑器](https://galacean.antgroup.com/editor/projects)中的基础节点与组件创建能力,搭配 `RectTool` (快捷键 `T` ),让交互界面的开发更加直观高效。
+- 渲染与交互组件:支持 `Image`,`Text` 等渲染组件,以及基础的交互组件,如 `Button`
+- 可传递的透明度与交互属性:通过 `UIGroup` 组件,可以继承或忽略**透明度**,**是否可交互**等属性
+- 事件:在兼容原有的 Pointer 事件的基础上,UI 组件触发的交互事件还支持**冒泡传递**。
+
+在本章节,您可以:
+
+- 学会如何快速开发 UI 界面:
+ - 创建 [根画布](/docs/UI/quickStart/canvas)
+ - 熟悉 [UITransform](/docs/UI/quickStart/transform)
+ - 创建 [Image](/docs/UI/quickStart/image)
+ - 创建 [Text](/docs/UI/quickStart/text)
+ - 创建 [Button](/docs/UI/quickStart/button)
+ - 创建 [UIGroup](/docs/UI/quickStart/group)
+- 了解 [UI 的整体架构与模块管理](/docs/UI/system)
+- 了解 [UI 的渲染顺序](/docs/UI/quickStart/order)
+- 了解 [UI 的事件机制](/docs/UI/quickStart/event)
diff --git a/docs/zh/UI/quickStart/button.md b/docs/zh/UI/quickStart/button.md
new file mode 100644
index 0000000000..50444dbe5c
--- /dev/null
+++ b/docs/zh/UI/quickStart/button.md
@@ -0,0 +1,44 @@
+---
+order: 4
+title: 创建按钮
+type: UI
+label: UI
+---
+
+`Button` 可以在 UICanvas 中构建交互按钮。
+
+## 编辑器使用
+
+### 添加 Button 节点
+
+在 **[层级面板](/docs/interface/hierarchy/)** 添加 `Button` 节点
+
+
+
+> 若父亲或祖先节点没有画布组件,会自动添加上根画布节点。
+
+### 设置 Transition
+
+在编辑器中可以方便地设置按钮在不同状态下的过渡表现
+
+
+
+## 属性
+
+| 属性名 | 描述 |
+| :------------ | :----------------- |
+| `transitions` | 按钮的所有过渡表现 |
+| `interactive` | 按钮是否可交互 |
+
+## 方法
+
+| 方法名 | 描述 |
+| :----------------- | :------------------- |
+| `addTransition` | 添加一个按钮过渡表现 |
+| `removeTransition` | 移除某个按钮过渡表现 |
+| `addClicked` | 添加一个点击回调函数 |
+| `removeClicked` | 移除某个点击回调函数 |
+
+## 脚本开发
+
+
diff --git a/docs/zh/UI/quickStart/canvas.md b/docs/zh/UI/quickStart/canvas.md
new file mode 100644
index 0000000000..0172440f7a
--- /dev/null
+++ b/docs/zh/UI/quickStart/canvas.md
@@ -0,0 +1,62 @@
+---
+order: 0
+title: 创建根画布
+type: UI
+label: UI
+---
+
+根画布是 UI 的基础,但不是所有的 `UICanvas` 都是根画布,接下来为大家演示如何在场景中创建根画布。
+
+## 编辑器使用
+
+### 添加 UICanvas 节点
+
+在 **[层级面板](/docs/interface/hierarchy/)** 添加 Canvas 节点
+
+
+
+### 设置属性
+
+选中添加了 `Canvas` 组件的节点,可以在 **[检查器面板](/docs/interface/inspector)** 设置相关的属性
+
+
+
+### 根画布
+
+如果新添加的画布节点的父亲或祖先节点已经包含激活的 `UICanvas` 组件,那么此画布不包含任何渲染与交互的功能。
+
+
+
+## 属性
+
+| 属性名 | 描述 |
+| :--------------------------- | :------------------------------------------------------------ |
+| `renderMode` | 画布的渲染模式 |
+| `renderCamera` | 当画布设置为 `ScreenSpaceCamera` 模式时,对应的渲染相机 |
+| `distance` | 当画布设置为 `ScreenSpaceCamera` 模式时,画布相较于相机的距离 |
+| `resolutionAdaptationMode` | 画布的适配模式,可以按需选择宽度适配或者高度适配等模式 |
+| `referenceResolution` | 画布在做尺寸适配时,依据的设计分辨率 |
+| `referenceResolutionPerUnit` | 画布中的单位与世界空间中单位的比例关系 |
+| `sortOrder` | 画布的渲染排序优先级 |
+
+## 脚本开发
+
+```typescript
+// Add camera
+const cameraEntity = root.createChild("Camera");
+const camera = cameraEntity.addComponent(Camera);
+
+// Add UICanvas
+const canvasEntity = root.createChild("canvas");
+const canvas = canvasEntity.addComponent(UICanvas);
+
+// Set renderMode to `ScreenSpaceOverlay`
+canvas.renderMode = CanvasRenderMode.ScreenSpaceOverlay;
+// Set renderMode to `ScreenSpaceCamera`
+canvas.renderMode = CanvasRenderMode.ScreenSpaceCamera;
+canvas.renderCamera = camera;
+// Set Reference Resolution
+canvas.referenceResolution.set(750, 1624);
+// Set Adaptation Mode
+canvas.resolutionAdaptationMode = ResolutionAdaptationMode.WidthAdaptation;
+```
diff --git a/docs/zh/UI/quickStart/event.md b/docs/zh/UI/quickStart/event.md
new file mode 100644
index 0000000000..fd3ab23243
--- /dev/null
+++ b/docs/zh/UI/quickStart/event.md
@@ -0,0 +1,40 @@
+---
+order: 3
+title: 事件系统
+type: UI
+label: UI
+---
+
+UI 事件遵循引擎的事件系统,在此基础之上,UI 组件派发的事件还额外支持冒泡机制。
+
+## 冒泡
+
+当前版本仅支持冒泡流程:
+
+| 接口 | 是否冒泡率 |
+| :---------------------------------------------------------- | :--------- |
+| [onPointerEnter](/apis/core/#Script-onPointerEnter) | 不冒泡 |
+| [onPointerExit](/apis/core/#Script-onPointerExit) | 不冒泡 |
+| [onPointerDown](/apis/core/#Script-onPointerDown) | 冒泡 |
+| [onPointerUp](/apis/core/#Script-onPointerUp) | 冒泡 |
+| [onPointerClick](/apis/core/#Script-onPointerClick) | 冒泡 |
+| [onPointerBeginDrag](/apis/core/#Script-onPointerBeginDrag) | 冒泡 |
+| [onPointerDrag](/apis/core/#Script-onPointerDrag) | 冒泡 |
+| [onPointerEndDrag](/apis/core/#Script-onPointerEndDrag) | 冒泡 |
+| [onPointerDrop](/apis/core/#Script-onPointerDrop) | 冒泡 |
+
+如下图所示,如果 C 节点触发 `pointerup` 事件, 将会沿着 C --> B --> A --> RootCanvas 路径一路派发此事件。
+
+```mermaid
+stateDiagram
+ RootCanvas --> A
+ RootCanvas --> F
+ A --> B
+ A --> E
+ B --> C
+ B --> D
+```
+
+## 脚本开发
+
+
diff --git a/docs/zh/UI/quickStart/group.md b/docs/zh/UI/quickStart/group.md
new file mode 100644
index 0000000000..08a51af701
--- /dev/null
+++ b/docs/zh/UI/quickStart/group.md
@@ -0,0 +1,28 @@
+---
+order: 5
+title: UIGroup
+type: UI
+label: UI
+---
+
+通过 `UIGroup` 组件,可以继承或忽略**透明度**,**是否可交互**等属性。
+
+## 编辑器使用
+
+选中节点,在 **[检查器面板](/docs/interface/inspector)** 点击 **添加组件** 并选择 **UIGroup**,即可通过修改设置控制多个 UI 元素的透明度。
+
+
+
+## 属性
+
+| 属性名 | 描述 |
+| :------------------ | :------------------------ |
+| `alpha` | 透明度 |
+| `interactive` | 是否可交互 |
+| `ignoreParentGroup` | 是否忽略上层 Group 的设置 |
+
+> UIGroup 解决了 UI 元素的属性无法由父传递给子的问题。
+
+## 脚本开发
+
+
diff --git a/docs/zh/UI/quickStart/image.md b/docs/zh/UI/quickStart/image.md
new file mode 100644
index 0000000000..18528a6fc6
--- /dev/null
+++ b/docs/zh/UI/quickStart/image.md
@@ -0,0 +1,48 @@
+---
+order: 2
+title: 创建图片
+type: UI
+label: UI
+---
+
+`Image` 组件用于在 `UICanvas` 中显示图片。
+
+## 编辑器使用
+
+### 添加 Image 节点
+
+在 **[层级面板](/docs/interface/hierarchy/)** 添加 `Image` 节点
+
+
+
+> 若父亲或祖先节点没有画布组件,会自动添加上根画布节点。
+
+### 设置 Sprite
+
+Image 的显示内容取决于设置的 [Sprite 资产]() ,选中添加了 `Image` 组件的节点,在 **[检查器面板](/docs/interface/inspector)** 的 Sprite 属性选择对应的精灵资产即可替换显示内容。
+
+
+
+### 修改渲染模式
+
+`Image` 目前提供三种绘制模式,分别是普通绘制,九宫绘制与平铺绘制(默认为普通绘制),在不同的绘制模式下,修改绘制宽高可以直观地感受到各种模式之间的渲染差异。
+
+
+
+### 调整尺寸
+
+请参照[快速调整 UI 元素的尺寸](/docs/UI/quickStart/transform)
+
+## 属性
+
+| 属性名 | 描述 |
+| :-- | :-- |
+| `sprite` | 渲染的精灵 |
+| `color` | 精灵颜色 |
+| `drawMode` | 绘制模式,支持普通,九宫和平铺绘制模式 |
+| `raycastEnabled` | 是否可以被射线检测到 |
+| `raycastPadding` | 射线检测的自定义边界与他的碰撞区域的距离,它是归一化的值并且 X,Y,Z,W 分别代表距离左下右上四条边的距离 |
+
+## 脚本开发
+
+
diff --git a/docs/zh/UI/quickStart/order.md b/docs/zh/UI/quickStart/order.md
new file mode 100644
index 0000000000..2e22e6a590
--- /dev/null
+++ b/docs/zh/UI/quickStart/order.md
@@ -0,0 +1,63 @@
+---
+order: 6
+title: 渲染顺序
+type: UI
+label: UI
+---
+
+UI 组件的渲染顺序遵从两个规则:
+
+- 不同的 `UICanvas` 依据 `RendererMode` 类型遵从特定的渲染顺序
+- 从属于 `UICanvas` 的 `UIRenderer` 则依据**深度优先且从父到子、从左到右**的次序进行渲染
+
+## UICanvas
+
+假设当前运行时:
+- 包含一个场景 `Scene`
+- 场景 `Scene ` 包含两个相机 `Camera1` 与 `Camera2`
+- 场景 `Scene ` 包含三个画布,其中
+ - `Canvas1` 的渲染模式为 `WorldSpace`
+ - `Canvas2` 的渲染模式为 `ScreenSpace-Overlay`
+ - `Canvas3` 的渲染模式为 `ScreenSpace-Camera`,且对应的渲染相机为 `Camera1`
+
+```mermaid
+journey
+ title Scene Rendering Cycle
+ section Camera1.render
+ Canvas1.render: 5
+ Canvas3.render: 5
+ section Camera2.render
+ Canvas1.render: 5
+ section Ending
+ Canvas2.render: 5
+```
+
+需要注意的是:
+- 渲染模式为 `ScreenSpace-Camera` 和的画布只会在其对应的相机中渲染,且它与渲染模式为 `ScreenSpace-Overlay` 的画布一样,都遵循通用的相机裁剪规则。
+- 渲染模式为 `ScreenSpace-Overlay` 的画布在没有相机的情况下依旧可以被渲染。
+- 同个相机下,`UICanvas` 之间的渲染排序遵循如下规则,overlay 的画布仅通过 `sortOrder` 决定渲染顺序。
+
+```mermaid
+flowchart TD
+ A[渲染数据排序] --> B{canvas.sortOrder}
+ B -->|不相等| C[返回比较结果]
+ B -->|相等| D{canvas 与相机的距离}
+ D -->|不相同| E[返回比较结果]
+ D -->|相同| F[返回比较结果]
+```
+
+## UIRenderer 渲染顺序
+
+```mermaid
+stateDiagram
+ RootCanvas --> A
+ RootCanvas --> F
+ A --> B
+ A --> E
+ B --> C
+ B --> D
+```
+
+如上图所示,根画布下的渲染顺序依次为 A -> B -> C -> D -> E -> F 需要注意的是,设置`UIRenderer.priority` 并不会改变它的渲染顺序。
+
+
diff --git a/docs/zh/UI/quickStart/text.md b/docs/zh/UI/quickStart/text.md
new file mode 100644
index 0000000000..5fb43b01f3
--- /dev/null
+++ b/docs/zh/UI/quickStart/text.md
@@ -0,0 +1,57 @@
+---
+order: 3
+title: 创建文字
+type: UI
+label: UI
+---
+
+`Text` 组件用于在 `UICanvas` 中显示文字。
+
+## 编辑器使用
+
+### 添加 Text 节点
+
+在 **[层级面板](/docs/interface/hierarchy/)** 添加 `Text` 节点
+
+
+
+> 若父亲或祖先节点没有画布组件,会自动添加上根画布节点。
+
+### 设置文本内容
+
+选中添加了 `Text` 组件的节点,在 **[检查器面板](/docs/interface/inspector)** 修改 `text` 属性可以改变 `Text` 元素的显示内容。
+
+
+
+### 设置字体
+
+选中添加了 `Text` 组件的节点,在 **[检查器面板](/docs/interface/inspector)** 修改 `font` 属性可以改变 `Text` 元素的字体类型。
+
+
+
+### 修改字体大小
+
+`Text` 组件可以通过调整 `FontSize` 修改渲染尺寸
+
+
+
+> 修改 `UITransform` 的 `size` 不会改变 `Text` 的渲染尺寸。
+
+## 属性
+
+| 属性名 | 描述 |
+| :-- | :-- |
+| `text` | 需要显示的文本 |
+| `color` | 文本颜色 |
+| `font` | 自定义字体 |
+| `fontSize` | 文本的字体大小 |
+| `fontStyle` | 字体样式设置:是否加粗/是否斜体 |
+| `lineSpacing` | 行间距 |
+| `horizontalAlignment` | 水平对齐方式,可选值有:Left/Center/Right |
+| `verticalAlignment` | 竖直对齐方式,可选值有:Top/Center/Bottom |
+| `enableWrapping` | 是否开启换行模式,打开换行模式后,会根据设置的宽来进行换行,如果这时候宽设置为 0,那么文本将不渲染 |
+| `overflowMode` | 当文本总高度超出设置的高的时候的处理方式,可选值有:Overflow/Truncate, Overflow 表示直接溢出显示, Truncate 表示只保留设置高度以内的内容显示,具体显示内容还和文本在竖直方向上的对齐方式有关 |
+
+## 脚本开发
+
+
diff --git a/docs/zh/UI/quickStart/transform.md b/docs/zh/UI/quickStart/transform.md
new file mode 100644
index 0000000000..37622e28bd
--- /dev/null
+++ b/docs/zh/UI/quickStart/transform.md
@@ -0,0 +1,34 @@
+---
+order: 1
+title: UITransform
+type: UI
+label: UI
+---
+
+`UITransform` 组件是专门设计用来表示 UI 元素的尺寸和位置的,它继承于 [Transform](/apis/core/#Transform) 。
+
+## 编辑器使用
+
+添加了 UI 组件的节点,会自动添加 `UITransform` 组件(替换原先旧的 [Transform](/apis/core/#Transform) 组件),在编辑器中,可以选中节点可以使用 `RectTool` (快捷键 `T` )快速设置属性,也可以在在 **[检查器面板](/docs/interface/inspector)** 设置精确属性。
+
+
+
+> 当主画布的渲染模式为 `Screen` 时,编辑器侧会禁止修改它的 `transform` 避免屏幕适配异常,因此在脚本中,**开发者应当自己避免去篡改屏幕空间主画布 `transform` 的属性**。
+
+## 属性
+
+| 属性名 | 描述 |
+| :------ | :----------------------------------------------------------------------------------- |
+| `size` | UI 元素的尺寸,`x` 代表宽度,`y` 代表高度,初始化都默认为 `100` |
+| `pivot` | UI 元素的锚点,它是一个以左下角为原点的归一化的二元向量,默认值为中心点,即(0.5,0.5) |
+
+## 脚本使用
+
+```typescript
+// Add UICanvas
+const canvasEntity = root.createChild("canvas");
+const canvas = canvasEntity.addComponent(UICanvas);
+const imageEntity = canvasEntity.create("Image");
+(imageEntity.transform).size.set(200, 200);
+(imageEntity.transform).pivot.set(0, 0);
+```
diff --git a/docs/zh/UI/system.md b/docs/zh/UI/system.md
new file mode 100644
index 0000000000..357f559a19
--- /dev/null
+++ b/docs/zh/UI/system.md
@@ -0,0 +1,108 @@
+---
+order: 2
+title: 架构
+type: UI
+label: UI
+---
+
+## 系统设计
+
+```mermaid
+---
+title: UI System
+---
+classDiagram
+ Component <|-- Transform
+ Script <|-- Component
+ Transform <|-- UITransform
+ Component <|-- Renderer
+ Renderer <|-- UIRenderer
+ UIRenderer <|-- Image
+ UIRenderer <|-- Text
+ UICanvas <|-- Component
+ UIInteractive <|-- Script
+ Button <|-- UIInteractive
+ UIGroup <|-- Component
+
+ class Script {
+ +void onUpdate()
+ }
+
+ class Transform{
+ +Vector3 position
+ +Vector3 rotation
+ +Vector3 scale
+ ...
+ }
+
+ class UITransform{
+ <>
+ +Vector2 size
+ +Vector2 pivot
+ }
+
+ class Renderer{
+ +BoundingBox bounds
+ +Material getMaterial()
+ +Material setMaterial()
+ }
+
+ class UIRenderer{
+ <>
+ +Color color
+ +boolean raycastEnabled
+ +vec4 raycastPadding
+ }
+
+ class Image{
+ <>
+ +Sprite sprite
+ +SpriteDrawMode drawMode
+ ...
+ }
+
+ class Text{
+ <>
+ +string text
+ +Font font
+ ...
+ }
+
+ class UICanvas {
+ <>
+ +CanvasRenderMode renderMode
+ +Camera renderCamera
+ ...
+ }
+
+ class UIInteractive {
+ <>
+ +boolean interactive
+ +Transition transitions
+ ...
+ }
+
+ class Button {
+ <>
+ +void addClicked()
+ +void removeClicked()
+ ...
+ }
+
+ class UIGroup {
+ <>
+ +boolean interactive
+ +number alpha
+ +boolean ignoreParentGroup
+ }
+```
+
+## 模块管理
+
+| 包 | 解释 | 相关文档 |
+| :----------------------------------------------------------------------- | :----------- | --------------------- |
+| [@galacean/engine-ui](https://www.npmjs.com/package/@galacean/engine-xr) | 核心架构逻辑 | [API](/apis/galacean) |
+
+> `@galacean/engine-ui` 是实现 **UI** 必须引入的依赖
+
+> 需遵守[版本依赖规则](/docs/basics/version/#版本依赖),即 `@galacean/engine-ui` 的版本需与 `@galacean/engine` 保持一致。
diff --git a/docs/zh/_meta.json b/docs/zh/_meta.json
index 0d959575b7..293f77643a 100644
--- a/docs/zh/_meta.json
+++ b/docs/zh/_meta.json
@@ -47,6 +47,12 @@
"collapse": true
}
},
+ "UI": {
+ "title": "UI",
+ "theme": {
+ "collapse": true
+ }
+ },
"xr": {
"title": "XR",
"theme": {
diff --git a/docs/zh/basics/overview.md b/docs/zh/basics/overview.md
index fd60853717..377559df99 100644
--- a/docs/zh/basics/overview.md
+++ b/docs/zh/basics/overview.md
@@ -35,7 +35,7 @@ flowchart LR
| [@galacean/engine](https://www.npmjs.com/package/@galacean/engine) | 核心架构逻辑和核心功能 | [API](/apis/galacean) |
| [@galacean/engine-physics-lite](https://www.npmjs.com/package/@galacean/engine-physics-lite) | 轻量级物理引擎 | [Doc](/docs/physics/overall) |
| [@galacean/engine-physics-physx](https://www.npmjs.com/package/@galacean/engine-physics-physx) | 全功能物理引擎 | [Doc](/docs/physics/overall) |
-| [@galacean/engine-shader-lab](https://www.npmjs.com/package/@galacean/engine-shader-lab) | Galacean Shader 编译器 | [Doc](/docs/graphics/shader/shaderLab/intro/) |
+| [@galacean/engine-shaderlab](https://www.npmjs.com/package/@galacean/engine-shaderlab) | Galacean Shader 编译器 | [Doc](/docs/graphics/shader/shaderLab/intro/) |
| [@galacean/engine-xr](https://www.npmjs.com/package/@galacean/engine-xr) | XR 逻辑包 | [Doc](/docs/xr/overall) |
| [@galacean/engine-xr-webxr](https://www.npmjs.com/package/@galacean/engine-xr-webxr) | WebXR 后端 | [Doc](/docs/xr/overall) |
diff --git a/docs/zh/graphics/2D/lottie.md b/docs/zh/graphics/2D/lottie.md
index 2c4a6f1ca5..6a45ab54ce 100644
--- a/docs/zh/graphics/2D/lottie.md
+++ b/docs/zh/graphics/2D/lottie.md
@@ -148,7 +148,8 @@ engine.resourceManager.load({
| 引擎版本 | Lottie 版本 |
| :--- | :--- |
| 1.2.x | 1.1.0-beta.0 |
-| 1.3.x | 1.1.0-beta.0 |
+| 1.3.x | engine-1.3 |
+| 1.4.x | engine-1.4 |
## 性能方面的建议
diff --git a/docs/zh/input/pointer.md b/docs/zh/input/pointer.md
index b2ded4840d..cfd9bb78c0 100644
--- a/docs/zh/input/pointer.md
+++ b/docs/zh/input/pointer.md
@@ -64,12 +64,15 @@ timeline
| 接口 | 触发时机与频率 |
| :------------------------------------------------- | :------------------------------------------------------------------------- |
-| [onPointerEnter](/apis/core/#Script-onPointerEnter) | 当触控点进入 Entity 的碰撞体范围时触发一次 |
-| [onPointerExit](/apis/core/#Script-onPointerExit) | 当触控点离开 Entity 的碰撞体范围时触发一次 |
-| [onPointerDown](/apis/core/#Script-onPointerDown) | 当触控点在 Entity 的碰撞体范围内按下时触发一次 |
-| [onPointerUp](/apis/core/#Script-onPointerUp) | 当触控点在 Entity 的碰撞体范围内松开时触发一次 |
-| [onPointerClick](/apis/core/#Script-onPointerClick) | 当触控点在 Entity 的碰撞体范围内按下并松开,在松开时触发一次 |
-| [onPointerDrag](/apis/core/#Script-onPointerDrag) | 当触控点在 Entity 的碰撞体范围内按下时**持续**触发,直至触控点解除按下状态 |
+| [onPointerEnter](/apis/core/#Script-onPointerEnter) | 当触控点进入 Entity 的碰撞范围时触发一次 |
+| [onPointerExit](/apis/core/#Script-onPointerExit) | 当触控点离开 Entity 的碰撞范围时触发一次 |
+| [onPointerDown](/apis/core/#Script-onPointerDown) | 当触控点在 Entity 的碰撞范围内按下时触发一次 |
+| [onPointerUp](/apis/core/#Script-onPointerUp) | 当触控点在 Entity 的碰撞范围内松开时触发一次 |
+| [onPointerClick](/apis/core/#Script-onPointerClick) | 当触控点在 Entity 的碰撞范围内按下并松开,在松开时触发一次 |
+| [onPointerBeginDrag](/apis/core/#Script-onPointerBeginDrag) | 当触控点在 Entity 的碰撞范围内按下时触发,表示开始拖动 |
+| [onPointerDrag](/apis/core/#Script-onPointerDrag) | 开始拖动后,触控点发生移动时触发 |
+| [onPointerEndDrag](/apis/core/#Script-onPointerEndDrag) | 开始拖动后,触控点松开时触发 |
+| [onPointerDrop](/apis/core/#Script-onPointerDrop) | 开始拖动后,当触控点在某个 Entity 的碰撞范围内松开时触发 |
> ⚠️ 触控回调**依赖物理引擎**,使用此功能前请确保物理引擎已初始化完毕。
diff --git a/e2e/.dev/vite.config.js b/e2e/.dev/vite.config.js
index 459bd71db3..b6d811f464 100644
--- a/e2e/.dev/vite.config.js
+++ b/e2e/.dev/vite.config.js
@@ -56,6 +56,7 @@ module.exports = {
"@galacean/engine-draco",
"@galacean/engine-lottie",
"@galacean/engine-spine",
+ "@galacean/engine-shaderlab",
"@galacean/tools-baker",
"@galacean/engine-toolkit",
"@galacean/engine-toolkit-auxiliary-lines",
diff --git a/examples/draw-lines.ts b/examples/draw-lines.ts
index 7c0d53dac8..9cf0a8939b 100644
--- a/examples/draw-lines.ts
+++ b/examples/draw-lines.ts
@@ -131,7 +131,7 @@ class DrawScript extends Script {
}
}
- onPointerDown(): void {
+ onPointerBeginDrag(): void {
// Screen pointer to world pointer.
this._preDrawTime = this.engine.time.elapsedTime;
const { x: screenX, y: screenY } =
diff --git a/examples/input-log.ts b/examples/input-log.ts
index 18d6c528b6..39a4afff94 100644
--- a/examples/input-log.ts
+++ b/examples/input-log.ts
@@ -21,7 +21,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
Pointer: true,
Keyboard: true,
Size: 1,
- Color: [255, 255, 255],
+ Color: [255, 0, 0],
OffsetX: 0,
OffsetY: 0,
};
@@ -53,5 +53,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
inputLogger.color.set(v[0] / 255, v[1] / 255, v[2] / 255, 1);
});
+ inputLogger.color.set(1, 0, 0, 1);
textFolder.open();
});
diff --git a/examples/input-pointer.ts b/examples/input-pointer.ts
index fd366d388d..8a79e1d523 100644
--- a/examples/input-pointer.ts
+++ b/examples/input-pointer.ts
@@ -17,149 +17,112 @@ import {
Entity,
Vector3,
WebGLEngine,
+ PointerEventData
} from "@galacean/engine";
import { LitePhysics } from "@galacean/engine-physics-lite";
// Create engine
-WebGLEngine.create({ canvas: "canvas", physics: new LitePhysics() }).then(
- (engine) => {
- engine.canvas.resizeByClientSize();
- const invCanvasWidth = 1 / engine.canvas.width;
- const invCanvasHeight = 1 / engine.canvas.height;
- // @ts-ignore
- const inputManager = engine.inputManager;
- const scene = engine.sceneManager.activeScene;
- const rootEntity = scene.createRootEntity("root");
-
- scene.ambientLight.diffuseSolidColor.set(1, 1, 1, 1);
- scene.ambientLight.diffuseIntensity = 1.2;
-
- // init camera
- const cameraEntity = rootEntity.createChild("camera");
- const camera = cameraEntity.addComponent(Camera);
- cameraEntity.transform.setPosition(10, 10, 10);
- cameraEntity.transform.lookAt(new Vector3(0, 0, 0));
-
- // init point light
- let light = rootEntity.createChild("light1");
- light.transform.setPosition(-8, -2, 8);
- light.addComponent(PointLight).intensity = 0.12;
-
- light = rootEntity.createChild("light2");
- light.transform.setPosition(8, -2, 0);
- light.addComponent(PointLight).intensity = 0.12;
-
- class PanScript extends Script {
- private startPointerPos = new Vector3();
- private tempVec2: Vector2 = new Vector2();
- private tempVec3: Vector3 = new Vector3();
- private zValue: number = 0;
-
- onPointerDown(pointer: Pointer) {
- this.zValue = camera.worldToViewportPoint(
- this.entity.transform.worldPosition,
- this.tempVec3
- ).z;
- const { tempVec2, tempVec3 } = this;
- tempVec2.copyFrom(pointer.position);
- tempVec3.set(
- tempVec2.x * invCanvasWidth,
- tempVec2.y * invCanvasHeight,
- this.zValue
- );
- camera.viewportToWorldPoint(tempVec3, this.startPointerPos);
- }
-
- onPointerDrag(pointer: Pointer) {
- const { tempVec2, tempVec3, startPointerPos } = this;
- const { transform } = this.entity;
- tempVec2.copyFrom(pointer.position);
- tempVec3.set(
- tempVec2.x * invCanvasWidth,
- tempVec2.y * invCanvasHeight,
- this.zValue
- );
- camera.viewportToWorldPoint(tempVec3, tempVec3);
- Vector3.subtract(tempVec3, startPointerPos, startPointerPos);
- transform.worldPosition.add(startPointerPos);
- startPointerPos.copyFrom(tempVec3);
- }
+WebGLEngine.create({ canvas: "canvas", physics: new LitePhysics() }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+ const invCanvasWidth = 1 / engine.canvas.width;
+ const invCanvasHeight = 1 / engine.canvas.height;
+ // @ts-ignore
+ const inputManager = engine.inputManager;
+ const scene = engine.sceneManager.activeScene;
+ const rootEntity = scene.createRootEntity("root");
+
+ scene.ambientLight.diffuseSolidColor.set(1, 1, 1, 1);
+ scene.ambientLight.diffuseIntensity = 1.2;
+
+ // init camera
+ const cameraEntity = rootEntity.createChild("camera");
+ const camera = cameraEntity.addComponent(Camera);
+ cameraEntity.transform.setPosition(10, 10, 10);
+ cameraEntity.transform.lookAt(new Vector3(0, 0, 0));
+
+ // init point light
+ let light = rootEntity.createChild("light1");
+ light.transform.setPosition(-8, -2, 8);
+ light.addComponent(PointLight).intensity = 0.12;
+
+ light = rootEntity.createChild("light2");
+ light.transform.setPosition(8, -2, 0);
+ light.addComponent(PointLight).intensity = 0.12;
+
+ class PanScript extends Script {
+ private startPointerPos = new Vector3();
+ private tempVec2: Vector2 = new Vector2();
+ private tempVec3: Vector3 = new Vector3();
+ private zValue: number = 0;
+
+ onPointerBeginDrag(eventData: PointerEventData) {
+ this.zValue = camera.worldToViewportPoint(this.entity.transform.worldPosition, this.tempVec3).z;
+ const { tempVec2, tempVec3 } = this;
+ tempVec2.copyFrom(eventData.pointer.position);
+ tempVec3.set(tempVec2.x * invCanvasWidth, tempVec2.y * invCanvasHeight, this.zValue);
+ camera.viewportToWorldPoint(tempVec3, this.startPointerPos);
}
- class ClickScript extends Script {
- private material: BlinnPhongMaterial;
- onStart() {
- this.material = (
- this.entity.getComponent(MeshRenderer).getInstanceMaterial()
- );
- }
-
- onPointerClick() {
- this.material.baseColor.set(
- Math.random(),
- Math.random(),
- Math.random(),
- 1.0
- );
- }
+ onPointerDrag(eventData: PointerEventData) {
+ const { tempVec2, tempVec3, startPointerPos } = this;
+ const { transform } = this.entity;
+ tempVec2.copyFrom(eventData.pointer.position);
+ tempVec3.set(tempVec2.x * invCanvasWidth, tempVec2.y * invCanvasHeight, this.zValue);
+ camera.viewportToWorldPoint(tempVec3, tempVec3);
+ Vector3.subtract(tempVec3, startPointerPos, startPointerPos);
+ transform.worldPosition.add(startPointerPos);
+ startPointerPos.copyFrom(tempVec3);
}
+ }
+
+ class ClickScript extends Script {
+ private material: BlinnPhongMaterial;
+ onStart() {
+ this.material = this.entity.getComponent(MeshRenderer).getInstanceMaterial();
+ }
+
+ onPointerClick() {
+ this.material.baseColor.set(Math.random(), Math.random(), Math.random(), 1.0);
+ }
+ }
- class EnterExitScript extends Script {
- private material: BlinnPhongMaterial;
- onStart() {
- this.material = (
- this.entity.getComponent(MeshRenderer).getInstanceMaterial()
- );
- }
-
- onPointerEnter() {
- this.material.baseColor.set(
- Math.random(),
- Math.random(),
- Math.random(),
- 1.0
- );
- }
-
- onPointerExit() {
- this.material.baseColor.set(
- Math.random(),
- Math.random(),
- Math.random(),
- 1.0
- );
- }
+ class EnterExitScript extends Script {
+ private material: BlinnPhongMaterial;
+ onStart() {
+ this.material = this.entity.getComponent(MeshRenderer).getInstanceMaterial();
}
- function createBox(x: number, y: number, z: number): Entity {
- // create box test entity
- const cubeSize = 2.0;
- const boxEntity = rootEntity.createChild("BoxEntity");
- boxEntity.transform.setPosition(x, y, z);
-
- const boxMtl = new BlinnPhongMaterial(engine);
- const boxRenderer = boxEntity.addComponent(MeshRenderer);
- boxMtl.baseColor.set(0.6, 0.3, 0.3, 1.0);
- boxRenderer.mesh = PrimitiveMesh.createCuboid(
- engine,
- cubeSize,
- cubeSize,
- cubeSize
- );
- boxRenderer.setMaterial(boxMtl);
-
- const boxCollider: StaticCollider =
- boxEntity.addComponent(StaticCollider);
- const boxColliderShape = new BoxColliderShape();
- boxColliderShape.size.set(cubeSize, cubeSize, cubeSize);
- boxCollider.addShape(boxColliderShape);
- return boxEntity;
+ onPointerEnter() {
+ this.material.baseColor.set(Math.random(), Math.random(), Math.random(), 1.0);
}
- createBox(0, 0, 0).addComponent(PanScript);
- createBox(3, 0, -3).addComponent(ClickScript);
- createBox(-3, 0, 3).addComponent(EnterExitScript);
- // Run engine
- engine.run();
+ onPointerExit() {
+ this.material.baseColor.set(Math.random(), Math.random(), Math.random(), 1.0);
+ }
}
-);
+
+ function createBox(x: number, y: number, z: number): Entity {
+ // create box test entity
+ const cubeSize = 2.0;
+ const boxEntity = rootEntity.createChild("BoxEntity");
+ boxEntity.transform.setPosition(x, y, z);
+
+ const boxMtl = new BlinnPhongMaterial(engine);
+ const boxRenderer = boxEntity.addComponent(MeshRenderer);
+ boxMtl.baseColor.set(0.6, 0.3, 0.3, 1.0);
+ boxRenderer.mesh = PrimitiveMesh.createCuboid(engine, cubeSize, cubeSize, cubeSize);
+ boxRenderer.setMaterial(boxMtl);
+
+ const boxCollider: StaticCollider = boxEntity.addComponent(StaticCollider);
+ const boxColliderShape = new BoxColliderShape();
+ boxColliderShape.size.set(cubeSize, cubeSize, cubeSize);
+ boxCollider.addShape(boxColliderShape);
+ return boxEntity;
+ }
+ createBox(0, 0, 0).addComponent(PanScript);
+ createBox(3, 0, -3).addComponent(ClickScript);
+ createBox(-3, 0, 3).addComponent(EnterExitScript);
+
+ // Run engine
+ engine.run();
+});
diff --git a/examples/physx-select.ts b/examples/physx-select.ts
index 0144e5955e..a2ce78b9c4 100644
--- a/examples/physx-select.ts
+++ b/examples/physx-select.ts
@@ -20,6 +20,7 @@ import {
PBRMaterial,
PlaneColliderShape,
Pointer,
+ PointerEventData,
PrimitiveMesh,
Quaternion,
Script,
@@ -29,7 +30,7 @@ import {
Texture2D,
Vector2,
Vector3,
- WebGLEngine,
+ WebGLEngine
} from "@galacean/engine";
import { PhysXPhysics } from "@galacean/engine-physics-physx";
@@ -47,35 +48,23 @@ class PanScript extends Script {
this.collider = this.entity.getComponent(DynamicCollider);
}
- onPointerDown(pointer: Pointer) {
+ onPointerDown(eventData: PointerEventData) {
// get depth
- this.camera.worldToViewportPoint(
- this.entity.transform.worldPosition,
- this.tempVec3
- );
- this.zValue = this.camera.worldToViewportPoint(
- this.entity.transform.worldPosition,
- this.tempVec3
- ).z;
+ this.camera.worldToViewportPoint(this.entity.transform.worldPosition, this.tempVec3);
+ this.zValue = this.camera.worldToViewportPoint(this.entity.transform.worldPosition, this.tempVec3).z;
const { tempVec3 } = this;
- tempVec3.set(
- pointer.position.x * this.invCanvasWidth,
- pointer.position.y * this.invCanvasHeight,
- this.zValue
- );
+ const { position } = eventData.pointer;
+ tempVec3.set(position.x * this.invCanvasWidth, position.y * this.invCanvasHeight, this.zValue);
this.camera.viewportToWorldPoint(tempVec3, this.startPointerPos);
this.collider.linearVelocity = new Vector3();
this.collider.angularVelocity = new Vector3();
}
- onPointerDrag(pointer: Pointer) {
+ onPointerDrag(eventData: PointerEventData) {
const { tempVec3, startPointerPos } = this;
const { transform } = this.entity;
- this.tempVec3.set(
- pointer.position.x * this.invCanvasWidth,
- pointer.position.y * this.invCanvasHeight,
- this.zValue
- );
+ const { position } = eventData.pointer;
+ this.tempVec3.set(position.x * this.invCanvasWidth, position.y * this.invCanvasHeight, this.zValue);
this.camera.viewportToWorldPoint(tempVec3, tempVec3);
Vector3.subtract(tempVec3, startPointerPos, startPointerPos);
transform.worldPosition = transform.worldPosition.add(startPointerPos);
@@ -83,19 +72,9 @@ class PanScript extends Script {
}
}
-function addPlane(
- rootEntity: Entity,
- size: Vector2,
- position: Vector3,
- rotation: Quaternion
-): Entity {
+function addPlane(rootEntity: Entity, size: Vector2, position: Vector3, rotation: Quaternion): Entity {
const mtl = new PBRMaterial(rootEntity.engine);
- mtl.baseColor.set(
- 0.2179807202597362,
- 0.2939682161541871,
- 0.31177952549087604,
- 1
- );
+ mtl.baseColor.set(0.2179807202597362, 0.2939682161541871, 0.31177952549087604, 1);
mtl.roughness = 0.0;
mtl.metallic = 0.0;
@@ -135,13 +114,7 @@ function addVerticalBox(
const boxRenderer = entity.addComponent(MeshRenderer);
boxMtl.baseTexture = texture;
boxMtl.baseTexture.anisoLevel = 12;
- boxRenderer.mesh = PrimitiveMesh.createCuboid(
- rootEntity.engine,
- 0.5,
- 0.33,
- 2,
- false
- );
+ boxRenderer.mesh = PrimitiveMesh.createCuboid(rootEntity.engine, 0.5, 0.33, 2, false);
boxRenderer.setMaterial(boxMtl);
const physicsBox = new BoxColliderShape();
@@ -153,8 +126,7 @@ function addVerticalBox(
const boxCollider = entity.addComponent(DynamicCollider);
boxCollider.addShape(physicsBox);
boxCollider.mass = 1;
- boxCollider.collisionDetectionMode =
- CollisionDetectionMode.ContinuousSpeculative;
+ boxCollider.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
const pan = entity.addComponent(PanScript);
pan.camera = camera;
@@ -181,12 +153,7 @@ function addHorizontalBox(
const boxRenderer = entity.addComponent(MeshRenderer);
boxMtl.baseTexture = texture;
boxMtl.baseTexture.anisoLevel = 12;
- boxRenderer.mesh = PrimitiveMesh.createCuboid(
- rootEntity.engine,
- 2,
- 0.33,
- 0.5
- );
+ boxRenderer.mesh = PrimitiveMesh.createCuboid(rootEntity.engine, 2, 0.33, 0.5);
boxRenderer.setMaterial(boxMtl);
const physicsBox = new BoxColliderShape();
@@ -198,8 +165,7 @@ function addHorizontalBox(
const boxCollider = entity.addComponent(DynamicCollider);
boxCollider.addShape(physicsBox);
boxCollider.mass = 0.5;
- boxCollider.collisionDetectionMode =
- CollisionDetectionMode.ContinuousSpeculative;
+ boxCollider.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
const pan = entity.addComponent(PanScript);
pan.camera = camera;
@@ -216,36 +182,9 @@ function addBox(
invCanvasHeight: number
): void {
for (let i: number = 0; i < 8; i++) {
- addVerticalBox(
- rootEntity,
- texture1,
- -0.65,
- 0.165 + i * 0.33 * 2,
- 0,
- camera,
- invCanvasWidth,
- invCanvasHeight
- );
- addVerticalBox(
- rootEntity,
- texture1,
- 0,
- 0.165 + i * 0.33 * 2,
- 0,
- camera,
- invCanvasWidth,
- invCanvasHeight
- );
- addVerticalBox(
- rootEntity,
- texture1,
- 0.65,
- 0.165 + i * 0.33 * 2,
- 0,
- camera,
- invCanvasWidth,
- invCanvasHeight
- );
+ addVerticalBox(rootEntity, texture1, -0.65, 0.165 + i * 0.33 * 2, 0, camera, invCanvasWidth, invCanvasHeight);
+ addVerticalBox(rootEntity, texture1, 0, 0.165 + i * 0.33 * 2, 0, camera, invCanvasWidth, invCanvasHeight);
+ addVerticalBox(rootEntity, texture1, 0.65, 0.165 + i * 0.33 * 2, 0, camera, invCanvasWidth, invCanvasHeight);
addHorizontalBox(
rootEntity,
@@ -257,16 +196,7 @@ function addBox(
invCanvasWidth,
invCanvasHeight
);
- addHorizontalBox(
- rootEntity,
- texture2,
- 0,
- 0.165 + 0.33 + i * 0.33 * 2,
- 0,
- camera,
- invCanvasWidth,
- invCanvasHeight
- );
+ addHorizontalBox(rootEntity, texture2, 0, 0.165 + 0.33 + i * 0.33 * 2, 0, camera, invCanvasWidth, invCanvasHeight);
addHorizontalBox(
rootEntity,
texture2,
@@ -281,69 +211,60 @@ function addBox(
}
//--------------------------------------------------------------------------------------------------------------------
-WebGLEngine.create({ canvas: "canvas", physics: new PhysXPhysics() }).then(
- (engine) => {
- engine.canvas.resizeByClientSize();
- const invCanvasWidth = 1 / engine.canvas.width;
- const invCanvasHeight = 1 / engine.canvas.height;
- const scene = engine.sceneManager.activeScene;
- scene.shadowDistance = 20;
- const rootEntity = scene.createRootEntity("root");
+WebGLEngine.create({ canvas: "canvas", physics: new PhysXPhysics() }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+ const invCanvasWidth = 1 / engine.canvas.width;
+ const invCanvasHeight = 1 / engine.canvas.height;
+ const scene = engine.sceneManager.activeScene;
+ scene.shadowDistance = 20;
+ const rootEntity = scene.createRootEntity("root");
- scene.ambientLight.diffuseSolidColor.set(0.5, 0.5, 0.5, 1);
- scene.ambientLight.diffuseIntensity = 1.2;
+ scene.ambientLight.diffuseSolidColor.set(0.5, 0.5, 0.5, 1);
+ scene.ambientLight.diffuseIntensity = 1.2;
- // init camera
- const cameraEntity = rootEntity.createChild("camera");
- const camera = cameraEntity.addComponent(Camera);
- cameraEntity.transform.setPosition(7, 7, 7);
- cameraEntity.transform.lookAt(new Vector3(0, 2, 0), new Vector3(0, 1, 0));
+ // init camera
+ const cameraEntity = rootEntity.createChild("camera");
+ const camera = cameraEntity.addComponent(Camera);
+ cameraEntity.transform.setPosition(7, 7, 7);
+ cameraEntity.transform.lookAt(new Vector3(0, 2, 0), new Vector3(0, 1, 0));
- const entity = cameraEntity.createChild("text");
- entity.transform.position = new Vector3(0, 3.5, -10);
- const renderer = entity.addComponent(TextRenderer);
- renderer.color = new Color();
- renderer.text = "Use mouse to move the bricks";
- renderer.font = Font.createFromOS(entity.engine, "Arial");
- renderer.fontSize = 40;
+ const entity = cameraEntity.createChild("text");
+ entity.transform.position = new Vector3(0, 3.5, -10);
+ const renderer = entity.addComponent(TextRenderer);
+ renderer.color = new Color();
+ renderer.text = "Use mouse to move the bricks";
+ renderer.font = Font.createFromOS(entity.engine, "Arial");
+ renderer.fontSize = 40;
- // init point light
- const light = rootEntity.createChild("light");
- light.transform.setPosition(0, 5, 8);
- light.transform.lookAt(new Vector3(0, 2, 0), new Vector3(0, 1, 0));
- const directLight = light.addComponent(DirectLight);
- directLight.shadowType = ShadowType.SoftLow;
+ // init point light
+ const light = rootEntity.createChild("light");
+ light.transform.setPosition(0, 5, 8);
+ light.transform.lookAt(new Vector3(0, 2, 0), new Vector3(0, 1, 0));
+ const directLight = light.addComponent(DirectLight);
+ directLight.shadowType = ShadowType.SoftLow;
- addPlane(rootEntity, new Vector2(30, 30), new Vector3(), new Quaternion());
+ addPlane(rootEntity, new Vector2(30, 30), new Vector3(), new Quaternion());
- Promise.all([
- engine.resourceManager.load({
- url: "https://gw.alipayobjects.com/mdn/rms_7c464e/afts/img/A*Wkn5QY0tpbcAAAAAAAAAAAAAARQnAQ",
- type: AssetType.Texture2D,
- }),
- engine.resourceManager.load({
- url: "https://gw.alipayobjects.com/mdn/rms_7c464e/afts/img/A*W5azT5DjDAEAAAAAAAAAAAAAARQnAQ",
- type: AssetType.Texture2D,
- }),
- ]).then((asset: Texture2D[]) => {
- addBox(
- rootEntity,
- asset[0],
- asset[1],
- camera,
- invCanvasWidth,
- invCanvasHeight
- );
+ Promise.all([
+ engine.resourceManager.load({
+ url: "https://gw.alipayobjects.com/mdn/rms_7c464e/afts/img/A*Wkn5QY0tpbcAAAAAAAAAAAAAARQnAQ",
+ type: AssetType.Texture2D
+ }),
+ engine.resourceManager.load({
+ url: "https://gw.alipayobjects.com/mdn/rms_7c464e/afts/img/A*W5azT5DjDAEAAAAAAAAAAAAAARQnAQ",
+ type: AssetType.Texture2D
+ })
+ ]).then((asset: Texture2D[]) => {
+ addBox(rootEntity, asset[0], asset[1], camera, invCanvasWidth, invCanvasHeight);
- engine.resourceManager
- .load({
- type: AssetType.Env,
- url: "https://gw.alipayobjects.com/os/bmw-prod/89c54544-1184-45a1-b0f5-c0b17e5c3e68.bin",
- })
- .then((ambientLight) => {
- scene.ambientLight = ambientLight;
- engine.run();
- });
- });
- }
-);
+ engine.resourceManager
+ .load({
+ type: AssetType.Env,
+ url: "https://gw.alipayobjects.com/os/bmw-prod/89c54544-1184-45a1-b0f5-c0b17e5c3e68.bin"
+ })
+ .then((ambientLight) => {
+ scene.ambientLight = ambientLight;
+ engine.run();
+ });
+ });
+});
diff --git a/examples/ui-Button.ts b/examples/ui-Button.ts
new file mode 100644
index 0000000000..3d2ab95f6a
--- /dev/null
+++ b/examples/ui-Button.ts
@@ -0,0 +1,63 @@
+/**
+ * @title UI Button
+ * @category UI
+ */
+import { AssetType, FontStyle, Sprite, SpriteDrawMode, Texture2D, WebGLEngine } from "@galacean/engine";
+import {
+ Button,
+ CanvasRenderMode,
+ ColorTransition,
+ Image,
+ ScaleTransition,
+ Text,
+ UICanvas,
+ UITransform
+} from "@galacean/engine-ui";
+
+// Create engine
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+
+ const scene = engine.sceneManager.activeScene;
+ const rootEntity = scene.createRootEntity();
+
+ // Add canvas
+ const canvasEntity = rootEntity.createChild("canvas");
+ const canvas = canvasEntity.addComponent(UICanvas);
+ canvas.renderMode = CanvasRenderMode.ScreenSpaceOverlay;
+ canvas.referenceResolutionPerUnit = 50;
+
+ // Add button
+ const buttonEntity = canvasEntity.createChild("Image");
+ const image = buttonEntity.addComponent(Image);
+ image.drawMode = SpriteDrawMode.Sliced;
+ (buttonEntity.transform).size.set(200, 70);
+ const text = buttonEntity.createChild("Text").addComponent(Text);
+ text.text = "Button";
+ text.fontStyle |= FontStyle.Bold;
+ text.color.set(0, 0, 0, 1);
+ text.fontSize = 60;
+ const button = buttonEntity.addComponent(Button);
+ const scaleTransition = new ScaleTransition();
+ scaleTransition.target = image;
+ const colorTransition = new ColorTransition();
+ colorTransition.target = image;
+ button.addTransition(scaleTransition);
+ button.addTransition(colorTransition);
+ button.addClicked(() => {
+ window.alert("button clicked");
+ });
+
+ engine.resourceManager
+ .load({
+ url: "https://mdn.alipayobjects.com/huamei_yo47yq/afts/img/A*mFpSS502qUYAAAAAAAAAAAAAehuCAQ/original",
+ type: AssetType.Texture2D
+ })
+ .then((texture) => {
+ const sprite = new Sprite(engine, texture);
+ sprite.border.set(0.49, 0.49, 0.49, 0.49);
+ image.sprite = sprite;
+ });
+
+ engine.run();
+});
diff --git a/examples/ui-Event.ts b/examples/ui-Event.ts
new file mode 100644
index 0000000000..27d15c91c1
--- /dev/null
+++ b/examples/ui-Event.ts
@@ -0,0 +1,85 @@
+/**
+ * @title UI Event
+ * @category UI
+ */
+import { AssetType, PointerEventData, Script, Sprite, Texture2D, WebGLEngine } from "@galacean/engine";
+import { CanvasRenderMode, Image, UICanvas, UITransform } from "@galacean/engine-ui";
+
+// Create engine
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+
+ const scene = engine.sceneManager.activeScene;
+ const rootEntity = scene.createRootEntity();
+
+ // Add canvas
+ const canvasEntity = rootEntity.createChild("canvas");
+ const canvas = canvasEntity.addComponent(UICanvas);
+ canvas.renderMode = CanvasRenderMode.ScreenSpaceOverlay;
+ canvas.referenceResolutionPerUnit = 50;
+
+ // Add ImageOne
+ const imageEntity1 = canvasEntity.createChild("Image1");
+ const image1 = imageEntity1.addComponent(Image);
+ image1.color.set(1, 0, 0, 1);
+ (imageEntity1.transform).size.set(300, 300);
+ imageEntity1.addComponent(EventScript);
+
+ const imageEntity2 = imageEntity1.createChild("Image2");
+ const image2 = imageEntity2.addComponent(Image);
+ image2.color.set(0, 1, 0, 1);
+ (imageEntity2.transform).size.set(200, 200);
+ imageEntity2.addComponent(EventScript);
+
+ const imageEntity3 = imageEntity2.createChild("Image3");
+ const image3 = imageEntity3.addComponent(Image);
+ image3.color.set(0, 0, 1, 1);
+ (imageEntity3.transform).size.set(100, 100);
+ imageEntity3.addComponent(EventScript);
+
+ engine.resourceManager
+ .load({
+ url: "https://mdn.alipayobjects.com/huamei_yo47yq/afts/img/A*wW-5TYANcJAAAAAAAAAAAAAADhuCAQ/original/Image.png",
+ type: AssetType.Texture2D
+ })
+ .then((texture) => {
+ const sprite = new Sprite(engine, texture);
+ image1.sprite = image2.sprite = image3.sprite = sprite;
+ });
+
+ engine.run();
+});
+
+class EventScript extends Script {
+ onPointerEnter(eventData: PointerEventData): void {
+ console.log(this.entity.name, "onPointerEnter");
+ }
+
+ onPointerDown(eventData: PointerEventData): void {
+ console.log(this.entity.name, "onPointerDown");
+ }
+
+ onPointerUp(eventData: PointerEventData): void {
+ console.log(this.entity.name, "onPointerUp");
+ }
+
+ onPointerClick(eventData: PointerEventData): void {
+ console.log(this.entity.name, "onPointerClick");
+ }
+
+ onPointerExit(eventData: PointerEventData): void {
+ console.log(this.entity.name, "onPointerExit");
+ }
+
+ onPointerBeginDrag(eventData: PointerEventData): void {
+ console.log(this.entity.name, "onPointerBeginDrag");
+ }
+
+ onPointerEndDrag(eventData: PointerEventData): void {
+ console.log(this.entity.name, "onPointerEndDrag");
+ }
+
+ onPointerDrop(eventData: PointerEventData): void {
+ console.log(this.entity.name, "onPointerDrop");
+ }
+}
diff --git a/examples/ui-Image.ts b/examples/ui-Image.ts
new file mode 100644
index 0000000000..a43bb9a2ca
--- /dev/null
+++ b/examples/ui-Image.ts
@@ -0,0 +1,74 @@
+/**
+ * @title UI Image
+ * @category UI
+ * @thumbnail https://mdn.alipayobjects.com/merchant_appfe/afts/img/A*t4cXTbFa6kkAAAAAAAAAAAAADiR2AQ/original
+ */
+
+import { AssetType, Sprite, SpriteDrawMode, Texture2D, WebGLEngine } from "@galacean/engine";
+import { CanvasRenderMode, Image, Text, UICanvas, UITransform } from "@galacean/engine-ui";
+
+// Create engine
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+ const rootEntity = engine.sceneManager.scenes[0].createRootEntity();
+
+ // Add canvas
+ const canvasEntity = rootEntity.createChild("canvas");
+ const canvas = canvasEntity.addComponent(UICanvas);
+
+ canvas.renderMode = CanvasRenderMode.ScreenSpaceOverlay;
+ canvas.referenceResolutionPerUnit = 50;
+
+ // Add Image
+ const simpleImageEntity = canvasEntity.createChild("Image");
+ const simpleImage = simpleImageEntity.addComponent(Image);
+ simpleImage.drawMode = SpriteDrawMode.Simple;
+ const simpleImageTransform = simpleImageEntity.transform;
+ simpleImageTransform.position.set(-300, 0, 0);
+ simpleImageTransform.size.set(200, 70);
+
+ const simpleTextEntity = canvasEntity.createChild("Text");
+ simpleTextEntity.transform.setPosition(-300, 70, 0);
+ const simpleText = simpleTextEntity.addComponent(Text);
+ simpleText.text = "Simple Image";
+
+ const slicedImageEntity = canvasEntity.createChild("Image");
+ const slicedImage = slicedImageEntity.addComponent(Image);
+ slicedImage.drawMode = SpriteDrawMode.Sliced;
+ const slicedImageTransform = slicedImageEntity.transform;
+ slicedImageTransform.size.set(200, 70);
+
+ const slicedTextEntity = canvasEntity.createChild("Text");
+ slicedTextEntity.transform.setPosition(0, 70, 0);
+ const slicedText = slicedTextEntity.addComponent(Text);
+ slicedText.text = "Sliced Image";
+
+ const tiledImageEntity = canvasEntity.createChild("Image");
+ const tiledImage = tiledImageEntity.addComponent(Image);
+ tiledImage.drawMode = SpriteDrawMode.Tiled;
+ const tiledImageTransform = tiledImageEntity.transform;
+ tiledImageTransform.position.set(300, 0, 0);
+ tiledImageTransform.size.set(200, 70);
+
+ const tiledTextEntity = canvasEntity.createChild("Text");
+ tiledTextEntity.transform.setPosition(300, 70, 0);
+ const tiledText = tiledTextEntity.addComponent(Text);
+ tiledText.text = "Tiled Image";
+
+ engine.resourceManager
+ .load({
+ url: "https://mdn.alipayobjects.com/huamei_yo47yq/afts/img/A*mFpSS502qUYAAAAAAAAAAAAAehuCAQ/original",
+ type: AssetType.Texture2D
+ })
+ .then((texture) => {
+ const simpleSprite = new Sprite(engine, texture);
+ const slicedSprite = new Sprite(engine, texture);
+ slicedSprite.border.set(0.49, 0.49, 0.49, 0.49);
+ const tiledSprite = new Sprite(engine, texture);
+ simpleImage.sprite = simpleSprite;
+ slicedImage.sprite = slicedSprite;
+ tiledImage.sprite = tiledSprite;
+ });
+
+ engine.run();
+});
diff --git a/examples/ui-Text.ts b/examples/ui-Text.ts
new file mode 100644
index 0000000000..fadafb4168
--- /dev/null
+++ b/examples/ui-Text.ts
@@ -0,0 +1,88 @@
+/**
+ * @title UI Text
+ * @category UI
+ * @thumbnail https://mdn.alipayobjects.com/merchant_appfe/afts/img/A*t4cXTbFa6kkAAAAAAAAAAAAADiR2AQ/original
+ */
+
+import { Color, Entity, Font, FontStyle, Vector3, WebGLEngine } from "@galacean/engine";
+import { CanvasRenderMode, Text, UICanvas } from "@galacean/engine-ui";
+
+// Create engine
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+ const rootEntity = engine.sceneManager.scenes[0].createRootEntity();
+
+ // Add canvas
+ const canvasEntity = rootEntity.createChild("canvas");
+ const canvas = canvasEntity.addComponent(UICanvas);
+
+ canvas.renderMode = CanvasRenderMode.ScreenSpaceOverlay;
+
+ // The position of text
+ const pos = new Vector3();
+ // The color of text
+ const color = new Color();
+
+ // Create text with default params
+ pos.set(0, 125, 0);
+ color.set(1, 1, 1, 1);
+ createText();
+ // Create text with cursive font family
+ pos.set(0, 75, 0);
+ color.set(1, 1, 1, 1);
+ const entity = createText("cursive");
+ for (let i = 0; i < 10; i++) {
+ entity.clone();
+ }
+ // Create text with font size 36
+ pos.set(0, 25, 0);
+ color.set(1, 0.5, 0.5, 1);
+ createText("Arial", 36);
+ // Create text with bold
+ pos.set(0, -25, 0);
+ color.set(1, 1, 1, 1);
+ createText("Arial", 26, true);
+ // Create text with italic
+ pos.set(0, -75, 0);
+ color.set(1, 1, 1, 1);
+ createText("Arial", 26, false, true);
+ // Create text with bold and italic
+ pos.set(0, -125, 0);
+ color.set(1, 1, 1, 1);
+ createText("Arial", 26, true, true);
+
+ engine.run();
+
+ /**
+ * Create text to display by params.
+ * @param fontFamily - The font family
+ * @param fontSize - The size of font
+ * @param bold - The text whether bold
+ * @param italic - The text whether italic
+ */
+ function createText(
+ fontFamily: string = "Arial",
+ fontSize: number = 26,
+ bold: boolean = false,
+ italic: boolean = false
+ ): Entity {
+ // Create text entity
+ const entity = canvasEntity.createChild("text");
+ entity.transform.position = pos;
+ // Add text renderer for text entity
+ const text = entity.addComponent(Text);
+ // Set text color
+ text.color = color;
+ // Set text to render
+ text.text = "The quick brown fox jumps over the lazy dog";
+ // Set font with font family
+ text.font = Font.createFromOS(entity.engine, fontFamily);
+ // Set font size
+ text.fontSize = fontSize;
+ // Set font whether bold
+ bold && (text.fontStyle |= FontStyle.Bold);
+ // Set font whether italic
+ italic && (text.fontStyle |= FontStyle.Italic);
+ return entity;
+ }
+});
diff --git a/tests/src/core/audio/AudioSource.test.ts b/tests/src/core/audio/AudioSource.test.ts
index af85d28032..9699e988c1 100644
--- a/tests/src/core/audio/AudioSource.test.ts
+++ b/tests/src/core/audio/AudioSource.test.ts
@@ -1,4 +1,4 @@
-import { AssetType, AudioClip, AudioSource, Engine } from "@galacean/engine-core";
+import { AssetType, AudioClip, AudioManager, AudioSource, Engine } from "@galacean/engine-core";
import "@galacean/engine-loader";
import { WebGLEngine } from "@galacean/engine-rhi-webgl";
import { beforeAll, describe, expect, it } from "vitest";
@@ -54,7 +54,11 @@ describe("AudioSource", () => {
audioSource.stop();
audioSource.play();
- // Because the audio play should interaction
- expect(audioSource.isPlaying).to.be.false;
+ if (AudioManager.isAudioContextRunning()) {
+ expect(audioSource.isPlaying).to.be.true;
+ } else {
+ // Because the audio play should interaction
+ expect(audioSource.isPlaying).to.be.false;
+ }
});
});