1

I am using the elf example that is a standard example with ThreeJS

Instead of the rotation shown in the example I want to move the object in both the x and y direction. This should be achievable by changing elf.position.x and elf.position.y in the init() function.

This issue I am facing it that I created an object(class) of the methods that create an elf so I can create multiple ones. I also have a function that should move the object over time. var e is not accessible in the move function. When I change it to this.e and I change this e = collada.scene; to this.e = collada.scene; I get the following error: Uncaught TypeError: Cannot set property 'e' of undefined

Code:

 class DrawElf {
    constructor(scene) {
        var e;
        this.loadingManager = {};
        this.loader = {};
        this.scene = scene;

        // loading manager
        this.loadingManager = new THREE.LoadingManager(function () {
            scene.add(e);
        });

        // collada
        this.loader = new THREE.ColladaLoader(this.loadingManager);
        this.loader.load('./models/collada/elf/elf.dae', function (collada) {
            e = collada.scene;
            e.scale.set(30, 30, 30);
            e.position.set(100, 10, 100);
            e.name = "elf.dae" + 0 + 0;

            e.traverse(function (child) {
                if (child instanceof THREE.Mesh) {
                    child.name = e.name;
                    ToIntersect.push(child);
                }
            });
        });
    }

    move(time) {
        // i want to move the object
    }
}

Hopefully someone can help out.

Ahmed Ali
  • 75
  • 2
  • 11
  • Do you have a running example of the code? At first glance you won't have access to the `e` variable from the `move` function to move the model and you're using `self` instead of `this` for defining class variables (unless you mean to access `window` here). – Garrett Johnson Jan 21 '19 at 22:30
  • @GarrettJohnson My bad, I meant to use this instead of self but the error still remains. Unfortunately, I don't have a publicly available running example. I am running everything locally. If I am not mistaken, if I initialise a variable with this.something in the constructor I should have access to said variable anywhere through my class. What am I doing differently here? – Ahmed Ali Jan 21 '19 at 22:35
  • Your `e` variable isn't available by accessing `this`. I've added an answer with some changes to your example code that should hopefully help. – Garrett Johnson Jan 21 '19 at 22:54

1 Answers1

0

I've edited your example code to show how to access the elf model in the move() function and called out the changes in comments below.

class DrawElf {
    constructor(scene) {

        // Change 1: Place the `e` variable on `this` so it's accessible
        // from member functions like `move`
        this.e = null;
        this.loadingManager = {};
        this.loader = {};
        this.scene = scene;

        // Change 2: Use arrow functions instead so that the scope of the
        // constructor is retained in the callback and `this` still references
        // the new `DrawElf` instance 
        // collada
        this.loader = new THREE.ColladaLoader();
        this.loader.load('./models/collada/elf/elf.dae', (collada) => {
            this.e = collada.scene;
            this.e.scale.set(30, 30, 30);
            this.e.position.set(100, 10, 100);
            this.e.name = "elf.dae" + 0 + 0;

            this.e.traverse(function (child) {
                if (child instanceof THREE.Mesh) {
                    child.name = this.e.name;
                    ToIntersect.push(child);
                }
            });


            // Change 3: Remove the loading manager because it's not needed to
            // add the elf to the scene and instead do so here
            scene.add(this.e);
        });
    }

    move(time) {
        // Change 4: Check if the scene has been loaded before trying to move
        // the model
        if (this.e) {
            // move the model here
        }
    }
}

The big change here is to use arrow functions instead of the original Javascript functions so this still references the object instance being constructed. This SO answer should explain the differences in scoping a bit more.

Hopefully that helps! Let me know if anything is unclear.

Garrett Johnson
  • 2,413
  • 9
  • 17
  • @GarretJohnson Thank you very much. Your answer and the link attached were very helpful. I noticed that when I ran your code I received an error on this line `child.name = e.name;`. Rewriting that function using the arrow syntax fixed everything. – Ahmed Ali Jan 21 '19 at 23:37
  • Oh I missed that referenced. I've updated the answer to use `child.name = this.e.name;`. Glad to help! – Garrett Johnson Jan 21 '19 at 23:41
  • Thanks again. Based on what I read on the link you attached and your reply, won't the code as it is raise the following error `Cannot read property 'e' of undefined`? And would rewriting `this.e.traverse` using the arrows syntax fix it? (at least for me it did) – Ahmed Ali Jan 21 '19 at 23:47
  • Your code will run and all the function callbacks will have access to the `e` variable because it's defined locally in a parent scope. However, `this.e` is undefined and you won't be able to access `e` in the `move` function, which is what you need in order to animate the model. – Garrett Johnson Jan 22 '19 at 00:06
  • Thank you for your additional reply. – Ahmed Ali Jan 22 '19 at 20:27