planck.js icon indicating copy to clipboard operation
planck.js copied to clipboard

allow custom body classes

Open keasy9 opened this issue 3 months ago • 8 comments

For now there is nice api with world.createBody(), but there is no typed way to create custom body classes and add it to the world. When I say typed I mean that there no public addBody method, which has described definition in typescript, while actualy world has method _addBody.

Why do I need this? I wanna add validation to body.setUserData() to improve type hinting and code consistency. Most convenient way is to make a class whick extends Body and redeclare setUserData method.

keasy9 avatar Sep 26 '25 14:09 keasy9

By validation do you mean type-safety, or actualling validating data? For just type-safety, could you use something like this:

declare module "planck" {
  interface Body {
    setUserData(data: MyDataType): void;
    getUserData(): MyDataType;
  }
}

shakiba avatar Sep 27 '25 15:09 shakiba

Yes, when I need only type safety this is good way, but sometimes I need actualy validating data, or for example have default value:

public getUserData(): MyDataType {
    const userData = super.getUserData();
    if (!userData) {
        this.setUserData({});
    }
    return super.getUserData() as MyDataType;
}

Another case is when i have complex data and wanna have shortcuts to its parts:

public setSprite(sprite?: Sprite): this {
    this.getUserData().sprite = sprite;
    return this;
}

public getSprite(): Sprite|undefined {
    return this.getUserData().sprite;
}

keasy9 avatar Sep 28 '25 02:09 keasy9

Another advantage of custom classes is that you can create your own interface to keep code-constistency of project, e.g.:

body.fixture() // custom method, that returns builder instance with method chaining
        .fromSprite(sprite)
        .appendToBody();

keasy9 avatar Sep 28 '25 02:09 keasy9

How about building your game data model and classes in plain JS, and then use planck to move physical objects?

Like this example: https://github.com/piqnt/polymatic-example-watermelon/blob/main/src/Physics.ts

shakiba avatar Sep 28 '25 06:09 shakiba

No, I havn't, but how it solves cusomisation problem? When I need custom Body, Fixture or other class there is only dirty ways like using private non-documented methods or modifying prototypes. Why not add clearly way if it anyway possible via dirty ways?

keasy9 avatar Sep 30 '25 13:09 keasy9

In the examples you provided above you are using inheritance to use physics classes. I recommend composition over inheritance. That is:

  • design game classes however you need
  • create corresponding physics object for each game object
  • keep a reference to physics object in your game object
  • use userData to keep a reference from physics objects to game objects

With composition you can also add other things to your game classes.

I'm already considering improving addBody and making it public in next major release, but I still do not recommend customizing physics classes with non-physics code.

shakiba avatar Sep 30 '25 14:09 shakiba

I still do not recommend customizing physics classes with non-physics code.

I understand that and understand why, and agreed with you. I am actually doing composition in my projects, e. g. Player have property body, but sometimes I need custom methods with physics (but not with game logic), like

// Player.ts, contructor of the class
...
this.body.fixture() // returns custom builder-class that collects data in a way that is convenient for me
    .dencity(.5)
    .rigidity(.3)
    .circle(5)
    .appendToBody(); // and creates fixture here, actually body.createFixture() under the hood

I'm already considering improving addBody and making it public in next major release

That is what I wanted to hear, thank you! Should I close this issue now? (never opened issues before, idk lol)

keasy9 avatar Oct 01 '25 12:10 keasy9

Sounds great! Meanwhile, just for builder what about creating a function that returns a wrapper around fixture definition:

fixtureBuilder() .density(.5) .rigidity(.3) .circle(5) .appendToBody(body);

shakiba avatar Oct 01 '25 13:10 shakiba