Skip to content
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

Opt physics experience and opt physics unit test #2420

Merged
merged 55 commits into from
Nov 28, 2024

Conversation

luzhuang
Copy link
Contributor

@luzhuang luzhuang commented Nov 5, 2024

Please check if the PR fulfills these requirements

  • The commit message follows our guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)

What is the current behavior? (You can also link to an open issue here)

What is the new behavior (if this is a feature change)?

Does this PR introduce a breaking change? (What changes might users need to make in their application due to this PR?)

Other information:

Summary by CodeRabbit

  • New Features

    • Enhanced property management in the CharacterController, Collider, and DynamicCollider classes.
    • New methods for retrieving physical properties in IDynamicCollider and PhysXDynamicCollider.
    • Added setContactOffset method in PhysXCharacterController.
    • Introduced new internal properties and methods for managing collider shapes in PhysXCollider and LiteCollider.
    • Updated handling of scaling in PhysXBoxColliderShape and PhysXCapsuleColliderShape to utilize _worldScale.
    • Removed swing offset functionality from HingeJoint and SpringJoint.
  • Bug Fixes

    • Improved error handling for shape management in various classes.
  • Documentation

    • Updated documentation for properties and methods across multiple classes to clarify usage and constraints.
  • Tests

    • Expanded and refined test suites for CharacterController, DynamicCollider, and various joint classes, improving coverage and error handling.
    • Added tests for cloning functionality in ColliderShape and verified the handling of contactOffset.

Copy link

coderabbitai bot commented Nov 5, 2024

Walkthrough

The pull request introduces extensive modifications across various classes and interfaces within the physics engine. Key changes include the addition of new properties and methods, updates to existing property management, and a shift from cloning to synchronization of properties with native counterparts. Several methods related to collider shapes have been removed or refactored, enhancing encapsulation and simplifying the management of shapes within the physics scene. The test suites have been significantly expanded and refined to cover new functionalities and ensure robust testing of the physics components.

Changes

File Path Change Summary
packages/core/src/physics/CharacterController.ts - Added private property _upDirection with @deepClone annotation.
- Updated addShape to enforce single shape constraint.
- Removed clearShapes.
- Streamlined _onEnableInScene and _onDisableInScene.
- Introduced _syncNative method.
packages/core/src/physics/Collider.ts - Refactored shape management methods.
- Simplified clearShapes to use _removeNativeShape.
- Added _handleShapesChanged and _syncNative methods.
packages/core/src/physics/DynamicCollider.ts - Removed @ignoreClone from several properties.
- Added properties for automatic center of mass and inertia tensor.
- Updated methods to check _phasedActiveInScene.
- Introduced isSleeping() method.
packages/core/src/physics/PhysicsMaterial.ts - Changed default values for _bounciness, _dynamicFriction, and _staticFriction from 0.1 to 0.
- Updated documentation for bounciness getter.
packages/core/src/physics/joint/FixedJoint.ts - Removed _onAwake method.
- Added _createJoint method with error handling.
packages/core/src/physics/joint/HingeJoint.ts - Changed @ignoreClone to @deepClone for several properties.
- Removed swingOffset.
- Updated getters and setters for axis, angle, and velocity.
packages/core/src/physics/joint/Joint.ts - Changed to abstract class.
- Added static and private properties.
- Introduced new methods for managing anchors and synchronization.
packages/core/src/physics/joint/JointLimits.ts - Changed properties to private with corresponding getters and setters.
- Added validation in setters.
packages/core/src/physics/joint/SpringJoint.ts - Removed swingOffset.
- Introduced _createJoint and _syncNative methods.
packages/core/src/physics/shape/BoxColliderShape.ts - Changed _size to @deepClone.
- Removed _cloneTo, added _syncNative.
packages/design/src/physics/IDynamicCollider.ts - Added several new methods for retrieving physical properties.
packages/design/src/physics/joints/IHingeJoint.ts - Removed setSwingOffset method.
packages/design/src/physics/joints/IJoint.ts - Renamed and added methods for setting anchors and inertia scales.
packages/physics-lite/src/LiteDynamicCollider.ts - Implemented additional methods from IDynamicCollider interface with error logging for unsupported functionality.
packages/physics-physx/src/PhysXCharacterController.ts - Added setContactOffset method and new internal property _scene.
packages/physics-physx/src/PhysXDynamicCollider.ts - Added several new methods for physical properties.
packages/physics-physx/src/PhysXPhysics.ts - Updated WebAssembly script URL in initialize method.
packages/physics-physx/src/joint/PhysXFixedJoint.ts - Modified parameters in createFixedJoint method.
packages/physics-physx/src/joint/PhysXHingeJoint.ts - Introduced new properties and updated constructor parameters.
packages/physics-physx/src/joint/PhysXJoint.ts - Significant refactoring of joint methods and properties for clarity and functionality.
packages/physics-physx/src/shape/PhysXColliderShape.ts - Added _contractOffset property and modified setContactOffset method.
tests/src/core/physics/CharacterController.test.ts - Enhanced test suite with new imports, helper functions, and tests for various functionalities.
tests/src/core/physics/Collider.test.ts - Introduced utility functions and expanded test cases for collider functionalities.
tests/src/core/physics/DynamicCollider.test.ts - Updated tests for properties and behaviors of DynamicCollider.
tests/src/core/physics/HingeJoint.test.ts - Comprehensive tests for HingeJoint functionalities.
tests/src/core/physics/Joint.test.ts - Revised test suite with new tests focusing on joint behaviors and properties.
tests/src/core/physics/PhysicsMaterial.test.ts - Restructured tests to cover various physics material behaviors.
tests/src/core/physics/SpringJoint.test.ts - Established a new test suite for SpringJoint functionalities.
packages/core/src/physics/shape/ColliderShape.ts - Enhanced cloning and synchronization mechanisms.
packages/core/src/physics/joint/JointMotor.ts - Refactored properties for encapsulation and introduced update management.
packages/core/src/physics/shape/CapsuleColliderShape.ts - Transitioned from cloning to synchronization for properties.
packages/core/src/physics/shape/SphereColliderShape.ts - Similar changes as CapsuleColliderShape, focusing on synchronization.
packages/core/src/physics/PhysicsScene.ts - Removed methods for managing collider shapes, focusing on colliders and character controllers.
packages/design/src/physics/IPhysicsScene.ts - Removed methods for managing collider shapes.
packages/physics-lite/src/LiteCollider.ts - Integrated shape management logic with LitePhysicsScene.
packages/physics-lite/src/LitePhysicsScene.ts - Removed public methods for collider shape management, added internal methods.
packages/physics-physx/src/PhysXCollider.ts - Enhanced shape management with new properties and methods.
packages/physics-physx/src/PhysXPhysicsScene.ts - Centralized collider shape management into new internal methods.
tests/src/core/physics/ColliderShape.test.ts - Updated tests for contactOffset and added cloning tests.
packages/core/src/Engine.ts - Introduced static property _physicalObjectsMap, changing its accessibility.

Suggested reviewers

  • MrKou47
  • zhuxudong

Poem

In the land of code where rabbits play,
Changes hop in a bright new way.
With shapes and joints, we leap and bound,
In physics realms, new magic found.
So let us cheer, with a joyful thump,
For all the tweaks that make our hearts jump! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

codecov bot commented Nov 5, 2024

Codecov Report

Attention: Patch coverage is 92.67327% with 74 lines in your changes missing coverage. Please review.

Project coverage is 67.10%. Comparing base (35f10e2) to head (f8dc9c4).
Report is 28 commits behind head on dev/1.4.

Files with missing lines Patch % Lines
packages/physics-lite/src/LiteDynamicCollider.ts 56.96% 34 Missing ⚠️
packages/physics-physx/src/PhysXPhysicsScene.ts 81.39% 8 Missing ⚠️
packages/core/src/physics/DynamicCollider.ts 95.94% 6 Missing ⚠️
packages/core/src/physics/joint/Joint.ts 96.73% 6 Missing ⚠️
packages/physics-physx/src/PhysXDynamicCollider.ts 91.89% 6 Missing ⚠️
packages/physics-lite/src/LitePhysicsScene.ts 86.48% 5 Missing ⚠️
packages/core/src/physics/joint/JointLimits.ts 95.65% 4 Missing ⚠️
...ages/physics-physx/src/shape/PhysXColliderShape.ts 60.00% 4 Missing ⚠️
...ysics-physx/src/shape/PhysXCapsuleColliderShape.ts 66.66% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           dev/1.4    #2420      +/-   ##
===========================================
- Coverage    67.22%   67.10%   -0.13%     
===========================================
  Files          895      897       +2     
  Lines        92094    93218    +1124     
  Branches      7309     7576     +267     
===========================================
+ Hits         61912    62550     +638     
- Misses       29937    30423     +486     
  Partials       245      245              
Flag Coverage Δ
unittests 67.10% <92.67%> (-0.13%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 23

🧹 Outside diff range and nitpick comments (53)
packages/core/src/physics/joint/FixedJoint.ts (1)

9-12: Add error handling for missing collider component.

The refactoring to _createJoint improves the class structure and aligns with other joint implementations. However, consider adding error handling for cases where the collider component is missing.

Consider this safer implementation:

  protected _createJoint(): void {
    const colliderInfo = this._colliderInfo;
    colliderInfo.collider = this.entity.getComponent(Collider);
+   if (!colliderInfo.collider) {
+     throw new Error("FixedJoint requires a Collider component on the entity");
+   }
    this._nativeJoint = PhysicsScene._nativePhysics.createFixedJoint(colliderInfo.collider._nativeCollider);
+   if (!this._nativeJoint) {
+     throw new Error("Failed to create native fixed joint");
+   }
  }
packages/core/src/physics/joint/JointLimits.ts (2)

9-11: Enhance contactDistance documentation with validation rules

While the default value calculation is now documented, the documentation could be improved by:

  1. Explaining why these specific default values were chosen
  2. Documenting any validation rules for custom values
  3. Providing example calculations

Consider expanding the documentation:

  /** Distance inside the limit value at which the limit will be considered to be active by the solver.
   * Default is the lesser of 0.1 radians, and 0.49 * (upperLimit - lowerLimit)
+  * @example
+  * // For limits between 0 and π/2 radians:
+  * // contactDistance = min(0.1, 0.49 * (π/2 - 0)) ≈ 0.1
+  * @remarks
+  * The 0.49 factor ensures stable constraint solving by preventing the contact
+  * distance from being too close to the total range.
   */

Line range hint 1-18: Consider adding validation in the constructor/setters

The class could benefit from runtime validation to ensure proper usage of the new radian-based limits.

Consider adding validation:

 export class JointLimits {
+  private static readonly MAX_CONTACT_DISTANCE = Math.PI * 2;
+
   /** The upper angular limit (in radians) of the joint. */
-  max: number = 0;
+  private _max: number = 0;
+  get max(): number { return this._max; }
+  set max(value: number) {
+    if (value < -Math.PI * 2 || value > Math.PI * 2) {
+      console.warn('Joint limit outside expected radian range [-2π, 2π]');
+    }
+    this._max = value;
+  }

   /** The lower angular limit (in radians) of the joint. */
-  min: number = 0;
+  private _min: number = 0;
+  get min(): number { return this._min; }
+  set min(value: number) {
+    if (value < -Math.PI * 2 || value > Math.PI * 2) {
+      console.warn('Joint limit outside expected radian range [-2π, 2π]');
+    }
+    if (value > this._max) {
+      throw new Error('Lower limit cannot be greater than upper limit');
+    }
+    this._min = value;
+  }

   /** Distance inside the limit value at which the limit will be considered to be active by the solver.
    * Default is the lesser of 0.1 radians, and 0.49 * (upperLimit - lowerLimit)
    */
-  contactDistance: number = -1;
+  private _contactDistance: number = -1;
+  get contactDistance(): number {
+    if (this._contactDistance === -1) {
+      return Math.min(0.1, 0.49 * (this._max - this._min));
+    }
+    return this._contactDistance;
+  }
+  set contactDistance(value: number) {
+    if (value < 0) {
+      throw new Error('Contact distance must be non-negative');
+    }
+    if (value > JointLimits.MAX_CONTACT_DISTANCE) {
+      console.warn('Large contact distance may impact performance');
+    }
+    this._contactDistance = value;
+  }
packages/physics-physx/src/joint/PhysXFixedJoint.ts (1)

Line range hint 8-22: Documentation and test coverage needed.

Given this is a significant behavioral change in joint initialization:

  1. The class documentation should be updated to clarify the joint's attachment behavior (whether it's between two colliders or to the world)
  2. Unit tests should be added to verify both attachment scenarios

Would you like me to:

  1. Generate improved documentation that clarifies the joint behavior?
  2. Create unit tests covering both attachment scenarios?
packages/core/src/physics/shape/BoxColliderShape.ts (1)

36-36: Consider using class fields with arrow functions for auto-binding.

While the current binding approach works, using class fields with arrow functions could provide a cleaner and more maintainable solution that automatically preserves the 'this' context.

Consider refactoring to:

-private _setSize(): void {
+private _setSize = (): void => {
   (<IBoxColliderShape>this._nativeShape).setSize(this._size);
 }

 constructor() {
   super();
   this._nativeShape = PhysicsScene._nativePhysics.createBoxColliderShape(
     this._id,
     this._size,
     this._material._nativeMaterial
   );
-  this._size._onValueChanged = this._setSize.bind(this);
+  this._size._onValueChanged = this._setSize;
 }

This approach:

  1. Eliminates the need for explicit binding
  2. Ensures the method is always bound correctly
  3. Reduces the chance of binding-related bugs
packages/design/src/physics/joints/IJoint.ts (4)

19-20: Add JSDoc documentation for the setAnchor method.

The method lacks documentation to explain its purpose and behavior. Please add JSDoc comments similar to setConnectedAnchor to clarify:

  • The purpose of this anchor point
  • Whether it's in world or local space
  • How it relates to connectedAnchor
+  /**
+   * The anchor position.
+   * @remarks Specify whether this is in world space or local space, and how it differs from connectedAnchor.
+   */
   setAnchor(value: Vector3): void;

27-29: Consider clarifying the naming pattern for mass scale methods.

The current naming pattern might be confusing:

  • setConnectedMassScale affects collider 0
  • setMassScale affects collider 1

This seems counterintuitive as "connected" typically implies the secondary entity. Consider either:

  1. Swapping the collider numbers to match the naming
  2. Renaming the methods to better reflect which collider they affect

50-51: Add comprehensive documentation for the destroy method.

This cleanup method requires documentation to specify:

  • When it should be called
  • What resources it cleans up
  • Whether it's safe to call multiple times
  • Any side effects or interactions with other joint properties
+  /**
+   * Cleans up resources associated with this joint.
+   * @remarks Document when this should be called, what it cleans up, and whether it's safe for multiple calls.
+   */
   destroy(): void;

Line range hint 1-51: Consider adding corresponding getter methods.

The interface currently only provides setter methods. Consider adding corresponding getters for properties like:

  • getAnchor(): Vector3
  • getConnectedAnchor(): Vector3
  • getMassScale(): number
  • etc.

This would make it easier to:

  • Read and validate joint configurations
  • Debug joint behavior
  • Implement features that need to query joint state
packages/design/src/physics/ICharacterController.ts (1)

52-55: Enhance documentation with more details about contact offset.

The JSDoc should explain:

  • The purpose of contact offset in character controller physics
  • The valid range of values (if any)
  • The impact on physics simulation
  • Default value (if any)

Example enhancement:

  /**
   * Sets the contact offset.
+  * Contact offset determines the distance at which the character controller starts
+  * detecting potential collisions. A larger value provides earlier collision detection
+  * but may impact performance.
   * @param offset The new contact offset for the controller.
+  * @remarks Typical values range from 0.01 to 0.1. Default is 0.02.
   */
packages/core/src/physics/PhysicsMaterial.ts (1)

30-32: Enhance property documentation

While specifying the range is helpful, the documentation could be more comprehensive to help users choose appropriate values.

Consider expanding the documentation:

 /**
- * The coefficient of bounciness, ranging from 0 to 1.
+ * The coefficient of bounciness, ranging from 0 to 1.
+ * @default 0
+ * @remarks
+ * - 0: No bounce (perfectly inelastic collision)
+ * - 1: Perfect bounce (perfectly elastic collision)
+ * - Common values: 0.3-0.5 for rubber, 0.7-0.9 for bouncy materials
  */
packages/core/src/physics/joint/SpringJoint.ts (1)

93-99: Optimize type casting and add null check.

The repeated type casting to ISpringJoint can be optimized, and a null check should be added for the native joint.

Consider this optimization:

 protected override _syncBackends(): void {
   super._syncBackends();
+  if (!this._nativeJoint) {
+    return;
+  }
+  const nativeJoint = <ISpringJoint>this._nativeJoint;
-  (<ISpringJoint>this._nativeJoint).setMinDistance(this._minDistance);
-  (<ISpringJoint>this._nativeJoint).setMaxDistance(this._maxDistance);
-  (<ISpringJoint>this._nativeJoint).setTolerance(this._tolerance);
-  (<ISpringJoint>this._nativeJoint).setStiffness(this._stiffness);
-  (<ISpringJoint>this._nativeJoint).setDamping(this._damping);
+  nativeJoint.setMinDistance(this._minDistance);
+  nativeJoint.setMaxDistance(this._maxDistance);
+  nativeJoint.setTolerance(this._tolerance);
+  nativeJoint.setStiffness(this._stiffness);
+  nativeJoint.setDamping(this._damping);
 }
packages/physics-physx/src/joint/PhysXJoint.ts (2)

34-42: Enhance documentation for anchor setting methods.

While the implementation is correct, the documentation could be more descriptive about:

  • The coordinate space used (local vs. world)
  • The effect of setting anchors on joint behavior
  • The relationship between the two anchor points
   /**
-   * {@inheritDoc IJoint.setConnectedAnchor }
+   * Sets the anchor point for the first body in its local coordinate space.
+   * This point defines where the joint is attached to the first body.
+   * @param value The anchor point in the first body's local space
    */

89-91: Enhance resource cleanup in destroy method.

To prevent memory leaks and dangling references, consider clearing all internal references after releasing the joint.

   destroy(): void {
     this._pxJoint.release();
+    this._pxJoint = null;
+    this._collider = null;
   }
packages/physics-physx/src/joint/PhysXHingeJoint.ts (3)

11-14: LGTM! Consider making _xAxis readonly for safety.

The new properties improve encapsulation by maintaining local state. The static _xAxis is appropriately shared across instances.

Consider making _xAxis readonly to prevent accidental modifications:

- protected static _xAxis = new Vector3(1, 0, 0);
+ protected static readonly _xAxis = new Vector3(1, 0, 0);

34-43: LGTM! Consider caching intermediate calculations.

The axis calculation and anchor updates are correct. However, the cross product and angle calculations could be cached if setAxis is called frequently.

Consider caching intermediate calculations:

  setAxis(value: Vector3): void {
    const xAxis = PhysXHingeJoint._xAxis;
    const axisRotationQuaternion = this._axisRotationQuaternion;
+   const normalizedValue = value.clone().normalize();
-   xAxis.set(1, 0, 0);
-   value.normalize();
-   const angle = Math.acos(Vector3.dot(xAxis, value));
-   Vector3.cross(xAxis, value, xAxis);
+   const angle = Math.acos(Vector3.dot(xAxis, normalizedValue));
+   const crossResult = Vector3.cross(xAxis, normalizedValue, new Vector3());
-   Quaternion.rotationAxisAngle(xAxis, angle, axisRotationQuaternion);
+   Quaternion.rotationAxisAngle(crossResult, angle, axisRotationQuaternion);
    this._setLocalPose(0, this._anchor, axisRotationQuaternion);
    this._setLocalPose(1, this._connectedAnchor, axisRotationQuaternion);
  }

45-52: LGTM! Add JSDoc comments for overridden methods.

The anchor methods correctly maintain local state and update poses. Consider adding documentation for clarity.

Add JSDoc comments to document the overridden behavior:

+ /**
+  * Sets the anchor point relative to the first actor.
+  * @param value The anchor point in world space.
+  */
  override setAnchor(value: Vector3): void {
    this._setLocalPose(0, value, this._axisRotationQuaternion);
    this._anchor.copyFrom(value);
  }

+ /**
+  * Sets the anchor point relative to the second actor.
+  * @param value The anchor point in world space.
+  */
  override setConnectedAnchor(value: Vector3): void {
    this._setLocalPose(1, value, this._axisRotationQuaternion);
    this._connectedAnchor.copyFrom(value);
  }
packages/design/src/physics/IDynamicCollider.ts (3)

22-23: Add JSDoc comments for damping getter methods.

The getter methods getLinearDamping() and getAngularDamping() should have JSDoc comments for consistency with their corresponding setter methods.

Apply these additions:

+  /**
+   * Gets the linear damping coefficient.
+   * @returns Linear damping coefficient.
+   */
   getLinearDamping(): number;

+  /**
+   * Gets the angular damping coefficient.
+   * @returns Angular damping coefficient.
+   */
   getAngularDamping(): number;

Also applies to: 30-30


49-50: Add JSDoc comment for angular velocity getter method.

The getAngularVelocity() method should have JSDoc comments for consistency with getLinearVelocity().

Apply this addition:

+  /**
+   * Get the angular velocity of the actor.
+   * @param out - The angular velocity of the actor.
+   */
   getAngularVelocity(out: Vector3): Vector3;

98-99: Add JSDoc comments for sleep-related getter methods.

The sleep-related getter methods should have documentation for consistency and clarity.

Apply these additions:

+  /**
+   * Gets the mass-normalized kinetic energy threshold below which an actor may go to sleep.
+   * @returns Energy threshold for sleeping.
+   */
   getSleepThreshold(): number;

+  /**
+   * Checks if the dynamic collider is currently sleeping.
+   * @returns True if the collider is sleeping, false otherwise.
+   */
   isSleeping(): boolean;

Also applies to: 148-149

packages/core/src/physics/Collider.ts (1)

145-148: Consider improving method name and adding error handling.

While centralizing shape synchronization is good, consider these improvements:

  1. The method name _syncBackends is vague. Consider _syncShapesToPhysicsBackend to better describe its purpose.
  2. Add error handling for invalid shapes or failed synchronization attempts.
-  protected _syncBackends(): void {
+  protected _syncShapesToPhysicsBackend(): void {
     for (let i = 0, n = this.shapes.length; i < n; i++) {
+      if (!this.shapes[i]) {
+        console.warn(`Invalid shape at index ${i}`);
+        continue;
+      }
       this._addPhysicsShape(this.shapes[i]);
     }
   }
packages/core/src/physics/CharacterController.ts (3)

19-33: Add validation for contact offset value.

The contact offset implementation looks good, but consider adding validation to prevent negative or zero values which could cause physics simulation issues.

 set contactOffset(value: number) {
+  if (value <= 0) {
+    throw new Error("Contact offset must be greater than 0");
+  }
   if (this._contactOffset !== value) {
     this._contactOffset = value;
     (<ICharacterController>this._nativeCollider).setContactOffset(value);
   }
 }

17-18: Use Math.SQRT1_2 for better precision.

Instead of the approximate value 0.707, consider using Math.SQRT1_2 which provides the exact value for cos(45°).

-  private _slopeLimit: number = 0.707;
+  private _slopeLimit: number = Math.SQRT1_2;
🧰 Tools
🪛 Biome

[error] 18-18: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)


Line range hint 13-194: Consider performance optimization opportunities.

While the code structure is solid, there might be optimization opportunities:

  1. Cache frequently accessed values like this._nativeCollider
  2. Consider using object pooling for Vector3 instances in frequently called methods
  3. Profile the impact of constant physics updates, especially in _onUpdate and _onLateUpdate

Would you like me to elaborate on any of these optimization strategies?

🧰 Tools
🪛 Biome

[error] 18-18: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)

packages/physics-lite/src/LiteDynamicCollider.ts (1)

Line range hint 21-189: Add unit tests for new methods.

Static analysis indicates that many new methods lack test coverage. Even though these are stub implementations, it's important to verify their behavior, especially:

  1. Methods that return default values
  2. Error handling in getter methods
  3. Console error logging in setter methods

Would you like me to help generate unit tests for these methods? Here's an example of what they might look like:

describe('LiteDynamicCollider', () => {
  it('should return input vector for getInertiaTensor', () => {
    const collider = new LiteDynamicCollider(new Vector3(), new Quaternion());
    const input = new Vector3(1, 2, 3);
    expect(collider.getInertiaTensor(input)).toBe(input);
  });

  it('should throw error for getAngularDamping', () => {
    const collider = new LiteDynamicCollider(new Vector3(), new Quaternion());
    expect(() => collider.getAngularDamping()).toThrow();
  });
});
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 65-66: packages/physics-lite/src/LiteDynamicCollider.ts#L65-L66
Added lines #L65 - L66 were not covered by tests


[warning] 69-70: packages/physics-lite/src/LiteDynamicCollider.ts#L69-L70
Added lines #L69 - L70 were not covered by tests


[warning] 80-81: packages/physics-lite/src/LiteDynamicCollider.ts#L80-L81
Added lines #L80 - L81 were not covered by tests


[warning] 125-126: packages/physics-lite/src/LiteDynamicCollider.ts#L125-L126
Added lines #L125 - L126 were not covered by tests


[warning] 135-135: packages/physics-lite/src/LiteDynamicCollider.ts#L135
Added line #L135 was not covered by tests


[warning] 153-153: packages/physics-lite/src/LiteDynamicCollider.ts#L153
Added line #L153 was not covered by tests


[warning] 164-164: packages/physics-lite/src/LiteDynamicCollider.ts#L164
Added line #L164 was not covered by tests


[warning] 182-182: packages/physics-lite/src/LiteDynamicCollider.ts#L182
Added line #L182 was not covered by tests

packages/physics-physx/src/PhysXDynamicCollider.ts (1)

Line range hint 160-179: Consider optimizing the collision detection mode implementation.

The current implementation has some inconsistencies and potential improvements:

  1. Inconsistent usage of physX vs this._physXPhysics._physX
  2. Repeated access to flag properties

Consider this refactor for better maintainability:

 setCollisionDetectionMode(value: number): void {
   const physX = this._physXPhysics._physX;
+  const flags = physX.PxRigidBodyFlag;
 
   switch (value) {
     case CollisionDetectionMode.Continuous:
-      this._pxActor.setRigidBodyFlag(this._physXPhysics._physX.PxRigidBodyFlag.eENABLE_CCD, true);
+      this._pxActor.setRigidBodyFlag(flags.eENABLE_CCD, true);
       break;
     case CollisionDetectionMode.ContinuousDynamic:
-      this._pxActor.setRigidBodyFlag(physX.PxRigidBodyFlag.eENABLE_CCD, false);
-      this._pxActor.setRigidBodyFlag(this._physXPhysics._physX.PxRigidBodyFlag.eENABLE_CCD_FRICTION, true);
+      this._pxActor.setRigidBodyFlag(flags.eENABLE_CCD, false);
+      this._pxActor.setRigidBodyFlag(flags.eENABLE_CCD_FRICTION, true);
       break;
     case CollisionDetectionMode.ContinuousSpeculative:
-      this._pxActor.setRigidBodyFlag(physX.PxRigidBodyFlag.eENABLE_CCD, false);
-      this._pxActor.setRigidBodyFlag(this._physXPhysics._physX.PxRigidBodyFlag.eENABLE_SPECULATIVE_CCD, true);
+      this._pxActor.setRigidBodyFlag(flags.eENABLE_CCD, false);
+      this._pxActor.setRigidBodyFlag(flags.eENABLE_SPECULATIVE_CCD, true);
       break;
     case CollisionDetectionMode.Discrete:
-      this._pxActor.setRigidBodyFlag(physX.PxRigidBodyFlag.eENABLE_CCD, false);
-      this._pxActor.setRigidBodyFlag(physX.PxRigidBodyFlag.eENABLE_CCD_FRICTION, false);
-      this._pxActor.setRigidBodyFlag(physX.PxRigidBodyFlag.eENABLE_SPECULATIVE_CCD, false);
+      this._pxActor.setRigidBodyFlag(flags.eENABLE_CCD, false);
+      this._pxActor.setRigidBodyFlag(flags.eENABLE_CCD_FRICTION, false);
+      this._pxActor.setRigidBodyFlag(flags.eENABLE_SPECULATIVE_CCD, false);
       break;
   }
 }
tests/src/core/physics/SpringJoint.test.ts (3)

11-22: Enhance helper function type safety and maintainability

Consider these improvements to the addBox helper function:

  1. Use a more specific type for cubeSize instead of Vector3
  2. Extract friction values to named constants
  3. Add parameter validation
+ const ZERO_FRICTION = 0;
+
+ interface BoxDimensions {
+   width: number;
+   height: number;
+   depth: number;
+ }
+
- function addBox(cubeSize: Vector3, type: typeof DynamicCollider | typeof StaticCollider, pos: Vector3) {
+ function addBox(dimensions: BoxDimensions, type: typeof DynamicCollider | typeof StaticCollider, pos: Vector3) {
+   if (dimensions.width <= 0 || dimensions.height <= 0 || dimensions.depth <= 0) {
+     throw new Error('Box dimensions must be positive numbers');
+   }
+
    const boxEntity = rootEntity.createChild("BoxEntity");
    boxEntity.transform.setPosition(pos.x, pos.y, pos.z);

    const physicsBox = new BoxColliderShape();
-   physicsBox.material.dynamicFriction = 0;
-   physicsBox.material.staticFriction = 0;
+   physicsBox.material.dynamicFriction = ZERO_FRICTION;
+   physicsBox.material.staticFriction = ZERO_FRICTION;
-   physicsBox.size = cubeSize;
+   physicsBox.size = new Vector3(dimensions.width, dimensions.height, dimensions.depth);
    const boxCollider = boxEntity.addComponent(type);
    boxCollider.addShape(physicsBox);
    return boxEntity;
  }

35-192: Reduce code duplication in test setup

Each test case contains similar setup code. Consider extracting common setup logic to beforeEach or helper methods.

interface SpringJointTestSetup {
  boxEntity1: Entity;
  boxEntity2: Entity;
  joint: SpringJoint;
  collider1: DynamicCollider;
  collider2: DynamicCollider;
}

function setupSpringJointTest(config: Partial<{
  stiffness: number;
  damping: number;
  minDistance: number;
  maxDistance: number;
  tolerance: number;
}> = {}): SpringJointTestSetup {
  const boxEntity1 = addBox({ width: 1, height: 1, depth: 1 }, DynamicCollider, new Vector3(0, 5, 0));
  const boxEntity2 = addBox({ width: 1, height: 1, depth: 1 }, DynamicCollider, new Vector3(2, 5, 0));
  
  const joint = boxEntity2.addComponent(SpringJoint);
  const collider1 = boxEntity1.getComponent(DynamicCollider);
  const collider2 = boxEntity2.getComponent(DynamicCollider);
  
  joint.connectedCollider = collider1;
  collider1.isKinematic = true;
  joint.autoConnectedAnchor = false;
  joint.anchor = new Vector3(-0.5, 0, 0);
  joint.connectedAnchor = new Vector3(0.5, 0, 0);
  
  // Apply defaults or custom config
  joint.stiffness = config.stiffness ?? 10000;
  joint.damping = config.damping ?? 1000;
  joint.minDistance = config.minDistance ?? 1;
  joint.maxDistance = config.maxDistance ?? 5;
  joint.tolerance = config.tolerance ?? 0;
  
  return { boxEntity1, boxEntity2, joint, collider1, collider2 };
}
🧰 Tools
🪛 Biome

[error] 41-41: 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] 71-71: 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] 94-94: 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] 124-124: 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] 147-147: 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] 169-169: 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-192: Enhance test coverage with edge cases and error scenarios

While the current tests cover the basic functionality, consider adding:

  1. Error scenarios (invalid property values)
  2. Edge cases (zero/negative values)
  3. More descriptive assertion messages

Example additional test cases:

it("should throw error for invalid property values", () => {
  const { joint } = setupSpringJointTest();
  
  expect(() => { joint.stiffness = -1; }).toThrow();
  expect(() => { joint.damping = -1; }).toThrow();
  expect(() => { joint.minDistance = -1; }).toThrow();
});

it("should handle zero stiffness", () => {
  const { joint, boxEntity2, collider2 } = setupSpringJointTest({ stiffness: 0 });
  collider2.applyForce(new Vector3(100, 0, 0));
  updatePhysics(engine.sceneManager.activeScene, 1);
  // Expect no constraint forces
  expect(boxEntity2.transform.position.x).toBeGreaterThan(2);
});

Also consider adding more descriptive messages to existing assertions:

- expect(boxEntity2.transform.position.x).eq(2);
+ expect(boxEntity2.transform.position.x).eq(2, "Box should maintain its position when within tolerance");
🧰 Tools
🪛 Biome

[error] 41-41: 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] 71-71: 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] 94-94: 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] 124-124: 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] 147-147: 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] 169-169: 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)

tests/src/core/physics/HingeJoint.test.ts (6)

21-32: Document the purpose of zero friction settings.

The friction values are set to 0 without explanation. Consider adding a comment explaining why this is necessary for the tests.

 const physicsBox = new BoxColliderShape();
+// Set frictions to 0 to eliminate friction forces from affecting test results
 physicsBox.material.dynamicFriction = 0;
 physicsBox.material.staticFriction = 0;

34-36: Consider using a named constant for precision value.

The magic number 100000 should be extracted into a named constant to explain its purpose.

+const PRECISION_FACTOR = 100000; // 5 decimal places precision for floating-point comparisons
+
 function formatValue(value: number) {
-  return Math.round(value * 100000) / 100000;
+  return Math.round(value * PRECISION_FACTOR) / PRECISION_FACTOR;
 }

38-47: Add cleanup in afterAll hook.

The test suite should clean up resources in an afterAll hook to prevent memory leaks.

 beforeEach(function () {
   rootEntity.clearChildren();
   engine.sceneManager.activeScene.physics.gravity = new Vector3(0, -9.81, 0);
 });
+
+afterAll(async function () {
+  await engine.destroy();
+});

173-194: Fix typo in test name "gearRation".

The test name contains a typo - "gearRation" should be "gearRatio".

-it("gearRation", function () {
+it("gearRatio", function () {

49-231: Consider refactoring common test setup into shared fixtures.

Each test has similar setup code for creating boxes and configuring the joint. Consider extracting this into a shared fixture or helper function to reduce duplication.

interface TestFixture {
  boxEntity1: Entity;
  boxEntity2: Entity;
  joint: HingeJoint;
}

function createBasicJointSetup(pos2: Vector3): TestFixture {
  const boxEntity1 = addBox(new Vector3(1, 1, 1), DynamicCollider, new Vector3(0, 5, 0));
  const boxEntity2 = addBox(new Vector3(1, 1, 1), DynamicCollider, pos2);
  const collider1 = boxEntity1.getComponent(DynamicCollider);
  collider1.isKinematic = true;
  
  const joint = boxEntity1.addComponent(HingeJoint);
  joint.connectedCollider = boxEntity2.getComponent(DynamicCollider);
  joint.anchor = new Vector3(0.5, 0, 0);
  joint.axis = new Vector3(0, 1, 0);
  
  return { boxEntity1, boxEntity2, joint };
}

49-231: Consider using constants for common test values.

Various magic numbers are used throughout the tests (e.g., torque values, motor velocities). Consider extracting these into named constants to improve maintainability and document their purpose.

const TEST_CONSTANTS = {
  TORQUE_FORCE: 1000,
  MOTOR_VELOCITY: 30,
  DEFAULT_STIFFNESS: 100,
  DEFAULT_DAMPING: 30,
  // ... other constants
} as const;
tests/src/core/physics/Collider.test.ts (2)

73-73: Consider documenting the reason for the bounciness value.

The addition of bounciness = 0.1 aligns with the new _contactOffset in CharacterController, but this relationship isn't documented. Consider adding a comment explaining why this specific value was chosen and its relationship to other physics properties.

    physicsBox.size = new Vector3(cubeSize, cubeSize, cubeSize);
+    // Set bounciness to 0.1 to match CharacterController._contactOffset for consistent collision behavior
    physicsBox.material.bounciness = 0.1;

Line range hint 1-270: Consider enhancing test coverage for material properties.

While the test suite comprehensively covers collision scenarios, it could benefit from explicit tests for material properties like bounciness. Consider adding test cases that verify:

  1. Different bounciness values and their impact on collision response
  2. Interaction between bounciness and other material properties
  3. Edge cases (e.g., bounciness = 0, bounciness = 1)

Here's a suggested test case structure:

it("should handle different bounciness values correctly", function () {
  const testValues = [0, 0.1, 0.5, 1];
  
  for (const value of testValues) {
    // Reset entities
    rootEntity.clearChildren();
    // ... setup code ...
    
    physicsBox.material.bounciness = value;
    
    const boxCollider = boxEntity.addComponent(DynamicCollider);
    boxCollider.addShape(physicsBox);
    const sphereCollider = sphereEntity.addComponent(DynamicCollider);
    sphereCollider.addShape(physicsSphere);
    
    // Apply force and update physics
    sphereCollider.applyForce(new Vector3(500, 0, 0));
    for (let i = 0; i < 5; i++) {
      engine.physicsManager._update(16);
    }
    
    // Verify position changes based on bounciness
    // Higher bounciness should result in greater rebound distance
    const finalPosition = sphereEntity.transform.position.x;
    // Add appropriate assertions based on expected behavior
  }
});
tests/src/core/physics/DynamicCollider.test.ts (5)

21-32: Consider enhancing the addPlane helper function.

The function could benefit from the following improvements:

  • Add type annotations for parameters
  • Extract magic numbers (20, 1, 20) into named constants
  • Add parameter validation
+ const PLANE_SIZE = 20;
+ const PLANE_HEIGHT = 1;
+
- function addPlane(x: number, y: number, z: number) {
+ function addPlane(x: number, y: number, z: number): Entity {
+   if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) {
+     throw new Error('Invalid coordinates provided to addPlane');
+   }
    const planeEntity = rootEntity.createChild("PlaneEntity");
    planeEntity.transform.setPosition(x, y, z);
-   planeEntity.transform.setScale(20, 1, 20);
+   planeEntity.transform.setScale(PLANE_SIZE, PLANE_HEIGHT, PLANE_SIZE);

61-89: Consider adding edge cases to shape manipulation tests.

The shape manipulation tests could be more comprehensive by including:

  • Attempting to add null/undefined shapes
  • Testing shape removal when the shape doesn't exist
  • Verifying error cases
it("addShape and removeShape", function () {
  // ... existing tests ...
  
  // Add tests for edge cases
  expect(() => collider.addShape(null)).to.throw();
  expect(() => collider.addShape(undefined)).to.throw();
  
  const nonExistentShape = new BoxColliderShape();
  collider.removeShape(nonExistentShape);
  expect(collider.shapes.length).eq(0);
});

158-175: Consider extracting test constants and improving mass test assertions.

The mass test uses magic numbers and could benefit from:

  1. Named constants for test values
  2. More precise assertions with acceptable margins
  3. Documentation of the expected behavior
+ const TEST_MASS = 1;
+ const SMALL_MASS = 0.01;
+ const TEST_FORCE = new Vector3(1, 0, 0);
+ const VELOCITY_EPSILON = 0.0001;

  it("mass", function () {
    const box = addBox(new Vector3(2, 2, 2), DynamicCollider, new Vector3(0, 0, 0));
    const boxCollider = box.getComponent(DynamicCollider);

-   boxCollider.mass = 1;
+   boxCollider.mass = TEST_MASS;
-   expect(boxCollider.mass).eq(1);
+   expect(boxCollider.mass).eq(TEST_MASS);
-   boxCollider.applyForce(new Vector3(1, 0, 0));
+   boxCollider.applyForce(TEST_FORCE);
    updatePhysics(1);
-   expect(formatValue(boxCollider.linearVelocity.x)).eq(0.01667);
+   expect(Math.abs(boxCollider.linearVelocity.x - 0.01667)).lessThan(VELOCITY_EPSILON);

372-393: Enhance force and torque tests with more scenarios.

The force and torque tests could be more comprehensive by:

  1. Testing combined forces and torques
  2. Validating edge cases (zero force/torque)
  3. Testing force application at different points
it("combined forces and torques", function () {
  const box = addBox(new Vector3(2, 2, 2), DynamicCollider, new Vector3(0, 0, 0));
  const boxCollider = box.getComponent(DynamicCollider);
  
  boxCollider.applyForce(new Vector3(10, 0, 0));
  boxCollider.applyTorque(new Vector3(0, 10, 0));
  updatePhysics(1);
  
  // Verify combined motion
  expect(box.transform.position.x).greaterThan(0);
  expect(box.transform.rotation.y).not.eq(0);
});

it("zero force and torque", function () {
  const box = addBox(new Vector3(2, 2, 2), DynamicCollider, new Vector3(0, 0, 0));
  const boxCollider = box.getComponent(DynamicCollider);
  
  boxCollider.applyForce(new Vector3(0, 0, 0));
  boxCollider.applyTorque(new Vector3(0, 0, 0));
  updatePhysics(1);
  
  // Verify no motion
  expect(box.transform.position.x).eq(0);
  expect(box.transform.rotation.y).eq(0);
});

17-421: Consider adding additional test categories.

The test suite would benefit from:

  1. Error handling tests (e.g., invalid parameters, component initialization failures)
  2. Performance tests (e.g., multiple colliders, complex scenarios)
  3. Memory management tests (e.g., proper cleanup of physics resources)

Example test structure:

describe("DynamicCollider", () => {
  describe("Error Handling", () => {
    it("should handle invalid parameters gracefully", () => {
      // Test invalid mass, forces, etc.
    });
    
    it("should cleanup resources on initialization failure", () => {
      // Test cleanup
    });
  });
  
  describe("Performance", () => {
    it("should handle multiple simultaneous collisions", () => {
      // Create multiple colliding objects
    });
  });
});
tests/src/core/physics/CharacterController.test.ts (5)

103-103: Use Math.SQRT1_2 Instead of Approximate Numeric Constant

To enhance precision and readability, replace the approximate value 0.707 with the standard library constant Math.SQRT1_2.

Apply this diff:

- expect(controller.slopeLimit).eq(0.707);
+ expect(controller.slopeLimit).eq(Math.SQRT1_2);
🧰 Tools
🪛 Biome

[error] 103-103: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)


151-151: Remove Redundant Self-Assignment

Assigning controller.upDirection to itself is redundant and can be safely removed to clean up the code.

Apply this diff:

- controller.upDirection = controller.upDirection;
🧰 Tools
🪛 Biome

[error] 151-151: upDirection is assigned to itself.

This is where is assigned.

(lint/correctness/noSelfAssign)


182-182: Use Math.SQRT1_2 Instead of Approximate Numeric Constant

Replace 0.707 with Math.SQRT1_2 to utilize the precise value provided by the standard library.

Apply this diff:

- expect(controller.slopeLimit).eq(0.707);
+ expect(controller.slopeLimit).eq(Math.SQRT1_2);
🧰 Tools
🪛 Biome

[error] 182-182: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)


197-197: Use Math.SQRT1_2 Instead of Approximate Numeric Constant

For better precision and code clarity, consider using Math.SQRT1_2 instead of the hardcoded value 0.707.

Apply this diff:

- expect(controller.slopeLimit).eq(0.707);
+ expect(controller.slopeLimit).eq(Math.SQRT1_2);
🧰 Tools
🪛 Biome

[error] 197-197: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)


64-64: Set Gravity Once During Initialization

Setting physicsManager.gravity inside onPhysicsUpdate causes it to be reassigned every physics update, which is unnecessary. To improve performance, set the gravity once during initialization.

In the beforeEach function, add:

+ engine.sceneManager.activeScene.physics.gravity = new Vector3(0, -9.8, 0);

And remove the gravity assignment from onPhysicsUpdate:

- physicsManager.gravity = new Vector3(0, -9.8, 0);
packages/core/src/physics/DynamicCollider.ts (5)

3-3: Consider using a more specific import.

Instead of importing the entire CloneManager module, consider importing only the specific functions that are being used in this file, such as deepClone and ignoreClone. This can help improve the clarity and maintainability of the code.

-import { deepClone, ignoreClone } from "../clone/CloneManager";
+import { deepClone } from "../clone/CloneManager";

31-32: Ensure the automatic properties are thoroughly tested.

The new properties _automaticCenterOfMass and _automaticInertiaTensor have been introduced to allow for automatic updates of the center of mass and inertia tensor when mass changes or shapes are added or removed. To maintain the reliability and correctness of the physics simulation, it's crucial to thoroughly test these properties and their impact on the DynamicCollider's behavior.


108-117: Ensure the new automatic properties are properly documented.

The new automaticCenterOfMass and automaticInertiaTensor properties have been added to control the automatic updating of the center of mass and inertia tensor. To improve the usability and maintainability of the DynamicCollider class, ensure that these properties are properly documented with clear descriptions of their purpose, behavior, and any relevant considerations for users.

Also applies to: 135-146

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 109-110: packages/core/src/physics/DynamicCollider.ts#L109-L110
Added lines #L109 - L110 were not covered by tests


[warning] 116-117: packages/core/src/physics/DynamicCollider.ts#L116-L117
Added lines #L116 - L117 were not covered by tests


357-359: Consider renaming the isSleeping method for clarity.

The isSleeping method has been added to check whether the collider is currently sleeping. To improve the clarity and consistency of the method name, consider renaming it to isSleeping() (with parentheses) to match the naming convention of other methods in the class.

-  isSleeping(): boolean {
+  isSleeping(): boolean {

396-401: Ensure the _setMassAndUpdateInertia method is thoroughly tested.

The new _setMassAndUpdateInertia method has been introduced to set the mass and update the inertia tensor of the collider when the collider is active in the scene. Given the critical role of this method in maintaining the accuracy of the physics simulation, it's important to ensure that it is thoroughly tested with different scenarios and edge cases.

tests/src/core/physics/PhysicsMaterial.test.ts (1)

149-152: Use rotation constraints instead of setting large inertia tensor values.

To prevent the boxes from rotating, large inertia tensor values are assigned after disabling automatic inertia calculation. Setting such large values can lead to numerical instability. Instead, consider freezing the rotation axes using rigid body constraints provided by the physics engine. This approach is more stable and intention-revealing.

Here's how you might adjust the code:

-    collider.automaticInertiaTensor = false;
-    collider.inertiaTensor.set(10000, 10000, 10000);
+    collider.freezeRotation = true;

Also applies to: 172-177, 196-197, 224-226, 258-260, 292-294, 326-328, 364-366, 402-404, 440-442, 458-459, 382-384

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 35f10e2 and 790e7e5.

📒 Files selected for processing (29)
  • packages/core/src/physics/CharacterController.ts (4 hunks)
  • packages/core/src/physics/Collider.ts (2 hunks)
  • packages/core/src/physics/DynamicCollider.ts (14 hunks)
  • packages/core/src/physics/PhysicsMaterial.ts (2 hunks)
  • packages/core/src/physics/joint/FixedJoint.ts (1 hunks)
  • packages/core/src/physics/joint/HingeJoint.ts (2 hunks)
  • packages/core/src/physics/joint/Joint.ts (5 hunks)
  • packages/core/src/physics/joint/JointLimits.ts (1 hunks)
  • packages/core/src/physics/joint/SpringJoint.ts (1 hunks)
  • packages/core/src/physics/shape/BoxColliderShape.ts (1 hunks)
  • packages/design/src/physics/ICharacterController.ts (1 hunks)
  • packages/design/src/physics/IDynamicCollider.ts (4 hunks)
  • packages/design/src/physics/joints/IHingeJoint.ts (0 hunks)
  • packages/design/src/physics/joints/IJoint.ts (2 hunks)
  • packages/physics-lite/src/LiteDynamicCollider.ts (2 hunks)
  • packages/physics-physx/src/PhysXCharacterController.ts (1 hunks)
  • packages/physics-physx/src/PhysXDynamicCollider.ts (6 hunks)
  • packages/physics-physx/src/PhysXPhysics.ts (1 hunks)
  • packages/physics-physx/src/joint/PhysXFixedJoint.ts (1 hunks)
  • packages/physics-physx/src/joint/PhysXHingeJoint.ts (2 hunks)
  • packages/physics-physx/src/joint/PhysXJoint.ts (2 hunks)
  • packages/physics-physx/src/shape/PhysXColliderShape.ts (0 hunks)
  • tests/src/core/physics/CharacterController.test.ts (2 hunks)
  • tests/src/core/physics/Collider.test.ts (1 hunks)
  • tests/src/core/physics/DynamicCollider.test.ts (1 hunks)
  • tests/src/core/physics/HingeJoint.test.ts (1 hunks)
  • tests/src/core/physics/Joint.test.ts (1 hunks)
  • tests/src/core/physics/PhysicsMaterial.test.ts (1 hunks)
  • tests/src/core/physics/SpringJoint.test.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/design/src/physics/joints/IHingeJoint.ts
  • packages/physics-physx/src/shape/PhysXColliderShape.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/physics-physx/src/PhysXPhysics.ts
🧰 Additional context used
🪛 Biome
packages/core/src/physics/CharacterController.ts

[error] 18-18: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)

tests/src/core/physics/CharacterController.test.ts

[error] 103-103: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)


[error] 151-151: upDirection is assigned to itself.

This is where is assigned.

(lint/correctness/noSelfAssign)


[error] 182-182: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)


[error] 197-197: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)

tests/src/core/physics/Joint.test.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] 231-231: 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)

tests/src/core/physics/SpringJoint.test.ts

[error] 41-41: 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] 71-71: 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] 94-94: 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] 124-124: 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] 147-147: 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] 169-169: 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: codecov/patch
packages/core/src/physics/CharacterController.ts

[warning] 172-172: packages/core/src/physics/CharacterController.ts#L172
Added line #L172 was not covered by tests

packages/core/src/physics/Collider.ts

[warning] 142-143: packages/core/src/physics/Collider.ts#L142-L143
Added lines #L142 - L143 were not covered by tests

packages/core/src/physics/DynamicCollider.ts

[warning] 103-104: packages/core/src/physics/DynamicCollider.ts#L103-L104
Added lines #L103 - L104 were not covered by tests


[warning] 109-110: packages/core/src/physics/DynamicCollider.ts#L109-L110
Added lines #L109 - L110 were not covered by tests


[warning] 116-117: packages/core/src/physics/DynamicCollider.ts#L116-L117
Added lines #L116 - L117 were not covered by tests


[warning] 125-125: packages/core/src/physics/DynamicCollider.ts#L125
Added line #L125 was not covered by tests


[warning] 136-137: packages/core/src/physics/DynamicCollider.ts#L136-L137
Added lines #L136 - L137 were not covered by tests


[warning] 143-144: packages/core/src/physics/DynamicCollider.ts#L143-L144
Added lines #L143 - L144 were not covered by tests


[warning] 172-172: packages/core/src/physics/DynamicCollider.ts#L172
Added line #L172 was not covered by tests


[warning] 413-414: packages/core/src/physics/DynamicCollider.ts#L413-L414
Added lines #L413 - L414 were not covered by tests


[warning] 419-420: packages/core/src/physics/DynamicCollider.ts#L419-L420
Added lines #L419 - L420 were not covered by tests

packages/core/src/physics/joint/HingeJoint.ts

[warning] 145-149: packages/core/src/physics/joint/HingeJoint.ts#L145-L149
Added lines #L145 - L149 were not covered by tests

packages/core/src/physics/joint/Joint.ts

[warning] 39-39: packages/core/src/physics/joint/Joint.ts#L39
Added line #L39 was not covered by tests


[warning] 85-86: packages/core/src/physics/joint/Joint.ts#L85-L86
Added lines #L85 - L86 were not covered by tests


[warning] 125-126: packages/core/src/physics/joint/Joint.ts#L125-L126
Added lines #L125 - L126 were not covered by tests


[warning] 209-210: packages/core/src/physics/joint/Joint.ts#L209-L210
Added lines #L209 - L210 were not covered by tests


[warning] 218-219: packages/core/src/physics/joint/Joint.ts#L218-L219
Added lines #L218 - L219 were not covered by tests


[warning] 252-253: packages/core/src/physics/joint/Joint.ts#L252-L253
Added lines #L252 - L253 were not covered by tests


[warning] 257-260: packages/core/src/physics/joint/Joint.ts#L257-L260
Added lines #L257 - L260 were not covered by tests

packages/physics-lite/src/LiteDynamicCollider.ts

[warning] 22-23: packages/physics-lite/src/LiteDynamicCollider.ts#L22-L23
Added lines #L22 - L23 were not covered by tests


[warning] 28-29: packages/physics-lite/src/LiteDynamicCollider.ts#L28-L29
Added lines #L28 - L29 were not covered by tests


[warning] 65-66: packages/physics-lite/src/LiteDynamicCollider.ts#L65-L66
Added lines #L65 - L66 were not covered by tests


[warning] 69-70: packages/physics-lite/src/LiteDynamicCollider.ts#L69-L70
Added lines #L69 - L70 were not covered by tests


[warning] 80-81: packages/physics-lite/src/LiteDynamicCollider.ts#L80-L81
Added lines #L80 - L81 were not covered by tests


[warning] 125-126: packages/physics-lite/src/LiteDynamicCollider.ts#L125-L126
Added lines #L125 - L126 were not covered by tests


[warning] 135-135: packages/physics-lite/src/LiteDynamicCollider.ts#L135
Added line #L135 was not covered by tests


[warning] 153-153: packages/physics-lite/src/LiteDynamicCollider.ts#L153
Added line #L153 was not covered by tests


[warning] 164-164: packages/physics-lite/src/LiteDynamicCollider.ts#L164
Added line #L164 was not covered by tests


[warning] 182-182: packages/physics-lite/src/LiteDynamicCollider.ts#L182
Added line #L182 was not covered by tests

packages/physics-physx/src/PhysXDynamicCollider.ts

[warning] 34-35: packages/physics-physx/src/PhysXDynamicCollider.ts#L34-L35
Added lines #L34 - L35 were not covered by tests

🔇 Additional comments (31)
packages/core/src/physics/joint/JointLimits.ts (1)

5-8: ⚠️ Potential issue

Breaking Change: Angular units changed from degrees to radians

The documentation update indicates a change from degrees to radians for both max and min properties. This is a breaking change that requires careful migration handling.

Let's verify the usage of these properties across the codebase:

Consider:

  1. Adding migration guide documentation
  2. Including runtime validation to detect values likely provided in degrees
  3. Adding deprecation warnings for the old degree-based API if maintaining backward compatibility
packages/physics-physx/src/joint/PhysXFixedJoint.ts (1)

14-20: ⚠️ Potential issue

Verify the actor parameter order change.

The modification in parameter order (swapping null and collider._pxActor) could significantly affect joint behavior. This change appears to modify the joint from being fixed to a collider to being fixed to the world/ground instead.

Please ensure:

  1. This change aligns with PhysX API requirements
  2. The new behavior is intentional and matches the physics requirements
  3. Existing simulations won't break with this change
packages/core/src/physics/shape/BoxColliderShape.ts (1)

36-36: Verify the timing of size updates in the physics engine.

The size change callback directly updates the native physics shape. Ensure this doesn't cause performance issues if the size is updated frequently during physics simulations.

Let's check for other size update patterns in the codebase:

✅ Verification successful

Let me gather more information about the value change handling patterns and physics update cycles.


Let me gather more information about the physics update cycle and size change handling.


Based on the analysis of the code, I can now provide a final response:

Size updates in physics are optimized and safe for frequent changes

The physics system is designed to handle size updates efficiently:

  • Size changes are batched and only applied during physics updates via the fixed timestep system
  • The physics engine uses a half-size optimization internally to reduce calculations
  • Updates are synchronized with the physics simulation cycle rather than being applied immediately
  • The implementation in both PhysX and Lite backends shows proper handling of scale changes
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Look for patterns of physics shape size updates
# to understand the update frequency and potential performance impact

# Check for direct size updates
rg -A 2 "setSize|_setSize" --type ts

# Look for Vector3 value change handlers in physics components
ast-grep --pattern 'class $_ extends ColliderShape {
  $$$
  _onValueChanged
  $$$
}'

Length of output: 1748


Script:

#!/bin/bash
# Check for other Vector3 value change handlers and update patterns
rg -A 5 "_onValueChanged|onUpdate" --type ts

# Look for physics update/simulation related code
rg -A 3 "updatePhysics|simulatePhysics|physicsUpdate" --type ts

# Check for any performance-related comments or documentation
rg -B 2 "performance|throttle|frequency" --type ts

Length of output: 127075


Script:

#!/bin/bash
# Check physics update implementation and frequency
rg -A 5 "_update|fixedUpdate" packages/core/src/physics/ --type ts

# Look for size-related physics shape implementations
rg -A 3 "setSize|_setSize" packages/physics-physx/src/ packages/physics-lite/src/ --type ts

# Check for any performance-related comments in physics code
rg -B 2 "performance|update" packages/core/src/physics/ packages/physics-physx/src/ packages/physics-lite/src/ --type ts

Length of output: 28768

packages/core/src/physics/PhysicsMaterial.ts (1)

9-11: ⚠️ Potential issue

Breaking Change: Default physics material properties changed to zero

Changing the default values from 0.1 to 0 for bounciness and friction properties is a breaking change that could significantly affect existing physics simulations. While starting from zero might be more predictable, it represents perfectly slippery surfaces with no bounce by default.

Let's verify the impact and usage of these properties:

Consider:

  1. Adding a migration guide for users
  2. Documenting the rationale for this change
  3. Potentially introducing a factory method for creating materials with the old default values
packages/physics-physx/src/joint/PhysXJoint.ts (1)

24-28: Verify the actor parameter order change across the codebase.

The parameter order in setActors has been reversed. While this might be intentional, we should verify that all derived joint classes and test cases have been updated accordingly to prevent incorrect physical connections.

✅ Verification successful

Let me analyze the results and generate additional verification scripts to understand the parameter order change better.

I need to see the actual implementations to verify if the parameter order change in setActors is correct and consistent with the interface design and base implementation.


After analyzing the code, I can now generate the final response:

The parameter order in setActors is correct and consistent with the design.

The implementation is verified to be correct because:

  1. The IJoint interface defines setConnectedCollider(value: ICollider) which is correctly implemented in PhysXJoint.
  2. The parameter order in setActors(this._collider?._pxActor || null, value?._pxActor || null) matches the expected behavior where:
    • First parameter is the actor from the joint's own collider
    • Second parameter is the actor from the connected collider being set
  3. This is consistent with the base Joint class implementation which manages the collider relationships in the same order.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for all joint implementations and their setActors usage
ast-grep --pattern 'class $_ extends PhysXJoint {
  $$$
}'

# Search for test cases involving setConnectedCollider
rg -l "setConnectedCollider.*" --type ts

Length of output: 14988


Script:

#!/bin/bash
# Check the base interface definition
cat packages/design/src/physics/joints/IJoint.ts

# Check the implementation in Joint.ts
cat packages/core/src/physics/joint/Joint.ts

# Check the full implementation of PhysXJoint
cat packages/physics-physx/src/joint/PhysXJoint.ts

Length of output: 13687

packages/physics-physx/src/joint/PhysXHingeJoint.ts (1)

21-24: Verify the reversed actor parameters in createRevoluteJoint.

The order of actors in createRevoluteJoint has been swapped. Please confirm this is intentional and doesn't affect joint behavior.

✅ Verification successful

The actor parameter order change in createRevoluteJoint is correct and intentional

Based on the code analysis:

  1. The change aligns with the PhysX joint creation pattern where the first actor is the primary body and the second is the connected body
  2. The test cases in HingeJoint.test.ts confirm this pattern, where the first collider is typically the kinematic/fixed body and the second is the dynamic/connected body
  3. The example in physx-joint-basic.ts also follows this pattern for hinge joint creation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other joint creation patterns to verify consistency
ast-grep --pattern 'createRevoluteJoint($_actor1, $_, $_, $_actor2, $_, $_)'

Length of output: 78


Script:

#!/bin/bash
# Let's try a broader search for revolute joint creation and usage patterns
rg -l "createRevoluteJoint" 

# Then look for the actual implementation and usage
rg "createRevoluteJoint" -A 5 -B 5

# Also search for any documentation or comments about joint creation
rg -l "RevoluteJoint|HingeJoint" 

# Look for test files that might show the expected usage
fd "test|spec" -e ts | xargs rg "RevoluteJoint|HingeJoint"

Length of output: 2529


Script:

#!/bin/bash
# Let's look at the PhysX implementation and interface definitions
cat packages/physics-physx/src/PhysXPhysics.ts

# Check the interface definition for hinge joint
cat packages/design/src/physics/joints/IHingeJoint.ts

# Look at example usage
cat examples/physx-joint-basic.ts

# Check the actual test implementation for proper usage
cat tests/src/core/physics/HingeJoint.test.ts

Length of output: 26624

packages/design/src/physics/IDynamicCollider.ts (1)

Line range hint 22-149: Overall changes look good!

The additions to IDynamicCollider interface:

  • Provide a more complete API surface for accessing physical properties
  • Maintain consistency with existing patterns
  • Follow TypeScript conventions
  • Don't introduce breaking changes

The interface is well-structured and the new methods enhance its functionality.

packages/core/src/physics/Collider.ts (2)

142-143: Add test coverage for cloning functionality.

The changes to _cloneTo lack test coverage. Since this is part of the ICustomClone interface and handles critical shape synchronization during cloning, it should be thoroughly tested.

Would you like me to help create test cases for the cloning functionality? The tests should verify:

  1. Shapes are properly synchronized after cloning
  2. No duplicate shapes are created
  3. Physics backend is properly updated
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 142-143: packages/core/src/physics/Collider.ts#L142-L143
Added lines #L142 - L143 were not covered by tests


123-123: ⚠️ Potential issue

Verify potential duplicate shape registration.

The _onEnableInScene method already iterates over shapes to register them with scene physics. The addition of _syncBackends() might cause shapes to be registered twice since _addPhysicsShape also registers shapes with scene physics when _phasedActiveInScene is true.

packages/physics-physx/src/PhysXCharacterController.ts (1)

57-62: LGTM! Implementation follows established patterns.

The implementation is consistent with other setter methods in the class, properly uses optional chaining for null safety, and is well-documented.

Let's verify the interface implementation is complete:

✅ Verification successful

Implementation correctly matches the interface and follows consistent usage pattern

The verification confirms:

  • The setContactOffset method signature in PhysXCharacterController exactly matches the ICharacterController interface definition
  • The implementation follows the same pattern used across the codebase in both PhysX and Lite physics implementations
  • The method is properly used by the core physics module through the interface
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all ICharacterController interface methods are implemented
# and that the setContactOffset signature matches the interface definition

# Search for the interface definition and implementation
ast-grep --pattern 'interface ICharacterController {
  $$$
  setContactOffset(offset: number): void;
  $$$
}'

# Verify no other signatures exist that might conflict
rg -A 1 'setContactOffset'

Length of output: 2151

packages/core/src/physics/joint/HingeJoint.ts (2)

37-37: LGTM! Type assertion is safe in this context.

The type assertion to IHingeJoint is safe here since the native joint is guaranteed to be initialized before this setter is called.


144-149: Add test coverage for motor synchronization.

The motor synchronization block lacks test coverage. Please add tests to verify that motor properties are correctly synchronized with the native backend.

Would you like me to help generate test cases for the motor synchronization logic?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 145-149: packages/core/src/physics/joint/HingeJoint.ts#L145-L149
Added lines #L145 - L149 were not covered by tests

packages/physics-physx/src/PhysXDynamicCollider.ts (4)

Line range hint 84-113: LGTM! Mass and inertia tensor handling is implemented correctly.

The methods for managing center of mass and inertia tensor properties are well-implemented with proper Vector3 conversions and clear method names.


Line range hint 115-147: LGTM! Physics property getters are properly implemented.

The new getter methods for maximum angular velocity, depenetration velocity, and sleep threshold are correctly implemented as straightforward pass-through methods to the PhysX actor.


240-242: LGTM! Sleep state checker is properly implemented.

The isSleeping method is correctly implemented as a pass-through to the PhysX actor and complements the existing sleep and wakeUp methods.


33-69: LGTM! Consider adding unit tests for the new getter methods.

The implementation of the getter methods for linear/angular damping and velocities is correct and maintains good symmetry with their corresponding setters. The Vector3 conversions are handled properly.

Let's verify the test coverage:

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 34-35: packages/physics-physx/src/PhysXDynamicCollider.ts#L34-L35
Added lines #L34 - L35 were not covered by tests

tests/src/core/physics/HingeJoint.test.ts (2)

1-15: LGTM! Imports are well-organized and complete.

All necessary dependencies are properly imported and logically grouped.


1-231: Overall, this is a well-structured and comprehensive test suite.

The test coverage is thorough, testing various aspects of the HingeJoint component including axis behavior, limits, motor controls, and free spin functionality. While there are some opportunities for improvement in code organization and documentation, the tests themselves are well-designed and provide good validation of the component's behavior.

tests/src/core/physics/Collider.test.ts (1)

Line range hint 89-270: Verify test stability with the new bounciness value.

The addition of bounciness could affect collision response timing. While the current tests use flexible assertions (toBeGreaterThan), we should verify that the tests remain stable across different environments and runs.

✅ Verification successful

Let me analyze the collision test cases and their assertions to determine if we can verify the stability through static analysis.


Based on the analysis of the test file, I can now generate a final response:

The test suite is resilient to the bounciness value change

The collision tests are well-designed to be stable with the new bounciness value (0.1) because:

  1. The collision event assertions (onCollisionEnter, onCollisionStay, onCollisionExit) use flexible toBeGreaterThan(1) checks that accommodate variations in physics simulation steps
  2. Position checks are binary (either equal(5) or not.to.be.equal(5)), not checking exact positions that could be affected by bounce physics
  3. The test cases properly separate different collision scenarios (Dynamic/Static/Kinematic) and their assertions match the expected behavior for each type
  4. The trigger-based tests are independent of bounciness as they only check for overlap events
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check test stability by running tests multiple times

# Test: Run the tests multiple times to check for consistent results
for i in {1..5}; do
  echo "Run #$i"
  # Using vitest to run specific test file
  npx vitest run tests/src/core/physics/Collider.test.ts
done

# Look for any test failures or inconsistencies in the results

Length of output: 844


Script:

#!/bin/bash
# Let's examine the test file and related physics components to understand the collision behavior

# Check for any bounciness-related changes in the test setup
rg -B 5 -A 5 "bounciness|restitution" tests/src/core/physics/Collider.test.ts

# Look for the physics box and sphere setup
rg -B 10 "physicsBox|physicsSphere" tests/src/core/physics/Collider.test.ts

# Check the CollisionScript implementation
fd -e ts -e js CollisionScript --exec cat {}

Length of output: 7783

packages/core/src/physics/joint/Joint.ts (2)

16-16: Appropriate change to abstract class

Converting Joint to an abstract class enhances the architecture by indicating that Joint is not meant to be instantiated directly but to be subclassed. This change improves code clarity and enforces proper usage.


19-22: Confirm necessity of deep cloning collider info

Replacing @ignoreClone with @deepClone for _colliderInfo and _connectedColliderInfo means these properties will be deeply cloned when cloning Joint instances. Ensure that deep cloning is required and does not introduce unintended side effects, such as unnecessary memory usage or performance implications.

packages/core/src/physics/DynamicCollider.ts (8)

66-67: LGTM!

The changes to the linearVelocity, angularVelocity, centerOfMass, inertiaTensor, and their respective getters now retrieve the values directly from the native collider. This ensures that the properties always reflect the current state of the collider, improving the accuracy and consistency of the physics simulation.

Also applies to: 80-81, 125-126, 136-137, 152-153


172-173: LGTM!

The changes to the applyForce, applyTorque, move, sleep, wakeUp, and other methods now include a check for the _phasedActiveInScene flag before executing their logic. This ensures that these operations are only performed when the collider is active in the scene, preventing unnecessary computations and potential errors.

Also applies to: 186-187, 200-201, 214-215, 228-229, 242-243, 256-257, 312-313, 320-321, 343-344, 350-351, 358-359, 365-366

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 172-172: packages/core/src/physics/DynamicCollider.ts#L172
Added line #L172 was not covered by tests


286-291: LGTM!

The addShape, removeShape, and clearShapes methods have been overridden to include calls to _setMassAndUpdateInertia() when either _automaticCenterOfMass or _automaticInertiaTensor is enabled. This ensures that the mass and inertia tensor are automatically updated whenever shapes are added, removed, or cleared, maintaining the accuracy and consistency of the physics simulation.

Also applies to: 293-298, 300-305


378-394: LGTM!

The _syncBackends method has been updated to synchronize various properties with the native collider, ensuring that the native collider always reflects the current state of the DynamicCollider instance. This improves the consistency and accuracy of the physics simulation across different backends.


405-406: LGTM!

The private methods _setLinearVelocity, _setAngularVelocity, _setCenterOfMass, and _setInertiaTensor have been updated to include checks for the _phasedActiveInScene flag and the respective automatic properties before updating the native collider. This ensures that the native collider is only updated when necessary, improving performance and maintaining consistency with the automatic updating behavior.

Also applies to: 408-409, 412-414, 418-420


25-26: Verify the impact of changing _maxDepenetrationVelocity to Infinity.

The _maxDepenetrationVelocity property has been updated from a fixed value of 1000 to Infinity. While this change may be intended to allow for unlimited depenetration velocity, it's important to assess the potential impact on the physics simulation and collision resolution.

#!/bin/bash
# Description: Verify the usage and impact of `_maxDepenetrationVelocity` property.

# Test: Search for usage of `_maxDepenetrationVelocity` property.
# Expect: Only accessed via getter/setter, no direct modifications.
rg -A 10 $'_maxDepenetrationVelocity'

# Test: Search for usage of `setMaxDepenetrationVelocity` method in other files.
# Expect: No manual setting of `maxDepenetrationVelocity` to a finite value.
rg -A 5 $'setMaxDepenetrationVelocity'

100-106: Verify the conditional logic for updating mass and inertia.

The mass setter now includes additional logic to conditionally call _setMassAndUpdateInertia() based on the values of _automaticInertiaTensor and _automaticCenterOfMass properties. While this change aims to improve the automatic updating of mass and inertia, it's important to verify that the conditional logic is correct and covers all necessary scenarios.

✅ Verification successful

Based on the search results, I can now generate the final response as I have enough information to verify the conditional logic for updating mass and inertia:

The conditional logic for mass and inertia updates is correctly implemented

The implementation follows a consistent pattern where:

  • _setMassAndUpdateInertia() is called when either _automaticInertiaTensor or _automaticCenterOfMass is true, which updates both mass and inertia together
  • setMass() is only called directly when both automatic properties are false, which updates just the mass value
  • This pattern is consistently applied across the codebase in various scenarios like shape removal, property changes, etc.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the conditional logic for updating mass and inertia.

# Test: Search for usage of `_setMassAndUpdateInertia` method.
# Expect: Called only when `_automaticInertiaTensor` or `_automaticCenterOfMass` is true.
rg -A 10 $'_setMassAndUpdateInertia'

# Test: Search for usage of `setMass` method.
# Expect: Called only when both `_automaticInertiaTensor` and `_automaticCenterOfMass` are false.
rg -A 10 $'setMass'

Length of output: 21713

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 103-104: packages/core/src/physics/DynamicCollider.ts#L103-L104
Added lines #L103 - L104 were not covered by tests


15-24: Verify the impact of changing @ignoreClone to @deepClone.

The @ignoreClone decorator has been replaced with @deepClone for several private properties. This change indicates a shift in how these properties are cloned. Ensure that this change aligns with the intended behavior and does not introduce any unintended side effects or performance implications.

✅ Verification successful

Let me generate additional scripts to properly verify the impact of changing @ignoreClone to @deepClone.

This will help us understand:

  1. How cloning is implemented in the DynamicCollider class
  2. The implementation differences between @deepClone and @ignoreClone decorators
  3. The cloning behavior of Vector3 objects which are the types being decorated

Let me generate additional scripts to understand the cloning behavior and its impact.

This will help us understand:

  1. How Vector3 implements cloning
  2. How these properties are used in DynamicCollider
  3. The implementation difference between @deepClone and @ignoreClone decorators

Let me generate one final script to check the clone decorator implementations.

This will help us understand:

  1. The implementation details of @deepClone and @ignoreClone decorators
  2. The Vector3 class implementation and its cloning behavior

Based on the code analysis, I can now provide a final response about the impact of changing from @ignoreClone to @deepClone.

The change from @ignoreClone to @deepClone for physics properties is appropriate and beneficial.

The change affects how _linearVelocity, _angularVelocity, _centerOfMass, and _inertiaTensor Vector3 properties are handled during cloning:

  • With @ignoreClone, these properties were completely skipped during cloning, which could lead to shared references between cloned instances
  • With @deepClone, each cloned DynamicCollider gets its own independent copies of these Vector3 properties, preventing unwanted shared state

The implementation shows these properties are used for critical physics calculations and are properly synchronized with the native physics engine through setter methods. Having independent copies for each collider instance is important because:

  1. Each property has its own _onValueChanged callback that syncs with the native physics engine
  2. The values are frequently updated through physics simulation
  3. Independent instances prevent one collider's physics state from accidentally affecting another
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage and impact of `@deepClone` decorator.

# Test: Search for usage of `@deepClone` decorator. 
# Expect: Only used on intended properties.
rg -A 5 $'@deepClone'

# Test: Search for usage of `_linearVelocity`, `_angularVelocity`, `_centerOfMass`, `_inertiaTensor` properties.
# Expect: No direct modifications, only accessed via getters/setters.
rg -A 10 $'_linearVelocity|_angularVelocity|_centerOfMass|_inertiaTensor'

Length of output: 6797


Script:

#!/bin/bash
# Search for clone-related functionality and usage
rg -A 5 "clone\(\)" packages/core/src/physics/DynamicCollider.ts

# Search for decorator definitions and their implementations
rg -A 5 "@deepClone|@ignoreClone" packages/core/src/

# Search for Vector3 cloning behavior
rg -A 5 "Vector3.*clone" packages/core/src/

Length of output: 67754


Script:

#!/bin/bash
# Search for clone-related functionality in Vector3 and related classes
rg -A 5 "clone\(\)" packages/core/src/math/Vector3.ts

# Search for DynamicCollider's usage of these properties
rg -A 5 "(_linearVelocity|_angularVelocity|_centerOfMass|_inertiaTensor)" packages/core/src/physics/DynamicCollider.ts

# Search for clone decorator implementation
rg -A 10 "@deepClone|@ignoreClone" packages/core/src/clone/

Length of output: 4285


Script:

#!/bin/bash
# Search for clone decorator implementations and their usage
fd -e ts -x cat {} \; -p packages/core/src/clone/

# Search for Vector3 implementation
fd Vector3.ts -x cat {} \; -p packages/core/src/math/

Length of output: 8660

tests/src/core/physics/PhysicsMaterial.test.ts (2)

20-32: Well-structured helper function addPlane.

The addPlane function cleanly encapsulates the creation and setup of a plane entity with its physics properties. This enhances code readability and reuse across tests.


34-45: Efficient addBox helper function for entity creation.

The addBox function effectively abstracts the setup of box entities, allowing flexibility in specifying size, type, and position. This promotes clean and maintainable test code.

packages/design/src/physics/ICharacterController.ts Outdated Show resolved Hide resolved
packages/core/src/physics/joint/HingeJoint.ts Show resolved Hide resolved
tests/src/core/physics/SpringJoint.test.ts Show resolved Hide resolved
tests/src/core/physics/HingeJoint.test.ts Show resolved Hide resolved
packages/core/src/physics/joint/Joint.ts Show resolved Hide resolved
packages/core/src/physics/joint/Joint.ts Show resolved Hide resolved
tests/src/core/physics/Joint.test.ts Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Outside diff range and nitpick comments (17)
packages/core/src/physics/joint/SpringJoint.ts (1)

26-26: Consider extracting native joint casting to improve maintainability.

The repeated type casting pattern (<ISpringJoint>this._nativeJoint) could be simplified by creating a protected getter.

+  protected get _nativeSpringJoint(): ISpringJoint {
+    return this._nativeJoint as ISpringJoint;
+  }

   set minDistance(value: number) {
     if (this._minDistance !== value) {
       this._minDistance = value;
-      (<ISpringJoint>this._nativeJoint)?.setMinDistance(value);
+      this._nativeSpringJoint?.setMinDistance(value);
     }
   }

Also applies to: 40-40, 54-54, 68-68, 82-82

packages/core/src/physics/shape/ColliderShape.ts (1)

136-136: Consider adding error handling in _cloneTo.

The simplified _cloneTo method could benefit from basic error handling to ensure target is valid and initialized before calling _syncBackends().

 _cloneTo(target: ColliderShape) {
+    if (!target) {
+      throw new Error("Clone target cannot be null or undefined");
+    }
     target._syncBackends();
 }
packages/core/src/physics/joint/Joint.ts (1)

224-242: Add documentation for anchor calculations

The anchor calculation logic is complex and would benefit from detailed documentation explaining the mathematical transformations and coordinate space conversions.

Add JSDoc comments explaining:

  • The coordinate spaces involved (local vs world)
  • The mathematical relationship between anchors
  • Why different calculations are needed for connected vs unconnected cases

Example:

/**
 * Calculates the connected anchor position based on the self anchor and transforms.
 * For connected joints: Transforms self anchor to connected body's local space
 * For unconnected joints: Transforms self anchor to world space
 * @private
 */
private _calculateConnectedAnchor(): void {
tests/src/core/physics/SpringJoint.test.ts (1)

35-231: Consider improving test structure and reducing duplication

The test cases share a lot of common setup code which could be extracted to improve maintainability and readability.

Consider these improvements:

  1. Extract common setup to a beforeEach hook:
describe("SpringJoint", () => {
  let boxEntity1: Entity;
  let boxEntity2: Entity;
  let joint: SpringJoint;
  
  beforeEach(() => {
    // Common setup
    engine.sceneManager.activeScene.physics.gravity = new Vector3(0, 0, 0);
    boxEntity1 = addBox(new Vector3(1, 1, 1), DynamicCollider, new Vector3(0, 5, 0));
    boxEntity2 = addBox(new Vector3(1, 1, 1), DynamicCollider, new Vector3(2, 5, 0));
    joint = boxEntity2.addComponent(SpringJoint);
    
    // Common joint configuration
    const collider = boxEntity1.getComponent(DynamicCollider);
    joint.connectedCollider = collider;
    collider.isKinematic = true;
    joint.autoConnectedAnchor = false;
    joint.anchor = new Vector3(-0.5, 0, 0);
    joint.connectedAnchor = new Vector3(0.5, 0, 0);
  });
  
  it("should respect minDistance", () => {
    // Test-specific configuration
    joint.stiffness = 10000;
    joint.damping = 1000;
    // ... rest of the test
  });
});
  1. Create helper functions for common operations:
function updatePhysics(deltaTime: number = 1) {
  // @ts-ignore
  engine.sceneManager.activeScene.physics._update(deltaTime);
}

function configureJoint(joint: SpringJoint, config: Partial<{
  stiffness: number;
  damping: number;
  minDistance: number;
  maxDistance: number;
  tolerance: number;
}>) {
  Object.assign(joint, config);
}

These changes would:

  • Reduce code duplication
  • Improve test maintainability
  • Make test cases more focused on what they're actually testing
  • Make it easier to add new test cases
🧰 Tools
🪛 Biome

[error] 41-41: 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] 71-71: 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] 94-94: 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] 124-124: 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] 147-147: 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] 169-169: 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] 200-200: 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)

tests/src/core/physics/Collider.test.ts (1)

295-346: Consider refactoring duplicate code in clone tests.

The clone StaticCollider and clone DynamicCollider tests share similar setup and verification logic. Consider extracting common test setup into a helper function to improve maintainability.

+ function setupColliderTest(box: Entity, collider: DynamicCollider) {
+   // Avoid the box rotating
+   collider.automaticInertiaTensor = false;
+   collider.inertiaTensor.set(10000000, 10000000, 10000000);
+   collider.applyForce(new Vector3(1000, 0, 0));
+   collider.shapes[0].material.dynamicFriction = 1;
+ }

  it("clone StaticCollider", function () {
    const box = addBox(new Vector3(1, 1, 1), DynamicCollider, new Vector3(0, 0.5, 0));
    const collider = box.getComponent(DynamicCollider);
-   // Avoid the box rotating
-   collider.automaticInertiaTensor = false;
-   collider.inertiaTensor.set(10000000, 10000000, 10000000);
-   collider.applyForce(new Vector3(1000, 0, 0));
-   collider.shapes[0].material.dynamicFriction = 1;
+   setupColliderTest(box, collider);
    // ... rest of the test
  });

  it("clone DynamicCollider", function () {
    const box = addBox(new Vector3(1, 1, 1), DynamicCollider, new Vector3(0, 0.5, 0));
    const collider = box.getComponent(DynamicCollider);
-   // Avoid the box rotating
-   collider.automaticInertiaTensor = false;
-   collider.inertiaTensor.set(10000000, 10000000, 10000000);
-   collider.applyForce(new Vector3(1000, 0, 0));
-   collider.shapes[0].material.dynamicFriction = 1;
+   setupColliderTest(box, collider);
    // ... rest of the test
  });
packages/core/src/physics/DynamicCollider.ts (4)

108-146: Document performance implications of automatic properties

The automatic update of mass and inertia when shapes change could impact performance, especially with frequent shape modifications. Consider adding JSDoc comments explaining:

  • Performance implications
  • When to use automatic vs. manual updates
  • Best practices for shape management
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 109-110: packages/core/src/physics/DynamicCollider.ts#L109-L110
Added lines #L109 - L110 were not covered by tests


[warning] 116-117: packages/core/src/physics/DynamicCollider.ts#L116-L117
Added lines #L116 - L117 were not covered by tests


[warning] 136-137: packages/core/src/physics/DynamicCollider.ts#L136-L137
Added lines #L136 - L137 were not covered by tests


[warning] 143-144: packages/core/src/physics/DynamicCollider.ts#L143-L144
Added lines #L143 - L144 were not covered by tests


286-305: Consider batching shape operations

When multiple shapes are added or removed in succession, each operation triggers a separate mass and inertia update. Consider adding batch operations to optimize performance.

+  /**
+   * Adds multiple shapes at once, updating mass and inertia only once.
+   * @param shapes - Array of shapes to add
+   */
+  addShapes(shapes: ColliderShape[]): void {
+    shapes.forEach(shape => super.addShape(shape));
+    if (this._automaticCenterOfMass || this._automaticInertiaTensor) {
+      this._setMassAndUpdateInertia();
+    }
+  }

Line range hint 312-365: Add tests for phase-dependent operations

The new phase checks (this._phasedActiveInScene) protect against invalid operations, but need test coverage for both active and inactive phases. This affects:

  • Force application
  • Movement
  • Sleep/wake operations

Would you like me to help generate comprehensive test cases?


389-405: Optimize backend synchronization

The _syncBackends method synchronizes all properties individually. Consider:

  1. Batching updates where possible
  2. Only syncing changed properties
  3. Adding debug logging for troubleshooting
tests/src/core/physics/Joint.test.ts (2)

11-26: Add JSDoc documentation to helper functions.

The helper functions addBox and formatValue would benefit from JSDoc documentation explaining their purpose, parameters, and return values.

Add documentation like this:

+/**
+ * Creates a box entity with specified size, collider type, and position
+ * @param cubeSize - The size of the box in 3D space
+ * @param type - The type of collider (DynamicCollider or StaticCollider)
+ * @param pos - The position of the box in 3D space
+ * @returns The created box entity
+ */
 function addBox(cubeSize: Vector3, type: typeof DynamicCollider | typeof StaticCollider, pos: Vector3) {

+/**
+ * Rounds a number to 5 decimal places
+ * @param value - The number to format
+ * @returns The formatted number
+ */
 function formatValue(value: number) {

120-142: Improve test case names for better clarity.

The test cases would benefit from more descriptive names that indicate the specific behavior being tested.

Consider renaming tests to better describe their purpose:

-  it("massScale", function () {
+  it("should maintain joint stability until mass scale threshold is exceeded", function () {

-  it("connectedMassScale", function () {
+  it("should maintain joint stability until connected mass scale threshold is exceeded", function () {

-  it("inertiaScale", function () {
+  it("should correctly scale angular motion based on inertia scale", function () {

-  it("breakForce", function () {
+  it("should break joint when applied force exceeds break force threshold", function () {

-  it("breakTorque", function () {
+  it("should break joint when applied torque exceeds break torque threshold", function () {

Also applies to: 144-166, 168-212, 214-258, 260-280, 282-301

tests/src/core/physics/DynamicCollider.test.ts (3)

21-32: Add return type annotation and extract magic numbers.

Consider these improvements to the addPlane helper function:

  1. Add return type annotation
  2. Extract magic numbers into named constants
+ const DEFAULT_PLANE_SCALE = 20;
+
- function addPlane(x: number, y: number, z: number) {
+ function addPlane(x: number, y: number, z: number): Entity {
   const planeEntity = rootEntity.createChild("PlaneEntity");
   planeEntity.transform.setPosition(x, y, z);
-  planeEntity.transform.setScale(20, 1, 20);
+  planeEntity.transform.setScale(DEFAULT_PLANE_SCALE, 1, DEFAULT_PLANE_SCALE);

34-45: Extract common physics material setup into a helper function.

The friction setup is duplicated between addPlane and addBox. Consider extracting it into a helper function.

+ function setupFrictionlessMaterial(shape: BoxColliderShape | PlaneColliderShape): void {
+   shape.material.dynamicFriction = 0;
+   shape.material.staticFriction = 0;
+ }

  function addBox(cubeSize: Vector3, type: typeof DynamicCollider | typeof StaticCollider, pos: Vector3): Entity {
    // ...
    const physicsBox = new BoxColliderShape();
-   physicsBox.material.dynamicFriction = 0;
-   physicsBox.material.staticFriction = 0;
+   setupFrictionlessMaterial(physicsBox);
    // ...
  }

Also applies to: 47-49


423-446: Add assertions to verify state modifications.

The "inActive modification" test sets multiple properties but doesn't verify their effects. Consider adding assertions to validate the modifications.

  it("inActive modification", function () {
    const box = addBox(new Vector3(2, 2, 2), DynamicCollider, new Vector3(0, 0, 0));
    const boxCollider = box.getComponent(DynamicCollider);
    box.isActive = false;
    
    boxCollider.linearDamping = 0.1;
+   expect(boxCollider.linearDamping).eq(0.1);
    
    boxCollider.mass = 1;
+   expect(boxCollider.mass).eq(1);
    
    // Add more assertions for other property modifications
  });
tests/src/core/physics/PhysicsMaterial.test.ts (2)

20-32: Helper functions are well-structured but could be more reusable.

The helper functions addPlane, addBox, and formatValue are well-implemented, but they could be moved to a shared test utilities file for reuse across other physics test files.

Consider moving these helper functions to a shared test utilities file (e.g., tests/src/core/physics/test-utils.ts).

Also applies to: 34-45, 47-49


61-469: Test cases are comprehensive but could benefit from better organization.

The test suite thoroughly covers different physics material properties and their combinations. However, the organization could be improved to make it more maintainable.

Consider:

  1. Using nested describe blocks to group related tests
  2. Adding more descriptive test names
  3. Using beforeEach to set up common test scenarios

Example structure:

describe('PhysicsMaterial', () => {
  describe('Bounciness', () => {
    describe('Combine Modes', () => {
      it('should average bounce values when mode is Average', () => {
        // ...
      });
      // ...
    });
  });
  
  describe('Friction', () => {
    describe('Static Friction', () => {
      // ...
    });
    describe('Dynamic Friction', () => {
      // ...
    });
  });
});
tests/src/core/physics/HingeJoint.test.ts (1)

21-36: Extract 'addBox' and 'formatValue' into a shared utility module for reuse.

The functions addBox and formatValue are utility functions that may be reused across multiple test files. By moving them into a shared module, you can reduce code duplication and enhance maintainability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 790e7e5 and 4e564b9.

📒 Files selected for processing (11)
  • packages/core/src/physics/DynamicCollider.ts (14 hunks)
  • packages/core/src/physics/joint/HingeJoint.ts (6 hunks)
  • packages/core/src/physics/joint/Joint.ts (7 hunks)
  • packages/core/src/physics/joint/SpringJoint.ts (5 hunks)
  • packages/core/src/physics/shape/ColliderShape.ts (4 hunks)
  • tests/src/core/physics/Collider.test.ts (3 hunks)
  • tests/src/core/physics/DynamicCollider.test.ts (1 hunks)
  • tests/src/core/physics/HingeJoint.test.ts (1 hunks)
  • tests/src/core/physics/Joint.test.ts (1 hunks)
  • tests/src/core/physics/PhysicsMaterial.test.ts (1 hunks)
  • tests/src/core/physics/SpringJoint.test.ts (1 hunks)
🧰 Additional context used
🪛 GitHub Check: codecov/patch
packages/core/src/physics/DynamicCollider.ts

[warning] 103-104: packages/core/src/physics/DynamicCollider.ts#L103-L104
Added lines #L103 - L104 were not covered by tests


[warning] 109-110: packages/core/src/physics/DynamicCollider.ts#L109-L110
Added lines #L109 - L110 were not covered by tests


[warning] 116-117: packages/core/src/physics/DynamicCollider.ts#L116-L117
Added lines #L116 - L117 were not covered by tests


[warning] 136-137: packages/core/src/physics/DynamicCollider.ts#L136-L137
Added lines #L136 - L137 were not covered by tests


[warning] 143-144: packages/core/src/physics/DynamicCollider.ts#L143-L144
Added lines #L143 - L144 were not covered by tests


[warning] 172-172: packages/core/src/physics/DynamicCollider.ts#L172
Added line #L172 was not covered by tests


[warning] 424-425: packages/core/src/physics/DynamicCollider.ts#L424-L425
Added lines #L424 - L425 were not covered by tests


[warning] 430-431: packages/core/src/physics/DynamicCollider.ts#L430-L431
Added lines #L430 - L431 were not covered by tests

packages/core/src/physics/joint/HingeJoint.ts

[warning] 153-157: packages/core/src/physics/joint/HingeJoint.ts#L153-L157
Added lines #L153 - L157 were not covered by tests

packages/core/src/physics/joint/Joint.ts

[warning] 38-38: packages/core/src/physics/joint/Joint.ts#L38
Added line #L38 was not covered by tests


[warning] 84-85: packages/core/src/physics/joint/Joint.ts#L84-L85
Added lines #L84 - L85 were not covered by tests


[warning] 110-110: packages/core/src/physics/joint/Joint.ts#L110
Added line #L110 was not covered by tests


[warning] 124-124: packages/core/src/physics/joint/Joint.ts#L124
Added line #L124 was not covered by tests


[warning] 246-247: packages/core/src/physics/joint/Joint.ts#L246-L247
Added lines #L246 - L247 were not covered by tests


[warning] 251-254: packages/core/src/physics/joint/Joint.ts#L251-L254
Added lines #L251 - L254 were not covered by tests

🪛 Biome
tests/src/core/physics/Joint.test.ts

[error] 174-174: 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] 220-220: 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)

tests/src/core/physics/SpringJoint.test.ts

[error] 41-41: 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] 71-71: 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] 94-94: 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] 124-124: 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] 147-147: 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] 169-169: 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] 200-200: 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)

🔇 Additional comments (21)
packages/core/src/physics/joint/SpringJoint.ts (2)

86-89: Past review comment about error handling is still applicable.


92-98: 🛠️ Refactor suggestion

Add error handling and verify native joint initialization.

The _syncBackends method should verify that the native joint is properly initialized before attempting to sync properties.

 protected override _syncBackends(): void {
   super._syncBackends();
+  if (!this._nativeJoint) {
+    throw new Error("Failed to sync SpringJoint: Native joint not initialized");
+  }
   (<ISpringJoint>this._nativeJoint).setMinDistance(this._minDistance);
   (<ISpringJoint>this._nativeJoint).setMaxDistance(this._maxDistance);
   (<ISpringJoint>this._nativeJoint).setTolerance(this._tolerance);
   (<ISpringJoint>this._nativeJoint).setStiffness(this._stiffness);
   (<ISpringJoint>this._nativeJoint).setDamping(this._damping);
 }
packages/core/src/physics/shape/ColliderShape.ts (2)

147-153: Verify test coverage for backend synchronization.

While the new _syncBackends method improves code organization, we should ensure it's properly covered by unit tests, especially for error cases and the order of operations.

#!/bin/bash
# Check for existing tests related to backend synchronization
rg -A 5 "describe.*ColliderShape.*sync|test.*sync.*backend" --type ts

5-5: Verify the impact of switching to @deepClone.

The change from @ignoreClone to @deepClone for _rotation and _position properties will result in deep cloning of these Vector3 objects. While this ensures complete isolation between cloned instances, it might impact performance during cloning operations.

Also applies to: 25-28

✅ Verification successful

These scripts will help us understand:

  1. How ColliderShape cloning is tested
  2. How Vector3 cloning is handled in similar contexts
  3. If there's a pattern of using deepClone with Vector3 properties

This will give us better context about whether the switch to @deepClone is justified and follows established patterns in the codebase.


These scripts will help us understand:

  1. The relationship between Vector3 properties and cloning decorators
  2. Current patterns of decorating Vector3 properties
  3. The context of ColliderShape class where these changes are being made

@deepClone is appropriate for position and rotation properties

The change from @ignoreClone to @deepClone for _position and _rotation Vector3 properties is justified:

  1. These properties are instance-specific and represent the shape's spatial configuration
  2. Other internal references like _collider, _nativeShape, and _id correctly use @ignoreClone
  3. Vector3 instances need deep cloning to prevent shared references between cloned shapes, which could lead to unintended modifications

The performance impact of deep cloning these two Vector3 properties is minimal compared to the potential bugs from shared references.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if these properties are modified after cloning in any test files
rg -A 5 "clone.*ColliderShape.*rotation|position" --type ts

Length of output: 71


Script:

#!/bin/bash
# Let's try a broader search for ColliderShape cloning tests
rg -A 10 "ColliderShape.*clone" --type ts

# Also search for Vector3 cloning tests since position and rotation are Vector3
rg -A 10 "Vector3.*clone" --type ts

# Check for any usage of deepClone decorator on Vector3 properties
ast-grep --pattern '@deepClone
$_: Vector3'

Length of output: 127


Script:

#!/bin/bash
# Let's try a different approach to understand the cloning behavior

# Check for any files that import both Vector3 and cloning decorators
rg -l "import.*Vector3.*import.*(@ignoreClone|@deepClone)" --type ts

# Look for existing patterns of Vector3 property decorators
ast-grep --pattern '@$_
$_: Vector3'

# Check the ColliderShape class implementation
rg -A 20 "class ColliderShape" --type ts

Length of output: 1696

packages/core/src/physics/joint/HingeJoint.ts (2)

146-157: 🛠️ Refactor suggestion

Add test coverage for motor synchronization.

The _syncBackends method needs test coverage for the motor synchronization logic. Also, note that the gearRation typo needs to be fixed here as well.

Let's verify the missing test coverage:

Apply this diff to fix the typo:

     if (motor) {
       (<IHingeJoint>this._nativeJoint).setDriveVelocity(motor.targetVelocity);
       (<IHingeJoint>this._nativeJoint).setDriveForceLimit(motor.forceLimit);
-      (<IHingeJoint>this._nativeJoint).setDriveGearRatio(motor.gearRation);
+      (<IHingeJoint>this._nativeJoint).setDriveGearRatio(motor.gearRatio);
       (<IHingeJoint>this._nativeJoint).setHingeJointFlag(HingeJointFlag.DriveFreeSpin, motor.freeSpin);
     }
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 153-157: packages/core/src/physics/joint/HingeJoint.ts#L153-L157
Added lines #L153 - L157 were not covered by tests


15-24: LGTM! Properties are well-structured.

The properties are well-organized with appropriate types and decorators. The caching mechanism for _angle and _velocity is a good optimization.

Let's verify test coverage for these properties:

✅ Verification successful

Properties are well-tested with comprehensive coverage

The test file shows thorough coverage of all properties:

  • _axis is tested in "axis" test case with proper verification of angle and velocity
  • _hingeFlags functionality is verified through useLimits, useMotor, and useSpring tests
  • _useSpring is tested in "softLimit" test case
  • _jointMonitor (motor) is extensively tested in "motor", "forceLimit", "gearRation", and "freeSpin" test cases
  • _limits is verified in both "hardLimit" and "softLimit" test cases
  • _angle and _velocity caching is validated across multiple test cases with precise value checks
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check test coverage for HingeJoint properties
rg -A 5 "describe.*HingeJoint" "tests/"

Length of output: 52


Script:

#!/bin/bash
# Let's try a broader search for test files related to joints and hinges
fd "test|spec" -e ts | xargs rg -l "HingeJoint|hinge.*joint" -i

# Also search for the test directory structure
fd "test|spec" -t d

Length of output: 212


Script:

#!/bin/bash
# Let's also check the implementation details to understand the test requirements
rg "class HingeJoint" -A 5

Length of output: 485


Script:

#!/bin/bash
# Search for any usage patterns to determine test coverage needs
rg "new HingeJoint" -A 3

Length of output: 26


Script:

#!/bin/bash
# Let's examine the test file content to verify coverage of the properties
cat "tests/src/core/physics/HingeJoint.test.ts"

Length of output: 10394

packages/core/src/physics/joint/Joint.ts (2)

15-26: LGTM! Good architectural improvements

The changes improve the class design by:

  • Making it abstract to enforce proper inheritance
  • Adding static temp vector to reduce GC pressure
  • Using deepClone decorators for proper object copying

191-222: LGTM! Well-structured lifecycle management

The lifecycle methods and sync logic are well implemented with proper separation of concerns. However, we should verify that all properties are properly synced when the joint is re-enabled.

Let's verify the sync behavior:

✅ Verification successful

Let me gather more information to verify if there are any property setters that modify these values and if they properly call _syncBackends.


Let me try a different approach to find property setters.


Property setters properly handle native joint updates

The verification shows that all property setters correctly update both the internal state and the native joint when it exists. The _syncBackends method in _onEnableInScene ensures a complete sync of all properties when the joint is re-enabled, while individual setters maintain consistency during runtime. This is a robust implementation that maintains proper synchronization between the high-level joint properties and the native joint instance.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any other places where these properties are modified without calling _syncBackends
rg -A 2 "this\._nativeJoint\.(set|get)" --type ts

Length of output: 1139


Script:

#!/bin/bash
# Search for property setters that might modify joint properties
ast-grep --pattern 'set $_($$$) {
  $$$
}'  packages/core/src/physics/joint/Joint.ts

Length of output: 85


Script:

#!/bin/bash
# Search for property setters using ripgrep
rg "set \w+\(" packages/core/src/physics/joint/Joint.ts -A 5

Length of output: 2189

tests/src/core/physics/SpringJoint.test.ts (3)

1-33: LGTM! Well-structured test setup

The test setup follows best practices with proper initialization, cleanup, and a reusable helper function.


41-41: Refactor assignments in expressions for better readability

The assignments within expressions make the code harder to read and maintain, as flagged by the static analysis tool. This pattern is repeated across multiple test cases.

Also applies to: 71-71, 94-94, 124-124, 147-147, 169-169, 200-200

🧰 Tools
🪛 Biome

[error] 41-41: 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-231: LGTM! Comprehensive test coverage

The test suite thoroughly covers:

  • Distance constraints (min/max)
  • Tolerance handling
  • Stiffness variations (low/high)
  • Damping effects
  • Component cloning
  • Inactive state modifications

The assertions effectively verify the expected behaviors in each scenario.

🧰 Tools
🪛 Biome

[error] 41-41: 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] 71-71: 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] 94-94: 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] 124-124: 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] 147-147: 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] 169-169: 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] 200-200: 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)

tests/src/core/physics/Collider.test.ts (3)

49-64: Well-structured helper functions!

The addition of addBox and formatValue helper functions improves test maintainability and readability. addBox nicely encapsulates common entity creation logic, while formatValue properly handles floating-point precision comparisons.


Line range hint 68-90: Test setup properly configured!

The test setup is well-structured with proper initialization in beforeAll and beforeEach hooks. The physicsBox.material.bounciness value of 0.1 correctly aligns with the default value in PhysicsMaterial.ts.


348-357: Good coverage of inactive collider modifications!

The test case thoroughly verifies that shape management operations (add, remove, clear) and collider destruction work correctly on inactive colliders.

packages/core/src/physics/DynamicCollider.ts (2)

25-25: Verify the impact of maxDepenetrationVelocity change

Changing maxDepenetrationVelocity from a finite value (1000) to Infinity could affect physics behavior, particularly in scenarios with deep penetration between colliders. This might lead to unexpected object behavior or performance issues.

#!/bin/bash
# Search for tests or usage patterns of maxDepenetrationVelocity
rg -l "maxDepenetrationVelocity" --type ts

Also applies to: 31-32


407-431: ⚠️ Potential issue

Enhance error handling and test coverage

The private utility methods need:

  1. Test coverage for error conditions
  2. Error handling for invalid physics states
  3. Validation of input parameters
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 424-425: packages/core/src/physics/DynamicCollider.ts#L424-L425
Added lines #L424 - L425 were not covered by tests


[warning] 430-431: packages/core/src/physics/DynamicCollider.ts#L430-L431
Added lines #L430 - L431 were not covered by tests

tests/src/core/physics/Joint.test.ts (1)

174-174: **** The assignments within expressions have already been addressed in previous review comments.

Also applies to: 220-220

🧰 Tools
🪛 Biome

[error] 174-174: 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)

tests/src/core/physics/DynamicCollider.test.ts (1)

50-59: LGTM!

The test setup follows best practices by:

  1. Creating fresh test environment in beforeAll
  2. Cleaning up between tests in beforeEach
tests/src/core/physics/PhysicsMaterial.test.ts (1)

246-249: Fix incorrect force application in tests.

The force is being applied to the deactivated collider instead of the new one in several test cases.

This issue was already identified in a previous review. The force should be applied to collider2 instead of collider.

Also applies to: 280-283, 314-317

tests/src/core/physics/HingeJoint.test.ts (2)

64-65: Consider encapsulating the physics update call and documenting @ts-ignore usage.

This comment addresses the repeated use of @ts-ignore for physics._update calls. It's recommended to create a helper function that encapsulates this internal API call and includes documentation explaining the necessity of @ts-ignore.

Also applies to: 90-91, 118-119, 140-141, 164-165, 168-169, 192-193, 214-215, 222-223, 227-228, 252-253


257-269: Ensure modifications to inactive entities behave as expected.

Modifying components on inactive entities may not have the intended effect until the entity is activated. Verify that this behavior is as expected for your test case.

packages/core/src/physics/shape/ColliderShape.ts Outdated Show resolved Hide resolved
packages/core/src/physics/joint/HingeJoint.ts Outdated Show resolved Hide resolved
packages/core/src/physics/joint/Joint.ts Outdated Show resolved Hide resolved
tests/src/core/physics/DynamicCollider.test.ts Outdated Show resolved Hide resolved
tests/src/core/physics/HingeJoint.test.ts Outdated Show resolved Hide resolved
tests/src/core/physics/HingeJoint.test.ts Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a 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 (11)
packages/core/src/physics/joint/JointMotor.ts (5)

4-8: Enhance class documentation

While the class has basic documentation, it would be more helpful to include:

  • Example usage
  • Common use cases
  • Relationship with other joint types

Consider expanding the documentation like this:

 /**
  * The JointMotor is used to motorize a joint.
+ * 
+ * Used to control joint motion by applying forces to reach a target velocity.
+ * Common use cases include:
+ * - Motorized hinges
+ * - Powered wheels
+ * - Robotic joint control
+ * 
+ * @example
+ * ```ts
+ * const motor = new JointMotor();
+ * motor.targetVelocity = 5; // rad/s
+ * motor.forceLimit = 1000; // N
+ * ```
  */

15-25: Specify units in targetVelocity documentation

The documentation should specify the units for velocity and force to prevent confusion.

Update the documentation:

  /**
-  * The motor will apply a force up to force to achieve targetVelocity.
+  * The motor will apply a force up to forceLimit to achieve the target velocity.
+  * @param value Target velocity in radians per second (rad/s)
   */

27-37: Improve forceLimit documentation and consider a more reasonable default

The current implementation uses Number.MAX_VALUE as default, which might be excessive. Also, the documentation should specify units and valid ranges.

Consider these improvements:

  /**
   * The force limit.
+  * @param value Maximum force in Newtons (N)
+  * @min 0
+  * @default 1000
   */
  get forceLimit(): number {
    return this._forceLimit;
  }

  private _forceLimit: number = Number.MAX_VALUE;
+ private _forceLimit: number = 1000; // Default to 1000N

39-49: Enhance gearRatio documentation

The current documentation doesn't explain the purpose or effect of the gear ratio.

Improve the documentation:

  /**
-  * Gear ration for the motor
+  * Gear ratio for the motor. Multiplies the motor's effective force and divides
+  * its velocity. A ratio > 1 increases torque but reduces speed, while a ratio < 1
+  * does the opposite.
+  * @param value The gear ratio multiplier
+  * @min 0
+  * @default 1.0
   */

51-61: Add usage example to freeSpin documentation

While the current documentation explains the behavior, an example would make it clearer.

Enhance the documentation:

  /**
   * If freeSpin is enabled the motor will only accelerate but never slow down.
+  * Useful for implementing one-way motors or ratchet mechanisms.
+  * @example
+  * ```ts
+  * // Create a motor that only accelerates in one direction
+  * motor.freeSpin = true;
+  * motor.targetVelocity = 5; // Motor will accelerate to 5 rad/s
+  * motor.targetVelocity = 2; // Motor will maintain current speed instead of slowing down
+  * ```
   */
packages/core/src/physics/joint/JointLimits.ts (2)

10-14: Consider adding value validation for physics properties.

While the private properties are well-encapsulated, some properties might benefit from validation:

  • _contactDistance starts at -1, but the documentation suggests it should be positive
  • _max and _min might need relative validation (max should be greater than min)

Consider adding validation in the setters:

 set max(value: number) {
+  if (value < this._min) {
+    throw new Error("Max value cannot be less than min value");
+  }
   if (this._max !== value) {
     this._max = value;
     this._updateFlagManager.dispatch();
   }
 }

59-85: Enhance validation and documentation for spring properties.

The stiffness and damping properties need additional validation and more detailed documentation:

  • Both properties should likely be non-negative
  • Documentation should specify units and typical value ranges

Add validation and enhance documentation:

 /**
  * The spring forces used to reach the target position.
+ * @param value Non-negative value representing the spring constant (N/m)
+ * @throws {Error} If value is negative
  */
 set stiffness(value: number) {
+  if (value < 0) {
+    throw new Error("Stiffness must be non-negative");
+  }
   if (this._stiffness !== value) {
     this._stiffness = value;
     this._updateFlagManager.dispatch();
   }
 }

 /**
  * The damper force uses to dampen the spring.
+ * @param value Non-negative value representing the damping coefficient (Ns/m)
+ * @throws {Error} If value is negative
  */
 set damping(value: number) {
+  if (value < 0) {
+    throw new Error("Damping must be non-negative");
+  }
   if (this._damping !== value) {
     this._damping = value;
     this._updateFlagManager.dispatch();
   }
 }
packages/core/src/physics/joint/HingeJoint.ts (2)

45-60: Document caching behavior for angle and velocity properties.

The getters now cache values in _angle and _velocity. Consider adding JSDoc comments to document this caching behavior and its implications.

   /**
    * The current angle in degrees of the joint relative to its rest position.
+   * @remarks The value is cached after reading from the native joint.
    */
   get angle(): number {

   /**
    * The angular velocity of the joint in degrees per second.
+   * @remarks The value is cached after reading from the native joint.
    */
   get velocity(): Readonly<number> {

Line range hint 113-141: Use optional chaining for event listener registration.

Improve the code's robustness by using optional chaining when registering event listeners.

   set motor(value: JointMotor) {
     if (this._jointMotor !== value) {
-      if (this._jointMotor) {
-        this._jointMotor._updateFlagManager.removeListener(this._onMotorChanged);
-      }
+      this._jointMotor?._updateFlagManager.removeListener(this._onMotorChanged);
       this._jointMotor = value;
-      value && value._updateFlagManager.addListener(this._onMotorChanged);
+      value?._updateFlagManager.addListener(this._onMotorChanged);
       this._onMotorChanged();
     }
   }

   set limits(value: JointLimits) {
     if (this._limits !== value) {
-      if (this._limits) {
-        this._limits._updateFlagManager.removeListener(this._onLimitsChanged);
-      }
+      this._limits?._updateFlagManager.removeListener(this._onLimitsChanged);
       this._limits = value;
-      value && value._updateFlagManager.addListener(this._onLimitsChanged);
+      value?._updateFlagManager.addListener(this._onLimitsChanged);
       this._onLimitsChanged();
     }
   }
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 109-109: packages/core/src/physics/joint/HingeJoint.ts#L109
Added line #L109 was not covered by tests


[warning] 115-116: packages/core/src/physics/joint/HingeJoint.ts#L115-L116
Added lines #L115 - L116 were not covered by tests

🪛 Biome

[error] 119-119: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

tests/src/core/physics/SpringJoint.test.ts (2)

11-22: Consider extracting addBox helper to a shared test utilities file

The addBox helper function is a great addition to streamline the creation of box entities in the tests. However, since it's a generic utility that could be useful across multiple test files, consider extracting it to a shared test utilities file.

This would promote reusability and maintainability of the test codebase.


221-231: Test inActive modification edge cases

The "inActive modification" test case covers the scenario where a SpringJoint component is added to an inactive entity and then destroyed.

Consider adding more edge cases to this test to ensure the robustness of the SpringJoint component when dealing with inactive entities, such as:

  1. Modifying joint properties while the entity is inactive and then activating the entity.
  2. Deactivating an entity with an active joint and verifying that the joint is properly deactivated.
  3. Destroying the joint component while the entity is inactive and then activating the entity.

I can provide examples of how to implement these additional test cases if you'd like. Do you want me to go ahead and propose the code changes?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 4e564b9 and 06dce2c.

📒 Files selected for processing (6)
  • packages/core/src/physics/joint/HingeJoint.ts (6 hunks)
  • packages/core/src/physics/joint/JointLimits.ts (1 hunks)
  • packages/core/src/physics/joint/JointMotor.ts (1 hunks)
  • tests/src/core/physics/HingeJoint.test.ts (1 hunks)
  • tests/src/core/physics/Joint.test.ts (1 hunks)
  • tests/src/core/physics/SpringJoint.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/src/core/physics/HingeJoint.test.ts
🧰 Additional context used
🪛 GitHub Check: codecov/patch
packages/core/src/physics/joint/HingeJoint.ts

[warning] 109-109: packages/core/src/physics/joint/HingeJoint.ts#L109
Added line #L109 was not covered by tests


[warning] 115-116: packages/core/src/physics/joint/HingeJoint.ts#L115-L116
Added lines #L115 - L116 were not covered by tests


[warning] 135-135: packages/core/src/physics/joint/HingeJoint.ts#L135
Added line #L135 was not covered by tests


[warning] 158-162: packages/core/src/physics/joint/HingeJoint.ts#L158-L162
Added lines #L158 - L162 were not covered by tests

🪛 Biome
packages/core/src/physics/joint/HingeJoint.ts

[error] 119-119: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 139-139: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

tests/src/core/physics/Joint.test.ts

[error] 174-174: 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] 220-220: 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)

tests/src/core/physics/SpringJoint.test.ts

[error] 41-41: 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] 71-71: 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] 94-94: 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] 124-124: 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] 147-147: 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] 169-169: 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] 200-200: 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)

🔇 Additional comments (12)
packages/core/src/physics/joint/JointMotor.ts (1)

8-8: Verify UpdateFlagManager usage across the physics system

The class consistently uses UpdateFlagManager to notify about property changes. Let's verify this pattern is used consistently across related classes.

Also applies to: 24-24, 36-36, 48-48, 60-60

✅ Verification successful

Based on the results, I can now generate the final response as we have sufficient information about the UpdateFlagManager usage pattern.

UpdateFlagManager is consistently implemented in physics joint components

The UpdateFlagManager is properly used in both JointMotor and JointLimits classes, with consistent dispatch patterns after property changes. The implementation follows the same pattern across all properties:

  • All property setters call dispatch() after value updates
  • Both classes maintain proper encapsulation with private fields and public accessors
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other physics-related files using UpdateFlagManager
echo "Checking UpdateFlagManager usage pattern in physics-related files:"
rg -l "UpdateFlagManager" "packages/core/src/physics"

echo "\nChecking dispatch pattern in physics-related files:"
rg "dispatch\(\)" "packages/core/src/physics" -A 1 -B 1

Length of output: 2426

packages/core/src/physics/joint/JointLimits.ts (1)

1-8: LGTM! Verify UpdateFlagManager integration.

The addition of UpdateFlagManager for change tracking is a good practice. The class documentation and internal flag setup look correct.

Let's verify the UpdateFlagManager usage pattern across other joint-related files:

✅ Verification successful

UpdateFlagManager integration is correctly implemented

The verification shows that:

  • Both JointLimits and JointMotor consistently use UpdateFlagManager with the same pattern
  • They are properly integrated into HingeJoint which manages them as private properties with getters/setters
  • Both classes are correctly exported through the joint module's index
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if UpdateFlagManager is consistently used across joint files
# Expected: Similar usage pattern in other joint files

# Search for UpdateFlagManager usage in joint files
rg -l "UpdateFlagManager" "packages/core/src/physics/joint/"

Length of output: 151


Script:

#!/bin/bash
# Let's check the implementation pattern in JointMotor.ts for comparison
rg "UpdateFlagManager" "packages/core/src/physics/joint/JointMotor.ts" -B 2 -A 2

# Also check how these classes are used together
rg "JointLimits|JointMotor" "packages/core/src/physics/joint/" -l

Length of output: 526


Script:

#!/bin/bash
# Let's check how HingeJoint uses these classes
rg "JointLimits|JointMotor" "packages/core/src/physics/joint/HingeJoint.ts" -B 1 -A 1

# Also verify the exports
cat "packages/core/src/physics/joint/index.ts"

Length of output: 956

packages/core/src/physics/joint/HingeJoint.ts (3)

15-24: LGTM: Improved property declarations with appropriate decorators.

The switch to @deepClone for complex objects (_axis, _jointMotor, _limits) ensures proper cloning behavior, which is essential for maintaining the integrity of joint instances when cloned.


165-183: LGTM: Well-structured event handlers.

The event handlers are well-implemented with:

  • Proper null checks before accessing the native joint
  • Clear separation between soft and hard limit handling
  • Appropriate synchronization of all relevant properties

151-162: Add test coverage for _syncBackends method.

The _syncBackends method lacks test coverage, particularly for the motor synchronization logic. This is critical functionality that should be thoroughly tested.

Would you like me to help generate comprehensive test cases for the _syncBackends method?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 158-162: packages/core/src/physics/joint/HingeJoint.ts#L158-L162
Added lines #L158 - L162 were not covered by tests

tests/src/core/physics/Joint.test.ts (3)

11-26: Well-structured helper functions!

The helper functions improve test maintainability:

  • addBox: Encapsulates box entity creation logic
  • formatValue: Handles floating-point precision consistently

39-338: Comprehensive test coverage!

The test suite thoroughly covers various joint behaviors:

  • Connection scenarios (null and inter-collider)
  • Mass and inertia scaling
  • Break force and torque
  • Cloning and inactive state modifications
🧰 Tools
🪛 Biome

[error] 174-174: 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] 220-220: 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)


39-338: Well-structured test cases!

The tests follow best practices:

  • Clear AAA (Arrange-Act-Assert) pattern
  • Focused test cases
  • Appropriate assertion matchers
🧰 Tools
🪛 Biome

[error] 174-174: 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] 220-220: 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)

tests/src/core/physics/SpringJoint.test.ts (4)

1-232: Comprehensive test suite for SpringJoint component

The test file provides a thorough set of unit tests for the SpringJoint component, covering various scenarios and configurations. The tests validate the behavior of the joint under different property settings such as minDistance, maxDistance, tolerance, stiffness, and damping. The use of the addBox helper function promotes code reuse and improves readability.

Great job on implementing a well-structured and comprehensive test suite!

🧰 Tools
🪛 Biome

[error] 41-41: 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] 71-71: 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] 94-94: 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] 124-124: 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] 147-147: 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] 169-169: 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] 200-200: 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)


52-53: Remove @ts-ignore by properly typing physics update method

The repeated use of @ts-ignore for physics._update suggests a typing issue that should be addressed properly instead of being ignored.

Consider either:

  1. Adding proper type definitions for the internal _update method
  2. Creating a test utility method that properly exposes this functionality
// Add to a test utilities file
export function updatePhysics(scene: Scene, deltaTime: number): void {
  (scene.physics as any)._update(deltaTime);
}

Also applies to: 82-83, 105-106, 135-136, 158-159, 180-181


41-41: Refactor assignments in expressions

The assignments within expressions make the code harder to read and maintain. They've also been flagged by the static analysis tool.

Refactor these assignments to be more explicit:

- const collider = (joint.connectedCollider = boxEntity.getComponent(DynamicCollider));
+ const collider = boxEntity.getComponent(DynamicCollider);
+ joint.connectedCollider = collider;

Also applies to: 71-71, 94-94, 124-124, 147-147, 169-169, 200-200

🧰 Tools
🪛 Biome

[error] 41-41: 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)


194-219: Verify cloned entity behavior

The "clone" test case creates a clone of boxEntity2 and deactivates the original entity. It then adds the cloned entity to the rootEntity and performs an update.

Verify that the cloned entity behaves independently of the original deactivated entity and that the joint configuration is correctly cloned.

To verify the cloned entity's behavior, you can add assertions to check the position and velocity of the cloned entity after the update and compare them with the original deactivated entity.

// ...

// @ts-ignore
engine.sceneManager.activeScene.physics._update(1);

expect(newBox.transform.position.x).eq(6);
expect(boxEntity2.transform.position.x).eq(2); // Original entity should remain at its initial position

const clonedCollider = newBox.getComponent(DynamicCollider);
expect(clonedCollider.linearVelocity.x).not.eq(0); // Cloned entity should have non-zero velocity
expect(collider2.linearVelocity.x).eq(0); // Original entity should have zero velocity
🧰 Tools
🪛 Biome

[error] 200-200: 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/physics/joint/JointMotor.ts Outdated Show resolved Hide resolved
packages/core/src/physics/joint/JointLimits.ts Outdated Show resolved Hide resolved
packages/core/src/physics/joint/JointLimits.ts Outdated Show resolved Hide resolved
tests/src/core/physics/SpringJoint.test.ts Show resolved Hide resolved
tests/src/core/physics/SpringJoint.test.ts Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
packages/core/src/physics/DynamicCollider.ts (2)

66-70: Consider refactoring the velocity getter implementations

The current implementation temporarily nullifies and reassigns _onValueChanged, which is a code smell. Consider:

  1. Creating a utility method to handle this pattern
  2. Finding an alternative approach that doesn't require ts-ignore
+  private _withoutValueChangeCallback<T>(vector: Vector3, callback: () => void): void {
+    const originalCallback = (vector as any)._onValueChanged;
+    (vector as any)._onValueChanged = null;
+    callback();
+    (vector as any)._onValueChanged = originalCallback;
+  }

   get linearVelocity(): Vector3 {
-    //@ts-ignore
-    this._linearVelocity._onValueChanged = null;
-    (<IDynamicCollider>this._nativeCollider).getLinearVelocity(this._linearVelocity);
-    //@ts-ignore
-    this._linearVelocity._onValueChanged = this._setLinearVelocity;
+    this._withoutValueChangeCallback(this._linearVelocity, () => {
+      (<IDynamicCollider>this._nativeCollider).getLinearVelocity(this._linearVelocity);
+    });
     return this._linearVelocity;
   }

Also applies to: 84-88


435-440: Consider breaking down _setMassAndUpdateInertia method

The method handles multiple responsibilities. Consider splitting it into smaller, focused methods for better maintainability.

+  private _updateCenterOfMass(): void {
+    !this._automaticCenterOfMass && 
+      (<IDynamicCollider>this._nativeCollider).setCenterOfMass(this._centerOfMass);
+  }
+
+  private _updateInertiaTensor(): void {
+    !this._automaticInertiaTensor && 
+      (<IDynamicCollider>this._nativeCollider).setInertiaTensor(this._inertiaTensor);
+  }

   private _setMassAndUpdateInertia(): void {
     (<IDynamicCollider>this._nativeCollider).setMassAndUpdateInertia(this._mass);
-    !this._automaticCenterOfMass && (<IDynamicCollider>this._nativeCollider).setCenterOfMass(this._centerOfMass);
-    !this._automaticInertiaTensor && (<IDynamicCollider>this._nativeCollider).setInertiaTensor(this._inertiaTensor);
+    this._updateCenterOfMass();
+    this._updateInertiaTensor();
   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 10f552b and 022f838.

📒 Files selected for processing (1)
  • packages/core/src/physics/DynamicCollider.ts (9 hunks)
🔇 Additional comments (4)
packages/core/src/physics/DynamicCollider.ts (4)

310-328: Add test coverage for shape management methods

These methods handle critical physics calculations when shapes are modified. Ensure comprehensive test coverage for:

  • Shape operations with automatic calculations enabled/disabled
  • Mass and inertia updates after shape changes

25-25: Verify the specific maxDepenetrationVelocity value

The value 1.0000000331813535e32 seems unusually specific. Please verify if this is the intended value and document the reasoning behind this specific number.

✅ Verification successful

Based on the search results, I can now provide a final response:

The maxDepenetrationVelocity value is intentionally set to a very large number

The value 1.0000000331813535e32 appears to be an intentionally set very large default value that effectively acts as "infinity" for the maximum velocity allowed when resolving penetration between colliding objects. This is evidenced by:

  • The tests show that this value can be overridden to smaller values like 0 or 10 when specific penetration velocity limits are needed
  • The PhysX implementation fully supports this parameter while the Physics-lite implementation explicitly does not support it
  • The documentation indicates this is used as "Maximum velocity of a collider when moving out of penetrating state"
  • Having a very large default allows objects to separate quickly by default when they penetrate each other, unless explicitly limited
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any documentation or comments about this value
rg -i "maxdepenetrationvelocity.*1\.0+e32"

Length of output: 44


Script:

#!/bin/bash
# Let's search for any occurrences of this specific number in the codebase
rg "1\.0000000331813535e32"

# Also search for any comments or documentation containing "depenetration"
rg -i "depenetration" -A 2 -B 2

# Let's also check if this value is used in any physics-related constants or configurations
rg "maxDepenetrationVelocity" -A 2 -B 2

Length of output: 39618


Line range hint 336-367: Verify inactive state handling

The addition of _phasedActiveInScene checks is good for preventing invalid operations, but needs testing to ensure proper behavior in all states.

✅ Verification successful

Let's also check the implementation of _phasedActiveInScene to understand its usage:

And let's look for any existing test patterns for similar physics operations:


Let's also check for any enable/disable testing patterns in the Component base class since _phasedActiveInScene is defined there:


Based on the examination of the DynamicCollider test file and the Component implementation, I can now provide a final response:

Inactive state handling is properly tested and verified

The DynamicCollider test file includes comprehensive testing for inactive state handling in the "inActive modification" test case, which verifies that:

  • All physics operations are properly gated by the _phasedActiveInScene check
  • Operations during inactive state don't affect the physics state
  • Operations resume correctly when the entity becomes active again

Key test evidence:

  • Test case "inActive modification" specifically tests all physics operations while inactive
  • The test verifies that position remains unchanged during inactive state
  • The test confirms operations resume working after reactivation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for tests covering inactive state handling
rg -l "test.*inactive.*collider|test.*collider.*inactive"

Length of output: 59


Script:

#!/bin/bash
# Let's search for test files related to DynamicCollider
fd "DynamicCollider.*test" --type f

# Also search for any test files containing "collider" and "active" or "inactive"
rg -l "collider.*(?:active|inactive)|(?:active|inactive).*collider" --type-add 'test:*.{test,spec}.*' -t test

Length of output: 196


Script:

#!/bin/bash
# Search for _phasedActiveInScene definition and usage
rg "_phasedActiveInScene" -A 2 -B 2

Length of output: 3592


Script:

#!/bin/bash
# Search for test cases related to physics operations
rg "(?:describe|it).*(?:force|torque|move)" --type-add 'test:*.{test,spec}.*' -t test -A 5

Length of output: 6728


Script:

#!/bin/bash
# Let's examine the DynamicCollider test file specifically
cat tests/src/core/physics/DynamicCollider.test.ts

Length of output: 21139


Script:

#!/bin/bash
# Search for enable/disable testing in Component tests
rg "(?:describe|it).*(?:enable|disable|active)" tests/src/core/Component.test.ts -A 5

Length of output: 160


13-14: Verify the impact of removing @ignoreClone decorators

The removal of @ignoreClone decorators from multiple properties will change their cloning behavior. This could impact how DynamicCollider instances are duplicated.

Also applies to: 19-19, 24-27, 30-32

packages/core/src/physics/DynamicCollider.ts Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (7)
packages/core/src/physics/Collider.ts (1)

127-130: Add JSDoc comment for the hook method

The _handleShapesChanged appears to be a hook method for derived classes. Please add documentation explaining its purpose and when it gets called.

packages/core/src/physics/CharacterController.ts (1)

64-65: Update documentation to reference Math.SQRT1_2

For consistency with the code change, update the documentation to reference Math.SQRT1_2 instead of the magic number.

  /**
   * The slope limit for the controller, the value is the cosine value of the maximum slope angle.
-  * @defaultValue 0.707(the cosine value of 45 degrees)
+  * @defaultValue Math.SQRT1_2 (the cosine value of 45 degrees)
   */
tests/src/core/physics/Joint.test.ts (2)

11-22: Add JSDoc documentation for the addBox helper function

The helper function would benefit from documentation describing its parameters and return value.

+/**
+ * Creates a box entity with physics properties
+ * @param cubeSize - The size of the box
+ * @param type - The type of collider to add
+ * @param pos - The position of the box
+ * @returns The created box entity
+ */
 function addBox(cubeSize: Vector3, type: typeof DynamicCollider | typeof StaticCollider, pos: Vector3) {

344-362: Split "inActive modification" test into multiple test cases

The test case "inActive modification" tests multiple behaviors in a single test. Consider splitting it into separate test cases for better clarity and maintainability:

  • Test autoConnectedAnchor modifications
  • Test collider connections
  • Test scale modifications
  • Test break force/torque modifications
it("should handle autoConnectedAnchor modifications when inactive", function () {
  // Test autoConnectedAnchor behavior
});

it("should handle collider connections when inactive", function () {
  // Test collider connection behavior
});

it("should handle scale modifications when inactive", function () {
  // Test scale modification behavior
});

it("should handle break force/torque modifications when inactive", function () {
  // Test break force/torque behavior
});
packages/core/src/physics/DynamicCollider.ts (2)

25-25: Consider documenting the magic number

The value 1.0000000331813535e32 seems to be a very specific constant. Consider extracting it to a named constant with documentation explaining its significance and how it was derived.

+ /** Maximum velocity constant derived from... */
+ private static readonly MAX_DEPENETRATION_VELOCITY = 1.0000000331813535e32;
- private _maxDepenetrationVelocity = 1.0000000331813535e32;
+ private _maxDepenetrationVelocity = DynamicCollider.MAX_DEPENETRATION_VELOCITY;

458-475: Consider using a custom error type for automatic mode warnings

The warning messages are helpful, but consider creating a custom error type for better error handling and consistency.

+ class AutomaticModeError extends Error {
+   constructor(property: string) {
+     super(`${property} is automatically calculated. Set automatic${property} to false for manual control.`);
+     this.name = 'AutomaticModeError';
+   }
+ }

  private _handleCenterOfMassChanged(): void {
    if (this._automaticCenterOfMass) {
-     console.warn(
-       "The center of mass is automatically calculated, please set automaticCenterOfMass to false if you want to set it manually."
-     );
+     console.warn(new AutomaticModeError('CenterOfMass').message);
    } else {
      (<IDynamicCollider>this._nativeCollider).setCenterOfMass(this._centerOfMass);
    }
  }
tests/src/core/physics/DynamicCollider.test.ts (1)

21-53: Consider improving helper functions for better maintainability.

The helper functions could be enhanced in several ways:

  1. Extract magic numbers into named constants
  2. Make friction values configurable parameters
  3. Use a more specific type for the collider parameter in addBox
+ const PLANE_SIZE = 20;
+ const DEFAULT_FRICTION = 0;
+
+ interface ColliderType {
+   new (...args: any[]): DynamicCollider | StaticCollider;
+ }
+
  function addPlane(x: number, y: number, z: number) {
    const planeEntity = rootEntity.createChild("PlaneEntity");
    planeEntity.transform.setPosition(x, y, z);
-   planeEntity.transform.setScale(20, 1, 20);
+   planeEntity.transform.setScale(PLANE_SIZE, 1, PLANE_SIZE);

    const physicsPlane = new PlaneColliderShape();
-   physicsPlane.material.dynamicFriction = 0;
-   physicsPlane.material.staticFriction = 0;
+   physicsPlane.material.dynamicFriction = DEFAULT_FRICTION;
+   physicsPlane.material.staticFriction = DEFAULT_FRICTION;
    const planeCollider = planeEntity.addComponent(StaticCollider);
    planeCollider.addShape(physicsPlane);
    return planeEntity;
  }

- function addBox(cubeSize: Vector3, type: typeof DynamicCollider | typeof StaticCollider, pos: Vector3) {
+ function addBox(cubeSize: Vector3, type: ColliderType, pos: Vector3) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 022f838 and 13cee3e.

📒 Files selected for processing (6)
  • packages/core/src/physics/CharacterController.ts (3 hunks)
  • packages/core/src/physics/Collider.ts (5 hunks)
  • packages/core/src/physics/DynamicCollider.ts (9 hunks)
  • packages/core/src/physics/shape/ColliderShape.ts (4 hunks)
  • tests/src/core/physics/DynamicCollider.test.ts (1 hunks)
  • tests/src/core/physics/Joint.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/physics/shape/ColliderShape.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/core/src/physics/CharacterController.ts

[error] 18-18: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)

tests/src/core/physics/Joint.test.ts

[error] 191-191: 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] 238-238: 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)

🔇 Additional comments (21)
packages/core/src/physics/Collider.ts (6)

54-54: LGTM! Method name change improves clarity

The rename from _addPhysicsShape to _addNativeShape better reflects its purpose of synchronizing with the native collider implementation.


66-66: LGTM! Consistent method naming

The rename to _removeNativeShape maintains consistency with the _addNativeShape pattern.


110-110: LGTM! Improved scene integration

The simplified scene integration provides better separation of concerns by delegating collider management to the physics scene.

Also applies to: 117-117


132-136: LGTM! Well-structured native sync method

The new _syncNative method provides a clean way to ensure all shapes are properly synchronized with the native collider.


143-149: LGTM! Proper cleanup sequence

The destroy logic now ensures proper cleanup by removing and destroying shapes before clearing the array.


153-161: LGTM! Clean native shape management

The new _addNativeShape and _removeNativeShape methods provide a clear and focused API for managing shapes in the native collider.

packages/core/src/physics/CharacterController.ts (4)

18-18: Use Math.SQRT1_2 instead of magic number 0.707

For better precision and maintainability, replace the approximate value with the standard library constant.

- private _slopeLimit = 0.707;
+ private _slopeLimit = Math.SQRT1_2;
🧰 Tools
🪛 Biome (1.9.4)

[error] 18-18: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)


21-32: LGTM! Good improvements to stepOffset handling

The added validation and improved documentation make the behavior clearer and more robust.


147-147: LGTM! Clean simplification of scene enable logic

The streamlined approach of directly calling physics scene methods improves code clarity.


154-155: LGTM! Consistent with enable logic changes

The simplified disable logic maintains symmetry with the enable method changes.

tests/src/core/physics/Joint.test.ts (2)

51-52: Create a test utility for physics updates

Multiple occurrences of @ts-ignore are used to access the internal _update method.

Also applies to: 76-77, 99-100, 147-148, 173-174, 198-199, 219-220, 245-246, 291-292, 310-311, 337-338


191-191: Avoid assignments within expressions

For better readability, separate the assignments from the expressions.

Also applies to: 238-238

🧰 Tools
🪛 Biome (1.9.4)

[error] 191-191: 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/physics/DynamicCollider.ts (5)

108-114: LGTM! Good handling of automatic vs manual modes

The mass setter properly handles both automatic and manual modes, ensuring that inertia and center of mass are updated when necessary.


310-328: LGTM! Consistent shape management implementation

The shape management methods properly handle mass and inertia updates when shapes are modified. The implementation is consistent across all methods.


Line range hint 336-367: LGTM! Good safety check for inactive colliders

The addition of the _phasedActiveInScene check prevents operations on inactive colliders, which is a good safety measure.


422-441: LGTM! Comprehensive synchronization implementation

The _syncNative method properly synchronizes all properties with the native collider, handling both automatic and manual modes correctly.


444-449: Add test coverage for mass and inertia updates

The _setMassAndUpdateInertia method handles critical physics properties. Please ensure comprehensive test coverage.

✅ Verification successful

Test coverage for mass and inertia updates exists and is comprehensive

The test file contains thorough coverage for mass and inertia-related functionality:

  • Mass updates and effects are tested in the "mass" test case
  • Center of mass is tested in "centerOfMass" and "automaticCenterOfMass" test cases
  • Inertia tensor is tested in "inertiaTensor" and "automaticInertiaTensor" test cases

The tests verify:

  • Mass property updates and their effects on physics behavior
  • Manual and automatic center of mass calculations
  • Manual and automatic inertia tensor calculations
  • The impact of these properties on object behavior
  • Warning messages for invalid property modifications
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check test coverage for mass and inertia updates
rg -l "describe.*DynamicCollider" | xargs rg "test.*mass.*update.*inertia"

Length of output: 76


Script:

#!/bin/bash
# First, let's find test files related to DynamicCollider
fd "DynamicCollider.*test|test.*DynamicCollider" -t f

# Then, let's look for any test files in physics directory
fd "test" -t f packages/core/src/physics/

# Let's also check for any mass or inertia related tests with broader search
rg -i "test.*mass|test.*inertia" packages/core/src/physics/

Length of output: 206


Script:

#!/bin/bash
# Let's examine the content of the DynamicCollider test file
cat tests/src/core/physics/DynamicCollider.test.ts

Length of output: 21192

tests/src/core/physics/DynamicCollider.test.ts (4)

103-110: 🛠️ Refactor suggestion

Create a test utility to encapsulate physics updates.

The test suite has excessive use of @ts-ignore to access internal physics update method. This makes the tests fragile and harder to maintain.

// PhysicsTestUtils.ts
export class PhysicsTestUtils {
  static updatePhysics(scene: Scene, deltaTime: number): void {
    (scene.physics as any)._update(deltaTime);
  }
}

// Usage in tests
PhysicsTestUtils.updatePhysics(engine.sceneManager.activeScene, 1);

Also applies to: 118-119, 133-134, 149-150, 169-170, 177-178, 189-190, 195-196, 237-238, 245-246


54-63: ⚠️ Potential issue

Add cleanup in afterEach/afterAll hooks.

The test setup should clean up resources after tests to prevent memory leaks and ensure test isolation.

  beforeEach(function () {
    rootEntity.clearChildren();
  });
+
+ afterEach(function () {
+   // Clean up any remaining physics objects
+   engine.sceneManager.activeScene.physics._update(0);
+ });
+
+ afterAll(function () {
+   // Dispose engine resources
+   engine.destroy();
+ });

Likely invalid or redundant comment.


395-420: 🛠️ Refactor suggestion

Decouple tests from PhysX implementation details.

The collisionDetectionMode test is tightly coupled with PhysX internals. Consider testing the behavior rather than implementation details.

  it("collisionDetectionMode", function () {
    const box = addBox(new Vector3(2, 2, 2), DynamicCollider, new Vector3(0, 0, 0));
+   const target = addBox(new Vector3(2, 2, 2), StaticCollider, new Vector3(10, 0, 0));
    const boxCollider = box.getComponent(DynamicCollider);
-   // @ts-ignore
-   const physX = boxCollider._nativeCollider._physXPhysics._physX;

    boxCollider.collisionDetectionMode = CollisionDetectionMode.Discrete;
-   // @ts-ignore
-   expect(boxCollider._nativeCollider._pxActor.getRigidBodyFlags(physX.PxRigidBodyFlag.eENABLE_CCD)).toBeFalsy();
+   // Test high-speed collision with Discrete mode
+   boxCollider.linearVelocity = new Vector3(1000, 0, 0);
+   engine.sceneManager.activeScene.physics._update(1/60);
+   // Expect tunneling through the target due to discrete mode
+   expect(box.transform.position.x).toBeGreaterThan(10);

    boxCollider.collisionDetectionMode = CollisionDetectionMode.Continuous;
-   // @ts-ignore
-   expect(boxCollider._nativeCollider._pxActor.getRigidBodyFlags(physX.PxRigidBodyFlag.eENABLE_CCD)).toBeTruthy();
+   box.transform.setPosition(0, 0, 0);
+   boxCollider.linearVelocity = new Vector3(1000, 0, 0);
+   engine.sceneManager.activeScene.physics._update(1/60);
+   // Expect collision detection despite high speed
+   expect(box.transform.position.x).toBeLessThan(10);

Likely invalid or redundant comment.


494-530: 🛠️ Refactor suggestion

Break down large test case into focused scenarios.

The "inActive modification" test case is too large and tests multiple behaviors. This makes it harder to understand and maintain.

- it("inActive modification", function () {
+ describe("inactive entity modifications", () => {
+   let box: Entity;
+   let boxCollider: DynamicCollider;
+
+   beforeEach(() => {
+     box = addBox(new Vector3(2, 2, 2), DynamicCollider, new Vector3(0, 0, 0));
+     boxCollider = box.getComponent(DynamicCollider);
+     box.isActive = false;
+   });
+
+   it("should preserve properties when entity becomes active", () => {
+     boxCollider.linearDamping = 0.1;
+     boxCollider.mass = 1;
+     box.isActive = true;
+     expect(boxCollider.linearDamping).eq(0.1);
+     expect(boxCollider.mass).eq(1);
+   });
+
+   it("should apply kinematic movement only when active", () => {
+     boxCollider.isKinematic = true;
+     boxCollider.move(new Vector3(1, 0, 0));
+     box.isActive = true;
+     engine.sceneManager.activeScene.physics._update(1/60);
+     expect(box.transform.position.x).eq(0);
+     
+     boxCollider.move(new Vector3(1, 0, 0));
+     engine.sceneManager.activeScene.physics._update(1/60);
+     expect(box.transform.position.x).eq(1);
+   });
+ });

Likely invalid or redundant comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (10)
packages/physics-physx/src/PhysXCollider.ts (1)

19-19: Consider adding proper typing for _pxActor

The _pxActor property is typed as any, which bypasses TypeScript's type checking. Consider adding proper typing to maintain type safety and improve code maintainability.

packages/core/src/physics/Collider.ts (1)

78-81: Consider optimizing shape removal loop

While the implementation is correct, consider using Array.forEach or for...of for better readability:

-    for (let i = 0, n = shapes.length; i < n; i++) {
-      this._removeNativeShape(shapes[i]);
-    }
+    shapes.forEach(shape => this._removeNativeShape(shape));
packages/core/src/physics/joint/HingeJoint.ts (3)

38-38: Improve type safety in axis setter.

While the optional chaining improves null safety, the type casting can be optimized.

-(<IHingeJoint>this._nativeJoint)?.setAxis(axis);
+const nativeJoint = this._nativeJoint as IHingeJoint;
+nativeJoint?.setAxis(axis);

74-74: Reduce type casting duplication in flag setters.

Extract the native joint casting to a private getter to reduce code duplication.

+private get _nativeHingeJoint(): IHingeJoint | null {
+  return this._nativeJoint as IHingeJoint;
+}

-(<IHingeJoint>this._nativeJoint)?.setHingeJointFlag(HingeJointFlag.LimitEnabled, value);
+this._nativeHingeJoint?.setHingeJointFlag(HingeJointFlag.LimitEnabled, value);

Also applies to: 88-88, 102-102


154-166: Improve code organization in _syncNative method.

The method has multiple type casts and could benefit from using the suggested _nativeHingeJoint getter.

protected override _syncNative(): void {
  super._syncNative();
  const motor = this._jointMotor;
+ const nativeJoint = this._nativeHingeJoint;
- (<IHingeJoint>this._nativeJoint).setAxis(this._axis);
+ nativeJoint?.setAxis(this._axis);
  // Apply similar changes to other lines
}
tests/src/core/physics/Collider.test.ts (2)

49-60: Add parameter validation to addBox utility function.

While the function effectively encapsulates box creation logic, it should validate input parameters to prevent runtime errors.

 function addBox(cubeSize: Vector3, type: typeof DynamicCollider | typeof StaticCollider, pos: Vector3) {
+    if (!cubeSize || !type || !pos) {
+        throw new Error("Required parameters cannot be null or undefined");
+    }
     const boxEntity = rootEntity.createChild("BoxEntity");
     boxEntity.transform.setPosition(pos.x, pos.y, pos.z);

68-69: Consider extracting physics material properties into constants.

The bounciness value of 0.1 should be defined as a named constant for better maintainability and clarity.

+const PHYSICS_MATERIAL_PROPERTIES = {
+    BOUNCINESS: 0.1,
+    DYNAMIC_FRICTION: 0,
+    STATIC_FRICTION: 0
+};

 beforeEach(function () {
     // ...
-    physicsBox.material.bounciness = 0.1;
+    physicsBox.material.bounciness = PHYSICS_MATERIAL_PROPERTIES.BOUNCINESS;

Also applies to: 90-90

packages/core/src/physics/DynamicCollider.ts (3)

24-24: Consider documenting the maxDepenetrationVelocity constant

The value 1.0000000331813535e32 seems unusually specific. Consider adding a comment explaining the significance of this precise value.


314-314: Consider adding debug logging for inactive colliders

When operations are skipped due to inactive colliders, consider adding debug logging to help with troubleshooting.

-  applyForce(force: Vector3): void {
-    this._phasedActiveInScene && (<IDynamicCollider>this._nativeCollider).addForce(force);
+  applyForce(force: Vector3): void {
+    if (!this._phasedActiveInScene) {
+      console.debug('Skipping force application on inactive collider');
+      return;
+    }
+    (<IDynamicCollider>this._nativeCollider).addForce(force);
   }

Also applies to: 322-322, 345-345


439-458: Consider using a shared utility for warning messages

The warning messages in _handleCenterOfMassChanged and _handleInertiaTensorChanged are similar and could be refactored to use a shared utility function.

+  private _warnAutoCalculated(property: string): void {
+    console.warn(
+      `The ${property} is automatically calculated, please set automatic${property} to false if you want to set it manually.`
+    );
+  }

   private _handleCenterOfMassChanged(): void {
     if (this._automaticCenterOfMass) {
-      console.warn(
-        "The center of mass is automatically calculated, please set automaticCenterOfMass to false if you want to set it manually."
-      );
+      this._warnAutoCalculated('center of mass');
     } else {
       (<IDynamicCollider>this._nativeCollider).setCenterOfMass(this._centerOfMass);
     }
   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 13cee3e and d5f9aab.

📒 Files selected for processing (9)
  • packages/core/src/physics/Collider.ts (5 hunks)
  • packages/core/src/physics/DynamicCollider.ts (9 hunks)
  • packages/core/src/physics/joint/HingeJoint.ts (6 hunks)
  • packages/core/src/physics/shape/ColliderShape.ts (4 hunks)
  • packages/physics-physx/src/PhysXCharacterController.ts (3 hunks)
  • packages/physics-physx/src/PhysXCollider.ts (3 hunks)
  • packages/physics-physx/src/PhysXPhysicsScene.ts (4 hunks)
  • packages/physics-physx/src/shape/PhysXColliderShape.ts (2 hunks)
  • tests/src/core/physics/Collider.test.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/physics-physx/src/PhysXCharacterController.ts
  • packages/physics-physx/src/shape/PhysXColliderShape.ts
🔇 Additional comments (30)
packages/physics-physx/src/PhysXCollider.ts (3)

32-35: ⚠️ Potential issue

Add parameter validation in addShape

The shape parameter should be validated before performing operations. Also consider adding error handling for duplicate shapes.

  addShape(shape: PhysXColliderShape): void {
+   if (!shape) {
+     throw new Error("Shape cannot be null or undefined");
+   }
+   if (this._shapes.includes(shape)) {
+     throw new Error("Shape is already attached to this collider");
+   }
    this._pxActor.attachShape(shape._pxShape);
    this._shapes.push(shape);
    this._scene?._addColliderShape(shape._id);
  }

43-45: ⚠️ Potential issue

Add index validation in removeShape

The current implementation directly splices the array without checking if the shape exists, which could throw an error if the shape isn't found.

  removeShape(shape: PhysXColliderShape): void {
+   if (!shape) {
+     throw new Error("Shape cannot be null or undefined");
+   }
    this._pxActor.detachShape(shape._pxShape, true);
    const shapes = this._shapes;
-   shapes.splice(shapes.indexOf(shape), 1);
+   const index = shapes.indexOf(shape);
+   if (index === -1) {
+     throw new Error("Shape is not attached to this collider");
+   }
+   shapes.splice(index, 1);
    this._scene?._removeColliderShape(shape._id);
  }

Likely invalid or redundant comment.


35-35: Verify scene shape management methods

The collider now delegates shape management to the scene through _addColliderShape and _removeColliderShape. Let's verify these methods exist and handle shapes correctly.

Also applies to: 45-45

✅ Verification successful

Shape management methods are properly implemented

The verification confirms that _addColliderShape and _removeColliderShape methods are correctly implemented in PhysXPhysicsScene:

  • _addColliderShape initializes event mapping for the shape
  • _removeColliderShape properly cleans up events associated with the shape
  • Both methods are consistently used across PhysX and Lite physics implementations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for shape management methods in PhysXPhysicsScene
ast-grep --pattern 'class PhysXPhysicsScene {
  $$$
  _addColliderShape($_) {
    $$$
  }
  $$$
  _removeColliderShape($_) {
    $$$
  }
  $$$
}'

Length of output: 147


Script:

#!/bin/bash
# Let's try a broader search for the scene class and its methods
rg -A 5 "class.*PhysXPhysicsScene" 

# Also search for method definitions directly
rg "_addColliderShape|_removeColliderShape" --type ts

Length of output: 2118


Script:

#!/bin/bash
# Let's check the implementation of these methods in PhysXPhysicsScene
rg -A 5 "_addColliderShape|_removeColliderShape" packages/physics-physx/src/PhysXPhysicsScene.ts

Length of output: 952

packages/core/src/physics/shape/ColliderShape.ts (6)

5-5: LGTM: Improved cloning strategy for position and rotation

The switch from @ignoreClone to @deepClone for position and rotation properties ensures proper deep cloning of these essential transform properties.

Also applies to: 27-29


132-133: Add test coverage for object tracking lifecycle

The object tracking implementation needs test coverage to verify proper lifecycle management.


61-61: LGTM: Added validation for contactOffset

The use of Math.max(0, value) properly enforces the documented constraint that contactOffset must be greater than or equal to 0.


140-140: LGTM: Improved cleanup and cloning implementation

The changes properly handle object lifecycle:

  • Cloning now uses centralized synchronization
  • Destroy method properly cleans up native references and removes tracking

Also applies to: 148-150


152-159: Add error handling to _syncNative method

The _syncNative method should include error handling for native shape operations.


164-164: Verify the impact of multiple shape change notifications

The addition of _handleShapesChanged calls in both _setPosition and _setRotation could lead to redundant notifications when both properties change simultaneously.

Also applies to: 169-169

✅ Verification successful

Multiple shape change notifications are handled appropriately

The implementation shows that _handleShapesChanged() serves different purposes:

  • In Collider class, it's an empty method that gets overridden
  • In DynamicCollider, it updates mass and inertia only when automatic calculations are enabled
  • The notifications in ColliderShape are necessary as each property change (position, rotation, material) can independently affect the physics calculations

The redundant notifications when both position and rotation change are not problematic because:

  1. The base implementation is a no-op
  2. The dynamic collider implementation has proper guards (if (this._automaticCenterOfMass || this._automaticInertiaTensor))
  3. Each property change needs to trigger recalculation independently for correctness
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for patterns where both position and rotation are set together
rg -A 5 '_setPosition.*_setRotation|_setRotation.*_setPosition'

# Check the implementation of _handleShapesChanged
ast-grep --pattern '_handleShapesChanged() {
  $$$
}'

Length of output: 121


Script:

#!/bin/bash
# Let's try a different approach to find the implementation and usage patterns

# Find the implementation of _handleShapesChanged
ast-grep --pattern 'class $_ {
  $$$
  _handleShapesChanged() {
    $$$
  }
  $$$
}'

# Look for patterns where shape properties are modified
rg -B 2 -A 2 "_handleShapesChanged"

# Check for position and rotation setters
ast-grep --pattern 'class $_ {
  $$$
  _setPosition($$$) {
    $$$
  }
  $$$
}'

ast-grep --pattern 'class $_ {
  $$$
  _setRotation($$$) {
    $$$
  }
  $$$
}'

Length of output: 2967

packages/core/src/physics/Collider.ts (6)

67-68: LGTM! Consistent shape management

The changes maintain symmetry with addShape implementation and properly notify shape changes.


126-132: LGTM! Clean implementation of cloning and change notifications

The implementation simplifies cloning through native synchronization and provides a proper hook for derived classes to handle shape changes.


134-137: LGTM! Well-structured native synchronization

The method provides a clean centralized way to synchronize shapes with the native physics backend.


145-162: LGTM! Proper resource cleanup and encapsulation

The implementation ensures proper cleanup of shapes during destruction and provides clean encapsulation of native shape operations.


112-112: LGTM! Verify physics system integration

The simplified implementation provides better separation of concerns by delegating shape management to the physics system.

Let's verify the physics system properly handles colliders:

Also applies to: 119-119

✅ Verification successful

Physics system properly manages colliders - Implementation verified

The physics system (PhysicsScene) has robust collider management:

  • Maintains collider collection with proper indexing
  • Handles native physics scene integration
  • Implements both _addCollider and _removeCollider with complete functionality

The simplified implementation in Collider.ts correctly delegates to these well-implemented methods.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for physics system implementation of collider management
ast-grep --pattern 'class $_ {
  $$$
  _addCollider($_) {
    $$$
  }
  _removeCollider($_) {
    $$$
  }
  $$$
}'

Length of output: 116


Script:

#!/bin/bash
# Let's try a different approach to find physics system implementation
# First, find the physics-related files
fd "Physics" packages/core/src

# Then search for collider management methods
rg "_addCollider|_removeCollider" packages/core/src -A 5

Length of output: 1887


54-55: LGTM! Verify shape change notifications

The changes improve naming consistency and add proper change notifications. The implementation correctly handles shape ownership transfer.

Let's verify if all collider implementations properly handle shape changes:

✅ Verification successful

Shape change notifications are properly implemented

The verification shows that the _handleShapesChanged hook is correctly implemented across the codebase:

  • Base Collider class provides the default empty implementation
  • DynamicCollider properly overrides it for dynamic physics updates
  • ColliderShape correctly triggers the notification on its parent collider
  • The hook is called at appropriate points: shape addition, removal, and property changes
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for classes extending Collider and verify they override _handleShapesChanged
ast-grep --pattern 'class $_ extends Collider {
  $$$
  _handleShapesChanged() {
    $$$
  }
  $$$
}'

Length of output: 103


Script:

#!/bin/bash
# Let's first find all files that might contain Collider implementations
fd -e ts -e js "Collider" 

# Then let's search for classes extending Collider
rg "class.*extends.*Collider" -A 10

# Also search for _handleShapesChanged usage
rg "_handleShapesChanged"

Length of output: 18258

packages/core/src/physics/joint/HingeJoint.ts (4)

Line range hint 110-138: LGTM! Null safety improvements implemented correctly.

The motor and limits setters now properly handle null cases and manage listeners safely.


148-152: Add error handling for missing Collider component.

The method assumes the Collider component exists on the entity.


168-188: Improve null safety and reduce type casting in change handlers.

The handlers would benefit from the same improvements suggested for other methods.


9-25: Verify the impact of switching from @ignoreClone to @deepClone.

The change from @ignoreClone to @deepClone for _axis, _jointMotor, and _limits properties affects how these properties are handled during object cloning. Ensure that deep cloning these properties doesn't create unintended side effects, especially for the motor and limits that maintain their own update listeners.

✅ Verification successful

The switch from @ignoreClone to @deepClone is correct and necessary

Based on the test file and implementation details:

  1. The test case "clone" explicitly verifies that cloned joints have independent _updateFlagManager instances for both limits and motor properties
  2. The test confirms that modifying properties of the cloned joint (limits.min and motor.targetVelocity) doesn't affect the original joint
  3. Both JointMotor and JointLimits classes use @deepClone for their _updateFlagManager to ensure independent update tracking
  4. The base Joint class also uses @deepClone for collider info properties that need independent copies

The change from @ignoreClone to @deepClone for _axis, _jointMotor, and _limits is correct as it ensures proper isolation between original and cloned instances, which is verified by the existing test cases.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for clone-related tests and usages
rg -A 5 "clone.*HingeJoint" --type ts

Length of output: 39


Script:

#!/bin/bash
# Let's try a broader search for clone-related tests and implementations
rg -l "CloneManager" --type ts

# Then search for HingeJoint usage patterns
rg -l "HingeJoint" --type ts

# Look for clone decorators usage in joint classes
ast-grep --pattern '@$_Clone
class $_ extends Joint'

Length of output: 2866


Script:

#!/bin/bash
# Let's examine the test file for cloning behavior
cat tests/src/core/physics/HingeJoint.test.ts

# Check JointMotor and JointLimits implementation for clone-related code
cat packages/core/src/physics/joint/JointMotor.ts
cat packages/core/src/physics/joint/JointLimits.ts

# Look for clone decorator usage in the base Joint class
cat packages/core/src/physics/joint/Joint.ts

Length of output: 27852

packages/physics-physx/src/PhysXPhysicsScene.ts (6)

121-126: LGTM! Clean implementation of collider removal.

The implementation properly handles cleanup of scene references, actor removal, and shape management.


Line range hint 133-144: LGTM! Proper character controller initialization.

The implementation correctly handles controller initialization and shape management.


227-229: Add input validation for shape ID.

The method should validate the shape ID before initializing the event map.

 _addColliderShape(id: number) {
+  if (typeof id !== 'number' || id < 0) {
+    throw new Error("Invalid shape ID");
+  }
   this._physXManager._eventMap[id] = {};
 }

234-249: LGTM! Thorough event cleanup implementation.

The implementation properly handles:

  • Cleanup of current events
  • Event pool management
  • Proper deletion of event map entries

154-158: 🛠️ Refactor suggestion

Remove redundant optional chaining operator.

The shape?._id has unnecessary optional chaining since there's already a truthy check with shape &&.

-    shape && this._removeColliderShape(shape?._id);
+    shape && this._removeColliderShape(shape._id);

Likely invalid or redundant comment.


109-114: LGTM! Verify shape management consistency.

The implementation correctly manages collider shapes and scene references. The iteration through shapes is properly handled.

Let's verify the shape management consistency:

✅ Verification successful

Shape management implementation is consistent and correct

The verification confirms that the shape management implementation is consistent across the codebase:

  • PhysXPhysicsScene correctly manages shape IDs through _addColliderShape and _removeColliderShape
  • PhysXCollider and PhysXCharacterController properly invoke these methods when shapes are added or removed
  • The shape ID management is consistently used for event handling and controller management
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining direct shape management calls
# and verify the consistency of shape handling across the codebase

# Check for any remaining direct shape management calls
rg -l "addColliderShape|removeColliderShape" --type ts

# Look for shape management patterns
ast-grep --pattern 'shape._id' --type ts

Length of output: 512


Script:

#!/bin/bash
# Let's verify shape management consistency with corrected commands

# Check shape management patterns in PhysXPhysicsScene.ts
rg "_addColliderShape|_removeColliderShape" packages/physics-physx/src/PhysXPhysicsScene.ts -A 3

# Check how shapes are managed in related files
rg "shapes\." packages/physics-physx/src/PhysXCollider.ts -A 2

# Check for shape ID usage patterns
rg "shape\._id" packages/physics-physx/src/ -A 2

Length of output: 2219

tests/src/core/physics/Collider.test.ts (3)

62-64: LGTM! Clean and focused utility function.

The formatValue function effectively handles floating-point precision issues for test comparisons.


349-362: LGTM! Comprehensive inactive modification test.

The test effectively verifies shape management operations with appropriate assertions.


364-371: LGTM! Thorough destroy test.

The test comprehensively verifies all aspects of collider destruction.

packages/core/src/physics/DynamicCollider.ts (2)

422-427: Add test coverage for mass and inertia updates

The _setMassAndUpdateInertia method lacks test coverage, particularly for the interaction between automatic and manual modes.


12-31: Verify the impact of removing @ignoreClone decorators

The removal of @ignoreClone decorators from several properties might affect the cloning behavior of DynamicCollider instances.

packages/core/src/physics/joint/HingeJoint.ts Show resolved Hide resolved
tests/src/core/physics/Collider.test.ts Outdated Show resolved Hide resolved
packages/core/src/physics/DynamicCollider.ts Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (7)
packages/physics-lite/src/LiteDynamicCollider.ts (1)

Line range hint 1-224: Consider improving the architecture documentation.

The class implements IDynamicCollider but most operations direct users to use Physics-PhysX instead. This suggests a need for:

  1. Clear documentation about when to use LiteDynamicCollider vs PhysX
  2. Consider adding a feature flag or configuration option to prevent instantiation of lite versions when PhysX features are required
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 76-77: packages/physics-lite/src/LiteDynamicCollider.ts#L76-L77
Added lines #L76 - L77 were not covered by tests


[warning] 83-84: packages/physics-lite/src/LiteDynamicCollider.ts#L83-L84
Added lines #L83 - L84 were not covered by tests


[warning] 90-91: packages/physics-lite/src/LiteDynamicCollider.ts#L90-L91
Added lines #L90 - L91 were not covered by tests


[warning] 97-98: packages/physics-lite/src/LiteDynamicCollider.ts#L97-L98
Added lines #L97 - L98 were not covered by tests


[warning] 105-105: packages/physics-lite/src/LiteDynamicCollider.ts#L105
Added line #L105 was not covered by tests


[warning] 112-112: packages/physics-lite/src/LiteDynamicCollider.ts#L112
Added line #L112 was not covered by tests


[warning] 119-119: packages/physics-lite/src/LiteDynamicCollider.ts#L119
Added line #L119 was not covered by tests


[warning] 126-126: packages/physics-lite/src/LiteDynamicCollider.ts#L126
Added line #L126 was not covered by tests


[warning] 133-133: packages/physics-lite/src/LiteDynamicCollider.ts#L133
Added line #L133 was not covered by tests


[warning] 140-140: packages/physics-lite/src/LiteDynamicCollider.ts#L140
Added line #L140 was not covered by tests


[warning] 147-148: packages/physics-lite/src/LiteDynamicCollider.ts#L147-L148
Added lines #L147 - L148 were not covered by tests


[warning] 153-154: packages/physics-lite/src/LiteDynamicCollider.ts#L153-L154
Added lines #L153 - L154 were not covered by tests


[warning] 160-161: packages/physics-lite/src/LiteDynamicCollider.ts#L160-L161
Added lines #L160 - L161 were not covered by tests


[warning] 168-168: packages/physics-lite/src/LiteDynamicCollider.ts#L168
Added line #L168 was not covered by tests


[warning] 175-175: packages/physics-lite/src/LiteDynamicCollider.ts#L175
Added line #L175 was not covered by tests


[warning] 182-183: packages/physics-lite/src/LiteDynamicCollider.ts#L182-L183
Added lines #L182 - L183 were not covered by tests


[warning] 189-189: packages/physics-lite/src/LiteDynamicCollider.ts#L189
Added line #L189 was not covered by tests


[warning] 196-196: packages/physics-lite/src/LiteDynamicCollider.ts#L196
Added line #L196 was not covered by tests


[warning] 203-203: packages/physics-lite/src/LiteDynamicCollider.ts#L203
Added line #L203 was not covered by tests


[warning] 210-210: packages/physics-lite/src/LiteDynamicCollider.ts#L210
Added line #L210 was not covered by tests

packages/physics-physx/src/PhysXPhysicsScene.ts (2)

Line range hint 133-144: Add null check before accessing shape._id

While the code handles shape existence check, it should also ensure shape._id is defined before using it.

   if (shape) {
     const lastPXManager = characterController._pxManager;
     if (lastPXManager !== this) {
       lastPXManager && characterController._destroyPXController();
       characterController._createPXController(this, shape);
     }
-    this._addColliderShape(shape._id);
+    if (shape._id !== undefined) {
+      this._addColliderShape(shape._id);
+    }
   }

234-249: LGTM with a suggestion for type safety

The event cleanup logic is thorough and handles all edge cases. Consider adding type safety for the id parameter.

-  _removeColliderShape(id: number) {
+  _removeColliderShape(id: number & { __brand: 'ShapeId' }) {
     const { _eventPool: eventPool, _currentEvents: currentEvents } = this;
     const { _eventMap: eventMap } = this._physXManager;
packages/core/src/physics/joint/Joint.ts (3)

15-16: Consider thread safety implications of static _tempVector3

The static _tempVector3 used for temporary calculations could lead to race conditions if the joint calculations happen concurrently.

Consider making this an instance variable or using a temporary vector pool for thread safety.


204-207: Enhance cleanup in _onDisableInScene

The cleanup of native joint could be more robust by checking for null and handling potential errors.

 override _onDisableInScene(): void {
+  if (this._nativeJoint) {
+    try {
       this._nativeJoint.destroy();
+    } catch (error) {
+      console.error("Failed to destroy native joint:", error);
+    }
     this._nativeJoint = null;
+  }
 }

77-80: Enhance warning message for connectedAnchor setter

The warning message could be more informative by suggesting the correct approach.

-    console.warn("Cannot set connectedAnchor when autoConnectedAnchor is true.");
+    console.warn("Cannot set connectedAnchor when autoConnectedAnchor is true. Set autoConnectedAnchor to false first if you need manual control.");
tests/src/core/physics/Collider.test.ts (1)

114-115: Consider improving type declarations for physics._update

Multiple @ts-ignore comments are used to access the internal _update method. While this is acceptable in test code, consider:

  1. Adding proper type declarations for testing purposes
  2. Creating a test utility function to wrap the physics update call
// Add to test utilities
function updatePhysics(engine: WebGLEngine, deltaTime: number) {
  (engine.sceneManager.activeScene.physics as any)._update(deltaTime);
}

Also applies to: 132-133, 151-152, 212-214, 254-256

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d5f9aab and 94cefcf.

📒 Files selected for processing (6)
  • packages/core/src/physics/PhysicsMaterial.ts (2 hunks)
  • packages/core/src/physics/joint/Joint.ts (7 hunks)
  • packages/physics-lite/src/LiteDynamicCollider.ts (2 hunks)
  • packages/physics-physx/src/PhysXDynamicCollider.ts (5 hunks)
  • packages/physics-physx/src/PhysXPhysicsScene.ts (4 hunks)
  • tests/src/core/physics/Collider.test.ts (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/physics/PhysicsMaterial.ts
🧰 Additional context used
🪛 GitHub Check: codecov/patch
packages/core/src/physics/joint/Joint.ts

[warning] 218-219: packages/core/src/physics/joint/Joint.ts#L218-L219
Added lines #L218 - L219 were not covered by tests


[warning] 251-252: packages/core/src/physics/joint/Joint.ts#L251-L252
Added lines #L251 - L252 were not covered by tests


[warning] 257-258: packages/core/src/physics/joint/Joint.ts#L257-L258
Added lines #L257 - L258 were not covered by tests

packages/physics-lite/src/LiteDynamicCollider.ts

[warning] 26-28: packages/physics-lite/src/LiteDynamicCollider.ts#L26-L28
Added lines #L26 - L28 were not covered by tests


[warning] 33-35: packages/physics-lite/src/LiteDynamicCollider.ts#L33-L35
Added lines #L33 - L35 were not covered by tests


[warning] 76-77: packages/physics-lite/src/LiteDynamicCollider.ts#L76-L77
Added lines #L76 - L77 were not covered by tests


[warning] 83-84: packages/physics-lite/src/LiteDynamicCollider.ts#L83-L84
Added lines #L83 - L84 were not covered by tests


[warning] 90-91: packages/physics-lite/src/LiteDynamicCollider.ts#L90-L91
Added lines #L90 - L91 were not covered by tests


[warning] 97-98: packages/physics-lite/src/LiteDynamicCollider.ts#L97-L98
Added lines #L97 - L98 were not covered by tests


[warning] 105-105: packages/physics-lite/src/LiteDynamicCollider.ts#L105
Added line #L105 was not covered by tests


[warning] 112-112: packages/physics-lite/src/LiteDynamicCollider.ts#L112
Added line #L112 was not covered by tests


[warning] 119-119: packages/physics-lite/src/LiteDynamicCollider.ts#L119
Added line #L119 was not covered by tests


[warning] 126-126: packages/physics-lite/src/LiteDynamicCollider.ts#L126
Added line #L126 was not covered by tests


[warning] 133-133: packages/physics-lite/src/LiteDynamicCollider.ts#L133
Added line #L133 was not covered by tests


[warning] 140-140: packages/physics-lite/src/LiteDynamicCollider.ts#L140
Added line #L140 was not covered by tests


[warning] 147-148: packages/physics-lite/src/LiteDynamicCollider.ts#L147-L148
Added lines #L147 - L148 were not covered by tests


[warning] 153-154: packages/physics-lite/src/LiteDynamicCollider.ts#L153-L154
Added lines #L153 - L154 were not covered by tests


[warning] 160-161: packages/physics-lite/src/LiteDynamicCollider.ts#L160-L161
Added lines #L160 - L161 were not covered by tests


[warning] 168-168: packages/physics-lite/src/LiteDynamicCollider.ts#L168
Added line #L168 was not covered by tests


[warning] 175-175: packages/physics-lite/src/LiteDynamicCollider.ts#L175
Added line #L175 was not covered by tests


[warning] 182-183: packages/physics-lite/src/LiteDynamicCollider.ts#L182-L183
Added lines #L182 - L183 were not covered by tests


[warning] 189-189: packages/physics-lite/src/LiteDynamicCollider.ts#L189
Added line #L189 was not covered by tests


[warning] 196-196: packages/physics-lite/src/LiteDynamicCollider.ts#L196
Added line #L196 was not covered by tests


[warning] 203-203: packages/physics-lite/src/LiteDynamicCollider.ts#L203
Added line #L203 was not covered by tests


[warning] 210-210: packages/physics-lite/src/LiteDynamicCollider.ts#L210
Added line #L210 was not covered by tests

🔇 Additional comments (17)
packages/physics-lite/src/LiteDynamicCollider.ts (4)

25-28: Avoid misleading return values in getter methods.

These getter methods return the input parameter unmodified after logging an error, which could be misleading as it suggests the method performed some calculation.

Also applies to: 32-35, 96-98, 159-161

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 26-28: packages/physics-lite/src/LiteDynamicCollider.ts#L26-L28
Added lines #L26 - L28 were not covered by tests


75-77: Standardize error handling across methods.

Methods inconsistently handle errors - some throw strings while others use console.error. This makes error handling unpredictable for consumers.

Also applies to: 82-84, 146-148, 188-189

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 76-77: packages/physics-lite/src/LiteDynamicCollider.ts#L76-L77
Added lines #L76 - L77 were not covered by tests


Line range hint 22-210: Add test coverage for new methods.

All new methods lack test coverage. Even though these methods only log errors or return default values, it's important to verify their behavior.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 76-77: packages/physics-lite/src/LiteDynamicCollider.ts#L76-L77
Added lines #L76 - L77 were not covered by tests


[warning] 83-84: packages/physics-lite/src/LiteDynamicCollider.ts#L83-L84
Added lines #L83 - L84 were not covered by tests


[warning] 90-91: packages/physics-lite/src/LiteDynamicCollider.ts#L90-L91
Added lines #L90 - L91 were not covered by tests


[warning] 97-98: packages/physics-lite/src/LiteDynamicCollider.ts#L97-L98
Added lines #L97 - L98 were not covered by tests


[warning] 105-105: packages/physics-lite/src/LiteDynamicCollider.ts#L105
Added line #L105 was not covered by tests


[warning] 112-112: packages/physics-lite/src/LiteDynamicCollider.ts#L112
Added line #L112 was not covered by tests


[warning] 119-119: packages/physics-lite/src/LiteDynamicCollider.ts#L119
Added line #L119 was not covered by tests


[warning] 126-126: packages/physics-lite/src/LiteDynamicCollider.ts#L126
Added line #L126 was not covered by tests


[warning] 133-133: packages/physics-lite/src/LiteDynamicCollider.ts#L133
Added line #L133 was not covered by tests


[warning] 140-140: packages/physics-lite/src/LiteDynamicCollider.ts#L140
Added line #L140 was not covered by tests


[warning] 147-148: packages/physics-lite/src/LiteDynamicCollider.ts#L147-L148
Added lines #L147 - L148 were not covered by tests


[warning] 153-154: packages/physics-lite/src/LiteDynamicCollider.ts#L153-L154
Added lines #L153 - L154 were not covered by tests


[warning] 160-161: packages/physics-lite/src/LiteDynamicCollider.ts#L160-L161
Added lines #L160 - L161 were not covered by tests


[warning] 168-168: packages/physics-lite/src/LiteDynamicCollider.ts#L168
Added line #L168 was not covered by tests


[warning] 175-175: packages/physics-lite/src/LiteDynamicCollider.ts#L175
Added line #L175 was not covered by tests


[warning] 182-183: packages/physics-lite/src/LiteDynamicCollider.ts#L182-L183
Added lines #L182 - L183 were not covered by tests


[warning] 189-189: packages/physics-lite/src/LiteDynamicCollider.ts#L189
Added line #L189 was not covered by tests


[warning] 196-196: packages/physics-lite/src/LiteDynamicCollider.ts#L196
Added line #L196 was not covered by tests


[warning] 203-203: packages/physics-lite/src/LiteDynamicCollider.ts#L203
Added line #L203 was not covered by tests


[warning] 210-210: packages/physics-lite/src/LiteDynamicCollider.ts#L210
Added line #L210 was not covered by tests


40-42: 🛠️ Refactor suggestion

Maintain consistency in setter method implementations.

The method should follow the same pattern as other setter methods in the class.

  setMassAndUpdateInertia(mass: number): void {
-   console.error("Physics-lite don't support setMassAndUpdateInertia. Use Physics-PhysX instead!");
+   console.error(`Physics-lite don't support setMassAndUpdateInertia. Use Physics-PhysX instead!`);
  }

Likely invalid or redundant comment.

packages/physics-physx/src/PhysXDynamicCollider.ts (3)

Line range hint 33-147: LGTM! Well-implemented getter methods.

The new getter methods are well-structured with:

  • Consistent use of out parameters for vector operations to minimize allocations
  • Clear documentation referencing the interface
  • Proper implementation of the PhysX API

255-261: LGTM! Clean implementation of isSleeping.

The method is well-documented and properly implements the interface requirement, completing the sleep-related functionality alongside the existing sleep() and wakeUp() methods.


182-183: Review the flag setting sequence in ContinuousDynamic mode.

There's a potential issue in the order of operations:

  1. Line 182 disables CCD
  2. Line 183 enables CCD friction

This sequence might create a brief window where neither flag is set. Consider setting both flags in a more atomic way or document why this specific order is necessary.

Let's check if this is a common pattern in other implementations:

packages/physics-physx/src/PhysXPhysicsScene.ts (2)

121-126: 🛠️ Refactor suggestion

Add null checks for defensive programming

Similar to addCollider, this method should validate its inputs.

 removeCollider(collider: PhysXCollider): void {
+  if (!collider) {
+    throw new Error("Collider cannot be null");
+  }
   collider._scene = null;
   this._pxScene.removeActor(collider._pxActor, true);
   const shapes = collider._shapes;
   for (let i = 0, n = shapes.length; i < n; i++) {
     this._removeColliderShape(shapes[i]._id);
   }
 }

227-229: 🛠️ Refactor suggestion

Add input validation for shape ID

The method should validate the shape ID before initializing the event map.

 _addColliderShape(id: number) {
+  if (typeof id !== 'number' || id < 0) {
+    throw new Error("Invalid shape ID");
+  }
   this._physXManager._eventMap[id] = {};
 }
packages/core/src/physics/joint/Joint.ts (4)

183-186: ⚠️ Potential issue

Remove @ts-ignore by implementing proper type definitions

The @ts-ignore comments mask potential type safety issues. This was previously flagged but hasn't been addressed.

Create proper interfaces for the event handlers:

interface ValueChangeHandler {
  _onValueChanged: (owner: AnchorOwner) => void;
}

interface Vector3WithChangeHandler extends Vector3, ValueChangeHandler {}

// Then update the property types
protected _colliderInfo = new JointColliderInfo<Vector3WithChangeHandler>();

211-227: ⚠️ Potential issue

Add error handling for native operations

The _syncNative method performs multiple native operations without error handling.

Wrap the native operations in try-catch blocks:

 protected _syncNative(): void {
   if (this._nativeJoint) {
+    try {
       this._nativeJoint.setConnectedCollider(this._connectedColliderInfo.collider?._nativeCollider || null);
       // ... other operations ...
       this._nativeJoint.setBreakTorque(this._torque);
+    } catch (error) {
+      console.error("Failed to sync joint properties:", error);
+    }
   }
 }
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 218-219: packages/core/src/physics/joint/Joint.ts#L218-L219
Added lines #L218 - L219 were not covered by tests


249-259: ⚠️ Potential issue

Add test coverage for transform change handlers

The transform change handlers are critical for joint behavior but lack test coverage.

Add test cases for transform change handlers to verify the correct behavior when scale changes occur.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 251-252: packages/core/src/physics/joint/Joint.ts#L251-L252
Added lines #L251 - L252 were not covered by tests


[warning] 257-258: packages/core/src/physics/joint/Joint.ts#L257-L258
Added lines #L257 - L258 were not covered by tests


229-247: 🛠️ Refactor suggestion

Add validation for vector calculations

The _calculateConnectedAnchor method performs vector operations without validating for edge cases.

Add validation for zero scales and NaN values to prevent potential issues.

tests/src/core/physics/Collider.test.ts (4)

49-64: Well-structured utility functions!

The addition of addBox and formatValue functions improves code maintainability by:

  • Reducing code duplication in test cases
  • Providing consistent box entity creation
  • Handling floating-point precision comparison properly

68-69: Clean setup improvements!

The changes enhance test setup clarity by:

  • Directly assigning rootEntity from the active scene
  • Explicitly setting the bounciness value for consistent physics behavior

Also applies to: 90-90


341-354: Well-structured shape management test!

The test case thoroughly verifies shape management operations with proper assertions:

  1. Shape addition verification
  2. Shape removal verification
  3. Shape clearing verification
  4. Collider destruction verification

356-363: Comprehensive destruction test!

The test case properly verifies:

  1. Collider destruction state
  2. Component removal
  3. Shape cleanup

tests/src/core/physics/Collider.test.ts Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (3)
tests/src/core/physics/SpringJoint.test.ts (1)

11-22: Add documentation for the helper function

The addBox helper function should include JSDoc comments explaining:

  • The purpose of the function
  • Why friction is set to 0
  • Parameter descriptions and return type
+/**
+ * Creates a box entity with a collider for physics testing.
+ * Friction is set to 0 to isolate joint behavior from friction effects.
+ * @param cubeSize - The size of the box collider
+ * @param type - The type of collider (dynamic or static)
+ * @param pos - The initial position of the box
+ * @returns The created box entity
+ */
 function addBox(cubeSize: Vector3, type: typeof DynamicCollider | typeof StaticCollider, pos: Vector3) {
tests/src/core/physics/Joint.test.ts (2)

39-362: Consider improving test organization and descriptions

While the test coverage is comprehensive, consider these improvements:

  1. Add descriptive comments before each test case explaining the specific behavior being tested
  2. Group related test cases using describe blocks (e.g., group all mass-related tests, all break-related tests)
  3. Consider adding edge cases for error conditions

Example structure:

describe('Joint Mass Properties', () => {
  it('should apply mass scale correctly', () => {
    // existing massScale test
  });
  
  it('should apply connected mass scale correctly', () => {
    // existing connectedMassScale test
  });
});
🧰 Tools
🪛 Biome (1.9.4)

[error] 191-191: 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] 238-238: 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)


132-157: Consider consolidating similar test cases

The massScale and connectedMassScale test cases share very similar setup and verification logic. Consider extracting common setup into a shared helper function to reduce duplication:

function setupMassScaleTest(useConnected: boolean) {
  engine.sceneManager.activeScene.physics.gravity = new Vector3(0, -1, 0);
  const box1 = addBox(new Vector3(1, 1, 1), DynamicCollider, new Vector3(0, 5, 0));
  const box2 = addBox(new Vector3(1, 1, 1), DynamicCollider, new Vector3(0, 1, 0));
  const box1Collider = box1.getComponent(DynamicCollider);
  const box2Collider = box2.getComponent(DynamicCollider);
  box1Collider.isKinematic = true;
  const fixedJoint = useConnected ? box1.addComponent(FixedJoint) : box2.addComponent(FixedJoint);
  fixedJoint.connectedCollider = useConnected ? box2.getComponent(DynamicCollider) : box1.getComponent(DynamicCollider);
  fixedJoint.automaticConnectedAnchor = true;
  fixedJoint.breakForce = 10;
  return { box1Collider, box2Collider, fixedJoint };
}

Also applies to: 159-183

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 94cefcf and d3fb9bb.

📒 Files selected for processing (4)
  • packages/core/src/physics/joint/Joint.ts (7 hunks)
  • tests/src/core/physics/HingeJoint.test.ts (1 hunks)
  • tests/src/core/physics/Joint.test.ts (1 hunks)
  • tests/src/core/physics/SpringJoint.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/src/core/physics/HingeJoint.test.ts
🧰 Additional context used
🪛 Biome (1.9.4)
tests/src/core/physics/Joint.test.ts

[error] 191-191: 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] 238-238: 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)

tests/src/core/physics/SpringJoint.test.ts

[error] 41-41: 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] 73-73: 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] 96-96: 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] 128-128: 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] 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] 175-175: 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] 208-208: 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)

🔇 Additional comments (13)
packages/core/src/physics/joint/Joint.ts (5)

15-17: LGTM! Good architectural decisions

Making the class abstract and using a static Vector3 for calculations are good practices for performance and proper inheritance hierarchy.


288-304: LGTM! Well-structured supporting types

The AnchorOwner enum and JointColliderInfo class are well-designed with appropriate use of decorators.


186-194: ⚠️ Potential issue

Fix TypeScript type safety issues in constructor

The constructor still contains multiple @ts-ignore comments which bypass type checking. This was previously flagged but hasn't been addressed.

Create proper interfaces for the event handlers:

interface ValueChangeHandler {
  _onValueChanged: (owner: AnchorOwner) => void;
}

interface Vector3WithChangeHandler extends Vector3, ValueChangeHandler {}

interface TransformUpdateManager {
  addListener(listener: (type: TransformModifyFlags) => void): void;
  removeListener(listener: (type: TransformModifyFlags) => void): void;
}

Then update the property types:

protected _colliderInfo = new JointColliderInfo<Vector3WithChangeHandler>();

232-250: 🛠️ Refactor suggestion

Add validation for vector calculations

The _calculateConnectedAnchor method performs vector operations without validating for edge cases.

Add validation for zero scales and NaN values:

 private _calculateConnectedAnchor(): void {
   const colliderInfo = this._colliderInfo;
   const connectedColliderInfo = this._connectedColliderInfo;
   const { worldPosition: selfPos } = this.entity.transform;
+  if (!selfPos || !this._isValidVector(selfPos)) {
+    console.warn("Invalid self position in joint calculation");
+    return;
+  }
   // ... rest of the method
 }

+ private _isValidVector(v: Vector3): boolean {
+   return !isNaN(v.x) && !isNaN(v.y) && !isNaN(v.z);
+ }

252-261: 🛠️ Refactor suggestion

Add test coverage for transform change handlers

The transform change handlers are critical for joint behavior but lack test coverage.

Add test cases for transform scale changes:

describe('Joint transform changes', () => {
  it('should update anchor when transform scale changes', () => {
    const joint = new TestJoint(entity);
    const spy = jest.spyOn(joint as any, '_updateActualAnchor');
    
    entity.transform.setWorldScale(2, 2, 2);
    expect(spy).toHaveBeenCalledWith(AnchorOwner.Self);
  });
});
tests/src/core/physics/SpringJoint.test.ts (4)

24-28: Add cleanup logic in afterAll hook

The engine resources should be properly disposed of after all tests are complete.


30-33: Remove redundant gravity reset

The gravity reset in beforeEach is unnecessary as each test explicitly sets gravity to (0, 0, 0).


53-54: Remove @ts-ignore by properly typing physics update method

The repeated use of @ts-ignore for physics._update should be addressed.

Also applies to: 82-83, 108-109, 140-141, 164-165, 187-188, 223-224


41-41: Refactor assignments in expressions

The assignments within expressions make the code harder to read and maintain.

Also applies to: 73-73, 96-96, 128-128, 152-152, 175-175, 208-208

🧰 Tools
🪛 Biome (1.9.4)

[error] 41-41: 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)

tests/src/core/physics/Joint.test.ts (4)

11-22: LGTM! Well-structured helper function.

The addBox helper function effectively encapsulates the common setup logic, improving test maintainability and reducing code duplication.


24-26: LGTM! Precise number formatting.

The formatValue helper function appropriately handles floating-point precision issues in comparisons.


51-52: Create a test utility for physics updates

Multiple occurrences of @ts-ignore are used to access the internal _update method. Consider creating a test utility to encapsulate this functionality.

Also applies to: 76-77, 99-100, 147-148, 173-174, 198-199, 219-220, 245-246, 291-292, 310-311


191-191: Avoid assignments within expressions

For better readability, separate the assignments from the expressions.

Also applies to: 238-238

🧰 Tools
🪛 Biome (1.9.4)

[error] 191-191: 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/physics/joint/Joint.ts Outdated Show resolved Hide resolved
tests/src/core/physics/SpringJoint.test.ts Show resolved Hide resolved
tests/src/core/physics/SpringJoint.test.ts Show resolved Hide resolved
tests/src/core/physics/Joint.test.ts Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a 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 (11)
packages/physics-physx/src/shape/PhysXBoxColliderShape.ts (1)

Line range hint 31-48: Good optimization of the scaling system.

The removal of _sizeScale in favor of direct _worldScale usage is a good architectural decision that:

  1. Reduces complexity by eliminating redundant scaling state
  2. Makes the scaling behavior more intuitive
  3. Maintains proper physics update sequence
packages/physics-physx/src/shape/PhysXCapsuleColliderShape.ts (3)

Line range hint 67-82: Consider optimizing controller updates

While the height scaling logic is correct, consider caching the controllers length to avoid repeated length property access in the loop.

-    const controllers = this._controllers;
-    for (let i = 0, n = controllers.length; i < n; i++) {
+    const controllers = this._controllers;
+    const len = controllers.length;
+    for (let i = 0; i < len; i++) {

Line range hint 120-144: Consider adding null check for controllers array

While the implementation is correct, consider adding a null/empty check for the controllers array to avoid unnecessary loop initialization.

-    const controllers = this._controllers;
-    for (let i = 0, n = controllers.length; i < n; i++) {
+    const controllers = this._controllers;
+    if (!controllers?.length) return;
+    for (let i = 0, n = controllers.length; i < n; i++) {

Line range hint 41-144: Consider extracting common scaling logic

The scaling logic is duplicated across methods. Consider extracting it into private helper methods to improve maintainability and reduce duplication.

private _getScaledRadius(): number {
  const sizeScale = this._worldScale;
  switch (this._upAxis) {
    case ColliderShapeUpAxis.X:
      return this._radius * Math.max(sizeScale.y, sizeScale.z);
    case ColliderShapeUpAxis.Y:
      return this._radius * Math.max(sizeScale.x, sizeScale.z);
    case ColliderShapeUpAxis.Z:
      return this._radius * Math.max(sizeScale.x, sizeScale.y);
  }
}

private _getScaledHeight(): number {
  const sizeScale = this._worldScale;
  switch (this._upAxis) {
    case ColliderShapeUpAxis.X:
      return this._halfHeight * sizeScale.x;
    case ColliderShapeUpAxis.Y:
      return this._halfHeight * sizeScale.y;
    case ColliderShapeUpAxis.Z:
      return this._halfHeight * sizeScale.z;
  }
}

private _updateControllers(radius: number, height: number): void {
  const controllers = this._controllers;
  if (!controllers?.length) return;
  for (let i = 0, n = controllers.length; i < n; i++) {
    const pxController = controllers.get(i)._pxController;
    if (pxController) {
      pxController.setRadius(radius);
      pxController.setHeight(height);
    }
  }
}
packages/core/src/physics/joint/HingeJoint.ts (4)

38-38: Optimize type casting in axis setter.

Consider storing the type-cast native joint in a local variable to avoid repeated casting.

-(<IHingeJoint>this._nativeJoint)?.setAxis(axis);
+const nativeJoint = this._nativeJoint as IHingeJoint;
+nativeJoint?.setAxis(axis);

74-74: Consolidate flag operations.

Both useLimits and useMotor setters follow the same pattern. Consider creating a helper method to handle flag operations.

+private _setJointFlag(flag: HingeJointFlag, value: boolean): void {
+  value ? (this._hingeFlags |= flag) : (this._hingeFlags &= ~flag);
+  const nativeJoint = this._nativeJoint as IHingeJoint;
+  nativeJoint?.setHingeJointFlag(flag, value);
+}

 set useLimits(value: boolean) {
   if (value !== this.useLimits) {
-    value ? (this._hingeFlags |= HingeJointFlag.LimitEnabled) : (this._hingeFlags &= ~HingeJointFlag.LimitEnabled);
-    (<IHingeJoint>this._nativeJoint)?.setHingeJointFlag(HingeJointFlag.LimitEnabled, value);
+    this._setJointFlag(HingeJointFlag.LimitEnabled, value);
   }
 }

Also applies to: 88-88


164-176: Optimize type casting in _syncNative.

Multiple type casts to IHingeJoint can be consolidated.

 protected override _syncNative(): void {
   super._syncNative();
+  const nativeJoint = this._nativeJoint as IHingeJoint;
   const motor = this._jointMotor;
-  (<IHingeJoint>this._nativeJoint).setAxis(this._axis);
-  (<IHingeJoint>this._nativeJoint).setHingeJointFlag(HingeJointFlag.LimitEnabled, this.useLimits);
-  (<IHingeJoint>this._nativeJoint).setHingeJointFlag(HingeJointFlag.DriveEnabled, this.useMotor);
+  nativeJoint.setAxis(this._axis);
+  nativeJoint.setHingeJointFlag(HingeJointFlag.LimitEnabled, this.useLimits);
+  nativeJoint.setHingeJointFlag(HingeJointFlag.DriveEnabled, this.useMotor);
   if (motor) {
-    (<IHingeJoint>this._nativeJoint).setDriveVelocity(motor.targetVelocity);
-    (<IHingeJoint>this._nativeJoint).setDriveForceLimit(motor.forceLimit);
-    (<IHingeJoint>this._nativeJoint).setDriveGearRatio(motor.gearRatio);
-    (<IHingeJoint>this._nativeJoint).setHingeJointFlag(HingeJointFlag.DriveFreeSpin, motor.freeSpin);
+    nativeJoint.setDriveVelocity(motor.targetVelocity);
+    nativeJoint.setDriveForceLimit(motor.forceLimit);
+    nativeJoint.setDriveGearRatio(motor.gearRatio);
+    nativeJoint.setHingeJointFlag(HingeJointFlag.DriveFreeSpin, motor.freeSpin);
   }
 }

178-198: Apply consistent type optimization pattern.

The event handlers should follow the same type optimization pattern suggested for _syncNative.

 private _onMotorChanged() {
   const motor = this._jointMotor;
   if (this._nativeJoint) {
+    const nativeJoint = this._nativeJoint as IHingeJoint;
-    (<IHingeJoint>this._nativeJoint).setDriveVelocity(motor.targetVelocity);
-    (<IHingeJoint>this._nativeJoint).setDriveForceLimit(motor.forceLimit);
-    (<IHingeJoint>this._nativeJoint).setDriveGearRatio(motor.gearRatio);
-    (<IHingeJoint>this._nativeJoint).setHingeJointFlag(HingeJointFlag.DriveFreeSpin, motor.freeSpin);
+    nativeJoint.setDriveVelocity(motor.targetVelocity);
+    nativeJoint.setDriveForceLimit(motor.forceLimit);
+    nativeJoint.setDriveGearRatio(motor.gearRatio);
+    nativeJoint.setHingeJointFlag(HingeJointFlag.DriveFreeSpin, motor.freeSpin);
   }
 }
packages/core/src/physics/joint/Joint.ts (3)

15-17: Consider thread safety implications of static temporary vector

The static _tempVector3 could lead to race conditions if multiple joint calculations occur concurrently. Consider making this an instance variable or using a vector pool.


252-264: Improve documentation for transform change handlers

The transform change handlers would benefit from more detailed documentation explaining:

  • When these handlers are triggered
  • The impact of different transform flags
  • The relationship between transform changes and anchor updates
+/**
+ * Handles changes in the entity's transform.
+ * @param type - The flags indicating which transform properties changed
+ * @remarks Only responds to world scale changes to update the actual anchor position
+ */
 @ignoreClone
 private _onSelfTransformChanged(type: TransformModifyFlags): void {

291-294: Add documentation for AnchorOwner enum values

The AnchorOwner enum values would benefit from detailed documentation explaining their purpose and usage.

 enum AnchorOwner {
+  /** Indicates the anchor belongs to the joint's entity */
   Self = 0x1,
+  /** Indicates the anchor belongs to the connected entity */
   Connected = 0x2,
+  /** Indicates both anchors are involved */
   Both = 0x3
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d3fb9bb and c768cc9.

📒 Files selected for processing (10)
  • packages/core/src/physics/joint/HingeJoint.ts (6 hunks)
  • packages/core/src/physics/joint/Joint.ts (7 hunks)
  • packages/core/src/physics/shape/BoxColliderShape.ts (2 hunks)
  • packages/core/src/physics/shape/ColliderShape.ts (4 hunks)
  • packages/design/src/physics/joints/ISpringJoint.ts (0 hunks)
  • packages/physics-physx/src/joint/PhysXHingeJoint.ts (2 hunks)
  • packages/physics-physx/src/joint/PhysXSpringJoint.ts (0 hunks)
  • packages/physics-physx/src/shape/PhysXBoxColliderShape.ts (2 hunks)
  • packages/physics-physx/src/shape/PhysXCapsuleColliderShape.ts (3 hunks)
  • packages/physics-physx/src/shape/PhysXColliderShape.ts (3 hunks)
💤 Files with no reviewable changes (2)
  • packages/design/src/physics/joints/ISpringJoint.ts
  • packages/physics-physx/src/joint/PhysXSpringJoint.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/core/src/physics/shape/BoxColliderShape.ts
  • packages/physics-physx/src/joint/PhysXHingeJoint.ts
🔇 Additional comments (17)
packages/physics-physx/src/shape/PhysXBoxColliderShape.ts (2)

31-34: LGTM! Simplified scaling logic looks correct.

The change to use _worldScale directly instead of _sizeScale simplifies the scaling logic while maintaining correct physics behavior. The geometry update sequence is properly maintained.

Let's verify that this change doesn't affect existing physics calculations:

✅ Verification successful

Based on the gathered information, I can now provide the final response:

Verified: Scale simplification is safe and correctly implemented

The change to use _worldScale directly instead of _sizeScale is correct because:

  • The base PhysXColliderShape class properly manages _worldScale and handles scale updates for both physics shapes and controllers
  • No other physics components were depending on _sizeScale (no references found in the codebase)
  • The implementation follows the IBoxColliderShape interface contract
  • The scaling logic maintains the proper update sequence: updating geometry, shape, and notifying controllers
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for tests that verify box collider scaling behavior
rg -l "BoxColliderShape.*scale" --type ts

Length of output: 43


Script:

#!/bin/bash
# Let's try a broader search for box collider tests and scaling-related code
rg -l "BoxCollider" --type ts

# Also search for any direct usage of PhysXBoxColliderShape
rg -l "PhysXBoxColliderShape" --type ts

# Look for test files specifically
fd "test.*\.ts$|spec.*\.ts$" 

Length of output: 4753


Script:

#!/bin/bash
# Let's examine the ColliderShape.test.ts and related test files for scaling tests
rg -A 5 "BoxCollider.*scale|setWorldScale" tests/src/core/physics/ColliderShape.test.ts tests/src/core/physics/CharacterController.test.ts

# Also check the interface definition to understand the expected behavior
cat packages/design/src/physics/shape/IBoxColliderShape.ts

# Look for any other usage of setWorldScale in physics components
rg "setWorldScale" packages/physics-physx/src/

Length of output: 1695


Script:

#!/bin/bash
# Let's check the base class implementation and interface
cat packages/physics-physx/src/shape/PhysXColliderShape.ts

# Also check if there are any direct references to _sizeScale in other files
rg "_sizeScale" packages/physics-physx/src/

Length of output: 5091


Line range hint 44-48: Consider handling negative scale values.

While the scaling logic is correct, it might be worth validating or documenting behavior with negative scale values, as they could affect physics calculations in unexpected ways.

Let's check if there are any existing scale value validations:

packages/core/src/physics/shape/ColliderShape.ts (7)

5-5: LGTM: Improved cloning strategy for position and rotation.

The switch from @ignoreClone to @deepClone for position and rotation properties ensures proper deep cloning of these essential transform properties.

Also applies to: 27-29


61-61: LGTM: Proper validation for contact offset.

The use of Math.max(0, value) ensures the contact offset remains non-negative, which is essential for physics calculations.


132-133: Add test coverage for physics object tracking.

While the implementation of object tracking in Engine._physicalObjectsMap is correct, test coverage is needed to verify the lifecycle management.


140-140: LGTM: Improved cloning implementation.

The use of _syncNative centralizes the synchronization logic, making the code more maintainable and less prone to errors.


148-149: LGTM: Proper cleanup implementation.

The destroy method now properly handles cleanup by:

  1. Nullifying the native shape reference
  2. Removing the instance from the physics object tracking map

162-171: LGTM: Proper change notification implementation.

The addition of _handleShapesChanged notifications in position and rotation setters ensures proper physics updates when transforms change.


152-160: 🛠️ Refactor suggestion

Add error handling and remove redundant notification.

Two issues to address:

  1. Add null checks and error handling for native shape operations
  2. The _handleShapesChanged call might be redundant as it's already called in _setPosition and _setRotation
packages/physics-physx/src/shape/PhysXColliderShape.ts (2)

86-86: LGTM! Good improvement in scale handling

The use of Math.abs ensures that scale values are always positive, which is crucial for proper physics simulation.


100-107: ⚠️ Potential issue

Enhance error handling and parameter validation

The method needs several improvements:

  1. Fix the property name usage from _contractOffset to _contactOffset
  2. Add parameter validation
  3. Add null checks for _pxShape
 setContactOffset(offset: number): void {
+   if (typeof offset !== 'number' || isNaN(offset) || offset < 0) {
+     throw new Error('Contact offset must be a non-negative number');
+   }
-   this._contractOffset = offset;
+   this._contactOffset = offset;
    const controllers = this._controllers;
    if (controllers.length) {
      for (let i = 0, n = controllers.length; i < n; i++) {
        controllers.get(i)._pxController?.setContactOffset(offset);
      }
    } else {
+     if (!this._pxShape) {
+       throw new Error('PhysX shape is not initialized');
+     }
      this._pxShape.setContactOffset(offset);
    }
  }
packages/physics-physx/src/shape/PhysXCapsuleColliderShape.ts (1)

Line range hint 41-57: LGTM: Correct scaling logic for radius calculation

The implementation correctly handles radius scaling based on the up axis orientation, using the maximum scale of the appropriate dimensions to maintain the capsule's circular cross-section.

packages/core/src/physics/joint/HingeJoint.ts (2)

151-156: Verify thread safety in _onDisableInScene.

The method reads values from the native joint before disabling. Ensure there are no race conditions if the joint is modified concurrently.


9-25: Verify deep cloning behavior for joint properties.

The switch from @ignoreClone to @deepClone for _axis, _jointMotor, and _limits properties implies these objects will be deeply cloned. Ensure that deep cloning these properties doesn't break any references or cause unexpected behavior in the physics simulation.

✅ Verification successful

Based on the gathered information, I can now provide the final response:

The switch to @deepClone decorator is appropriate and consistent with the codebase patterns

The change from @ignoreClone to @deepClone for _axis, _jointMotor, and _limits properties is correct because:

  1. The HingeJoint class needs to maintain independent copies of these properties for each instance
  2. These properties are mutable objects (Vector3, JointMotor, JointLimits) that affect the physics simulation state
  3. Other joint classes in the codebase (SpringJoint, FixedJoint) follow similar patterns for their properties
  4. The codebase's test suite includes extensive clone-related tests for various components, showing that deep cloning is a well-established pattern for component state management
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other joint classes using these decorators
rg "@(ignore|deep)Clone.*_(axis|jointMotor|limits)" --type ts

# Check for any clone-related tests
rg "clone.*HingeJoint" --type test

Length of output: 129


Script:

#!/bin/bash
# Search for other joint classes using these decorators with broader context
rg "@(ignore|deep)Clone.*_" --type ts -B 1 -A 1

# Check for clone-related tests with correct file extension
rg "clone.*HingeJoint" -g "*.ts" -g "*.js"

# Look for clone manager usage patterns
ast-grep --pattern 'class $_ extends Joint {
  $$$
  @deepClone
  $_
  $$$
}'

Length of output: 14920


Script:

#!/bin/bash
# Search for other joint classes to understand the decorator pattern
rg "class \w+Joint extends Joint" --type ts -B 1 -A 5

# Look for JointMotor and JointLimits class definitions
ast-grep --pattern 'class $_ {
  $$$
  _updateFlagManager
  $$$
}'

# Search for clone-related tests in the test directory
fd -e test.ts -e spec.ts -x rg "clone" {}

Length of output: 21748

packages/core/src/physics/joint/Joint.ts (3)

186-193: Remove @ts-ignore by properly typing event handlers

The constructor contains multiple @ts-ignore comments which bypass TypeScript's type checking.


199-210: Add error handling for native joint lifecycle

The lifecycle methods should include error handling for native operations to prevent crashes.


232-250: Add validation for vector calculations

The vector calculations should include validation for edge cases.

Comment on lines +31 to +32
/** @internal */
_contractOffset: number = 0.02;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix property name typo: _contractOffset_contactOffset

The property name appears to be misspelled. It should be _contactOffset to maintain consistency with the setContactOffset method and accurately represent its purpose.

-  _contractOffset: number = 0.02;
+  _contactOffset: number = 0.02;
📝 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.

Suggested change
/** @internal */
_contractOffset: number = 0.02;
/** @internal */
_contactOffset: number = 0.02;

Comment on lines +91 to +97
get automaticConnectedAnchor(): boolean {
return this._automaticConnectedAnchor;
}

set automaticConnectedAnchor(value: boolean) {
this._automaticConnectedAnchor = value;
value && this._calculateConnectedAnchor();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation for automaticConnectedAnchor changes

The setter for automaticConnectedAnchor should validate the current state before applying changes. Consider checking if:

  1. The joint is properly initialized
  2. The connected collider exists (if required)
 set automaticConnectedAnchor(value: boolean) {
+    if (this._automaticConnectedAnchor === value) {
+      return;
+    }
     this._automaticConnectedAnchor = value;
-    value && this._calculateConnectedAnchor();
+    if (value) {
+      try {
+        this._calculateConnectedAnchor();
+      } catch (error) {
+        console.error("Failed to calculate connected anchor:", error);
+        this._automaticConnectedAnchor = 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.

Suggested change
get automaticConnectedAnchor(): boolean {
return this._automaticConnectedAnchor;
}
set automaticConnectedAnchor(value: boolean) {
this._automaticConnectedAnchor = value;
value && this._calculateConnectedAnchor();
get automaticConnectedAnchor(): boolean {
return this._automaticConnectedAnchor;
}
set automaticConnectedAnchor(value: boolean) {
if (this._automaticConnectedAnchor === value) {
return;
}
this._automaticConnectedAnchor = value;
if (value) {
try {
this._calculateConnectedAnchor();
} catch (error) {
console.error("Failed to calculate connected anchor:", error);
this._automaticConnectedAnchor = false;
}
}
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (4)
packages/core/src/physics/DynamicCollider.ts (4)

24-24: Document the rationale for the specific maxDepenetrationVelocity value

The value 1.0000000331813535e32 seems unusually specific. Please add a comment explaining why this particular value was chosen.


107-113: Document performance implications of automatic calculations

The automatic calculation features for mass, center of mass, and inertia tensor may have performance implications when shapes are frequently modified. Consider adding documentation about:

  • Performance characteristics
  • Best practices for when to use automatic vs. manual calculations
  • Impact on physics simulation stability

Also applies to: 115-128, 151-166


316-316: Document the _phasedActiveInScene check

The addition of the _phasedActiveInScene check is good for performance, but consider adding a comment explaining:

  • When a collider is considered "phased active"
  • The implications of skipping physics operations when inactive

Also applies to: 324-324, 347-347


357-363: Enhance isSleeping documentation

The isSleeping method's documentation should explain:

  • Its relationship with sleepThreshold
  • When and why a collider might go to sleep
  • Impact on performance and physics simulation
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c768cc9 and 1672ab8.

📒 Files selected for processing (2)
  • packages/core/src/physics/CharacterController.ts (3 hunks)
  • packages/core/src/physics/DynamicCollider.ts (9 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/core/src/physics/CharacterController.ts

[error] 18-18: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)

🔇 Additional comments (8)
packages/core/src/physics/CharacterController.ts (4)

18-18: Use Math.SQRT1_2 instead of magic number

Replace the approximate value 0.707 with Math.SQRT1_2 (≈0.7071067811865476) from the standard library for better precision and maintainability.

- private _slopeLimit = 0.707;
+ private _slopeLimit = Math.SQRT1_2;
🧰 Tools
🪛 Biome (1.9.4)

[error] 18-18: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)


Line range hint 21-33: LGTM! Improved property validation and documentation

Good improvements:

  • Added validation to ensure stepOffset is non-negative
  • Clear documentation explaining the relationship between stepOffset and obstacle height
  • Proper synchronization with native collider
🧰 Tools
🪛 Biome (1.9.4)

[error] 18-18: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)


Line range hint 108-116: LGTM! Proper shape constraint enforcement

Good implementation of the single shape constraint with clear error messaging.

🧰 Tools
🪛 Biome (1.9.4)

[error] 18-18: Prefer constants from the standard library.

Unsafe fix: Use Math.SQRT1_2 instead.

(lint/suspicious/noApproximativeNumericConstant)


157-162: ⚠️ Potential issue

Add missing contactOffset synchronization

The _syncNative method should also synchronize the contactOffset property with the native collider.

 protected override _syncNative(): void {
   super._syncNative();
   (<ICharacterController>this._nativeCollider).setStepOffset(this._stepOffset);
   (<ICharacterController>this._nativeCollider).setNonWalkableMode(this._nonWalkableMode);
   (<ICharacterController>this._nativeCollider).setUpDirection(this._upDirection);
   (<ICharacterController>this._nativeCollider).setSlopeLimit(this._slopeLimit);
+  (<ICharacterController>this._nativeCollider).setContactOffset(this._contactOffset);
 }

Likely invalid or redundant comment.

packages/core/src/physics/DynamicCollider.ts (4)

65-69: Add error handling for native getter calls

This is a duplicate of a previous review comment regarding the need for error handling in native getter calls.

Also applies to: 83-87


402-422: Add error handling in _syncNative

This is a duplicate of a previous review comment regarding the need for error handling in the _syncNative method.


424-429: ⚠️ Potential issue

Add @ignoreClone to _setMassAndUpdateInertia

The _setMassAndUpdateInertia method interacts with native code and should be decorated with @ignoreClone to prevent issues during cloning.

+  @ignoreClone
   private _setMassAndUpdateInertia(): void {

Likely invalid or redundant comment.


12-13: Verify clone behavior for physics properties

The removal of @ignoreClone from several properties might lead to unexpected behavior during object cloning, as physics properties often need special handling.

Also applies to: 18-18, 23-26, 29-31

✅ Verification successful

Let's examine the test files to understand how cloning is handled.


Let's check the actual clone implementation in DynamicCollider to see how these properties are handled.


Let's try a different approach to find the clone implementation.


Based on the evidence gathered, I can now provide the final response:

Removal of @ignoreClone is intentional and safe

The removal of @ignoreClone from the physics properties is correct because:

  1. DynamicCollider implements proper clone behavior through _cloneTo() method which explicitly copies all physics properties (linearVelocity, angularVelocity, centerOfMass, inertiaTensor)
  2. The test suite (Collider.test.ts and ColliderShape.test.ts) verifies correct cloning behavior for various collider configurations
  3. Only internal implementation details (like _index) retain the @ignoreClone decorator in the parent class
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for clone-related tests to verify proper handling
rg -l "clone.*DynamicCollider" --type ts

Length of output: 172


Script:

#!/bin/bash
# Check clone-related tests for DynamicCollider
rg "clone.*DynamicCollider" -A 10 -B 2 tests/src/core/physics/Collider.test.ts tests/src/core/physics/ColliderShape.test.ts

Length of output: 3641


Script:

#!/bin/bash
# Check clone implementation in DynamicCollider
ast-grep --pattern 'clone() {
  $$$
}'  packages/core/src/physics/DynamicCollider.ts

Length of output: 85


Script:

#!/bin/bash
# Search for clone-related code in DynamicCollider
rg "clone" -A 10 -B 5 packages/core/src/physics/DynamicCollider.ts

# Also check the parent class implementation
rg "clone" -A 10 -B 5 packages/core/src/physics/Collider.ts

Length of output: 2137

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (1)
tests/src/core/physics/Collider.test.ts (1)

49-64: Add JSDoc documentation and improve type safety.

The utility functions would benefit from:

  1. JSDoc documentation explaining their purpose and parameters
  2. Return type annotations for better type safety
+/**
+ * Creates a box entity with physics collider
+ * @param cubeSize - The size of the box
+ * @param type - The type of collider to add
+ * @param pos - The position to place the box
+ * @returns The created box entity
+ */
-function addBox(cubeSize: Vector3, type: typeof DynamicCollider | typeof StaticCollider, pos: Vector3) {
+function addBox(cubeSize: Vector3, type: typeof DynamicCollider | typeof StaticCollider, pos: Vector3): Entity {

+/**
+ * Formats a number to 5 decimal places for consistent assertions
+ * @param value - The number to format
+ * @returns The formatted number
+ */
-function formatValue(value: number) {
+function formatValue(value: number): number {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1672ab8 and f8dc9c4.

📒 Files selected for processing (2)
  • packages/core/src/physics/PhysicsMaterial.ts (2 hunks)
  • tests/src/core/physics/Collider.test.ts (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/physics/PhysicsMaterial.ts
🔇 Additional comments (3)
tests/src/core/physics/Collider.test.ts (3)

Line range hint 68-90: LGTM! Setup modifications are well-structured.

The changes to the setup hooks improve the test environment by:

  1. Streamlining root entity assignment
  2. Adding proper physics material properties

341-363: LGTM! Comprehensive testing of inactive and destroy scenarios.

The tests thoroughly verify:

  1. Shape management in inactive state
  2. Proper cleanup on destruction
  3. Component state after destruction

287-339: 🛠️ Refactor suggestion

Reduce duplication in clone tests.

The clone tests share similar setup code and assertions. Consider extracting common logic into helper functions.

interface ColliderTestSetup {
  box: Entity;
  collider: DynamicCollider;
}

function setupColliderTest(position: Vector3): ColliderTestSetup {
  const box = addBox(new Vector3(1, 1, 1), DynamicCollider, position);
  const collider = box.getComponent(DynamicCollider);
  collider.automaticInertiaTensor = false;
  collider.inertiaTensor.set(10000000, 10000000, 10000000);
  collider.applyForce(new Vector3(1000, 0, 0));
  collider.shapes[0].material.dynamicFriction = 1;
  return { box, collider };
}

Comment on lines +114 to +119
// @ts-ignore
engine.sceneManager.activeScene.physics._update(1);

expect(collisionScript.onCollisionEnter.mock.calls.length).toBeGreaterThan(1);
expect(collisionScript.onCollisionStay.mock.calls.length).toBeGreaterThan(1);
expect(collisionScript.onCollisionExit.mock.calls.length).toBe(1);
expect(collisionScript.onCollisionExit.mock.calls.length).eq(1);
Copy link

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 test utility for physics updates.

The repeated @ts-ignore comments and magic numbers could be encapsulated in a utility function.

+/**
+ * Updates physics simulation for testing
+ * @param engine - The game engine instance
+ * @param deltaTime - Time step in milliseconds
+ */
+function updatePhysics(engine: WebGLEngine, deltaTime: number): void {
+  // @ts-ignore - Accessing internal method for testing
+  engine.sceneManager.activeScene.physics._update(deltaTime);
+}

-// @ts-ignore
-engine.sceneManager.activeScene.physics._update(1);
+updatePhysics(engine, 1);

Also, consider adding constants for the time steps:

const PHYSICS_TIME_STEPS = {
  COLLISION_CHECK: 1,
  TRIGGER_CHECK: 2,
  KINEMATIC_MOVE: 16
} as const;

Also applies to: 132-137, 151-157, 212-214, 254-256

@GuoLei1990 GuoLei1990 merged commit 785455c into galacean:dev/1.4 Nov 28, 2024
8 of 9 checks passed
@GuoLei1990 GuoLei1990 added enhancement New feature or request and removed bug Something isn't working labels Nov 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants