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?
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:
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)
@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.
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.
@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.
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.
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.