I have an issue while coding a game. I am setting up classes so it is easier and more organized to acces certain variables. I put instances of those classes in arrays, because I want to have multiple different Instancess of those classes. The game runs fine with one instance, but as soon as I add another (which in my case looked almost identical), an error occurs:
AssertionError: !is_empty() at line 1045 of panda/src/pgraph/nodePath.cxx
And now I dont know what to do. Ive searched for a few solutions, but I didnt really understand what was meant (Something with nodepath?). Although I am not a complete beginner in coding, this error has set a major roadblock in my path.
In short, this indicates I believe that the code attempted to do something with a NodePath that, for whatever reason, had no associated node. It may be that the node was removed, for example.
As to this problem specifically, are you perhaps calling âremoveNodeâ on any of the NodePaths your array, but without then removing those NodePaths from the array?
If not, it might perhaps help to see more of your codeâspecifically, anything that interacts with your array, whether it be adding, removing, or operating on things within said array.
Ok now that I look at it the error is in another array, not the original i mentioned, but i think i see what you mean. I am removing stuff from that array, maybe thats the problem? if so i dont know how to code it diffrently. I am trying to generate objects, save them in an array as well as saving a few other variables in other arrays with the same index as the generated object, then removing them noce they hit something. Currently i am moving each generated object in the âobjects arrayâ one by one (in a for loop) and checking, wether they hit something. If so, i add the index of that object to another array (to later remove them) and once each object was moved (and checked), I check if there are any indexes in the âremove later arrayâ and if so, i go through each index and remove the object (and its elements form the other arrays) with that index from the âobject listâ.
Here is the code (it is in the update() method)
#for as many elements that are in the bullets list
for i in range(len(bullets)):
#move the bullets (move is a custom function)
move(bullets[i], bulletDirs[i], BULLETSPEED, 1)
origin = bullets[i].position
#checking from who the bullet is
if(bulletOrigins[i] == 2):
checking for any collision
ray = raycast(origin, (0, 0, 90), ignore=[bullets[i],player2,], distance=0.5, debug=False)
if(ray.hit and ray.entity == player1):
damageP(1, bulletDamage[i])
#add index to removeBullets (bullets that have to removed)
cacheMisc.removeBullets.append(i)
else:
ray = raycast(origin, (0, 0, 90), ignore=[bullets[i],player1,], distance=0.5, debug=False)
if(ray.hit and ray.entity == player2):
damageP(2, bulletDamage[i])
cacheMisc.removeBullets.append(i)
#after moving bullets remove collided ones
if(len(cacheMisc.removeBullets) > 0):
#for as many elements in the removeBullets (array with the indexes)
for i in range(len(cacheMisc.removeBullets)):
#remove bullets (and other variables) from arrays
destroy(bullets[i])
bullets.remove(bullets[cacheMisc.removeBullets[i]])
bulletDirs.remove(bulletDirs[cacheMisc.removeBullets[i]])
bulletOrigins.remove(bulletOrigins[cacheMisc.removeBullets[i]])
bulletDamage.remove(bulletDamage[cacheMisc.removeBullets[i]])
cacheMisc.removeBullets.remove(cacheMisc.removeBullets[i])
The error pointed towards the move() function specificly when i modify the position. I think that, due to there being no more object to move it threw that error
So I did a few tests. If I only shoot at the player, it works. But if I shoot around (not at the player/ outside of the map since I have nothing stopping them from going âout of boundsâ), then it happens
This may not be related, but let me add a note of caution here:
If you have more than one index to remove in a given frame, the removal of the first index will presumably invalidate all of the subsequent indices, meaning that you may well remove the wrong objects. (Or, indeed, attempt to remove an object beyond the end of the array.)
To illustrate, let me construct an example:
Letâs say that we start with this array:
array---->["cat", "mew", "purr", "meow"]
indices--> 0 1 2 3
We then find that âmewâ and âpurrâ are both to be removed, so we store their indices: 1 and 2.
Now, we iterate over those stored indices. Removing the first index, 1, results in this:
array---->["cat", "mew", "purr", "meow"]
indices--> 0 **1** 2 3
Remove item at index 1:
array---->["cat", "purr", "meow"]
indices--> 0 1 2
However, since the object at index 1 was removed, the contents of the array have changed, and so the object at the next stored index, 2, is âmeowâ, not âpurrâ. As a result, the code ends up removing the former rather than the latter.
And indeed, looking at your code, this may be the source of your problem: You may be destroying bullets that are still valid, and leaving in your arrays bullets that are no longer valid.
Hmm⌠What Iâd suggest is what Python calls âlist comprehensionsâ. This allows one to construct a list (or other such thing) in a single line, based on an existing list or group of lists.
Before I get to that, let me note an alternative way of constructing a âfor-loopâ in Python. For one thing, the list comprehension stuff might be easier to follow if you know about it, and for another, it can be useful.
~
For-Loops
So, from what I see you know how to construct an index-based âfor-loopâ, like so:
for i in range(someRange):
# Do something
However, if youâre drawing from a list or other iterable thing (a tuple, a dictionary, etc.), you can also do this:
# An arbitrary list for purposes of the example:
myList = ["cat", "mew", "purr"]
# And now, iterate over it:
for thing in myList:
# "thing" here--and that name is arbitrary--ends up holding
# each item in "myList" in turn--first "cat", then "mew",
# then "purr"--as the loop iterates.
print (thing)
Which should print the following:
cat
mew
purr
So, with that said, on to list comprehensions!
~
List Comprehensions
The basic form looks like this:
newList = [thing for thing in oldList]
Note the section starting with âforââthat amounts to much the same as the âfor-loopâ that I showed above, I do believe.
The result, then, is a new list containing the contents of the old list.
Now, you can also add conditions to the comprehension, to determine which items are added. These are placed after the âfor-loopâ section, and reference it. Like so, for example:
newList = [thing for thing in oldList if len(thing) > 0]
This, then, will only place a given âthingâ into the new list if that âthingâ has a length greater than zero.
Now, there are more complexitiesâyou can have multiple âfor-loopsâ, or nested âthingsâ. And I recommend that you look into some what can be done with list comprehensionsâtheyâre useful, I do find! But for our current purposes, the above should do.
But one more thing: This doesnât assign the new list to the variable on the left of the line of code until the new list has been completed. That means that, if that variable is the same as the one being referenced in the comprehension, the content of the variableâthe listâisnât changed until the work is completed. This is useful for our current purposes, I feel!
So, on to using this in your case:
~
Using a List Comprehension
In your case, you have a list of indices to remove. So, what we can perhaps do is create a comprehension that includes only the objects that donât match those indices. That is, create a comprehension that makes a new list that contains only the objects that we want to keep. Something like this:
(Noting that I havenât tested the following code.)
bullets = [bullets[i] for i in range(len(bullets)) if i not in cacheMisc.removeBullets]
What this is intended to do is as follows:
- It iterates over the indices of the bullets
- For each one, it checks whether that index is in the list named âcacheMisc.removeBulletsâ
- If so, it ignores it
- If not, it includes it in the comprehension
- Once all is done, it replaces the old âbulletsâ-list with the new one created by the comprehension
Thank you a lot.
This will definetly be useful to know. Although I havent tried any of it yet, I am thankful you took your time explaining it.
Itâs my pleasure! I hope that it proves helpful!
Hmm. So Ive typed in your solution and python sees the reiteration as a declaration of a local variable, and with that previos lines of code throw an error telling me that i define a given variable later (locally), although I defined them at the very start of the entire script
Ahâwhere then does the âbulletsâ list come from? I donât see its initial declaration in the code that you posted.
It is at the top outside the Update() function, where you would normally define them (so like after the import and class methods)
Do I have to add something to the first definition (up top) or is âbullets = [ ]â already enaugh?
Ah, but theyâre not instance-variables, however? I note that theyâre not prepended with âself.ââŚ
As a result, indeed, declaring them as I described above might result in their being âshadowedâ by a new local variable in that name.
Iâm not really familiar with declaring variables in the way that you describe, if Iâm following you correctlyâI usually declare them as instance (or occasionally class) variables. Iâm not sure of how to get around the issue there aside from, well, making them instance or class variables, allowing identification via the âselfâ-reference.
That makes it a global variable.
There are two ways you can deal with this:
- let Python know itâs a global variable by using the
global
keyword; at the top inside theUpdate
function, before the line where you want to alter its value, write this line:global bullets
- (my preferred solution) just use slice assignment:
(note that I use enumeration here as an alternative to Thaumaturgeâs code).bullets[:] = [b for i, b in enumerate(bullets) if i not in cacheMisc.removeBullets]
Hope this helps!
Ok ill try that
so I tried the âglobal bulletsâ thing in the update method and it works!!