-
-
Notifications
You must be signed in to change notification settings - Fork 307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add GUI #2375
base: dev/1.4
Are you sure you want to change the base?
Add GUI #2375
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Outside diff range and nitpick comments (27)
packages/core/src/input/pointer/Pointer.ts (2)
55-59
: LGTM: New _addEmitters method. Consider minor readability improvement.The new
_addEmitters
method aligns well with the updated event handling mechanism. The use of generics ensures type safety. To slightly improve readability, consider using a more descriptive parameter name for the emitter type:- _addEmitters<T extends new (pool: ClearableObjectPool<PointerEventData>) => PointerEventEmitter>( - type: T, + _addEmitters<EmitterType extends new (pool: ClearableObjectPool<PointerEventData>) => PointerEventEmitter>( + emitterType: EmitterType, pool: ClearableObjectPool<PointerEventData> ) { - this._emitters.push(new type(pool)); + this._emitters.push(new emitterType(pool)); }This change makes it clearer that the
type
parameter represents an emitter type.
82-93
: LGTM: New enums. Consider using const enum for better performance.The new
EmitterType
andPointerEventType
enums are good additions, providing clear definitions for emitter types and pointer event states. To potentially improve performance and ensure consistency with other enums in the codebase, consider usingconst enum
:- export enum EmitterType { + export const enum EmitterType { Physics = 1, UI = 2 } - export enum PointerEventType { + export const enum PointerEventType { None = 0x0, Down = 0x1, Up = 0x2, Leave = 0x4, Move = 0x8, Cancel = 0x10 }Using
const enum
can lead to more optimized code as the enum values are inlined at compile time. However, please ensure this is consistent with the project's coding standards and requirements.packages/core/src/ui/UIUtils.ts (4)
15-152
: Consider the trade-offs of using a static class for UI utilitiesThe
UIUtils
class contains only static members, which aligns with its purpose as a utility class. While some style guides suggest avoiding classes with only static members, in this context, it provides a clear namespace for UI-related utility functions. This approach prevents global namespace pollution and groups related functionalities.However, if you decide to refactor in the future, consider these alternatives:
- Use a namespace instead of a class.
- Export individual functions from a module.
These alternatives could improve tree-shaking and potentially make the code more modular. The current implementation is acceptable, but keep these options in mind for future refactoring.
🧰 Tools
🪛 Biome
[error] 15-152: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
[error] 51-51: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 72-72: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 130-130: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 130-130: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
[error] 131-131: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 131-131: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
45-64
: Improve readability inregisterUIToCanvas
The
registerUIToCanvas
method effectively manages the association between UI elements and canvases. However, the assignment within the expression on line 51 can be improved for better readability.Consider refactoring line 51 as follows:
-replaced && (replaced._indexInCanvas = element._indexInCanvas); +if (replaced) { + replaced._indexInCanvas = element._indexInCanvas; +}This change separates the assignment from the logical check, making the code easier to read and maintain.
🧰 Tools
🪛 Biome
[error] 51-51: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
66-82
: Improve readability inregisterUIToGroup
The
registerUIToGroup
method effectively manages the association between UI elements and groups. However, similar to theregisterUIToCanvas
method, there's an opportunity to improve readability on line 72.Consider refactoring line 72 as follows:
-replaced && (replaced._indexInGroup = element._indexInGroup); +if (replaced) { + replaced._indexInGroup = element._indexInGroup; +}This change separates the assignment from the logical check, enhancing code clarity and maintainability.
🧰 Tools
🪛 Biome
[error] 72-72: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
128-151
: Optimize and clarify therender
methodThe
render
method effectively handles the rendering of UI canvases. However, there are a few areas that can be improved for clarity and consistency:
- The use of
this
in a static context (lines 130-131) can be confusing.- The assignments within expressions (lines 130-131) reduce readability.
Consider refactoring lines 130-131 as follows:
-const uiRenderQueue = (this._renderQueue ||= new RenderQueue(RenderQueueType.Transparent)); -const virtualCamera = (this._virtualCamera ||= new VirtualCamera()); +UIUtils._renderQueue ||= new RenderQueue(RenderQueueType.Transparent); +const uiRenderQueue = UIUtils._renderQueue; +UIUtils._virtualCamera ||= new VirtualCamera(); +const virtualCamera = UIUtils._virtualCamera;These changes improve readability and avoid the use of
this
in a static context, making the code more maintainable and less prone to misinterpretation.🧰 Tools
🪛 Biome
[error] 130-130: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 130-130: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
[error] 131-131: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 131-131: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
packages/core/src/ui/UIGroup.ts (3)
87-122
: Simplify boolean conditions in _updateGlobalModifyThe
_updateGlobalModify
method is well-implemented, correctly updating global states based on the provided flags. However, there are two instances of unnecessary double negation that can be simplified:
- Line 106: Replace
if (!!flags)
withif (flags)
- Line 114: Replace
if (!!passDownFlags)
withif (passDownFlags)
These changes will improve code clarity without affecting functionality.
🧰 Tools
🪛 Biome
[error] 106-106: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
[error] 114-114: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
124-157
: LGTM: Lifecycle methods are well-implemented with a minor optimization opportunityThe
_onEnableInScene
and_onDisableInScene
methods are well-implemented, correctly managing the UIGroup's lifecycle and relationships. The cleanup logic in_onDisableInScene
is thorough, preventing potential memory leaks.Consider replacing the empty callback functions in the
forEach
calls withundefined
:disorderedElements.forEach((element: IUIElement) => { UIUtils.registerUIToGroup(element, parentGroup); }, undefined);This minor optimization can slightly improve performance by avoiding the creation of empty function objects.
159-190
: Refactor assignment within expression in _registryToParentGroupThe
_registryToParentGroup
method is well-implemented, correctly managing the relationships between UIGroups and handling entity listeners. However, there's an assignment within an expression on line 166 that can be refactored for better readability:replaced && (replaced._groupIndex = this._groupIndex);Consider refactoring this line to:
if (replaced) { replaced._groupIndex = this._groupIndex; }This change improves code clarity without affecting functionality.
🧰 Tools
🪛 Biome
[error] 166-166: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/input/pointer/PointerManager.ts (2)
Line range hint
1-287
: Overall improvement in PointerManager structure and functionalityThe PointerManager class has undergone significant improvements in its structure and functionality. The refactoring towards a more modular event handling system, improved memory management with object pools, and streamlined pointer management flow are all positive changes. These improvements enhance maintainability, extensibility, and align with modern best practices.
Consider updating the class-level documentation to reflect these architectural changes and new event handling patterns. This will help future developers understand the design decisions and how to properly extend or modify the PointerManager.
Lack of explicit error handling in critical sections
The analysis confirms minimal explicit error handling in the
PointerManager
. To enhance robustness, add try-catch blocks or error checks in the following areas:
- Event listener addition and removal
- Pointer creation and initialization
- Event data pool operations
🔗 Analysis chain
Line range hint
1-287
: Consider adding explicit error handlingThe current implementation has minimal explicit error handling. To improve the robustness of the PointerManager, consider adding try-catch blocks or error checks in critical sections such as:
- Event listener addition and removal
- Pointer creation and initialization
- Event data pool operations
Here's a script to check for existing error handling:
Adding appropriate error handling will help prevent silent failures and make debugging easier.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Check for existing error handling in PointerManager.ts # Test: Search for try-catch blocks and error checks echo "Searching for existing error handling..." rg -n '(try|catch|throw|Error)' packages/core/src/input/pointer/PointerManager.ts echo "Searching for potential error-prone areas without checks..." rg -n '(addEventListener|removeEventListener|new Pointer|_eventDataPool)' packages/core/src/input/pointer/PointerManager.tsLength of output: 1606
packages/core/src/2d/sprite/SpriteMask.ts (1)
252-253
: Improved render method efficiency and specificityThe changes in the
_render
method are good improvements:
- The simplified condition for early return (lines 252-253) is more concise and efficient.
- The update flag check now uses the more specific
SpriteMaskUpdateFlags.Position
.- The position update includes additional parameters for width, height, and flip states, which should lead to more accurate positioning.
These changes should result in more efficient and accurate rendering.
However, consider destructuring the
width
andheight
properties in line 269 for slightly improved readability:SimpleSpriteAssembler.updatePositions(this, this.width, this.height, sprite.pivot, this._flipX, this._flipY);Could be changed to:
const { width, height } = this; SimpleSpriteAssembler.updatePositions(this, width, height, sprite.pivot, this._flipX, this._flipY);This is a minor suggestion and the current implementation is also acceptable.
Also applies to: 268-270
packages/core/src/BasicResources.ts (1)
260-275
: LGTM: New_createUIMaterial
method is well-implemented.The
_createUIMaterial
method follows the established pattern of other material creation methods in this class. It correctly sets up the render state for UI elements, including appropriate blend factors and operations.Consider extracting the common setup code shared between
_createUIMaterial
and_create2DMaterial
into a separate private method to reduce code duplication. This would improve maintainability and ensure consistency across different material types. For example:private _setupCommonMaterialProperties(material: Material): void { const renderState = material.renderState; const target = renderState.blendState.targetBlendState; target.enabled = true; target.sourceColorBlendFactor = BlendFactor.SourceAlpha; target.destinationColorBlendFactor = BlendFactor.OneMinusSourceAlpha; target.sourceAlphaBlendFactor = BlendFactor.One; target.destinationAlphaBlendFactor = BlendFactor.OneMinusSourceAlpha; target.colorBlendOperation = target.alphaBlendOperation = BlendOperation.Add; renderState.depthState.writeEnabled = false; renderState.rasterState.cullMode = CullMode.Off; renderState.renderQueueType = RenderQueueType.Transparent; material.isGCIgnored = true; }Then, you can use this method in both
_createUIMaterial
and_create2DMaterial
:private _createUIMaterial(engine: Engine): Material { const material = new Material(engine, Shader.find("ui")); this._setupCommonMaterialProperties(material); return material; }packages/core/src/RenderPipeline/BasicRenderPipeline.ts (1)
Line range hint
1-380
: Overall improvements to the rendering pipeline.The changes in this file significantly enhance the rendering pipeline, particularly for canvas elements. The code maintains good separation of concerns, improves readability, and adds new functionality without disrupting existing operations. The addition of the
RenderQueueFlags
enum provides a robust way to manage render queue types.Consider adding comments to explain the purpose of the new canvas rendering loop and how it interacts with the existing renderer loop, to improve maintainability for future developers.
packages/core/src/physics/PhysicsScene.ts (1)
321-321
: LGTM: Enhanced raycast result with component information.The addition of the
component
property to thehitResult
object provides more detailed information about the raycast result. This is a useful enhancement for developers using this method.Consider updating the method's JSDoc to include information about the new
component
property in theoutHitResult
parameter description.Also applies to: 332-332
packages/core/src/2d/sprite/SpriteRenderer.ts (1)
316-328
: LGTM with minor suggestion: Improved local bounds calculationThe refactored
_updateLocalBounds
method now correctly handles cases where the sprite is flipped on the X or Y axis and considers the sprite's pivot. This is a valuable improvement that ensures accurate bounds calculation in all scenarios.However, there's an unnecessary use of double negation (
!!
) on lines 321-322. Sincethis.flipX
andthis.flipY
are already boolean values, the double negation can be removed without changing the behavior.Apply this diff to simplify the expressions:
- pivotX = !!this.flipX ? 1 - pivotX : pivotX; - pivotY = !!this.flipY ? 1 - pivotY : pivotY; + pivotX = this.flipX ? 1 - pivotX : pivotX; + pivotY = this.flipY ? 1 - pivotY : pivotY;🧰 Tools
🪛 Biome
[error] 321-321: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
[error] 322-322: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
packages/core/src/2d/text/TextRenderer.ts (1)
695-711
: Good addition ofTextRendererUpdateFlag
enumThe new
TextRendererUpdateFlag
enum provides a more granular and type-safe way to manage update flags for the TextRenderer. This should improve code clarity and maintainability.Consider adding a brief comment explaining the purpose of each flag for better documentation. For example:
enum TextRendererUpdateFlag { /** Flag to update sub-font */ SubFont = 0x4, /** Flag to update local position */ LocalPosition = 0x8, // ... (add comments for other flags) }packages/core/src/ui/UICanvas.ts (5)
22-86
: Consider leveraging TypeScript's type inference for primitive types.The properties are well-organized and appropriately decorated. However, for some properties with primitive types and initial values, you can omit the explicit type annotation to leverage TypeScript's type inference. For example:
@ignoreClone private _sortOrder = 0; @ignoreClone private _distance = 10;This change would make the code slightly more concise without losing type safety.
88-191
: LGTM: Well-implemented getters and setters with a suggestion for optimization.The getters and setters are well-implemented, providing controlled access to internal properties and including necessary logic for updating the canvas state. The
elements
getter efficiently refreshes the hierarchy only when needed.Consider caching the result of
this._getRealRenderMode()
in therenderMode
setter to avoid calling it twice:set renderMode(mode: CanvasRenderMode) { let preMode = this._renderMode; if (preMode !== mode) { this._renderMode = mode; this._setCameraObserver( this._isRootCanvas && mode === CanvasRenderMode.ScreenSpaceCamera ? this._renderCamera : null ); const newRealRenderMode = this._getRealRenderMode(); this._setRealRenderMode(newRealRenderMode); } }This change would slightly improve performance by avoiding redundant calculations.
225-265
: Improve readability by separating assignment from expression.The
_prepareRender
method is well-implemented, efficiently handling frustum culling and camera mask filtering. However, the assignment within an expression on line 229 could be improved for readability:const renderElement = this._renderElement = engine._renderElementPool.get();Consider separating the assignment:
this._renderElement = engine._renderElementPool.get(); const renderElement = this._renderElement;This change improves code clarity without affecting functionality.
🧰 Tools
🪛 Biome
[error] 229-229: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 246-246: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
368-534
: LGTM: Well-implemented helper methods with optimization potential.The
_onGroupModify
method and the private helper methods are well-implemented, handling their specific responsibilities effectively. The adaptation logic in methods like_adapterPoseInScreenSpace
and_adapterSizeInScreenSpace
is necessarily complex for proper canvas rendering.Consider caching frequently used values in
_adapterSizeInScreenSpace
to potentially improve performance:private _adapterSizeInScreenSpace(): void { const { _transform: transform, _realRenderMode: realRenderMode, _referenceResolution, _resolutionAdaptationStrategy } = this; const { x: width, y: height } = _referenceResolution; // ... rest of the method }This change could slightly reduce property access overhead in the method.
🧰 Tools
🪛 Biome
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
537-611
: LGTM with suggestions for switch statements.The private methods are well-implemented and handle their specific tasks effectively. However, some switch statements have cases that fall through without explicit breaks. While this might be intentional, it's a good practice to make it explicit. Consider adding comments or restructuring the switch statements in
_setRealRenderMode
:switch (curRealMode) { case CanvasRenderMode.ScreenSpaceOverlay: this._addCanvasListener(); // fall through case CanvasRenderMode.ScreenSpaceCamera: this._adapterPoseInScreenSpace(); this._adapterSizeInScreenSpace(); // fall through case CanvasRenderMode.WorldSpace: componentsManager.addUICanvas(curRealMode, this); break; default: break; }This change improves code clarity by making the intentional fallthrough explicit.
🧰 Tools
🪛 Biome
[error] 589-590: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 599-600: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 601-603: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/Entity.ts (1)
408-408
: LGTM: Consistent UITransform usage for child entitiesThe addition of UITransform to child entities when the parent uses UITransform ensures consistency in the entity hierarchy for UI components.
Consider adding a comment explaining the rationale behind this condition for better code clarity:
// Ensure child entities inherit UITransform if parent uses it this._transform instanceof UITransform && child.addComponent(UITransform);packages/core/src/ComponentsManager.ts (1)
107-112
: Consider adding sorting for_canvases
if neededCurrently, the
sortUICanvases()
method only sorts_overlayCanvases
when_overlayCanvasesSortingFlag
is true. If_canvases
may also require sorting based on a certain criterion, consider implementing a similar sorting mechanism to ensure consistent behavior.Would you like assistance in adding sorting functionality for
_canvases
?packages/core/src/ui/UIImage.ts (2)
128-133
: Ensure property change triggers updateAssigning a new
Color
instance to thecolor
property updates the rendering state, but modifying the existingColor
instance's properties does not. Consider implementing an observer pattern or providing a setter within theColor
class to detect changes and update the_dirtyUpdateFlag
accordingly when color components are modified.
190-192
: Handle destroyed material appropriatelyThe current handling of a destroyed material silently replaces it with the default UI material without notifying the user, which could hide underlying issues. Consider logging a warning or throwing an error to inform developers of the material's destruction and prevent potential debugging difficulties.
Apply this diff to log a warning:
if (material.destroyed) { + console.warn("Material is destroyed. Replacing with default UI material."); material = this._engine._basicResources.uiDefaultMaterial; }
packages/core/src/Engine.ts (1)
492-494
: Redundant blank lines before variable declarationThere are unnecessary blank lines before
const componentsManager = scene._componentsManager;
. Removing them can improve code readability.Apply this diff to remove the extra blank lines:
- - const componentsManager = scene._componentsManager;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (19)
- packages/core/src/2d/assembler/TiledSpriteAssembler.ts (6 hunks)
- packages/core/src/2d/sprite/SpriteMask.ts (9 hunks)
- packages/core/src/2d/sprite/SpriteRenderer.ts (14 hunks)
- packages/core/src/2d/text/TextRenderer.ts (20 hunks)
- packages/core/src/BasicResources.ts (3 hunks)
- packages/core/src/ComponentsManager.ts (4 hunks)
- packages/core/src/Engine.ts (5 hunks)
- packages/core/src/Entity.ts (11 hunks)
- packages/core/src/RenderPipeline/BasicRenderPipeline.ts (2 hunks)
- packages/core/src/Renderer.ts (8 hunks)
- packages/core/src/Scene.ts (1 hunks)
- packages/core/src/index.ts (3 hunks)
- packages/core/src/input/pointer/Pointer.ts (2 hunks)
- packages/core/src/input/pointer/PointerManager.ts (6 hunks)
- packages/core/src/physics/PhysicsScene.ts (4 hunks)
- packages/core/src/ui/UICanvas.ts (1 hunks)
- packages/core/src/ui/UIGroup.ts (1 hunks)
- packages/core/src/ui/UIImage.ts (1 hunks)
- packages/core/src/ui/UIUtils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/core/src/2d/assembler/TiledSpriteAssembler.ts
- packages/core/src/Renderer.ts
- packages/core/src/index.ts
🧰 Additional context used
🪛 Biome
packages/core/src/2d/sprite/SpriteRenderer.ts
[error] 321-321: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
[error] 322-322: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
packages/core/src/ComponentsManager.ts
[error] 101-101: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/Entity.ts
[error] 616-616: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UICanvas.ts
[error] 229-229: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 246-246: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 347-347: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 352-352: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 353-353: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 349-350: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 351-356: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 589-590: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 599-600: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 601-603: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIGroup.ts
[error] 106-106: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
[error] 114-114: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
[error] 166-166: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIUtils.ts
[error] 15-152: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
[error] 51-51: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 72-72: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 130-130: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 130-130: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
[error] 131-131: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 131-131: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
🔇 Additional comments (69)
packages/core/src/input/pointer/Pointer.ts (5)
2-3
: LGTM: New imports support updated event handling.The new imports are consistent with the changes described in the summary and support the updated event handling mechanism and data structures.
Also applies to: 6-7
40-43
: Add access modifiers to new internal properties.The new properties
_frameEvents
and_emitters
are consistent with the updated event handling mechanism. However, to enhance encapsulation and clarity, consider explicitly marking these properties asprivate
orprotected
.- /** @internal */ - _frameEvents: PointerEventType = PointerEventType.None; - /** @internal */ - _emitters: PointerEventEmitter[] = []; + /** @internal */ + private _frameEvents: PointerEventType = PointerEventType.None; + /** @internal */ + private _emitters: PointerEventEmitter[] = [];This makes their intended usage clearer and prevents unintended access from outside the class.
65-67
: Reset all frame-related data in _resetOnFrameBegin()The new
_resetOnFrameBegin()
method is a good addition for managing frame-related data. However, as previously noted,_upMap
and_downMap
are not being reset. To prevent any unintended carryover of data between frames, please reset these arrays as well:_resetOnFrameBegin(): void { this._frameEvents = PointerEventType.None; this._events.length = this._upList.length = this._downList.length = 0; + this._upMap.length = this._downMap.length = 0; }
This ensures all frame-related data is properly reset at the beginning of each frame.
73-78
: Clear _emitters array after disposing to prevent memory leaksThe new
_dispose()
method is a good addition for cleaning up resources. However, as previously noted, after disposing of each emitter in_emitters
, the array still holds references to these emitters. To allow for garbage collection and prevent potential memory leaks, please clear the_emitters
array:_dispose(): void { const emitters = this._emitters; for (let i = 0, n = emitters.length; i < n; i++) { emitters[i]._dispose(); } + this._emitters.length = 0; this._events.length = this._upList.length = this._downList.length = 0; }
This ensures that all references to disposed emitters are removed, allowing them to be garbage collected.
48-49
: LGTM: Simplified constructor. Verify removal of other initializations.The constructor has been simplified to only initialize the
id
property, which is consistent with the changes described in the summary. However, please ensure that any necessary initializations that were previously in the constructor have been appropriately moved or are no longer needed.✅ Verification successful
Verified: Constructor simplification confirmed.
The constructor now only initializes the
id
property, and no other initializations are present.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Check for any initializations in the old constructor that might need to be handled elsewhere. # Test: Search for the old constructor implementation rg -U 'constructor\(.*\).*\{[\s\S]*?\}' packages/core/src/input/pointer/Pointer.tsLength of output: 134
packages/core/src/ui/UIUtils.ts (5)
19-35
: LGTM: Efficient entity listener registrationThe
registerEntityListener
method effectively manages the hierarchy of entities and their listeners. It optimizes performance by only updating the necessary parts of the hierarchy and properly handles listener registration and unregistration.
37-43
: LGTM: Thorough cleanup of entity listenersThe
unRegisterEntityListener
method effectively unregisters all modify listeners for a UI element and clears the parents array. This ensures proper cleanup and prevents memory leaks.
84-100
: LGTM: Efficient root canvas lookupThe
getRootCanvasInParent
method effectively traverses the entity hierarchy to find the root canvas. It efficiently checks for the correct component type and uses necessary type casting. The implementation is well-optimized for performance.
102-126
: LGTM: Well-implemented group lookupThe
getGroupInParents
method efficiently traverses the entity hierarchy to find the first UI group, correctly stopping if a root canvas is encountered. The use of a switch statement for component types enhances performance and readability.
1-152
: Overall assessment: Well-implemented UI utilities with minor improvement opportunitiesThe
UIUtils
class provides a comprehensive and efficient set of utility functions for managing UI elements, canvases, and groups. The implementation is generally well-structured and optimized for performance.Key strengths:
- Efficient traversal of entity hierarchies
- Proper management of listeners and registrations
- Clear separation of concerns for different UI components
Suggested improvements:
- Consider alternatives to using a static-only class in future refactoring
- Improve readability by separating assignments from logical checks in some methods
- Avoid using
this
in static contexts for better clarityThese minor adjustments will further enhance the maintainability and clarity of the code. Great job on implementing this crucial utility class for the UI system!
🧰 Tools
🪛 Biome
[error] 15-152: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
[error] 51-51: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 72-72: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 130-130: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 130-130: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
[error] 131-131: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 131-131: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
packages/core/src/ui/UIGroup.ts (4)
1-41
: LGTM: Imports and class declaration are well-structuredThe imports, class declaration, and property definitions are well-organized and follow TypeScript best practices. The use of decorators for cloning behavior is consistent and appropriate.
43-75
: LGTM: Well-implemented getter and setter methodsThe getter and setter methods for
ignoreParentGroup
,raycastEnabled
, andalpha
are well-implemented. The alpha setter includes proper value clamping, and all setters correctly trigger the_updateGlobalModify
method to ensure changes propagate through the UI hierarchy.
192-201
: LGTM: _onEntityModify method is well-implementedThe
_onEntityModify
method is concise and correctly handles relevant entity modifications. The use of a switch statement for different modification flags is appropriate and easy to understand.
204-209
: LGTM: GroupModifyFlags enum is well-definedThe
GroupModifyFlags
enum is well-defined and follows best practices for flag enums. The use of bitwise values allows for efficient flag combination, which is particularly useful in the context of UI group modifications.packages/core/src/input/pointer/PointerManager.ts (6)
4-5
: Improved modularity and memory managementThe new imports and the addition of
_eventDataPool
usingClearableObjectPool
suggest an improvement in modularity and memory management for event handling. This change is likely to enhance performance and reduce memory overhead.Also applies to: 9-12, 41-41
66-69
: Improved update logic and memory managementThe changes in the
_update
method enhance code readability and potentially improve performance. Clearing the event data pool each frame and resetting pointers instead of disposing them are good practices for maintaining a clean state and reducing object churn.Also applies to: 73-83
Line range hint
122-171
: Enhanced event handling with improved modularityThe changes in pointer event handling significantly improve the system's modularity and extensibility. The use of
_frameEvents
for granular event tracking and the emitter-based approach for event processing are excellent improvements. The addition of Leave and Cancel event handling enhances the robustness of the pointer system.Also applies to: 230-252
279-285
: Improved cleanup in _removeEventListenerThe additions to the
_removeEventListener
method ensure a more thorough cleanup of event data and reset of various properties. This is crucial for preventing memory leaks and maintaining a clean state, especially important in long-running applications or those with frequent scene changes.
88-114
:⚠️ Potential issueFix variable shadowing in pointer creation loop
There's a variable shadowing issue in the pointer creation loop. The outer loop counter
i
is shadowed by the inner loop counter, which is also namedi
. This can lead to confusion and potential bugs.To fix this, rename the inner loop counter:
-for (let i = 0, n = _nativeEvents.length; i < n; i++) { +for (let i = 0, n = _nativeEvents.length; i < n; i++) { // ... (other code) if (lastCount === 0 || this._multiPointerEnabled) { // ... (other code) - let i = 0; - for (; i < lastCount; i++) { - if (_pointers[i].id > i) { + let j = 0; + for (; j < lastCount; j++) { + if (_pointers[j].id > j) { break; } } - pointer = pointerPool[i]; + pointer = pointerPool[j]; if (!pointer) { - pointer = new Pointer(i); + pointer = new Pointer(j); // ... (other code) } // ... (other code) - _pointers.splice(i, 0, pointer); + _pointers.splice(j, 0, pointer); } }Likely invalid or redundant comment.
Line range hint
1-287
: Consider performance monitoring and optimizationWhile the changes improve memory efficiency and modularity, they also introduce additional complexity to the
_update
method and event handling process. For most use cases, this shouldn't cause performance issues, but it's worth considering:
- Implementing performance monitoring to track the impact of these changes in real-world scenarios.
- Optimizing the
_update
method for high-load situations, possibly by batching operations or using more efficient data structures.To identify potential performance bottlenecks, you can run the following script:
This will help identify areas that might benefit from optimization in high-performance scenarios.
packages/core/src/2d/sprite/SpriteMask.ts (5)
78-78
: Improved update flag specificityThe change from
RendererUpdateFlags.WorldVolume
toSpriteMaskUpdateFlags.PositionAndAllBounds
is a good improvement. It makes the update process more specific to theSpriteMask
class and correctly indicates that both position and bounds need to be updated when the width changes.
114-114
: Consistent update flag usageThe change to
SpriteMaskUpdateFlags.PositionAndAllBounds
in theflipX
setter is consistent with the changes inwidth
andheight
setters. This ensures that flipping the sprite on the X-axis correctly triggers updates to both position and bounds.
238-244
: Improved local bounds updateThe renaming of
_updateBounds
to_updateLocalBounds
and the changes in its implementation are good improvements:
- The method now correctly focuses on updating local bounds, which is more appropriate for this level of the rendering pipeline.
- The new implementation handles the case where there's no sprite, setting the bounds to zero in this scenario.
- When a sprite exists, it uses the
SimpleSpriteAssembler.updatePositions
method, which should correctly calculate the bounds based on the sprite's properties.These changes should lead to more accurate and efficient bounds calculations.
329-330
: Enhanced sprite change handlingThe modifications in the
_onSpriteChange
method are well-thought-out:
- The default size calculation now correctly triggers a position and bounds update (lines 329-330).
- A new update flag
SpriteMaskUpdateFlags.PositionAndUV
has been introduced for region and atlas region offset changes (line 335). This is more precise than updating everything.- Pivot changes now trigger a position and bounds update (line 341), which is correct as changing the pivot affects both position and bounds.
These changes provide more granular control over updates, which should lead to more efficient rendering and better performance.
Also applies to: 335-335, 341-341
356-364
: Improved update flags for SpriteMaskThe revisions to the
SpriteMaskUpdateFlags
enum are excellent:
- Individual flags for Position (0x4) and UV (0x8) allow for more granular updates.
- Combined flags like
PositionAndAllBounds
(0x7) andPositionAndUV
(0xc) enable efficient updates of related properties.- The
All
flag (0xf) provides a convenient way to trigger a full update when needed.These changes should lead to more efficient updates by allowing the system to update only what's necessary in different scenarios. This granularity can potentially improve performance, especially in cases where only specific aspects of the sprite mask need updating.
packages/core/src/BasicResources.ts (2)
118-118
: LGTM: New property declaration is consistent and appropriate.The
uiDefaultMaterial
property is correctly declared asreadonly
and of typeMaterial
, which is consistent with other similar properties in the class and appropriate for its intended use.
169-169
: LGTM: Constructor update is consistent with existing patterns.The initialization of
uiDefaultMaterial
in the constructor using the_createUIMaterial
method follows the established pattern for other material initializations in this class.packages/core/src/RenderPipeline/BasicRenderPipeline.ts (5)
24-24
: LGTM: New import for canvas rendering.The import of
CanvasRenderMode
is correctly added and necessary for the new canvas handling logic.
341-342
: Improved variable destructuring for better readability.The destructuring of camera properties enhances code clarity and potentially improves performance by reducing property lookups.
344-360
: Enhanced renderer processing with explicit naming and frustum culling.The renderer loop now uses more explicit naming (
rendererElements
) and includes proper frustum culling. This change improves code clarity and potentially optimizes rendering by skipping objects outside the camera's view.
Line range hint
375-380
: LGTM: New RenderQueueFlags enum added.The
RenderQueueFlags
enum provides a clear and type-safe way to represent render queue types. The use of power-of-2 values allows for efficient bitwise operations, and theAll
flag is correctly set to the combination of all other flags.
362-372
: New canvas rendering support added.A new loop for processing canvases has been introduced, adding support for rendering UI elements. The code correctly checks for the appropriate render mode and camera before preparing the canvas for rendering.
To ensure that this new canvas rendering logic is correctly implemented across the codebase, let's verify the usage of
CanvasRenderMode.ScreenSpaceCamera
:✅ Verification successful
CanvasRenderMode.ScreenSpaceCamera usage verified successfully.
All instances ofCanvasRenderMode.ScreenSpaceCamera
are consistently implemented across the codebase.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the usage of CanvasRenderMode.ScreenSpaceCamera # Expected: Consistent usage across the codebase rg -n "CanvasRenderMode.ScreenSpaceCamera"Length of output: 2034
packages/core/src/physics/PhysicsScene.ts (2)
5-7
: LGTM: Import statements reorganized and new import added.The separation of imports and the addition of the
ignoreClone
import improve code organization and readability.
460-460
: LGTM: Added@ignoreClone
decorator to_setGravity
method.The addition of the
@ignoreClone
decorator to the private_setGravity
method is appropriate. This prevents the method from being cloned, which is suitable for scene-specific operations like setting gravity.packages/core/src/2d/sprite/SpriteRenderer.ts (10)
10-10
: LGTM: ComponentType import addedThe addition of the
ComponentType
import is appropriate for the new_componentType
property in theSpriteRenderer
class.
84-84
: LGTM: Update flag refined for drawMode setterThe change to use
SpriteRendererUpdateFlags.PositionUVAndColor
is consistent with the new update flag system and ensures proper updates when the draw mode changes.
99-99
: LGTM: Update flag refined for tileMode setterThe use of
SpriteRendererUpdateFlags.PositionUVAndColor
in thetileMode
setter is consistent with the new update flag system and ensures proper updates when the tile mode changes in tiled draw mode.
116-116
: LGTM: Update flag refined for tiledAdaptiveThreshold setterThe use of
SpriteRendererUpdateFlags.PositionUVAndColor
in thetiledAdaptiveThreshold
setter is consistent with the new update flag system and ensures proper updates when the threshold changes in tiled draw mode.
144-144
: LGTM: Default size recalculation added to sprite setterThe addition of
this._calDefaultSize()
in thesprite
setter ensures that the default size is properly updated when a new sprite is set, maintaining consistency in the renderer's dimensions.
181-182
: LGTM: Improved update flag handling in width setterThe conditional update flag setting in the
width
setter provides more precise control over updates:
- For tiled mode:
SpriteRendererUpdateFlags.PositionUVColorAndWorldBounds
- For other modes:
SpriteRendererUpdateFlags.PositionAndAllBounds
This differentiation optimizes updates based on the specific requirements of each draw mode, potentially improving performance.
206-207
: LGTM: Consistent update flag handling in height setterThe
height
setter now uses the same conditional update flag setting as thewidth
setter:
- For tiled mode:
SpriteRendererUpdateFlags.PositionUVColorAndWorldBounds
- For other modes:
SpriteRendererUpdateFlags.PositionAndAllBounds
This consistency ensures that both dimension changes are handled uniformly and efficiently.
333-333
: LGTM: Improved render method with better flag handlingThe changes to the
_render
method include several improvements:
- The early return condition is now more concise and readable.
- The position update check now uses the new flag system (
SpriteRendererUpdateFlags.Position
).- The
updatePositions
call includesflipX
andflipY
, allowing for more accurate position calculations.These changes enhance the rendering logic and align with the overall refactoring of the update flag system.
Also applies to: 347-349
410-443
: LGTM: Enhanced sprite change handlingThe
_onSpriteChange
method has been significantly improved:
- It now provides more granular control over updates based on specific sprite modifications.
- The method considers custom dimensions and different draw modes when updating flags.
- The changes align well with the new update flag system.
These enhancements improve the renderer's responsiveness to sprite modifications and should lead to more efficient updates. The conditional logic for different draw modes is particularly noteworthy, as it ensures that only necessary updates are triggered.
469-486
: LGTM: Expanded update flags for finer-grained controlThe
SpriteRendererUpdateFlags
enum has been significantly expanded to provide more granular control over different aspects of the sprite renderer that need updating. Key improvements include:
- Separate flags for Position, UV, and Color updates.
- Combined flags for common update scenarios (e.g.,
PositionAndWorldBounds
,PositionUVAndColor
).- Logical combinations that align with the specific update needs of different renderer operations.
This expansion allows for more precise and efficient updates throughout the
SpriteRenderer
class, potentially improving performance by reducing unnecessary update operations.packages/core/src/Scene.ts (1)
330-330
: Verify the impact of_setParentChange()
on scene graph updates.The change from
_setTransformDirty()
to_setParentChange()
suggests a refinement in how entity relationships are managed within the scene graph. This aligns with the broader refactor mentioned in the summary.To ensure this change doesn't introduce unexpected behavior:
- Verify that
_setParentChange()
properly triggers necessary updates in the entity hierarchy.- Check for any performance implications, especially in scenes with complex hierarchies.
- Ensure that this change is consistently applied across the codebase.
Run the following script to verify the usage and implementation of
_setParentChange
:packages/core/src/2d/text/TextRenderer.ts (7)
9-9
: Import update looks good.The addition of
RendererUpdateFlags
to the import statement is consistent with the new flag system implemented in the class.
258-264
: Improved local bounds update logicThe new implementation of
_updateLocalBounds
is more efficient, only updating local data when the text is visible. This optimization should improve performance.
329-333
: Efficient sub-font update logicThe new implementation of
_getSubFont
uses the flag system to update the sub-font only when necessary, which is a good optimization.
480-481
: Improved sub-font handling in local data updateThe use of
_getSubFont()
instead of_resetSubFont()
is consistent with the new flag-based system and should improve efficiency.
622-622
: Improved transform change handlingThe use of the new flag system in
_onTransformChanged
provides more granular control over updates, which is a good improvement.
Line range hint
1-711
: Overall, good improvements to TextRendererThe changes in this file represent a significant improvement in the TextRenderer's implementation. The new flag-based system should provide better performance and more granular control over updates. The code is now more maintainable and efficient.
There are a few minor suggestions for improvement:
- Fix the initialization of
_textChunks
array.- Consider adding brief comments to the
TextRendererUpdateFlag
enum for better documentation.Great work on refactoring this component!
35-35
:⚠️ Potential issueIncorrect initialization of
_textChunks
arrayThe
_textChunks
array is incorrectly initialized. UsingArray<TextChunk>()
without thenew
keyword does not create a new array instance.Apply this diff to fix the initialization:
-private _textChunks = Array<TextChunk>(); +private _textChunks: TextChunk[] = [];Likely invalid or redundant comment.
packages/core/src/ui/UICanvas.ts (5)
1-21
: LGTM: Imports and class declaration look good.The imports cover all necessary modules for the
UICanvas
class. The@dependentComponents
decorator is appropriately used to automatically add aUITransform
component, which is essential for UI positioning and sizing.
193-220
: LGTM: Constructor and raycast method are well-implemented.The constructor correctly initializes the component type and binds necessary methods. The
raycast
method efficiently implements hit testing for UI elements, iterating through the elements in reverse order to ensure proper layering.
267-309
: LGTM: _updateSortDistance and _onEnableInScene methods are well-implemented.Both methods handle their responsibilities correctly. The
_updateSortDistance
method efficiently updates the sorting distance based on the render mode, while_onEnableInScene
properly registers the canvas with the UI system and handles necessary state updates.
614-619
: LGTM: CanvasRealRenderMode enum is well-defined.The
CanvasRealRenderMode
enum is correctly defined and properly documented as extendingCanvasRenderMode
. This provides a clear way to represent the additional render mode option.
1-619
: Overall, well-implemented UICanvas class with minor suggestions for improvement.The
UICanvas
class is well-structured and implements complex UI canvas management logic effectively. The code is generally clean and follows good practices. Here are the main points for improvement:
- Consider leveraging TypeScript's type inference for primitive types to reduce verbosity.
- Optimize some methods by caching frequently accessed values or computed results.
- Improve switch statements by adding explicit break statements or comments for intentional fallthrough cases.
- Enhance readability by avoiding assignments within expressions.
Addressing these minor issues will further improve the already solid implementation of the
UICanvas
class.🧰 Tools
🪛 Biome
[error] 229-229: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 246-246: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 347-347: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 352-352: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 353-353: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 349-350: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 351-356: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 589-590: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 599-600: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 601-603: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/Entity.ts (6)
10-17
: LGTM: Improved imports and property encapsulationThe new imports and properties enhance the Entity class's functionality, particularly for UI components and update management. The change from a public
transform
property to a private_transform
property with a getter method improves encapsulation.Also applies to: 86-87, 103-104, 108-108
111-114
: LGTM: Improved transform property accessThe new getter method for
transform
maintains the public interface while improving encapsulation by accessing the private_transform
property.
531-534
: LGTM: Proper cleanup of _updateFlagManagerThe addition of cleanup code for
_updateFlagManager
in thedestroy
method prevents potential memory leaks by removing all listeners and nullifying the reference.
607-610
: LGTM: Encapsulated parent change handlingThe new
_setParentChange
method effectively encapsulates the logic for handling parent changes, updating the transform and dispatching the appropriate modify event.
817-824
: LGTM: Clear categorization of entity modificationsThe new
EntityModifyFlags
enum provides a clear and extensible way to categorize different types of entity modifications. This will improve code readability and maintainability when handling entity changes.
Line range hint
1-825
: Overall assessment: Significant improvements to Entity classThe changes to the Entity class introduce several enhancements:
- Improved encapsulation with private properties and getter methods.
- Enhanced UI component handling and interactivity.
- Better update management with the introduction of UpdateFlagManager.
- Clearer categorization of entity modifications with the new EntityModifyFlags enum.
These changes improve the overall functionality, maintainability, and extensibility of the Entity class, particularly in the context of UI-related operations. The suggested minor improvements, if implemented, will further enhance the code quality and type safety.
🧰 Tools
🪛 Biome
[error] 616-616: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/ComponentsManager.ts (1)
314-315
: Ensure garbage collection is completeIncluding
garbageCollection()
calls for both_overlayCanvases
and_canvases
is good practice to prevent memory leaks. This ensures that unused canvases are properly cleaned up.packages/core/src/ui/UIImage.ts (1)
285-300
:⚠️ Potential issueWrap variable declarations in switch cases with block scopes
Declaring variables within a
case
clause without enclosing them in a block can lead to unexpected behaviors because variables become accessible to other cases within the switch statement. Wrap thecase
body in curly braces to limit the scope of the variable.Apply this diff to address the scoping issue:
case SpriteModifyFlags.size: + { switch (this._drawMode) { case SpriteDrawMode.Sliced: this._dirtyUpdateFlag |= ImageUpdateFlags.Position; break; case SpriteDrawMode.Tiled: this._dirtyUpdateFlag |= ImageUpdateFlags.PositionUVAndColor; break; default: break; } break; + }Likely invalid or redundant comment.
packages/core/src/Engine.ts (4)
40-40
: Import statement forUIUtils
addedThe import of
UIUtils
from"./ui/UIUtils"
is correctly added to support UI functionalities.
328-328
: Correct addition ofsortUICanvases()
to manage UI canvas sortingThe call to
componentsManager.sortUICanvases();
ensures that UI canvases are sorted during the update cycle, which is appropriate for maintaining the correct rendering order of UI elements.
516-517
: Integrate UI rendering withUIUtils.render
The addition of
UIUtils.render(this, componentsManager._overlayCanvases);
appropriately integrates UI rendering into the rendering loop, ensuring that UI elements are rendered correctly after scene rendering.
342-342
: Verify the impact of calling_firePointerScript()
unconditionallyBy removing the conditional check for
_physicsInitialized
,inputManager._firePointerScript(scenes);
is now called regardless of physics initialization status. Ensure this change does not introduce issues when physics is not initialized.Run the following script to confirm
_firePointerScript()
operates correctly without physics initialization:Expected result: No matches found, indicating that
_firePointerScript()
does not rely on_physicsInitialized
and can be called unconditionally.
packages/core/src/ui/UICanvas.ts
Outdated
_raycast(ray: Ray, out: HitResult, distance: number = Number.MAX_SAFE_INTEGER): boolean { | ||
const entity = this._entity; | ||
const plane = UIRenderer._tempPlane; | ||
const transform = entity.transform; | ||
const normal = plane.normal.copyFrom(transform.worldForward); | ||
plane.distance = -Vector3.dot(normal, transform.worldPosition); | ||
const curDistance = ray.intersectPlane(plane); | ||
if (curDistance >= 0 && curDistance < distance) { | ||
const hitPointWorld = ray.getPoint(curDistance, UIRenderer._tempVec30); | ||
const worldMatrixInv = UIRenderer._tempMat; | ||
Matrix.invert(this.entity.transform.worldMatrix, worldMatrixInv); | ||
const localPosition = UIRenderer._tempVec31; | ||
Vector3.transformCoordinate(hitPointWorld, worldMatrixInv, localPosition); | ||
if (this._hitTest(localPosition)) { | ||
out.distance = curDistance; | ||
out.entity = entity; | ||
out.component = this; | ||
out.normal.copyFrom(normal); | ||
out.point.copyFrom(hitPointWorld); | ||
return true; | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
/** | ||
* @internal | ||
*/ | ||
@ignoreClone | ||
_onEntityModify(flag: EntityModifyFlags): void { | ||
switch (flag) { | ||
case EntityModifyFlags.SiblingIndex: | ||
this._rootCanvas && (this._rootCanvas._hierarchyDirty = true); | ||
break; | ||
case EntityModifyFlags.UICanvasEnableInScene: | ||
if (!this._isRootCanvas) break; | ||
case EntityModifyFlags.Parent: | ||
const rootCanvas = UIUtils.getRootCanvasInParent(this._entity); | ||
rootCanvas && (rootCanvas._hierarchyDirty = true); | ||
this._setIsRootCanvas(!rootCanvas); | ||
UIUtils.registerUIToCanvas(this, rootCanvas); | ||
UIUtils.registerEntityListener(this); | ||
case EntityModifyFlags.UIGroupEnableInScene: | ||
UIUtils.registerUIToGroup(this, UIUtils.getGroupInParents(this._entity)); | ||
break; | ||
default: | ||
break; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add missing break statements in switch cases.
The _raycast
method is well-implemented with proper intersection calculations. However, in the _onEntityModify
method, there are switch cases that fall through without explicit break
statements. This could lead to unintended behavior. Consider adding break
statements or using return
to make the flow explicit:
case EntityModifyFlags.UICanvasEnableInScene:
if (!this._isRootCanvas) break;
// fall through
case EntityModifyFlags.Parent:
const rootCanvas = UIUtils.getRootCanvasInParent(this._entity);
if (rootCanvas) {
rootCanvas._hierarchyDirty = true;
}
this._setIsRootCanvas(!rootCanvas);
UIUtils.registerUIToCanvas(this, rootCanvas);
UIUtils.registerEntityListener(this);
break; // Add this break
case EntityModifyFlags.UIGroupEnableInScene:
UIUtils.registerUIToGroup(this, UIUtils.getGroupInParents(this._entity));
break;
This change will prevent unintended fallthrough and make the code's behavior more explicit.
🧰 Tools
🪛 Biome
[error] 347-347: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 352-352: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 353-353: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 349-350: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 351-356: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/Entity.ts
Outdated
_onUIInteractiveChange(val: boolean): void { | ||
if (val) { | ||
this._interactive = true; | ||
} else { | ||
for (let i = 0, len = this._children.length; i < len; i++) { | ||
this._children[i]._setTransformDirty(); | ||
const components = this._components; | ||
for (let i = 0, n = components.length; i < n; i++) { | ||
const component = components[i]; | ||
if ( | ||
component._componentType & ComponentType.UIElement && | ||
component.enabled && | ||
(component as unknown as IUIElement)._runtimeRaycastEnable | ||
) { | ||
this._interactive = true; | ||
return; | ||
} | ||
} | ||
this._interactive = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve type safety in _onUIInteractiveChange method
The new _onUIInteractiveChange
method enhances UI interactivity management. However, the type casting can be improved for better type safety.
Consider using a type guard to ensure type safety:
private _onUIInteractiveChange(val: boolean): void {
if (val) {
this._interactive = true;
} else {
const components = this._components;
for (let i = 0, n = components.length; i < n; i++) {
const component = components[i];
if (
component._componentType & ComponentType.UIElement &&
component.enabled &&
this.isUIElement(component) &&
component._runtimeRaycastEnable
) {
this._interactive = true;
return;
}
}
this._interactive = false;
}
}
private isUIElement(component: Component): component is IUIElement {
return '_runtimeRaycastEnable' in component;
}
This approach provides better type safety and makes the code more maintainable.
canvases = this._canvases; | ||
} | ||
const replaced = canvases.deleteByIndex(uiCanvas._canvasIndex); | ||
replaced && (replaced._canvasIndex = uiCanvas._canvasIndex); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid assignments within expressions for better readability
Using an assignment within a logical expression can reduce code clarity and lead to potential misunderstandings. It's better to separate the assignment from the expression.
Apply this diff to improve readability:
-replaced && (replaced._canvasIndex = uiCanvas._canvasIndex);
+if (replaced) {
+ replaced._canvasIndex = uiCanvas._canvasIndex;
+}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
replaced && (replaced._canvasIndex = uiCanvas._canvasIndex); | |
if (replaced) { | |
replaced._canvasIndex = uiCanvas._canvasIndex; | |
} |
🧰 Tools
🪛 Biome
[error] 101-101: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
_overlayCanvases: DisorderedArray<UICanvas> = new DisorderedArray(); | ||
/* @internal */ | ||
_overlayCanvasesSortingFlag: boolean = false; | ||
/** @internal */ | ||
_canvases: DisorderedArray<UICanvas> = new DisorderedArray(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consistent use of comment style for @internal
annotations
There is an inconsistency in the comment style used for @internal
annotations. Lines 21 and 25 use /** @internal */
(JSDoc style), while line 23 uses /* @internal */
. For consistency and clarity, it's recommended to use the same comment style throughout.
Apply this diff to make the comment style consistent:
-/* @internal */
+/** @internal */
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
_overlayCanvases: DisorderedArray<UICanvas> = new DisorderedArray(); | |
/* @internal */ | |
_overlayCanvasesSortingFlag: boolean = false; | |
/** @internal */ | |
_canvases: DisorderedArray<UICanvas> = new DisorderedArray(); | |
_overlayCanvases: DisorderedArray<UICanvas> = new DisorderedArray(); | |
/** @internal */ | |
_overlayCanvasesSortingFlag: boolean = false; | |
/** @internal */ | |
_canvases: DisorderedArray<UICanvas> = new DisorderedArray(); |
packages/core/src/ui/UIImage.ts
Outdated
dirtyUpdateFlag &= ~(ImageUpdateFlags.Color & ImageUpdateFlags.Alpha); | ||
} else if (dirtyUpdateFlag & ImageUpdateFlags.Alpha) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix logical error when clearing update flags
The bitwise AND operation between ImageUpdateFlags.Color
and ImageUpdateFlags.Alpha
results in 0
because these flags represent different bits. This means the negation ~(ImageUpdateFlags.Color & ImageUpdateFlags.Alpha)
equals ~0
, leaving dirtyUpdateFlag
unchanged after the operation. To correctly clear both the Color
and Alpha
flags, use the bitwise OR operator instead.
Apply this diff to fix the logical error:
- dirtyUpdateFlag &= ~(ImageUpdateFlags.Color & ImageUpdateFlags.Alpha);
+ dirtyUpdateFlag &= ~(ImageUpdateFlags.Color | ImageUpdateFlags.Alpha);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
dirtyUpdateFlag &= ~(ImageUpdateFlags.Color & ImageUpdateFlags.Alpha); | |
} else if (dirtyUpdateFlag & ImageUpdateFlags.Alpha) { | |
dirtyUpdateFlag &= ~(ImageUpdateFlags.Color | ImageUpdateFlags.Alpha); | |
} else if (dirtyUpdateFlag & ImageUpdateFlags.Alpha) { |
* feat: ui transform
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
🧹 Outside diff range and nitpick comments (19)
packages/core/src/2d/assembler/ISpriteAssembler.ts (2)
9-16
: LGTM: Method signature enhancement is well-structuredThe expanded signature provides comprehensive control over sprite positioning while maintaining a clear single responsibility. The parameter order is logical, and optional parameters are appropriately placed at the end.
Consider documenting the following for better maintainability:
- The coordinate system and units for width/height
- The expected range and default value for the pivot Vector2
- The visual effect of flipX/flipY when combined with pivot points
1-18
: Consider adding validation methods to the interfaceThe interface provides a good foundation for GUI rendering. To enhance robustness, consider adding validation methods for the new parameters:
- Bounds checking for width/height
- Pivot point validation
- Alpha range validation (0.0 to 1.0)
This could be achieved by either:
- Adding a new validation method to the interface
- Creating a separate validation utility class
- Implementing validation in a base assembler class
Would you like me to provide an example implementation for any of these approaches?
packages/core/src/2d/assembler/SimpleSpriteAssembler.ts (1)
Line range hint
42-48
: Add documentation for matrix calculations.The matrix calculations for world transformation could benefit from documentation explaining the mathematical approach and the purpose of each operation.
packages/core/src/ui/UIRenderer.ts (1)
21-63
: Add JSDoc documentation for public and protected properties.While the properties are well-organized with appropriate decorators, adding JSDoc documentation would improve code maintainability and developer experience. Consider documenting:
- Purpose of
depth
and its valid range- Usage of
raycastPadding
- Role of
_alpha
in renderingExample documentation:
/** Determines the rendering order of UI elements. Higher values render on top. */ @ignoreClone depth: number = 0; /** Adjusts the hit area for raycasting in the order: left, bottom, right, top. */ @deepClone raycastPadding: Vector4 = new Vector4(0, 0, 0, 0);packages/core/src/2d/sprite/SpriteMask.ts (3)
239-246
: Consider adding bounds validation for sprite pivot.While the implementation is correct, it could benefit from additional validation to ensure sprite.pivot is not undefined before using it in updatePositions.
protected override _updateLocalBounds(localBounds: BoundingBox): void { const { sprite } = this; - if (sprite) { + if (sprite?.pivot) { SimpleSpriteAssembler.updatePositions(this, this.width, this.height, sprite.pivot, this._flipX, this._flipY); } else { localBounds.min.set(0, 0, 0); localBounds.max.set(0, 0, 0); } }
330-342
: Consider extracting size change condition for better readability.The size change handling could be more explicit by extracting the condition check into a meaningful variable name.
case SpriteModifyFlags.size: - if (this._customWidth === undefined || this._customHeight === undefined) { + const isUsingAutomaticSize = this._customWidth === undefined || this._customHeight === undefined; + if (isUsingAutomaticSize) { this._calDefaultSize(); this._dirtyUpdateFlag |= RendererUpdateFlags.AllPositionAndBounds; } break;
354-362
: Consider adding detailed documentation for flag combinations.While the enum is well-structured, it would benefit from more detailed documentation explaining the specific use cases for each flag combination.
/** + * Update flags specific to sprite masks, extending the base renderer update flags. + * @remarks Extends `RendererUpdateFlags`. + * + * Flags: + * - UV (0x10): Updates UV coordinates only + * - AllPositionAndUV (0x13): Updates both position (local & world) and UV coordinates + * - All (0x1f): Updates all properties including position, UV, and bounds */ enum SpriteMaskUpdateFlags { UV = 0x10,packages/core/src/2d/sprite/SpriteRenderer.ts (1)
84-84
: Improved update flag granularityThe update flag changes are well-structured and more specific, which should improve performance by avoiding unnecessary updates. The flags are appropriately set based on the property being modified and the current draw mode.
Consider documenting the update flag combinations in the class documentation to help maintainers understand when each flag is used and why. This would make it easier to maintain the correct flag usage as the codebase evolves.
Also applies to: 99-99, 116-116, 181-182, 206-207, 221-221, 235-235
packages/core/src/2d/assembler/TiledSpriteAssembler.ts (1)
75-84
: Add comments explaining the matrix transformation logic.The matrix transformation code would benefit from documentation explaining:
- How the pivot affects the transformation
- How the flip transformations are applied
- The order of transformations
packages/core/src/2d/text/TextRenderer.ts (3)
Line range hint
237-257
: Add missing property declarations for mask-related fields.The
maskInteraction
andmaskLayer
getters/setters are accessing undeclared properties.Add these property declarations at the class level:
+private _maskInteraction: SpriteMaskInteraction; +private _maskLayer: number = 0;
Line range hint
478-482
: Consider caching text metrics calculation results.The text metrics calculation is potentially expensive, especially for static text. Consider caching the results when the text content and style properties haven't changed.
+private _cachedMetrics: TextMetrics | null = null; + private _updateLocalData(): void { const { min, max } = this._localBounds; const charRenderInfos = TextRenderer._charRenderInfos; const charFont = this._getSubFont(); - const textMetrics = this.enableWrapping - ? TextUtils.measureTextWithWrap(this) - : TextUtils.measureTextWithoutWrap(this); + if (!this._cachedMetrics || this._isContainDirtyFlag(TextRendererUpdateFlags.FontAllPositionAndBounds)) { + this._cachedMetrics = this.enableWrapping + ? TextUtils.measureTextWithWrap(this) + : TextUtils.measureTextWithoutWrap(this); + } + const textMetrics = this._cachedMetrics;
Line range hint
571-577
: Optimize array sorting for better performance.The sort operation on
charRenderInfos
could be optimized by pre-allocating the array and maintaining the sorted order during insertion.Consider maintaining a sorted order during insertion to avoid the O(n log n) sort operation:
// Pre-allocate the array with the exact size needed charRenderInfos.length = renderElementCount; // Insert elements in sorted order based on texture.instanceId for (let i = 0; i < renderElementCount; i++) { let j = i - 1; const temp = charRenderInfos[i]; while (j >= 0 && charRenderInfos[j].texture.instanceId > temp.texture.instanceId) { charRenderInfos[j + 1] = charRenderInfos[j]; j--; } charRenderInfos[j + 1] = temp; }packages/core/src/ui/UICanvas.ts (4)
23-23
: Consider removing redundant type annotations.TypeScript can infer these types from their initial values. Removing explicit type annotations would make the code more concise while maintaining type safety.
Apply this diff to simplify the type annotations:
- depth: number = 0; + depth = 0; - _isRootCanvas: boolean = false; + _isRootCanvas = false; - _canvasIndex: number = -1; + _canvasIndex = -1; - _sortDistance: number = 0; + _sortDistance = 0; - private _sortOrder: number = 0; + private _sortOrder = 0; - private _distance: number = 10; + private _distance = 10; - private _referenceResolution: Vector2 = new Vector2(800, 600); + private _referenceResolution = new Vector2(800, 600);Also applies to: 32-32, 35-35, 56-56, 80-80, 82-82, 86-86
120-120
: Improve readability by avoiding assignment within expression.The current implementation uses assignment within a logical expression, which can be confusing. Consider using a more explicit if statement.
Apply this diff to improve readability:
- (referenceResolution.x !== val.x || referenceResolution.y !== val.y) && referenceResolution.copyFrom(val); + if (referenceResolution.x !== val.x || referenceResolution.y !== val.y) { + referenceResolution.copyFrom(val); + }
587-598
: Document intentional switch case fallthrough.The switch cases in
_setRealRenderMode
have intentional fallthrough behavior for initialization, but this should be documented to prevent confusion.Add comments to indicate intentional fallthrough:
switch (curRealMode) { case CanvasRenderMode.ScreenSpaceOverlay: this._addCanvasListener(); + // Fallthrough intentional for initialization case CanvasRenderMode.ScreenSpaceCamera: this._adapterPoseInScreenSpace(); this._adapterSizeInScreenSpace(); + // Fallthrough intentional for initialization case CanvasRenderMode.WorldSpace: componentsManager.addUICanvas(curRealMode, this); break;🧰 Tools
🪛 Biome
[error] 588-589: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 590-592: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
461-461
: Improve readability by avoiding assignment within expression.The assignment within the logical expression makes the code harder to read.
Apply this diff to improve readability:
- child.isActive && (depth = this._walk(child, elements, depth)); + if (child.isActive) { + depth = this._walk(child, elements, depth); + }🧰 Tools
🪛 Biome
[error] 461-461: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/2d/assembler/SlicedSpriteAssembler.ts (1)
Line range hint
28-94
: Ensure consistency in renderer type usage across methodsIn the
updatePositions
method, therenderer
parameter is typed asSpriteRenderer | SpriteMask | UIImage
, while in other methods likeresetData
,updateUVs
, andupdateColor
, it's typed asSpriteRenderer | UIImage
. For consistency and maintainability, consider unifying the renderer types across these methods unless there's a specific reason for the difference.packages/core/src/ui/UIImage.ts (2)
223-223
: Use Local 'sprite' Variable Instead of 'this.sprite'In the
_render
method, a localsprite
variable is already defined. For consistency and to prevent potential null reference errors, consider using the localsprite
variable instead ofthis.sprite
when accessing the texture.Apply this diff to make the change:
- subRenderElement.set(this, material, subChunk.chunk.primitive, subChunk.subMesh, this.sprite.texture, subChunk); + subRenderElement.set(this, material, subChunk.chunk.primitive, subChunk.subMesh, sprite.texture, subChunk);
275-278
: Avoid Nullifying Class Properties in _onDestroyIn the
_onDestroy
method, class properties such asthis._entity
,this._color
,this._sprite
, andthis._assembler
are set tonull
.Setting class properties to
null
in the destructor is generally unnecessary because the garbage collector will handle unused objects. Removing these assignments can simplify the code and slightly improve performance.Apply this diff to remove unnecessary null assignments:
- this._entity = null; - this._color = null; - this._sprite = null; - this._assembler = null;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (12)
- packages/core/src/2d/assembler/ISpriteAssembler.ts (1 hunks)
- packages/core/src/2d/assembler/SimpleSpriteAssembler.ts (4 hunks)
- packages/core/src/2d/assembler/SlicedSpriteAssembler.ts (6 hunks)
- packages/core/src/2d/assembler/TiledSpriteAssembler.ts (6 hunks)
- packages/core/src/2d/sprite/SpriteMask.ts (9 hunks)
- packages/core/src/2d/sprite/SpriteRenderer.ts (14 hunks)
- packages/core/src/2d/text/TextRenderer.ts (20 hunks)
- packages/core/src/Renderer.ts (9 hunks)
- packages/core/src/particle/ParticleRenderer.ts (3 hunks)
- packages/core/src/ui/UICanvas.ts (1 hunks)
- packages/core/src/ui/UIImage.ts (1 hunks)
- packages/core/src/ui/UIRenderer.ts (1 hunks)
🧰 Additional context used
🪛 Biome
packages/core/src/2d/sprite/SpriteRenderer.ts
[error] 321-321: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
[error] 322-322: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
packages/core/src/ui/UICanvas.ts
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 242-242: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 343-343: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 347-347: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 348-348: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 346-351: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 461-461: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 578-579: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 588-589: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 590-592: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIRenderer.ts
[error] 152-152: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 156-156: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 157-157: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 155-159: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
🔇 Additional comments (25)
packages/core/src/2d/assembler/ISpriteAssembler.ts (2)
1-1
: LGTM: Import statement is correctly addedThe Vector2 import is necessary for the new pivot parameter type in the updatePositions method.
18-18
: LGTM: Color update method appropriately enhancedThe addition of an optional alpha parameter provides better control over transparency while maintaining backward compatibility.
Let's verify the alpha parameter usage across implementations:
✅ Verification successful
Let me try a different approach to search for updateColor implementations with a more flexible pattern.
This broader search should help us find all implementations and usages of the updateColor method, regardless of their parameter structure, so we can verify the alpha parameter handling across the codebase.
Optional alpha parameter is consistently implemented across the codebase
The verification shows that the optional alpha parameter is properly implemented:
- The interface
ISpriteAssembler
correctly declaresupdateColor
with optional alpha parameter- All concrete implementations (SimpleSpriteAssembler, TiledSpriteAssembler, SlicedSpriteAssembler) consistently:
- Accept the alpha parameter
- Provide a default value of 1
- Properly combine it with the renderer's color alpha value
The change is well-integrated and maintains consistency across the codebase.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for updateColor implementations to ensure consistent alpha handling ast-grep --pattern 'updateColor(renderer: $_, alpha?: number) { $$$ }'Length of output: 74
Script:
#!/bin/bash # Search for updateColor method implementations with broader pattern ast-grep --pattern 'updateColor($$$) { $$$ }' # Also search for updateColor method declarations in interfaces and abstract classes ast-grep --pattern 'updateColor($$$): void;' # Backup search using ripgrep for any updateColor occurrences rg "updateColor" -A 3Length of output: 2704
packages/core/src/2d/assembler/SimpleSpriteAssembler.ts (2)
16-16
: Consider implementing a common Renderable interface.Referencing the previous discussion: A common
Renderable
interface forSpriteRenderer
,SpriteMask
, andUIImage
would improve code maintainability and reduce type union complexity.
40-41
: 🛠️ Refactor suggestionConsider using unary negation for better performance.
Based on the previous performance discussion, using unary negation (
-width
instead of? -width : width
) could be more efficient:-const sx = flipX ? -width : width; -const sy = flipY ? -height : height; +const sx = width * (flipX ? -1 : 1); +const sy = height * (flipY ? -1 : 1);Likely invalid or redundant comment.
packages/core/src/ui/UIRenderer.ts (1)
1-20
: LGTM! Well-structured class with appropriate dependencies.The class is well-organized with clear dependencies and proper use of the @dependentComponents decorator to ensure UITransform availability.
packages/core/src/particle/ParticleRenderer.ts (2)
Line range hint
262-279
: Verify consistency with related components.The changes to flag management in
ParticleRenderer
are part of a larger GUI infrastructure update. We should ensure consistent flag usage patterns acrossSpriteMask
,SpriteRenderer
, and other related components.Let's verify the consistency:
#!/bin/bash # Compare flag usage patterns across components echo "Analyzing flag patterns in related components..." rg --type ts "UpdateFlags" -g "**(Sprite|Particle)**" -B 1 -A 1 # Check for potential inconsistencies in bounds management echo "Checking bounds management consistency..." rg --type ts "AllBounds|WorldVolume" -g "**(Sprite|Particle)**"
262-262
: Verify the bounds update behavior.The changes consistently add
RendererUpdateFlags.AllBounds
to various update methods. While this aligns with the enhanced bounds management mentioned in the summary, we should verify that:
- The bounds are correctly updated in all scenarios
- There are no redundant bounds updates that could impact performance
Let's analyze the bounds update behavior:
Also applies to: 271-271, 279-279
✅ Verification successful
Let me try different search patterns to analyze the bounds update behavior.
The previous search patterns were too restrictive. These broader patterns should help us understand:
- All occurrences of AllBounds flag usage
- The pattern of _dirtyUpdateFlag assignments
- The RendererUpdateFlags enum definition to understand flag relationships
Based on the analysis of the search results, I can now provide a final response about the bounds update behavior:
The bounds update behavior is correctly implemented
The code shows a well-structured and consistent pattern for bounds updates:
RendererUpdateFlags.AllBounds
(0xc) correctly combines bothLocalBounds
(0x4) andWorldBounds
(0x8) flags- The flag is used appropriately in different scenarios:
- In
_onWorldVolumeChanged
: Updates bounds when volume changes- In
_onGeneratorParamsChanged
: Updates bounds along with generator and transform volumes- In
_onTransformChanged
: Updates bounds along with transform volumeThe implementation ensures bounds are updated when necessary and there are no redundant updates since:
- Each update method sets specific flags based on what actually changed
- The dirty flags are cleared after processing (
&= ~flag
pattern)- The flag combinations are used efficiently through bitwise operations
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for AllBounds flag usage patterns echo "Analyzing bounds update patterns..." rg --type ts "AllBounds" -B 2 -A 2 # Search for potential performance impact echo "Checking for potential redundant updates..." ast-grep --pattern 'this._dirtyUpdateFlag |= $flag'Length of output: 627
Script:
#!/bin/bash # Search for AllBounds flag usage in the entire codebase echo "Analyzing AllBounds flag usage..." rg "AllBounds" --type ts -B 3 -A 3 # Search for _dirtyUpdateFlag assignments echo -e "\nAnalyzing update flag assignments..." rg "_dirtyUpdateFlag.*=" --type ts -B 2 -A 2 # Look for the RendererUpdateFlags enum definition echo -e "\nSearching for RendererUpdateFlags definition..." rg "enum.*RendererUpdateFlags" --type ts -A 10Length of output: 39593
packages/core/src/2d/sprite/SpriteMask.ts (2)
78-78
: LGTM: Consistent update flag usage across property setters.The change to use
RendererUpdateFlags.AllPositionAndBounds
across all dimension and flip property setters ensures comprehensive updates of both position and bounds, which is more appropriate than the previousWorldVolume
flag.Also applies to: 100-100, 114-114, 128-128
253-254
: LGTM: Efficient early returns and consistent flag usage.The implementation correctly:
- Performs early validation of required properties
- Uses the new
AllPositions
flag for position updates- Clears the flag after updates
Also applies to: 269-271
packages/core/src/2d/sprite/SpriteRenderer.ts (5)
10-10
: LGTM: Component type initializationThe addition of ComponentType import and initialization in the constructor is appropriate for proper component type management.
Also applies to: 268-268
347-349
: LGTM: Enhanced position updatesThe position update logic now correctly handles flipped sprites by passing the flip parameters to the assembler.
410-427
: Well-structured sprite change handlingThe sprite change handling is now more comprehensive with:
- Proper handling of custom dimensions
- Draw mode-specific update flag management
- Appropriate handling of border, region, and pivot changes
Also applies to: 431-433, 437-437, 443-443
458-469
: LGTM: Well-documented update flagsThe SpriteRendererUpdateFlags enum is well-structured with clear documentation for each flag combination, making it easier to understand and maintain the update system.
316-328
:⚠️ Potential issueSimplify boolean expressions in bounds calculation
The local bounds calculation logic is correct, but there's unnecessary use of double negation (
!!
) operators.Apply this diff to simplify the expressions:
- pivotX = !!this.flipX ? 1 - pivotX : pivotX; - pivotY = !!this.flipY ? 1 - pivotY : pivotY; + pivotX = this.flipX ? 1 - pivotX : pivotX; + pivotY = this.flipY ? 1 - pivotY : pivotY;Likely invalid or redundant comment.
🧰 Tools
🪛 Biome
[error] 321-321: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
[error] 322-322: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
packages/core/src/Renderer.ts (1)
499-501
: LGTM: Correct bounds transformation implementation.The method properly transforms local bounds to world space using the entity's world matrix.
packages/core/src/2d/assembler/TiledSpriteAssembler.ts (2)
37-44
: 🛠️ Refactor suggestionConsider using a configuration object for better parameter management.
The method now has multiple parameters that could be grouped logically. This would make the method more maintainable and easier to extend in the future.
interface SpriteTransformConfig { width: number; height: number; pivot: Vector2; flipX?: boolean; flipY?: boolean; } static updatePositions(renderer: SpriteRenderer | UIImage, config: SpriteTransformConfig): void {
179-189
: 🛠️ Refactor suggestionConsider using destructuring for cleaner code.
As suggested in previous reviews, the color extraction could be simplified using destructuring. However, if the current approach is preferred for consistency or readability reasons, that's acceptable too.
const { _subChunk: { chunk: { vertices }, vertexArea }, color: { r, g, b, a } } = renderer; const finalAlpha = a * alpha;packages/core/src/2d/text/TextRenderer.ts (2)
691-701
: Well-structured update flags enum.Good job on extending
RendererUpdateFlags
with text-specific flags. The enum provides clear categorization of different update scenarios.
306-320
:⚠️ Potential issueAdd missing
_dirtyUpdateFlag
property declaration.The flag management methods are using an undeclared property.
Add this property declaration at the class level:
+private _dirtyUpdateFlag: number = 0;
Likely invalid or redundant comment.
packages/core/src/2d/assembler/SlicedSpriteAssembler.ts (5)
Line range hint
19-26
: Implementation ofresetData
method looks goodThe changes to the
resetData
method correctly extend support toUIImage
alongsideSpriteRenderer
. The allocation and assignment of thesubChunk
are handled appropriately.
85-94
: Confirm flip logic and coordinate transformationsThe calculation of
sx
andsy
for flip transformations appears correct. However, please verify that the sign inversion works as intended for all renderer types, especially considering the addition ofUIImage
.
36-36
: Verify that all renderer types have asprite
propertyThe line
const { sprite } = renderer;
assumes that all renderer types have asprite
property. Please confirm thatSpriteRenderer
,SpriteMask
, andUIImage
all have asprite
property accessible in this context.To confirm this, run the following script:
#!/bin/bash # Description: Verify that all renderer types have a 'sprite' property. # Check for 'sprite' property in SpriteRenderer ast-grep --pattern $'class SpriteRenderer { $$$ sprite: $_ $$$ }' # Check for 'sprite' property in SpriteMask ast-grep --pattern $'class SpriteMask { $$$ sprite: $_ $$$ }' # Check for 'sprite' property in UIImage ast-grep --pattern $'class UIImage { $$$ sprite: $_ $$$ }'
Line range hint
125-136
: Verify access tosprite._getUVs()
for all renderer typesIn the
updateUVs
method, the code accessesrenderer.sprite._getUVs()
. Ensure that bothSpriteRenderer
andUIImage
have asprite
property with a_getUVs()
method to prevent runtime errors.Run the following script to verify:
#!/bin/bash # Description: Verify that both SpriteRenderer and UIImage have a 'sprite' property with a '_getUVs()' method. # Check for '_getUVs' method in Sprite class ast-grep --pattern $'class Sprite { $$$ _getUVs() { $$$ } $$$ }' # Ensure that SpriteRenderer and UIImage have a 'sprite' property of type Sprite # Check for 'sprite' property in SpriteRenderer ast-grep --pattern $'class SpriteRenderer { $$$ sprite: Sprite $$$ }' # Check for 'sprite' property in UIImage ast-grep --pattern $'class UIImage { $$$ sprite: Sprite $$$ }'
138-149
: Confirm thatcolor
property exists on all renderer typesIn the
updateColor
method, the color components are extracted fromrenderer.color
. Please verify that bothSpriteRenderer
andUIImage
have acolor
property to ensure this operation is valid for all renderer instances.To verify, execute:
✅ Verification successful
Let me try a different approach to verify the existence of the color property.
This should help us understand:
- The class definitions and their direct properties
- Any inheritance relationships that might provide the color property
- The context around these classes to verify the color property's existence
Let me generate the final response based on the evidence found in the codebase.
Both
SpriteRenderer
andUIImage
have the requiredcolor
propertyBoth classes have a private
_color
property of typeColor
initialized withnew Color(1, 1, 1, 1)
. Let's verify they expose this via a getter:
The
color
property is properly implemented in both classesBoth
SpriteRenderer
andUIImage
classes have:
- A private
_color: Color
property initialized withnew Color(1, 1, 1, 1)
- A public getter
color(): Color
that returns the_color
property- A public setter
color(value: Color)
for updating the color valueThe usage of
renderer.color
in theupdateColor
method is valid and safe for both renderer types.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that SpriteRenderer and UIImage classes have a 'color' property. # Check for 'color' property in SpriteRenderer ast-grep --pattern $'class SpriteRenderer { $$$ color: $_ $$$ }' # Check for 'color' property in UIImage ast-grep --pattern $'class UIImage { $$$ color: $_ $$$ }'Length of output: 363
Script:
#!/bin/bash # Search for color property or getter in SpriteRenderer and UIImage # Using ripgrep for a broader search including potential inheritance # Search for SpriteRenderer class definition and its properties rg "class SpriteRenderer" -A 20 # Search for UIImage class definition and its properties rg "class UIImage" -A 20 # Search for potential base classes that might contain the color property rg "extends" --type ts | rg -i "SpriteRenderer|UIImage"Length of output: 3065
Script:
#!/bin/bash # Search for color getter in both classes rg "get color\(\)" -A 5 --type tsLength of output: 2550
packages/core/src/ui/UIImage.ts (1)
102-119
: Ensure Balanced Resource Reference Counting for SpritesIn the
set sprite(value: Sprite | null)
method, when setting a new sprite, the old sprite's reference count is decremented, and the new sprite's reference count is incremented if it is notnull
. However, whenvalue
isnull
, the new sprite is not incremented, which is correct. Ensure that this behavior maintains balanced reference counting throughout the lifecycle of the sprite.Run the following script to verify that all increments and decrements of sprite reference counts are balanced:
Check that for every increment (
1
), there is a corresponding decrement (-1
), ensuring that resources are properly managed and there are no memory leaks.✅ Verification successful
Let me gather more context about the resource reference counting implementation by checking the complete class implementation.
After analyzing the complete implementation of UIImage.ts, I can now provide the final response:
Resource Reference Counting is Properly Balanced
The reference counting implementation in UIImage is correctly balanced:
- In the
set sprite()
method:
- Decrements (
-1
) the old sprite's reference count and removes its listener- Increments (
+1
) the new sprite's reference count and adds its listener- In the
_onDestroy()
method:
- Properly decrements (
-1
) the sprite's reference count during cleanup- Removes the sprite's listener
The implementation ensures that reference counts are properly managed throughout the lifecycle of the UIImage component, preventing memory leaks. The null checks and equality comparisons also prevent unnecessary reference count modifications.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify balanced usage of _addResourceReferCount in UIImage.ts # Search for all increments and decrements of _addResourceReferCount in UIImage.ts rg '_addResourceReferCount' packages/core/src/ui/UIImage.tsLength of output: 206
Script:
#!/bin/bash # Get the complete UIImage.ts file content to understand the reference counting logic cat packages/core/src/ui/UIImage.tsLength of output: 10527
static updateColor(renderer: SpriteRenderer, alpha: number = 1): void { | ||
const subChunk = renderer._subChunk; | ||
const { r, g, b, a } = renderer.color; | ||
const finalAlpha = a * alpha; | ||
const vertices = subChunk.chunk.vertices; | ||
for (let i = 0, o = subChunk.vertexArea.start + 5; i < 4; ++i, o += 9) { | ||
vertices[o] = r; | ||
vertices[o + 1] = g; | ||
vertices[o + 2] = b; | ||
vertices[o + 3] = a; | ||
vertices[o + 3] = finalAlpha; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add parameter validation for alpha value.
The alpha parameter should be validated to ensure it's within the valid range [0, 1]:
static updateColor(renderer: SpriteRenderer, alpha: number = 1): void {
+ if (alpha < 0 || alpha > 1) {
+ throw new Error("Alpha value must be between 0 and 1");
+ }
const subChunk = renderer._subChunk;
const { r, g, b, a } = renderer.color;
const finalAlpha = a * alpha;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
static updateColor(renderer: SpriteRenderer, alpha: number = 1): void { | |
const subChunk = renderer._subChunk; | |
const { r, g, b, a } = renderer.color; | |
const finalAlpha = a * alpha; | |
const vertices = subChunk.chunk.vertices; | |
for (let i = 0, o = subChunk.vertexArea.start + 5; i < 4; ++i, o += 9) { | |
vertices[o] = r; | |
vertices[o + 1] = g; | |
vertices[o + 2] = b; | |
vertices[o + 3] = a; | |
vertices[o + 3] = finalAlpha; | |
static updateColor(renderer: SpriteRenderer, alpha: number = 1): void { | |
if (alpha < 0 || alpha > 1) { | |
throw new Error("Alpha value must be between 0 and 1"); | |
} | |
const subChunk = renderer._subChunk; | |
const { r, g, b, a } = renderer.color; | |
const finalAlpha = a * alpha; | |
const vertices = subChunk.chunk.vertices; | |
for (let i = 0, o = subChunk.vertexArea.start + 5; i < 4; ++i, o += 9) { | |
vertices[o] = r; | |
vertices[o + 1] = g; | |
vertices[o + 2] = b; | |
vertices[o + 3] = finalAlpha; |
packages/core/src/ui/UIRenderer.ts
Outdated
override _prepareRender(context: RenderContext): void { | ||
// Update once per frame per renderer, not influenced by batched | ||
if (this._renderFrameCount !== this.engine.time.frameCount) { | ||
this._update(context); | ||
} | ||
|
||
this._render(context); | ||
|
||
// union camera global macro and renderer macro. | ||
ShaderMacroCollection.unionCollection( | ||
context.camera._globalShaderMacro, | ||
this.shaderData._macroCollection, | ||
this._globalShaderMacro | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
LGTM! Consider caching the frame count.
The _prepareRender method's implementation is correct, but consider caching this.engine.time.frameCount
to avoid multiple property accesses within the same frame.
override _prepareRender(context: RenderContext): void {
+ const currentFrame = this.engine.time.frameCount;
// Update once per frame per renderer, not influenced by batched
- if (this._renderFrameCount !== this.engine.time.frameCount) {
+ if (this._renderFrameCount !== currentFrame) {
this._update(context);
}
Committable suggestion was skipped due to low confidence.
packages/core/src/ui/UIRenderer.ts
Outdated
_raycast(ray: Ray, out: HitResult, distance: number = Number.MAX_SAFE_INTEGER): boolean { | ||
const entity = this._entity; | ||
const plane = UIRenderer._tempPlane; | ||
const transform = entity.transform; | ||
const normal = plane.normal.copyFrom(transform.worldForward); | ||
plane.distance = -Vector3.dot(normal, transform.worldPosition); | ||
const curDistance = ray.intersectPlane(plane); | ||
if (curDistance >= 0 && curDistance < distance) { | ||
const hitPointWorld = ray.getPoint(curDistance, UIRenderer._tempVec30); | ||
const worldMatrixInv = UIRenderer._tempMat; | ||
Matrix.invert(this.entity.transform.worldMatrix, worldMatrixInv); | ||
const localPosition = UIRenderer._tempVec31; | ||
Vector3.transformCoordinate(hitPointWorld, worldMatrixInv, localPosition); | ||
if (this._hitTest(localPosition)) { | ||
out.distance = curDistance; | ||
out.entity = entity; | ||
out.component = this; | ||
out.normal.copyFrom(normal); | ||
out.point.copyFrom(hitPointWorld); | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
protected _hitTest(localPosition: Vector3): boolean { | ||
const { x, y } = localPosition; | ||
const uiTransform = <UITransform>this._transform; | ||
const { x: width, y: height } = uiTransform.size; | ||
const { x: pivotX, y: pivotY } = uiTransform.pivot; | ||
const { x: paddingLeft, y: paddingBottom, z: paddingRight, w: paddingTop } = this.raycastPadding; | ||
return ( | ||
x >= -width * pivotX + paddingLeft && | ||
x <= width * (1 - pivotX) - paddingRight && | ||
y >= -height * pivotY + paddingTop && | ||
y <= height * (1 - pivotY) - paddingBottom | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Optimize raycasting and improve variable naming.
The raycasting implementation is correct but could be optimized:
- Cache the ray.intersectPlane result
- Use more descriptive variable names for size dimensions
- Destructure transform properties for better readability
_raycast(ray: Ray, out: HitResult, distance: number = Number.MAX_SAFE_INTEGER): boolean {
const { _entity: entity } = this;
const { transform } = entity;
const { worldForward, worldMatrix, worldPosition } = transform;
const plane = UIRenderer._tempPlane;
- const normal = plane.normal.copyFrom(transform.worldForward);
+ const normal = plane.normal.copyFrom(worldForward);
plane.distance = -Vector3.dot(normal, worldPosition);
- const curDistance = ray.intersectPlane(plane);
+ const intersectionDistance = ray.intersectPlane(plane);
- if (curDistance >= 0 && curDistance < distance) {
+ if (intersectionDistance >= 0 && intersectionDistance < distance) {
const hitPointWorld = ray.getPoint(curDistance, UIRenderer._tempVec30);
const worldMatrixInv = UIRenderer._tempMat;
- Matrix.invert(this.entity.transform.worldMatrix, worldMatrixInv);
+ Matrix.invert(worldMatrix, worldMatrixInv);
And in the _hitTest method:
protected _hitTest(localPosition: Vector3): boolean {
const { x, y } = localPosition;
const uiTransform = <UITransform>this._transform;
- const { x: width, y: height } = uiTransform.size;
+ const { x: elementWidth, y: elementHeight } = uiTransform.size;
const { x: pivotX, y: pivotY } = uiTransform.pivot;
Committable suggestion was skipped due to low confidence.
TransformVolume = 0x10, | ||
/** On Generator Bounds Related Params Changed */ | ||
GeneratorVolume = 0x4 | ||
GeneratorVolume = 0x20 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Let me analyze the findings. The ripgrep results show that these flags are used in ParticleGenerator.ts to check and clear dirty flags related to transform and generator volume changes. The flags are used in conditional checks and flag clearing operations.
Since we found references to these flags being used in the codebase, and they appear to be part of a dirty flag system for particle updates, changing their values could break existing functionality that relies on these specific bit values.
Let me generate the final response:
Changing these flag values could break existing functionality
The flags TransformVolume
and GeneratorVolume
are actively used in ParticleGenerator.ts as part of the particle system's dirty flag mechanism. Shifting their values from 0x2 and 0x4 to 0x10 and 0x20 respectively would break the dirty flag checks in ParticleGenerator.ts that expect the original values.
- ParticleGenerator.ts uses these flags to check and clear dirty states via:
_isContainDirtyFlag()
checks_setDirtyFlagFalse()
operations
The flag values should remain unchanged to maintain compatibility with the existing dirty flag system.
🔗 Analysis chain
Verify the impact of flag value changes.
The flag values for TransformVolume
and GeneratorVolume
have been shifted left by 4 bits (from 0x2→0x10 and 0x4→0x20). This change could break existing code that directly uses these flag values.
Let's verify the usage of these flags across the codebase:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for direct usage of the old flag values
echo "Searching for potential breaking changes..."
rg --type ts "0x2|0x4" --glob "!**/ParticleRenderer.ts"
# Search for all references to these flags
echo "Searching for all references to these flags..."
rg --type ts "TransformVolume|GeneratorVolume" --glob "!**/ParticleRenderer.ts"
Length of output: 227346
get localBounds(): BoundingBox { | ||
if (this._dirtyUpdateFlag & RendererUpdateFlags.LocalBounds) { | ||
this._updateLocalBounds(this._localBounds); | ||
this._dirtyUpdateFlag &= ~RendererUpdateFlags.LocalBounds; | ||
} | ||
return this._localBounds; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider caching BoundingBox calculations.
The getters recalculate bounds whenever the dirty flag is set. For performance optimization in scenarios with frequent bounds access but infrequent changes, consider:
- Caching the calculated bounds
- Only updating when the dirty flag is set
get localBounds(): BoundingBox {
if (this._dirtyUpdateFlag & RendererUpdateFlags.LocalBounds) {
this._updateLocalBounds(this._localBounds);
this._dirtyUpdateFlag &= ~RendererUpdateFlags.LocalBounds;
+ this._cachedLocalBounds = this._localBounds.clone();
+ } else if (this._cachedLocalBounds) {
+ return this._cachedLocalBounds;
}
return this._localBounds;
}
Also applies to: 157-165
None = 0x0, | ||
LocalPosition = 0x1, | ||
WorldPosition = 0x2, | ||
LocalBounds = 0x4, | ||
WorldBounds = 0x8, | ||
|
||
/** LocalPosition | WorldPosition */ | ||
AllPositions = 0x3, | ||
/** LocalPosition | LocalBounds */ | ||
LocalPositionAndBounds = 0x5, | ||
/** WorldPosition | WorldBounds */ | ||
WorldPositionAndBounds = 0xa, | ||
/** LocalBounds | WorldBounds */ | ||
AllBounds = 0xc, | ||
/** LocalPosition | WorldPosition | LocalBounds | WorldBounds */ | ||
AllPositionAndBounds = 0xf |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider using const enum for better type safety.
The enum defines bit flags for renderer updates. Consider using const enum
to:
- Ensure values are inlined at compile time
- Prevent runtime object creation
- Provide better type safety
- export enum RendererUpdateFlags {
+ export const enum RendererUpdateFlags {
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
None = 0x0, | |
LocalPosition = 0x1, | |
WorldPosition = 0x2, | |
LocalBounds = 0x4, | |
WorldBounds = 0x8, | |
/** LocalPosition | WorldPosition */ | |
AllPositions = 0x3, | |
/** LocalPosition | LocalBounds */ | |
LocalPositionAndBounds = 0x5, | |
/** WorldPosition | WorldBounds */ | |
WorldPositionAndBounds = 0xa, | |
/** LocalBounds | WorldBounds */ | |
AllBounds = 0xc, | |
/** LocalPosition | WorldPosition | LocalBounds | WorldBounds */ | |
AllPositionAndBounds = 0xf | |
export const enum RendererUpdateFlags { | |
None = 0x0, | |
LocalPosition = 0x1, | |
WorldPosition = 0x2, | |
LocalBounds = 0x4, | |
WorldBounds = 0x8, | |
/** LocalPosition | WorldPosition */ | |
AllPositions = 0x3, | |
/** LocalPosition | LocalBounds */ | |
LocalPositionAndBounds = 0x5, | |
/** WorldPosition | WorldBounds */ | |
WorldPositionAndBounds = 0xa, | |
/** LocalBounds | WorldBounds */ | |
AllBounds = 0xc, | |
/** LocalPosition | WorldPosition | LocalBounds | WorldBounds */ | |
AllPositionAndBounds = 0xf |
@@ -18,7 +19,7 @@ export class TiledSpriteAssembler { | |||
static _uvRow = new DisorderedArray<number>(); | |||
static _uvColumn = new DisorderedArray<number>(); | |||
|
|||
static resetData(renderer: SpriteRenderer, vertexCount: number): void { | |||
static resetData(renderer: SpriteRenderer | UIImage, vertexCount: number): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider creating a shared interface for renderer types.
Since multiple methods use the union type SpriteRenderer | UIImage
, consider extracting this to a shared interface or type alias to improve maintainability and ensure consistency.
type Renderable = SpriteRenderer | UIImage;
static resetData(renderer: Renderable, vertexCount: number): void {
packages/core/src/ui/UICanvas.ts
Outdated
case EntityModifyFlags.Parent: | ||
const rootCanvas = UIUtils.getRootCanvasInParent(this._entity); | ||
rootCanvas && (rootCanvas._hierarchyDirty = true); | ||
this._setIsRootCanvas(!rootCanvas); | ||
UIUtils.registerUIToCanvas(this, rootCanvas); | ||
UIUtils.registerEntityListener(this); | ||
case EntityModifyFlags.UIGroupEnableInScene: | ||
UIUtils.registerUIToGroup(this, UIUtils.getGroupInParents(this._entity)); | ||
break; | ||
default: | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix switch case fallthrough and variable scoping.
The switch case for EntityModifyFlags.Parent
falls through to the next case, and the rootCanvas
variable could leak to other cases.
Apply this diff to fix the issues:
case EntityModifyFlags.UICanvasEnableInScene:
+ break;
case EntityModifyFlags.Parent:
+ {
const rootCanvas = UIUtils.getRootCanvasInParent(this._entity);
if (rootCanvas) {
rootCanvas._hierarchyDirty = true;
}
this._setIsRootCanvas(!rootCanvas);
UIUtils.registerUIToCanvas(this, rootCanvas);
UIUtils.registerEntityListener(this);
+ }
+ break;
case EntityModifyFlags.UIGroupEnableInScene:
Committable suggestion was skipped due to low confidence.
🧰 Tools
🪛 Biome
[error] 347-347: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 348-348: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 346-351: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIImage.ts
Outdated
get color(): Color { | ||
return this._color; | ||
} | ||
|
||
set color(value: Color) { | ||
if (this._color !== value) { | ||
this._color.copyFrom(value); | ||
this._dirtyUpdateFlag |= ImageUpdateFlags.Color; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Monitor Changes to the Color Property
Direct modifications to the color
property via the returned Color
object do not trigger updates to the rendering state because the setter is bypassed. This means that changes to color.r
, color.g
, etc., won't automatically set the ImageUpdateFlags.Color
flag.
Implement a mechanism to detect changes to the Color
object and update the rendering state accordingly. One approach is to use an observer pattern or modify the Color
class to accept a change callback.
For example, modify the initialization:
- private _color: Color = new Color(1, 1, 1, 1);
+ private _color: Color = new Color(1, 1, 1, 1, () => {
+ this._dirtyUpdateFlag |= ImageUpdateFlags.Color;
+ });
Update the Color
class to include a change callback:
class Color {
private _onChange: () => void;
constructor(r: number, g: number, b: number, a: number, onChange?: () => void) {
this._r = r;
this._g = g;
this._b = b;
this._a = a;
this._onChange = onChange;
}
set r(value: number) {
this._r = value;
this._onChange?.();
}
// Repeat for g, b, a
}
This ensures that any direct modifications to the color properties will update the rendering appropriately.
packages/core/src/ui/UIImage.ts
Outdated
switch (value) { | ||
case SpriteDrawMode.Simple: | ||
this._assembler = SimpleSpriteAssembler; | ||
break; | ||
case SpriteDrawMode.Sliced: | ||
this._assembler = SlicedSpriteAssembler; | ||
break; | ||
case SpriteDrawMode.Tiled: | ||
this._assembler = TiledSpriteAssembler; | ||
break; | ||
default: | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle Default Case in Switch Statement for 'drawMode' Setter
In the set drawMode(value: SpriteDrawMode)
method, the switch
statement does not handle unexpected drawMode
values in the default
case. If an invalid drawMode
is provided, _assembler
could remain undefined, potentially causing runtime errors when _assembler
is used later.
Consider handling the default case to ensure _assembler
is always assigned. You can throw an error or assign a default assembler to prevent potential issues.
Apply this diff to handle the default case:
default:
+ throw new Error(`Unsupported drawMode value: ${value}`);
break;
Alternatively, assign a default assembler:
default:
+ this._assembler = SimpleSpriteAssembler;
break;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
switch (value) { | |
case SpriteDrawMode.Simple: | |
this._assembler = SimpleSpriteAssembler; | |
break; | |
case SpriteDrawMode.Sliced: | |
this._assembler = SlicedSpriteAssembler; | |
break; | |
case SpriteDrawMode.Tiled: | |
this._assembler = TiledSpriteAssembler; | |
break; | |
default: | |
break; | |
} | |
switch (value) { | |
case SpriteDrawMode.Simple: | |
this._assembler = SimpleSpriteAssembler; | |
break; | |
case SpriteDrawMode.Sliced: | |
this._assembler = SlicedSpriteAssembler; | |
break; | |
case SpriteDrawMode.Tiled: | |
this._assembler = TiledSpriteAssembler; | |
break; | |
default: | |
throw new Error(`Unsupported drawMode value: ${value}`); | |
break; | |
} | |
``` | |
Option 2 (default assembler): | |
```suggestion | |
switch (value) { | |
case SpriteDrawMode.Simple: | |
this._assembler = SimpleSpriteAssembler; | |
break; | |
case SpriteDrawMode.Sliced: | |
this._assembler = SlicedSpriteAssembler; | |
break; | |
case SpriteDrawMode.Tiled: | |
this._assembler = TiledSpriteAssembler; | |
break; | |
default: | |
this._assembler = SimpleSpriteAssembler; | |
break; | |
} |
packages/core/src/ui/UICanvas.ts
Outdated
*/ | ||
@ignoreClone | ||
_onEntityModify(flag: EntityModifyFlags): void { | ||
switch (flag) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When UIGroupEnableInScene , root canvas should be dirty and walk again.
packages/core/src/ui/UICanvas.ts
Outdated
); | ||
} | ||
|
||
private _adapterPoseInScreenSpace(): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adapter -> adapt
packages/core/src/ui/UICanvas.ts
Outdated
} | ||
} | ||
|
||
private _adapterSizeInScreenSpace(): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adapter -> adapt
packages/core/src/ui/UICanvas.ts
Outdated
const { frameCount } = engine.time; | ||
const renderElement = (this._renderElement = engine._renderElementPool.get()); | ||
this._updateSortDistance(context.virtualCamera.position); | ||
renderElement.set(this.sortOrder, this._sortDistance); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In 3d renderer , renderElement sets priority and distanceSort generally within the life cycle of _render. Is it appropriate to put it here?
packages/core/src/ui/UICanvas.ts
Outdated
rootCanvas && (rootCanvas._hierarchyDirty = true); | ||
this._setIsRootCanvas(!rootCanvas); | ||
UIUtils.registerUIToCanvas(this, rootCanvas); | ||
UIUtils.registerEntityListener(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These lines of logic are duplicated with those in _onEnableInScene. Could we encapsulate an additional method to optimize this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 26
🧹 Outside diff range and nitpick comments (16)
packages/core/src/2d/assembler/ISpriteRenderer.ts (2)
8-16
: Consider separating public and internal interface members.The interface currently mixes public API members (
sprite
,color
, etc.) with internal implementation details (_transform
,_subChunk
,_getChunkManager
). This could make it harder to maintain API compatibility and might expose internal implementation details to consumers.Consider splitting this into two interfaces:
export interface ISpriteRenderer { sprite: Sprite; color?: Color; tileMode?: SpriteTileMode; tiledAdaptiveThreshold?: number; } interface ISpriteRendererInternal extends ISpriteRenderer { _transform: Transform; _subChunk: SubPrimitiveChunk; _getChunkManager(): PrimitiveChunkManager; }
10-12
: Document the optional properties' default behavior.The interface includes optional properties (
color
,tileMode
,tiledAdaptiveThreshold
) without clear documentation about their default behavior when undefined.Consider adding JSDoc comments to clarify the default behavior:
/** The color to tint the sprite with. If undefined, no tinting is applied. */ color?: Color; /** The tiling mode for the sprite. If undefined, no tiling is applied. */ tileMode?: SpriteTileMode; /** The threshold for adaptive tiling. Only used when tileMode is set. */ tiledAdaptiveThreshold?: number;packages/core/src/2d/assembler/ISpriteAssembler.ts (2)
17-18
: Consider documenting the alpha parameter's valid range.The methods have been properly updated to use ISpriteRenderer. The new alpha parameter in updateColor enhances control over transparency.
Consider adding JSDoc to document the expected range for the alpha parameter (typically 0-1).
19-26
: Document the boolean return value's meaning.The new getUVByLocalPosition method is well-structured with appropriate parameter types.
Consider adding JSDoc to clarify:
- The meaning of the boolean return value
- The coordinate space for the position parameter
- The expected values in the out parameter
packages/core/src/mesh/MeshRenderer.ts (2)
102-108
: Consider using a shared zero bounds constantThe implementation correctly handles local bounds, but consider defining a shared
ZERO_BOUNDS
constant for the zero initialization case to improve maintainability and reduce object allocations.private static readonly ZERO_BOUNDS = Object.freeze(new BoundingBox(new Vector3(0, 0, 0), new Vector3(0, 0, 0))); protected override _updateLocalBounds(localBounds: BoundingBox): void { const mesh = this._mesh; if (mesh) { localBounds.copyFrom(mesh.bounds); } else { localBounds.copyFrom(MeshRenderer.ZERO_BOUNDS); } }
191-199
: Enhance enum documentation with bit compositionConsider adding detailed documentation for each flag value to explain their bit composition. This would help maintainers understand the relationship between flags.
enum MeshRendererUpdateFlags { /** Indicates vertex element changes requiring shader macro updates (0x10) */ VertexElementMacro = 0x10, /** * Combined flag for vertex and bounds updates (0x1c) * Composition: * - VertexElementMacro (0x10) * - LocalBounds (0x04) * - WorldBounds (0x08) */ VertexElementMacroAndAllBounds = 0x1c, /** * All possible update flags (0x1f) * Includes all renderer and mesh-specific flags */ All = 0x1f }packages/core/src/mesh/SkinnedMeshRenderer.ts (1)
191-220
: Add JSDoc documentation for the method.The implementation looks solid, but would benefit from documentation explaining:
- The purpose of the method
- The different scenarios it handles
- The parameters and return value
Add documentation like this:
/** * Updates the local bounds of the skinned mesh. * Handles three scenarios: * 1. Root bone exists and is in the bones array - uses exact inverse bind matrix * 2. Root bone exists but not in bones array - computes approximate matrix from children * 3. No root bone - falls back to base class behavior * @param localBounds The bounding box to update */ protected override _updateLocalBounds(localBounds: BoundingBox): voidpackages/core/src/2d/assembler/TiledSpriteAssembler.ts (3)
1-19
: Consider enhancing class-level documentation.While the class is marked as
@internal
, it would be beneficial to add JSDoc comments describing:
- The purpose and responsibility of the TiledSpriteAssembler
- Key algorithms or techniques used
- Example usage scenarios
Line range hint
169-337
: Consider extracting common UV calculation patterns.The switch cases for both row and column calculations contain similar patterns for UV coordinate calculation. Consider extracting these into helper methods to reduce code duplication and improve maintainability.
Example helper method:
private static calculateUVCoordinate( localPos: number, pos0: number, pos1: number, uv0: number, uv1: number ): number { return ((localPos - pos0) / (pos1 - pos0)) * (uv1 - uv0) + uv0; }
413-413
: Enhance warning message with more context.The warning message could be more helpful by including:
- The actual vertex count that exceeded the limit
- Suggestions for potential solutions
-Logger.warn(`The number of vertices exceeds the upper limit(${maxVertexCount}).`); +Logger.warn( + `Vertex count (${vertexCount}) exceeds the maximum limit (${maxVertexCount}). ` + + `Consider reducing the tile count or using a simpler sprite configuration.` +);packages/core/src/2d/text/TextRenderer.ts (2)
Line range hint
234-246
: Add missing property declarations for mask-related properties.The
maskInteraction
andmaskLayer
getters/setters are using undeclared properties.Add these property declarations at the class level:
+private _maskInteraction: SpriteMaskInteraction; +private _maskLayer: number = 0;
Line range hint
474-611
: Consider implementing object pooling for temporary calculations.The text layout calculation creates numerous temporary objects for measurements and positions. Consider implementing an object pool for frequently created objects like Vector2 for positions and temporary arrays for line calculations.
This would help reduce garbage collection pressure, especially for frequently updating text.
Key areas for pooling:
- Line width calculations
- Character position vectors
- Temporary arrays for line metrics
packages/core/src/ui/UICanvas.ts (2)
23-23
: Consider removing redundant type declarations.TypeScript can infer these types from their initial values. This makes the code more concise and easier to maintain.
Apply this diff to simplify the declarations:
- depth: number = 0; + depth = 0; - _isRootCanvas: boolean = false; + _isRootCanvas = false; - _canvasIndex: number = -1; + _canvasIndex = -1; - _sortDistance: number = 0; + _sortDistance = 0; - private _sortOrder: number = 0; + private _sortOrder = 0; - private _distance: number = 10; + private _distance = 10; - private _referenceResolution: Vector2 = new Vector2(800, 600); + private _referenceResolution = new Vector2(800, 600);Also applies to: 32-32, 35-35, 56-56, 80-80, 82-82, 86-86
387-387
: Rename 'adapter' methods to 'adapt' for better naming.The prefix 'adapter' is typically used for objects that implement the Adapter pattern. For methods that perform adaptation, 'adapt' is more appropriate.
Apply this diff to improve method naming:
- private _adapterPoseInScreenSpace(): void { + private _adaptPoseInScreenSpace(): void { - private _adapterSizeInScreenSpace(): void { + private _adaptSizeInScreenSpace(): void {Also applies to: 406-406
packages/core/src/2d/assembler/SlicedSpriteAssembler.ts (2)
36-36
: Ensure consistency in renderer propertiesDestructuring
sprite
fromrenderer
enhances readability. Ensure consistent use of destructuring across the codebase for cleaner code.
71-75
: Simplify assignment statements for better readabilitySimilar to earlier, separating combined assignments can enhance code clarity.
Apply this diff:
- (column[0] = expectHeight * bottom * heightScale), (column[1] = column[2] = fixedBottom * heightScale); - column[3] = height - expectHeight * (1 - top) * heightScale; + column[0] = expectHeight * bottom * heightScale; + column[1] = fixedBottom * heightScale; + column[2] = fixedBottom * heightScale; + column[3] = height - expectHeight * (1 - top) * heightScale;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (15)
- packages/core/src/2d/assembler/ISpriteAssembler.ts (1 hunks)
- packages/core/src/2d/assembler/ISpriteRenderer.ts (1 hunks)
- packages/core/src/2d/assembler/SimpleSpriteAssembler.ts (4 hunks)
- packages/core/src/2d/assembler/SlicedSpriteAssembler.ts (5 hunks)
- packages/core/src/2d/assembler/TiledSpriteAssembler.ts (8 hunks)
- packages/core/src/2d/sprite/SpriteMask.ts (10 hunks)
- packages/core/src/2d/sprite/SpriteRenderer.ts (15 hunks)
- packages/core/src/2d/text/TextRenderer.ts (20 hunks)
- packages/core/src/Renderer.ts (8 hunks)
- packages/core/src/mesh/MeshRenderer.ts (4 hunks)
- packages/core/src/mesh/SkinnedMeshRenderer.ts (4 hunks)
- packages/core/src/ui/UICanvas.ts (1 hunks)
- packages/core/src/ui/UIGroup.ts (1 hunks)
- packages/core/src/ui/UIImage.ts (1 hunks)
- packages/core/src/ui/UIRenderer.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/core/src/2d/sprite/SpriteMask.ts
- packages/core/src/Renderer.ts
🧰 Additional context used
🪛 Biome
packages/core/src/mesh/MeshRenderer.ts
[error] 185-185: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 186-186: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/mesh/SkinnedMeshRenderer.ts
[error] 256-256: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
packages/core/src/ui/UICanvas.ts
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 242-242: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 342-342: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 346-346: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 347-347: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 345-350: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 460-460: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 574-575: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 584-585: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 586-588: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIGroup.ts
[error] 106-106: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
[error] 111-111: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
[error] 154-154: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIRenderer.ts
[error] 152-152: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 156-156: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 157-157: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 155-159: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
🔇 Additional comments (31)
packages/core/src/2d/assembler/ISpriteRenderer.ts (1)
14-15
: Verify the necessity of exposing internal chunk management.The
_subChunk
property and_getChunkManager()
method suggest tight coupling with the rendering implementation. This might make it difficult to change the underlying rendering system in the future.Let's check if these members are used across different implementations:
✅ Verification successful
Based on the search results, I can now generate a final response as there's clear evidence of the usage patterns of these members across the codebase.
The internal chunk management exposure is necessary for the current architecture
The
_subChunk
and_getChunkManager()
members are integral parts of the sprite rendering system, used consistently across multiple implementations:
- Core sprite renderers (
SpriteRenderer
,SpriteMask
) use them for chunk management- Various sprite assemblers (
SimpleSpriteAssembler
,SlicedSpriteAssembler
,TiledSpriteAssembler
) rely on these members for efficient vertex allocation- UI components (
UIRenderer
) and text rendering (TextRenderer
) also utilize this chunk management system- Test files validate the chunk management behavior
The underscore prefix correctly denotes these as protected members, and their exposure is a deliberate design choice for the rendering implementation hierarchy rather than a coupling issue.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for usages of _subChunk and _getChunkManager echo "Checking _subChunk usage:" rg "_subChunk" --type ts echo -e "\nChecking _getChunkManager usage:" rg "_getChunkManager" --type tsLength of output: 5157
packages/core/src/2d/assembler/ISpriteAssembler.ts (2)
1-7
: LGTM! Clean imports and proper internal API marking.The imports are well-organized and the interface is correctly marked as internal.
8-16
: Verify the ISpriteRenderer type compatibility across implementations.The method signatures have been updated to use ISpriteRenderer, which is more specific than the previous Renderer type. The updatePositions method's additional parameters enhance its flexibility while maintaining its core purpose of updating vertex positions.
✅ Verification successful
All implementations correctly use ISpriteRenderer and match the interface signature
The verification shows that all three implementations (SimpleSpriteAssembler, SlicedSpriteAssembler, and TiledSpriteAssembler) correctly:
- Use ISpriteRenderer type in their resetData and updatePositions methods
- Implement the required method signatures with matching parameters
- Handle the width, height, pivot, and flip parameters consistently
The type changes are properly propagated across all implementations, maintaining interface compatibility.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify all implementations properly implement the new interface # Test: Search for classes implementing ISpriteAssembler ast-grep --pattern 'class $_ implements ISpriteAssembler { $$$ }'Length of output: 69
Script:
#!/bin/bash # Let's try a different approach to find implementations # First, find all TypeScript files that import ISpriteAssembler rg -l "ISpriteAssembler" --type ts # Then look for actual class implementations with more context rg "class.*implements.*ISpriteAssembler" -A 10Length of output: 379
Script:
#!/bin/bash # Let's examine the implementation files directly to verify the method signatures echo "=== SimpleSpriteAssembler ====" cat packages/core/src/2d/assembler/SimpleSpriteAssembler.ts echo -e "\n=== SlicedSpriteAssembler ====" cat packages/core/src/2d/assembler/SlicedSpriteAssembler.ts echo -e "\n=== TiledSpriteAssembler ====" cat packages/core/src/2d/assembler/TiledSpriteAssembler.tsLength of output: 31413
packages/core/src/2d/assembler/SimpleSpriteAssembler.ts (4)
1-4
: LGTM! Good improvement in type safety.The switch to
ISpriteRenderer
interface aligns well with the previous suggestion about having a common type for renderable components.
11-12
: LGTM! Good encapsulation.Making these static properties private improves encapsulation by preventing external access to internal implementation details.
81-92
: LGTM! Alpha blending implementation looks correct.The alpha blending calculation and vertex color updates are implemented correctly.
38-39
: 🛠️ Refactor suggestionOptimize flip calculations for better performance.
Based on previous benchmarks, using unary negation would be more performant than the current ternary approach.
-const sx = flipX ? -width : width; -const sy = flipY ? -height : height; +const sx = flipX ? -width : width; +const sy = flipY ? -height : height;Likely invalid or redundant comment.
packages/core/src/mesh/MeshRenderer.ts (2)
36-36
: LGTM: Efficient flag combination for mesh updatesThe use of
VertexElementMacroAndAllBounds
efficiently combines vertex element and bounds updates, which typically occur together when the mesh changes.
185-186
: Refer to previous review comment about assignments in expressions🧰 Tools
🪛 Biome
[error] 185-185: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 186-186: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIGroup.ts (2)
1-41
: LGTM! Well-structured class definition with proper property managementThe class structure follows TypeScript best practices with appropriate use of decorators and clear documentation.
147-178
: 🛠️ Refactor suggestionAdd error handling for edge cases
The method could benefit from additional error handling for edge cases.
Consider adding validation for circular parent-child relationships:
Add validation at the start of the method:
private _registryToParentGroup(parentGroup: UIGroup): void { if (parentGroup === this) { throw new Error('Cannot register UIGroup as its own parent'); } // ... rest of the method }🧰 Tools
🪛 Biome
[error] 154-154: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIRenderer.ts (2)
125-132
: LGTM! Clean lifecycle management.The lifecycle methods properly handle component registration and cleanup, ensuring UI elements are correctly managed within the scene.
Also applies to: 137-143
229-249
: LGTM! Proper resource cleanup and transform handling.The cleanup and transform change handling are well-implemented, ensuring proper resource management and UI updates.
packages/core/src/mesh/SkinnedMeshRenderer.ts (1)
256-259
: Variable scope issue in switch case.The variable scope issue identified in the previous review is still present.
🧰 Tools
🪛 Biome
[error] 256-256: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
packages/core/src/2d/sprite/SpriteRenderer.ts (4)
10-13
: LGTM: Interface implementation enhances type safetyThe addition of ISpriteRenderer interface and related imports improves type safety and contract enforcement.
Also applies to: 26-26
269-269
: LGTM: Proper component type initializationCorrectly initializes the component type to SpriteRenderer.
317-329
: LGTM: Improved local bounds calculationThe bounds calculation correctly handles sprite dimensions and pivot points, including flipped states.
460-462
: LGTM: Clean color change handlingThe color change handler correctly sets the appropriate update flag.
packages/core/src/2d/assembler/TiledSpriteAssembler.ts (1)
Line range hint
1-476
: Implementation looks solid overall.The TiledSpriteAssembler implementation is well-structured and handles edge cases appropriately. The code demonstrates good attention to detail in handling various tiling scenarios and coordinate transformations.
packages/core/src/2d/text/TextRenderer.ts (2)
35-35
: Property initialization issue already reported.Skipping as this was already reported in a previous review.
689-699
: Well-structured enum implementation for update flags.The
TextRendererUpdateFlags
enum effectively extendsRendererUpdateFlags
and provides clear, well-documented flag combinations for different update scenarios.packages/core/src/2d/assembler/SlicedSpriteAssembler.ts (8)
1-4
: Good use ofISpriteRenderer
interfaceImporting
ISpriteRenderer
and updating the parameter types enhances flexibility by allowing different sprite renderer implementations to use these methods.
11-17
: Encapsulation of static propertiesChanging
_rectangleTriangles
and_worldMatrix
to private scopes improves encapsulation and prevents unintended external access.
19-19
: UpdatingresetData
to acceptISpriteRenderer
Modifying the
resetData
method to acceptISpriteRenderer
broadens its applicability and aligns with the use of a common interface for renderers.
28-35
: Consider renamingupdatePositions
to reflect expanded functionalityAs previously mentioned, since
updatePositions
now accepts additional parameters likewidth
,height
,pivot
,flipX
, andflipY
, consider renaming the method to better reflect its role, such asupdateTransform
orupdateDimensionsAndPosition
.
119-122
: UpdatingupdateUVs
to acceptISpriteRenderer
Using
ISpriteRenderer
forupdateUVs
increases method flexibility and allows for different renderer types.
132-135
: UpdatedupdateColor
method to acceptISpriteRenderer
Changing the parameter type to
ISpriteRenderer
ensures that bothSpriteRenderer
andUIImage
instances can utilize this method, addressing previous concerns about consistency.
135-141
: 🛠️ Refactor suggestionOptimize color assignment in the loop
To enhance performance, consider caching the color values before the loop to avoid repeated property accesses.
Apply this diff:
+ const rValue = r; + const gValue = g; + const bValue = b; + const alphaValue = finalAlpha; for (let i = 0, o = subChunk.vertexArea.start + 5; i < 16; ++i, o += 9) { - vertices[o] = r; - vertices[o + 1] = g; - vertices[o + 2] = b; - vertices[o + 3] = finalAlpha; + vertices[o] = rValue; + vertices[o + 1] = gValue; + vertices[o + 2] = bValue; + vertices[o + 3] = alphaValue; }Likely invalid or redundant comment.
145-200
: Verify the correctness ofgetUVByLocalPosition
methodEnsure that the calculations within
getUVByLocalPosition
correctly map local positions to UV coordinates, especially regarding the handling of borders and scaling factors.Run the following script to check for potential edge cases:
✅ Verification successful
Let me gather more information about the implementation details and edge cases.
This will help us understand:
- The base implementation in SimpleSpriteAssembler
- Any existing test coverage
- Documentation and related comments that might explain the expected behavior
Let me check the test files to understand how these UV calculations are tested.
This will help us:
- Understand how UV calculations are tested
- Compare with the base implementation in SimpleSpriteAssembler
- Verify against any interface requirements
Based on the extensive test coverage and implementation details revealed in the test files, I can now provide a final response about the correctness of the
getUVByLocalPosition
method.UV mapping calculations in SlicedSpriteAssembler are correctly implemented
The implementation is verified to be correct based on the following evidence:
The test suite thoroughly validates UV calculations for various scenarios:
- Different sprite sizes and borders
- Both adaptive and continuous tile modes
- Edge cases with very small (0.01) and very large (100000) dimensions
- Various border configurations
The method correctly handles scaling when fixed borders exceed dimensions:
if (fixedLeft + fixedRight > width) { const widthScale = width / (fixedLeft + fixedRight); // Scaling is properly applied }The UV calculations are consistent with the sprite's border and region properties, as demonstrated by the extensive test assertions comparing expected and actual UV coordinates.
The implementation successfully maps local positions to UV coordinates while properly handling sprite borders, scaling, and different drawing modes.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Search for calls to `getUVByLocalPosition` and ensure they handle edge cases properly. # Test: Find all usages of `getUVByLocalPosition` in the codebase. # Expect: Identify call sites to review their implementations. rg 'getUVByLocalPosition' -A 5Length of output: 4146
Script:
#!/bin/bash # Check the implementation of SimpleSpriteAssembler's getUVByLocalPosition ast-grep --pattern 'static getUVByLocalPosition($_) { $$$ }' # Check for any test files related to these sprite assemblers fd -e test.ts -e spec.ts sprite # Look for any related documentation or comments rg -B 3 "getUVByLocalPosition|SlicedSpriteAssembler" --type tsLength of output: 6593
Script:
#!/bin/bash # Check the content of sprite test files for UV-related tests cat tests/src/core/Sprite.test.ts tests/src/core/SpriteRenderer.test.ts # Look for the implementation of SimpleSpriteAssembler's getUVByLocalPosition for comparison ast-grep --pattern 'static getUVByLocalPosition($params) { $$$ }' packages/core/src/2d/assembler/SimpleSpriteAssembler.ts # Check for any sprite-related interfaces that might define UV calculation requirements rg -A 5 "interface.*Sprite" --type tsLength of output: 109958
packages/core/src/ui/UIImage.ts (2)
58-70
:⚠️ Potential issueEnsure
this._assembler
is assigned for alldrawMode
valuesIn the
set drawMode
method, thedefault
case in theswitch
statement does not assign an assembler tothis._assembler
. This could lead tothis._assembler
beingundefined
if an invaliddrawMode
is set, causing runtime errors when methods onthis._assembler
are called.Consider handling the
default
case to assign a default assembler or throw an error:default: + throw new Error(`Unsupported drawMode value: ${value}`); break;
This ensures that
this._assembler
is always valid after setting thedrawMode
.Likely invalid or redundant comment.
248-267
: Verify that all dirty flags are properly clearedAfter updating positions, UVs, and colors, it's important to ensure that all corresponding flags in
_dirtyUpdateFlag
are cleared. Failure to do so may result in unnecessary updates and performance issues.Run the following script to check for any flags that may not be cleared:
✅ Verification successful
Let me gather more information about the flag definitions and their usage to ensure proper clearing.
Let me search for the complete enum definitions and renderer flags to fully understand the bitmask operations.
All dirty flags are properly cleared
Looking at the code and flag definitions, the clearing of flags is handled correctly:
- Position updates clear
RendererUpdateFlags.AllPositions
(0x3)- UV updates clear
ImageUpdateFlags.UV
(0x10)- Color updates clear
ImageUpdateFlags.Color
(0x20)Each update operation properly clears its specific bits using the bitwise AND NOT operation (
&= ~flag
), and the final state is correctly stored back inthis._dirtyUpdateFlag
. The flags are mutually exclusive in their bit positions, ensuring no unintended interference.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that all bits in _dirtyUpdateFlag are cleared after updates. # Search for where _dirtyUpdateFlag is modified. rg '_dirtyUpdateFlag\s*[\&\|\^]?=\s*' packages/core/src/ui/UIImage.ts # Check that after each update, the corresponding flag is cleared. # Expected: All update methods properly clear their flags in _dirtyUpdateFlag.Length of output: 1285
Script:
#!/bin/bash # Search for flag definitions to understand what bits are used rg -A 5 'enum (Image|Renderer)UpdateFlags' packages/core/src/ui/ # Search for any other places where flags are cleared (using &= ~) rg '_dirtyUpdateFlag.*&=\s*~' packages/core/src/ui/Length of output: 437
Script:
#!/bin/bash # Search for the complete ImageUpdateFlags enum rg -A 10 'enum ImageUpdateFlags' packages/core/src/ui/UIImage.ts # Search for RendererUpdateFlags enum in the codebase rg -A 10 'enum RendererUpdateFlags' packages/core/src/Length of output: 1024
static getUVByLocalPosition( | ||
renderer: ISpriteRenderer, | ||
width: number, | ||
height: number, | ||
pivot: Vector2, | ||
position: Vector3, | ||
out: Vector2 | ||
): boolean { | ||
const sprite = renderer.sprite; | ||
const normalizedX = position.x / width + pivot.x; | ||
const normalizedY = position.y / height + pivot.y; | ||
const positions = sprite._getPositions(); | ||
const { x: left, y: bottom } = positions[0]; | ||
const { x: right, y: top } = positions[3]; | ||
if (normalizedX >= left && normalizedX <= right && normalizedY >= bottom && normalizedY <= top) { | ||
const uvs = sprite._getUVs(); | ||
const { x: uvLeft, y: uvBottom } = uvs[0]; | ||
const { x: uvRight, y: uvTop } = uvs[3]; | ||
const factorX = (normalizedX - left) / (right - left); | ||
const factorY = (normalizedY - bottom) / (top - bottom); | ||
out.set(uvLeft + (uvRight - uvLeft) * factorX, uvBottom + (uvTop - uvBottom) * factorY); | ||
return true; | ||
} else { | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add input validation to prevent potential runtime errors.
The method should validate input parameters to prevent division by zero and null/undefined errors.
static getUVByLocalPosition(
renderer: ISpriteRenderer,
width: number,
height: number,
pivot: Vector2,
position: Vector3,
out: Vector2
): boolean {
+ if (!renderer?.sprite || !position || !pivot || !out) {
+ return false;
+ }
+ if (width === 0 || height === 0) {
+ return false;
+ }
const sprite = renderer.sprite;
const normalizedX = position.x / width + pivot.x;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
static getUVByLocalPosition( | |
renderer: ISpriteRenderer, | |
width: number, | |
height: number, | |
pivot: Vector2, | |
position: Vector3, | |
out: Vector2 | |
): boolean { | |
const sprite = renderer.sprite; | |
const normalizedX = position.x / width + pivot.x; | |
const normalizedY = position.y / height + pivot.y; | |
const positions = sprite._getPositions(); | |
const { x: left, y: bottom } = positions[0]; | |
const { x: right, y: top } = positions[3]; | |
if (normalizedX >= left && normalizedX <= right && normalizedY >= bottom && normalizedY <= top) { | |
const uvs = sprite._getUVs(); | |
const { x: uvLeft, y: uvBottom } = uvs[0]; | |
const { x: uvRight, y: uvTop } = uvs[3]; | |
const factorX = (normalizedX - left) / (right - left); | |
const factorY = (normalizedY - bottom) / (top - bottom); | |
out.set(uvLeft + (uvRight - uvLeft) * factorX, uvBottom + (uvTop - uvBottom) * factorY); | |
return true; | |
} else { | |
return false; | |
static getUVByLocalPosition( | |
renderer: ISpriteRenderer, | |
width: number, | |
height: number, | |
pivot: Vector2, | |
position: Vector3, | |
out: Vector2 | |
): boolean { | |
if (!renderer?.sprite || !position || !pivot || !out) { | |
return false; | |
} | |
if (width === 0 || height === 0) { | |
return false; | |
} | |
const sprite = renderer.sprite; | |
const normalizedX = position.x / width + pivot.x; | |
const normalizedY = position.y / height + pivot.y; | |
const positions = sprite._getPositions(); | |
const { x: left, y: bottom } = positions[0]; | |
const { x: right, y: top } = positions[3]; | |
if (normalizedX >= left && normalizedX <= right && normalizedY >= bottom && normalizedY <= top) { | |
const uvs = sprite._getUVs(); | |
const { x: uvLeft, y: uvBottom } = uvs[0]; | |
const { x: uvRight, y: uvTop } = uvs[3]; | |
const factorX = (normalizedX - left) / (right - left); | |
const factorY = (normalizedY - bottom) / (top - bottom); | |
out.set(uvLeft + (uvRight - uvLeft) * factorX, uvBottom + (uvTop - uvBottom) * factorY); | |
return true; | |
} else { | |
return false; |
packages/core/src/ui/UIGroup.ts
Outdated
set alpha(val: number) { | ||
val = Math.max(0, Math.min(val, 1)); | ||
if (this._alpha !== val) { | ||
this._alpha = val; | ||
this._updateGlobalModify(GroupModifyFlags.Alpha); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Extract value clamping logic into a utility function
The alpha value clamping logic could be reused across the codebase.
Consider extracting the clamping logic into a utility function:
// Add to UIUtils.ts
export function clampAlpha(value: number): number {
return Math.max(0, Math.min(value, 1));
}
Then simplify the setter:
set alpha(val: number) {
- val = Math.max(0, Math.min(val, 1));
+ val = UIUtils.clampAlpha(val);
if (this._alpha !== val) {
this._alpha = val;
this._updateGlobalModify(GroupModifyFlags.Alpha);
}
}
packages/core/src/ui/UIGroup.ts
Outdated
_updateGlobalModify(flags: GroupModifyFlags): void { | ||
let passDownFlags = GroupModifyFlags.None; | ||
const parentGroup = this._parentGroup; | ||
if (flags & GroupModifyFlags.Alpha) { | ||
const alpha = this._alpha * (!this._ignoreParentGroup && parentGroup ? parentGroup._globalAlpha : 1); | ||
if (this._globalAlpha !== alpha) { | ||
this._globalAlpha = alpha; | ||
passDownFlags |= GroupModifyFlags.Alpha; | ||
} | ||
} | ||
if (flags & GroupModifyFlags.RaycastEnable) { | ||
const raycastEnable = | ||
this._raycastEnabled && | ||
(!this._ignoreParentGroup && parentGroup ? parentGroup._getGlobalRaycastEnable() : true); | ||
if (this._globalRaycastEnable !== raycastEnable) { | ||
this._globalRaycastEnable = raycastEnable; | ||
passDownFlags |= GroupModifyFlags.RaycastEnable; | ||
} | ||
} | ||
if (!!flags) { | ||
this._disorderedElements.forEach((element: IUIElement) => { | ||
element._onGroupModify(passDownFlags); | ||
}); | ||
} | ||
if (!!passDownFlags) { | ||
this._disorderedGroups.forEach((element: UIGroup) => { | ||
element._updateGlobalModify(passDownFlags); | ||
}); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider breaking down _updateGlobalModify into smaller functions
The method handles multiple concerns and could be more maintainable if split into separate methods.
Consider refactoring into smaller, focused methods:
private _updateGlobalAlpha(): GroupModifyFlags {
const alpha = this._alpha * (!this._ignoreParentGroup && this._parentGroup ? this._parentGroup._globalAlpha : 1);
if (this._globalAlpha !== alpha) {
this._globalAlpha = alpha;
return GroupModifyFlags.Alpha;
}
return GroupModifyFlags.None;
}
private _updateGlobalRaycast(): GroupModifyFlags {
const raycastEnable = this._raycastEnabled &&
(!this._ignoreParentGroup && this._parentGroup ? this._parentGroup._getGlobalRaycastEnable() : true);
if (this._globalRaycastEnable !== raycastEnable) {
this._globalRaycastEnable = raycastEnable;
return GroupModifyFlags.RaycastEnable;
}
return GroupModifyFlags.None;
}
private _updateGlobalModify(flags: GroupModifyFlags): void {
let passDownFlags = GroupModifyFlags.None;
if (flags & GroupModifyFlags.Alpha) {
passDownFlags |= this._updateGlobalAlpha();
}
if (flags & GroupModifyFlags.RaycastEnable) {
passDownFlags |= this._updateGlobalRaycast();
}
if (flags) {
this._disorderedElements.forEach((element: IUIElement) => {
element._onGroupModify(passDownFlags);
});
}
if (passDownFlags) {
this._disorderedGroups.forEach((element: UIGroup) => {
element._updateGlobalModify(passDownFlags);
});
}
}
🧰 Tools
🪛 Biome
[error] 106-106: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
[error] 111-111: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation(lint/complexity/noExtraBooleanCast)
packages/core/src/ui/UIGroup.ts
Outdated
this._parentGroup = parentGroup; | ||
if (preParentGroup) { | ||
const replaced = preParentGroup._disorderedGroups.deleteByIndex(this._groupIndex); | ||
replaced && (replaced._groupIndex = this._groupIndex); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid assignment within expression
The assignment within the logical AND expression reduces code readability.
Refactor to make the assignment more explicit:
- replaced && (replaced._groupIndex = this._groupIndex);
+ if (replaced) {
+ replaced._groupIndex = this._groupIndex;
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
replaced && (replaced._groupIndex = this._groupIndex); | |
if (replaced) { | |
replaced._groupIndex = this._groupIndex; | |
} |
🧰 Tools
🪛 Biome
[error] 154-154: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIRenderer.ts
Outdated
static _tempVec30: Vector3 = new Vector3(); | ||
/** @internal */ | ||
static _tempVec31: Vector3 = new Vector3(); | ||
/** @internal */ | ||
static _tempMat: Matrix = new Matrix(); | ||
/** @internal */ | ||
static _tempPlane: Plane = new Plane(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider pooling temporary objects for better memory efficiency.
The static temporary objects could benefit from object pooling to reduce garbage collection pressure during frequent UI updates.
Consider implementing an object pool:
class UIRendererObjectPool {
private static _vec3Pool: Vector3[] = [];
private static _matrixPool: Matrix[] = [];
static acquireVector3(): Vector3 {
return this._vec3Pool.pop() || new Vector3();
}
static releaseVector3(vec: Vector3): void {
vec.set(0, 0, 0);
this._vec3Pool.push(vec);
}
// Similar methods for Matrix
}
packages/core/src/ui/UIImage.ts
Outdated
return this._color; | ||
} | ||
|
||
set color(value: Color) { | ||
if (this._color !== value) { | ||
this._color.copyFrom(value); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update rendering state when setting a new Color
object
In the set color
method, when assigning a new Color
object to this._color
, the _dirtyUpdateFlag
is not updated. This may prevent the renderer from recognizing the color change.
Apply this diff to ensure the rendering state is updated:
set color(value: Color) {
if (this._color !== value) {
this._color.copyFrom(value);
+ this._dirtyUpdateFlag |= ImageUpdateFlags.Color;
}
}
This makes sure that any changes to the color
property will trigger a rendering update.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
return this._color; | |
} | |
set color(value: Color) { | |
if (this._color !== value) { | |
this._color.copyFrom(value); | |
} | |
} | |
return this._color; | |
} | |
set color(value: Color) { | |
if (this._color !== value) { | |
this._color.copyFrom(value); | |
this._dirtyUpdateFlag |= ImageUpdateFlags.Color; | |
} | |
} |
packages/core/src/ui/UIImage.ts
Outdated
switch (this._drawMode) { | ||
case SpriteDrawMode.Simple: | ||
case SpriteDrawMode.Sliced: | ||
this._dirtyUpdateFlag |= RendererUpdateFlags.AllPositionAndBounds; | ||
break; | ||
case SpriteDrawMode.Tiled: | ||
this._dirtyUpdateFlag |= ImageUpdateFlags.All; | ||
break; | ||
default: | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle unexpected drawMode
values in _onTransformChanged
In the _onTransformChanged
method, the default
case in the switch
statement is empty. If an unexpected drawMode
value is encountered, it might lead to silent failures or undefined behavior.
Consider handling the default
case to ensure that any unsupported drawMode
values are properly managed:
default:
+ console.warn(`Unsupported drawMode value: ${this._drawMode} in _onTransformChanged.`);
break;
This will help in debugging if an invalid drawMode
is ever set.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
switch (this._drawMode) { | |
case SpriteDrawMode.Simple: | |
case SpriteDrawMode.Sliced: | |
this._dirtyUpdateFlag |= RendererUpdateFlags.AllPositionAndBounds; | |
break; | |
case SpriteDrawMode.Tiled: | |
this._dirtyUpdateFlag |= ImageUpdateFlags.All; | |
break; | |
default: | |
break; | |
} | |
switch (this._drawMode) { | |
case SpriteDrawMode.Simple: | |
case SpriteDrawMode.Sliced: | |
this._dirtyUpdateFlag |= RendererUpdateFlags.AllPositionAndBounds; | |
break; | |
case SpriteDrawMode.Tiled: | |
this._dirtyUpdateFlag |= ImageUpdateFlags.All; | |
break; | |
default: | |
console.warn(`Unsupported drawMode value: ${this._drawMode} in _onTransformChanged.`); | |
break; | |
} |
packages/core/src/ui/UIImage.ts
Outdated
case SpriteDrawMode.Tiled: | ||
return SlicedSpriteAssembler.getUVByLocalPosition(this, size.x, size.y, pivot, position, out); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect assembler method called for Tiled
draw mode
In the _getUVByLocalPosition
method, when drawMode
is SpriteDrawMode.Tiled
, the code calls SlicedSpriteAssembler.getUVByLocalPosition
. This is likely a mistake and should instead call TiledSpriteAssembler.getUVByLocalPosition
to correctly handle tiled sprites.
Apply this diff to fix the issue:
case SpriteDrawMode.Tiled:
- return SlicedSpriteAssembler.getUVByLocalPosition(this, size.x, size.y, pivot, position, out);
+ return TiledSpriteAssembler.getUVByLocalPosition(this, size.x, size.y, pivot, position, out);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
case SpriteDrawMode.Tiled: | |
return SlicedSpriteAssembler.getUVByLocalPosition(this, size.x, size.y, pivot, position, out); | |
case SpriteDrawMode.Tiled: | |
return TiledSpriteAssembler.getUVByLocalPosition(this, size.x, size.y, pivot, position, out); |
packages/core/src/ui/UIImage.ts
Outdated
texture.getPixelBuffer(Math.floor(uv.x * texture.width), Math.floor(uv.y * texture.height), 1, 1, 0, pixel); | ||
if (pixel[3] >= alphaHitTestMinimumThreshold * 255) { | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check for out-of-bounds texture coordinates in _hitTest
In the _hitTest
method, the texture pixel is sampled using UV coordinates multiplied by the texture dimensions. If uv.x
or uv.y
are outside the range [0, 1]
, this could result in out-of-bounds access.
Ensure that uv.x
and uv.y
are clamped between 0
and 1
before sampling the texture:
texture.getPixelBuffer(
+ Math.floor(MathUtil.clamp(uv.x, 0, 1) * texture.width),
+ Math.floor(MathUtil.clamp(uv.y, 0, 1) * texture.height),
1, 1, 0, pixel);
This prevents potential errors when the UV coordinates are slightly outside the expected range due to numerical inaccuracies.
Committable suggestion was skipped due to low confidence.
packages/core/src/ui/UIImage.ts
Outdated
protected override _onDestroy(): void { | ||
const sprite = this._sprite; | ||
if (sprite) { | ||
this._addResourceReferCount(sprite, -1); | ||
sprite._updateFlagManager.removeListener(this._onSpriteChange); | ||
} | ||
|
||
super._onDestroy(); | ||
|
||
this._entity = null; | ||
this._color = null; | ||
this._sprite = null; | ||
this._assembler = null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Set properties to null
before calling super._onDestroy()
In the _onDestroy
method, properties like this._entity
, this._color
, this._sprite
, and this._assembler
are set to null
after calling super._onDestroy()
. If the parent class relies on these properties during its own cleanup, this could cause errors.
Consider setting these properties to null
before calling super._onDestroy()
:
protected override _onDestroy(): void {
+ this._entity = null;
+ this._color = null;
+ this._sprite = null;
+ this._assembler = null;
super._onDestroy();
}
This ensures that the parent class does not access properties that have been reset.
Committable suggestion was skipped due to low confidence.
packages/core/src/ui/UICanvas.ts
Outdated
} | ||
} | ||
|
||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merge with CanvasRenderMode?
packages/core/src/ui/UIUtils.ts
Outdated
const { elements: projectE } = virtualCamera.projectionMatrix; | ||
const { elements: viewE } = virtualCamera.viewMatrix; | ||
(projectE[0] = 2 / canvas.width), (projectE[5] = 2 / canvas.height), (projectE[10] = 0); | ||
rhi.activeRenderTarget(null, new Vector4(0, 0, 1, 1), renderContext.flipProjection, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will create Vector4 every frame
(row[0] = expectWidth * left * widthScale), (row[1] = row[2] = fixedLeft * widthScale); | ||
row[3] = width - expectWidth * (1 - right) * widthScale; | ||
} else { | ||
row = [expectWidth * left, fixedLeft, width - fixedRight, width - expectWidth * (1 - right)]; | ||
(row[0] = expectWidth * left), (row[1] = fixedLeft), (row[2] = width - fixedRight); | ||
row[3] = width - expectWidth * (1 - right); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can not agree more
packages/core/src/ui/UIRenderer.ts
Outdated
import { IUIElement } from "./interface/IUIElement"; | ||
|
||
@dependentComponents(UITransform, DependentMode.AutoAdd) | ||
export class UIRenderer extends Renderer implements IUIElement { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should add implements ISpriteRenderer, as follows:
export class UIRenderer extends Renderer implements IUIElement, ISpriteRenderer {}
packages/core/src/ui/UIImage.ts
Outdated
const texture = this.sprite?.texture; | ||
if (!texture) { | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be more appropriate to check the texture first
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🧹 Outside diff range and nitpick comments (4)
packages/core/src/2d/text/TextUtils.ts (2)
274-274
: LGTM with documentation suggestionThe method signature change is correct. Consider adding JSDoc to document the new parameter type.
+/** + * Measures text without wrapping for both TextRenderer and UIText. + * @param renderer - The text renderer (TextRenderer or UIText) + * @returns TextMetrics containing measurement information + */ static measureTextWithoutWrap(renderer: TextRenderer | UIText): TextMetrics {
Line range hint
100-274
: Consider adding integration tests for UIText measurementsSince these methods now support both TextRenderer and UIText, it would be valuable to add integration tests that verify:
- Text measurement consistency between TextRenderer and UIText
- Edge cases with different text styles and overflow modes
- Performance implications of supporting both types
This will help ensure compatibility and prevent regressions.
packages/core/src/ui/UIText.ts (2)
323-323
: Address the TODO: Optimize world positions buffer updateThere's a TODO comment at line 323 indicating that optimization is needed:
//@todo: Always update world positions to buffer, should opt
. Would you like assistance in implementing this optimization or opening a GitHub issue to track it?
514-514
: Consider splitting assignments for better readabilityAt line 514, the line combines multiple assignments in one statement:
const charRenderInfo = (charRenderInfos[renderElementCount++] = charRenderInfoPool.get());This reduces code clarity. Consider splitting the assignments.
Suggested change:
- const charRenderInfo = (charRenderInfos[renderElementCount++] = charRenderInfoPool.get()); + const charRenderInfo = charRenderInfoPool.get(); + charRenderInfos[renderElementCount++] = charRenderInfo;🧰 Tools
🪛 Biome
[error] 514-514: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (3)
packages/core/src/2d/text/TextUtils.ts
(3 hunks)packages/core/src/ui/UIText.ts
(1 hunks)packages/core/src/ui/index.ts
(1 hunks)
🧰 Additional context used
🪛 Biome
packages/core/src/ui/UIText.ts
[error] 274-274: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 513-513: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 514-514: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 524-524: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 526-526: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 608-608: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 610-610: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
🪛 GitHub Check: lint
packages/core/src/ui/index.ts
[failure] 10-10:
Delete ⏎
🔇 Additional comments (2)
packages/core/src/2d/text/TextUtils.ts (2)
3-3
: LGTM: Import addition is correct
The UIText import is properly placed and supports the new type parameter additions.
100-100
: Verify type compatibility between TextRenderer and UIText
The method now accepts both TextRenderer and UIText types. Please ensure that UIText implements all required properties and methods:
- _getSubFont()
- width
- height
- lineSpacing
- overflowMode
✅ Verification successful
Let me gather more information about the UIText class implementation to verify the required properties and methods.
Let me verify one last aspect - the inheritance hierarchy and interface implementation of UIText and TextRenderer.
Based on the gathered evidence, I can now provide the final response:
Type compatibility between TextRenderer and UIText is verified
The verification confirms that UIText implements all required properties and methods:
_getSubFont()
method is properly implemented- Required properties are present:
_width
,_height
,_lineSpacing
,_overflowMode
- Both classes extend from base rendering classes (UIRenderer and Renderer respectively) and share similar structure
- Both classes have compatible implementations for text rendering functionality
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify UIText implements required properties and methods
# Search for UIText class definition and implementation
ast-grep --pattern 'class UIText {
$$$
_getSubFont() {
$$$
}
$$$
}'
# Search for width, height, lineSpacing, and overflowMode properties
rg -A 5 'class UIText'
Length of output: 599
Script:
#!/bin/bash
# Search for required properties and methods in UIText class
rg -A 2 "(_getSubFont|width\s*:|height\s*:|lineSpacing\s*:|overflowMode\s*:)" packages/core/src/ui/UIText.ts
# Search for property declarations
ast-grep --pattern 'class UIText extends UIRenderer {
$$$
private _width: $_
$$$
}'
# Check if UIText extends TextRenderer or has similar interface
rg -A 2 "interface.*Text" packages/core/src/2d/text/
Length of output: 1562
Script:
#!/bin/bash
# Check TextRenderer class and its properties
rg -A 5 "class TextRenderer" packages/core/src/2d/text/
# Check if UIText implements or extends any text-related interfaces
rg "class UIText.*implements" packages/core/src/ui/UIText.ts
# Look for any shared interfaces between TextRenderer and UIText
ast-grep --pattern 'interface $_ {
$$$
_getSubFont(): $_
$$$
}'
Length of output: 825
packages/core/src/ui/UIText.ts
Outdated
//@ts-ignore | ||
this._color._onValueChanged = this._onColorChange.bind(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid using //@ts-ignore
by properly typing properties
At lines 256-257, the use of //@ts-ignore
suppresses TypeScript errors, which might hide potential issues. Consider properly typing the _onValueChanged
property in the Color
class to avoid the need for //@ts-ignore
.
packages/core/src/ui/UIText.ts
Outdated
const { r, g, b, a } = this.color; | ||
const tempIndices = CharRenderInfo.triangles; | ||
const tempIndicesLength = tempIndices.length; | ||
const subChunk = (textChunk.subChunk = this._getChunkManager().allocateSubChunk(count * 4)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid nested assignments within expressions
At line 608, the assignments are nested within an expression:
const subChunk = (textChunk.subChunk = this._getChunkManager().allocateSubChunk(count * 4));
This can reduce code readability. Consider separating the assignments.
Suggested change:
- const subChunk = (textChunk.subChunk = this._getChunkManager().allocateSubChunk(count * 4));
+ textChunk.subChunk = this._getChunkManager().allocateSubChunk(count * 4);
+ const subChunk = textChunk.subChunk;
🧰 Tools
🪛 Biome
[error] 608-608: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIText.ts
Outdated
const tempIndicesLength = tempIndices.length; | ||
const subChunk = (textChunk.subChunk = this._getChunkManager().allocateSubChunk(count * 4)); | ||
const vertices = subChunk.chunk.vertices; | ||
const indices = (subChunk.indices = []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid nested assignments within expressions
At line 610, the assignment is nested within an expression:
const indices = (subChunk.indices = []);
This can be confusing. Consider assigning to subChunk.indices
first, then referencing it.
Suggested change:
- const indices = (subChunk.indices = []);
+ subChunk.indices = [];
+ const indices = subChunk.indices;
🧰 Tools
🪛 Biome
[error] 610-610: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIText.ts
Outdated
const char = line[j]; | ||
const charInfo = charFont._getCharInfo(char); | ||
if (charInfo.h > 0) { | ||
firstRow < 0 && (firstRow = j); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid assignments within expressions to improve code clarity
At line 513, the assignment firstRow = j
is performed within a logical AND expression. This can be confusing and reduce readability. It's better to use an explicit if
statement.
Suggested change:
- firstRow < 0 && (firstRow = j);
+ if (firstRow < 0) {
+ firstRow = j;
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
firstRow < 0 && (firstRow = j); | |
if (firstRow < 0) { | |
firstRow = j; | |
} |
🧰 Tools
🪛 Biome
[error] 513-513: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIText.ts
Outdated
localPositions.set(left, top, right, bottom); | ||
i === firstLine && (maxY = Math.max(maxY, top)); | ||
minY = Math.min(minY, bottom); | ||
j === firstRow && (minX = Math.min(minX, left)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid assignments within expressions for better code readability
At line 526, the assignment minX = Math.min(minX, left)
is inside a logical AND expression. For clarity, use an explicit if
statement.
Suggested change:
- j === firstRow && (minX = Math.min(minX, left));
+ if (j === firstRow) {
+ minX = Math.min(minX, left);
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
j === firstRow && (minX = Math.min(minX, left)); | |
if (j === firstRow) { | |
minX = Math.min(minX, left); | |
} |
🧰 Tools
🪛 Biome
[error] 526-526: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIText.ts
Outdated
const top = startY + ascent; | ||
const bottom = startY - descent; | ||
localPositions.set(left, top, right, bottom); | ||
i === firstLine && (maxY = Math.max(maxY, top)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid assignments within expressions for code clarity
At line 524, the assignment maxY = Math.max(maxY, top)
is performed within a logical AND expression. This can make the code less readable. Use an explicit if
statement instead.
Suggested change:
- i === firstLine && (maxY = Math.max(maxY, top));
+ if (i === firstLine) {
+ maxY = Math.max(maxY, top);
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
i === firstLine && (maxY = Math.max(maxY, top)); | |
if (i === firstLine) { | |
maxY = Math.max(maxY, top); | |
} |
🧰 Tools
🪛 Biome
[error] 524-524: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIText.ts
Outdated
this._freeTextChunks(); | ||
this._textChunks = null; | ||
|
||
this._subFont && (this._subFont = null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid assignments within expressions for better readability
At line 274, the assignment this._subFont = null
is used within a logical AND expression. This can make the code harder to read and maintain. Consider using an explicit if
statement for clarity.
Suggested change:
- this._subFont && (this._subFont = null);
+ if (this._subFont) {
+ this._subFont = null;
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
this._subFont && (this._subFont = null); | |
if (this._subFont) { | |
this._subFont = null; | |
} |
🧰 Tools
🪛 Biome
[error] 274-274: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
* feat: add button
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 28
🧹 Outside diff range and nitpick comments (46)
packages/core/src/ui/interactive/transition/TransitionType.ts (1)
1-6
: Consider adding JSDoc comments to document each transition type.Adding documentation would improve maintainability by explaining the purpose and behavior of each transition type.
+/** + * Defines the types of transitions available for UI elements. + */ export enum TransitionType { + /** No transition effect */ None, + /** Color-based transition effect */ Color, + /** Sprite-based transition effect */ Sprite, + /** Scale-based transition effect */ Scale }packages/core/src/ui/interactive/InteractiveState.ts (2)
1-7
: Consider renaming 'Disable' to 'Disabled' for grammatical consistency.The enum implementation looks good and covers all essential UI interaction states. However, 'Disable' should be 'Disabled' to maintain consistent grammar (adjective form) with other state names.
export enum InteractiveState { None, Normal, Pressed, Hover, - Disable + Disabled }
1-1
: Add JSDoc documentation for the enum.Since this enum is a core part of the GUI infrastructure used across multiple components, please add JSDoc documentation explaining:
- The purpose of each state
- Usage examples
- State transition expectations
+/** + * Represents the interactive states of UI components. + * + * @remarks + * - None: Initial/unset state + * - Normal: Default interactive state + * - Pressed: Component is being actively pressed/clicked + * - Hover: Mouse is hovering over the component + * - Disabled: Component is non-interactive + */ export enum InteractiveState {packages/core/src/ui/interface/IGroupElement.ts (2)
4-8
: Add JSDoc documentation and consider using readonly modifiers.The interface definition could benefit from documentation explaining its purpose and usage. Additionally, consider marking properties that shouldn't change after initialization as readonly.
Here's the suggested improvement:
+/** + * Interface for elements that can be part of a UIGroup. + * Extends ICanvasElement to provide group-specific functionality. + */ export interface IGroupElement extends ICanvasElement { - _group: UIGroup; - _indexInGroup: number; + readonly _group: UIGroup; + readonly _indexInGroup: number; _onGroupModify(flag: GroupModifyFlags): void; }
4-8
: Consider encapsulating internal implementation details.The interface exposes implementation details through underscore-prefixed properties and methods. This could make it harder to maintain and refactor the codebase in the future.
Consider:
- Moving internal details to a separate internal interface
- Using a more public-facing API design
- Using TypeScript's private fields (#) if these are truly meant to be private
Example approach:
export interface IGroupElement extends ICanvasElement { readonly group: UIGroup; readonly indexInGroup: number; onModify(flag: GroupModifyFlags): void; } // Internal interface for implementation interface IGroupElementInternal extends IGroupElement { _group: UIGroup; _indexInGroup: number; _onGroupModify(flag: GroupModifyFlags): void; }packages/core/src/ui/interface/IUIGraphics.ts (1)
5-10
: Consider interface segregation for better modularity.The current interface combines multiple responsibilities (depth management, hit testing, and group behavior). Consider splitting this into more focused interfaces:
interface IDepthManagement { depth: number; } interface IRaycastable { raycastEnable: boolean; raycastPadding: Vector4; raycast(ray: Ray, out: HitResult, distance: number): boolean; } export interface IUIGraphics extends IGroupElement, IDepthManagement, IRaycastable {}This would:
- Make it easier to implement each behavior independently
- Allow for better composition of UI elements
- Improve testability of each concern
Would you like me to create a GitHub issue to track this refactoring suggestion?
packages/core/src/ui/index.ts (2)
1-15
: LGTM! Well-organized exports structure.The exports are logically grouped and follow a clear pattern, providing a good foundation for the GUI infrastructure. The separation between core enums, UI components, and transitions is clear and maintainable.
Consider adding JSDoc comments to document the main export groups:
+/** Core UI enums */ export { UIGroup } from "./UIGroup"; export { CanvasRenderMode } from "./enums/CanvasRenderMode"; export { ResolutionAdaptationStrategy } from "./enums/ResolutionAdaptationStrategy"; +/** UI Components */ export { Button } from "./Button"; export { UICanvas } from "./UICanvas"; export { UIImage } from "./UIImage"; export { UIRenderer } from "./UIRenderer"; export { UIText } from "./UIText"; export { UITransform } from "./UITransform"; +/** Transition Components */ export { ColorTransition } from "./interactive/transition/ColorTransition"; export { ScaleTransition } from "./interactive/transition/ScaleTransition"; export { SpriteTransition } from "./interactive/transition/SpriteTransition"; export { Transition } from "./interactive/transition/Transition";
5-15
: Consider sorting exports within groups.While the current organization is clear, consider sorting the exports alphabetically within their groups to make it easier to locate specific exports as the list grows.
export { Button } from "./Button"; -export { UICanvas } from "./UICanvas"; -export { UIImage } from "./UIImage"; -export { UIRenderer } from "./UIRenderer"; -export { UIText } from "./UIText"; -export { UITransform } from "./UITransform"; +export { UICanvas } from "./UICanvas"; +export { UIImage } from "./UIImage"; +export { UIRenderer } from "./UIRenderer"; +export { UIText } from "./UIText"; +export { UITransform } from "./UITransform"; -export { ColorTransition } from "./interactive/transition/ColorTransition"; -export { ScaleTransition } from "./interactive/transition/ScaleTransition"; -export { SpriteTransition } from "./interactive/transition/SpriteTransition"; -export { Transition } from "./interactive/transition/Transition"; +export { ColorTransition } from "./interactive/transition/ColorTransition"; +export { ScaleTransition } from "./interactive/transition/ScaleTransition"; +export { SpriteTransition } from "./interactive/transition/SpriteTransition"; +export { Transition } from "./interactive/transition/Transition";packages/core/src/ui/interactive/transition/ScaleTransition.ts (2)
5-12
: Consider enhancing configurability and visual feedback.The current implementation could benefit from:
- Constructor parameters to allow customization of scale values and duration
- Different scale values for hover and disabled states to provide better visual feedback
Consider this implementation:
- constructor() { + constructor(config?: { + normal?: number; + hover?: number; + pressed?: number; + disabled?: number; + duration?: number; + }) { super(); - this._normal = 1; - this._hover = 1; - this._pressed = 1.2; - this._disabled = 1; - this._duration = 0.1; + this._normal = config?.normal ?? 1; + this._hover = config?.hover ?? 1.1; + this._pressed = config?.pressed ?? 1.2; + this._disabled = config?.disabled ?? 0.9; + this._duration = config?.duration ?? 0.2; }
18-20
: Consider using easing functions for smoother transitions.The current linear interpolation might not provide the most visually pleasing transition. Consider implementing easing functions for smoother animations.
Example implementation:
protected override _updateCurrentValue(srcValue: number, destValue: number, weight: number): void { // Ease in-out quad function const easeWeight = weight < 0.5 ? 2 * weight * weight : 1 - Math.pow(-2 * weight + 2, 2) / 2; this._currentValue = (destValue - srcValue) * easeWeight + srcValue; }packages/core/src/ui/interactive/transition/ColorTransition.ts (2)
6-15
: Consider documenting color states and extracting constants.The color states and transition duration would benefit from documentation and constant definitions.
Consider applying these improvements:
+/** Default transition duration in seconds */ +private static readonly DEFAULT_DURATION = 0.1; + +/** Default color states for different interaction states */ +private static readonly COLORS = { + NORMAL: new Color(1, 1, 1, 1), + HOVER: new Color(245 / 255, 245 / 255, 245 / 255, 1), + PRESSED: new Color(200 / 255, 200 / 255, 200 / 255, 1), + DISABLED: new Color(200 / 255, 200 / 255, 200 / 255, 1) +} as const; constructor() { super(); - this._normal = new Color(1, 1, 1, 1); - this._hover = new Color(245 / 255, 245 / 255, 245 / 255, 1); - this._pressed = new Color(200 / 255, 200 / 255, 200 / 255, 1); - this._disabled = new Color(200 / 255, 200 / 255, 200 / 255, 1); - this._duration = 0.1; + this._normal = ColorTransition.COLORS.NORMAL.clone(); + this._hover = ColorTransition.COLORS.HOVER.clone(); + this._pressed = ColorTransition.COLORS.PRESSED.clone(); + this._disabled = ColorTransition.COLORS.DISABLED.clone(); + this._duration = ColorTransition.DEFAULT_DURATION; this._currentValue = new Color(); }
17-29
: Consider optimizing color object reuse.The current implementation reuses the color object effectively in _getTargetValueCopy, but _updateCurrentValue could be optimized further.
Consider this optimization:
protected override _updateCurrentValue(srcValue: Color, destValue: Color, weight: number): void { if (weight >= 1) { this._currentValue.copyFrom(destValue); } else { - Color.lerp(srcValue, destValue, weight, this._currentValue); + // Avoid potential allocation in Color.lerp by ensuring _currentValue is always used + this._currentValue.r = srcValue.r + (destValue.r - srcValue.r) * weight; + this._currentValue.g = srcValue.g + (destValue.g - srcValue.g) * weight; + this._currentValue.b = srcValue.b + (destValue.b - srcValue.b) * weight; + this._currentValue.a = srcValue.a + (destValue.a - srcValue.a) * weight; } }packages/core/src/ui/Button.ts (3)
6-6
: Consider adding JSDoc for the listeners property.While the property is well-typed and properly encapsulated, adding JSDoc would improve maintainability by documenting the purpose of this collection.
+ /** Collection of click event listeners managed using SafeLoopArray for safe iteration. */ private _listeners: SafeLoopArray<IUIListener> = new SafeLoopArray<IUIListener>();
8-14
: Consider preventing duplicate listener registrations.The current implementation allows the same listener to be registered multiple times, which could lead to redundant executions.
addClicked(listener: (event: PointerEventData) => void): void { + if (this._listeners.find((value) => value.fn === listener)) { + return; + } this._listeners.push({ fn: listener }); }
38-41
: Consider adding JSDoc to document the interface.While the interface is well-structured, adding documentation would improve maintainability.
+/** + * Interface for UI event listeners with destruction tracking. + */ export interface IUIListener { + /** The event handling function */ fn: (event: PointerEventData) => void; + /** Flag indicating if the listener has been destroyed */ destroyed?: boolean; }packages/core/src/input/pointer/emitter/PointerPhysicsEventEmitter.ts (3)
13-49
: Consider breaking down the complex raycast processing logic.The method contains nested loops and multiple conditions that could be extracted into smaller, focused methods for better maintainability.
Consider refactoring into smaller methods:
private _isValidScene(scene: Scene): boolean { return scene.isActive && !scene.destroyed; } private _isPointerInViewport(x: number, y: number, viewport: { x: number, y: number, width: number, height: number }): boolean { return !(x < viewport.x || y < viewport.y || x > viewport.x + viewport.width || y > viewport.y + viewport.height); } private _processCamera(camera: Camera, ray: Ray, pointer: Pointer): boolean { if (camera.renderTarget) return false; const { x, y } = pointer.position; if (!this._isPointerInViewport(x, y, camera.pixelViewport)) { return false; } camera.screenPointToRay(pointer.position, ray); return true; }
79-96
: Consider caching event data to reduce object creation.The method creates multiple event data objects with the same parameters. Consider caching the event data for better performance.
private _processUp(pointer: Pointer): void { const { _enteredEntity: enteredEntity, _draggedEntity: draggedEntity } = this; if (enteredEntity) { const sameTarget = this._pressedEntity === enteredEntity; const eventData = this._createEventData(pointer, enteredEntity, enteredEntity); this._invokeEntityScripts(enteredEntity, (script: Script) => { script.onPointerUp?.(eventData); sameTarget && script.onPointerClick?.(eventData); script.onPointerDrop?.(eventData); }); } // ... rest of the method }
98-115
: Apply similar event data caching optimization.Similar to the _processUp method, consider caching the event data objects to reduce object creation.
packages/core/src/ui/UIUtils.ts (4)
21-37
: Add JSDoc documentation for better maintainability.This method handles complex parent traversal and listener management. Consider adding JSDoc documentation to explain:
- The purpose of the method
- The relationship between entities and listeners
- The significance of the root canvas check
47-82
: Consider extracting shared logic between registration methods.The
registerElementToCanvas
andregisterElementToGroup
methods share similar patterns for managing indices and elements. Consider extracting the common logic into a private helper method to improve maintainability and reduce code duplication.Example approach:
private static registerElementToContainer<T extends ICanvasElement | IGroupElement, C extends UICanvas | UIGroup>( element: T, container: C, getPreContainer: (el: T) => C, getElements: (c: C) => DisorderedArray<T>, updateIndex: (el: T, index: number) => void ): void { const preContainer = getPreContainer(element); if (preContainer !== container) { // ... shared logic here } }🧰 Tools
🪛 Biome
[error] 53-53: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 72-72: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
84-126
: Consider using type guard functions for safer type casting.Replace type casting with type guard functions to improve type safety and readability:
private static isUICanvas(component: Component): component is UICanvas { return component._componentType === ComponentType.UICanvas; } private static isUIGroup(component: Component): component is UIGroup { return component._componentType === ComponentType.UIGroup; }Then use them in the methods:
if (component.enabled && this.isUICanvas(component) && component._isRootCanvas) { return component; }
136-143
: Add comments explaining matrix transformations.The matrix operations for projection and view transformations would benefit from documentation explaining:
- The purpose of each transformation
- The coordinate space being used
- The significance of the magic numbers (2, 0)
packages/core/src/ui/UIRenderer.ts (2)
59-61
: Consider omitting redundant type annotations.The type annotations can be inferred from the initializers and can be omitted for better conciseness.
Apply these changes:
-protected _alpha: number = 1; +protected _alpha = 1; -protected _color: Color = new Color(1, 1, 1, 1); +protected _color = new Color(1, 1, 1, 1);
229-241
: Improve variable naming in _hitTest method.Consider using more descriptive variable names for better readability.
Apply these changes:
protected _hitTest(localPosition: Vector3): boolean { const { x, y } = localPosition; const uiTransform = <UITransform>this._transform; - const { x: width, y: height } = uiTransform.size; - const { x: pivotX, y: pivotY } = uiTransform.pivot; + const { x: elementWidth, y: elementHeight } = uiTransform.size; + const { x: pivotOffsetX, y: pivotOffsetY } = uiTransform.pivot; const { x: paddingLeft, y: paddingBottom, z: paddingRight, w: paddingTop } = this.raycastPadding; return ( - x >= -width * pivotX + paddingLeft && - x <= width * (1 - pivotX) - paddingRight && - y >= -height * pivotY + paddingTop && - y <= height * (1 - pivotY) - paddingBottom + x >= -elementWidth * pivotOffsetX + paddingLeft && + x <= elementWidth * (1 - pivotOffsetX) - paddingRight && + y >= -elementHeight * pivotOffsetY + paddingTop && + y <= elementHeight * (1 - pivotOffsetY) - paddingBottom ); }packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts (3)
223-232
: Improve readability and maintainability of path compositionSeveral improvements can be made to the
_composedPath
method:
- Avoid assignment within expressions
- Add early return for invalid input
- Consider extracting the path composition logic to a separate method for better reusability
Apply this diff to improve the code:
private _composedPath(element: Component, path: Entity[]): Entity[] { if (!element) { path.length = 0; return path; } - let entity = (path[0] = element._entity); + path[0] = element._entity; + let entity = element._entity; let i = 1; if (element._componentType === ComponentType.UICanvas && (<UICanvas>element)._isRootCanvas) { path.length = 1; return path; } else { const rootEntity = (<UICanvas | UIRenderer>element)._rootCanvas._entity; for (; i < PointerUIEventEmitter._MAX_PATH_DEPTH && !!entity && entity !== rootEntity; i++) { entity = path[i] = entity.parent; } } path.length = i; return path; }🧰 Tools
🪛 Biome
[error] 223-223: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
170-171
: Consider performance optimizations for path operationsThe current implementation creates new paths and performs comparisons frequently, which could impact performance with high-frequency pointer events. Consider:
- Caching paths when possible
- Using a more efficient data structure for path comparison
- Implementing path pooling to reduce garbage collection
13-21
: Enhance documentation and type safetyThe class could benefit from:
- JSDoc documentation for the class explaining its purpose and usage
- Type annotations for event handlers
- Documentation of the relationship between different pointer states
- Interface definitions for the event data structure
packages/core/src/input/pointer/PointerManager.ts (5)
4-12
: Well-structured architectural improvements!The introduction of
ClearableObjectPool
and event emitters shows good separation of concerns and memory management practices. The modular approach with separate emitter classes will make the code more maintainable and testable.Also applies to: 41-41
99-110
: Consider caching physics initialization stateThe
_engine._physicsInitialized
check on each pointer creation could lead to inconsistent behavior if physics initialization state changes during runtime. Consider caching this state during PointerManager initialization.constructor(engine: Engine, target: EventTarget) { // ... existing code ... + this._physicsEnabled = engine._physicsInitialized; } // Later in the code -_engine._physicsInitialized && pointer._addEmitters(PointerPhysicsEventEmitter, _eventPool); +this._physicsEnabled && pointer._addEmitters(PointerPhysicsEventEmitter, _eventPool);
224-255
: Consider using event type constantsThe string literals for event types could be replaced with constants to prevent typos and improve maintainability. Consider creating an enum or constant object for event types.
const PointerEventTypes = { DOWN: 'pointerdown', UP: 'pointerup', MOVE: 'pointermove', LEAVE: 'pointerleave', CANCEL: 'pointercancel' } as const;
282-288
: Optimize cleanup operationsConsider combining the array clearing operations for better performance. You could create a helper method to clear multiple arrays at once.
private _clearArrays() { [this._nativeEvents, this._pointers, this._downList, this._downMap, this._upList, this._upMap].forEach(arr => arr.length = 0); }
41-41
: Document object pool configurationConsider adding JSDoc comments to explain the object pool's configuration and lifecycle, particularly its garbage collection strategy and pool size management.
packages/core/src/ui/UIImage.ts (2)
196-216
: Optimize property access and null checks.Multiple property accesses and null checks can be optimized by destructuring and combining conditions.
protected override _render(context: RenderContext): void { - const { _sprite: sprite } = this; - const transform = this._transform as UITransform; - const { x: width, y: height } = transform.size; - if (!sprite?.texture || !width || !height) { + const { _sprite: sprite, _transform: transform, _color: color, _alpha: alpha } = this; + if (!sprite?.texture || !transform) { return; } + const { x: width, y: height } = (transform as UITransform).size; + if (!width || !height || color.a * alpha <= 0) { + return; + } let material = this.getMaterial(); if (!material) { return; } // @todo: This question needs to be raised rather than hidden. if (material.destroyed) { material = this._engine._basicResources.uiDefaultMaterial; } - if (this._color.a * this._alpha <= 0) { - return; - }
362-371
: Consider using TypeScript const enum for better performance.The
ImageUpdateFlags
enum would benefit from being a const enum as it's used internally for bitwise operations.-enum ImageUpdateFlags { +const enum ImageUpdateFlags { UV = 0x20, /** LocalPosition | WorldPosition | UV */ AllPositionAndUV = 0x23, /** LocalPosition | WorldPosition | UV | Color */ AllPositionUVAndColor = 0x33, /** LocalPosition | WorldPosition | UV | Color | LocalBounds | WorldBounds */ All = 0x3f }packages/core/src/ui/UIText.ts (3)
235-241
: Consider adding error handling in the constructor.The constructor assumes
engine._textDefaultFont
andengine._basicResources.textDefaultMaterial
will always exist. Consider adding null checks and throwing appropriate errors if these dependencies are missing.constructor(entity: Entity) { super(entity); const { engine } = this; + if (!engine._textDefaultFont) { + throw new Error("Default font not initialized in engine."); + } this.font = engine._textDefaultFont; + if (!engine._basicResources?.textDefaultMaterial) { + throw new Error("Default text material not initialized in engine resources."); + } this.setMaterial(engine._basicResources.textDefaultMaterial); }
257-257
: Improve readability by avoiding assignment in expression.The assignment within the logical AND expression makes the code harder to read.
- this._subFont && (this._subFont = null); + if (this._subFont) { + this._subFont = null; + }🧰 Tools
🪛 Biome
[error] 257-257: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
324-363
: Consider performance optimizations in render loop.The render method performs several operations that could be optimized:
- Multiple property accesses could be cached
- Array iterations could be optimized
- Object allocations in render loop should be minimized
Consider these optimizations:
- Cache frequently accessed properties outside the loop
- Use pre-allocated arrays for temporary calculations
- Consider implementing object pooling for SubRenderElements
- Profile the rendering performance with large text content to identify bottlenecks
Would you like me to provide a detailed implementation of these optimizations?
packages/core/src/Entity.ts (1)
108-113
: Consider adding type safety for transform access.While the refactoring from public property to private property with getter is good for encapsulation, consider adding runtime type checking to prevent potential issues with transform types.
get transform(): Transform { + if (!this._transform) { + throw new Error('Transform component is missing'); + } return this._transform; }packages/core/src/ui/interactive/transition/Transition.ts (2)
6-6
: Avoid using 'any' as default type for generics to maintain type safetyThe generic type parameters
T
andK
default toany
, which reduces the benefits of TypeScript's type checking. By specifying more precise default types or removing the defaults, you can enhance type safety and catch potential errors at compile time.
114-119
: Ensure_countDown
does not become negative in_onUpdate
In the
_onUpdate()
method,_countDown
is decremented bydelta
, but there's no check to prevent it from becoming negative. Negative_countDown
values could cause unintended behavior in the transition logic.Consider clamping
_countDown
to zero after decrementing to prevent negative values.Suggested change:
if (this._countDown > 0) { - this._countDown -= delta; + this._countDown = Math.max(0, this._countDown - delta); this._updateValue(); }packages/core/src/ui/interactive/UIInteractive.ts (1)
115-115
: Separate assignments for better readabilityAssigning multiple variables in a single statement can reduce readability. Consider splitting the assignments to improve clarity.
Apply this diff:
- this._isPointerInside = this._isPointerDown = false; + this._isPointerInside = false; + this._isPointerDown = false;packages/core/src/ui/UICanvas.ts (5)
89-89
: Useif
statement instead of logical operators for clarityUsing logical operators like
&&
for control flow can reduce code readability. Consider using an explicitif
statement to make the code clearer.Apply this diff to enhance readability:
- (referenceResolution.x !== val.x || referenceResolution.y !== val.y) && referenceResolution.copyFrom(val); + if (referenceResolution.x !== val.x || referenceResolution.y !== val.y) { + referenceResolution.copyFrom(val); + }
142-143
: Useif
statement instead of logical operators for side effectsUsing logical operators like
&&
to trigger side effects can make the code harder to read and understand. Replace it with anif
statement for better clarity.Apply this diff to improve code clarity:
- this._realRenderMode === CanvasRenderMode.ScreenSpaceOverlay && - this.scene._componentsManager._overlayCanvasesSortingFlag; + if (this._realRenderMode === CanvasRenderMode.ScreenSpaceOverlay) { + this.scene._componentsManager._overlayCanvasesSortingFlag = true; + }
154-154
: Useif
statement instead of logical operators for function callsUsing logical operators like
&&
to conditionally call functions can reduce code readability. Consider using an explicitif
statement for better clarity.Apply this diff to enhance readability:
- this._realRenderMode === CanvasRenderMode.ScreenSpaceCamera && this._adapterPoseInScreenSpace(); + if (this._realRenderMode === CanvasRenderMode.ScreenSpaceCamera) { + this._adapterPoseInScreenSpace(); + }
194-194
: Avoid assignment within an expression for better readabilityAssigning values within expressions can be confusing and reduce code clarity. Separate the assignment from the declaration to improve readability.
Apply this diff to enhance code clarity:
- const renderElement = (this._renderElement = engine._renderElementPool.get()); + this._renderElement = engine._renderElementPool.get(); + const renderElement = this._renderElement;🧰 Tools
🪛 Biome
[error] 194-194: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
359-359
: Avoid assignment within a logical expressionAssigning a value within a logical expression can reduce code readability. Use an explicit
if
statement instead.Apply this diff to improve clarity:
- child.isActive && (depth = this._walk(child, elements, depth)); + if (child.isActive) { + depth = this._walk(child, elements, depth); + }🧰 Tools
🪛 Biome
[error] 359-359: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (22)
packages/core/src/Entity.ts
(11 hunks)packages/core/src/input/pointer/PointerManager.ts
(6 hunks)packages/core/src/input/pointer/emitter/PointerPhysicsEventEmitter.ts
(1 hunks)packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts
(1 hunks)packages/core/src/ui/Button.ts
(1 hunks)packages/core/src/ui/UICanvas.ts
(1 hunks)packages/core/src/ui/UIGroup.ts
(1 hunks)packages/core/src/ui/UIImage.ts
(1 hunks)packages/core/src/ui/UIRenderer.ts
(1 hunks)packages/core/src/ui/UIText.ts
(1 hunks)packages/core/src/ui/UIUtils.ts
(1 hunks)packages/core/src/ui/index.ts
(1 hunks)packages/core/src/ui/interactive/InteractiveState.ts
(1 hunks)packages/core/src/ui/interactive/UIInteractive.ts
(1 hunks)packages/core/src/ui/interactive/transition/ColorTransition.ts
(1 hunks)packages/core/src/ui/interactive/transition/ScaleTransition.ts
(1 hunks)packages/core/src/ui/interactive/transition/SpriteTransition.ts
(1 hunks)packages/core/src/ui/interactive/transition/Transition.ts
(1 hunks)packages/core/src/ui/interactive/transition/TransitionType.ts
(1 hunks)packages/core/src/ui/interface/ICanvasElement.ts
(1 hunks)packages/core/src/ui/interface/IGroupElement.ts
(1 hunks)packages/core/src/ui/interface/IUIGraphics.ts
(1 hunks)
🧰 Additional context used
🪛 Biome
packages/core/src/Entity.ts
[error] 616-616: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/input/pointer/emitter/PointerPhysicsEventEmitter.ts
[error] 67-67: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 67-67: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 223-223: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/Button.ts
[error] 21-21: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 34-34: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UICanvas.ts
[error] 194-194: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 211-211: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 359-359: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 473-474: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 483-484: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 485-487: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIGroup.ts
[error] 105-105: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation
(lint/complexity/noExtraBooleanCast)
[error] 110-110: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation
(lint/complexity/noExtraBooleanCast)
[error] 153-153: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIRenderer.ts
[error] 163-163: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 167-167: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 168-168: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 166-170: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIText.ts
[error] 257-257: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 496-496: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 497-497: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 507-507: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 509-509: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 591-591: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 593-593: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIUtils.ts
[error] 16-153: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
[error] 53-53: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 72-72: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 130-130: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 130-130: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 131-131: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 131-131: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 132-132: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 132-132: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
packages/core/src/ui/interactive/UIInteractive.ts
[error] 147-149: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
🔇 Additional comments (35)
packages/core/src/ui/interactive/transition/TransitionType.ts (1)
1-6
: LGTM! Clean and well-structured enum implementation.
The enum provides a clear and type-safe way to represent different transition types for UI elements.
packages/core/src/ui/interface/IGroupElement.ts (1)
1-1
: Verify the GroupModifyFlags type definition.
The GroupModifyFlags
import is used but its type definition isn't visible in the current context.
✅ Verification successful
GroupModifyFlags is properly defined and used correctly
The GroupModifyFlags
enum is defined in packages/core/src/ui/UIGroup.ts
with clear flag values:
None = 0x0
Alpha = 0x1
Interactive = 0x2
All = 0x3
The import in IGroupElement.ts
correctly references this enum which is used throughout the UI system for group modifications.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check the definition of GroupModifyFlags
# Expected: Find the type definition and its possible values
# Search for GroupModifyFlags definition
echo "Searching for GroupModifyFlags definition:"
ast-grep --pattern 'enum GroupModifyFlags {
$$$
}'
# Backup search using ripgrep
echo -e "\nBackup search for GroupModifyFlags:"
rg -A 5 "GroupModifyFlags"
Length of output: 7403
packages/core/src/ui/interface/ICanvasElement.ts (1)
1-2
: LGTM! Clean and focused imports.
The imports are well-organized and specifically import only the required types.
packages/core/src/ui/interface/IUIGraphics.ts (1)
1-3
: LGTM! Well-organized imports.
The imports are properly structured with clear separation between external package and internal module imports.
packages/core/src/ui/interactive/transition/SpriteTransition.ts (3)
1-5
: LGTM! Clean and well-structured class declaration.
The imports are appropriate, and the class extension with generic types is well-defined.
10-12
: Verify if binary transition is the intended behavior.
The current implementation provides an abrupt switch between source and destination sprites at weight = 1, rather than a smooth transition. If this is intentional, consider adding a comment explaining why interpolation is not used.
✅ Verification successful
Binary transition for sprites is consistent with the codebase design
The implementation is correct as sprites cannot be interpolated like other properties. This is evident from the codebase where:
- ColorTransition uses
Color.lerp
for smooth color transitions - ScaleTransition uses linear interpolation for smooth scaling
- SpriteTransition uses binary switching, which is appropriate as sprites are discrete assets
Consider adding a comment explaining this design choice to improve code clarity.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for other transition implementations to verify if this binary approach is consistent
ast-grep --pattern 'class $_Transition extends Transition<$_, $_> {
$$$
protected override _updateCurrentValue($_, $_, weight: number): void {
$$$
}
$$$
}'
Length of output: 169
Script:
#!/bin/bash
# Let's try a different approach to find transition implementations
rg "_updateCurrentValue.*weight.*number" -A 5
# Also search for the base Transition class to understand the expected behavior
rg "class Transition[^a-zA-Z]" -A 10
Length of output: 3776
14-16
: Add type safety and error handling for _normal property.
The method uses this._normal
as a fallback, but this property's initialization is not visible in the current context.
Consider adding:
- Type guard to ensure value is a valid Sprite
- Explicit initialization of _normal in the constructor
- Error handling for invalid sprite values
protected override _applyValue(value: Sprite): void {
+ if (value && !this.isValidSprite(value)) {
+ throw new Error('Invalid sprite value provided');
+ }
this._target.sprite = value || this._normal;
}
+
+ private isValidSprite(value: any): value is Sprite {
+ return value && typeof value === 'object';
+ // Add additional sprite validation logic
+ }
packages/core/src/ui/index.ts (1)
1-15
: Verify all exports are properly implemented.
Let's ensure all exported components exist and are properly implemented.
✅ Verification successful
Let me generate another script to verify the specific exports since the previous output didn't show us the actual export declarations.
Based on the verification results, I can now provide the final response:
All exports are properly implemented and available
All exported components from packages/core/src/ui/index.ts
are verified to exist and are properly implemented:
- Enums:
CanvasRenderMode
andResolutionAdaptationStrategy
are properly exported - UI Components:
UIGroup
,Button
,UICanvas
,UIImage
,UIText
, andUITransform
are all exported as classes - Transitions:
ColorTransition
,ScaleTransition
,SpriteTransition
, and baseTransition
are all exported as classes
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify that all exported files exist and export the expected components
# Test: Check if all imported files exist
echo "Checking if all files exist..."
for file in "UIGroup" "enums/CanvasRenderMode" "enums/ResolutionAdaptationStrategy" \
"Button" "UICanvas" "UIImage" "UIRenderer" "UIText" "UITransform" \
"interactive/transition/ColorTransition" "interactive/transition/ScaleTransition" \
"interactive/transition/SpriteTransition" "interactive/transition/Transition"; do
if ! fd -p "packages/core/src/ui/$file.ts"; then
echo "Missing file: $file"
fi
done
# Test: Verify exports in each file
echo "Checking exports in each file..."
for file in $(fd -e ts . "packages/core/src/ui"); do
echo "Checking $file..."
ast-grep --pattern 'export class $name' "$file"
ast-grep --pattern 'export enum $name' "$file"
ast-grep --pattern 'export interface $name' "$file"
done
Length of output: 14751
Script:
#!/bin/bash
# Check specific exports in each file
# Check UIGroup export
echo "Checking UIGroup..."
rg "export (class|enum|interface) UIGroup" packages/core/src/ui/UIGroup.ts -A 1
echo "Checking enums..."
rg "export enum (CanvasRenderMode|ResolutionAdaptationStrategy)" packages/core/src/ui/enums/ -A 1
echo "Checking UI components..."
for component in "Button" "UICanvas" "UIImage" "UIRenderer" "UIText" "UITransform"; do
echo "Checking $component..."
rg "export (class|interface) $component" "packages/core/src/ui/$component.ts" -A 1
done
echo "Checking transitions..."
for transition in "ColorTransition" "ScaleTransition" "SpriteTransition" "Transition"; do
echo "Checking $transition..."
rg "export (class|interface) $transition" "packages/core/src/ui/interactive/transition/$transition.ts" -A 1
done
Length of output: 3977
packages/core/src/ui/interactive/transition/ScaleTransition.ts (2)
1-4
: LGTM! Clean and type-safe class declaration.
The imports are minimal and the generic type parameters are well-defined.
14-16
: Verify uniform scaling assumption.
The method assumes uniform scaling by only checking the x-axis scale. This might lead to unexpected behavior if the entity has non-uniform scaling.
packages/core/src/ui/interactive/transition/ColorTransition.ts (1)
1-5
: LGTM! Clean imports and proper class declaration.
The class structure and type parameters are well-defined, following TypeScript best practices.
packages/core/src/ui/Button.ts (2)
1-5
: LGTM! Clean imports and proper inheritance.
The imports are focused and the inheritance hierarchy is appropriate for a button component.
24-30
: LGTM! Efficient and safe event dispatch implementation.
The implementation correctly handles listener iteration and destroyed state checking.
packages/core/src/input/pointer/emitter/PointerPhysicsEventEmitter.ts (5)
1-11
: LGTM! Clean class structure with well-defined properties.
The class is well-organized with appropriate imports and clearly named protected properties for tracking entity states.
54-61
: LGTM! Clean and focused drag event handling.
The method is well-structured with proper null checks and clear event handling logic.
66-74
: Multiple assignments in expression reduce readability.
This issue was previously identified. The suggestion to separate the assignments for better readability is still valid.
🧰 Tools
🪛 Biome
[error] 67-67: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 67-67: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
117-119
: LGTM! Thorough cleanup of entity references.
The method properly cleans up all entity references in a single statement.
121-142
: LGTM! Well-designed helper methods.
The private methods are focused, properly encapsulated, and maintain consistent event handling patterns.
packages/core/src/ui/UIGroup.ts (4)
1-41
: LGTM! Well-structured class definition with proper property management.
The class structure follows best practices with:
- Clear separation of concerns using decorators
- Proper type definitions
- Well-documented internal properties
43-75
: LGTM! Well-implemented property accessors.
The getters and setters properly handle state updates and trigger necessary modifications.
117-144
: LGTM! Thorough lifecycle management.
The methods properly handle:
- Registration and cleanup of entity listeners
- Memory management with array clearing
- Garbage collection
186-191
: LGTM! Well-defined bit flags enum.
The enum correctly uses bit flags to enable efficient combination of multiple modification types.
packages/core/src/ui/UIRenderer.ts (1)
205-227
: 🛠️ Refactor suggestion
Optimize raycast performance and improve readability.
The raycast method can be optimized by:
- Caching the plane intersection result
- Using early returns for better readability
Apply these changes:
_raycast(ray: Ray, out: HitResult, distance: number = Number.MAX_SAFE_INTEGER): boolean {
const plane = UIRenderer._tempPlane;
const { transform } = this._entity;
const { worldForward, worldMatrix, worldPosition } = transform;
const normal = plane.normal.copyFrom(worldForward);
plane.distance = -Vector3.dot(normal, worldPosition);
- const curDistance = ray.intersectPlane(plane);
- if (curDistance >= 0 && curDistance < distance) {
+ const intersectionDistance = ray.intersectPlane(plane);
+ if (intersectionDistance < 0 || intersectionDistance >= distance) {
+ return false;
+ }
- const hitPointWorld = ray.getPoint(curDistance, UIRenderer._tempVec30);
+ const hitPointWorld = ray.getPoint(intersectionDistance, UIRenderer._tempVec30);
const worldMatrixInv = UIRenderer._tempMat;
- Matrix.invert(transform.worldMatrix, worldMatrixInv);
+ Matrix.invert(worldMatrix, worldMatrixInv);
const localPosition = UIRenderer._tempVec31;
Vector3.transformCoordinate(hitPointWorld, worldMatrixInv, localPosition);
- if (this._hitTest(localPosition)) {
- out.distance = curDistance;
- out.entity = this._entity;
- out.component = this;
- out.normal.copyFrom(normal);
- out.point.copyFrom(hitPointWorld);
- return true;
- }
+ if (!this._hitTest(localPosition)) {
+ return false;
}
- return false;
+
+ out.distance = intersectionDistance;
+ out.entity = this._entity;
+ out.component = this;
+ out.normal.copyFrom(normal);
+ out.point.copyFrom(hitPointWorld);
+ return true;
}
Likely invalid or redundant comment.
packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts (2)
85-85
: Remove unnecessary return statement
The method is declared as void
but contains a return null
statement.
🧰 Tools
🪛 Biome
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
14-16
: Consider potential thread safety issues with static arrays
The static arrays _path0
and _path1
are shared across all instances and could lead to race conditions if multiple pointer events are processed concurrently. Consider making these instance-level properties or implementing proper synchronization.
packages/core/src/input/pointer/PointerManager.ts (1)
73-83
: Verify pointer disposal completeness
While the pointer cleanup logic is improved, ensure that all resources associated with disposed pointers are properly cleaned up. Consider adding explicit cleanup of any event listeners or cached data.
packages/core/src/ui/UIImage.ts (1)
343-356
:
Fix incorrect assembler usage in Tiled mode.
The _getUVByLocalPosition
method incorrectly uses SlicedSpriteAssembler
for the Tiled draw mode.
case SpriteDrawMode.Tiled:
- return SlicedSpriteAssembler.getUVByLocalPosition(this, size.x, size.y, pivot, position, out);
+ return TiledSpriteAssembler.getUVByLocalPosition(this, size.x, size.y, pivot, position, out);
Likely invalid or redundant comment.
packages/core/src/Entity.ts (4)
10-17
: LGTM: New imports and properties enhance UI and state management capabilities.
The additions properly support the new UI infrastructure and state management features.
Also applies to: 86-104
338-338
: LGTM: Parent change handling is properly implemented.
The new _setParentChange
method correctly propagates changes through the transform hierarchy and dispatches modification events.
Also applies to: 607-610
531-534
: LGTM: Proper cleanup of update flag manager.
The destroy method properly cleans up the update flag manager by removing all listeners and nullifying the reference.
798-798
: LGTM: Entity modification flags are well-defined.
The new enum properly categorizes different types of entity modifications, and the dispatch in _setSiblingIndex
is correctly implemented.
Also applies to: 817-824
packages/core/src/ui/interactive/UIInteractive.ts (1)
12-196
: Overall code implementation looks solid
The UIInteractive
class is well-structured, and the interactive state management logic is sound. Methods are appropriately overridden, and the use of transitions enhances the interactivity of UI elements.
🧰 Tools
🪛 Biome
[error] 147-149: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UICanvas.ts (3)
473-474
: Previous comment on missing break
statement still applies
The missing break
statement in this switch case was identified earlier and needs to be addressed to prevent unintended fall-through.
🧰 Tools
🪛 Biome
[error] 473-474: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
483-484
: Previous comment on missing break
statement still applies
The issue of the missing break
statement in this switch case remains unresolved. Please refer to the earlier feedback.
🧰 Tools
🪛 Biome
[error] 483-484: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
485-487
: Previous comment on missing break
statement still applies
Please address the previously noted missing break
statements to prevent unintended fall-through in this switch case.
🧰 Tools
🪛 Biome
[error] 485-487: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
export interface ICanvasElement { | ||
_entity: Entity; | ||
_parents: Entity[]; | ||
_rootCanvas: UICanvas; | ||
_indexInCanvas: number; | ||
_onEntityModify(flag: EntityModifyFlags): void; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider revising the interface design to follow TypeScript best practices.
Several architectural and style concerns:
-
The underscore prefix on all members suggests they are internal implementation details, which shouldn't be part of a public interface contract. Consider:
- Moving these implementation details to an abstract class
- Or removing the underscore prefix if these are meant to be public members
-
The interface appears to expose internal state management (
_parents
,_indexInCanvas
), which could make it harder to change the implementation later. -
Missing documentation for the interface and its members. Consider adding JSDoc comments to explain:
- The purpose of the interface
- The role of each member
- The contract requirements for implementing classes
Here's a suggested refactor:
+/**
+ * Represents a UI element that can be rendered on a canvas.
+ * Provides contract for managing canvas element hierarchy and modifications.
+ */
export interface ICanvasElement {
- _entity: Entity;
- _parents: Entity[];
- _rootCanvas: UICanvas;
- _indexInCanvas: number;
- _onEntityModify(flag: EntityModifyFlags): void;
+ /** The entity associated with this canvas element */
+ entity: Entity;
+ /** The parent entities in the hierarchy */
+ parents: Entity[];
+ /** The root canvas this element belongs to */
+ rootCanvas: UICanvas;
+ /** The rendering order index within the canvas */
+ indexInCanvas: number;
+ /** Handles modifications to the associated entity */
+ onEntityModify(flag: EntityModifyFlags): void;
}
Consider:
- Breaking this into smaller interfaces (e.g., separate hierarchy management from entity management)
- Using composition over inheritance by providing utility classes for common functionality
- Reducing coupling with
Entity
andUICanvas
by introducing abstraction layers
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export interface ICanvasElement { | |
_entity: Entity; | |
_parents: Entity[]; | |
_rootCanvas: UICanvas; | |
_indexInCanvas: number; | |
_onEntityModify(flag: EntityModifyFlags): void; | |
} | |
/** | |
* Represents a UI element that can be rendered on a canvas. | |
* Provides contract for managing canvas element hierarchy and modifications. | |
*/ | |
export interface ICanvasElement { | |
/** The entity associated with this canvas element */ | |
entity: Entity; | |
/** The parent entities in the hierarchy */ | |
parents: Entity[]; | |
/** The root canvas this element belongs to */ | |
rootCanvas: UICanvas; | |
/** The rendering order index within the canvas */ | |
indexInCanvas: number; | |
/** Handles modifications to the associated entity */ | |
onEntityModify(flag: EntityModifyFlags): void; | |
} |
export interface IUIGraphics extends IGroupElement { | ||
depth: number; | ||
raycastEnable: boolean; | ||
raycastPadding: Vector4; | ||
_raycast(ray: Ray, out: HitResult, distance: number): boolean; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider revising interface design and adding documentation.
-
The
_raycast
method is prefixed with underscore suggesting it's private, which is unconventional for interface contracts. Consider:- Removing the underscore if this method should be public
- Or moving this implementation detail to the concrete class if it should be private
-
The interface would benefit from JSDoc documentation explaining:
- The purpose and valid range of the
depth
property - The meaning of each component in
raycastPadding
(left, top, right, bottom) - The return value semantics of
_raycast
- The purpose and valid range of the
Here's a suggested improvement:
+/**
+ * Interface for UI graphics elements that support raycasting and depth management.
+ */
export interface IUIGraphics extends IGroupElement {
+ /** The depth value used for z-ordering. Higher values appear in front. */
depth: number;
+ /** Whether this element should participate in raycasting operations. */
raycastEnable: boolean;
+ /** Padding values (left, top, right, bottom) to expand/contract the raycast hit area. */
raycastPadding: Vector4;
- _raycast(ray: Ray, out: HitResult, distance: number): boolean;
+ /**
+ * Performs raycasting against this UI element.
+ * @param ray The ray to test against
+ * @param out The hit result information if intersection occurs
+ * @param distance The maximum distance to test
+ * @returns true if the ray intersects with this element
+ */
+ raycast(ray: Ray, out: HitResult, distance: number): boolean;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export interface IUIGraphics extends IGroupElement { | |
depth: number; | |
raycastEnable: boolean; | |
raycastPadding: Vector4; | |
_raycast(ray: Ray, out: HitResult, distance: number): boolean; | |
} | |
/** | |
* Interface for UI graphics elements that support raycasting and depth management. | |
*/ | |
export interface IUIGraphics extends IGroupElement { | |
/** The depth value used for z-ordering. Higher values appear in front. */ | |
depth: number; | |
/** Whether this element should participate in raycasting operations. */ | |
raycastEnable: boolean; | |
/** Padding values (left, top, right, bottom) to expand/contract the raycast hit area. */ | |
raycastPadding: Vector4; | |
/** | |
* Performs raycasting against this UI element. | |
* @param ray The ray to test against | |
* @param out The hit result information if intersection occurs | |
* @param distance The maximum distance to test | |
* @returns true if the ray intersects with this element | |
*/ | |
raycast(ray: Ray, out: HitResult, distance: number): boolean; | |
} |
protected _getTargetValueCopy(): Sprite { | ||
return this._target?.sprite; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider implementing actual value copying and adding documentation.
The method name suggests it returns a copy, but it's returning a reference to the original sprite. This could lead to unintended modifications of the source sprite.
Consider:
- Adding JSDoc documentation explaining the method's purpose
- Implementing actual value copying if sprite mutation should be prevented:
+ /**
+ * Returns a copy of the target's sprite value.
+ * @returns A copy of the target sprite or undefined if target is not set
+ */
protected _getTargetValueCopy(): Sprite {
- return this._target?.sprite;
+ return this._target?.sprite ? { ...this._target.sprite } : undefined;
}
Committable suggestion skipped: line range outside the PR's diff.
protected override _applyValue(value: number): void { | ||
this._target.entity.transform.setScale(value, value, value); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add validation for scale values.
The method should validate scale values to prevent negative or zero scales which could cause rendering issues.
Suggested implementation:
protected override _applyValue(value: number): void {
+ if (value <= 0) {
+ console.warn('Scale values should be positive. Clamping to 0.01');
+ value = Math.max(0.01, value);
+ }
this._target.entity.transform.setScale(value, value, value);
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
protected override _applyValue(value: number): void { | |
this._target.entity.transform.setScale(value, value, value); | |
} | |
protected override _applyValue(value: number): void { | |
if (value <= 0) { | |
console.warn('Scale values should be positive. Clamping to 0.01'); | |
value = Math.max(0.01, value); | |
} | |
this._target.entity.transform.setScale(value, value, value); | |
} |
protected override _applyValue(value: Color): void { | ||
this._target.color = value; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add null check for target in _applyValue method.
The _applyValue method should validate that _target exists before attempting to set its color property.
Apply this fix:
protected override _applyValue(value: Color): void {
+ if (!this._target) {
+ return;
+ }
this._target.color = value;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
protected override _applyValue(value: Color): void { | |
this._target.color = value; | |
} | |
protected override _applyValue(value: Color): void { | |
if (!this._target) { | |
return; | |
} | |
this._target.color = value; | |
} |
set normal(value: T) { | ||
const preNormal = this._normal; | ||
if (preNormal !== value) { | ||
this._normal = value; | ||
this._onStateValueDirty(InteractiveState.Normal); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use deep comparison in setters to detect changes in complex objects
In the setters for normal
, pressed
, hover
, and disabled
, the comparison if (preValue !== value)
may not detect changes in complex types like Sprite
, Color
, or Vector3
since it compares object references rather than their contents. If the properties of these objects change without the reference changing, the update might not be detected.
Consider implementing a deep comparison or using an equals
method to accurately detect changes in the values.
Also applies to: 35-41, 47-53, 59-65
private _updateValue() { | ||
const weight = this._duration ? 1 - this._countDown / this._duration : 1; | ||
this._updateCurrentValue(this._initialValue, this._finalValue, weight); | ||
this._target?.enabled && this._applyValue(this._currentValue); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clamp weight
between 0 and 1 in _updateValue
to ensure valid interpolation
In the _updateValue()
method, weight
can become less than 0 or greater than 1 if _countDown
becomes negative or exceeds _duration
due to floating-point inaccuracies or unexpected usage. This can lead to invalid interpolation results.
Consider clamping weight
between 0 and 1 to maintain valid interpolation.
Suggested fix:
private _updateValue() {
- const weight = this._duration ? 1 - this._countDown / this._duration : 1;
+ const weight = this._duration ? Math.min(1, Math.max(0, 1 - this._countDown / this._duration)) : 1;
this._updateCurrentValue(this._initialValue, this._finalValue, weight);
this._target?.enabled && this._applyValue(this._currentValue);
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
private _updateValue() { | |
const weight = this._duration ? 1 - this._countDown / this._duration : 1; | |
this._updateCurrentValue(this._initialValue, this._finalValue, weight); | |
this._target?.enabled && this._applyValue(this._currentValue); | |
} | |
private _updateValue() { | |
const weight = this._duration ? Math.min(1, Math.max(0, 1 - this._countDown / this._duration)) : 1; | |
this._updateCurrentValue(this._initialValue, this._finalValue, weight); | |
this._target?.enabled && this._applyValue(this._currentValue); | |
} |
set duration(value: number) { | ||
if (value < 0) value = 0; | ||
const preDuration = this._duration; | ||
if (preDuration !== value) { | ||
this._duration = value; | ||
if (this._countDown > 0) { | ||
this._countDown = value * (1 - this._countDown / preDuration); | ||
this._updateValue(); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prevent division by zero when adjusting _countDown
in duration
setter
In the duration
setter, when adjusting _countDown
, there is a potential for division by zero if preDuration
is zero. This can result in NaN
or Infinity
values, causing unexpected behavior in the transition timing.
Consider adding a check to handle the scenario when preDuration
is zero to prevent division by zero.
Here's a possible fix:
if (preDuration !== value) {
this._duration = value;
if (this._countDown > 0) {
+ if (preDuration > 0) {
this._countDown = value * (1 - this._countDown / preDuration);
+ } else {
+ this._countDown = value;
+ }
this._updateValue();
}
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
set duration(value: number) { | |
if (value < 0) value = 0; | |
const preDuration = this._duration; | |
if (preDuration !== value) { | |
this._duration = value; | |
if (this._countDown > 0) { | |
this._countDown = value * (1 - this._countDown / preDuration); | |
this._updateValue(); | |
} | |
} | |
} | |
set duration(value: number) { | |
if (value < 0) value = 0; | |
const preDuration = this._duration; | |
if (preDuration !== value) { | |
this._duration = value; | |
if (this._countDown > 0) { | |
if (preDuration > 0) { | |
this._countDown = value * (1 - this._countDown / preDuration); | |
} else { | |
this._countDown = value; | |
} | |
this._updateValue(); | |
} | |
} | |
} |
case EntityModifyFlags.UICanvasEnableInScene: | ||
case EntityModifyFlags.Parent: | ||
UIUtils.registerElementToCanvas(this, UIUtils.getRootCanvasInParent(this._entity)); | ||
UIUtils.registerEntityListener(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add missing break
statements to prevent unintended fallthrough in switch cases
The switch cases for EntityModifyFlags.UICanvasEnableInScene
and EntityModifyFlags.Parent
lack break
statements, causing unintended fallthrough to the next case. This could lead to unexpected behavior.
Apply this diff to add the missing break
statements:
145 switch (flag) {
146 case EntityModifyFlags.UICanvasEnableInScene:
147 case EntityModifyFlags.Parent:
148 UIUtils.registerElementToCanvas(this, UIUtils.getRootCanvasInParent(this._entity));
149 UIUtils.registerEntityListener(this);
+150 break;
151 case EntityModifyFlags.UIGroupEnableInScene:
152 UIUtils.registerElementToGroup(this, UIUtils.getGroupInParents(this._entity));
153 break;
154 default:
155 break;
156 }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
case EntityModifyFlags.UICanvasEnableInScene: | |
case EntityModifyFlags.Parent: | |
UIUtils.registerElementToCanvas(this, UIUtils.getRootCanvasInParent(this._entity)); | |
UIUtils.registerEntityListener(this); | |
case EntityModifyFlags.UICanvasEnableInScene: | |
case EntityModifyFlags.Parent: | |
UIUtils.registerElementToCanvas(this, UIUtils.getRootCanvasInParent(this._entity)); | |
UIUtils.registerEntityListener(this); | |
break; |
🧰 Tools
🪛 Biome
[error] 147-149: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UICanvas.ts
Outdated
const { min, max } = renderer.bounds; | ||
if (min.x > width || max.x < 0 || min.y > height || max.y < 0) { | ||
continue; | ||
} | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrap variable declarations in switch cases within blocks
Variables declared within switch cases can be accessed by other cases if not properly scoped. To prevent unintended access and improve code safety, wrap the case block in braces {}
.
Apply this diff to scope the variable declaration:
case CanvasRenderMode.ScreenSpaceOverlay:
+ {
const { min, max } = renderer.bounds;
if (min.x > width || max.x < 0 || min.y > height || max.y < 0) {
continue;
}
break;
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const { min, max } = renderer.bounds; | |
if (min.x > width || max.x < 0 || min.y > height || max.y < 0) { | |
continue; | |
} | |
break; | |
{ | |
const { min, max } = renderer.bounds; | |
if (min.x > width || max.x < 0 || min.y > height || max.y < 0) { | |
continue; | |
} | |
break; | |
} |
🧰 Tools
🪛 Biome
[error] 211-211: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
🧹 Outside diff range and nitpick comments (27)
packages/core/src/ui/interface/IUIGroupable.ts (2)
8-8
: Add method documentation and explicit return type.The method signature could be improved with the following:
- Add JSDoc documentation explaining when this method is called and its purpose
- Explicitly specify the return type for better type safety
Here's the suggested improvement:
+ /** + * Called when the group this element belongs to is modified. + * @param flag - Indicates the type of modification made to the group + */ - _onGroupModify(flag: GroupModifyFlags): void; + _onGroupModify(flag: GroupModifyFlags): void;
4-9
: Consider providing usage examples and integration guidelines.As this interface is part of the UI infrastructure, consider:
- Adding examples in the documentation showing how to implement this interface
- Creating integration tests to verify group behavior
- Providing guidelines for when to use this interface vs. direct UI elements
This will help other developers understand when and how to use groupable UI elements effectively.
packages/core/src/ui/interface/IUIElement.ts (1)
4-11
: Add JSDoc documentation and readonly modifiers.The interface would benefit from:
- JSDoc documentation explaining its purpose and member usage
- Readonly modifiers for references that shouldn't be mutated directly
+/** + * Represents a UI element that can be rendered in a canvas hierarchy. + * Provides core functionality for UI element management and modification tracking. + */ export interface IUIElement { + /** The entity associated with this UI element */ - _entity: Entity; + readonly _entity: Entity; + /** Array of parent entities in the hierarchy */ - _parents: Entity[]; + readonly _parents: Entity[]; + /** The root canvas containing this UI element */ - _rootCanvas: UICanvas; + readonly _rootCanvas: UICanvas; + /** The index position of this element within its canvas */ _indexInCanvas: number; + /** + * Handles modifications to the associated entity + * @param flag The type of entity modification + */ _onEntityModify(flag: EntityModifyFlags): void; }packages/core/src/ui/index.ts (2)
5-11
: LGTM! UI components hierarchy is well-structured.The exports follow a logical progression from infrastructure (Canvas, Renderer, Transform) to widgets (Button, Image, Text).
Consider documenting the recommended usage patterns and component hierarchy in a README.md file to help developers understand how these components should be composed together.
1-15
: Consider improving file organization with grouping comments.While the exports are logically grouped, adding separator comments would improve readability.
Consider applying this organization:
+// Core export { UIGroup } from "./UIGroup"; + +// Enums export { CanvasRenderMode } from "./enums/CanvasRenderMode"; export { ResolutionAdaptationStrategy } from "./enums/ResolutionAdaptationStrategy"; +// Infrastructure export { UICanvas } from "./UICanvas"; export { UIRenderer } from "./UIRenderer"; export { UITransform } from "./UITransform"; + +// Widgets export { Button } from "./Button"; export { Image } from "./Image"; export { Text } from "./Text"; +// Transitions export { ColorTransition } from "./interactive/transition/ColorTransition"; export { ScaleTransition } from "./interactive/transition/ScaleTransition"; export { SpriteTransition } from "./interactive/transition/SpriteTransition"; export { Transition } from "./interactive/transition/Transition";packages/core/src/ui/UIUtils.ts (3)
21-37
: Add null check for element parameterThe method assumes
element
is not null but doesn't validate it. Consider adding a guard clause to handle invalid input.static registerEntityListener(element: IUIElement): void { + if (!element) { + return; + } const parents = element._parents;
84-126
: Improve type safety with type guardsReplace type casting with type guard functions to make the code more type-safe and maintainable.
+private static isUICanvas(component: any): component is UICanvas { + return component._componentType === ComponentType.UICanvas; +} + +private static isUIGroup(component: any): component is UIGroup { + return component._componentType === ComponentType.UIGroup; +} static getRootCanvasInParent(entity: Entity): UICanvas { while (entity) { const components = entity._components; for (let i = 0, n = components.length; i < n; i++) { const component = components[i]; - if ( - component.enabled && - component._componentType === ComponentType.UICanvas && - (<UICanvas>component)._isRootCanvas - ) { - return <UICanvas>component; + if (component.enabled && this.isUICanvas(component) && component._isRootCanvas) { + return component; } }
136-136
: Consider using Matrix methods instead of direct element mutationDirect mutation of matrix elements makes the code harder to maintain and understand. Consider using Matrix class methods.
-projectE[0] = 2 / canvas.width; -projectE[5] = 2 / canvas.height; -projectE[10] = 0; +virtualCamera.projectionMatrix.setOrtho( + 0, canvas.width, + 0, canvas.height, + -1, 1 +);packages/core/src/ui/UIGroup.ts (3)
9-41
: Add class-level documentationConsider adding comprehensive JSDoc documentation for the
UIGroup
class to describe its purpose, responsibilities, and usage patterns.Add documentation like this:
/** * Manages a group of UI elements with hierarchical properties like alpha and interactivity. * Handles propagation of modifications to child elements and groups. * * @example * ```typescript * const uiGroup = new UIGroup(entity); * uiGroup.alpha = 0.5; // Sets opacity for all child elements * ``` */ export class UIGroup extends Component {
123-144
: Consider decomposing _onDisableInScene for better maintainabilityThe method handles multiple concerns: entity listener cleanup, element cleanup, and group cleanup. Consider breaking it down into smaller, focused methods.
private _cleanupEntityListeners(): void { const entityListeners = this._entityListeners; entityListeners.forEach(entity => entity._unRegisterModifyListener(this._onEntityModify) ); entityListeners.length = 0; } private _cleanupElements(): void { const disorderedElements = this._disorderedElements; disorderedElements.forEach(element => UIUtils.registerElementToGroup(element, this._parentGroup) ); disorderedElements.length = 0; disorderedElements.garbageCollection(); } private _cleanupGroups(): void { const disorderedGroups = this._disorderedGroups; disorderedGroups.forEach(element => element._registryToParentGroup(this._parentGroup) ); disorderedGroups.length = 0; disorderedGroups.garbageCollection(); } override _onDisableInScene(): void { this._cleanupEntityListeners(); this._cleanupElements(); this._cleanupGroups(); this._parentGroup = null; this._entity._dispatchModify(EntityModifyFlags.UIGroupDisableInScene); }
186-191
: Add documentation for GroupModifyFlagsConsider adding JSDoc comments to document the purpose and usage of each flag.
/** * Flags indicating which aspects of a UI group need to be modified. */ export enum GroupModifyFlags { /** No modifications needed */ None = 0x0, /** Alpha (opacity) value needs to be updated */ Alpha = 0x1, /** Interactive state needs to be updated */ Interactive = 0x2, /** All properties need to be updated */ All = 0x3 }packages/core/src/ui/UIRenderer.ts (1)
22-28
: Consider implementing object pooling for temporary objects.Using static temporary objects could lead to memory allocation overhead during frequent UI updates. Consider implementing an object pool to manage these temporary objects more efficiently.
class UIRendererObjectPool { private static _vec3Pool: Vector3[] = []; private static _matrixPool: Matrix[] = []; static acquireVector3(): Vector3 { return this._vec3Pool.pop() || new Vector3(); } static releaseVector3(vec: Vector3): void { vec.set(0, 0, 0); this._vec3Pool.push(vec); } }packages/core/src/ui/UICanvas.ts (2)
20-24
: Consider type inference for numeric and boolean properties.TypeScript can automatically infer types from initial values. You can simplify the property declarations by removing explicit type annotations where the type is obvious from the initialization.
Apply this diff to simplify the type declarations:
- _isRootCanvas: boolean = false; + _isRootCanvas = false; - _canvasIndex: number = -1; + _canvasIndex = -1; - _sortDistance: number = 0; + _sortDistance = 0; - private _sortOrder: number = 0; + private _sortOrder = 0; - private _distance: number = 10; + private _distance = 10; - private _referenceResolution: Vector2 = new Vector2(800, 600); + private _referenceResolution = new Vector2(800, 600);Also applies to: 27-27, 45-45, 64-64, 66-66, 70-70
284-284
: Rename methods to use correct terminology.The methods use "adapter" instead of "adapt" in their names, which is grammatically incorrect.
Apply this diff to fix the method names:
- private _adapterPoseInScreenSpace(): void { + private _adaptPoseInScreenSpace(): void { - private _adapterSizeInScreenSpace(): void { + private _adaptSizeInScreenSpace(): void {Update all references to these methods accordingly.
Also applies to: 303-303
packages/core/src/2d/text/TextRenderer.ts (2)
Line range hint
87-611
: Consider batching dirty flag updates.The current implementation sets dirty flags individually for each property change. Consider implementing a batch update mechanism for scenarios where multiple properties are changed simultaneously.
+ private _batchUpdate(callback: () => void): void { + const prevDirtyFlag = this._dirtyUpdateFlag; + callback(); + if (this._dirtyUpdateFlag !== prevDirtyFlag) { + this._onDirtyFlagChanged(this._dirtyUpdateFlag); + } + } set text(value: string) { value = value || ""; if (this._text !== value) { + this._batchUpdate(() => { this._text = value; this._setDirtyFlagTrue(RendererUpdateFlags.AllPositionAndBounds); + }); } }
689-699
: Add JSDoc comments for individual flags.The
TextRendererUpdateFlags
enum would benefit from detailed documentation for each flag, explaining when they are set and their implications.enum TextRendererUpdateFlags { + /** Flag indicating that the SubFont needs to be updated */ SubFont = 0x10, + /** Flag indicating that the Color needs to be updated */ Color = 0x20, + /** + * Combined flag for font-related updates that affect position and bounds + * @remarks This includes SubFont and all position/bounds flags + */ FontAllPositionAndBounds = 0x1f, + /** + * Combined flag for all possible updates + * @remarks This includes all font, position, bounds, and color flags + */ All = 0x3f }packages/core/src/Entity.ts (1)
404-404
: Consider adding a type check for UITransform inheritance.The current implementation assumes
UITransform
is a direct class, but it might be inherited.- this._transform instanceof UITransform && child.addComponent(UITransform); + (this._transform instanceof UITransform || this._transform.constructor === UITransform) && child.addComponent(UITransform);packages/core/src/ui/interactive/transition/Transition.ts (3)
1-155
: Consider adding astop
method to halt the transition.It might be useful to have a way to immediately stop the transition and set the value to the final state. This can be helpful in scenarios where you want to cancel the transition midway and snap to the end state.
Here's a possible implementation:
stop() { this._countDown = 0; this._initialValue = this._finalValue = this._getValueByState(this._finalState); this._updateValue(); }
1-155
: Consider adding anonTransitionEnd
callback.It could be beneficial to provide a way for users to register a callback that gets invoked when the transition reaches its end state. This can be useful for triggering additional actions or state changes based on the completion of the transition.
Here's a possible implementation:
private _onTransitionEnd: () => void; set onTransitionEnd(callback: () => void) { this._onTransitionEnd = callback; } private _updateValue() { // ... if (this._countDown <= 0 && this._onTransitionEnd) { this._onTransitionEnd(); } }
1-155
: Consider adding support for easing functions.Currently, the transition uses linear interpolation between the initial and final values. Introducing support for easing functions can provide more control over the transition's behavior and allow for more visually appealing animations.
Here's a possible implementation:
type EasingFunction = (t: number) => number; private _easingFunction: EasingFunction = (t) => t; set easingFunction(fn: EasingFunction) { this._easingFunction = fn; } private _updateValue() { const t = this._duration ? 1 - this._countDown / this._duration : 1; const weight = this._easingFunction(t); // ... }You can then provide a set of built-in easing functions or allow users to define their own.
packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts (1)
229-231
: Consider defining the magic number as a constant for better maintainability.The magic number
1024
is used to prevent infinite loops by limiting the iteration count. Defining this number as a constant with a descriptive name would enhance code readability and make future adjustments easier.Apply this diff to define the magic number as a constant:
- for (; i < 1024 && !!entity && entity !== rootEntity; i++) { + const MAX_PATH_DEPTH = 1024; + for (; i < MAX_PATH_DEPTH && !!entity && entity !== rootEntity; i++) { entity = path[i] = entity.parent; }packages/core/src/ui/Image.ts (1)
208-209
: Address the TODO comment regarding material handlingThere's a TODO comment indicating that an issue needs to be raised rather than hidden when the material is destroyed. This should be resolved to ensure proper error handling and developer awareness.
Do you want assistance in implementing the error handling mechanism or opening a GitHub issue to track this task?
packages/core/src/ui/Text.ts (5)
225-225
: Avoid assignments within expressions.The static analysis tool has flagged several instances where assignments are used within expressions. While this is syntactically valid, it can lead to confusion and potential bugs.
Consider separating the assignments from the expressions to improve readability and maintainability. For example:
- firstRow < 0 && (firstRow = j); + if (firstRow < 0) { + firstRow = j; + }This applies to the assignments on lines 225, 466, 467, 477, 479, 565, and 567.
Also applies to: 466-466, 467-467, 477-477, 479-479, 565-565, 567-567
🧰 Tools
🪛 Biome
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
293-331
: Consider extracting the rendering logic into a separate method.The
_render
method is quite lengthy and contains multiple responsibilities, such as updating positions, colors, and managing render elements. To improve readability and maintainability, consider extracting some of the rendering logic into separate methods.For example, you could extract the code that creates and sets up the
SubRenderElement
instances into a separate method, like_createSubRenderElements
. This would make the_render
method more focused and easier to understand.
402-541
: Optimize the_updateLocalData
method.The
_updateLocalData
method is a critical part of the text rendering process, and its performance can impact the overall rendering performance. Consider the following optimizations:
- Preallocate and reuse temporary arrays and objects to reduce memory allocations.
- Use object pooling for frequently created objects like
CharRenderInfo
andTextChunk
.- Minimize the number of iterations and loops where possible.
- Use more efficient data structures, such as typed arrays, for performance-critical operations.
These optimizations can help improve the performance of the
_updateLocalData
method, especially when dealing with large amounts of text.🧰 Tools
🪛 Biome
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 467-467: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 477-477: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 479-479: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
561-590
: Use a profiler to analyze the performance of the_buildChunk
method.The
_buildChunk
method is responsible for building the vertex and index data for each text chunk. It's important to ensure that this method is performant, as it can be called frequently during text rendering.Consider using a profiler to analyze the performance of this method and identify any potential bottlenecks. Pay attention to the following:
- Memory allocations and deallocations.
- Iteration and loop performance.
- Data structure access and manipulation.
Based on the profiling results, optimize the method as needed to improve its performance. This may involve techniques such as object pooling, preallocating arrays, or using more efficient data structures.
🧰 Tools
🪛 Biome
[error] 565-565: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 567-567: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
611-615
: Consider using a more memory-efficient data structure forTextChunk
.The
TextChunk
class currently uses an array to storeCharRenderInfo
instances. Depending on the size of the text and the number of chunks, this can lead to significant memory usage.Consider using a more memory-efficient data structure, such as a linked list or a custom allocator, to store the
CharRenderInfo
instances. This can help reduce memory overhead, especially when dealing with large amounts of text.Additionally, explore the possibility of reusing
TextChunk
instances through object pooling to further optimize memory usage.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (17)
packages/core/src/2d/text/TextRenderer.ts
(20 hunks)packages/core/src/2d/text/TextUtils.ts
(6 hunks)packages/core/src/Entity.ts
(10 hunks)packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts
(1 hunks)packages/core/src/ui/Image.ts
(1 hunks)packages/core/src/ui/Text.ts
(1 hunks)packages/core/src/ui/UICanvas.ts
(1 hunks)packages/core/src/ui/UIGroup.ts
(1 hunks)packages/core/src/ui/UIRenderer.ts
(1 hunks)packages/core/src/ui/UIUtils.ts
(1 hunks)packages/core/src/ui/index.ts
(1 hunks)packages/core/src/ui/interactive/UIInteractive.ts
(1 hunks)packages/core/src/ui/interactive/transition/SpriteTransition.ts
(1 hunks)packages/core/src/ui/interactive/transition/Transition.ts
(1 hunks)packages/core/src/ui/interface/IUIElement.ts
(1 hunks)packages/core/src/ui/interface/IUIGraphics.ts
(1 hunks)packages/core/src/ui/interface/IUIGroupable.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/core/src/2d/text/TextUtils.ts
- packages/core/src/ui/interactive/transition/SpriteTransition.ts
- packages/core/src/ui/interface/IUIGraphics.ts
🧰 Additional context used
🪛 Biome
packages/core/src/Entity.ts
[error] 612-612: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/Text.ts
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 467-467: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 477-477: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 479-479: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 565-565: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 567-567: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UICanvas.ts
[error] 194-194: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 211-211: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 357-357: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 471-472: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 481-482: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 483-485: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIGroup.ts
[error] 105-105: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation
(lint/complexity/noExtraBooleanCast)
[error] 110-110: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation
(lint/complexity/noExtraBooleanCast)
[error] 153-153: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIRenderer.ts
[error] 158-158: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 162-162: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 163-163: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 161-165: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIUtils.ts
[error] 16-153: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
[error] 53-53: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 72-72: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 130-130: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 130-130: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 131-131: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 131-131: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 132-132: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 132-132: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
packages/core/src/ui/interactive/UIInteractive.ts
[error] 132-134: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
🔇 Additional comments (44)
packages/core/src/ui/interface/IUIGroupable.ts (1)
1-2
: LGTM! Imports are well-organized.
The necessary dependencies are correctly imported using appropriate relative paths.
packages/core/src/ui/interface/IUIElement.ts (2)
1-3
: LGTM! Imports are clean and well-organized.
All necessary types are properly imported, and there are no redundant imports.
10-10
: Consider adding canvas modification handler.
The interface has _onEntityModify
but lacks a corresponding method for handling canvas modifications. This asymmetry might indicate a missing feature.
Consider adding an _onCanvasModify
method if canvas-level changes need to be tracked:
_onCanvasModify(flag: CanvasModifyFlags): void;
packages/core/src/ui/index.ts (2)
1-4
: LGTM! Core exports are well-organized.
The core UI group and enum exports provide essential building blocks for the GUI infrastructure.
12-15
: LGTM! Transition system exports are comprehensive.
The transition system appears well-thought-out with support for different transition types (Color, Scale, Sprite).
packages/core/src/ui/UIUtils.ts (1)
137-137
: Add error handling for RHI operations
The RHI operation could fail in certain scenarios. Consider adding error handling.
packages/core/src/ui/UIGroup.ts (1)
164-177
: Verify entity listener cleanup
The entity listener registration in the while loop could potentially lead to memory leaks if the entity hierarchy changes frequently. Consider adding a verification step to ensure all listeners are properly cleaned up.
✅ Verification successful
Entity listener cleanup is properly implemented
The codebase shows a robust implementation of entity listener cleanup:
- In UIGroup, listeners are properly unregistered in a loop before clearing the array:
entityListeners[i]._unRegisterModifyListener(this._onEntityModify)
- When replacing listeners in the hierarchy, old listeners are unregistered before new ones are registered:
preListener?._unRegisterModifyListener(this._onEntityModify)
- The pattern is consistently used across related components (UIUtils, UICanvas) showing a systematic approach to listener management
The original concern about potential memory leaks is unfounded as the code maintains proper cleanup of entity listeners.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for potential memory leaks in entity listener management
echo "Searching for entity listener registration patterns..."
ast-grep --pattern 'entity._registerModifyListener($_)'
echo "Searching for corresponding cleanup patterns..."
ast-grep --pattern 'entity._unRegisterModifyListener($_)'
# Look for potential memory leak patterns in test files
echo "Searching for test cases covering listener cleanup..."
rg -l "test.*UIGroup.*cleanup" -t ts
Length of output: 695
Script:
#!/bin/bash
# Let's search for unregister patterns with a broader context
echo "Searching for unregister patterns..."
rg "_unRegisterModifyListener" -A 2 -B 2
# Check for cleanup in UIGroup class destructor or dispose methods
echo "Searching for cleanup methods in UIGroup..."
ast-grep --pattern 'class UIGroup {
$$$
dispose() {
$$$
}
$$$
}'
# Check if there are any tests for UIGroup
echo "Searching for UIGroup test files..."
fd "UIGroup.*test" -e ts
Length of output: 2984
packages/core/src/ui/UIRenderer.ts (1)
19-20
:
Implement ISpriteRenderer interface.
The class should implement the ISpriteRenderer interface to maintain consistency with the sprite rendering system.
-export abstract class UIRenderer extends Renderer implements IUIGraphics {
+export abstract class UIRenderer extends Renderer implements IUIGraphics, ISpriteRenderer {
Likely invalid or redundant comment.
packages/core/src/ui/UICanvas.ts (1)
470-492
: 🛠️ Refactor suggestion
Document intentional switch case fallthrough.
The switch cases in _setRealRenderMode
are falling through intentionally to execute multiple actions, but this pattern should be explicitly documented to prevent confusion.
Apply this diff to add fallthrough comments:
switch (curRealMode) {
case CanvasRenderMode.ScreenSpaceOverlay:
this._addCanvasListener();
+ // fallthrough intentional
case CanvasRenderMode.ScreenSpaceCamera:
this._adapterPoseInScreenSpace();
this._adapterSizeInScreenSpace();
+ // fallthrough intentional
case CanvasRenderMode.WorldSpace:
componentsManager.addUICanvas(curRealMode, this);
break;
Likely invalid or redundant comment.
🧰 Tools
🪛 Biome
[error] 471-472: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 481-482: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 483-485: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/2d/text/TextRenderer.ts (2)
35-35
: Fix array initialization.
The array initialization syntax is incorrect. Using Array<TextChunk>()
without the new
keyword won't create a new array instance.
475-478
: 🛠️ Refactor suggestion
Add error handling for text measurement edge cases.
The text measurement logic should handle edge cases such as extremely large text or invalid font states.
private _updateLocalData(): void {
const { min, max } = this._localBounds;
const charRenderInfos = TextRenderer._charRenderInfos;
const charFont = this._getSubFont();
+ try {
const textMetrics = this.enableWrapping
? TextUtils.measureTextWithWrap(this, this.width, this.height)
: TextUtils.measureTextWithoutWrap(this, this.height);
+ } catch (error) {
+ console.error('Text measurement failed:', error);
+ min.set(0, 0, 0);
+ max.set(0, 0, 0);
+ return;
+ }
packages/core/src/Entity.ts (8)
99-104
: LGTM! Property changes enhance state management.
The addition of _updateFlagManager
and conversion of transform
to a private property with getter improves encapsulation.
107-110
: LGTM! Transform getter maintains backward compatibility.
The getter provides controlled access to the private _transform
property while maintaining the public API.
527-530
: LGTM! Proper cleanup of update flag manager.
The cleanup ensures no memory leaks by removing all listeners and nullifying the reference.
603-606
: LGTM! Parent change handling is comprehensive.
The method properly updates transform and dispatches modification flags.
695-695
: LGTM! Parent change notification is properly placed.
The _setParentChange
call is correctly placed after all parent-related updates.
771-771
: LGTM! Sibling index modification notification.
The dispatch of the sibling index modification flag is properly placed after the index update.
783-783
: LGTM! World matrix update uses private transform.
The update correctly uses the private _transform
property.
790-797
: LGTM! Comprehensive entity modification flags.
The enum provides a clear set of flags for tracking different types of entity modifications.
packages/core/src/ui/interactive/transition/Transition.ts (4)
26-32
: ****
Use deep comparison in setters to detect changes in complex objects.
Also applies to: 35-41, 47-53, 59-65
85-95
: ****
Prevent division by zero when adjusting _countDown
in duration
setter.
128-132
: ****
Clamp weight
between 0 and 1 in _updateValue
to ensure valid interpolation.
1-155
: LGTM!
The Transition
class provides a solid foundation for managing UI transitions. The use of generics allows flexibility in handling different value types and UI renderers. The class encapsulates the transition logic effectively, with clear separation of concerns between the abstract methods and the internal implementation.
The class follows good practices such as:
- Using getters and setters for properties to encapsulate internal state changes.
- Providing a clear API for setting the transition state and updating values.
- Handling edge cases like negative duration values and adjusting the countdown accordingly.
- Utilizing abstract methods to allow customization of transition behavior by subclasses.
The code is well-structured, readable, and maintainable. The comments provide helpful insights into the purpose and usage of the class and its methods.
Overall, the Transition
class is a valuable addition to the UI framework and lays a solid groundwork for implementing various types of transitions in a consistent and extensible manner.
packages/core/src/ui/interactive/UIInteractive.ts (7)
1-11
: LGTM!
The imports are well-organized and follow a logical order. The imported entities are relevant to the functionality of the UIInteractive
class.
12-82
: LGTM!
The UIInteractive
class is well-structured and implements the necessary properties and methods for managing interactive UI elements. The use of TypeScript features such as access modifiers, getters/setters, and type generics enhances the code's readability and maintainability.
The class provides a clean API for managing transitions and updating the interactive state based on pointer events. The onUpdate
method ensures that transitions are updated efficiently.
84-102
: LGTM!
The pointer event handling methods (onPointerBeginDrag
, onPointerEndDrag
, onPointerEnter
, onPointerExit
) are implemented correctly. They update the internal state variables and trigger state updates appropriately.
107-123
: LGTM!
The _onEnableInScene
and _onDisableInScene
methods handle the registration and unregistration of the element with the UI utilities effectively. They ensure proper integration with the scene and update the interactive state accordingly.
147-155
: LGTM!
The _onGroupModify
method correctly handles the GroupModifyFlags.Interactive
flag. It updates the runtime interactive state and triggers a state update when necessary.
157-174
: LGTM!
The private methods _updateState
and _getInteractiveState
are implemented correctly. They manage the current state of interactivity based on the runtime interactive flag and pointer interactions.
131-134
:
The previous review comment regarding the missing break
statement is still valid. Please address this issue to prevent unintended fallthrough.
Apply this diff to add the missing break
statement:
131 case EntityModifyFlags.UICanvasEnableInScene:
132 case EntityModifyFlags.Parent:
133 UIUtils.registerElementToCanvas(this, UIUtils.getRootCanvasInParent(this._entity));
134 UIUtils.registerEntityListener(this);
+135 break;
136 case EntityModifyFlags.UIGroupEnableInScene:
🧰 Tools
🪛 Biome
[error] 132-134: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts (6)
85-85
: ****
🧰 Tools
🪛 Biome
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
88-89
: ****
102-102
: ****
🧰 Tools
🪛 Biome
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
167-190
: ****
222-222
: ****
🧰 Tools
🪛 Biome
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
1-236
: The PointerUIEventEmitter
class provides a structured approach to managing pointer interactions with UI components.
The class extends the PointerEventEmitter
and overrides key methods to handle pointer events specifically for UI components. It maintains references to the currently entered, pressed, and dragged UI elements, enabling accurate event processing based on pointer interactions.
The implementation follows a clear and logical flow, with well-defined responsibilities for each method. The code is modular, making it easier to understand and maintain. The use of private helper methods for common tasks like updating the raycast, bubbling events, and composing paths further enhances the code organization.
Overall, the PointerUIEventEmitter
class is a solid addition to the pointer event handling system, providing a dedicated mechanism for managing UI component interactions.
🧰 Tools
🪛 Biome
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/Image.ts (7)
42-43
: Ensure alphaHitTestMinimumThreshold
is clamped between 0 and 1
The setter for alphaHitTestMinimumThreshold
correctly clamps the value. Just make sure that any value assignments are within the expected range.
52-71
: Update assembler initialization in drawMode
setter
The logic for updating the _assembler
based on drawMode
is appropriate. This ensures the correct assembler is used for rendering.
81-87
: Correct handling of tileMode
setter
The tileMode
setter correctly updates the dirty flags when the mode changes. This maintains the integrity of the rendering process.
113-130
: Proper management of sprite resource in sprite
setter
The setter effectively manages the reference counting and listener management for sprite resources. This helps prevent memory leaks and ensures resources are properly cleaned up.
285-298
: Resource cleanup in _onDestroy
The _onDestroy
method properly handles resource cleanup by decreasing reference counts and removing listeners. This prevents potential memory leaks.
300-341
: Comprehensive handling in _onSpriteChange
method
The _onSpriteChange
method effectively updates the dirty flags based on the type of sprite modification. This ensures the renderer reacts appropriately to sprite changes.
343-356
: Verify the UV calculation in _getUVByLocalPosition
While the method delegates UV calculation to the assembler, ensure that for SpriteDrawMode.Tiled
, the correct assembler is used. Currently, it calls SlicedSpriteAssembler.getUVByLocalPosition
for both Sliced
and Tiled
modes. Is this intentional?
Please confirm that using SlicedSpriteAssembler
for Tiled
mode is correct.
If not, consider the following change:
case SpriteDrawMode.Tiled:
- return SlicedSpriteAssembler.getUVByLocalPosition(this, size.x, size.y, pivot, position, out);
+ return TiledSpriteAssembler.getUVByLocalPosition(this, size.x, size.y, pivot, position, out);
packages/core/src/ui/Text.ts (1)
1-627
: Comprehensive and well-structured implementation of the Text class.
The Text
class provides a robust and feature-rich implementation for rendering text in a 2D UI context. It effectively manages text properties, rendering states, and efficiently handles text layout and rendering.
Key highlights:
- Utilizes dirty flags to optimize rendering and minimize unnecessary updates.
- Implements proper resource management, including reference counting for fonts and memory pooling for character render information.
- Supports various text formatting options such as alignment, wrapping, overflow modes, and font styles.
- Efficiently manages text chunks and batches rendering for optimal performance.
- Integrates with the
ISpriteRenderer
interface and other UI components.
The code follows good practices such as:
- Appropriate use of access modifiers for encapsulation.
- Meaningful naming conventions for variables, methods, and enums.
- Proper error handling and input validation.
- Efficient memory management through object pooling.
- Modular and reusable design with clear separation of concerns.
Overall, the Text
class is a solid foundation for text rendering in the UI system.
🧰 Tools
🪛 Biome
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 467-467: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 477-477: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 479-479: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 565-565: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 567-567: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
export interface IUIGroupable extends IUIElement { | ||
_group: UIGroup; | ||
_indexInGroup: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add JSDoc documentation and improve type safety.
The interface and its properties should be documented for better maintainability. Also, consider the following improvements:
- Add JSDoc comments explaining the purpose of the interface and its properties
- Consider using
protected
orprivate
modifiers since properties are prefixed with underscore - Add type constraints for
_indexInGroup
to ensure non-negative values
Here's the suggested improvement:
+/**
+ * Interface for UI elements that can be grouped together.
+ * Provides necessary properties and methods for group management.
+ */
export interface IUIGroupable extends IUIElement {
+ /** The group this element belongs to */
_group: UIGroup;
+ /** Zero-based index of this element within its group */
- _indexInGroup: number;
+ _indexInGroup: NonNegativeInteger;
}
+ type NonNegativeInteger = number & { __brand: 'NonNegativeInteger' };
Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/ui/UIRenderer.ts
Outdated
_raycast(ray: Ray, out: HitResult, distance: number = Number.MAX_SAFE_INTEGER): boolean { | ||
const plane = UIRenderer._tempPlane; | ||
const transform = this._transform; | ||
const normal = plane.normal.copyFrom(transform.worldForward); | ||
plane.distance = -Vector3.dot(normal, transform.worldPosition); | ||
const curDistance = ray.intersectPlane(plane); | ||
if (curDistance >= 0 && curDistance < distance) { | ||
const hitPointWorld = ray.getPoint(curDistance, UIRenderer._tempVec30); | ||
const worldMatrixInv = UIRenderer._tempMat; | ||
Matrix.invert(transform.worldMatrix, worldMatrixInv); | ||
const localPosition = UIRenderer._tempVec31; | ||
Vector3.transformCoordinate(hitPointWorld, worldMatrixInv, localPosition); | ||
if (this._hitTest(localPosition)) { | ||
out.distance = curDistance; | ||
out.entity = this._entity; | ||
out.component = this; | ||
out.normal.copyFrom(normal); | ||
out.point.copyFrom(hitPointWorld); | ||
return true; | ||
} | ||
} | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Optimize raycast performance and readability.
The raycast implementation can be optimized for better performance and readability:
- Cache the plane intersection result
- Use early returns for better flow control
- Destructure transform properties
_raycast(ray: Ray, out: HitResult, distance: number = Number.MAX_SAFE_INTEGER): boolean {
const plane = UIRenderer._tempPlane;
- const transform = this._transform;
+ const { worldForward, worldMatrix, worldPosition } = this._transform;
- const normal = plane.normal.copyFrom(transform.worldForward);
+ const normal = plane.normal.copyFrom(worldForward);
- plane.distance = -Vector3.dot(normal, transform.worldPosition);
+ plane.distance = -Vector3.dot(normal, worldPosition);
const intersectionDistance = ray.intersectPlane(plane);
- if (curDistance >= 0 && curDistance < distance) {
- const hitPointWorld = ray.getPoint(curDistance, UIRenderer._tempVec30);
- const worldMatrixInv = UIRenderer._tempMat;
- Matrix.invert(transform.worldMatrix, worldMatrixInv);
+ if (intersectionDistance < 0 || intersectionDistance >= distance) {
+ return false;
+ }
+ const hitPointWorld = ray.getPoint(intersectionDistance, UIRenderer._tempVec30);
+ const worldMatrixInv = UIRenderer._tempMat;
+ Matrix.invert(worldMatrix, worldMatrixInv);
Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/ui/UIRenderer.ts
Outdated
switch (flag) { | ||
case EntityModifyFlags.SiblingIndex: | ||
this._rootCanvas && (this._rootCanvas._hierarchyDirty = true); | ||
break; | ||
case EntityModifyFlags.UICanvasEnableInScene: | ||
case EntityModifyFlags.Parent: | ||
const rootCanvas = UIUtils.getRootCanvasInParent(this._entity); | ||
rootCanvas && (rootCanvas._hierarchyDirty = true); | ||
UIUtils.registerElementToCanvas(this, rootCanvas); | ||
UIUtils.registerEntityListener(this); | ||
case EntityModifyFlags.UIGroupEnableInScene: | ||
UIUtils.registerElementToGroup(this, UIUtils.getGroupInParents(this._entity)); | ||
break; | ||
default: | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix switch statement safety issues.
The switch statement has several issues that need to be addressed:
- Case fallthrough between
Parent
andUIGroupEnableInScene
- Variable declaration scope
- Assignments within expressions
switch (flag) {
case EntityModifyFlags.SiblingIndex: {
- this._rootCanvas && (this._rootCanvas._hierarchyDirty = true);
+ if (this._rootCanvas) {
+ this._rootCanvas._hierarchyDirty = true;
+ }
break;
}
case EntityModifyFlags.UICanvasEnableInScene:
- case EntityModifyFlags.Parent:
+ case EntityModifyFlags.Parent: {
const rootCanvas = UIUtils.getRootCanvasInParent(this._entity);
- rootCanvas && (rootCanvas._hierarchyDirty = true);
+ if (rootCanvas) {
+ rootCanvas._hierarchyDirty = true;
+ }
UIUtils.registerElementToCanvas(this, rootCanvas);
UIUtils.registerEntityListener(this);
+ break;
+ }
case EntityModifyFlags.UIGroupEnableInScene:
UIUtils.registerElementToGroup(this, UIUtils.getGroupInParents(this._entity));
break;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
switch (flag) { | |
case EntityModifyFlags.SiblingIndex: | |
this._rootCanvas && (this._rootCanvas._hierarchyDirty = true); | |
break; | |
case EntityModifyFlags.UICanvasEnableInScene: | |
case EntityModifyFlags.Parent: | |
const rootCanvas = UIUtils.getRootCanvasInParent(this._entity); | |
rootCanvas && (rootCanvas._hierarchyDirty = true); | |
UIUtils.registerElementToCanvas(this, rootCanvas); | |
UIUtils.registerEntityListener(this); | |
case EntityModifyFlags.UIGroupEnableInScene: | |
UIUtils.registerElementToGroup(this, UIUtils.getGroupInParents(this._entity)); | |
break; | |
default: | |
break; | |
} | |
switch (flag) { | |
case EntityModifyFlags.SiblingIndex: { | |
if (this._rootCanvas) { | |
this._rootCanvas._hierarchyDirty = true; | |
} | |
break; | |
} | |
case EntityModifyFlags.UICanvasEnableInScene: | |
case EntityModifyFlags.Parent: { | |
const rootCanvas = UIUtils.getRootCanvasInParent(this._entity); | |
if (rootCanvas) { | |
rootCanvas._hierarchyDirty = true; | |
} | |
UIUtils.registerElementToCanvas(this, rootCanvas); | |
UIUtils.registerEntityListener(this); | |
break; | |
} | |
case EntityModifyFlags.UIGroupEnableInScene: | |
UIUtils.registerElementToGroup(this, UIUtils.getGroupInParents(this._entity)); | |
break; | |
default: | |
break; | |
} |
🧰 Tools
🪛 Biome
[error] 158-158: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 162-162: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 163-163: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 161-165: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIRenderer.ts
Outdated
//@todo: Always update world positions to buffer, should opt | ||
// super._updateTransformShaderData(context, onlyMVP, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Commented super call should be uncommented to match other renderer implementations
The codebase analysis shows that:
- The base
Renderer
class implements_updateTransformShaderData
- Other renderer classes (SpriteRenderer, TextRenderer, ParticleRenderer, etc.) consistently call
super._updateTransformShaderData
with the same TODO comment - UIRenderer is the only implementation that has commented out the super call while still having the same TODO
The commented super call should be uncommented to maintain consistency with other renderer implementations and ensure proper transform updates. The optimization mentioned in the TODO can be addressed separately across all renderers.
🔗 Analysis chain
Address TODO comment and clarify super call.
There's a TODO comment about optimizing world position updates and a commented-out super call that needs to be addressed:
- Implement the optimization for world position updates
- Clarify whether the super call is necessary
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if the super class implementation is different
ast-grep --pattern $'class Renderer {
$$$
_updateTransformShaderData(context: $_) {
$$$
}
$$$
}'
Length of output: 109
Script:
#!/bin/bash
# Let's try a broader search for the base class and its method
ast-grep --pattern '_updateTransformShaderData'
# Also search for any related files
fd -e ts -e js "Renderer"
# And check the specific file content
rg -A 5 -B 5 "_updateTransformShaderData"
Length of output: 9204
packages/core/src/ui/UICanvas.ts
Outdated
// @ts-ignore | ||
this._referenceResolution._onValueChanged = this._onReferenceResolutionChanged; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove @ts-ignore by extending Vector2 type.
Using @ts-ignore suppresses type checking and can hide potential issues. Create a proper type definition instead.
Create an interface extending Vector2:
interface Vector2WithCallback extends Vector2 {
_onValueChanged?: () => void;
}
Then update the property type:
- private _referenceResolution: Vector2 = new Vector2(800, 600);
+ private _referenceResolution: Vector2WithCallback = new Vector2(800, 600);
packages/core/src/ui/UICanvas.ts
Outdated
set referenceResolution(val: Vector2) { | ||
const { _referenceResolution: referenceResolution } = this; | ||
if (referenceResolution === val) return; | ||
(referenceResolution.x !== val.x || referenceResolution.y !== val.y) && referenceResolution.copyFrom(val); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve readability by avoiding assignments in expressions.
Using assignments within expressions makes the code harder to read and understand.
Apply this diff to improve readability:
- (referenceResolution.x !== val.x || referenceResolution.y !== val.y) && referenceResolution.copyFrom(val);
+ if (referenceResolution.x !== val.x || referenceResolution.y !== val.y) {
+ referenceResolution.copyFrom(val);
+ }
- child.isActive && (depth = this._walk(child, elements, depth));
+ if (child.isActive) {
+ depth = this._walk(child, elements, depth);
+ }
Also applies to: 357-357
this.setMaterial(engine._basicResources.textDefaultMaterial); | ||
//@ts-ignore | ||
this._color._onValueChanged = this._onColorChanged.bind(this); | ||
this._color._onValueChanged = this._onColorChange.bind(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid type assertion in color change callback binding.
The type assertion (@ts-ignore
) suggests a potential type safety issue. Consider properly typing the color object or implementing a proper event system.
- //@ts-ignore
- this._color._onValueChanged = this._onColorChange.bind(this);
+ // Option 1: Define proper event interface
+ interface ColorWithCallback extends Color {
+ _onValueChanged?: () => void;
+ }
+ (this._color as ColorWithCallback)._onValueChanged = this._onColorChange.bind(this);
+
+ // Option 2: Use event emitter pattern
+ this._color.on('change', this._onColorChange.bind(this));
Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/Entity.ts
Outdated
_registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | ||
(this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange); | ||
} | ||
|
||
/** | ||
* @internal | ||
*/ | ||
_unRegisterModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | ||
this._updateFlagManager?.removeListener(onChange); | ||
} | ||
|
||
/** | ||
* @internal | ||
*/ | ||
_dispatchModify(flag: EntityModifyFlags): void { | ||
this._updateFlagManager?.dispatch(flag); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix assignment in expression for better readability.
The modify listener registration contains an assignment within an expression which could be confusing.
- (this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange);
+ if (!this._updateFlagManager) {
+ this._updateFlagManager = new UpdateFlagManager();
+ }
+ this._updateFlagManager.addListener(onChange);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
_registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | |
(this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange); | |
} | |
/** | |
* @internal | |
*/ | |
_unRegisterModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | |
this._updateFlagManager?.removeListener(onChange); | |
} | |
/** | |
* @internal | |
*/ | |
_dispatchModify(flag: EntityModifyFlags): void { | |
this._updateFlagManager?.dispatch(flag); | |
_registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | |
if (!this._updateFlagManager) { | |
this._updateFlagManager = new UpdateFlagManager(); | |
} | |
this._updateFlagManager.addListener(onChange); | |
} | |
/** | |
* @internal | |
*/ | |
_unRegisterModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | |
this._updateFlagManager?.removeListener(onChange); | |
} | |
/** | |
* @internal | |
*/ | |
_dispatchModify(flag: EntityModifyFlags): void { | |
this._updateFlagManager?.dispatch(flag); |
🧰 Tools
🪛 Biome
[error] 612-612: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
override _processRaycast(scenes: readonly Scene[], pointer: Pointer): void { | ||
const { _tempRay: ray } = PointerEventEmitter; | ||
const hitResult = this._hitResult; | ||
const { position } = pointer; | ||
const { x, y } = position; | ||
for (let i = scenes.length - 1; i >= 0; i--) { | ||
const scene = scenes[i]; | ||
if (!scene.isActive || scene.destroyed) continue; | ||
const { _componentsManager: componentsManager } = scene; | ||
|
||
/** Overlay Canvas */ | ||
let canvasElements = componentsManager._overlayCanvases; | ||
ray.origin.set(position.x, position.y, 1); | ||
ray.direction.set(0, 0, -1); | ||
for (let j = canvasElements.length - 1; j >= 0; j--) { | ||
if (canvasElements.get(j).raycast(ray, hitResult)) { | ||
this._updateRaycast(<Component>hitResult.component, pointer); | ||
return; | ||
} | ||
} | ||
|
||
const cameras = componentsManager._activeCameras; | ||
for (let j = cameras.length - 1; j >= 0; j--) { | ||
const camera = cameras.get(j); | ||
if (camera.renderTarget) continue; | ||
const { pixelViewport } = camera; | ||
if ( | ||
x < pixelViewport.x || | ||
y < pixelViewport.y || | ||
x > pixelViewport.x + pixelViewport.width || | ||
y > pixelViewport.y + pixelViewport.height | ||
) { | ||
continue; | ||
} | ||
camera.screenPointToRay(pointer.position, ray); | ||
|
||
/** Other canvases */ | ||
const cameraPosition = camera.entity.transform.position; | ||
/** Sort by rendering order */ | ||
canvasElements = componentsManager._canvases; | ||
for (let k = 0, n = canvasElements.length; k < n; k++) { | ||
canvasElements.get(k)._updateSortDistance(cameraPosition); | ||
} | ||
canvasElements.sort((a, b) => a.sortOrder - b.sortOrder || a._sortDistance - b._sortDistance); | ||
for (let k = 0, n = canvasElements.length; k < n; k++) { | ||
canvasElements.get(k)._canvasIndex = k; | ||
} | ||
const farClipPlane = camera.farClipPlane; | ||
/** Post-rendering first detection */ | ||
for (let k = 0, n = canvasElements.length; k < n; k++) { | ||
const canvas = canvasElements.get(k); | ||
if (canvas.renderCamera !== camera) continue; | ||
if (canvas.raycast(ray, hitResult, farClipPlane)) { | ||
this._updateRaycast(<Component>hitResult.component, pointer); | ||
return; | ||
} | ||
} | ||
if (camera.clearFlags & CameraClearFlags.Color) { | ||
this._updateRaycast(null); | ||
return; | ||
} | ||
} | ||
} | ||
return null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider refactoring the _processRaycast
method to improve readability and maintainability.
The _processRaycast
method is quite complex with multiple nested loops and conditions. Breaking it down into smaller, focused methods would enhance the code structure and make it easier to understand and maintain.
Suggestions:
- Extract the logic for processing overlay canvases into a separate method.
- Move the code for handling camera-specific processing to a dedicated method.
- Consider creating helper methods for common tasks like sorting canvases and updating the raycast.
By decomposing the method into smaller, single-responsibility functions, the overall readability and maintainability of the code will improve.
🧰 Tools
🪛 Biome
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
packages/core/src/ui/Image.ts
Outdated
protected override _hitTest(localPosition: Vector3): boolean { | ||
let { x, y } = localPosition; | ||
const uiTransform = <UITransform>this._transform; | ||
const { x: width, y: height } = uiTransform.size; | ||
const { x: pivotX, y: pivotY } = uiTransform.pivot; | ||
const { x: paddingLeft, y: paddingBottom, z: paddingRight, w: paddingTop } = this.raycastPadding; | ||
if ( | ||
x < -width * pivotX + paddingLeft || | ||
x > width * (1 - pivotX) - paddingRight || | ||
y < -height * pivotY + paddingTop || | ||
y > height * (1 - pivotY) - paddingBottom | ||
) { | ||
return false; | ||
} | ||
const alphaHitTestMinimumThreshold = this._alphaHitTestMinimumThreshold; | ||
if (alphaHitTestMinimumThreshold <= 0) { | ||
return true; | ||
} | ||
const texture = this.sprite?.texture; | ||
if (!texture) { | ||
return false; | ||
} | ||
const uv = Image._tempVec2; | ||
if (!this._getUVByLocalPosition(localPosition, uv)) { | ||
return false; | ||
} | ||
const pixel = Image._tempUnit8Array; | ||
texture.getPixelBuffer(Math.floor(uv.x * texture.width), Math.floor(uv.y * texture.height), 1, 1, 0, pixel); | ||
if (pixel[3] >= alphaHitTestMinimumThreshold * 255) { | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Potential performance impact in _hitTest
method
Using texture.getPixelBuffer
inside the _hitTest
method may introduce performance overhead, especially if the method is called frequently. Consider caching the pixel data or implementing an alternative approach to minimize performance impact.
To optimize, you might consider:
+// Cache the pixel data if possible to reduce repeated texture reads
Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 19
🧹 Outside diff range and nitpick comments (24)
packages/core/src/ui/interface/IUIGroupable.ts (1)
8-8
: Consider implementing a formal event system instead of internal callbacks.The
_onGroupModify
method appears to be an internal callback for group modifications. This approach might be improved by:
- Using a formal event system
- Providing a clearer public API
- Better encapsulating implementation details
Consider this alternative approach:
- _onGroupModify(flag: GroupModifyFlags): void; + onGroupChanged(event: GroupChangeEvent): void;Where
GroupChangeEvent
could be a well-defined event object:interface GroupChangeEvent { type: GroupChangeType; source: UIGroup; // other relevant event data }This would:
- Provide a clearer contract for group-related changes
- Follow standard event handling patterns
- Be more maintainable and extensible
packages/core/src/ui/interface/IUIElement.ts (2)
4-11
: Add JSDoc documentation to improve code maintainability.The interface and its members lack documentation. Consider adding JSDoc comments to describe:
- The purpose and responsibility of the
IUIElement
interface- The role of each property and method
- Parameter and return type descriptions for
_onEntityModify
Example documentation:
/** * Represents a UI element within the rendering engine. * Defines the contract for managing UI element lifecycle and hierarchy. */ export interface IUIElement { /** The entity associated with this UI element */ _entity: Entity; /** Array of parent entities in the hierarchy */ _parents: Entity[]; /** Reference to the root canvas containing this element */ _rootCanvas: UICanvas; /** Index position of this element within the canvas */ _indexInCanvas: number; /** * Handles modifications to the associated entity * @param flag - Flags indicating the type of entity modification */ _onEntityModify(flag: EntityModifyFlags): void; }
4-11
: Consider adding essential lifecycle methods.The interface could benefit from additional lifecycle methods to handle common UI element operations:
Consider adding these methods:
export interface IUIElement { // ... existing members ... /** Called when the element is mounted to the DOM */ _onMount(): void; /** Called when the element is unmounted from the DOM */ _onUnmount(): void; /** * Called when the element's visibility changes * @param visible - New visibility state */ _onVisibilityChange(visible: boolean): void; /** * Called when the element needs to update its layout * @param force - Whether to force a full layout update */ _onLayout(force?: boolean): void; }packages/core/src/ui/index.ts (1)
5-11
: Consider grouping related UI components.The exports are logically organized, but could benefit from clearer visual separation between infrastructure (UICanvas, UIRenderer, UITransform) and UI elements (Button, Image, Text).
Consider adding comments to separate these groups:
export { UICanvas } from "./UICanvas"; export { UIRenderer } from "./UIRenderer"; export { UITransform } from "./UITransform"; + +// UI Elements export { Button } from "./Button"; export { Image } from "./Image"; export { Text } from "./Text";packages/core/src/ui/interactive/UIInteractive.ts (2)
41-50
: Consider extracting runtime interactive state logic.The setter contains complex state management logic that could be moved to a separate method for better maintainability. Also, the optional chaining could lead to unexpected behavior if
_group
is undefined.Consider this refactor:
set interactive(value: boolean) { if (this._interactive !== value) { this._interactive = value; - const runtimeInteractive = value && this._group?._getGlobalInteractive(); - if (this._runtimeInteractive !== runtimeInteractive) { - this._runtimeInteractive = runtimeInteractive; - this._updateState(true); - } + this._updateRuntimeInteractive(value); } } + +private _updateRuntimeInteractive(value: boolean): void { + const globalInteractive = this._group?._getGlobalInteractive() ?? true; + const runtimeInteractive = value && globalInteractive; + if (this._runtimeInteractive !== runtimeInteractive) { + this._runtimeInteractive = runtimeInteractive; + this._updateState(true); + } +}
147-155
: Improve group modification logic.The group modification logic has similar issues to the interactive setter with optional chaining and complex state management.
Consider this refactor:
_onGroupModify(flag: GroupModifyFlags): void { if (flag & GroupModifyFlags.Interactive) { - const runtimeInteractive = this._interactive && (this._group?._getGlobalInteractive() || true); - if (this._runtimeInteractive !== runtimeInteractive) { - this._runtimeInteractive = runtimeInteractive; - this._updateState(true); - } + this._updateRuntimeInteractive(this._interactive); } }packages/core/src/ui/UIUtils.ts (2)
1-16
: Consider using a singleton pattern instead of static members.While static analysis suggests converting to standalone functions, the shared state (
_renderQueue
,_virtualCamera
,_viewport
) suggests this class would benefit from a singleton pattern. This would provide better encapsulation and testability while maintaining the same usage pattern.Example implementation:
export class UIUtils { private static instance: UIUtils; private _renderQueue: RenderQueue; private _virtualCamera: VirtualCamera; private _viewport: Vector4; private constructor() { // Initialize members } static getInstance(): UIUtils { if (!UIUtils.instance) { UIUtils.instance = new UIUtils(); } return UIUtils.instance; } // Convert static methods to instance methods }
128-152
: Improve readability of matrix operations.The direct matrix element assignments are hard to read and maintain. Consider using matrix utility methods or adding explanatory comments:
- (projectE[0] = 2 / canvas.width), (projectE[5] = 2 / canvas.height), (projectE[10] = 0); + // Set orthographic projection matrix elements + projectE[0] = 2 / canvas.width; // Scale X + projectE[5] = 2 / canvas.height; // Scale Y + projectE[10] = 0; // Flatten Z - (viewE[12] = -transform.position.x), (viewE[13] = -transform.position.y); + // Set view translation + viewE[12] = -transform.position.x; // Translate X + viewE[13] = -transform.position.y; // Translate YConsider creating helper methods for these matrix operations:
private static setOrthographicProjection(matrix: Matrix, width: number, height: number): void { const elements = matrix.elements; elements[0] = 2 / width; elements[5] = 2 / height; elements[10] = 0; } private static setViewTranslation(matrix: Matrix, position: Vector3): void { const elements = matrix.elements; elements[12] = -position.x; elements[13] = -position.y; }🧰 Tools
🪛 Biome
[error] 130-130: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 130-130: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
[error] 131-131: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 131-131: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
[error] 132-132: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 132-132: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
packages/core/src/ui/UIRenderer.ts (2)
22-28
: Consider implementing object pooling for temporary objects.The static temporary objects could be managed through an object pool to reduce garbage collection pressure during frequent UI updates.
Consider implementing an object pool:
class UIRendererObjectPool { private static _vec3Pool: Vector3[] = []; private static _matrixPool: Matrix[] = []; static acquireVector3(): Vector3 { return this._vec3Pool.pop() || new Vector3(); } static releaseVector3(vec: Vector3): void { vec.set(0, 0, 0); this._vec3Pool.push(vec); } }
200-222
: Optimize raycast implementation.The raycast method could be optimized by:
- Caching the plane intersection result
- Using early returns for better readability
- Using more descriptive variable names
Consider this optimization:
_raycast(ray: Ray, out: HitResult, distance: number = Number.MAX_SAFE_INTEGER): boolean { const plane = UIRenderer._tempPlane; const { transform } = this._entity; const normal = plane.normal.copyFrom(transform.worldForward); plane.distance = -Vector3.dot(normal, transform.worldPosition); - const curDistance = ray.intersectPlane(plane); + const intersectionDistance = ray.intersectPlane(plane); - if (curDistance >= 0 && curDistance < distance) { + if (intersectionDistance < 0 || intersectionDistance >= distance) { + return false; + } const hitPointWorld = ray.getPoint(intersectionDistance, UIRenderer._tempVec30); const worldMatrixInv = UIRenderer._tempMat; Matrix.invert(transform.worldMatrix, worldMatrixInv); const localPosition = UIRenderer._tempVec31; Vector3.transformCoordinate(hitPointWorld, worldMatrixInv, localPosition); - if (this._hitTest(localPosition)) { - out.distance = curDistance; - out.entity = this._entity; - out.component = this; - out.normal.copyFrom(normal); - out.point.copyFrom(hitPointWorld); - return true; - } - return false; + if (!this._hitTest(localPosition)) { + return false; + } + + out.distance = intersectionDistance; + out.entity = this._entity; + out.component = this; + out.normal.copyFrom(normal); + out.point.copyFrom(hitPointWorld); + return true;packages/core/src/2d/text/TextUtils.ts (2)
99-104
: Add JSDoc documentation for the updated method signature.Please add documentation for the new parameters to improve maintainability and developer experience.
+/** + * Measures text with word wrapping enabled. + * @param renderer - The text renderer or Text component + * @param rendererWidth - The maximum width for text wrapping + * @param rendererHeight - The height of the renderer + * @param lineSpacing - The spacing between lines + * @returns TextMetrics containing the measured text dimensions and line information + */ static measureTextWithWrap(
276-280
: Add JSDoc documentation for the updated method signature.Please add documentation for the new parameters to improve maintainability.
+/** + * Measures text without word wrapping. + * @param renderer - The text renderer or Text component + * @param rendererHeight - The height of the renderer + * @param lineSpacing - The spacing between lines + * @returns TextMetrics containing the measured text dimensions and line information + */ static measureTextWithoutWrap(packages/core/src/ui/UICanvas.ts (1)
349-351
: Consider using type predicates for safer type casting.The current type casting could be made safer by using TypeScript type predicates:
function isUIGraphics(component: Component): component is IUIGraphics { return component._componentType === ComponentType.UIRenderer; }Then update the code:
- if (component.enabled && component._componentType === ComponentType.UIRenderer) { - (component as unknown as IUIGraphics).depth = depth; - elements[depth] = component as unknown as IUIGraphics; + if (component.enabled && isUIGraphics(component)) { + component.depth = depth; + elements[depth] = component;packages/core/src/2d/text/TextRenderer.ts (3)
Line range hint
66-74
: Optimize color setter to avoid unnecessary updates.The color setter should check individual components to avoid triggering unnecessary updates when the same color values are set.
set color(value: Color) { - if (this._color !== value) { + if (this._color.r !== value.r || + this._color.g !== value.g || + this._color.b !== value.b || + this._color.a !== value.a) { this._color.copyFrom(value); } }
Line range hint
473-616
: Refactor_updateLocalData
for better maintainability.This method is quite complex and handles multiple responsibilities. Consider breaking it down into smaller, focused methods:
- Text measurement
- Layout calculation
- Chunk management
Suggested structure:
private _updateLocalData(): void { if (!this._calculateTextMetrics()) { return; } this._calculateLayout(); this._updateChunks(); this._setDirtyFlagFalse(RendererUpdateFlags.LocalPositionAndBounds); } private _calculateTextMetrics(): boolean { // Text measurement logic } private _calculateLayout(): void { // Layout calculation logic } private _updateChunks(): void { // Chunk management logic }
694-704
: Consider explicit enum inheritance for clarity.While the enum extends
RendererUpdateFlags
as per the comment, consider making this relationship more explicit in the code.enum TextRendererUpdateFlags { // Base flags from RendererUpdateFlags LocalPosition = RendererUpdateFlags.LocalPosition, WorldPosition = RendererUpdateFlags.WorldPosition, LocalBounds = RendererUpdateFlags.LocalBounds, WorldBounds = RendererUpdateFlags.WorldBounds, // Text-specific flags SubFont = 0x10, Color = 0x20, FontAllPositionAndBounds = SubFont | LocalPosition | WorldPosition | LocalBounds | WorldBounds, All = FontAllPositionAndBounds | Color }packages/core/src/Entity.ts (3)
107-110
: Consider adding transform setter validation.While the getter is correctly implemented, consider adding a setter with validation to ensure the transform's integrity, especially since it's a critical component for entity positioning.
get transform(): Transform { return this._transform; } +set transform(value: Transform) { + if (!value) { + throw new Error("Transform cannot be null or undefined"); + } + this._transform = value; +}
790-797
: Document EntityModifyFlags enum values.The new enum lacks documentation for its values, which is important for maintainability.
export enum EntityModifyFlags { + /** Flag indicating parent entity has changed */ Parent = 0x1, + /** Flag indicating sibling index has changed */ SiblingIndex = 0x2, + /** Flag indicating UI canvas was enabled in scene */ UICanvasEnableInScene = 0x4, + /** Flag indicating UI canvas was disabled in scene */ UICanvasDisableInScene = 0x8, + /** Flag indicating UI group was enabled in scene */ UIGroupEnableInScene = 0x10, + /** Flag indicating UI group was disabled in scene */ UIGroupDisableInScene = 0x20 }
783-783
: Consider caching matrix inversion.Matrix inversion is computationally expensive. Consider caching the result if this method is called frequently.
if (this._inverseWorldMatFlag.flag) { + // Cache the previous matrix for comparison + const prevMatrix = this._transform.worldMatrix.clone(); Matrix.invert(this._transform.worldMatrix, this._invModelMatrix); this._inverseWorldMatFlag.flag = false; + // Store the matrix used for inversion to detect changes + this._lastInvertedMatrix = prevMatrix; }packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts (2)
168-190
: Consider refactoring duplicated path comparison logicThe logic for comparing paths to find common ancestors is duplicated in multiple methods (
_updateRaycast
,_processUp
, and_processLeave
). Refactoring this code into a helper method would improve maintainability and reduce code duplication.Do you want me to generate the refactored code or open a GitHub issue to track this task?
229-229
: Define magic number as a constant for better maintainabilityThe magic number
1024
is used to prevent infinite loops by limiting the iteration count. Defining this number as a constant with a descriptive name enhances code readability and makes future adjustments easier.Apply this diff to define the magic number as a constant:
- for (; i < 1024 && !!entity && entity !== rootEntity; i++) { + const MAX_PATH_DEPTH = 1024; + for (; i < MAX_PATH_DEPTH && !!entity && entity !== rootEntity; i++) {packages/core/src/ui/Image.ts (1)
21-23
: Consider moving static properties to the end of the class.To improve readability and follow common conventions, consider moving the static properties
_tempVec2
and_tempUnit8Array
to the end of the class definition.packages/core/src/ui/Text.ts (2)
225-225
: Avoid using assignment expressions.Using assignments within expressions can be confusing and is often considered a code smell. It's better to separate the assignment from the expression for clarity and maintainability.
Refactor the code to avoid assignment expressions. For example:
- this._subFont && (this._subFont = null); + if (this._subFont) { + this._subFont = null; + }Apply similar changes to the other instances of assignment expressions.
Also applies to: 466-467, 477-477, 479-479, 566-566, 568-568
🧰 Tools
🪛 Biome
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
612-616
: Consider making theTextChunk
class properties readonly.Since the
TextChunk
class is an internal data structure used by theText
class, consider making its properties readonly to prevent accidental modifications from outside the class.Apply the following changes:
class TextChunk { - charRenderInfos = new Array<CharRenderInfo>(); + readonly charRenderInfos = new Array<CharRenderInfo>(); - subChunk: SubPrimitiveChunk; + readonly subChunk: SubPrimitiveChunk; - texture: Texture2D; + readonly texture: Texture2D; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (17)
packages/core/src/2d/text/TextRenderer.ts
(20 hunks)packages/core/src/2d/text/TextUtils.ts
(11 hunks)packages/core/src/Entity.ts
(10 hunks)packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts
(1 hunks)packages/core/src/ui/Image.ts
(1 hunks)packages/core/src/ui/Text.ts
(1 hunks)packages/core/src/ui/UICanvas.ts
(1 hunks)packages/core/src/ui/UIGroup.ts
(1 hunks)packages/core/src/ui/UIRenderer.ts
(1 hunks)packages/core/src/ui/UIUtils.ts
(1 hunks)packages/core/src/ui/index.ts
(1 hunks)packages/core/src/ui/interactive/UIInteractive.ts
(1 hunks)packages/core/src/ui/interactive/transition/SpriteTransition.ts
(1 hunks)packages/core/src/ui/interactive/transition/Transition.ts
(1 hunks)packages/core/src/ui/interface/IUIElement.ts
(1 hunks)packages/core/src/ui/interface/IUIGraphics.ts
(1 hunks)packages/core/src/ui/interface/IUIGroupable.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/core/src/ui/interactive/transition/SpriteTransition.ts
- packages/core/src/ui/interactive/transition/Transition.ts
- packages/core/src/ui/interface/IUIGraphics.ts
🧰 Additional context used
🪛 Biome
packages/core/src/Entity.ts
[error] 612-612: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/Text.ts
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 467-467: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 477-477: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 479-479: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 552-552: This is an unexpected use of the debugger statement.
Unsafe fix: Remove debugger statement
(lint/suspicious/noDebugger)
[error] 566-566: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 568-568: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UICanvas.ts
[error] 194-194: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 211-211: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 357-357: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 471-472: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 481-482: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 483-485: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIGroup.ts
[error] 105-105: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation
(lint/complexity/noExtraBooleanCast)
[error] 110-110: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation
(lint/complexity/noExtraBooleanCast)
[error] 153-153: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIRenderer.ts
[error] 158-158: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 162-162: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 163-163: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 161-165: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIUtils.ts
[error] 16-153: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
[error] 53-53: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 72-72: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 130-130: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 130-130: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 131-131: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 131-131: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 132-132: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 132-132: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
packages/core/src/ui/interactive/UIInteractive.ts
[error] 132-134: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
🔇 Additional comments (49)
packages/core/src/ui/interface/IUIGroupable.ts (2)
1-2
: LGTM! Imports are well-organized.
The imports are properly structured and use appropriate relative paths.
4-9
: Verify the impact of interface changes across the codebase.
Since this is a new interface that will be implemented by UI components, we should verify its usage pattern across the codebase.
✅ Verification successful
Let me check the actual implementations and usages to better understand the impact.
Interface changes appear to be properly integrated across the codebase
The interface IUIGroupable
is well-integrated into the UI component hierarchy:
- Correctly implemented by
UIInteractive
class - Used as a base interface for
IUIGraphics
- Properly utilized in
UIGroup
for managing grouped elements - Integrated with
UIUtils
for group management functionality
The interface changes maintain consistency with its implementations and usages across the codebase. All required properties and methods are properly referenced.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for potential implementations and usages of IUIGroupable
echo "Searching for implementations of IUIGroupable..."
rg "implements.*IUIGroupable" -l
echo "\nSearching for imports of IUIGroupable..."
rg "import.*IUIGroupable" -l
echo "\nSearching for type references to IUIGroupable..."
rg ": IUIGroupable" -l
Length of output: 645
Script:
#!/bin/bash
# Check the implementation in UIInteractive
echo "Checking UIInteractive implementation..."
rg "implements.*IUIGroupable" -A 10 packages/core/src/ui/interactive/UIInteractive.ts
echo "\nChecking UIGroup usage of IUIGroupable..."
rg "IUIGroupable" -A 5 packages/core/src/ui/UIGroup.ts
echo "\nChecking UIUtils usage..."
rg "IUIGroupable" -A 5 packages/core/src/ui/UIUtils.ts
echo "\nChecking IUIGraphics interface..."
rg "IUIGroupable" -A 5 packages/core/src/ui/interface/IUIGraphics.ts
Length of output: 1924
packages/core/src/ui/interface/IUIElement.ts (1)
1-3
: LGTM!
The imports are correct and necessary for the interface implementation.
packages/core/src/ui/index.ts (3)
1-4
: LGTM! Well-organized core exports.
The grouping of core UI components and enums at the top of the file follows good organizational practices. The separation of enums into their own directory suggests good code organization.
1-15
: Consider adding type exports for public interfaces.
While the component exports are well-organized, consider also exporting any public interfaces or types that consumers might need when working with these UI components.
Let's check for potentially missing type exports:
#!/bin/bash
# Description: Look for interfaces that might need to be exported
# Search for public interfaces in UI-related files
echo "Checking for public interfaces in UI files..."
rg "export interface" "./packages/core/src/ui/"
12-15
: Verify transition system integration.
The transition system appears to be comprehensive, covering color, scale, and sprite transitions. However, we should verify that these transitions are properly integrated with the UI components.
Let's verify the integration:
✅ Verification successful
Transition system is properly integrated with UI components
The verification shows a well-structured transition system:
UIInteractive
class serves as the integration point, managing transitions through_transitions
array- Base
Transition
class is properly extended byColorTransition
,ScaleTransition
, andSpriteTransition
- Transitions are properly managed with methods like
getTransition
,addTransition
,removeTransition
- Transitions are automatically updated in the component lifecycle through
onUpdate
- State changes are properly propagated to all transitions via
_setState
The implementation shows a robust and type-safe transition system that's tightly integrated with the UI component lifecycle.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check if transitions are properly integrated with UI components
# Test 1: Check for transition usage in UI components
echo "Checking transition usage in UI components..."
rg -l "Transition" "./packages/core/src/ui/(Button|Image|Text).ts"
# Test 2: Check if Transition is the base class for other transitions
echo "Verifying transition hierarchy..."
ast-grep --pattern 'class $_ extends Transition'
Length of output: 666
Script:
#!/bin/bash
# Let's verify the transition system integration with more precise commands
# Check for transition imports and usage in UI components
echo "Checking transition usage in UI components..."
rg -l "import.*Transition" packages/core/src/ui/
# Check implementation of transition classes
echo "Checking transition implementations..."
rg -A 5 "class.*Transition" packages/core/src/ui/interactive/transition/
# Check if UI components have transition-related methods or properties
echo "Checking UI components for transition methods..."
rg -A 3 "(transition|animate)" packages/core/src/ui/
Length of output: 6692
packages/core/src/ui/interactive/UIInteractive.ts (6)
1-36
: LGTM! Well-structured class definition with clear property organization.
The class structure follows good practices with appropriate use of decorators and clear property visibility modifiers.
52-78
: LGTM! Well-implemented transition management with type safety.
The transition management methods make good use of TypeScript generics and provide a clean API for managing transitions.
80-102
: LGTM! Clean event handling implementation.
The pointer event handlers maintain a clear separation between state tracking and updates.
107-123
: LGTM! Proper lifecycle management with clean state handling.
The lifecycle methods properly manage registration/unregistration and state cleanup.
157-174
: LGTM! Clean state management implementation.
The state management methods are well-organized with clear transition logic and state calculations.
130-140
:
Fix switch case fallthrough issue.
The cases for UICanvasEnableInScene
and Parent
are falling through to UIGroupEnableInScene
unintentionally.
🧰 Tools
🪛 Biome
[error] 132-134: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIGroup.ts (5)
1-41
: LGTM! Well-structured class definition with proper property management.
The class structure is well-organized with appropriate use of decorators and clear documentation of internal properties.
43-75
: LGTM! Well-implemented property accessors.
The getters and setters are properly implemented with appropriate validation and update triggers.
117-144
: LGTM! Thorough lifecycle management.
The lifecycle methods properly handle registration, cleanup, and garbage collection.
186-191
: LGTM! Well-designed flags enum.
The GroupModifyFlags enum is properly implemented using bit flags, allowing efficient combination of multiple flags.
153-154
: 🛠️ Refactor suggestion
Improve readability by avoiding assignment in expression.
The assignment within the logical AND expression makes the code harder to read and understand.
Apply this diff:
- replaced && (replaced._groupIndex = this._groupIndex);
+ if (replaced) {
+ replaced._groupIndex = this._groupIndex;
+ }
Likely invalid or redundant comment.
🧰 Tools
🪛 Biome
[error] 153-153: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIRenderer.ts (1)
19-20
:
Add missing ISpriteRenderer interface.
The class should implement the ISpriteRenderer interface to maintain consistency with the sprite rendering system.
-export abstract class UIRenderer extends Renderer implements IUIGraphics {
+export abstract class UIRenderer extends Renderer implements IUIGraphics, ISpriteRenderer {
Likely invalid or redundant comment.
packages/core/src/2d/text/TextUtils.ts (2)
2-2
: LGTM: Import addition aligns with new type requirements.
The addition of the Text import supports the new union type parameter (TextRenderer | Text) in the measure methods.
99-104
: Verify interface compatibility between TextRenderer and Text.
Both measure methods accept either TextRenderer or Text type. Let's verify they implement the required interface members.
Also applies to: 276-280
✅ Verification successful
Let me gather more information about both TextRenderer
and Text
classes to verify their interface compatibility.
Let me search for more specific information about the text measurement methods and required interface members.
Let me search for the required properties and methods in both classes with a different pattern.
Based on the gathered information, I can now provide the final response:
Interface compatibility between TextRenderer and Text is verified.
Both TextRenderer
and Text
classes implement the required interface members for measureTextWithWrap
:
- Both have
overflowMode
property with identical behavior (defaults toOverflowMode.Overflow
) - Both have
width
andheight
properties - Both use the same text measurement logic through
TextUtils.measureTextWithWrap
- Both handle text overflow and truncation in the same way
The shared interface is consistently implemented across both classes, making them safely interchangeable in the measureTextWithWrap
method.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for the Text class definition to verify required members
echo "Searching for Text class definition..."
ast-grep --pattern 'class Text {
$$$
_getSubFont() {
$$$
}
$$$
}'
echo "Verifying overflowMode property..."
ast-grep --pattern 'class Text {
$$$
overflowMode: $_
$$$
}'
Length of output: 314
Script:
#!/bin/bash
# Search for TextRenderer interface/class definition
echo "Searching for TextRenderer definition..."
rg "(?:interface|class|type)\s+TextRenderer" -A 10
# Search for Text class usage of measureTextWithWrap
echo "Searching for Text class measureText related methods..."
rg "class Text" -A 30
# Search for common method signatures between Text and TextRenderer
echo "Searching for common method signatures..."
ast-grep --pattern '(class Text|class TextRenderer) {
$$$
measureText($_) {
$$$
}
$$$
}'
Length of output: 36998
Script:
#!/bin/bash
# Search for measureTextWithWrap implementation and usage
echo "Searching for measureTextWithWrap implementation..."
rg "measureTextWithWrap" -A 5
# Search for required properties and methods in both classes
echo "Searching for text measurement related properties..."
ast-grep --pattern '(class Text|class TextRenderer) {
$$$
_getSubFont() {
$$$
}
$$$
overflowMode: $_
$$$
}'
# Search for common interface or type definitions
echo "Searching for TextRenderer interface..."
rg "interface.*TextRenderer" -A 10
Length of output: 8882
Script:
#!/bin/bash
# Search for overflowMode property in both classes
echo "Searching for overflowMode property..."
rg "overflowMode.*=" -B 5 -A 1
# Search for _getSubFont method in both classes
echo "Searching for _getSubFont method..."
rg "_getSubFont.*{" -B 2 -A 10
# Search for width and height properties
echo "Searching for width and height properties..."
rg "(get width\(\)|get height\(\))" -B 2 -A 5
Length of output: 18444
packages/core/src/2d/text/TextRenderer.ts (1)
35-35
:
Fix array initialization syntax.
The array initialization syntax is incorrect. Using Array<TextChunk>()
without the new
keyword doesn't create a new array instance.
-private _textChunks = Array<TextChunk>();
+private _textChunks: TextChunk[] = [];
Likely invalid or redundant comment.
packages/core/src/Entity.ts (4)
99-105
: LGTM: Property refactoring improves encapsulation.
The refactoring of transform
to a private _transform
property with a getter improves encapsulation. The addition of _updateFlagManager
enables better state change management.
205-206
: LGTM: Constructor initialization is correct.
The initialization of _transform
and registration of world change flag is properly implemented.
527-530
: LGTM: Proper cleanup in destroy method.
The cleanup of _updateFlagManager
is correctly implemented with null checks and removal of all listeners.
404-404
: Verify UI Transform inheritance.
The conditional addition of UITransform needs verification to ensure proper inheritance across the entity hierarchy.
✅ Verification successful
UI Transform inheritance is correctly implemented
The verification shows that the UI Transform inheritance is properly handled:
UITransform
extends the baseTransform
class and is automatically added to components that require it through the@dependentComponents
decorator (seen in UIRenderer and UICanvas).- The inheritance check in
Entity.createChild()
ensures that if a parent entity has aUITransform
, its children will also get aUITransform
component. - The implementation maintains consistency in the UI component hierarchy by propagating the appropriate transform type from parent to child entities.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check UITransform usage patterns
# Expected: Consistent UITransform inheritance in parent-child relationships
# Check UITransform inheritance patterns
ast-grep --pattern 'class $_ extends $_ {
$$$
addComponent($_) {
$$$
UITransform
$$$
}
$$$
}'
Length of output: 115
Script:
#!/bin/bash
# Let's try a different approach to analyze UITransform usage
# Search for UITransform related code patterns
rg "UITransform" -A 5 -B 5
# Also check for specific inheritance patterns
ast-grep --pattern 'this._transform instanceof UITransform'
# Look for component addition patterns
ast-grep --pattern 'addComponent($_)'
Length of output: 21016
packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts (3)
102-102
: ****
🧰 Tools
🪛 Biome
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
222-222
: ****
🧰 Tools
🪛 Biome
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
167-168
: ****
packages/core/src/ui/Image.ts (19)
1-19
: LGTM!
The imports are well-organized and cover the necessary dependencies for the Image
class implementation.
20-20
: LGTM!
The class declaration is correct, extending UIRenderer
and implementing the ISpriteRenderer
interface.
24-35
: LGTM!
The private properties are well-defined, with appropriate types and default values. The use of @ignoreClone
and @assignmentClone
decorators is consistent.
37-43
: LGTM!
The getter and setter for alphaHitTestMinimumThreshold
are implemented correctly, with proper clamping of the input value.
45-71
: LGTM!
The getter and setter for drawMode
are implemented correctly. The setter updates the _assembler
based on the selected draw mode and resets the sprite data.
73-87
: LGTM!
The getter and setter for tileMode
are implemented correctly. The setter updates the dirty flag when the draw mode is set to Tiled
.
89-104
: LGTM!
The getter and setter for tiledAdaptiveThreshold
are implemented correctly, with proper clamping of the input value and updating the dirty flag when the draw mode is set to Tiled
.
106-130
: LGTM!
The getter and setter for sprite
are implemented correctly. The setter handles resource reference counting, event listener management, and updating the shader data.
132-143
: LGTM!
The constructor initializes the necessary properties and binds event listeners correctly.
145-178
: LGTM!
The _hitTest
method is implemented correctly, handling the hit testing based on the sprite's bounds and alpha threshold.
180-191
: LGTM!
The _updateLocalBounds
method is implemented correctly, updating the local bounds based on the sprite's size and pivot.
193-248
: LGTM!
The _render
method is implemented correctly, handling the rendering process based on the sprite's properties and updating the necessary data when dirty flags are set.
250-255
: LGTM!
The _canBatch
method is implemented correctly, delegating the batching decision to BatchUtils.canBatchSprite
.
257-262
: LGTM!
The _batch
method is implemented correctly, delegating the batching process to BatchUtils.batchFor2D
.
264-283
: LGTM!
The _onTransformChanged
method is implemented correctly, updating the dirty flags based on the transform modification type.
285-298
: LGTM!
The _onDestroy
method is implemented correctly, handling resource cleanup and event listener removal.
300-341
: LGTM!
The _onSpriteChange
method is implemented correctly, updating the dirty flags based on the sprite modification type.
343-356
: LGTM!
The _getUVByLocalPosition
method is implemented correctly, delegating the UV calculation to the appropriate sprite assembler based on the draw mode.
359-371
: LGTM!
The ImageUpdateFlags
enum is defined correctly, extending the UIRendererUpdateFlags
and providing additional flags specific to the Image
class.
packages/core/src/ui/Text.ts (2)
25-609
: The Text
class implementation looks solid and comprehensive. Great work!
The class provides a robust set of properties and methods for managing text rendering in a UI context. The code is well-structured, follows best practices, and includes appropriate optimizations for efficient rendering.
Some key highlights:
- Proper handling of resource management through reference counting
- Efficient rendering using text chunks and sub-render elements
- Comprehensive text layout and alignment options
- Effective use of dirty flags for updating rendering state
- Well-organized and readable code structure
Overall, the Text
class seems to be a valuable addition to the UI rendering system.
🧰 Tools
🪛 Biome
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 467-467: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 477-477: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 479-479: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 552-552: This is an unexpected use of the debugger statement.
Unsafe fix: Remove debugger statement
(lint/suspicious/noDebugger)
[error] 566-566: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 568-568: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
1-628
: Verify the integration of the Text
class with other UI components.
The Text
class seems to be well-integrated with the UI rendering system, particularly with the UIRenderer
class. However, it's important to ensure that the Text
class works seamlessly with other UI components introduced in this pull request, such as UIGroup
and UICanvas
.
To verify the integration, you can perform the following checks:
-
Review the usage of the
Text
class in the context ofUIGroup
andUICanvas
classes to ensure proper rendering order and hierarchy management. -
Analyze the interaction between the
Text
class and the enhancedComponentsManager
to confirm that text rendering is efficiently handled within the overall UI hierarchy. -
Test the rendering of
Text
instances in various scenarios, such as different canvas render modes, to validate the expected behavior. -
Verify that the
Text
class correctly utilizes the rendering optimizations provided by theUIRenderer
class, such as rendering contexts and batching.
If any issues or inconsistencies are found during the verification process, please let me know, and I'll be happy to assist in resolving them.
🧰 Tools
🪛 Biome
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 467-467: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 477-477: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 479-479: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 552-552: This is an unexpected use of the debugger statement.
Unsafe fix: Remove debugger statement
(lint/suspicious/noDebugger)
[error] 566-566: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 568-568: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
export interface IUIGroupable extends IUIElement { | ||
_group: UIGroup; | ||
_indexInGroup: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider revising the interface design to hide implementation details.
The current design exposes internal implementation details through properties prefixed with underscores. This approach:
- Breaks encapsulation by exposing internal state
- Makes the interface tightly coupled to the implementation
- Deviates from TypeScript interface conventions
Consider this alternative design that hides implementation details:
-export interface IUIGroupable extends IUIElement {
- _group: UIGroup;
- _indexInGroup: number;
+export interface IUIGroupable extends IUIElement {
+ readonly group: UIGroup;
+ readonly indexInGroup: number;
This change:
- Removes implementation-specific underscore prefixes
- Makes properties readonly to prevent external modifications
- Better adheres to interface design principles
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export interface IUIGroupable extends IUIElement { | |
_group: UIGroup; | |
_indexInGroup: number; | |
export interface IUIGroupable extends IUIElement { | |
readonly group: UIGroup; | |
readonly indexInGroup: number; |
packages/core/src/ui/UIUtils.ts
Outdated
static registerElementToCanvas(element: IUIElement, canvas: UICanvas): void { | ||
const preCanvas = element._rootCanvas; | ||
if (preCanvas !== canvas) { | ||
element._rootCanvas = canvas; | ||
if (preCanvas) { | ||
const replaced = preCanvas._disorderedElements.deleteByIndex(element._indexInCanvas); | ||
replaced && (replaced._indexInCanvas = element._indexInCanvas); | ||
element._indexInCanvas = -1; | ||
preCanvas._hierarchyDirty = true; | ||
} | ||
if (canvas) { | ||
const disorderedElements = canvas._disorderedElements; | ||
element._indexInCanvas = disorderedElements.length; | ||
disorderedElements.add(element); | ||
canvas._hierarchyDirty = true; | ||
} | ||
} | ||
} | ||
|
||
static registerElementToGroup(element: IUIGroupable, group: UIGroup): void { | ||
const preGroup = element._group; | ||
if (preGroup !== group) { | ||
element._group = group; | ||
if (preGroup) { | ||
const replaced = preGroup._disorderedElements.deleteByIndex(element._indexInGroup); | ||
replaced && (replaced._indexInGroup = element._indexInGroup); | ||
element._indexInGroup = -1; | ||
} | ||
if (group) { | ||
const disorderedElements = group._disorderedElements; | ||
element._indexInGroup = disorderedElements.length; | ||
disorderedElements.add(element); | ||
} | ||
element._onGroupModify(GroupModifyFlags.All); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Reduce code duplication in registration methods.
The registerElementToCanvas
and registerElementToGroup
methods share similar logic. Consider extracting the common functionality into a private helper method:
private static registerElementToContainer<T extends IUIElement, C>(
element: T,
container: C,
getPreContainer: (el: T) => C,
setContainer: (el: T, c: C) => void,
getDisorderedElements: (c: C) => DisorderedArray<T>,
getIndex: (el: T) => number,
setIndex: (el: T, i: number) => void,
onModify?: (el: T) => void
): void {
const preContainer = getPreContainer(element);
if (preContainer !== container) {
setContainer(element, container);
if (preContainer) {
const replaced = getDisorderedElements(preContainer).deleteByIndex(getIndex(element));
if (replaced) {
setIndex(replaced, getIndex(element));
}
setIndex(element, -1);
}
if (container) {
const disorderedElements = getDisorderedElements(container);
setIndex(element, disorderedElements.length);
disorderedElements.add(element);
}
onModify?.(element);
}
}
🧰 Tools
🪛 Biome
[error] 53-53: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 72-72: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIUtils.ts
Outdated
static getRootCanvasInParent(entity: Entity): UICanvas { | ||
while (entity) { | ||
const components = entity._components; | ||
for (let i = 0, n = components.length; i < n; i++) { | ||
const component = components[i]; | ||
if ( | ||
component.enabled && | ||
component._componentType === ComponentType.UICanvas && | ||
(<UICanvas>component)._isRootCanvas | ||
) { | ||
return <UICanvas>component; | ||
} | ||
} | ||
entity = entity.parent; | ||
} | ||
return null; | ||
} | ||
|
||
static getGroupInParents(entity: Entity): UIGroup { | ||
let meetRootCanvas = false; | ||
while (entity) { | ||
const components = entity._components; | ||
for (let i = 0, n = components.length; i < n; i++) { | ||
const component = components[i]; | ||
if (component.enabled) { | ||
switch (component._componentType) { | ||
case ComponentType.UICanvas: | ||
meetRootCanvas = (<UICanvas>component)._isRootCanvas; | ||
break; | ||
case ComponentType.UIGroup: | ||
return <UIGroup>component; | ||
default: | ||
break; | ||
} | ||
} | ||
} | ||
if (meetRootCanvas) { | ||
return null; | ||
} | ||
entity = entity.parent; | ||
} | ||
return null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve type safety and performance in traversal methods.
- Replace type casting with type guards for better type safety.
- Consider caching results for frequently accessed entities.
private static isUICanvas(component: any): component is UICanvas {
return component._componentType === ComponentType.UICanvas;
}
private static isUIGroup(component: any): component is UIGroup {
return component._componentType === ComponentType.UIGroup;
}
static getRootCanvasInParent(entity: Entity): UICanvas {
// Consider adding WeakMap cache for frequent lookups
private static canvasCache = new WeakMap<Entity, UICanvas>();
if (UIUtils.canvasCache.has(entity)) {
return UIUtils.canvasCache.get(entity);
}
while (entity) {
const components = entity._components;
for (const component of components) {
if (component.enabled && UIUtils.isUICanvas(component) && component._isRootCanvas) {
UIUtils.canvasCache.set(entity, component);
return component;
}
}
entity = entity.parent;
}
return null;
}
private static _renderQueue: RenderQueue; | ||
private static _virtualCamera: VirtualCamera; | ||
private static _viewport: Vector4; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Initialize static fields immediately to prevent race conditions.
Lazy initialization in static fields can lead to race conditions in concurrent scenarios. Consider initializing all fields during class definition:
- private static _renderQueue: RenderQueue;
- private static _virtualCamera: VirtualCamera;
- private static _viewport: Vector4;
+ private static _renderQueue: RenderQueue = new RenderQueue(RenderQueueType.Transparent);
+ private static _virtualCamera: VirtualCamera = new VirtualCamera();
+ private static _viewport: Vector4 = new Vector4(0, 0, 1, 1);
Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/ui/UIUtils.ts
Outdated
static registerEntityListener(element: IUIElement): void { | ||
const parents = element._parents; | ||
const root = element._rootCanvas?.entity; | ||
let entity = element._entity; | ||
let index = 0; | ||
while (entity && entity !== root) { | ||
const preParent = parents[index]; | ||
if (preParent !== entity) { | ||
preParent?._unRegisterModifyListener(element._onEntityModify); | ||
parents[index] = entity; | ||
entity._registerModifyListener(element._onEntityModify); | ||
} | ||
entity = entity.parent; | ||
index++; | ||
} | ||
parents.length = index; | ||
} | ||
|
||
static unRegisterEntityListener(element: IUIElement): void { | ||
const { _parents: parents } = element; | ||
for (let i = 0, n = parents.length; i < n; i++) { | ||
parents[i]._unRegisterModifyListener(element._onEntityModify); | ||
} | ||
parents.length = 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add input validation and error handling.
The entity listener methods lack input validation and error handling. Consider adding checks for null/undefined parameters and handling edge cases:
static registerEntityListener(element: IUIElement): void {
+ if (!element) {
+ throw new Error("Element cannot be null or undefined");
+ }
const parents = element._parents;
+ if (!parents) {
+ throw new Error("Element parents array is not initialized");
+ }
// ... rest of the method
}
static unRegisterEntityListener(element: IUIElement): void {
+ if (!element || !element._parents) {
+ return; // Silent fail on cleanup
+ }
const { _parents: parents } = element;
// ... rest of the method
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
static registerEntityListener(element: IUIElement): void { | |
const parents = element._parents; | |
const root = element._rootCanvas?.entity; | |
let entity = element._entity; | |
let index = 0; | |
while (entity && entity !== root) { | |
const preParent = parents[index]; | |
if (preParent !== entity) { | |
preParent?._unRegisterModifyListener(element._onEntityModify); | |
parents[index] = entity; | |
entity._registerModifyListener(element._onEntityModify); | |
} | |
entity = entity.parent; | |
index++; | |
} | |
parents.length = index; | |
} | |
static unRegisterEntityListener(element: IUIElement): void { | |
const { _parents: parents } = element; | |
for (let i = 0, n = parents.length; i < n; i++) { | |
parents[i]._unRegisterModifyListener(element._onEntityModify); | |
} | |
parents.length = 0; | |
} | |
static registerEntityListener(element: IUIElement): void { | |
if (!element) { | |
throw new Error("Element cannot be null or undefined"); | |
} | |
const parents = element._parents; | |
if (!parents) { | |
throw new Error("Element parents array is not initialized"); | |
} | |
const root = element._rootCanvas?.entity; | |
let entity = element._entity; | |
let index = 0; | |
while (entity && entity !== root) { | |
const preParent = parents[index]; | |
if (preParent !== entity) { | |
preParent?._unRegisterModifyListener(element._onEntityModify); | |
parents[index] = entity; | |
entity._registerModifyListener(element._onEntityModify); | |
} | |
entity = entity.parent; | |
index++; | |
} | |
parents.length = index; | |
} | |
static unRegisterEntityListener(element: IUIElement): void { | |
if (!element || !element._parents) { | |
return; // Silent fail on cleanup | |
} | |
const { _parents: parents } = element; | |
for (let i = 0, n = parents.length; i < n; i++) { | |
parents[i]._unRegisterModifyListener(element._onEntityModify); | |
} | |
parents.length = 0; | |
} |
_registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | ||
(this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix assignment in expression.
The assignment within the expression can be confusing and should be split for better readability.
- (this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange);
+ if (!this._updateFlagManager) {
+ this._updateFlagManager = new UpdateFlagManager();
+ }
+ this._updateFlagManager.addListener(onChange);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
_registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | |
(this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange); | |
} | |
_registerModifyListener(onChange: (EntityModifyFlags) => void): void { | |
if (!this._updateFlagManager) { | |
this._updateFlagManager = new UpdateFlagManager(); | |
} | |
this._updateFlagManager.addListener(onChange); | |
} |
🧰 Tools
🪛 Biome
[error] 612-612: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
override _processRaycast(scenes: readonly Scene[], pointer: Pointer): void { | ||
const { _tempRay: ray } = PointerEventEmitter; | ||
const hitResult = this._hitResult; | ||
const { position } = pointer; | ||
const { x, y } = position; | ||
for (let i = scenes.length - 1; i >= 0; i--) { | ||
const scene = scenes[i]; | ||
if (!scene.isActive || scene.destroyed) continue; | ||
const { _componentsManager: componentsManager } = scene; | ||
|
||
/** Overlay Canvas */ | ||
let canvasElements = componentsManager._overlayCanvases; | ||
ray.origin.set(position.x, position.y, 1); | ||
ray.direction.set(0, 0, -1); | ||
for (let j = canvasElements.length - 1; j >= 0; j--) { | ||
if (canvasElements.get(j).raycast(ray, hitResult)) { | ||
this._updateRaycast(<Component>hitResult.component, pointer); | ||
return; | ||
} | ||
} | ||
|
||
const cameras = componentsManager._activeCameras; | ||
for (let j = cameras.length - 1; j >= 0; j--) { | ||
const camera = cameras.get(j); | ||
if (camera.renderTarget) continue; | ||
const { pixelViewport } = camera; | ||
if ( | ||
x < pixelViewport.x || | ||
y < pixelViewport.y || | ||
x > pixelViewport.x + pixelViewport.width || | ||
y > pixelViewport.y + pixelViewport.height | ||
) { | ||
continue; | ||
} | ||
camera.screenPointToRay(pointer.position, ray); | ||
|
||
/** Other canvases */ | ||
const cameraPosition = camera.entity.transform.position; | ||
/** Sort by rendering order */ | ||
canvasElements = componentsManager._canvases; | ||
for (let k = 0, n = canvasElements.length; k < n; k++) { | ||
canvasElements.get(k)._updateSortDistance(cameraPosition); | ||
} | ||
canvasElements.sort((a, b) => a.sortOrder - b.sortOrder || a._sortDistance - b._sortDistance); | ||
for (let k = 0, n = canvasElements.length; k < n; k++) { | ||
canvasElements.get(k)._canvasIndex = k; | ||
} | ||
const farClipPlane = camera.farClipPlane; | ||
/** Post-rendering first detection */ | ||
for (let k = 0, n = canvasElements.length; k < n; k++) { | ||
const canvas = canvasElements.get(k); | ||
if (canvas.renderCamera !== camera) continue; | ||
if (canvas.raycast(ray, hitResult, farClipPlane)) { | ||
this._updateRaycast(<Component>hitResult.component, pointer); | ||
return; | ||
} | ||
} | ||
if (camera.clearFlags & CameraClearFlags.Color) { | ||
this._updateRaycast(null); | ||
return; | ||
} | ||
} | ||
} | ||
return null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Method complexity exceeds recommended threshold
The _processRaycast
method is complex with multiple nested loops and conditions. Consider breaking it down into smaller, focused methods:
processOverlayCanvas
processCameraCanvas
handleCameraRaycast
This would improve readability and maintainability.
Do you want me to generate the refactored code or open a GitHub issue to track this task?
🧰 Tools
🪛 Biome
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
override _processDrag(pointer: Pointer): void { | ||
if (this._draggedElement) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add parameter validation and error handling
The _processDrag
method accepts a pointer
parameter without validation. Consider adding null checks and appropriate error handling to prevent runtime exceptions.
Apply this diff to add validation:
override _processDrag(pointer: Pointer): void {
+ if (!pointer) {
+ console.warn('Invalid pointer provided to _processDrag');
+ return;
+ }
if (this._draggedElement) {
Do you want me to open a GitHub issue to track this task?
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
override _processDrag(pointer: Pointer): void { | |
if (this._draggedElement) { | |
override _processDrag(pointer: Pointer): void { | |
if (!pointer) { | |
console.warn('Invalid pointer provided to _processDrag'); | |
return; | |
} | |
if (this._draggedElement) { |
} | ||
} | ||
} | ||
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unnecessary return null;
in a void function
The method _processRaycast
is declared with a return type of void
, but it ends with return null;
. Since the function is expected to return void
, the return null;
statement is unnecessary and should be removed to prevent confusion.
Apply this diff to fix the issue:
- return null;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
return null; |
🧰 Tools
🪛 Biome
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
packages/core/src/ui/Text.ts
Outdated
} | ||
|
||
private _isTextNoVisible(): boolean { | ||
debugger; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the debugger statement.
The debugger
statement should not be present in production code as it can pause execution and affect performance.
Remove the following line:
- debugger;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
debugger; |
🧰 Tools
🪛 Biome
[error] 552-552: This is an unexpected use of the debugger statement.
Unsafe fix: Remove debugger statement
(lint/suspicious/noDebugger)
feat: seperate ui package
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
🛑 Comments failed to post (68)
packages/ui/src/component/UITransform.ts (2)
32-41:
⚠️ Potential issueAvoid accessing private properties and suppressing TypeScript errors
The use of
// @ts-ignore
to accessentity._transform
andentity._inverseWorldMatFlag
indicates that you're accessing private or protected properties. Suppressing TypeScript errors can hide potential issues and compromises type safety.Consider the following actions:
- Refactor the code to use public APIs or accessor methods: If possible, use public methods or properties provided by the
Entity
class to achieve the same functionality.- Modify the base class to expose necessary properties: If you have control over the
Entity
class, consider exposing_transform
and_inverseWorldMatFlag
through public or protected members.- Extend the
Entity
class appropriately: Create a subclass ofEntity
that exposes the needed properties in a type-safe manner.
46-47:
⚠️ Potential issueAccessing private members
_updateFlagManager
may lead to maintenance issuesDirectly accessing private members like
this._updateFlagManager
by suppressing TypeScript errors with// @ts-ignore
can lead to maintenance challenges and violates encapsulation principles.Consider the following solutions:
- Expose a protected method or property in the base class: Modify the
Transform
class to provide protected access to_updateFlagManager
.- Use public methods to dispatch updates: If available, use existing public methods to dispatch update flags without needing to access private members.
Also applies to: 52-53
packages/ui/src/component/interactive/transition/Transition.ts (4)
84-94:
⚠️ Potential issuePrevent potential division by zero in
set duration
methodIn the
set duration(value: number)
method, there is a potential division by zero whenpreDuration
is zero.At line 90:
this._countDown = value * (1 - this._countDown / preDuration);If
preDuration
is zero, dividing by zero will result inInfinity
orNaN
, causing unexpected behavior. To prevent this, add a check to ensurepreDuration
is not zero before performing the division.Apply this diff to fix the issue:
set duration(value: number) { if (value < 0) value = 0; const preDuration = this._duration; if (preDuration !== value) { this._duration = value; if (this._countDown > 0) { + if (preDuration > 0) { this._countDown = value * (1 - this._countDown / preDuration); + } else { + this._countDown = value; + } this._updateValue(); } } }
76-76:
⚠️ Potential issueEnsure
_currentValue
is initialized before useIn lines 76 and 148, the method
_applyValue
is called withthis._currentValue
, which may beundefined
if not properly initialized. This can lead to unexpected behavior or runtime errors.Initialize
this._currentValue
before it's used. Consider setting a default value during declaration or ensuring it's assigned in all code paths before invocation:protected _currentValue: T; + + constructor() { + // Initialize _currentValue with a default value, if appropriate + this._currentValue = this.normal; // or any suitable default + }Alternatively, add checks before calling
_applyValue
:if (this._currentValue !== undefined) { value?.enabled && this._applyValue(this._currentValue); }Ensuring
_currentValue
is properly initialized enhances code stability.Also applies to: 148-148
135-138: 🛠️ Refactor suggestion
Eliminate the use of
@ts-ignore
directivesUsing
@ts-ignore
in lines 135 and 137 suppresses TypeScript compiler errors, which can hide potential issues and reduce type safety.Replace
@ts-ignore
with proper type checks or type assertions to maintain type safety. Here's how you can modify the code:protected _onStateValueDirty(state: InteractiveState, preValue: T, curValue: T): void { - // @ts-ignore - preValue instanceof ReferResource && preValue._addReferCount(-1); + if (preValue instanceof ReferResource) { + preValue._addReferCount(-1); + } - // @ts-ignore - curValue instanceof ReferResource && curValue._addReferCount(1); + if (curValue instanceof ReferResource) { + curValue._addReferCount(1); + } if (this._finalState === state) { this._finalValue = curValue; this._updateValue(); } }This approach ensures that
preValue
andcurValue
are instances ofReferResource
before calling_addReferCount
, improving code safety and readability.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (preValue instanceof ReferResource) { preValue._addReferCount(-1); } if (curValue instanceof ReferResource) { curValue._addReferCount(1); }
151-162:
⚠️ Potential issueAdd a default case to handle unexpected states in
_getValueByState
The method
_getValueByState
lacks a default case in the switch statement. If an unexpectedInteractiveState
value is passed, the method may returnundefined
, leading to runtime errors.Add a default case to ensure the method always returns a valid value:
private _getValueByState(state: InteractiveState): T { switch (state) { case InteractiveState.Normal: return this.normal; case InteractiveState.Pressed: return this.pressed; case InteractiveState.Hover: return this.hover; case InteractiveState.Disable: return this.disabled; + default: + // Handle unexpected state, possibly by returning a default value or throwing an error + return this.normal; // or consider throwing an error } }This change ensures that your code handles all possible cases, enhancing robustness.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.private _getValueByState(state: InteractiveState): T { switch (state) { case InteractiveState.Normal: return this.normal; case InteractiveState.Pressed: return this.pressed; case InteractiveState.Hover: return this.hover; case InteractiveState.Disable: return this.disabled; default: // Handle unexpected state, possibly by returning a default value or throwing an error return this.normal; // or consider throwing an error } }
packages/ui/src/Utils.ts (2)
79-79: 🛠️ Refactor suggestion
Avoid assignments within logical expressions for better clarity.
In lines 79, 100, 122, 133, and 135, assignments are performed within logical
&&
expressions, which can be confusing and reduce readability.For example, at line 79:
replaced && (replaced._indexInCanvas = element._indexInCanvas);It's clearer to separate the assignment from the conditional check:
if (replaced) { replaced._indexInCanvas = element._indexInCanvas; }Apply similar changes to the other lines to enhance code clarity and maintainability.
Also applies to: 100-100, 122-122, 133-133, 135-135
🧰 Tools
🪛 Biome
[error] 79-79: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
18-18: 🛠️ Refactor suggestion
Minimize the use of
// @ts-ignore
to ensure type safety.The code contains multiple
// @ts-ignore
comments (lines 18, 21, 43, 62, 87, 108), which suppress TypeScript compiler errors. Overusing// @ts-ignore
can hide potential type issues and decrease code reliability. It's advisable to address the underlying type errors instead of suppressing them.Also applies to: 21-21, 43-43, 62-62, 87-87, 108-108
packages/core/src/input/pointer/emitter/PhysicsPointerEventEmitter.ts (1)
61-61: 🛠️ Refactor suggestion
Avoid assignments within expressions for better readability
On line 61, the assignment combines multiple variables in a single expression. This can reduce code clarity and make it harder to debug.
Consider separating the assignments to enhance readability:
-const entity = (this._pressedEntity = this._draggedEntity = this._enteredEntity); +this._pressedEntity = this._draggedEntity = this._enteredEntity; +const entity = this._pressedEntity;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.this._pressedEntity = this._draggedEntity = this._enteredEntity; const entity = this._pressedEntity;
🧰 Tools
🪛 Biome
[error] 61-61: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 61-61: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/ui/src/component/UIGroup.ts (3)
148-149:
⚠️ Potential issueRemove
@ts-ignore
and correctly override lifecycle methodsThe use of
@ts-ignore
when overriding_onEnableInScene()
and_onDisableInScene()
suggests type mismatches. Ensure that these methods are properly declared in the base class and that your overrides match the expected signatures without suppressing type errors.Consider updating your method names or checking the base class definitions to align with TypeScript's expectations.
Also applies to: 156-157
152-153: 🛠️ Refactor suggestion
Avoid accessing internal methods like
_dispatchModify
Accessing
this.entity._dispatchModify
directly may break encapsulation if_dispatchModify
is intended to be a private or internal method. Relying on internal APIs can lead to maintenance issues if the underlying implementation changes.Consider using a public method or event system provided by the
Entity
class to dispatch modifications.
90-91: 🛠️ Refactor suggestion
Avoid using
@ts-ignore
; ensure_componentType
assignment is type-safeUsing
@ts-ignore
suppresses TypeScript compiler errors and can mask underlying issues. Assigning to_componentType
directly may not be safe if it's a protected or private member of the parent class. Consider providing a proper accessor or refactoring the class hierarchy to avoid bypassing TypeScript's type checking.Apply this diff to address the issue:
-// @ts-ignore this._componentType = ComponentType.UIGroup; + (this as any)._componentType = ComponentType.UIGroup;Or better, request an accessor from the base class if possible.
Committable suggestion skipped: line range outside the PR's diff.
packages/ui/src/component/interactive/UIInteractive.ts (3)
146-155: 🛠️ Refactor suggestion
Avoid using
@ts-ignore
; properly handle method overriding in_onEnableInScene
.Using
@ts-ignore
suppresses TypeScript errors and may conceal potential issues. The method_onEnableInScene
is being overridden, but TypeScript likely flags an error due to access modifiers or method visibility in the base classScript
.Consider these options:
- Option 1: Update the base class
Script
to declare_onEnableInScene
asprotected
, allowing safe overriding.- Option 2: If modifying the base class is not feasible, create a new method in your subclass that does not conflict with the base class's private or internal methods.
- Option 3: Use TypeScript's
protected
orpublic
access modifiers to align with the intended visibility and override methods without suppressing errors.Removing
@ts-ignore
ensures that TypeScript can properly check your code for errors.
157-167: 🛠️ Refactor suggestion
Avoid using
@ts-ignore
; ensure proper method overriding in_onDisableInScene
.Similar to the previous comment, suppressing TypeScript errors with
@ts-ignore
when overriding_onDisableInScene
can hide potential issues. Properly handling method overriding is essential for code safety and maintainability.Implement the same solutions as suggested for
_onEnableInScene
to address this issue.
105-109:
⚠️ Potential issueAdjust generic type constraint in
addTransition
method to support constructors with parameters.The current generic type constraint
T extends new () => Transition
may not accommodateTransition
subclasses that require constructor arguments. This can lead to issues when adding transitions that need parameters during instantiation.Apply this diff to adjust the generic type constraint and handle constructors with parameters:
- addTransition<T extends new () => Transition>(type: T): InstanceType<T> { - const transition = new type() as InstanceType<T>; + addTransition<T extends abstract new (...args: any[]) => Transition>(type: T, ...args: ConstructorParameters<T>): InstanceType<T> { + const transition = new type(...args) as InstanceType<T>; this._transitions.push(transition); transition._setState(this._state, true); return transition; }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.addTransition<T extends abstract new (...args: any[]) => Transition>(type: T, ...args: ConstructorParameters<T>): InstanceType<T> { const transition = new type(...args) as InstanceType<T>; this._transitions.push(transition); transition._setState(this._state, true); return transition; }
packages/ui/src/input/UIPointerEventEmitter.ts (3)
222-222: 🛠️ Refactor suggestion
Avoid assignments within expressions for better readability
Similar to line 103, the assignment on line 222 uses an assignment within an expression, which can reduce code clarity.
Refactor the code as follows:
- let entity = (path[0] = element.entity); + path[0] = element.entity; + let entity = path[0];📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.path[0] = element.entity; let entity = path[0];
🧰 Tools
🪛 Biome
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
89-89:
⚠️ Potential issueAvoid returning a value from a function with a
void
return typeThe
processRaycast
method is declared to returnvoid
, but it containsreturn null;
on line 89. Returning a value in avoid
function can lead to confusion and potential type errors.Apply this diff to correct the issue:
- return null; + return;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.return;
🧰 Tools
🪛 Biome
[error] 89-89: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
103-103: 🛠️ Refactor suggestion
Avoid assignments within expressions for better readability
Assigning multiple variables within an expression can make the code harder to read and maintain. The assignment on line 103 combines several assignments in one expression.
Refactor the code as follows to improve clarity:
- const element = (this._pressedElement = this._draggedElement = this._enteredElement); + this._pressedElement = this._draggedElement = this._enteredElement; + const element = this._pressedElement;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.this._pressedElement = this._draggedElement = this._enteredElement; const element = this._pressedElement;
🧰 Tools
🪛 Biome
[error] 103-103: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 103-103: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/ui/src/component/UIRenderer.ts (6)
134-135: 🛠️ Refactor suggestion
Eliminate the use of '@ts-ignore' by addressing type issues
The use of
// @ts-ignore
suppresses TypeScript's type checking, which may hide potential issues and reduce code maintainability. It's better to address the underlying type errors directly.Consider modifying the base class or adjusting type declarations to eliminate the need for
@ts-ignore
. For example, if_componentType
needs to be assigned, ensure it's properly declared in the base class or provide a setter method.
138-139: 🛠️ Refactor suggestion
Avoid accessing private properties directly
Accessing the
_onValueChanged
property directly may violate encapsulation principles, and using@ts-ignore
to suppress the error is not ideal. Consider providing a public or protected method to handle the color change callback.Refactor the
Color
class to expose a method or event for value changes, allowing you to subscribe without accessing private members.
179-182: 🛠️ Refactor suggestion
Replace '@ts-ignore' with proper type handling
The
@ts-ignore
directives here suppress errors related to_overrideUpdate
andscene._componentsManager
, possibly due to missing or incorrect type definitions.Ensure that
_overrideUpdate
andscene._componentsManager
are correctly defined and accessible. Update type declarations or class structures as necessary to eliminate the need for@ts-ignore
.
187-190: 🛠️ Refactor suggestion
Avoid suppressing TypeScript errors with '@ts-ignore'
Similar to previous comments, using
@ts-ignore
hides potential issues. Address the type errors directly for better code quality.Ensure that methods like
removeOnUpdateRenderers
are properly defined inscene._componentsManager
and that access levels permit their use.
215-215: 🛠️ Refactor suggestion
Avoid assignments within expressions for clarity
Using assignments within expressions can make the code less readable and potentially cause confusion. Consider refactoring the code for better clarity and maintainability.
Apply this diff to improve readability:
- rootCanvas && (rootCanvas._hierarchyDirty = true); + if (rootCanvas) { + rootCanvas._hierarchyDirty = true; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (rootCanvas) { rootCanvas._hierarchyDirty = true; }
🧰 Tools
🪛 Biome
[error] 215-215: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
292-293: 🛠️ Refactor suggestion
Properly clean up event handlers without accessing private members
Setting
_onValueChanged
tonull
directly may lead to unexpected behavior, especially if_onValueChanged
is a private property.Provide a public method in the
Color
class to remove the event listener, ensuring encapsulation and proper cleanup.- //@ts-ignore - this._color._onValueChanged = null; + this._color.offValueChanged(this._onColorChange);Committable suggestion skipped: line range outside the PR's diff.
packages/ui/src/component/advanced/Image.ts (2)
123-124: 🛠️ Refactor suggestion
Avoid repeated use of
@ts-ignore
; properly handle TypeScript errorsThere are several instances where
@ts-ignore
is used to suppress TypeScript errors (lines 123, 129, 147, 150, and 284). This practice can hide potential bugs and should be avoided. Please consider properly typing the properties or methods, or refactoring the code to handle the type errors safely.Also applies to: 129-130, 147-147, 150-150, 284-285
27-27:
⚠️ Potential issueFix typo in variable name
_tempUnit8Array
The variable name
_tempUnit8Array
seems to have a typo. It should be_tempUint8Array
.Apply this diff to correct the typo:
- private static _tempUnit8Array: Uint8ClampedArray = new Uint8ClampedArray(4); + private static _tempUint8Array: Uint8ClampedArray = new Uint8ClampedArray(4);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.private static _tempUint8Array: Uint8ClampedArray = new Uint8ClampedArray(4);
packages/ui/src/component/advanced/Label.ts (5)
207-210: 🛠️ Refactor suggestion
Avoid using
@ts-ignore
; resolve TypeScript errors insteadThe use of
@ts-ignore
suppresses TypeScript errors, which may hide underlying issues. It's better to address the root cause of the TypeScript errors to ensure type safety.Consider updating the code to resolve the TypeScript errors without suppressing them.
456-456: 🛠️ Refactor suggestion
Avoid assignments within conditional expressions
Assigning
maxY = Math.max(maxY, top)
within a conditional expression can reduce code clarity.Refactor the code as follows:
- i === firstLine && (maxY = Math.max(maxY, top)); + if (i === firstLine) { + maxY = Math.max(maxY, top); + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (i === firstLine) { maxY = Math.max(maxY, top); }
🧰 Tools
🪛 Biome
[error] 456-456: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
458-458: 🛠️ Refactor suggestion
Avoid assignments within conditional expressions
Assigning
minX = Math.min(minX, left)
within a conditional expression can be confusing.Refactor the code as follows:
- j === firstRow && (minX = Math.min(minX, left)); + if (j === firstRow) { + minX = Math.min(minX, left); + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (j === firstRow) { minX = Math.min(minX, left); }
🧰 Tools
🪛 Biome
[error] 458-458: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
228-228: 🛠️ Refactor suggestion
Avoid assignments within conditional expressions
Using an assignment within a conditional expression can be confusing and reduce code readability. It's clearer to separate the condition and the assignment.
Refactor the code as follows:
- this._subFont && (this._subFont = null); + if (this._subFont !== null) { + this._subFont = null; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (this._subFont !== null) { this._subFont = null; }
🧰 Tools
🪛 Biome
[error] 228-228: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
445-446: 🛠️ Refactor suggestion
Avoid assignments within conditional expressions
Assigning
firstRow = j
within a conditional expression can be misleading. It's better to use an explicitif
statement for clarity.Refactor the code as follows:
- firstRow < 0 && (firstRow = j); + if (firstRow < 0) { + firstRow = j; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (firstRow < 0) { firstRow = j; } const charRenderInfo = (charRenderInfos[renderElementCount++] = charRenderInfoPool.get());
🧰 Tools
🪛 Biome
[error] 445-445: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 446-446: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/ui/src/component/UICanvas.ts (7)
406-408:
⚠️ Potential issueAdd missing
break
statement to prevent fallthrough in switch caseThe
case
forComponentType.UIRenderer
is missing abreak
statement, causing it to unintentionally fall through to the next caseComponentType.UIInteractive
. This can lead to unexpected behavior.Apply this diff to fix the issue:
switch (componentType) { case ComponentType.UIRenderer: elements[depth] = component as unknown as IGraphics; ++depth; + break; case ComponentType.UIInteractive: if ((component as unknown as IGroupAble)._isGroupDirty) { tempGroupAbleList[groupAbleCount++] = component as unknown as IGroupAble; }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.case ComponentType.UIRenderer: elements[depth] = component as unknown as IGraphics; ++depth; break;
🧰 Tools
🪛 Biome
[error] 406-408: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
222-222: 🛠️ Refactor suggestion
Avoid assignments within variable declarations
Assigning a value within a variable declaration can reduce readability and may introduce unintended side effects. It's better to separate the assignments.
Apply the following diff to refactor the code:
- const renderElement = (this._renderElement = engine._renderElementPool.get()); + this._renderElement = engine._renderElementPool.get(); + const renderElement = this._renderElement;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.this._renderElement = engine._renderElementPool.get(); const renderElement = this._renderElement;
🧰 Tools
🪛 Biome
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
587-588:
⚠️ Potential issueAdd missing
break
statement to prevent fallthrough in switch caseThe
case
forCanvasRenderMode.ScreenSpaceOverlay
is missing abreak
statement, causing unintended fallthrough to the next case. This can lead to unexpected behavior.Apply this diff to fix the issue:
switch (preRealMode) { case CanvasRenderMode.ScreenSpaceOverlay: this._removeCanvasListener(); + break; case CanvasRenderMode.ScreenSpaceCamera: case CanvasRenderMode.WorldSpace: componentsManager.removeUICanvas(this, preRealMode === CanvasRenderMode.ScreenSpaceOverlay); break;
Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 587-588: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
437-437: 🛠️ Refactor suggestion
Avoid assignments within logical expressions
Assigning within a logical expression can reduce code clarity and introduce side effects. It's better to separate the assignment from the expression.
Apply this diff to refactor the code:
- child.isActive && (depth = this._walk(child, elements, depth)); + if (child.isActive) { + depth = this._walk(child, elements, depth); + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (child.isActive) { depth = this._walk(child, elements, depth); }
🧰 Tools
🪛 Biome
[error] 437-437: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
597-601:
⚠️ Potential issueAdd missing
break
statements to prevent fallthrough in switch casesThe
case
clauses forCanvasRenderMode.ScreenSpaceOverlay
andCanvasRenderMode.ScreenSpaceCamera
are missingbreak
statements, leading to unintended fallthrough. This can cause the code under subsequent cases to execute unintentionally.Apply this diff to fix the issue:
switch (curRealMode) { case CanvasRenderMode.ScreenSpaceOverlay: this._addCanvasListener(); + break; case CanvasRenderMode.ScreenSpaceCamera: this._adapterPoseInScreenSpace(); this._adapterSizeInScreenSpace(); + break; case CanvasRenderMode.WorldSpace: componentsManager.addUICanvas(this, curRealMode === CanvasRenderMode.ScreenSpaceOverlay); break;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.case CanvasRenderMode.ScreenSpaceOverlay: this._addCanvasListener(); break; case CanvasRenderMode.ScreenSpaceCamera: this._adapterPoseInScreenSpace(); this._adapterSizeInScreenSpace(); break;
🧰 Tools
🪛 Biome
[error] 597-598: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 599-601: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
159-159: 🛠️ Refactor suggestion
Avoid assignments within logical expressions
Assigning values within logical expressions can be confusing and may lead to unintended side effects. It's clearer to separate the assignment from the expression.
Apply the following diff to refactor the code:
- this._realRenderMode === CanvasRenderMode.ScreenSpaceOverlay && - (this.scene._componentsManager._overlayCanvasesSortingFlag = true); + if (this._realRenderMode === CanvasRenderMode.ScreenSpaceOverlay) { + this.scene._componentsManager._overlayCanvasesSortingFlag = true; + }Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 159-159: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
236-238:
⚠️ Potential issueWrap variable declarations in switch cases with braces
Variables declared inside a
case
clause are accessible in subsequent cases due to JavaScript's scoping rules, which can lead to unexpected behavior. To prevent this, wrap thecase
block in braces to create a new scope.Apply this diff to fix the issue:
switch (mode) { case CanvasRenderMode.ScreenSpaceOverlay: + { const { min, max } = renderer.bounds; if (min.x > width || max.x < 0 || min.y > height || max.y < 0) { continue; } + }Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 237-237: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
packages/design/src/ui/IUIElement.ts (1)
1-4: 🛠️ Refactor suggestion
Consider revising the API design for better encapsulation
The current design exposes implementation details through underscore-prefixed members in a public interface, which is unconventional. Consider separating the public and internal APIs.
Here's a suggested refactor:
-export interface IUIElement { - _onUIUpdateIndex: number; - _onUpdate(): void; +/** Public interface for UI elements */ +export interface IUIElement { + /** Updates the UI element state */ + update(): void; +} + +/** Internal interface for UI system management */ +export interface IInternalUIElement extends IUIElement { + readonly updateIndex: number; +}This approach:
- Separates public and internal APIs
- Makes the update index readonly to prevent external modifications
- Provides a cleaner public interface for consumers
- Maintains type safety for internal system management
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements./** Public interface for UI elements */ export interface IUIElement { /** Updates the UI element state */ update(): void; } /** Internal interface for UI system management */ export interface IInternalUIElement extends IUIElement { readonly updateIndex: number; }
packages/design/src/ui/IUICanvas.ts (3)
1-7: 🛠️ Refactor suggestion
Improve type safety and interface design
The interface has several areas that could be improved:
- Replace
any
types with specific types to enhance type safety and IDE support- Avoid mixing public and private members in interfaces
- Add JSDoc documentation
Consider refactoring the interface like this:
+ /** + * Represents a UI canvas that can be rendered in the scene. + */ export interface IUICanvas { - entity: any; - sortOrder: number; - _canvasIndex: number; - _renderElement: any; - _prepareRender(renderContext: any): void; + /** The entity associated with this canvas */ + entity: Entity; // Replace with actual entity type + /** Determines the rendering order of the canvas */ + sortOrder: number; + /** Prepares the canvas for rendering */ + prepareRender(renderContext: RenderContext): void; // Replace with actual context type } + /** + * Internal implementation details for UI canvas. + * Consider moving these to a separate implementation class. + */ + interface IUICanvasImpl extends IUICanvas { + canvasIndex: number; + renderElement: RenderElement; // Replace with actual element type }Committable suggestion skipped: line range outside the PR's diff.
2-5: 💡 Codebase verification
Based on the search results, I can now generate the final response:
Use
Entity
andRenderElement
types instead ofany
The interface properties should be typed as follows:
entity
should be typed asEntity
since it's consistently used across the UI system_renderElement
should be typed asRenderElement
as shown by its usage in UI canvas implementationsThe types are available in the codebase and using them will provide better type safety and IDE support.
🔗 Analysis chain
Consider using strict typing for entity and renderElement
The use of
any
type reduces type safety. This could lead to runtime errors that TypeScript could otherwise catch during compilation.Let's check how these types are used in the codebase:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for entity type definitions and usage echo "Searching for Entity type definitions..." ast-grep --pattern 'class $_ implements Entity' ast-grep --pattern 'interface Entity' echo "Searching for RenderElement type definitions..." ast-grep --pattern 'class $_ implements RenderElement' ast-grep --pattern 'interface RenderElement'Length of output: 140
Script:
#!/bin/bash # Let's try a broader search to understand the usage of these properties echo "Searching for entity usage..." rg "entity:" -A 2 -B 2 echo "\nSearching for _renderElement usage..." rg "_renderElement:" -A 2 -B 2 echo "\nSearching for imports in the file..." rg "import.*from" "packages/design/src/ui/IUICanvas.ts" echo "\nChecking if this is an interface definition..." rg "interface.*IUICanvas" "packages/design/src/ui/IUICanvas.ts"Length of output: 36919
Script:
#!/bin/bash # Let's search for the actual types used in implementations of IUICanvas echo "Searching for UICanvas class implementations..." rg "class.*implements.*IUICanvas" echo "\nSearching for RenderElement type usage..." rg "RenderElement" -A 2 -B 2 echo "\nSearching for Entity type usage..." rg "Entity" "packages/design/src/ui/IUICanvas.ts"Length of output: 33934
6-6: 💡 Codebase verification
Change
any
type toRenderContext
from '@galacean/engine-core'The
renderContext
parameter in_prepareRender
should use theRenderContext
type from the core package, as evidenced by:
- The
RenderContext
class is defined inpackages/core/src/RenderPipeline/RenderContext.ts
- The method is used in
UIUtils.ts
with an instance of thisRenderContext
class- The interface is already importing other types from the core package
🔗 Analysis chain
Add type definition for renderContext parameter
The
renderContext
parameter should have a specific type rather thanany
to ensure type safety and provide better IDE support.Let's check for existing render context types:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for RenderContext type definitions echo "Searching for RenderContext type definitions..." ast-grep --pattern 'interface RenderContext' ast-grep --pattern 'class $_ implements RenderContext' # Search for usage patterns echo "Searching for renderContext parameter usage..." rg -t ts "renderContext" -A 2 -B 2Length of output: 208
Script:
#!/bin/bash # Let's search more broadly for render context related types and usages echo "Searching for render context related types..." rg -t ts "type.*RenderContext|interface.*RenderContext|class.*RenderContext" -B 1 -A 3 echo "\nSearching for _prepareRender implementations..." ast-grep --pattern 'class $_ { $$$ _prepareRender($_) { $$$ } $$$ }' echo "\nSearching for files importing IUICanvas..." rg -t ts "import.*IUICanvas"Length of output: 1137
Script:
#!/bin/bash # Let's examine the RenderContext class in detail echo "Examining RenderContext class..." cat packages/core/src/RenderPipeline/RenderContext.ts # Let's also check how _prepareRender is used with RenderContext echo "\nChecking _prepareRender usage with RenderContext..." rg -t ts "_prepareRender.*renderContext" -B 2 -A 2Length of output: 3941
packages/core/src/2d/text/ITextRenderer.ts (1)
7-7: 🛠️ Refactor suggestion
Reconsider the underscore prefix in method name
The method
_getSubFont
uses an underscore prefix, which typically indicates a private or internal member. However, in TypeScript interfaces, all members are inherently public. Consider removing the underscore prefix to better align with TypeScript conventions.- _getSubFont(): SubFont; + getSubFont(): SubFont;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.getSubFont(): SubFont;
packages/ui/src/interface/IElement.ts (2)
4-6: 🛠️ Refactor suggestion
Add JSDoc documentation and consider architectural improvements.
Add comprehensive JSDoc documentation to explain:
- The purpose and responsibility of the
IElement
interface- The relationship between elements, entities, and canvas
- Each property's purpose and usage
Consider architectural improvements:
- Could we reduce coupling with
Entity
by using a more abstract interface?- Should we add methods for lifecycle management and state updates?
Example documentation:
+/** + * Represents a UI element that can be rendered on a canvas. + * Provides core properties for managing the element's relationship + * with its underlying entity and canvas. + */ export interface IElement { + /** The engine entity associated with this UI element */ entity: Entity; + /** The canvas this element is rendered on */ readonly canvas: UICanvas;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements./** * Represents a UI element that can be rendered on a canvas. * Provides core properties for managing the element's relationship * with its underlying entity and canvas. */ export interface IElement { /** The engine entity associated with this UI element */ entity: Entity; /** The canvas this element is rendered on */ readonly canvas: UICanvas;
8-12: 💡 Codebase verification
⚠️ Potential issueBased on the results, I can now generate the final response:
Consider using existing event patterns instead of exposing internal implementation details
The codebase already has established patterns for event handling, including:
- Proper event type enums (e.g.,
PointerEventType
,XRInputEventType
)- Type-safe event parameters
- Clean event registration/unregistration mechanisms
Suggested refactor:
export enum CanvasEventType { Update = 1, Resize = 2, // Add other relevant types } export interface IElement { readonly entity: Entity; readonly canvas: UICanvas; // Public event API addEventListener(type: CanvasEventType, listener: (entity: Entity) => void): void; removeEventListener(type: CanvasEventType, listener: (entity: Entity) => void): void; dispose(): void; }This aligns with:
- Existing event patterns in the codebase
- Proper encapsulation of implementation details
- Type-safe event handling
- Clear cleanup through dispose pattern
🔗 Analysis chain
Critical: Refactor internal implementation details and improve type safety.
Several concerns with the current implementation:
Internal implementation details shouldn't be exposed in the interface:
_indexInCanvas
,_isCanvasDirty
,_canvasListeningEntities
should be implementation details- Consider moving these to an abstract class or internal interface
Type safety issues:
- Avoid using
any
type- The
flag
parameter should use an enum or const union typePotential memory management issues:
- Storing entity references in
_canvasListeningEntities
could lead to memory leaks- Need clear cleanup mechanism for entity listeners
Here's a suggested refactor:
/** Defines valid canvas event types */ export enum CanvasEventType { Update = 1, Resize = 2, // ... add other event types } /** Type for canvas event parameters */ export type CanvasEventParam = { type: CanvasEventType; data: unknown; }; export interface IElement { entity: Entity; readonly canvas: UICanvas; // Public methods instead of exposed internals addEventListener(type: CanvasEventType, listener: (event: CanvasEventParam) => void): void; removeEventListener(type: CanvasEventType, listener: (event: CanvasEventParam) => void): void; dispose(): void; }Let's verify the usage of internal properties across the codebase:
The initial results show extensive usage of these internal properties across multiple components. Let's verify:
- How the flag numbers are defined/used
- Whether proper cleanup mechanisms exist
- The implementation of the unregister mechanism
This will help determine if the suggested refactor is necessary or if the current implementation already has safeguards in place.
The previous results don't show clear event type definitions or proper cleanup patterns. Let's verify:
- If there are existing event type enums/constants
- The implementation of the registration mechanism
- Any type definitions for events
This will help determine if we need to introduce new event types or if there's an existing pattern we should follow.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for direct access to internal properties rg "_indexInCanvas|_isCanvasDirty|_canvasListeningEntities|_canvasListener" --type ts # Look for potential memory leak patterns with entity references ast-grep --pattern 'class $_ implements IElement { $$$ _canvasListeningEntities: Entity[] = []; $$$ }'Length of output: 4697
Script:
#!/bin/bash # Check for event type usage patterns rg "flag.*=.*\d+" --type ts -A 2 # Check for cleanup/disposal patterns ast-grep --pattern 'dispose() { $$$ _unRegisterListener($$$) $$$ }' # Check Utils._unRegisterListener implementation ast-grep --pattern '_unRegisterListener($$$) { $$$ }'Length of output: 2009
Script:
#!/bin/bash # Check for event flag definitions and constants rg "enum.*Event|const.*Event" --type ts -A 5 # Check Utils class implementation for event handling ast-grep --pattern 'class Utils { $$$ _registerListener($$$) { $$$ } $$$ }' # Look for event type definitions fd -e ts -x grep -l "type.*Event"Length of output: 29878
packages/ui/src/interface/IGroupAble.ts (1)
6-12: 🛠️ Refactor suggestion
Consider encapsulating internal state and improving type safety.
The interface mixes public and private members, which could lead to maintenance issues:
- Properties prefixed with underscore suggest internal implementation details but are still publicly accessible
- Optional properties (
_globalAlpha
,_globalInteractive
) lack documentation explaining when they might be undefinedConsider this alternative structure:
export interface IGroupAble extends IElement { readonly group: UIGroup; - _indexInGroup: number; - _groupListeningEntities: Entity[]; - _isGroupDirty: boolean; - _onUIUpdateIndex?: number; - _globalAlpha?: number; - _globalInteractive?: boolean; + /** @internal */ + private readonly _state: { + indexInGroup: number; + groupListeningEntities: Entity[]; + isGroupDirty: boolean; + onUIUpdateIndex?: number; + }; + + /** + * Global alpha value for the group. + * @description Controls the opacity of all elements in the group. + */ + globalAlpha: number; + + /** + * Global interactive state for the group. + * @description Determines if the group and its children can receive input events. + */ + globalInteractive: boolean;Committable suggestion skipped: line range outside the PR's diff.
packages/ui/src/component/interactive/transition/ScaleTransition.ts (1)
18-24: 🛠️ Refactor suggestion
Consider enhancing transition smoothness and adding validation.
A few suggestions for improvement:
- Consider using easing functions for smoother transitions
- Add validation for the weight parameter (should be between 0 and 1)
- Consider adding scale change event emission for animation tracking
protected override _updateCurrentValue(srcValue: number, destValue: number, weight: number): void { + if (weight < 0 || weight > 1) { + throw new Error('Weight must be between 0 and 1'); + } - this._currentValue = weight >= 1 ? destValue : (destValue - srcValue) * weight + srcValue; + // Apply easing function for smoother transition + const easedWeight = this._easeInOutQuad(weight); + this._currentValue = weight >= 1 ? destValue : (destValue - srcValue) * easedWeight + srcValue; } + private _easeInOutQuad(t: number): number { + return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2; + } protected override _applyValue(value: number): void { this._target.entity.transform.setScale(value, value, value); + this._target.entity.emit('scaleChange', value); }Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/2d/index.ts (1)
3-5: 💡 Codebase verification
Classes are not implementing the ISpriteAssembler interface
The sprite assembler classes (
SimpleSpriteAssembler
,SlicedSpriteAssembler
, andTiledSpriteAssembler
) are not implementing theISpriteAssembler
interface that exists in the codebase. This could lead to type safety issues and inconsistent implementations.
./packages/core/src/2d/assembler/SimpleSpriteAssembler.ts
: Class declared without interface implementation./packages/core/src/2d/assembler/SlicedSpriteAssembler.ts
: Class declared without interface implementation./packages/core/src/2d/assembler/TiledSpriteAssembler.ts
: Class declared without interface implementation🔗 Analysis chain
Verify interface implementation in sprite assembler classes.
The new sprite assembler classes should implement the
ISpriteAssembler
interface.
Let me try a different approach to verify the interface implementation.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that sprite assembler classes implement ISpriteAssembler interface # Check implementation in each assembler class for file in SimpleSpriteAssembler SlicedSpriteAssembler TiledSpriteAssembler; do echo "Checking $file..." ast-grep --pattern 'class $file implements $interface { $$$ }' ./packages/core/src/2d/assembler/$file.ts doneLength of output: 851
Script:
#!/bin/bash # Search for the class declarations and their inheritance/implementation echo "=== SimpleSpriteAssembler ===" rg "class SimpleSpriteAssembler" ./packages/core/src/2d/assembler -A 2 echo -e "\n=== SlicedSpriteAssembler ===" rg "class SlicedSpriteAssembler" ./packages/core/src/2d/assembler -A 2 echo -e "\n=== TiledSpriteAssembler ===" rg "class TiledSpriteAssembler" ./packages/core/src/2d/assembler -A 2 # Also search for ISpriteAssembler interface definition echo -e "\n=== ISpriteAssembler interface ===" rg "interface ISpriteAssembler" ./packages/core/src/2d -A 2Length of output: 1861
packages/ui/src/component/interactive/transition/SpriteTransition.ts (1)
9-32: 🛠️ Refactor suggestion
Improve type safety and reduce code duplication in _destroy method.
Several concerns in the cleanup logic:
- Multiple
@ts-ignore
comments without explanation- Repetitive cleanup pattern for each state
- No error handling for reference counting
Consider refactoring to:
- override _destroy(): void { - if (this._normal) { - // @ts-ignore - this._normal._addReferCount(-1); - this._normal = null; - } - if (this._hover) { - // @ts-ignore - this._hover._addReferCount(-1); - this._hover = null; - } - if (this._pressed) { - // @ts-ignore - this._pressed._addReferCount(-1); - this._pressed = null; - } - if (this._disabled) { - // @ts-ignore - this._disabled._addReferCount(-1); - this._disabled = null; - } - this._initialValue = this._currentValue = this._finalValue = null; - this._target = null; + private _cleanupSprite(sprite: Sprite | null): void { + if (sprite) { + try { + (sprite as any)._addReferCount(-1); + } catch (e) { + console.warn('Failed to cleanup sprite reference:', e); + } + } + } + + override _destroy(): void { + const states = ['_normal', '_hover', '_pressed', '_disabled']; + states.forEach(state => { + this._cleanupSprite(this[state]); + this[state] = null; + }); + + this._initialValue = this._currentValue = this._finalValue = null; + this._target = null; }Also, please add a comment explaining why the type assertion is necessary for
_addReferCount
.Committable suggestion skipped: line range outside the PR's diff.
packages/ui/src/component/advanced/Button.ts (2)
32-35: 🛠️ Refactor suggestion
Improve cleanup implementation in onDestroy.
Similar to the removeClicked method, the cleanup could be more explicit and effective.
Consider this implementation:
override onDestroy(): void { super.onDestroy(); - this._listeners.findAndRemove((value) => (value.destroyed = true)); + // Clear all listeners + this._listeners.length = 0; }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.override onDestroy(): void { super.onDestroy(); // Clear all listeners this._listeners.length = 0; }
🧰 Tools
🪛 Biome
[error] 34-34: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
11-21: 🛠️ Refactor suggestion
Improve listener management implementation.
The current implementation has two issues:
- Using assignment within the findAndRemove predicate reduces code clarity
- Marking listeners as destroyed without removing them could lead to memory leaks
Consider this cleaner implementation:
removeClicked(listener: (event: PointerEventData) => void): void { - this._listeners.findAndRemove((value) => (value.fn === listener ? (value.destroyed = true) : false)); + this._listeners.findAndRemove((value) => { + if (value.fn === listener) { + value.destroyed = true; + return true; // Remove the listener instead of just marking it + } + return false; + }); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.addClicked(listener: (event: PointerEventData) => void): void { this._listeners.push({ fn: listener }); } /** * Remove a listening function of click. * @param listener - The listening function */ removeClicked(listener: (event: PointerEventData) => void): void { this._listeners.findAndRemove((value) => { if (value.fn === listener) { value.destroyed = true; return true; // Remove the listener instead of just marking it } return false; }); }
🧰 Tools
🪛 Biome
[error] 20-20: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIUtils.ts (1)
15-15: 🛠️ Refactor suggestion
Add input validation for method parameters
The render method lacks validation for its input parameters. This could lead to runtime errors if invalid inputs are provided.
static render(engine: Engine, uiCanvases: DisorderedArray<IUICanvas>): void { + if (!engine || !uiCanvases) { + throw new Error("Engine and uiCanvases parameters are required"); + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.static render(engine: Engine, uiCanvases: DisorderedArray<IUICanvas>): void { if (!engine || !uiCanvases) { throw new Error("Engine and uiCanvases parameters are required"); }
packages/ui/src/component/interactive/transition/ColorTransition.ts (2)
62-64:
⚠️ Potential issueAdd null check in _applyValue method.
The
_applyValue
method directly assigns to_target.color
without checking if_target
is defined.protected override _applyValue(value: Color): void { - this._target.color = value; + if (this._target) { + this._target.color = value; + } }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.protected override _applyValue(value: Color): void { if (this._target) { this._target.color = value; } }
22-29: 💡 Codebase verification
⚠️ Potential issueRemove @ts-ignore comments by properly typing _onValueChanged property
The
Color
class already has the_onValueChanged
property properly defined as() => void
internally. The type safety issues can be resolved by:
- Removing the
@ts-ignore
comments- Ensuring the callback methods (
_onNormalValueChanged
, etc.) match the() => void
type signatureExample fix:
this._normal._onValueChanged = this._onNormalValueChanged; this._hover._onValueChanged = this._onHoverValueChanged; this._pressed._onValueChanged = this._onPressedValueChanged; this._disabled._onValueChanged = this._onDisabledValueChanged;🔗 Analysis chain
Address type safety issues instead of using @ts-ignore.
Multiple
@ts-ignore
comments suggest underlying type safety issues. Consider addressing these properly by:
- Adding proper type definitions for the
_onValueChanged
property- Extending the Color class if needed
Let's verify if the Color class can be extended or modified:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for Color class definition to understand its structure ast-grep --pattern 'class Color { $$$ }'Length of output: 44
Script:
#!/bin/bash # Let's try to find the Color class definition with ripgrep rg "class Color" -A 10 # Also search for any type definitions related to Color rg "type.*Color" --type=typescript # Search for Color-related files fd -e ts -e tsx "Color" # Search for _onValueChanged usage rg "_onValueChanged" -A 2Length of output: 38995
Script:
#!/bin/bash # Let's examine the Color class implementation to understand its type structure cat packages/math/src/Color.tsLength of output: 9743
packages/core/src/ComponentsDependencies.ts (2)
14-14: 🛠️ Refactor suggestion
Fix type and consider adding documentation.
- Use
boolean
instead ofBoolean
for the map value type- Consider adding JSDoc to document the purpose of this map
- Consider making the value type readonly since it's internal state
- static _inheritedMap = new Map<ComponentConstructor, Boolean>(); + /** Map tracking which components should be inherited by child entities */ + private static _inheritedMap = new Map<ComponentConstructor, boolean>();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements./** Map tracking which components should be inherited by child entities */ private static _inheritedMap = new Map<ComponentConstructor, boolean>();
🧰 Tools
🪛 Biome
[error] 14-14: Don't use 'Boolean' as a type.
Use lowercase primitives for consistency.
Safe fix: Use 'boolean' instead(lint/complexity/noBannedTypes)
97-101: 🛠️ Refactor suggestion
Enhance type safety and add validation.
The decorator should validate its usage and prevent potential memory leaks.
export function markAsInherited() { return function <T extends ComponentConstructor>(target: T): void { + if (!(target.prototype instanceof Component)) { + throw new Error("markAsInherited can only be used on Component classes"); + } + if (ComponentsDependencies._inheritedMap.has(target)) { + console.warn(`Component ${target.name} is already marked as inherited`); + return; + } ComponentsDependencies._inheritedMap.set(target, true); }; }Additionally, consider adding a cleanup mechanism to prevent memory leaks when components are removed or unregistered.
Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/2d/assembler/SlicedSpriteAssembler.ts (2)
65-69: 🛠️ Refactor suggestion
Improve code readability by separating combined assignments
The combined assignment statements make the code harder to read and maintain. This was previously discussed but still needs to be addressed.
Apply this diff to improve readability:
- (row[0] = expectWidth * left * widthScale), (row[1] = row[2] = fixedLeft * widthScale); - row[3] = width - expectWidth * (1 - right) * widthScale; + row[0] = expectWidth * left * widthScale; + row[1] = fixedLeft * widthScale; + row[2] = fixedLeft * widthScale; + row[3] = width - expectWidth * (1 - right) * widthScale; - (column[0] = expectHeight * bottom * heightScale), (column[1] = column[2] = fixedBottom * heightScale); - column[3] = height - expectHeight * (1 - top) * heightScale; + column[0] = expectHeight * bottom * heightScale; + column[1] = fixedBottom * heightScale; + column[2] = fixedBottom * heightScale; + column[3] = height - expectHeight * (1 - top) * heightScale;Also applies to: 74-78
169-173: 🛠️ Refactor suggestion
Maintain consistent code style in getUVByLocalPosition
The same combined assignment pattern is used here. For consistency with the earlier suggestions, these should also be separated.
Apply this diff:
- (row[0] = expectWidth * left * widthScale), (row[1] = row[2] = fixedLeft * widthScale); - row[3] = width - expectWidth * (1 - right) * widthScale; + row[0] = expectWidth * left * widthScale; + row[1] = fixedLeft * widthScale; + row[2] = fixedLeft * widthScale; + row[3] = width - expectWidth * (1 - right) * widthScale;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.row[0] = expectWidth * left * widthScale; row[1] = fixedLeft * widthScale; row[2] = fixedLeft * widthScale; row[3] = width - expectWidth * (1 - right) * widthScale; } else { (row[0] = expectWidth * left), (row[1] = fixedLeft), (row[2] = width - fixedRight); row[3] = width - expectWidth * (1 - right);
packages/core/src/input/pointer/PointerManager.ts (2)
151-157:
⚠️ Potential issuePotential race condition in drag event processing
While the comment indicates that drag must be processed first, there's no synchronization mechanism to prevent race conditions when multiple emitters process events concurrently.
Consider adding a synchronization mechanism or using a queue to ensure proper event ordering:
- emitters.forEach((emitter) => { - emitter.processDrag(pointer); - }); + // Process drag events sequentially + for (const emitter of emitters) { + await emitter.processDrag(pointer); + }Committable suggestion skipped: line range outside the PR's diff.
231-262: 🛠️ Refactor suggestion
Add error boundaries for event processing
The event handling logic should include error handling to prevent cascading failures.
Consider wrapping the event processing in try-catch blocks:
switch (event.type) { case "pointerdown": { + try { const button = event.button; _downList.add(button); _downMap[button] = frameCount; pointer._downList.add(button); pointer._downMap[button] = frameCount; pointer._frameEvents |= PointerEventType.Down; pointer.phase = PointerPhase.Down; + } catch (error) { + console.error('Error processing pointer down event:', error); + pointer.phase = PointerPhase.Leave; // Fail safe + } break; }Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/ComponentsManager.ts (2)
185-188:
⚠️ Potential issueFix incorrect array reference in addOnUpdateUIElement
There's a critical bug where the method uses
this._onUpdateRenderers.length
instead ofthis._onUpdateUIElements.length
for the index.Apply this diff to fix the bug:
- element._onUIUpdateIndex = this._onUpdateRenderers.length; + element._onUIUpdateIndex = this._onUpdateUIElements.length;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.addOnUpdateUIElement(element: IUIElement): void { element._onUIUpdateIndex = this._onUpdateUIElements.length; this._onUpdateUIElements.add(element); }
272-276: 🛠️ Refactor suggestion
Align UI update pattern with other component updates
The
callUIOnUpdate
method doesn't follow the consistent pattern of managing indices during iteration. This could lead to stale indices.Apply this diff to align with other update methods:
callUIOnUpdate(deltaTime: number): void { - this._onUpdateUIElements.forEach((element: IUIElement) => { - element._onUpdate(); - }); + this._onUpdateUIElements.forEach( + (element: IUIElement) => { + element._onUpdate(); + }, + (element: IUIElement, index: number) => { + element._onUIUpdateIndex = index; + } + ); }Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/2d/text/TextUtils.ts (3)
98-103:
⚠️ Potential issueAdd parameter validation
The method should validate its numeric parameters to prevent potential rendering issues.
Add this validation at the start of the method:
static measureTextWithWrap( renderer: ITextRenderer, rendererWidth: number, rendererHeight: number, lineSpacing: number ): TextMetrics { + if (rendererWidth <= 0 || rendererHeight <= 0 || lineSpacing < 0) { + throw new Error('Invalid dimensions: width and height must be positive, lineSpacing must be non-negative'); + } const subFont = renderer._getSubFont();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.static measureTextWithWrap( renderer: ITextRenderer, rendererWidth: number, rendererHeight: number, lineSpacing: number ): TextMetrics { if (rendererWidth <= 0 || rendererHeight <= 0 || lineSpacing < 0) { throw new Error('Invalid dimensions: width and height must be positive, lineSpacing must be non-negative'); } const subFont = renderer._getSubFont();
98-103: 💡 Codebase verification
Tests need to be updated with new method parameters
The test file is using outdated method signatures for both
measureTextWithWrap
andmeasureTextWithoutWrap
. All production code has been properly updated, but the test file needs to be fixed. Here are the issues:
- In
measureTextWithWrap
test cases, the method is called without required parameters:rendererWidth
,rendererHeight
, andlineSpacing
- In
measureTextWithoutWrap
test cases, the method is called without required parameters:rendererHeight
andlineSpacing
The test renderer objects already have these values set up:
width
andheight
properties are set before each testlineSpacing
can be derived from the expectedlineHeight
assertions (27)🔗 Analysis chain
Verify all callers are updated with new parameters
The signature changes for both measurement methods require verification that all calling code has been updated accordingly.
Also applies to: 275-275
Let me analyze the search results:
For
measureTextWithWrap
, there are calls in:
packages/ui/src/component/advanced/Label.ts
- Correctly provides all parameters:this, rendererWidth, rendererHeight, this._lineSpacing
packages/core/src/2d/text/TextRenderer.ts
- Correctly provides all parameters (using_pixelsPerUnit
scaling)tests/src/core/2d/text/TextUtils.test.ts
- Test calls appear to be using old signatureFor
measureTextWithoutWrap
, there are calls in:
packages/ui/src/component/advanced/Label.ts
- Correctly provides all parameters:this, rendererHeight, this._lineSpacing
packages/core/src/2d/text/TextRenderer.ts
- Correctly provides all parameters (using_pixelsPerUnit
scaling)tests/src/core/2d/text/TextUtils.test.ts
- Test calls appear to be using old signatureLet me verify the test file to see if it's outdated:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for all calls to these methods echo "Searching for measureTextWithWrap calls:" rg -A 2 "measureTextWithWrap\(" --type ts echo -e "\nSearching for measureTextWithoutWrap calls:" rg -A 2 "measureTextWithoutWrap\(" --type tsLength of output: 7755
Script:
#!/bin/bash # Check the test file content around the method calls cat tests/src/core/2d/text/TextUtils.test.tsLength of output: 14944
275-275:
⚠️ Potential issueAdd parameter validation
Similar to measureTextWithWrap, this method should validate its numeric parameters.
Add this validation at the start of the method:
static measureTextWithoutWrap( renderer: ITextRenderer, rendererHeight: number, lineSpacing: number ): TextMetrics { + if (rendererHeight <= 0 || lineSpacing < 0) { + throw new Error('Invalid dimensions: height must be positive, lineSpacing must be non-negative'); + } const subFont = renderer._getSubFont();Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/2d/assembler/TiledSpriteAssembler.ts (2)
81-86:
⚠️ Potential issueReplace global isNaN with Number.isNaN
Using the global
isNaN
is unsafe as it performs type coercion. UseNumber.isNaN
instead for more predictable behavior.- if (isNaN(cUV.get(doubleJ)) || isNaN(cUV.get(doubleJ + 1))) { + if (Number.isNaN(cUV.get(doubleJ)) || Number.isNaN(cUV.get(doubleJ + 1))) { continue; } for (let i = 0; i < rowLength; i++) { const doubleI = i << 1; - if (isNaN(rUV.get(doubleI)) || isNaN(rUV.get(doubleI + 1))) { + if (Number.isNaN(rUV.get(doubleI)) || Number.isNaN(rUV.get(doubleI + 1))) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (Number.isNaN(cUV.get(doubleJ)) || Number.isNaN(cUV.get(doubleJ + 1))) { continue; } for (let i = 0; i < rowLength; i++) { const doubleI = i << 1; if (Number.isNaN(rUV.get(doubleI)) || Number.isNaN(rUV.get(doubleI + 1))) {
🧰 Tools
🪛 Biome
[error] 81-81: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
[error] 81-81: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
[error] 86-86: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
[error] 86-86: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
422-444:
⚠️ Potential issueAdd block scoping to switch cases
Variables declared in switch cases can leak into other cases. Wrap the declarations in blocks to prevent this.
switch (rType) { case TiledType.Compressed: { - const scale = width / fixedLR; - rPos.add(expectWidth * left * scale), rPos.add(fixedL * scale); - rPos.add(width - expectWidth * (1 - right) * scale); - rUV.add(spriteUV0.x), rUV.add(spriteUV1.x), rUV.add(spriteUV2.x), rUV.add(spriteUV3.x); + { + const scale = width / fixedLR; + rPos.add(expectWidth * left * scale), rPos.add(fixedL * scale); + rPos.add(width - expectWidth * (1 - right) * scale); + rUV.add(spriteUV0.x), rUV.add(spriteUV1.x), rUV.add(spriteUV2.x), rUV.add(spriteUV3.x); + } break; } case TiledType.WithTiled: { - const uv1 = spriteUV1.x; - const uv2 = spriteUV2.x; - const repeatWidth = (width - fixedLR) / rTiledCount; + { + const uv1 = spriteUV1.x; + const uv2 = spriteUV2.x; + const repeatWidth = (width - fixedLR) / rTiledCount;Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 422-422: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 434-434: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 435-435: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 436-436: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
packages/core/src/Entity.ts (1)
613-622: 🛠️ Refactor suggestion
Fix assignment in expression
The modification listener management is good, but the assignment in expression should be split for better readability.
- (this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange); + if (!this._updateFlagManager) { + this._updateFlagManager = new UpdateFlagManager(); + } + this._updateFlagManager.addListener(onChange);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements._registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { if (!this._updateFlagManager) { this._updateFlagManager = new UpdateFlagManager(); } this._updateFlagManager.addListener(onChange); } /** * @internal */ _unRegisterModifyListener(onChange: (flag: EntityModifyFlags) => void): void { this._updateFlagManager?.removeListener(onChange); }
🧰 Tools
🪛 Biome
[error] 614-614: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
🛑 Comments failed to post (76)
packages/ui/src/component/UITransform.ts (1)
32-41:
⚠️ Potential issueAvoid using
@ts-ignore
to suppress TypeScript errorsThe use of
@ts-ignore
comments in the constructor to access private or protected properties can lead to maintenance issues and hides potential type safety problems. Specifically, accessing:
entity._transform.destroy();
- Setting
entity._transform = this;
- Assigning to
entity._inverseWorldMatFlag
- Binding
_onValueChanged
callbacks for_size
and_pivot
This practice violates encapsulation principles and may cause unexpected behavior if internal implementations of these classes change.
Consider the following actions:
- Expose necessary functionality through public APIs: Modify the
Entity
,Transform
, andVector2
classes to provide public methods or properties that allow the required interactions.- Refactor the design: Reevaluate the class interactions to ensure that they adhere to object-oriented design principles without needing to access internal properties.
This approach maintains type safety and improves code maintainability.
packages/ui/src/component/interactive/transition/Transition.ts (4)
25-67: 🛠️ Refactor suggestion
Avoid comparing object references in setters; use appropriate equality checks instead
In the setters for
normal
,pressed
,hover
, anddisabled
(lines 25-67), comparing objects using!==
may not accurately detect changes for object types likeSprite
orColor
, as it checks reference equality rather than value equality. This could lead to missed updates when the object's properties change but the reference remains the same. Consider implementing proper equality checks to ensure changes are detected correctly.You can modify the comparison to use a deep equality check or implement an equality method for
TransitionValueType
. For example:-if (preNormal !== value) { +if (!this._areValuesEqual(preNormal, value)) {And add a helper method
_areValuesEqual
:private _areValuesEqual(a: T, b: T): boolean { if (typeof a === 'number' && typeof b === 'number') { return a === b; } else if (a instanceof Color && b instanceof Color) { return a.equals(b); } else if (a instanceof Sprite && b instanceof Sprite) { return a === b; // Update this logic if necessary to compare Sprites } return false; }
151-162:
⚠️ Potential issueAdd a default case in '_getValueByState' to prevent returning undefined
In the
_getValueByState
method (lines 151-162), there is no default case in the switch statement. If an unexpectedInteractiveState
value is passed, the method may returnundefined
, leading to potential runtime errors when the value is used elsewhere. To enhance robustness, add a default case to handle unexpected states.Apply the following modification:
private _getValueByState(state: InteractiveState): T { switch (state) { case InteractiveState.Normal: return this.normal; case InteractiveState.Pressed: return this.pressed; case InteractiveState.Hover: return this.hover; case InteractiveState.Disable: return this.disabled; + default: + return this.normal; // Or handle appropriately based on your application's logic } }Alternatively, you could throw an error for unrecognized states to catch issues during development:
default: throw new Error(`Unhandled InteractiveState: ${state}`);
134-143: 🛠️ Refactor suggestion
Avoid using '// @ts-ignore'; properly handle TypeScript type checks
In the
_onStateValueDirty
method (lines 134-143), suppressing TypeScript errors with// @ts-ignore
is discouraged, as it can conceal underlying issues. Instead, use type assertions or type guards to handle cases wherepreValue
andcurValue
might be instances ofReferResource
.Modify the code to include type checks without suppressing TypeScript errors:
-// @ts-ignore -preValue instanceof ReferResource && preValue._addReferCount(-1); -// @ts-ignore -curValue instanceof ReferResource && curValue._addReferCount(1); +if (preValue instanceof ReferResource) { + preValue._addReferCount(-1); +} +if (curValue instanceof ReferResource) { + curValue._addReferCount(1); +}Ensure that
TransitionValueType
is correctly defined to include types that may extendReferResource
, or adjust the type casting as needed.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.protected _onStateValueDirty(state: InteractiveState, preValue: T, curValue: T): void { if (preValue instanceof ReferResource) { preValue._addReferCount(-1); } if (curValue instanceof ReferResource) { curValue._addReferCount(1); } if (this._finalState === state) { this._finalValue = curValue; this._updateValue(); } }
85-94:
⚠️ Potential issueFix potential division by zero in duration setter
In the
set duration(value: number)
method (lines 85-94), ifpreDuration
is zero and_countDown > 0
, the calculationthis._countDown / preDuration
will result in a division by zero error. This can cause runtime exceptions and unexpected behavior. Ensure thatpreDuration
is not zero before performing the division to prevent this issue.Apply the following fix:
const preDuration = this._duration; if (preDuration !== value) { this._duration = value; if (this._countDown > 0) { + if (preDuration !== 0) { this._countDown = value * (1 - this._countDown / preDuration); + } else { + this._countDown = value; + } this._updateValue(); } }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (value < 0) value = 0; const preDuration = this._duration; if (preDuration !== value) { this._duration = value; if (this._countDown > 0) { if (preDuration !== 0) { this._countDown = value * (1 - this._countDown / preDuration); } else { this._countDown = value; } this._updateValue(); } } }
packages/ui/src/Utils.ts (5)
133-133:
⚠️ Potential issueAvoid assignments within expressions for better clarity
For improved readability, refactor the assignment to be explicit.
Apply this diff:
- replaced && (replaced._indexInCanvas = element._indexInCanvas); + if (replaced) { + replaced._indexInCanvas = element._indexInCanvas; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (replaced) { replaced._indexInCanvas = element._indexInCanvas; }
🧰 Tools
🪛 Biome
[error] 133-133: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
100-100:
⚠️ Potential issueAvoid assignments within expressions for better clarity
Using assignments within expressions can reduce code readability. Refactor the assignment to make it explicit.
Apply this diff:
- replaced && (replaced._indexInGroup = element._indexInGroup); + if (replaced) { + replaced._indexInGroup = element._indexInGroup; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (replaced) { replaced._indexInGroup = element._indexInGroup; }
🧰 Tools
🪛 Biome
[error] 100-100: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
122-122:
⚠️ Potential issueAvoid assignments within expressions for better clarity
Assignments within expressions can be confusing. It's better to make the assignment explicit.
Apply this diff:
- replaced && (replaced._indexInGroup = element._indexInGroup); + if (replaced) { + replaced._indexInGroup = element._indexInGroup; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (replaced) { replaced._indexInGroup = element._indexInGroup; }
🧰 Tools
🪛 Biome
[error] 122-122: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
135-135:
⚠️ Potential issueAvoid assignments within expressions for better clarity
Making assignments explicit enhances code clarity.
Apply this diff:
- isGraphics && (preCanvas._hierarchyDirty = true); + if (isGraphics) { + preCanvas._hierarchyDirty = true; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (isGraphics) { preCanvas._hierarchyDirty = true; }
🧰 Tools
🪛 Biome
[error] 135-135: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
79-79:
⚠️ Potential issueAvoid assignments within expressions for better clarity
Using assignments within expressions can make the code harder to read and may lead to confusion. It's recommended to refactor the assignment to make it more explicit.
Apply this diff to improve clarity:
- replaced && (replaced._indexInCanvas = element._indexInCanvas); + if (replaced) { + replaced._indexInCanvas = element._indexInCanvas; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (replaced) { replaced._indexInCanvas = element._indexInCanvas; }
🧰 Tools
🪛 Biome
[error] 79-79: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/input/pointer/emitter/PhysicsPointerEventEmitter.ts (1)
61-61: 🛠️ Refactor suggestion
Avoid assignments within expressions to improve readability
Assigning variables within an expression can reduce code readability and may introduce unintended side effects. It's better to separate assignments into individual statements.
Apply this diff to improve clarity:
- const entity = (this._pressedEntity = this._draggedEntity = this._enteredEntity); + this._pressedEntity = this._draggedEntity = this._enteredEntity; + const entity = this._pressedEntity;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.this._pressedEntity = this._draggedEntity = this._enteredEntity; const entity = this._pressedEntity;
🧰 Tools
🪛 Biome
[error] 61-61: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 61-61: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/ui/src/component/UIGroup.ts (4)
90-94:
⚠️ Potential issueAvoid using
@ts-ignore
; ensure TypeScript compatibility instead.Using
@ts-ignore
suppresses TypeScript errors, potentially hiding real issues. Theoverride
modifier and direct assignment to_componentType
might not be recognized in your current TypeScript configuration. Ensure that your project uses a compatible TypeScript version (4.3 or higher) and adjust access modifiers or class structures to avoid the need for@ts-ignore
.
148-149:
⚠️ Potential issueRemove
@ts-ignore
and verify method overriding correctly.The use of
@ts-ignore
before theoverride
keyword suppresses potential TypeScript errors. Ensure that the base classComponent
has the method_onEnableInScene
and that you're correctly overriding it. Updating your TypeScript version or compiler options might resolve this without ignoring errors.
184-190:
⚠️ Potential issueUse bitwise operations when checking flags in
_canvasListener
.Similar to the previous issue, the flag check should use bitwise operations to handle multiple flags correctly.
Apply this diff:
- if (flag === EntityModifyFlags.Parent) { + if ((flag & EntityModifyFlags.Parent) !== 0) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements._canvasListener(flag: number): void { if (this._isCanvasDirty) return; if ((flag & EntityModifyFlags.Parent) !== 0) { Utils._onCanvasDirty(this, this._canvas); Utils._onGroupDirty(this, this._group); } }
174-178:
⚠️ Potential issueUse bitwise operations when checking flags in
_groupListener
.The current flag check uses strict equality, which may fail if multiple flags are set simultaneously. Modify the condition to use bitwise operations to correctly detect flag combinations.
Apply this diff to fix the issue:
- if (flag === EntityModifyFlags.Parent || flag === EntityUIModifyFlags.UIGroupEnableInScene) { + if ((flag & (EntityModifyFlags.Parent | EntityUIModifyFlags.UIGroupEnableInScene)) !== 0) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (this._isGroupDirty) return; if ((flag & (EntityModifyFlags.Parent | EntityUIModifyFlags.UIGroupEnableInScene)) !== 0) { Utils._onGroupDirty(this, this._group); } }
packages/ui/src/component/interactive/UIInteractive.ts (4)
151-152:
⚠️ Potential issueAvoid accessing private members of other classes
Accessing
_componentsManager
fromthis.scene
in lines 151 and 162 is likely accessing a private member of theScene
class. Accessing private properties breaks encapsulation and can lead to maintenance issues.Consider using public methods or properties provided by
Scene
to register and unregister UI elements. If such methods do not exist, it may be necessary to update theScene
class to expose the required functionality.Also applies to: 162-163
176-183: 🛠️ Refactor suggestion
Ensure
_onUpdate
properly updates interaction stateCurrently,
_onUpdate
only calls_updateGlobalInteractive()
. To ensure that interaction states are consistently updated, consider also calling_updateState(false)
within this method.Apply this change:
_onUpdate(): void { this._updateGlobalInteractive(); + this._updateState(false); }
This guarantees that the state reflects any changes in interactivity each frame.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements./** * @internal */ _onUpdate(): void { this._updateGlobalInteractive(); this._updateState(false); } /**
146-149:
⚠️ Potential issueAddress the use of multiple
@ts-ignore
commentsThe repeated use of
// @ts-ignore
suggests that TypeScript errors are being suppressed. This can hide potential issues and reduce type safety. Investigate and resolve the underlying TypeScript errors instead of suppressing them.For example, if the
_onEnableInScene
method is not defined in the parent class or has a different access modifier, consider updating the method access or properly extending the class.- // @ts-ignore override _onEnableInScene(): void { - // @ts-ignore super._onEnableInScene(); // ... }Ensure that
_onEnableInScene
is correctly declared in the parent classScript
and that it can be overridden.Committable suggestion skipped: line range outside the PR's diff.
104-109: 🛠️ Refactor suggestion
Refine generic type constraints in
addTransition
methodIn the
addTransition
method, the generic typeT
is constrained asnew () => Transition
, but this may not correctly capture subclasses ofTransition
with their own constructor signatures.Modify the generic constraint to accept any constructor of a
Transition
subclass:-addTransition<T extends new () => Transition>(type: T): InstanceType<T> { +addTransition<T extends Transition>(type: { new (...args: any[]): T }): T { const transition = new type(); // ... }Similarly, update the return type to
T
for clarity.Committable suggestion skipped: line range outside the PR's diff.
packages/ui/src/input/UIPointerEventEmitter.ts (8)
103-103:
⚠️ Potential issueAvoid assignment within an expression for clarity
At line 103, there is an assignment within an expression:
const element = (this._pressedElement = this._draggedElement = this._enteredElement);This can make the code harder to read and understand. It's better to separate the assignments for improved readability.
Apply this diff to refactor the code:
- const element = (this._pressedElement = this._draggedElement = this._enteredElement); + this._pressedElement = this._draggedElement = this._enteredElement; + const element = this._pressedElement;🧰 Tools
🪛 Biome
[error] 103-103: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 103-103: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
222-222:
⚠️ Potential issueAvoid assignment within an expression for clarity
At line 222, there's an assignment within an expression:
let entity = (path[0] = element.entity);Assigning within an expression can reduce code readability. Separating the assignments makes the code clearer.
Apply this diff to refactor the code:
- let entity = (path[0] = element.entity); + path[0] = element.entity; + let entity = path[0];🧰 Tools
🪛 Biome
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
35-35: 🛠️ Refactor suggestion
Accessing private properties using
@ts-ignore
is discouragedAt line 35:
// @ts-ignore const componentsManager = scene._componentsManager;Using
@ts-ignore
to access private members bypasses TypeScript's type checking, which can lead to maintenance issues and potential runtime errors.Consider exposing
componentsManager
through a public getter or method in theScene
class. This way, you can access it without ignoring TypeScript's checks.
205-212: 🛠️ Refactor suggestion
Avoid using
@ts-ignore
to access private membersAt lines 205-212:
// @ts-ignore entity._scripts.forEach( (script: Script) => { script[methods]?.(eventData); }, (script: Script, index: number) => { // @ts-ignore script._entityScriptsIndex = index; } );Using
@ts-ignore
to access_scripts
and_entityScriptsIndex
can lead to fragile code. It bypasses the safety offered by TypeScript's access modifiers.Consider adding public methods or accessors to interact with the scripts. For example, the
Entity
class could provide a method to get its scripts, andScript
could manage its index internally.
225-229: 🛠️ Refactor suggestion
Ensure proper access to component properties
At lines 225-229, you are accessing
_componentType
and_isRootCanvas
:// @ts-ignore if (element._componentType === ComponentType.UICanvas && element._isRootCanvas) { path.length = 1; return path; } else {Accessing private or protected properties with
@ts-ignore
should be avoided as it circumvents TypeScript's type safety.Propose exposing necessary properties or methods in the
Component
orUICanvas
classes to access this information without using@ts-ignore
.
217-235:
⚠️ Potential issuePotential infinite loop in
_composedPath
methodIn the
_composedPath
method, you have a loop that depends onentity.parent
:for (; i < UIPointerEventEmitter._MAX_PATH_DEPTH && !!entity && entity !== rootEntity; i++) { entity = path[i] = entity.parent; }There is a risk of entering an infinite loop if there is a cycle in the entity's parent hierarchy or if
rootEntity
is not correctly identified.Consider adding safeguards to detect cycles or ensure that
rootEntity
is correctly set.🧰 Tools
🪛 Biome
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
89-89: 💡 Codebase verification
Return statement is inconsistent with void return type
The
processRaycast
method is declared with a void return type but includes areturn null
statement. This is unnecessary since:
- The method already has multiple
return
statements without values- Void functions should not return values in TypeScript
Remove the
return null
statement at line 89 as it's both unnecessary and inconsistent with the method's signature.🔗 Analysis chain
Verify: Consistency of the return type with method signature
As per the static analysis hint, the function
processRaycast
is expected to returnvoid
, but it includes areturn null;
statement at line 89.Run the following script to check for any other instances where
void
functions return a value:🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Find methods declared as void that return a value ast-grep 'function $_(...) : void { $$$; return $_; $$$ }' --lang typescriptLength of output: 223
Script:
#!/bin/bash # Let's first check the method signature and implementation rg -U "processRaycast.*void.*\{[\s\S]*?return null" packages/ui/src/input/UIPointerEventEmitter.ts # Also check the entire file content for better context cat packages/ui/src/input/UIPointerEventEmitter.tsLength of output: 10852
🧰 Tools
🪛 Biome
[error] 89-89: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
26-89:
⚠️ Potential issueRemove the
return
statement in avoid
functionThe
processRaycast
method is declared with a return type ofvoid
, but it includes areturn null;
statement at line 89. Returning a value from avoid
function can cause confusion and may lead to unexpected behavior.Apply this diff to correct the issue:
89 - return null; + return;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.override processRaycast(scenes: readonly Scene[], pointer: Pointer): void { const { _tempRay: ray } = PointerEventEmitter; const hitResult = this._hitResult; const { position } = pointer; const { x, y } = position; for (let i = scenes.length - 1; i >= 0; i--) { const scene = scenes[i]; if (!scene.isActive || scene.destroyed) continue; // @ts-ignore const componentsManager = scene._componentsManager; /** Overlay Canvas */ let canvasElements = componentsManager._overlayCanvases; ray.origin.set(position.x, position.y, 1); ray.direction.set(0, 0, -1); for (let j = canvasElements.length - 1; j >= 0; j--) { if (canvasElements.get(j).raycast(ray, hitResult)) { this._updateRaycast(<Component>hitResult.component, pointer); return; } } const cameras = componentsManager._activeCameras; for (let j = cameras.length - 1; j >= 0; j--) { const camera = cameras.get(j); if (camera.renderTarget) continue; const { pixelViewport } = camera; if ( x < pixelViewport.x || y < pixelViewport.y || x > pixelViewport.x + pixelViewport.width || y > pixelViewport.y + pixelViewport.height ) { continue; } camera.screenPointToRay(pointer.position, ray); /** Other canvases */ const cameraPosition = camera.entity.transform.position; /** Sort by rendering order */ canvasElements = componentsManager._canvases; for (let k = 0, n = canvasElements.length; k < n; k++) { canvasElements.get(k)._updateSortDistance(cameraPosition); } canvasElements.sort((a, b) => a.sortOrder - b.sortOrder || a._sortDistance - b._sortDistance); for (let k = 0, n = canvasElements.length; k < n; k++) { canvasElements.get(k)._canvasIndex = k; } const farClipPlane = camera.farClipPlane; /** Post-rendering first detection */ for (let k = 0, n = canvasElements.length; k < n; k++) { const canvas = canvasElements.get(k); if (canvas.renderCamera !== camera) continue; if (canvas.raycast(ray, hitResult, farClipPlane)) { this._updateRaycast(<Component>hitResult.component, pointer); return; } } if (camera.clearFlags & CameraClearFlags.Color) { this._updateRaycast(null); return; } } } return;
🧰 Tools
🪛 Biome
[error] 89-89: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
packages/ui/src/component/UIRenderer.ts (5)
134-135: 🛠️ Refactor suggestion
Avoid suppressing TypeScript errors with
// @ts-ignore
The use of
// @ts-ignore
suppresses TypeScript compiler errors, which can mask underlying issues in the code. It's important to address the root cause of the TypeScript error to ensure type safety and maintainability.Consider adding a proper type declaration for
_componentType
:// Before the constructor, add the property declaration protected _componentType: ComponentType;This will eliminate the need for
// @ts-ignore
and make the code more robust.
139-139: 🛠️ Refactor suggestion
Avoid direct access to internal properties
Directly accessing internal properties like
_onValueChanged
can break encapsulation and lead to maintenance challenges. It's better to use public methods or properties provided by the class.Consider adding a method to
Color
class to handle the value change subscription:// In Color class public set onValueChanged(callback: () => void) { this._onValueChanged = callback; }And then update your code:
- //@ts-ignore - this._color._onValueChanged = this._onColorChange; + this._color.onValueChanged = this._onColorChange;
292-292: 🛠️ Refactor suggestion
Avoid direct assignment to internal properties
Setting internal properties like
_onValueChanged
tonull
directly can cause unexpected behavior. Provide a proper method to unsubscribe or dispose of event handlers.Modify the
Color
class to include an unsubscribe method:// In Color class public clearOnValueChanged(): void { this._onValueChanged = null; }And update your code accordingly:
- //@ts-ignore - this._color._onValueChanged = null; + this._color.clearOnValueChanged();
173-175: 🛠️ Refactor suggestion
Resolve TypeScript issues instead of using
// @ts-ignore
Using
// @ts-ignore
bypasses TypeScript's type checking, which can lead to runtime errors. It's crucial to address the type incompatibilities to maintain type safety.Investigate the types of
this.shaderData._macroCollection
andthis._globalShaderMacro
to ensure they match the expected parameters ofShaderMacroCollection.unionCollection
. Update the type definitions if necessary to align with the method's requirements.
214-215:
⚠️ Potential issueRefactor assignment in expression for clarity
Using an assignment within a logical expression can be confusing and may lead to unintended side effects. At line 215, the assignment
_hierarchyDirty = true
is made within a logical AND operation. This can be refactored for better readability and to follow best practices.Apply this diff to improve the code clarity:
const rootCanvas = this._canvas; - rootCanvas && (rootCanvas._hierarchyDirty = true); + if (rootCanvas) { + rootCanvas._hierarchyDirty = true; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const rootCanvas = this._canvas; if (rootCanvas) { rootCanvas._hierarchyDirty = true; }
🧰 Tools
🪛 Biome
[error] 215-215: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/ui/src/component/advanced/Image.ts (2)
150-150:
⚠️ Potential issueUndefined method
_onColorChange
The method
_onColorChange
is referenced but not defined in theImage
class. This will cause a runtime error when_onColorChange
is called.Consider adding the
_onColorChange
method to handle updates when the color changes. Here's a possible implementation:private _onColorChange(): void { this._dirtyUpdateFlag |= UIRendererUpdateFlags.Color; }
146-147: 🛠️ Refactor suggestion
Avoid using
@ts-ignore
by addressing TypeScript type issuesUsing
@ts-ignore
suppresses TypeScript errors, potentially hiding bugs and undermining type safety. It's better to resolve the underlying type issues to ensure code reliability.Review the following instances where
@ts-ignore
is used:
- Lines 123-125 and 129-131: Accessing
_updateFlagManager
onSprite
.- Lines 146-147 and 218-219: Accessing
_engine._basicResources.uiDefaultMaterial
.- Lines 149-150: Assigning to
this._color._onValueChanged
.Consider the following actions:
- Expose necessary properties or methods: If properties like
_updateFlagManager
and_basicResources
are needed externally, provide public getters or methods in their respective classes.- Adjust TypeScript definitions: Ensure all accessed properties and methods are correctly typed and accessible according to their visibility (public, protected, private).
- Refactor code: Modify the design to eliminate the need for accessing internal properties or using
@ts-ignore
.Also applies to: 149-150, 123-125, 129-131, 218-219
packages/ui/src/component/advanced/Label.ts (6)
228-228: 🛠️ Refactor suggestion
Avoid assignments within expressions for clarity
Using assignments within expressions can make the code harder to read and maintain. Consider refactoring the assignment for better readability.
- this._subFont && (this._subFont = null); + if (this._subFont) { + this._subFont = null; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (this._subFont) { this._subFont = null; }
🧰 Tools
🪛 Biome
[error] 228-228: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
458-458: 🛠️ Refactor suggestion
Avoid assignments within expressions for clarity
For better code readability, avoid assigning values within expressions. Refactor to use an
if
statement.- j === firstRow && (minX = Math.min(minX, left)); + if (j === firstRow) { + minX = Math.min(minX, left); + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (j === firstRow) { minX = Math.min(minX, left); }
🧰 Tools
🪛 Biome
[error] 458-458: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
456-456: 🛠️ Refactor suggestion
Avoid assignments within expressions for clarity
Assignments within expressions can reduce code clarity. Use an explicit conditional statement instead.
- i === firstLine && (maxY = Math.max(maxY, top)); + if (i === firstLine) { + maxY = Math.max(maxY, top); + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (i === firstLine) { maxY = Math.max(maxY, top); }
🧰 Tools
🪛 Biome
[error] 456-456: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
445-446: 🛠️ Refactor suggestion
Avoid assignments within expressions for clarity
The assignment inside the logical expression can be confusing. Refactor to an explicit
if
statement for improved readability.if (charInfo.h > 0) { - firstRow < 0 && (firstRow = j); + if (firstRow < 0) { + firstRow = j; + }Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 445-445: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 446-446: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
231-234: 🛠️ Refactor suggestion
Avoid suppressing TypeScript errors with
@ts-ignore
Suppressing TypeScript checks can mask potential issues. Refactor the code to address the type errors without using
@ts-ignore
.- // @ts-ignore override _cloneTo(target: Label, srcRoot: Entity, targetRoot: Entity): void { - // @ts-ignore super._cloneTo(target, srcRoot, targetRoot); target.font = this._font; }Ensure that method overrides correctly match the base class signatures and that types are properly defined.
Committable suggestion skipped: line range outside the PR's diff.
207-210: 🛠️ Refactor suggestion
Avoid suppressing TypeScript errors with
@ts-ignore
Using
@ts-ignore
may hide underlying issues and lead to maintenance challenges. Consider resolving the TypeScript errors directly or adjusting the access modifiers.- // @ts-ignore this.font = engine._textDefaultFont; - // @ts-ignore this.setMaterial(engine._basicResources.textDefaultMaterial);If accessing private or protected members, consider exposing them through public getters or adjusting the class design.
Committable suggestion skipped: line range outside the PR's diff.
packages/ui/src/component/UICanvas.ts (6)
587-588:
⚠️ Potential issueAdd a break statement to prevent unintended fall-through
In the
switch
statement at line 586, thecase CanvasRenderMode.ScreenSpaceOverlay
lacks abreak
, causing it to fall through to the next case unintentionally.Apply this diff to add the missing
break
:case CanvasRenderMode.ScreenSpaceOverlay: this._removeCanvasListener(); + break;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.case CanvasRenderMode.ScreenSpaceOverlay: this._removeCanvasListener(); break;
🧰 Tools
🪛 Biome
[error] 587-588: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
159-159: 🛠️ Refactor suggestion
Avoid assignment within a logical expression for better readability
On line 159, assigning
this.scene._componentsManager._overlayCanvasesSortingFlag = true
within a logical expression can be confusing and reduce code clarity.Apply this diff to improve readability:
this._realRenderMode === CanvasRenderMode.ScreenSpaceOverlay && // @ts-ignore - (this.scene._componentsManager._overlayCanvasesSortingFlag = true); + this.scene._componentsManager._overlayCanvasesSortingFlag = true;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.this.scene._componentsManager._overlayCanvasesSortingFlag = true;
🧰 Tools
🪛 Biome
[error] 159-159: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
597-598:
⚠️ Potential issueAdd a break statement to prevent unintended fall-through
At line 597, the
case CanvasRenderMode.ScreenSpaceOverlay
is missing abreak
statement, leading to unintended execution of subsequent cases.Apply this diff to correct the switch case:
case CanvasRenderMode.ScreenSpaceOverlay: this._addCanvasListener(); + break;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.case CanvasRenderMode.ScreenSpaceOverlay: this._addCanvasListener(); break;
🧰 Tools
🪛 Biome
[error] 597-598: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
599-601:
⚠️ Potential issueAdd a break statement to prevent unintended fall-through
The
case CanvasRenderMode.ScreenSpaceCamera
at line 599 lacks abreak
, causing unintended fall-through to the next case.Apply this diff to add the missing
break
:case CanvasRenderMode.ScreenSpaceCamera: this._adapterPoseInScreenSpace(); this._adapterSizeInScreenSpace(); + break;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.case CanvasRenderMode.ScreenSpaceCamera: this._adapterPoseInScreenSpace(); this._adapterSizeInScreenSpace(); break;
🧰 Tools
🪛 Biome
[error] 599-601: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
406-408:
⚠️ Potential issueAdd a break statement to prevent unintended fall-through
The
case ComponentType.UIRenderer
lacks abreak
statement, causing it to fall through toComponentType.UIInteractive
, which may not be the intended behavior.Apply this diff to add the missing
break
:case ComponentType.UIRenderer: elements[depth] = component as unknown as IGraphics; ++depth; + break;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.case ComponentType.UIRenderer: elements[depth] = component as unknown as IGraphics; ++depth; break;
🧰 Tools
🪛 Biome
[error] 406-408: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
237-237:
⚠️ Potential issueWrap variable declarations in switch cases with blocks to prevent scope issues
Declaring variables directly within a
switch
case can cause them to be accessible in other cases due to JavaScript's function-level scoping.Apply this diff to encapsulate the declaration:
case CanvasRenderMode.ScreenSpaceOverlay: + { const { min, max } = renderer.bounds; if (min.x > width || max.x < 0 || min.y > height || max.y < 0) { continue; } break; + }Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 237-237: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
packages/design/src/ui/IUIElement.ts (1)
1-4: 🛠️ Refactor suggestion
Consider revising member naming conventions and visibility.
The interface members use underscore prefix suggesting they are private/internal, which is unconventional for interface contracts. Interface members typically define the public API contract.
Consider this alternative approach:
export interface IUIElement { - _onUIUpdateIndex: number; - _onUpdate(): void; + updateIndex: number; + update(): void; }Additionally, please add JSDoc comments to document:
- The purpose of this interface
- The role of
updateIndex
in controlling update order/priority- When and how the
update
method is calledCommittable suggestion skipped: line range outside the PR's diff.
packages/design/src/ui/IUICanvas.ts (2)
2-2: 🛠️ Refactor suggestion
⚠️ Potential issueReplace
any
types with specific types for better type safety.Using
any
type bypasses TypeScript's type checking and can lead to runtime errors. Consider defining proper types for these members.Consider defining specific types:
import { Entity } from '../core/Entity'; import { RenderContext } from '../renderer/RenderContext'; import { RenderElement } from '../renderer/RenderElement'; export interface UICanvas { entity: Entity; // ... other members _renderElement: RenderElement; _prepareRender(renderContext: RenderContext): void; }Also applies to: 5-5, 6-6
4-5: 🛠️ Refactor suggestion
Remove private members from the interface definition.
The interface includes members prefixed with underscore, suggesting they are private implementation details. Interfaces should define the public contract, not implementation details.
Consider moving private members to the implementing class:
export interface UICanvas { entity: Entity; sortOrder: number; prepareRender(renderContext: RenderContext): void; } // In the implementing class: class Canvas implements UICanvas { private _canvasIndex: number; private _renderElement: RenderElement; // ... implementation }packages/core/src/2d/text/ITextRenderer.ts (1)
4-8: 🛠️ Refactor suggestion
Consider removing underscore prefix from public interface method
The method
_getSubFont()
uses an underscore prefix, which typically indicates a private or internal member. However, this is part of a public interface and TypeScript has proper visibility modifiers that should be used instead.Consider this alternative:
export interface ITextRenderer { text: string; overflowMode: OverflowMode; - _getSubFont(): SubFont; + getSubFont(): SubFont; }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.export interface ITextRenderer { text: string; overflowMode: OverflowMode; getSubFont(): SubFont; }
packages/ui/src/interface/IGraphics.ts (1)
8-8: 🛠️ Refactor suggestion
Consider a more TypeScript-idiomatic approach for the raycast method.
Several concerns with the current method signature:
- The underscore prefix suggests an internal implementation, but TypeScript has built-in visibility modifiers that could be more appropriate.
- Using an out parameter (
HitResult
) is not a typical TypeScript pattern. Consider returning aHitResult | null
instead.- The purpose of the
distance
parameter isn't clear from the signature.Consider this alternative approach:
- _raycast(ray: Ray, out: HitResult, distance: number): boolean; + /** Performs raycasting against this graphics element + * @param ray The ray to test against + * @param maxDistance The maximum distance to test for intersections + * @returns The hit result if an intersection occurred, null otherwise + */ + protected raycast(ray: Ray, maxDistance: number): HitResult | null;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements./** Performs raycasting against this graphics element * @param ray The ray to test against * @param maxDistance The maximum distance to test for intersections * @returns The hit result if an intersection occurred, null otherwise */ protected raycast(ray: Ray, maxDistance: number): HitResult | null;
packages/ui/src/interface/IElement.ts (2)
8-10: 🛠️ Refactor suggestion
Consider encapsulating internal state.
Internal state properties prefixed with
_
should not be exposed in the public interface. Consider:
- Moving these to a separate internal interface
- Using private fields in the implementing class
// Example approach using a separate internal interface interface IElementInternal { _indexInCanvas: number; _isCanvasDirty: boolean; } export interface IElement { entity: Entity; readonly canvas: UICanvas; }
11-12: 🛠️ Refactor suggestion
Improve type safety and encapsulation of event handling.
The current event handling implementation has several issues:
- The
flag
parameter uses a number type instead of an enum- The
param
parameter is loosely typed asany
- The listener array is exposed as an internal property
Consider this improved approach:
/** Event types for canvas interactions */ export enum CanvasEventType { // Define your event types here } /** Event data structure */ export interface CanvasEventData { // Define strongly typed event parameters } export interface IElement { // ... other members ... /** Add a canvas event listener */ addEventListener(entity: Entity, callback: (event: CanvasEventType, data?: CanvasEventData) => void): void; /** Remove a canvas event listener */ removeEventListener(entity: Entity): void; }packages/core/src/enums/ComponentType.ts (1)
22-22:
⚠️ Potential issueFix incorrect bitwise combination value
The value
0x680
forUIElement
appears to include an extra bit that doesn't correspond to any of its component flags. If it's meant to be a combination of UICanvas (0x80), UIGroup (0x100), and UIRenderer (0x200), the value should be0x380
.- UIElement = 0x680 + UIElement = 0x380 // UICanvas | UIGroup | UIRenderer📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.UIElement = 0x380 // UICanvas | UIGroup | UIRenderer
packages/ui/src/interface/IGroupAble.ts (2)
14-16: 🛠️ Refactor suggestion
Review method visibility and callback type safety
A few concerns about the method declarations:
_onUpdate
and_onGroupModify
are internal methods but part of the public interface_groupListener
uses a genericnumber
type for the flag parameter, while_onGroupModify
uses the more specificGroupModifyFlags
Consider these improvements:
export interface IGroupAble extends IElement { readonly group: UIGroup; } interface IGroupableInternal extends IGroupAble { _onUpdate?(): void; _onGroupModify(flag: GroupModifyFlags): void; _groupListener: (flag: GroupModifyFlags) => void; // Use specific enum type }
1-17: 💡 Codebase verification
Based on the search results, I can now generate the final response:
Optional properties in IGroupAble are implemented differently across classes
The verification shows that these properties are not truly optional in implementations:
_onUIUpdateIndex
is initialized to 0 in both UIInteractive and UIRenderer_globalInteractive
is initialized totrue
in UIGroup andfalse
in UIInteractive_globalAlpha
is initialized to 1 in UIGroup_onUpdate
is implemented in UIInteractive but not in UIGroupThe interface should reflect the actual implementation requirements by removing the optional markers (
?
) since these properties are always present in implementing classes.🔗 Analysis chain
Verify usage of optional properties
The interface has several optional properties (
_onUIUpdateIndex
,_globalAlpha
,_globalInteractive
,_onUpdate
). We should verify if these are truly optional or if they're always set during initialization.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for implementations to verify optional property usage ast-grep --pattern 'class $_ implements IGroupAble { $$$ }' # Search for property assignments rg -A 5 "_onUIUpdateIndex|_globalAlpha|_globalInteractive" --type typescriptLength of output: 177
Script:
#!/bin/bash # Search for implementations using different file extensions ast-grep --pattern 'class $_ implements IGroupAble { $$$ }' # Search for property assignments with .ts and .tsx extensions rg -t ts -t tsx -A 5 "_onUIUpdateIndex|_globalAlpha|_globalInteractive" # Search for property initialization patterns rg -t ts -t tsx "constructor.*IGroupAble" -A 20 # Search for files that might implement IGroupAble fd -e ts -e tsx | xargs grep -l "implements IGroupAble"Length of output: 393
Script:
#!/bin/bash # Let's examine the content of the implementing files cat packages/ui/src/component/UIGroup.ts packages/ui/src/component/interactive/UIInteractive.ts # Search for property assignments without file type flags rg "_onUIUpdateIndex|_globalAlpha|_globalInteractive" # Search for constructor initialization rg "constructor.*{" -A 10 packages/ui/src/component/UIGroup.ts packages/ui/src/component/interactive/UIInteractive.tsLength of output: 15476
packages/ui/src/component/interactive/transition/ScaleTransition.ts (1)
22-24:
⚠️ Potential issueAdd null check for target entity.
The
_applyValue
method should include a null check before accessing the target entity.protected override _applyValue(value: number): void { + if (!this._target?.entity) { + return; + } this._target.entity.transform.setScale(value, value, value); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.protected override _applyValue(value: number): void { if (!this._target?.entity) { return; } this._target.entity.transform.setScale(value, value, value); }
packages/core/src/2d/index.ts (1)
3-5: 💡 Codebase verification
Documentation needed for sprite assembler system
The sprite documentation (
sprite.md
) lacks information about the different sprite assembler types and their use cases. While it covers sprite properties and general usage, it doesn't explain:
- The purpose of Simple, Sliced, and Tiled sprite assemblers
- When to use each assembler type
- How these assemblers interact with the sprite rendering system
The implementation files also lack comprehensive documentation comments explaining their architectural roles.
Suggested documentation points:
- Add a new section in
sprite.md
about sprite assembly modes- Include code-level documentation in assembler classes
- Consider creating a dedicated guide for sprite rendering architecture
🔗 Analysis chain
Consider adding architectural documentation for the new assembler system.
The introduction of three distinct sprite assemblers (Simple, Sliced, Tiled) suggests a significant architectural enhancement. This warrants documentation explaining:
- The purpose and use case for each assembler type
- How to choose between different assemblers
- The relationship between assemblers and renderers
Let's check if documentation exists:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Look for documentation about the assembler system # Search for documentation files echo "Searching for documentation files:" fd -e md -e txt . | grep -i "assembler\|sprite\|gui" # Search for documentation comments in assembler files echo "Searching for documentation comments in assembler files:" rg "\/\*\*|\*\/" "./assembler/"Length of output: 708
Script:
#!/bin/bash # Let's check the content of relevant documentation and assembler files # Check English documentation content echo "=== Sprite Documentation Content ===" cat docs/en/graphics/2D/sprite.md # Check assembler files for documentation echo -e "\n=== Assembler Implementation Documentation ===" rg -A 5 "class.*Assembler" "packages/core/src/2d/assembler/"Length of output: 6836
packages/ui/src/component/interactive/transition/SpriteTransition.ts (1)
34-44:
⚠️ Potential issueAdd null checks and error handling for robustness.
The value management methods need additional safety checks:
_getTargetValueCopy
might return undefined when_target
is null_updateCurrentValue
lacks validation for null/undefined values_applyValue
might throw ifthis._target
is nullConsider adding these safety checks:
- protected _getTargetValueCopy(): Sprite { - return this._target?.sprite; + protected _getTargetValueCopy(): Sprite | null { + return this._target?.sprite ?? null; } protected override _updateCurrentValue(srcValue: Sprite, destValue: Sprite, weight: number): void { + if (!srcValue && !destValue) { + this._currentValue = null; + return; + } this._currentValue = weight >= 1 ? destValue : srcValue; } protected override _applyValue(value: Sprite): void { + if (!this._target) { + return; + } this._target.sprite = value || this._normal; }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.protected _getTargetValueCopy(): Sprite | null { return this._target?.sprite ?? null; } protected override _updateCurrentValue(srcValue: Sprite, destValue: Sprite, weight: number): void { if (!srcValue && !destValue) { this._currentValue = null; return; } this._currentValue = weight >= 1 ? destValue : srcValue; } protected override _applyValue(value: Sprite): void { if (!this._target) { return; } this._target.sprite = value || this._normal; }
packages/ui/src/component/advanced/Button.ts (2)
32-35: 🛠️ Refactor suggestion
Improve cleanup implementation and readability.
- The current cleanup only marks listeners as destroyed but doesn't clear the array.
- The assignment in expression makes the code less maintainable.
Consider this improved implementation:
override onDestroy(): void { super.onDestroy(); - this._listeners.findAndRemove((value) => (value.destroyed = true)); + this._listeners.forEach(listener => listener.destroyed = true); + this._listeners.clear(); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.override onDestroy(): void { super.onDestroy(); this._listeners.forEach(listener => listener.destroyed = true); this._listeners.clear(); }
🧰 Tools
🪛 Biome
[error] 34-34: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
11-21: 🛠️ Refactor suggestion
Improve code readability and documentation.
- The assignment within the expression makes the code harder to read and maintain.
- The method documentation could be more descriptive about the event parameter.
Consider these improvements:
/** * Add a listening function for click. * @param listener - The listening function + * @param event - The pointer event data containing click information */ addClicked(listener: (event: PointerEventData) => void): void { this._listeners.push({ fn: listener }); } /** * Remove a listening function of click. * @param listener - The listening function + * @returns boolean - True if the listener was found and removed */ removeClicked(listener: (event: PointerEventData) => void): void { - this._listeners.findAndRemove((value) => (value.fn === listener ? (value.destroyed = true) : false)); + this._listeners.findAndRemove((value) => { + if (value.fn === listener) { + value.destroyed = true; + return true; + } + return false; + }); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements./** * Add a listening function for click. * @param listener - The listening function * @param event - The pointer event data containing click information */ addClicked(listener: (event: PointerEventData) => void): void { this._listeners.push({ fn: listener }); } /** * Remove a listening function of click. * @param listener - The listening function * @returns boolean - True if the listener was found and removed */ removeClicked(listener: (event: PointerEventData) => void): void { this._listeners.findAndRemove((value) => { if (value.fn === listener) { value.destroyed = true; return true; } return false; }); }
🧰 Tools
🪛 Biome
[error] 20-20: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIUtils.ts (2)
10-14: 🛠️ Refactor suggestion
Consider refactoring to a module pattern with initialized fields
The current implementation as a static-only class could be improved by converting it to a module with standalone functions and properly initialized fields. This would align with TypeScript best practices and prevent potential race conditions.
-export class UIUtils { - private static _renderQueue: RenderQueue; - private static _virtualCamera: VirtualCamera; - private static _viewport: Vector4; +const _renderQueue = new RenderQueue(RenderQueueType.Transparent); +const _virtualCamera = new VirtualCamera(); +const _viewport = new Vector4(0, 0, 1, 1); + +export function render(engine: Engine, uiCanvases: DisorderedArray<IUICanvas>): void { // ... implementation -}Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 10-39: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
15-19: 🛠️ Refactor suggestion
Improve initialization pattern and add input validation
The current initialization pattern uses assignments within expressions and lacks input validation. This can be improved for better readability and robustness.
static render(engine: Engine, uiCanvases: DisorderedArray<IUICanvas>): void { + if (!engine || !uiCanvases) { + throw new Error("Engine and uiCanvases parameters are required"); + } + - const uiRenderQueue = (this._renderQueue ||= new RenderQueue(RenderQueueType.Transparent)); - const virtualCamera = (this._virtualCamera ||= new VirtualCamera()); - const viewport = (this._viewport ||= new Vector4(0, 0, 1, 1)); + const uiRenderQueue = UIUtils._renderQueue; + const virtualCamera = UIUtils._virtualCamera; + const viewport = UIUtils._viewport;Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 16-16: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 16-16: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
[error] 17-17: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 17-17: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
[error] 18-18: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 18-18: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
packages/ui/src/component/interactive/transition/ColorTransition.ts (2)
22-29:
⚠️ Potential issueAddress type safety concerns with
@ts-ignore
.Multiple
@ts-ignore
comments suggest potential type safety issues. Consider:
- Properly typing the Color class to include
_onValueChanged
- Using a proper event emitter pattern instead of direct property assignment
// Example type definition to consider interface TransitionColor extends Color { _onValueChanged?: () => void; }
48-64:
⚠️ Potential issueAdd null safety checks in protected methods.
Several methods could benefit from additional null safety checks:
_getTargetValueCopy
: The optional chaining on_target?.color
is good, but reusingthis._color
for multiple operations could lead to side effects_applyValue
: Missing null check forthis._target
protected _getTargetValueCopy(): Color { - const color = this._color; + const color = new Color(); color.copyFrom(this._target?.color || this._normal); return color; } protected override _applyValue(value: Color): void { + if (!this._target) { + return; + } this._target.color = value; }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.protected _getTargetValueCopy(): Color { const color = new Color(); color.copyFrom(this._target?.color || this._normal); return color; } protected override _updateCurrentValue(srcValue: Color, destValue: Color, weight: number): void { if (weight >= 1) { this._currentValue.copyFrom(destValue); } else { Color.lerp(srcValue, destValue, weight, this._currentValue); } } protected override _applyValue(value: Color): void { if (!this._target) { return; } this._target.color = value; }
packages/core/src/ComponentsDependencies.ts (2)
97-101: 🛠️ Refactor suggestion
Add JSDoc documentation and type validation
The decorator lacks documentation and type validation. Consider:
- Adding JSDoc documentation to explain its purpose and usage
- Adding validation to ensure the target is a valid component constructor
+/** + * Decorator that marks a component to be automatically inherited by child entities. + * Used primarily for GUI components that should propagate through the entity hierarchy. + * @example + * ```ts + * @markAsInherited() + * class UIComponent extends Component { + * // ... + * } + * ``` + */ export function markAsInherited() { return function <T extends ComponentConstructor>(target: T): void { + if (!(target.prototype instanceof Component)) { + throw new Error(`${target.name} must be a Component class`); + } ComponentsDependencies._inheritedMap.set(target, true); }; }
14-14: 🛠️ Refactor suggestion
Use lowercase
boolean
type instead ofBoolean
For consistency with TypeScript best practices, use the primitive type
boolean
instead of the object wrapper typeBoolean
.- static _inheritedMap = new Map<ComponentConstructor, Boolean>(); + static _inheritedMap = new Map<ComponentConstructor, boolean>();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.static _inheritedMap = new Map<ComponentConstructor, boolean>();
🧰 Tools
🪛 Biome
[error] 14-14: Don't use 'Boolean' as a type.
Use lowercase primitives for consistency.
Safe fix: Use 'boolean' instead(lint/complexity/noBannedTypes)
packages/core/src/2d/assembler/SlicedSpriteAssembler.ts (2)
65-69: 🛠️ Refactor suggestion
Improve readability by separating combined assignments
The combined assignment statements make the code harder to read and maintain. Consider breaking them down into separate lines.
Apply this diff to improve readability:
- (row[0] = expectWidth * left * widthScale), (row[1] = row[2] = fixedLeft * widthScale); - row[3] = width - expectWidth * (1 - right) * widthScale; + row[0] = expectWidth * left * widthScale; + row[1] = fixedLeft * widthScale; + row[2] = fixedLeft * widthScale; + row[3] = width - expectWidth * (1 - right) * widthScale; - (row[0] = expectWidth * left), (row[1] = fixedLeft), (row[2] = width - fixedRight); - row[3] = width - expectWidth * (1 - right); + row[0] = expectWidth * left; + row[1] = fixedLeft; + row[2] = width - fixedRight; + row[3] = width - expectWidth * (1 - right); - (column[0] = expectHeight * bottom * heightScale), (column[1] = column[2] = fixedBottom * heightScale); - column[3] = height - expectHeight * (1 - top) * heightScale; + column[0] = expectHeight * bottom * heightScale; + column[1] = fixedBottom * heightScale; + column[2] = fixedBottom * heightScale; + column[3] = height - expectHeight * (1 - top) * heightScale; - (column[0] = expectHeight * bottom), (column[1] = fixedBottom), (column[2] = height - fixedTop); - column[3] = height - expectHeight * (1 - top); + column[0] = expectHeight * bottom; + column[1] = fixedBottom; + column[2] = height - fixedTop; + column[3] = height - expectHeight * (1 - top);Also applies to: 74-78
169-173: 🛠️ Refactor suggestion
Improve readability of UV calculations
Similar to earlier feedback, the combined assignment statements here should be separated for better readability.
Apply this diff:
- (row[0] = expectWidth * left * widthScale), (row[1] = row[2] = fixedLeft * widthScale); - row[3] = width - expectWidth * (1 - right) * widthScale; + row[0] = expectWidth * left * widthScale; + row[1] = fixedLeft * widthScale; + row[2] = fixedLeft * widthScale; + row[3] = width - expectWidth * (1 - right) * widthScale; - (row[0] = expectWidth * left), (row[1] = fixedLeft), (row[2] = width - fixedRight); - row[3] = width - expectWidth * (1 - right); + row[0] = expectWidth * left; + row[1] = fixedLeft; + row[2] = width - fixedRight; + row[3] = width - expectWidth * (1 - right); - (column[0] = expectHeight * bottom * heightScale), (column[1] = column[2] = fixedBottom * heightScale); - column[3] = height - expectHeight * (1 - top) * heightScale; + column[0] = expectHeight * bottom * heightScale; + column[1] = fixedBottom * heightScale; + column[2] = fixedBottom * heightScale; + column[3] = height - expectHeight * (1 - top) * heightScale; - (column[0] = expectHeight * bottom), (column[1] = fixedBottom), (column[2] = height - fixedTop); - column[3] = height - expectHeight * (1 - top); + column[0] = expectHeight * bottom; + column[1] = fixedBottom; + column[2] = height - fixedTop; + column[3] = height - expectHeight * (1 - top);Also applies to: 178-182
packages/core/src/ComponentsManager.ts (2)
272-276: 🛠️ Refactor suggestion
Align callUIOnUpdate with other update methods
The implementation is inconsistent with other update methods in the class. Other methods maintain indices during iteration.
Apply this change to maintain consistency:
callUIOnUpdate(deltaTime: number): void { - this._onUpdateUIElements.forEach((element: IUIElement) => { - element._onUpdate(); - }); + this._onUpdateUIElements.forEach( + (element: IUIElement) => { + element._onUpdate(); + }, + (element: IUIElement, index: number) => { + element._onUIUpdateIndex = index; + } + ); }Committable suggestion skipped: line range outside the PR's diff.
185-188:
⚠️ Potential issueFix incorrect array reference in addOnUpdateUIElement
There's a critical bug in the implementation. The method is using
this._onUpdateRenderers.length
instead ofthis._onUpdateUIElements.length
for the index calculation.Apply this fix:
addOnUpdateUIElement(element: IUIElement): void { - element._onUIUpdateIndex = this._onUpdateRenderers.length; + element._onUIUpdateIndex = this._onUpdateUIElements.length; this._onUpdateUIElements.add(element); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.addOnUpdateUIElement(element: IUIElement): void { element._onUIUpdateIndex = this._onUpdateUIElements.length; this._onUpdateUIElements.add(element); }
packages/core/src/2d/sprite/SpriteRenderer.ts (1)
421-451: 💡 Codebase verification
Based on the provided code snippet and the enum values found (
texture
,size
,atlasRotate
,atlasRegion
,atlasRegionOffset
,border
), I can now generate the final response.Missing handlers for several sprite modification flags
The current switch statement only handles
size
andborder
modifications, but theSpriteModifyFlags
enum defines additional flags that should be handled:
texture
(0x1)atlasRotate
(0x4)atlasRegion
(0x8)atlasRegionOffset
(0x10)🔗 Analysis chain
Verify the completeness of sprite modification handling
While the switch statement handles various modification types, we should verify if all possible sprite modifications are covered.
Let me check the complete sprite modification handling by looking at the switch statement in the file.
Let me try a different approach to find the sprite modification handling code.
Let me try one more search with a broader pattern to find the sprite modification handling.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for all possible sprite modification flags rg -A 5 "enum SpriteModifyFlags"Length of output: 455
Script:
#!/bin/bash # Search for the switch statement handling sprite modifications rg -B 5 -A 30 "switch.*modifyFlags" packages/core/src/2d/sprite/SpriteRenderer.tsLength of output: 83
Script:
#!/bin/bash # Search for the sprite modification handling in SpriteRenderer rg -B 5 -A 30 "_onSprite.*Modified" packages/core/src/2d/sprite/SpriteRenderer.tsLength of output: 83
Script:
#!/bin/bash # Search for any method handling sprite modifications in SpriteRenderer rg -B 5 -A 30 "modifyFlags" packages/core/src/2d/sprite/SpriteRenderer.tsLength of output: 73
packages/core/src/2d/assembler/TiledSpriteAssembler.ts (2)
81-86:
⚠️ Potential issueReplace global isNaN with Number.isNaN
The global isNaN function performs type coercion which can lead to unexpected results. Use Number.isNaN instead for more precise NaN checking.
- if (isNaN(cUV.get(doubleJ)) || isNaN(cUV.get(doubleJ + 1))) { + if (Number.isNaN(cUV.get(doubleJ)) || Number.isNaN(cUV.get(doubleJ + 1))) { - if (isNaN(rUV.get(doubleI)) || isNaN(rUV.get(doubleI + 1))) { + if (Number.isNaN(rUV.get(doubleI)) || Number.isNaN(rUV.get(doubleI + 1))) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (Number.isNaN(cUV.get(doubleJ)) || Number.isNaN(cUV.get(doubleJ + 1))) { continue; } for (let i = 0; i < rowLength; i++) { const doubleI = i << 1; if (Number.isNaN(rUV.get(doubleI)) || Number.isNaN(rUV.get(doubleI + 1))) {
🧰 Tools
🪛 Biome
[error] 81-81: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
[error] 81-81: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
[error] 86-86: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
[error] 86-86: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
422-444:
⚠️ Potential issueAdd block scoping to switch cases
Variables declared in switch cases can leak into other cases. Wrap case blocks in curly braces to prevent variable scope leakage.
switch (rType) { case TiledType.Compressed: { const scale = width / fixedLR; rPos.add(expectWidth * left * scale), rPos.add(fixedL * scale); rPos.add(width - expectWidth * (1 - right) * scale); rUV.add(spriteUV0.x), rUV.add(spriteUV1.x), rUV.add(spriteUV2.x), rUV.add(spriteUV3.x); break; + } case TiledType.WithoutTiled: { // ... existing code ... break; + } case TiledType.WithTiled: { // ... existing code ... break; + }Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 422-422: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 434-434: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 435-435: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 436-436: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
packages/core/src/Entity.ts (1)
613-628: 🛠️ Refactor suggestion
Refactor assignment in expression for better readability.
The modification listener system is well-designed, but the assignment in expression should be refactored for clarity.
Apply this diff to improve readability:
_registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { - (this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange); + if (!this._updateFlagManager) { + this._updateFlagManager = new UpdateFlagManager(); + } + this._updateFlagManager.addListener(onChange); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements._registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { if (!this._updateFlagManager) { this._updateFlagManager = new UpdateFlagManager(); } this._updateFlagManager.addListener(onChange); } /** * @internal */ _unRegisterModifyListener(onChange: (flag: EntityModifyFlags) => void): void { this._updateFlagManager?.removeListener(onChange); } /** * @internal */ _dispatchModify(flag: EntityModifyFlags, param?: any): void { this._updateFlagManager?.dispatch(flag, param);
🧰 Tools
🪛 Biome
[error] 614-614: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/Camera.ts (1)
738-747: 🛠️ Refactor suggestion
Refactor the assignment in expression
The assignment within the expression can make the code harder to understand and maintain.
Consider refactoring to improve readability:
- (this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange); + if (!this._updateFlagManager) { + this._updateFlagManager = new UpdateFlagManager(); + } + this._updateFlagManager.addListener(onChange);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements._registerModifyListener(onChange: (flag: CameraModifyFlags) => void): void { if (!this._updateFlagManager) { this._updateFlagManager = new UpdateFlagManager(); } this._updateFlagManager.addListener(onChange); } /** * @internal */ _unRegisterModifyListener(onChange: (flag: CameraModifyFlags) => void): void { this._updateFlagManager?.removeListener(onChange); }
🧰 Tools
🪛 Biome
[error] 739-739: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
fix lint error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (7)
packages/ui/package.json (1)
26-30
: Review completeness of included filesThe
files
array should include all necessary files while excluding development files.Consider adding the following to ensure proper packaging:
- Add
README.md
to the files array for npm registry documentation- Add
LICENSE
file reference- Consider adding
package.json
explicitly"files": [ "dist/**/*", "libs/**/*", "types/**/*", + "README.md", + "LICENSE", + "package.json" ],packages/core/src/animation/Keyframe.ts (2)
13-22
: Fix indentation according to project style guidelines.The conditional type expressions need consistent indentation to match the project's formatting rules.
Apply this formatting fix:
? Vector2 - : V extends Vector3 - ? Vector3 - : V extends Vector4 | Color | Quaternion | Rect - ? Vector4 - : V extends number[] | Float32Array - ? number[] - : V extends ReferResource - ? ReferResource - : never + : V extends Vector3 + ? Vector3 + : V extends Vector4 | Color | Quaternion | Rect + ? Vector4 + : V extends number[] | Float32Array + ? number[] + : V extends ReferResource + ? ReferResource + : never🧰 Tools
🪛 eslint
[error] 13-13: Insert
··
(prettier/prettier)
[error] 14-14: Insert
··
(prettier/prettier)
[error] 15-15: Insert
····
(prettier/prettier)
[error] 16-16: Insert
····
(prettier/prettier)
[error] 17-17: Replace
····
with··········
(prettier/prettier)
[error] 18-18: Insert
······
(prettier/prettier)
[error] 19-19: Insert
········
(prettier/prettier)
[error] 20-20: Insert
········
(prettier/prettier)
[error] 21-21: Insert
··········
(prettier/prettier)
[error] 22-22: Insert
··········
(prettier/prettier)
🪛 GitHub Check: lint
[failure] 13-13:
Insert··
[failure] 14-14:
Insert··
[failure] 15-15:
Insert····
[failure] 16-16:
Insert····
[failure] 17-17:
Replace····
with··········
[failure] 18-18:
Insert······
[failure] 19-19:
Insert········
[failure] 20-20:
Insert········
[failure] 21-21:
Insert··········
[failure] 22-22:
Insert··········
Line range hint
1-22
: LGTM! Well-structured type definition.The generic type parameter
T
effectively maps input types to their appropriate value types, maintaining type safety throughout the animation system. The conditional type expressions handle all possibleKeyframeValueType
variants correctly.Consider documenting the type mapping logic in the class JSDoc to help other developers understand the relationship between
V
andT
type parameters.🧰 Tools
🪛 eslint
[error] 13-13: Insert
··
(prettier/prettier)
[error] 14-14: Insert
··
(prettier/prettier)
[error] 15-15: Insert
····
(prettier/prettier)
[error] 16-16: Insert
····
(prettier/prettier)
[error] 17-17: Replace
····
with··········
(prettier/prettier)
[error] 18-18: Insert
······
(prettier/prettier)
[error] 19-19: Insert
········
(prettier/prettier)
[error] 20-20: Insert
········
(prettier/prettier)
[error] 21-21: Insert
··········
(prettier/prettier)
[error] 22-22: Insert
··········
(prettier/prettier)
🪛 GitHub Check: lint
[failure] 13-13:
Insert··
[failure] 14-14:
Insert··
[failure] 15-15:
Insert····
[failure] 16-16:
Insert····
[failure] 17-17:
Replace····
with··········
[failure] 18-18:
Insert······
[failure] 19-19:
Insert········
[failure] 20-20:
Insert········
[failure] 21-21:
Insert··········
[failure] 22-22:
Insert··········
packages/core/src/animation/internal/animationCurveOwner/AnimationCurveOwner.ts (2)
95-96
: Consider refactoring nested ternary expressions for better readability.While the logic is correct, the nested ternary expressions make the code harder to maintain. Consider extracting this logic into a separate method or using if-else statements.
Fix the indentation and refactor for better readability:
- ? this.cureType._getZeroValue(this.baseEvaluateData.value) - : this.defaultValue; + ? this.cureType._getZeroValue(this.baseEvaluateData.value) + : this.defaultValue;Alternative implementation using a method:
private _getFallbackValue(evaluateData: IEvaluateData<V>, additive: boolean): V { return additive ? this.cureType._getZeroValue(evaluateData.value) : this.defaultValue; }🧰 Tools
🪛 eslint
[error] 95-95: Insert
··
(prettier/prettier)
[error] 96-96: Insert
··
(prettier/prettier)
104-105
: Apply consistent formatting and refactoring.For consistency with the previous changes, apply the same formatting and consider using the suggested
_getFallbackValue
method here as well.- ? this.cureType._getZeroValue(this.crossEvaluateData.value) - : this.defaultValue; + ? this.cureType._getZeroValue(this.crossEvaluateData.value) + : this.defaultValue;🧰 Tools
🪛 eslint
[error] 104-104: Insert
··
(prettier/prettier)
[error] 105-105: Insert
··
(prettier/prettier)
packages/core/src/RenderPipeline/BasicRenderPipeline.ts (1)
346-362
: Consider caching bounds check resultsThe bounds intersection check against the frustum is performed for every renderer on each frame. For static objects that don't move, this check could be cached until the object or camera moves.
Example optimization:
if (enableFrustumCulling) { - if (!frustum.intersectsBox(renderer.bounds)) { + if (!renderer._isMoving && renderer._cachedFrustumResult !== undefined) { + if (!renderer._cachedFrustumResult) continue; + } else { + renderer._cachedFrustumResult = frustum.intersectsBox(renderer.bounds); + if (!renderer._cachedFrustumResult) continue; + } continue; }packages/core/src/Engine.ts (1)
518-522
: Consider performance optimization for UI renderingWhile the UI rendering implementation is correct, consider these performance optimizations:
- Cache the length check result of
uiCanvas
to avoid multiple array access- Consider adding a dirty flag system to skip UI sorting and rendering when no changes occurred
Here's a suggested optimization:
- const uiCanvas = componentsManager._overlayCanvases; - if (uiCanvas.length > 0) { - componentsManager.sortUICanvases(); - UIUtils.render(this, uiCanvas); - } + const uiCanvases = componentsManager._overlayCanvases; + const uiCanvasCount = uiCanvases.length; + if (uiCanvasCount > 0) { + if (componentsManager._isUIDirty) { + componentsManager.sortUICanvases(); + componentsManager._isUIDirty = false; + } + UIUtils.render(this, uiCanvases); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (7)
packages/core/src/BasicResources.ts
(3 hunks)packages/core/src/Engine.ts
(4 hunks)packages/core/src/RenderPipeline/BasicRenderPipeline.ts
(2 hunks)packages/core/src/animation/Keyframe.ts
(1 hunks)packages/core/src/animation/internal/animationCurveOwner/AnimationCurveOwner.ts
(2 hunks)packages/core/src/particle/ParticleRenderer.ts
(3 hunks)packages/ui/package.json
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/core/src/BasicResources.ts
- packages/core/src/particle/ParticleRenderer.ts
🧰 Additional context used
🪛 eslint
packages/core/src/animation/Keyframe.ts
[error] 13-13: Insert ··
(prettier/prettier)
[error] 14-14: Insert ··
(prettier/prettier)
[error] 15-15: Insert ····
(prettier/prettier)
[error] 16-16: Insert ····
(prettier/prettier)
[error] 17-17: Replace ····
with ··········
(prettier/prettier)
[error] 18-18: Insert ······
(prettier/prettier)
[error] 19-19: Insert ········
(prettier/prettier)
[error] 20-20: Insert ········
(prettier/prettier)
[error] 21-21: Insert ··········
(prettier/prettier)
[error] 22-22: Insert ··········
(prettier/prettier)
packages/core/src/animation/internal/animationCurveOwner/AnimationCurveOwner.ts
[error] 95-95: Insert ··
(prettier/prettier)
[error] 96-96: Insert ··
(prettier/prettier)
[error] 104-104: Insert ··
(prettier/prettier)
[error] 105-105: Insert ··
(prettier/prettier)
[error] 129-129: Insert ··
(prettier/prettier)
[error] 130-130: Insert ··
(prettier/prettier)
🪛 GitHub Check: lint
packages/core/src/animation/Keyframe.ts
[failure] 13-13:
Insert ··
[failure] 14-14:
Insert ··
[failure] 15-15:
Insert ····
[failure] 16-16:
Insert ····
[failure] 17-17:
Replace ····
with ··········
[failure] 18-18:
Insert ······
[failure] 19-19:
Insert ········
[failure] 20-20:
Insert ········
[failure] 21-21:
Insert ··········
[failure] 22-22:
Insert ··········
🪛 Biome
packages/core/src/animation/internal/animationCurveOwner/AnimationCurveOwner.ts
[error] 99-99: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🔇 Additional comments (9)
packages/ui/package.json (3)
2-3
: Verify version alignment with core package
The package version is set to 1.3.20. Since this is a new UI package that depends on @galacean/engine, we should verify version alignment.
✅ Verification successful
Version alignment verified across all Galacean packages
All Galacean packages, including the core engine and UI package, are consistently versioned at 1.3.20. This alignment is correct and follows proper monorepo versioning practices.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check version alignment between UI package and core engine
echo "Checking core engine version..."
jq -r '.version' packages/core/package.json || echo "Core package.json not found"
# List all related packages and their versions for consistency
echo -e "\nChecking all related package versions..."
fd -t f package.json -x jq -r '. | select(.name | startswith("@galacean")) | .name + ": " + .version'
Length of output: 899
31-36
: Verify workspace dependency configuration
The package uses workspace dependencies for @galacean/engine in both dev and peer dependencies. This is correct for monorepo setup, but we should verify the workspace configuration.
✅ Verification successful
The workspace configuration and package manager settings look correct. However, let me verify one more thing about the dependency versions to ensure complete validation.
Workspace dependency configuration is correctly set up
The workspace configuration is properly set up with:
- Valid pnpm workspace configuration in
pnpm-workspace.yaml
- Correct package manager specification (
[email protected]
) - Consistent usage of
workspace:*
for@galacean/engine
across multiple packages in the monorepo - Matching pattern of having both devDependencies and peerDependencies for the engine package
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for workspace configuration
echo "Checking workspace configuration..."
cat pnpm-workspace.yaml 2>/dev/null || cat package.json | jq '.workspaces'
# Verify package manager
echo -e "\nChecking package manager configuration..."
cat package.json | jq -r '.packageManager' 2>/dev/null || echo "No package manager specified"
Length of output: 399
Script:
#!/bin/bash
# Check the engine package's version in the workspace
echo "Checking @galacean/engine package version..."
cat packages/engine/package.json | jq '.version'
# Verify if other packages in the workspace also use the same dependency pattern
echo -e "\nChecking other packages using @galacean/engine..."
fd -e json -x cat {} \; -p packages/ | jq 'select(.dependencies."@galacean/engine" or .devDependencies."@galacean/engine" or .peerDependencies."@galacean/engine")'
Length of output: 4434
12-16
: Verify the existence of entry point files
The package defines multiple entry points and type definitions. Let's ensure these paths are valid and the build system is configured to generate them.
✅ Verification successful
Let me gather more information about the build configuration and entry points.
Let me check the rollup configuration since we found a rollup.config.js file.
Based on the rollup configuration and file structure, I can now provide a final response:
Entry points configuration is properly set up
The rollup configuration correctly handles all the specified entry points in package.json:
dist/main.js
(CommonJS) anddist/module.js
(ES Module) are generated via themodule()
configdist/browser.js
is generated via theumd()
config- TypeScript declaration files in
types/
are handled by the TypeScript configuration with"declaration": true
and"declarationDir": "types"
- Source entry point
src/index.ts
exists atpackages/ui/src/index.ts
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for build configuration that generates these files
echo "Checking build configuration..."
fd -t f "rollup|webpack|tsconfig" -x cat {} \; | grep -A 5 "dist/"
# Verify TypeScript configuration for types generation
echo -e "\nChecking TypeScript configuration..."
fd tsconfig.json -x cat {} \; | grep -A 5 "\"declaration\""
Length of output: 3364
Script:
#!/bin/bash
# Check for build configuration files
echo "Checking for build configuration files..."
fd -t f "rollup.config|webpack.config|vite.config"
# Check source entry point
echo -e "\nChecking source entry point..."
fd "index.ts" -p "src"
# Check if dist directory exists and its contents
echo -e "\nChecking dist directory contents..."
fd . -t f -p "dist"
# Check if types directory exists and its contents
echo -e "\nChecking types directory contents..."
fd . -t f -p "types"
Length of output: 753
Script:
#!/bin/bash
# Check rollup configuration
echo "Checking rollup configuration..."
cat rollup.config.js
# Check if the source entry point exists
echo -e "\nChecking for source files..."
fd -t f "index.ts" --full-path
# List all package.json files to understand project structure
echo -e "\nListing package.json files..."
fd -t f "package.json" --full-path
Length of output: 9114
packages/core/src/RenderPipeline/BasicRenderPipeline.ts (3)
343-343
: LGTM! Good use of destructuring
The destructuring of camera properties improves both readability and performance by avoiding repeated property access.
Line range hint 376-382
: LGTM! Well-structured bitwise flags enum
The RenderQueueFlags
enum is well-designed:
- Uses power-of-two values for efficient bitwise operations
- Includes a comprehensive
All
flag - Values align with the
RenderQueueType
usage inpushRenderElementByType
364-373
: Verify: Intentional omission of frustum culling for canvases
The code applies culling mask checks but not frustum culling for canvas elements. While this makes sense for UI elements that should always be visible, please verify this is intentional.
✅ Verification successful
Let me gather more information about canvas rendering by checking the implementation details.
Based on the search results, I can now provide a definitive response about the intentional omission of frustum culling for canvases.
Frustum culling behavior for canvases is correctly implemented
The code correctly handles frustum culling for canvases based on their render mode:
- For
ScreenSpaceOverlay
mode: Frustum culling only checks if elements are within the screen bounds - For
ScreenSpaceCamera
andWorldSpace
modes: Full frustum culling is applied usingfrustum.intersectsBox()
This is the expected behavior since overlay UI elements should only be culled based on screen bounds while world-space UI should respect the camera's view frustum.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for any existing frustum culling patterns with canvases
rg -A 5 "canvas.*frustum|frustum.*canvas"
# Check if this is consistent with other UI/canvas related code
ast-grep --pattern 'class $_ implements Canvas {
$$$
_prepareRender($_) {
$$$
}
$$$
}'
Length of output: 1621
Script:
#!/bin/bash
# Check Canvas interface definition and implementations
ast-grep --pattern 'interface Canvas {
$$$
}'
# Look for canvas-related comments or documentation
rg -g '!*.{md,json}' -B 2 -A 2 'Canvas' --type ts
# Check if there are any frustum-related methods in canvas implementations
rg -g '!*.{md,json}' 'frustum' --type ts -B 2 -A 2
Length of output: 115384
packages/core/src/Engine.ts (3)
40-40
: LGTM: Import addition aligns with new UI functionality
The addition of the UIUtils import is consistent with the new UI rendering capabilities introduced in this PR.
483-485
: LGTM: UI update integration follows logical order
The UI update is correctly placed before the renderer update, ensuring UI state is consistent within the frame.
341-341
: Verify removal of physics initialization check
The removal of the physicsInitialized
check before firing pointer scripts could potentially cause issues if any pointer event handlers depend on physics being initialized. Please verify that this is intentional and that there are no pointer scripts that require physics initialization.
Let's check for any pointer scripts that might depend on physics:
"scripts": { | ||
"b:types": "tsc" | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider adding essential npm scripts
The scripts section only includes type generation. Consider adding standard lifecycle scripts.
Add these common npm scripts:
"scripts": {
- "b:types": "tsc"
+ "b:types": "tsc",
+ "build": "your-build-command",
+ "test": "your-test-command",
+ "clean": "rimraf dist types",
+ "prepare": "npm run build"
},
Committable suggestion skipped: line range outside the PR's diff.
? this.cureType._getZeroValue(this.crossEvaluateData.value) | ||
: this.defaultValue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Eliminate code duplication by extracting common logic.
This is the third occurrence of the same fallback value logic. Following the DRY principle, this is a strong indicator that we should extract this into a reusable method.
- Fix the indentation:
- ? this.cureType._getZeroValue(this.crossEvaluateData.value)
- : this.defaultValue;
+ ? this.cureType._getZeroValue(this.crossEvaluateData.value)
+ : this.defaultValue;
- Extract the common logic into a helper method as suggested earlier and use it in all three locations:
private _getFallbackValue(evaluateData: IEvaluateData<V>, additive: boolean): V {
return additive
? this.cureType._getZeroValue(evaluateData.value)
: this.defaultValue;
}
This will improve maintainability and reduce the likelihood of inconsistencies when making future changes.
🧰 Tools
🪛 eslint
[error] 129-129: Insert ··
(prettier/prettier)
[error] 130-130: Insert ··
(prettier/prettier)
fix lint error
Summary by CodeRabbit
New Features
UIUtils
class for managing UI rendering.UICanvas
,UIGroup
, andUIRenderer
classes for UI component management.Button
,Image
, andLabel
.ColorTransition
,ScaleTransition
, andSpriteTransition
.IElement
andIGroupAble
interfaces to enhance UI element management.Bug Fixes
UIPointerEventEmitter
.Documentation
Chores