Private class members

Hi,
Whilst working on my codebase I have sometimes needed to adapt some BabylonJS class methods to my (very specific) needs.
Sometimes this has meant adjusting private class methods by forcing my own methods in place of the default methods often involving casting to “any”.

The downside of this is that sometimes when I upgrade to a new version of BabylonJS, typescript does not pick up breaking changes and I end up with bugs I need to fix.

I would much rather derive my own classes and override certain protected methods to handle my needs. This way, if a base class changes, I will see it in the compilation.

Is it possible to make more (all?) methods protected rather than private? I realise this is not strictly correct programming practice, but it would be really helpful to me.

I believe the easiest way is to have your own fork of Babylon where you can do whatever you want with all the source code.

I expected something like that as the answer :smile:, but it would be overkill for me at this point. I’ll keep doing what I currently do for now.

Hey, it’s absolutely not an overkill:

https://doc.babylonjs.com/contribute/toBabylon/HowToContribute#link-a-public-project-to-an-external-one

Should be as easy as (not tested):

  1. Fork Babylon.js on GitHub
  2. Clone forked repo
  3. cd Babylon.js
  4. npm i
  5. npm run prepublishOnly -w @babylonjs/core
  6. cd you-project-dir-root
  7. npm link @babylonjs/core
  8. you can import whatever from @babylonjs/core

Read the whole article here:

https://doc.babylonjs.com/contribute/toBabylon/HowToContribute

2 Likes

I see your point :+1:
I would need to publish it to npm so that my project’s github docker build works correctly and also pull in and changes when the main BabylonJS updates…

I’ll give it some thought

Just for reference, the main method I am currently looking at changing is scene._evaluateActiveMeshes - I believe it could be optimised for my specific needs, and it would likely to be useful to other users to optimise too.

Maybe just make that one protected for now? :stuck_out_tongue_winking_eye:

Another solution:

In this case you can override the types provided by babylon.js by your own so you don’t have to cast to any. Typescript will use your types you just need to add the dir containing your type declarations d.ts files to tsconfig.json:

"typeRoots": [
 "./node_modules/@types",
 "types",
], 

This will search for types in src/types as well.

Breaking changes? I believe there should be no breaking changes only when it’s absolutely needed.

No.

1 Like

Breaking changes? I believe there should be no breaking changes only when it’s absolutely needed.

Not in the public interface, but as I’m changing things that are private or protected, it is expected that things will change and break what I’m doing. No way around that one if I’m poking around casting to any

That’s why are those functions private :slight_smile: so you don’t break the core stuff which is used by a lot of other functions…

What do you exactly need to achieve? Maybe it can be programmed an other way which will not need to mess with hacking the private functions of a class.

@roland - currently I’m looking at a few optimisations. I have a few categories of mesh in my scene and I’d like to optimise it by making some of them permanently active so they are completely ignored within the scene._evaluateActiveMeshes.
Other meshes only need to be partly ignored - they need to be in the active list but as they are skeletal I need to do some updates per frame. Finally, there are some meshes that need to be updated each frame.

I think I have figured out a way to do what I need by disabling _evaluateActiveMeshes by calling freezeActiveMeshes and then using the onBeforeCameraRenderObservable to perform my own evaluation. So although I would prefer overriding _evaluateActiveMeshes in my own version of a derived scene class and using the override keyword to protect myself against changes, I can still do what I wanted. Though a number of members within scene.ts that I will need to access may be private so I’ll end up casting to any to access them.

In the past I added & changed functionality within the skeleton and bone classes - again very specific to me, so there would be no desire to put it within BabylonJS, but I got hit by the recent skeleton cleanups/improvements, and everything stopped animating and I didn’t have any typescript warnings to help me.

Similar for loading files…

I do realise that all the above issues are my own fault - no worries :wink:

I had a look at the scene._evaluateActiveMeshes() and seems that your approach by freezing the meshes and use the onBeforeCameraRenderObservable or onBeforeActiveMeshesEvaluationObservable is a good place to do your evaluation.

When I got into a situation where I must cast to any to access any function or property I rather use
https://lodash.com/docs/#get

and

https://lodash.com/docs/#set

It looks nicer :slight_smile:

You can improve the readibility of your code by exposing public/protected class methods which are doing the dirty job of setting/getting using lodash. Your class methods will be reusable and you get full IntelliSense support in VS Code:

class SceneX extends BABYLON.Scene {
    setActiveMeshes(meshes: BABYLON.AbstractMesh[]) { 
        set(this, "_activeMeshes", meshes)
    }
   // add more here
}

const scene = new SceneX(engine);
scene.setActiveMeshes(arrayOfMeshes)
2 Likes

Here are some design patterns that can be used to add additional functionalities to a function:

  1. Decorator Pattern: This pattern allows adding new functionalities to a class dynamically by creating wrapper classes around the original class.
  2. Chain of Responsibility Pattern: With this pattern, multiple objects (handlers) can handle a request sequentially. Each object decides whether it can handle the request itself or passes it on to the next object in the chain.
  3. Strategy Pattern: Algorithms or behaviors can be encapsulated into separate classes. These can then be swapped out at runtime to change the behavior of a function.
  4. Composite Pattern: This pattern allows treating groups of objects as a single object. This enables managing multiple functions as a unit.
  5. Observer Pattern: If changes in a function need to be observed, the Observer pattern can be used. Observers are registered to be notified of changes and can react accordingly.
2 Likes

But unfortunatelly there is no valid/clear solution to override a private member whatever approach you use.

2 Likes