Problem with attachChildAt

When using attachChildAt I get an exception:

java.lang.IndexOutOfBoundsException: Index: 10, Size: 0
at java.util.ArrayList.rangeCheckForAdd(ArrayList.java:579)
at java.util.ArrayList.add(ArrayList.java:393)
at com.jme3.util.SafeArrayList.add(SafeArrayList.java:270)
at com.jme3.scene.Node.attachChildAt(Node.java:282)


Is attachChildAt supposed to work with any index number or do I have to set a size of it somehow?

Considering its an array list, you can only attachAt length-1



If you want to use IDs of objects/entities, my suggestion is to use name.



For example make the nodes/spatials you are attaching have a name something like



“NPC10”

“OBJECT10”

etc…



Then you can get the spatial by doing node.get(name)

1 Like

Is there a way to set the size of the array?



I know about names but figured that using the index should be faster.

Don’t try to access the array directly. Names shouldn’t really be much slower, string comparison is pretty efficient with existing strings. But why do you want to access it like this at all? Can’t you just save a reference to the spatial where you would save your index number now?

@rasmuseneman said:
Is there a way to set the size of the array?

I know about names but figured that using the index should be faster.


No. And all of the null entries would really mess things up and waste way way way more time than your lookups.

And anyway, your lookups like this should be infrequent or you are doing something wrong.

It wasn’t the String comparison that I were worried about but that it had to enter each Spatial to get the name.



I’m doing a very rare type of game. So I actually need to do this every frame.

Before I had a Map with references but that felt unnecessary and wastefully.



Well I could keep a map whit Booleans if I need to check this frame, but that feels even more ugly but maybe a bit quicker.

@rasmuseneman said:
It wasn't the String comparison that I were worried about but that it had to enter each Spatial to get the name.

I'm doing a very rare type of game. So I actually need to do this every frame.
Before I had a Map with references but that felt unnecessary and wastefully.

Well I could keep a map whit Booleans if I need to check this frame, but that feels even more ugly but maybe a bit quicker.


Well, attempting to reuse the internal child array for ID lookups is wasteful because you will potentially run through (and the engine would have to be modified to check for nulls) a bunch of extra entries a few times per spatial every frame.

"A very rate type of game" doesn't tell us much, though. I will say that there still may be a better way. Maybe tell us more specifically what you are doing.

Okey, I (and a friend) is doing like an Audiosurf clone. And I’m using the Id or name of the Spatial to check for collision as ordinary collision checking is to slow for the high speed and small objects. Maximum speed is about 130wu/s and collision boxes are less that 0.5wu deep.



I just got anaother error aswell, while trying to use Batch Node. If I batch the Batch Node (.batch()) I get this exception:

SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
java.lang.NullPointerException
at com.jme3.scene.BatchNode.updateSubBatch(BatchNode.java:154)
at com.jme3.scene.Geometry.updateWorldTransforms(Geometry.java:297)
at com.jme3.scene.Spatial.updateGeometricState(Spatial.java:702)
at com.jme3.scene.Node.updateGeometricState(Node.java:176)
at com.jme3.scene.BatchNode.updateGeometricState(BatchNode.java:120)
at com.jme3.scene.Node.updateGeometricState(Node.java:176)
at com.jme3.scene.Node.updateGeometricState(Node.java:176)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:246)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:184)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)
at java.lang.Thread.run(Thread.java:679)

By too slow you mean performance or you mean it misses collisions due to skipping over them? If the latter are you aware of sweep tests?

@rasmuseneman said:
Okey, I (and a friend) is doing like an Audiosurf clone. And I'm using the Id or name of the Spatial to check for collision as ordinary collision checking is to slow for the high speed and small objects. Maximum speed is about 130wu/s and collision boxes are less that 0.5wu deep.


Not sure what collision checks have to do with ID lookups.

@zarch both :slight_smile:



@pspeed as we know where every block is, we can put the location as the ID. And do something like

if (node.getChild(playerPos) != null) or when we where using a Map if (map.get(playerPos) != null)

So you want to define the position of a player using an integer, which also is the node index?

What if two players are in the very same location, attaching the second player with the same id would overwrite it.

Sounds like you are trying to hammer a round screw into a square hole.





Essentially the game is a straightforward race-along-a-tunnel type experience then?



Are you colliding in 2d or 3d?





I’d be tempted to use a bitfield for this but that’s the old-school c programmer in me coming out from hiding. This is definitely the sort of problem where working smart rather than hard will pay dividends though.

Regardless of how you’re gonna go about this thing, you better be careful when working with child lists (a jme SafeArrayList). Despite the name they’re not actual arraylists, or even lists (as in java.util).

@androlo said:
Regardless of how you're gonna go about this thing, you better be careful when working with child lists (a jme SafeArrayList). Despite the name they're not actual arraylists, or even lists (as in java.util).


? It is an actual java.util.List. I'm not sure what you meant here.
@rasmuseneman said:
@zarch both :)

@pspeed as we know where every block is, we can put the location as the ID. And do something like
if (node.getChild(playerPos) != null) or when we where using a Map if (map.get(playerPos) != null)


Then keep a separate data structure. That's not what the child list is meant for.

Okey, I’m going back to a Map then. Found a better way to pass the reference so it’s a bit cleaner.


So you want to define the position of a player using an integer, which also is the node index?

What if two players are in the very same location, attaching the second player with the same id would overwrite it.

No, I want to define the position of the blocks using integers. Then I get the local translation of the player

and compares that to the blocks to get if there is an block on the same “row” in the Z axis. If there is I check

if the block is on the same lane as the player using a variable in the blocks class.



Essentially the game is a straightforward race-along-a-tunnel type experience then?

Yeah



Are you colliding in 2d or 3d?

2D, but this approach were just for 1D



Thanks everybody for your thoughts :slight_smile:

I meant SafeArrayList is nothing like a regular list, but it’s a specialized one. If someone start using it like a normal list it’ll cause un-expected behaviors in an app. Better use the higher level stuff (like the Node methods etc.).



It’s not an arraylist, that I know. I can’t remember exactly why, but I experienced some problems with it before and the docs mentioned the class isn’t an actual list either, but works differently with iterators etc. This was when I added the paging system to Forester, which was like 6 months ago, so it might have been changed.



Stay clear of it is my general advice tho.

This is what I meant by bitfields:



Assuming you have less than 32 columns. For 64 columns use a long.



Do an array of integers for your track.



Each integer is a horizontal slice. For each column put a 0 or 1 bit in the integer depending on if anything is present at that location.



Define the player’s current location as a bitfield (i.e. if we simplify things to 8 columns then player in the 3rd column = 00000100) If you want the player to cover multiple columns then its easy, just do 00001100



Obstructed cells on a certain row might look like 00001100 or 01101010.



Then to check for a collision on a row just do if (obstructions.get(y) & shipLocation != 0) // collision detected, process accordingly



So with the 1 cell wide ship 00001100 & 00000100 = 00000100, collision

So with the 1 cell wide ship 01101010 & 00000100 = 00000000, no collision



Whereas with the 2 cell wide ship



00001100 & 00001100 = 00001100, collision

01101010 & 00001100 = 00001000, collision



In fact a 2 cell wide ship would be unable to pass through that second set of blockages at all.







So collision for a row of potential obstacles comes down to a single bitwise and followed by a comparison: super-fast.

@androlo said:
I meant SafeArrayList is nothing like a regular list, but it's a specialized one. If someone start using it like a normal list it'll cause un-expected behaviors in an app. Better use the higher level stuff (like the Node methods etc.).

It's not an arraylist, that I know. I can't remember exactly why, but I experienced some problems with it before and the docs mentioned the class isn't an actual list either, but works differently with iterators etc. This was when I added the paging system to Forester, which was like 6 months ago, so it might have been changed.

Stay clear of it is my general advice tho.


It works almost exactly like CopyOnWriteArrayList except that it's not thread safe. If you've found otherwise then it's a bug. It should work exactly like ArrayList except that you can modify and iterate at the same time. The only odd case is potentially when using iterator.remove() to remove elements from a list that has lots of duplicate entries. And actually, CopyOnWriteArrayList simply throws UnsupportedOp in that case.

Well, and these caveats:

Important caveats over normal java.util.Lists:
Even though this class supports modifying the list, the subList() method returns a read-only list. This technically breaks the List contract.
The ListIterators returned by this class only support the remove() modification method. add() and set() are not supported on the iterator. Even after ListIterator.remove() or Iterator.remove() is called, this change is not reflected in the iterator instance as it is still refering to its original snapshot.


But in my experience, these sorts of things are common with different list implementations. (And CopyOnWriteArrayList's implementation of subList() has its own set of gotchas that are obvious if you expect them... I just chose to avoid the issue.) These features could probably be implemented but they complicate the code considerably and are almost never used.

And technically only the "subList() is read-only" behavior breaks the standard java.util.List contract.

Anyway, we're derailing this thread.