Script does not recognize sub elements

@yaustar Were you able to enter the project? I put it as public.
I keep testing codes, but how I do this? “reference the parent entity first” and later “call findByName on the parent to find a child with that name.” :thinking: :thinking:

Hi, @Ariel_Cancio

Don’t give up :slight_smile:

‘.findByName()’ method searches for children nodes from the one you call it on. And its grand-children. And its grand-grand-children. And so on. So, when you use this.app.root.findByName(), you are calling it on root. This means anything that is under root will be searched for, which is pretty much the whole scene.

Another example, if you do this.entity.findByName(), you will search among the children of the entity you are calling this script from.

So, when @yaustar said you can reference the parent first, it simply means you can search for the parent first, and then for the child:

var parent = this.app.root.findByName('Some Parent Unique Name');
var child = parent.findByName('Child Name Under the Parent');

This way parents may have children with the same name, and you would reference to the correct one. Otherwise, if you would search root for a child with a non-unique name, the search result may point to a child under some other parent.

1 Like

Hello @LeXXik Master Jedi :grin: :grin:

ok i tried to do this but i see an error

Line 16:

 myYellow.element.on('mouseup', this.onTopBaseA_Range_YellowBtnPressed, this);

var ChangeYellow = pc.createScript('TPYELLOW');

// Reference a list of textures that we can cycle through
ChangeYellow.attributes.add("textures", {type: "asset", assetType: "texture", array: true, title: "Textures"});

// initialize code called once per entity
ChangeYellow.prototype.initialize = function() {
    var self = this;
    
    // Keep track of which texture in the array we are currently using
    this.textureIndex = 0;

    // Change textures on button press
    var parent =  this.app.root.findByName('TopBaseA_Range_Beige');
    var child = parent.findByName('TopBaseA_Range_Yellow');
    myYellow.element.on('mouseup', this.onTopBaseA_Range_YellowBtnPressed, this);
    };
 

ChangeYellow.prototype.onTopBaseA_Range_YellowBtnPressed = function(dt) {
    // Index the next texture in the list, wrapping around if we reach the end
    this.textureIndex = (this.textureIndex + 1) % this.textures.length;

    // Reference the texture
    var texture = this.textures[this.textureIndex].resource;        

    // Go through all the mesh instances of the model and change the diffuse texture on the
    // material to our new one
    var meshInstances = this.entity.model.meshInstances;
    for (var i = 0; i < meshInstances.length; ++i) { 
        var mesh = meshInstances[i];
        mesh.material.diffuseMap = texture;
        mesh.material.update();
    }
};

Yes, check what the error says. The very first line on it.
ReferenceError: myYellow is not defined

You know its on lilne 16, so go there and see what’s happening. You tell the engine that you want to use an element property of myYellow variable. But you never told what that variable is, so it tells you myYellow is not defined, tell me what that is first. Well, not word by word, by you got the point.

HInt: you did define child variable, which you probably wanted to use instead.

1 Like

I’m not making it @LeKKik
I feel like you’re almost telling me, but I can’t figure it out:
“you did define child variable, which you probably wanted to use instead.”

The child variable:

 var child = parent.findByName('TopBaseA_Range_Yellow');

The line 16

 myYellow.element.on('mouseup', this.onTopBaseA_Range_YellowBtnPressed, this);

I don’t understand what it is the Yellow Variable

This is related to what I was talking about in the other thread: Change material properties with a button

myYellow has no value and not referencing an object. So you either have define it as a variable in this function and give it a value/reference an object or use another variable that has the reference to the object that you need to get the element from.

To further explain this, this line of code is saying:

On the object that myYellow is referencing, get the element object and call the on function.

1 Like

Ok, I think this is closer, but it still doesn’t work. Follow the same error

var parent =  this.app.root.findByName('TopBaseA_Range_Beige');
    var child = parent.findByName('TopBaseA_Range_Yellow');
    myYellow.element.on = parent.findByName('TopBaseA_Range_Yellow');

I feel like I’m shooting blindfolded, if I succeed it will be pure luck :persevere: :persevere:

You give up too early :wink: I will give an answer tomorrow, if you are still stuck by then.

Hint: myYellow is not defined, but child is

Yeap no problem @LeXXik, we continue tomorrow. I’ll keep trying. I have noticed that if I erase the line

myYellow.element.on = parent.findByName ('TopBaseA_Range_Yellow');

the error disappears but does not change color, let’s say it does not work. From what I deduce that deleting is not the solution but to correctly complete the code.

Hi @LeXXik, I’m still stuck, I’ve tried many things and searched the API guide but I can’t find how to define myYellow as a variable in this function, therefore I haven’t achieved what @yaustar suggests "give it a value / reference an object ", I don’t know if I should add a line of code, if I should change some values in what is written, etc.

@LeXXik I have added this line to give myYellow a value but it doesn’t work, obviously it’s wrong and I don’t understand why

    var parent =  this.app.root.findByName('TopBaseA_Range_Beige');
    var child = parent.findByName('TopBaseA_Range_Yellow');
    myYellow.element.on('mouseup', this.onTopBaseA_Range_YellowBtnPressed, this);
    this.myYellow = this.parent.findByName('TopBaseA_Range_Yellow');

Howdy :slight_smile:

First of all, let’s get rid of your early bad habbits in syntax:

  1. Don’t use underscores _ in the names of your methods. Javascript developers use camelCase (google it) for naming convention:
    .onTopBaseA_Range_YellowBtnPressed()
    vs
    .onTopBaseARangeYellowBtnPressed()
  2. Try to keep the method names short. The name should only tell what the method does or when it is called. For example:
    .onClick()
    .moveForward()
    vs
    onRedButtonWithYellowStripesClickedTwoTimes()

Now, about your problem. Lets refresh how we use variables first.

  • Rule number 1: Define your variable.
var myVariable;
  • Rule number 2: Assign some value to it (in other words, store something inside that variable)
myVariable = 5;
  • Rule number 3: Use the data stored inside the variable only after rules 1 and 2 are passed.
myVariable + 2; // 7

You must follow these rules with anything in Javascript, be it objects, properties, variables, libraries, JSON payloads. Remember them. If you skip rules 1 or 2 or both, and jump straght to number 3, the compiler will complain that the variable is undefined. You must start from rule 1.

Often it is verbose to write rules 1 and 2 on new lines, so Javascript allows to merge them into a single string:

var myVariable = 5;

But the order is still preserved - the variable is defined first, then it is assigned a value.

Can you tell by now where you problem is?

Perhaps, to fully open your issue, I could introduce you to a new concept - Javascript Objects. Well, its not really new. You’ve actually been using them all this time.

Say you want to create a character:

var character;

To make it more than just an empty blob of nothingness, you want to add a name to it and a language it speaks:

var charName = "LeXXik";
var language = "Marsian";

But now, those character treats are stored in their own variables, so you can’t simply pass character variable to some function. You would also have to send charName and language. But what if the char has dozens of properties? You can see how it quickly becomes hard to handle. Javascript objects are the answer.

A Javascript object is a simple collection of key:value pairs. Where you can name keys anything you want, and assign them any value, another object or even a function that returns a value. Pretty much anything, really. The objects syntax is to enclose the pairs in curly brackets { }. So, our character would look something like this:

{
    name: "LeXXik",
    language: "Marsian"
}

Great, now we have a character, but how do we use it? How can I get that name, for example? Well, remember rule number 1:

var character = {
    name: "LeXXik",
    language: "Marsian"
};

We defined the variable and assigned a value to it. So, now our variable stores an object that describes our character. How do we get that name? Pretty simple - we use so called “dot notation” to access object properties. This should be familiar to you:

var name = character.name;

We can even extend it further:

var character = {
    name: "LeXXik",
    language: {
        name: "Marsian",
        level: "pretty good"
    }
};
var languageLevel = character.language.level;

Here we added another object with properties that describe the language LeXXik may speak. This is a very common and powerful method, which gives you an ability to create rich descriptions about an object. Now, instead of passing a dozens of variables, you can simply send a single character variable that stores the object.

The answer.

Here is your code:

var child = parent.findByName(...);
myYellow.element.on(...)

You broke the rule number 1 and 2, and straight went to number 3 by using myYellow variable without defining it or giving it any value. The variable myYellow is empty, and has no object inside, that would have a key with a name element. However your child variable does, and should be used instead:

var child = parent.findByName(...);
child.element.on(...)

Your child was properly defined and stores an entity object that was found by the engine:

var child = {
    ...
    ...
    element: {
        ...
        ...
        on: function() {  ...  }
        ...
    }
};

So, when you run child.element.on(), the engine looks inside the object stored in the variable and using the dot notation finds the .on() method.

4 Likes

Thank you very much @LeXXik I am reading everything very carefully.

Hello @LeXXik I have reviewed everything and I think I have understood it, thank you very much, the error does not appear anymore but it still does not change color (I think it is a problem that the child button is linked to the parent button in root), basically when I press the yellow button “I drag” the beige button I leave you some screenshots so you can visualize
(Can you notice that when I pose above the red button, the beige button is also activated?)


Jerarquia_01

The corrected code with your suggestions (I cross my fingers that it is correct) :

    var parent =  this.app.root.findByName('TopBaseA_Range_Beige');
    var child = parent.findByName('TopBaseA_Range_Yellow');
    child.element.on('mouseup', this.onTopBaseA_Range_YellowBtnPressed, this);

pd: Thanks for your CamelCase advice, I’ll use it when rewriting this

Yep, the code is correct.

Those names in the scene tree don’t really tell me what those entities are. I see the hierarchy however. Is “Beige” a button element, that has many other buttons as children? If yes, what is the idea behind this approach? This doesn’t sound right, as usually the children of a button are only cosmetics, like an image, or a text label. You generally don’t want to put any other active elements as children under the button. Its like the end of the branch of your scene hierarchy tree. Don’t grow another branch from a leaf.

So, what’s the idea here in your feature? Describe the user flow, so I can recommend a proper path.

Hi @LeXXik, this is exactly how you have interpreted it, Beige a button element that has many other buttons as children. It’s the best way I can imagine to solve a hierarchy problem, but it’s probably misfocused. I will try to explain a little how I want the button system and its hierarchy to work, I hope it is clear enough and you can understand it and give me your opinion:

The “TOP BASE A”, “TRIM STRIPE B”, “TRIM STIPE C” and “TRIM STRIPE D” buttons are main buttons that select a specific part of the plane in which we want to apply a certain range of color (beige, yellow, red, gray, purple, green and black), then what I have thought is that the beige, yellow, red, gray, purple, green and black buttons are secondary / children of each
button “TOP BASE A”, “TRIM STRIPE B”, “TRIM STIPE C” and “TRIM STRIPE D”. In turn, each button that defines a color range (beige,
yellow, red, gray, purple, green and black) when pressed, it must display a set of buttons with multiple colors that correspond to the chosen color range. In other words, if you press the “beige” button, a variety of beige tones are displayed that will only be applied to the part of the plane previously chosen with the “TOP BASE A”, “TRIM STRIPE B”, “TRIM STIPE C” and "TRIM buttons. STRIPE D ". Actually, the buttons that change the colors are those of the third level, that is, the ones that are displayed when you press the button corresponding to the color range you choose.
If you remember a post of mine from a few weeks ago, I told you that I wanted to write a script that could work with hierarchies.
Well it was just for this project.
I determined that it would be too complex and tedious to have all the buttons on the same level of the scene tree, if I do it from this
way I imagine dozens of scripts that control each one to a button. So I thought about putting the buttons together like a tree in the scene:
The buttons “TOP BASE A”, “TRIM STRIPE B”, “TRIM STIPE C” and “TRIM STRIPE D” would be the trunk of the tree, the buttons “beige”, “yellow”, “red”, “gray”, " violet “,” green “and” black "would be the branches, and the colored buttons for each range would be the leaves. By doing this
So I imagine a single script (or as few as possible) that can control each tree indicating parent / child functions as I am trying to achieve. I hope you have understood what I am trying to achieve.

The perfect script should say something like this:

First event / function:
When the “TOP BASE A” button is pressed, the “beige”, “yellow”, “red”, “gray”, “violet”, “green” and “black” buttons that correspond to the “TOP BASE A” button should be displayed and keep hidden the" beige “,” yellow “,” red “,” gray “,” purple “,” green “and” black “buttons corresponding to the” TRIM STRIPE B “,” TRIM STIPE C " and " TRIM STRIPE D " button.

Second event / function:
When the “Beige” button is pressed corresponding to of the “TOP BASE A” button that was pressed / selected in the first event / function, all the color buttons (Beige Color Range) corresponding to the “Beige” button must be shown and all the color range buttons corresponding to the buttons called “yellow”, “red”, “gray”, “purple”, “green” and “black” must be kept hidden

Third event / function:
When is press some color button in the beige range that was activated / displayed when the “beige” button was pressed in the second event / function, it are must put that color/material in the object to which to the refers the “TOP BASE A” button.

Captura03

Sounds like you can do this with two scripts.

One for each button that represents a part of the product that shows/hides the buttons for each colour possible.

And another one for each colour button that referenced the material and the colour to change it to.

Right, got you. Basically, as @yaustar said. So, there are 2 ways in doing this, and it simply depends on your app design:

  1. You have multiple base parts. Each base part has one or many shared or unique colors.
  2. You have multiple base parts. All base parts share the same set of color choices.

Option 1:
You can keep your current setup, and only adjust it so that the Buttons are not parents. For example:

Base A               // Entity
  |- Button A        // Button Element
  |- Colors          // Entity
  |    |- Color A    // Button Elements
  |    |- Color B
  |    |- Color C
Base B
Base C

Colors entity can be disabled at start. Since it is a parent, all its children will be disabled too. When ‘Button A’ is clicked, you can enable Colors entity, which would also enable all its children.
The trick here is to know, which Button was actually pressed, since all Base entities (A, B, C) has the same script attached. Was it Button A from Base A? Or Button B from Base B? The answer would allow you to know which Base colors to show and which base colors to hide.
One way to solve it is to hide absolutely all colors on a button click, and then show only the ones that are under the current parent of the Button that was clicked. There are different ways and perhaps you can find another option that works for you better. No right or wrong here.

Option 2:
If all color choices are same for all base parts, then you can simplify the setup:

Logic              // Entity
Bases              // Entity
  |- Base A        // Button Elements
  |- Base B
  |- Base C
Colors             // Entity
  |- Color A       // Button Elements
  |- Color B
  |- Color C

In this case, your Logic entity could have a script that would listen for button clicks. It would catch the base part button click and a color button click, save them in its properties (this.base, this.color). When color button is clicked, it would also change the mesh material based on the selections stored in those properties.

2 Likes

Hello friends, thank you very much for your help. @LeXXik I think option 1 is the closest to what we have at the moment, it also seems to me that the script created yesterday can be the base to start with, what do you think? Let’s get to work, I will be consulting you, I hope you can guide me in this challenge
A query, the Base A entity and the Colors entity should not be buttons, right?