Node.getChild(name) returns deep spatial instead of direct child?

Node - A
|_Node - B
| |_Geometry - 1:name=background
|_Geometry - 2:name=background

So, Node - A.getChild("background") is returning Geometry - 1, instead of IMO expected Geometry - 2 (it’s direct child).
If the childs could be organized so Nodes would come last in the child array, it would work.

In the other hand, if every spatial were give unique names, that would not be a problem, but there is no constraint for that.

Using getChild(name) is fine for prototypes and test code, but really I wouldn’t want to use it in an actual application.

My guess is that it’s doing a depth first search. No matter what, users requesting ambiguous results are going to be unhappy with some approach.

If you really must search the scene graph in this way it’s probably better to use a scene graph visitor (and breadth first search in your case) instead of relying on this convenience method.

1 Like

oh ok, I will look for “scene graph visitor” (SGV).

Btw, as an alternative I am willing to setUserData() with the geom background just to have a direct link, but I feel that is a bit messy…

EDIT: another useful and simple way would be to return all children matching that name, but I havent looked at SGV to have a chance to prefer it yet.

We can’t include every call that every user might want to make. Personally, I’d rather just get rid of the getChild(name) method completely… but that would break a bunch of games that inappropriately rely on it, I guess.

2 Likes

Thats quite an incredibly precise guess!
First time I hear that Spatial names are not that important… I have been using it for ‘EDIT:a few’ things… Now I think I should scan my projects to try to avoid using it…

EDIT: it turns out I’ve been using it to add functionality/tweaks, like shrink the listbox selector, increate the text cursor width (for better visibility) and so on…

In Lemur?

That’s a sign I haven’t done my job right exposing that stuff. A problem easily remedied.

Even if not…
…the approach you’re using is fragile since those names could change for no good reason. Relying on the names like that is adding an invisible dependency that may cause code to randomly break at a later date.

In all of my years of JME use, the only reason I’ve ever used getChild(name) was when looking up children of loaded models… and in more than half those cases, I modified the model and resaved it. In the other cases, it was a scripted lookup and so more mutable.

1 Like

Well… it works perfectly fine if you have unique names for your spatials… What I recommend btw, especially if you want to query them by name…

1 Like

But needing to do that in regular code is a sign of a larger design problem somewhere else.

You actually can’t hide them at all… the current JME Spatials tree can be easily browsed and it is not that hard to guess wich Spatial is being used to function as a listbox selector or a textcursor, etc.

But… the point is, I wanted to tweak lemur without creating a fork (1*).
So, instead of codifying something to provide such funcionality in a proper way (a fork or a patch), I just went to the point and tweaked whatever was accessible (without hacking) to make things work the way I needed/wanted.

In the other hand, if lemur had more protected fields and methods, we could just extend them and create my tweaks in a semi-proper way. But I believe you make most things private at lemur so we are not tempted to mess with things that you want to change freely to improve or fix things without breaking our code. I see many restrictions like that in the very JME too.

As I remember, that’s the main reason I created the Conditional state (I was unable to tweak the existing one enough): https://github.com/AquariusPower/CommandsConsoleGUI/blob/master/src/com/github/commandsconsolegui/jmegui/ConditionalStateAbs.java (where I can validate and postpone each step, therefore not requiring a fully precise initialization of everything each state may depend on, letting me spend more time developing the project than trying to make it work every time it stopped…).

1-So, weighting it all, I think the access freedom JME still provides concerning its Spatials tree is good enough to let us create tweaks.
1.A) But, these tweaks may break something or may malfunction. Like if I change a geometry material color, lemur may just update it, so I have to keep updating it back to what I want, a needless fight if it was implemented in a proper way.
1.B) Or worse if I change the scale and lemur depends on that scale to make something else work properly, that would be a mess to make it work without a proper patch/fork indeed.
2) so basically, concerning you tips, whatever code we create as a tweak, must work as an optional functionality that requires validation. So, in case of a Spatial search failure event (name not found, or not possible to guess were it is now after updating the library), it will not break our application, it will just slightly cripple it.

1* - The main problem (for me) is time. I have little time to implement my own complex project, which development walks like a sick and blind turtle, so I cant afford maintaining any forks. The most things I do is create some patches for specific things I really care/need and that I am not able to workaround. It is not what I would like to do, it is just what I can do :frowning:

I don’t want to hide them. I want to expose them properly. Relying on a spatial named “background” is extremely fragile because that background component could be anything with whatever name. You can’t count on it.

But many of Lemur’s GUI elements already expose their subparts. There are just things I haven’t gotten to yet… which is an easy fix if someone points it out. (For example, Slider already provides access to all of its own subparts.)

protected fields are evil. People use them thinking they made their class extensible but in reality they took a dangerous short cut. Classes that can be extended to enhance behavior require proper design and almost never result in protected fields. So as a rule, they are bad. (And 90% of the time, a proper design will show that composition is better anyway… like Lemur uses.)

I’m still not sure I understand what this was for… but if it’s useful to you then that’s fine. We might be able to point to better approaches if you have time to consider them. I’ve put together lots of games and UIs and never needed anything like this yet.

That all depends on what you were counting on it for. Subtle bugs like this have a way of exposing other subtle bugs on up the line until you are scratching your head wondering why all of your space ships are flying backwards and the explosion graphics are monkey heads.

Removing getName() doesn’t remove this freedom (and we aren’t doing it anyway) as you can still traverse the tree any way you want. My desire to remove getName() is because I think it causes more bad behavior and tech support issues on the forum than it provides ease-of-use in proper situations. ie: it costs more than it provides. It’s too late to remove it now, though.

1 Like

May be, rename to getDebugName() would make ppl understand it is not to be used as a base to implement, but only eventually in debug.

Composition like in: panel.getControl(GuiControl.class).setComponent("text",...), mmm… that’s good indeed. I will see if I can change some things to use it instead.

Oh yes, more exposed things are good, but I guess they will require extra testing time to grant they were exposed properly too.

The ConditionalState allowed me to be less precise when and where initializing such state dependencies. I just skipped frames until objects were ready, even fully initialized and “stabilized” in cases, to let the ConditionalState start its proper/granted/safe initialization.
So I only created a configure() (for really basic/simple settings) and postponed the initialization based on custom conditions, so my ConditionalStateManager (that is a JME AppState) was created to make the ConditionalStates wait.
After that was fully implanted here, things started initializing very smooth, it spares me a lot of time concerning finding out (have it been documented or not) what has to be setup/configure/initialized before what else.

[blabering/thinking about my own projects]
Protected or private (or composite)?
I really like private (concerning less trouble),
but protected is tempting (like we throw the responsibility on our library users)…,
Extensible composite sounds best indeed. But every tweak a user may want to do may require another thing to be exposed.

Ex. If I create a set() for every field, that could work I guess (but I don’t see how that would result in something much different than protected in terms of creating troubles).

Basically everything that is exposed may cause trouble, and to prevent that we need test time.

And, by not exposing untested fields may make the library more reliable, what I think is best on the long run… but may throw creative ppl away hehe…
Mmm… weighting: small team + reliable + long run + life time + less trouble VS creativity requests/interest + more trouble. The former wins indeed, and a fork is always possible for creative ppl.

I dont like it but I think I will go private too, that will make me more tranquil on the long run hehe… thx!

Because you’ve protected your data members meaning if you redesign the internals of your class later then you can still somehow support these set()/get() methods.

General approach, make fields private. Done.
If some subclass needs access to that field’s value, add a protected get() method. Done.
If some subclass really must set that value, redesign your whole class with extensibility in mind as there are probably a bunch of other broken things now.

Very occasionally, allowing a protected setter is ok. But having that setter() lets your base class be aware of the ‘event’ of setting that value. It can do all kinds of things behind the scenes if needed, such as resetting dependent fields, firing events, or whatever.

A raw protected field gets none of that. Essentially you are slowly turning your object in a struct. Just a bag of nearly-public fields.

I guess in my cases I get away with just not attaching states until I know they will have what they need. Or the very rare case of just doing lazy initialization of some field under a getter.

1 Like