Help with a class scoping issue and 2D gui in 3D space (VR)

So, I have a “treasure object” which is a cube with a 2D gui on it in 3D space (VR).

When I use this object in the create scene (not as a class), it works perfectly.

However, when I use it as a class and instantiate it, when I click the button with my pointer, no matter what I do, somehow the code cannot reach the material of the mesh.

Here is my code (it’s part of a huge codebase, so I can’t section it out into a playground):

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      class Treasure {
        constructor(x, y, z){
          this.pos = new BABYLON.Vector3(x, y, z);
          this.body = BABYLON.MeshBuilder.CreateBox(
          "boxGUI",
          { width: 3, height: 3, depth: 3 },
          scene
          );
          this.body.position = this.pos;
          //is this necessary for solving my problem (?)
          //this.material = new BABYLON.StandardMaterial("myMaterial", scene);
          //this.diffuseColor = new BABYLON.Color3(0, 0, 0);
          //this.body.material=this.material;
          /////////
          this.texture = BABYLON.GUI.AdvancedDynamicTexture.CreateForMesh(
          this.body
          );
        function genText(){
          //26590 = length of chars of textC
          var start = Math.floor((Math.random()*25500));          
          var randomText = txtC.substring(start, start+1000);
          return randomText;
        }
        this.text = genText();
        this.button = BABYLON.GUI.Button.CreateSimpleButton("but1", this.text);
        this.button.width = 1;
        this.button.height = 1;
        this.button.color = "white";
        this.button.fontSize = 30;
        this.button.background = "black";
        this.texture.addControl(this.button);
        this.body.material.opacityTexture = null;
        //this is where it breaks
        //this is some kind of class based scoping issue, I think
        this.button.onPointerDownObservable.add(function() {
          //problem is here...
          //console.log("hi");
          //console.log(this.body.material);  
          this.body.material.alpha = 0.5;
          this.body.material.backFaceCulling = false;
        });     
        }
      }
      //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Here is the error:

VM142:1284 TypeError: Cannot read property 'material' of undefined
    at r.callback ((index):398)
    at e.notifyObservers (babylon.js:16)
    at Button../2D/controls/control.ts.Control._onPointerDown (control.ts:1918)
    at Button../2D/controls/button.ts.Button._onPointerDown (button.ts:146)
    at Button../2D/controls/control.ts.Control._processObservables (control.ts:1990)
    at Button../2D/controls/button.ts.Button._processPicking (button.ts:117)
    at Container../2D/controls/container.ts.Container._processPicking (container.ts:495)
    at AdvancedDynamicTexture../2D/advancedDynamicTexture.ts.AdvancedDynamicTexture._doPicking (advancedDynamicTexture.ts:673)
    at r.callback (advancedDynamicTexture.ts:824)
    at e.notifyObservers (babylon.js:16)

So, somehow within this onButtonCLick function, it cannot see the mesh’s material, even though when I go into the console and type treasureOne.material, I can see the material. I’m assuming this is some kind of simple scoping error that I don’t fully understand.

I tried moving the f(x) out of the constructor, but still no dice. I also know that for sure the button is getting pressed, it just can’t see the material on the treasure class’s mesh. I also know that before the button down function, I can still console.log the material no problem.

Some nudges in the right direction would be helpful…

Make the callback an arrow function () => {} instead of a function () {} to ensure of your this.

that 100% worked, I would have never figured that out on my own thank you so much

1 Like