Skip to content

Support Tiled 1.12 capsule shape, refactor RoundRect and TMXObject#1326

Merged
obiot merged 1 commit intomasterfrom
feature/capsule-object
Mar 28, 2026
Merged

Support Tiled 1.12 capsule shape, refactor RoundRect and TMXObject#1326
obiot merged 1 commit intomasterfrom
feature/capsule-object

Conversation

@obiot
Copy link
Copy Markdown
Member

@obiot obiot commented Mar 28, 2026

Summary

RoundRect — extends Polygon directly

  • Now extends Polygon instead of Rect for proper SAT collision support (previously treated as a plain rectangle)
  • Generates polygon vertices approximating rounded corners (8 segments per corner arc)
  • Precomputed cos/sin lookup table shared across all instances
  • Reuses existing Vector2d instances when vertex count is unchanged (size/radius changes)
  • Own width/height/left/right/top/bottom/centerX/centerY getters
  • Rendering unchanged — renderer still uses native context.roundRect() via shape.type === "RoundRect"

TMXObject — refactored shape detection

  • New detectShape() function returns a string type instead of setting multiple boolean flags
  • Single shapeType property replaces scattered is* flags (booleans still set for backward compatibility)
  • parseTMXShapes() refactored from nested if/else to clean switch statement
  • Capsule support: creates RoundRect with radius = min(width, height) / 2

SAT collision

  • Added RoundRect* and Rectangle* entries to SAT_LOOKUP table in detector.js

Capsule object shape (Tiled 1.12)

  • XML: <capsule/> marker on <object> (same pattern as <ellipse/>)
  • JSON: "capsule": true boolean property

Test plan

  • All 1823 tests pass (74 new tests across 3 files)
  • Platformer example works correctly
  • Capsule collision shape visually confirmed with debug plugin
  • RoundRect tests (43 new): properties/getters, contains edge cases (corners, edges, center, Vector2d overload, radius=0, max radius), containsRectangle, copy/clone/equality, polygon approximation (convexity, vertices, clamping, capsule shapes), vertex reuse optimization (same array/instance on resize, reallocate on 0↔>0 transition, stability after multiple changes, bounds correctness, contains after reuse, small/large dimensions, radius clamping on shrink)
  • TMXObject tests (26 new): shape detection (all 7 types including XML capsule {}), parseTMXShapes (all shapes: rectangle, ellipse, capsule, point, convex polygon, concave polygon → triangles, polyline), rotation on all applicable shapes, basic properties (position, rotation, class/type, opacity, visible)
  • TMXUtils capsule parsing tests (4 new): XML <capsule/>, with rotation, not on rectangle, not on ellipse

🤖 Generated with Claude Code

…ject

RoundRect:
- Extend Polygon directly instead of Rect for proper SAT collision
- Generate polygon vertices approximating rounded corners (8 segments/corner)
- Precompute cos/sin lookup table, reuse vertex objects on size/radius changes
- Own width/height/left/right/top/bottom/centerX/centerY getters

TMXObject:
- Refactor shape detection into detectShape() returning a string type
- Replace multiple is* boolean flags with single shapeType property
  (booleans still set for backward compatibility)
- Refactor parseTMXShapes() from nested if/else to clean switch
- Add capsule case creating RoundRect with radius = min(w,h)/2

SAT collision:
- Add RoundRect and Rectangle entries to SAT_LOOKUP table in detector.js

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 28, 2026 01:28
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds support for Tiled 1.12 “capsule” object shapes by representing them as polygon-approximated RoundRect shapes, and updates collision detection dispatch to recognize RoundRect/Rectangle type combinations.

Changes:

  • Refactors RoundRect to extend Polygon and generate vertices approximating rounded corners (enabling SAT-based collision).
  • Refactors TMXObject shape detection/parsing to use a shapeType string and adds capsule parsing (capsule → RoundRect with radius = min(w,h)/2).
  • Extends the SAT dispatch lookup to handle RoundRect* and Rectangle* shape type pairings; adds/extends tests for capsule parsing and RoundRect behavior.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/melonjs/src/geometries/roundrect.ts Reimplements RoundRect as a Polygon with rounded-corner vertex approximation and updated geometry API.
packages/melonjs/src/level/tiled/TMXObject.js Refactors shape detection/parsing and adds capsule → RoundRect shape creation.
packages/melonjs/src/physics/detector.js Adds SAT dispatch entries for RoundRect/Rectangle type combinations.
packages/melonjs/tests/roundrect.spec.ts Adds extensive RoundRect tests (getters, contains, vertex generation/reuse).
packages/melonjs/tests/tmxobject.spec.js Adds new TMXObject tests for shape detection and shape parsing (including capsule).
packages/melonjs/tests/tmxutils.spec.js Adds XML capsule marker parsing tests (<capsule/>).
packages/melonjs/CHANGELOG.md Documents capsule support and RoundRect collision behavior change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +159 to +173
set width(value) {
this._width = value;
this._updateVertices();
}

/**
* height of the RoundRect
*/
get height() {
return this._height;
}
set height(value) {
this._height = value;
this._updateVertices();
}
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

width/height setters call _updateVertices() without re-clamping radius to the new dimensions. Since updateRoundRectVertices assumes the radius is already clamped, shrinking the shape via these setters can leave _radius > min(width,height)/2, producing invalid vertices/bounds and incorrect contains() results. Consider routing these setters through setSize() (or reapplying this.radius = this._radius after updating _width/_height) so radius is always clamped on dimension changes.

Copilot uses AI. Check for mistakes.
Comment on lines 221 to +234
get radius() {
return this._radius;
}
set radius(value) {
// verify the rectangle is at least as wide and tall as the rounded corners.
if (this.width < 2 * value) {
value = this.width / 2;
// clamp radius to half the shorter side
if (this._width < 2 * value) {
value = this._width / 2;
}
if (this.height < 2 * value) {
value = this.height / 2;
if (this._height < 2 * value) {
value = this._height / 2;
}
this._radius = value;
this._updateVertices();
}
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

radius can be set to a negative number and stored in _radius (no lower-bound clamp). This violates the stated “corner radius” semantics and also breaks the assumption that the value passed into updateRoundRectVertices is a clamped radius. Suggest clamping to >= 0 in the setter (e.g., value = Math.max(0, value)) before the size-based clamping.

Copilot uses AI. Check for mistakes.
Comment on lines +359 to +370
/**
* Returns true if the rounded rectangle contains the given rectangle
* @param rectangle - rectangle to test
* @returns true if contained
*/
containsRectangle(rectangle: RoundRect) {
return (
rectangle.left >= this.left &&
rectangle.right <= this.right &&
rectangle.top >= this.top &&
rectangle.bottom <= this.bottom
);
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

containsRectangle is typed as containsRectangle(rectangle: RoundRect). Since RoundRect previously inherited Rect.containsRectangle(rectangle: Rect) (when it extended Rect), this narrows the TypeScript API and forces callers to cast when they have a Rect. Consider accepting Rect (or a structural type with left/right/top/bottom) to preserve the prior API surface.

Copilot uses AI. Check for mistakes.
@obiot obiot merged commit 7e50927 into master Mar 28, 2026
10 checks passed
@obiot obiot deleted the feature/capsule-object branch April 11, 2026 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants