nodes as variables (solved)

Hello. I’m new here.
I had a problem when trying to remove my long gone distant bullet_nodes.

I’ve written something like this, and i know it’s not the right thing.

        ...
	def beamer (self, x, task):
  		if task.time < 0.02:
			return task.cont
		if self.beam_x.getY() > 500:
			self.beam_x.removeNode()
			return task.done
		 else:
			self.beam_x.setY(self.beam_x, 10)
			return task.again

I want my function to remove only the distant objects, but it completely eliminates the concept of self_beam_x.
Probably it’s mostly about python, but i couldn’t find anything about it. And i’m pretty amateur on this.
So, what should be the function (beamer), or/and the name “self.beam_x”?
Thanks.

Hi turk,

The code you posted shows that in your class you have only 1 bullet node -self.__beam_x and I’m guessing you have more than 1 bullet in your game that is shown. So I think you should use list or dict where you can keep all your bullets and can remove exactly those ones that has reached some distance. Right now when the first bullet reach 500 it removes the node and all other bullets do not have real node anymore.

P.S. When posting code be sure it is with the correct indentation - this is Python and it is critical for reading and executing the code.

Thanks, boundless.
I thought about lists and then i thought it might be a little much to do for this. A friend of mine said that i’ld use “.this” in c++. So, do we have something names objects better, and provide us to do things like this? If not, thats okay. I’ll use lists.
I was cautious about the indentation. I did it right, but after submit, it happened to be like this. I meant to edit, but it was tidy again. I don’t know what happened. Maybe i should use spaces instead of tabs.

I don’t know what is your overall design, but you should accomplish your goal with list; and it should not be hard actually.

And a small advice from me - use always spaces when you write python code - never tabs. I believe all popular IDEs have the option to turn tab to number of spaces. This way when you open your file with another editor (or copy-paste and send to someone) it will look always the same.

Thanks again.
But so if we go back to Panda3d, is this the best way to remove distant bullets from the game; creating bullet lists for each spaceship in the game?
I’m sure that I’m missing something here.

I don’t know whether it’s the best way, but I tend to keep a single list of bullets (or even just of “active objects”, depending on the game) and then update them all, removing as called for. Something like this:

##### Part of our "bullet" class
def __init__(self, <whatever other parameters are called for...>):
    # Other initialisation...

    self.alive = True

    # Further initialisation...

def update(self, dt):
    # Miscellaneous updating, such as moving the object...

    if <whatever condition destroys the object, such as range from start>:
        self.destroy()

# A method called when the bullet hits something
def onCollision(self, impactee):
    #  Deal with whatever should be done on impact,
    # such as reducing target health, creating explosions, etc.

    self.destroy()

def destroy(self):
    self.alive = False

    # Clean up the object...

##
##
##
##### In a "world" or "level" class
def __init__(self, <whatever other parameters are called for...>):
    # Other initialisation...

    self.bullets = []

    # Further initialisation...

# I tend to use a task for my update method
def update(self, task):
    # Miscellaneous updating, including getting the delta-time, "dt", for this frame

    [obj.update(dt) for obj in self.bullets]
    self.bullets = [obj for obj in self.bullets if obj.alive]

    # More updating...

One potential issue is how to get the bullets from whatever class spawns them into the list in the “world” or “level” class; personally, I just use a static “common” class that stores a reference to the “world”/“level” and access that. I realise that globals are commonly spoken against, but I feel that in this case the use of a global “common” class is much more elegant and much less convoluted than passing objects around as much as one can end up doing if one exclusively uses parameters and return values for this purpose.

Thanks for your informative reply Thaumaturge.
Okay, at first I thought that i could do it, but now i see that i can’t, at all.

    [obj.update(dt) for obj in self.bullets]
    self.bullets = [obj for obj in self.bullets if obj.alive]

I’m lost in this part. I see what it means, but i don’t know how to use it.
Can you give me a little more detailed example on this part. How can i update only the alive objects, defined like [obj for obj in self.bullets if obj.alive] in the list. What would be the function like?
If can i make a list like this, Can i also write something like obj.setY()? Apparently i can’t.
This is one of those big steps for me. So, Thanks again (:

Edit: I know, I’ve edited this part so much. Sorry if i caused confusion.

Ah, my apologies–I should have thought to explain those lines. ^^;

The two lines that you quotes are, I believe, called “list comprehensions”; in short, they’re clever ways that Python provides for making lists. However, only the second of those actually makes use of the resulting list. The first instead employs the feature to call a method of each object in a list a little more efficiently than would be the case if we were to simply iterate over the list using a for-loop, I believe.

First of all, my code doesn’t update only the “living” objects; instead, it updates all of the objects, then (effectively) removes those that aren’t “living”, so that on the next update only those that are currently “living” should be updated.

To elaborate:

Line 1

[obj.update(dt) for obj in self.bullets]

This calls the “update” method of each object in the list “self.bullets”, passing in our value “dt”. We could so something similar, using a more convenional for-loop–but less efficiently, I think–like so:

for obj in self.bullets: # Note the similarity between this and the last part of the above line
    obj.update(dt)

As a side effect we create a new list, but since we don’t assign it to anything it’s just discarded. But see the next line…

Line 2

self.bullets = [obj for obj in self.bullets if obj.alive]

This creates a new list, taking every object in “self.bullets” in which the variable “alive” is set to True; the list is then assigned to self.bullets, replacing the previous “bullets” list. In other words, we’re replacing “self.bullets” with a new list made up of only those objects for which the “alive” flag is set to True. Note that in my example the “alive” flag is set to false in the object’s “destroy” method.

Where are you putting “obj.setY(obj, 10)”? Perhaps if you post the version that you’re trying I might spot the problem.

My code is something like this. I added the list part a little carelessly in order to act quikly (:

def __init__(self):
  .....
  self.accept('g', self.firebeam, [0, 'task'])
  inputState.watchWithModifiers('attack', 'g')
  print (taskMgr)

  self.bullets = []
  taskMgr.add (self.beaming, 'beaming', extraArgs = ['x'], appendTask=True)

def beaming (self, obj, task):
  if task.time < 0.02:
    return task.cont
  [obj.update(dt) for obj in self.bullets]
  self.bullets = [obj for obj in self.bullets if obj.alive]
  obj.setY(obj, 10)
  if self.beam_x.getY() > 150:
    self.alive = False
    self.beam_x.removeNode()
  return task.again

def firebeam (self, lol, task):
  global i
  firesound = base.loader.loadSfx("gun.mp3")
  firesound.setVolume(0.1)
  if lol == 0:
    self.baller(i)
    i = i + 1
    firesound.play()
  else:
    pass
  if inputState.isSet('attack') == True:
    taskMgr.doMethodLater(0.6, self.firebeam, 'firebeam')
  else:
    pass

def baller (self, x):
  self.beam_x = loader.loadModel("tetra")
  ...
  self.alive = True
  self.bullets.append(self.beam_x)

Ah, okay–I believe that I see why the “obj.setY” line isn’t working:

[obj.update(dt) for obj in self.bullets]
self.bullets = [obj for obj in self.bullets if obj.alive]
obj.setY(obj, 10)

I think that by the time that you call “obj.setY”, “obj” has left scope: it only exists for those two list comprehension lines, and is created separately in each (we could change “obj” to “cat” in the second line and have no change to the outcome, I believe).

Looking at your bullet-creation code, you might consider either creating a new class for bullets (take a look at my first post in this thread for a skeletal example), which would then have an “update” method that would perform such functions as calling the “setY” method, or you might create an “updateObject” method in your “level”/“world” class to do the same, in which case you would presumably call that method, passing it the object as a parameter, instead of “obj.update” in the first-quoted line above.

Okay, I’ve created an update method, which i definitely should’ve done in the first time.
But i still have problems. Sorry, this is really a new era for me.

So, can I overcome this;
panda.NodePath’ object has no attribute ‘alive’
only with typing self.alive = True in the bullet creating method or somewhere else?
Well, I see that I can’t. How can i put the aliveness in those mortal objects?

I’m still using only one class for those stuff. My head isn’t ready for another twenty lines of code yet:D If it’s not the only way to do this?

In the bullet creating method;

...
self.alive = True
self.bullets.append(self.beam_x)   #This is necessary right?

Since I’m still a little far away from the two lines of yours…
and it kept saying: object has no attribute ‘self’, or ‘update’ according to whatever I was typing, I did this:

...
for obj in self.bullets:
  taskMgr.add (self.update, 'updater', extraArgs = [obj], appendTask = True)
  self.bullets = [obj for obj in self.bullets if obj.alive]

So i could have the error: panda.NodePath’ object has no attribute ‘alive’

I didn’t get any closer to it, did i?

Don’t worry, I fear that I may not be the best teacher. ^^;

It seems that you’re adding a new update task for each bullet; instead, just add the task once, at the start of the game. Something like this:

def __init__(self):
  .....
  #  Notes: this is no longer in a for-loop, and no longer has the "extraArgs" parameter:
  # we're using one call for all objects
  taskMgr.add (self.update, 'updater', appendTask = True)

#  It looks as though your "beaming" method in your previous posts
# corresponds to this update method, by the way.
def update(self, task):
  ...

As written above, it appears that you’re adding the “alive” variable to the class that you’re using, and not to the bullet. Sorry, looking back it seems that I missed that issue in your earlier post. ^^;

First of all, since you’re storing your bullets in a list, you might not have a reason to keep “self.beam_x” around (you’re effectively storing it in your class, I believe). Try something like this:

def baller (self, x):
  #  We're here creating a local variable, "beam_x", that will be discarded
  # once we leave the method--but that's okay, because we're storing it
  # in our list.
  beam_x = loader.loadModel("tetra")
  ...
  # Now we add the "alive" flag to "beam_x"
  beam_x.alive = True
  #  Again, note that "bullets" has "self" before it--we're using the list
  # created earlier in the class--but that "beam_x" doesn't.
  self.bullets.append(beam_x)

That’s okay, there is another way: instead of having a “bullet” class that updates itself, we’ll create a method in your current class that update bullets. This changes a few things, but not terribly so. However, please note that it will probably be wise for you to eventually start making more classes, as they can be terribly useful, and aid significantly in the organisation of one’s code, I find.

Our new updating code might look something like this:
(I’m ignoring the “dt” part for now in order to keep things simple; we can get into time-based updating a little later, I think.)

def update(self, task):
  if task.time < 0.02:
    return task.cont

  #  Note that I'm now calling "self.updateObject" rather than "obj.update":
  # since we have only one class, I'm keeping the "updateObject" method in that.
  #  Since I halready have a method named "update", I'm calling the method that
  # affects individuals "updateObject"
  [self.updateObject(obj) for obj in self.bullets]
  self.bullets = [obj for obj in self.bullets if obj.alive]

  return task.again

# Actually update the positions, etc. of an individual object
#  Note that everthing acts on "obj", the parameter passed in,
# which is the object that we're updating on; it does not act
# on "beam_x", which doesn't exist any more.
def updateObject(self, obj):
  obj.setY(obj, 10)
  if obj.getY() > 150:
    obj.alive = False
    obj.removeNode()

I did it finally. But i didn’t understand how to use the “bullet.alive” thing, and i didn’t use it.
I did instead:

   def updateObject(self, obj):
      if obj.getY() > 2000:
         self.bullets.remove(obj)
         obj.removeNode()

( Note the distance :stuck_out_tongue: )
It kept saying that object has no attribute alive, when i tried to set bullet.alive. I searched the internet for it, couldn’t have a proper solution. I even tried to create an object class, even though i had no idea what it was. This time with that class, it said that it doesn’t have the attribute setY and such, and i saw that it wasn’t me. I stopped right there.
And i heard that I can’t store values in objects, because objects doesn’t have the dictionary thing and such. Anyways…
Then i thought again, that i can use panda3d’s setTag method, or any other method like setHpr and such, since the obj was going to disappear anyway. But i don’t need it, for now.
Now i’m thinking, that maybe you wanted me to create the alive method myself, a function that toggles between the values of the objects. I don’t know if there is a way to do it or not, probably yes, but i don’t have it.
Anyways, I’ve solved my problem thanks to you :smiley:

I’m glad that you got it working. :slight_smile:

… I’m honestly not sure of what’s meant by that. One can easily have an object that had variables in which one can store values, I believe, and I seem to recall adding arbitrary variables to objects.

Well, no, your bullet class probably wouldn’t have it–but if you were including the model that you loaded in your bullet class (perhaps in a variable named “model”) then you should have been able to update your bullet’s position with something along the lines of “bullet.model.setY”.

I was suggesting a variable, not a method.

However, if I may suggest, it might be a good idea to look at some tutorials covering object-oriented programming in Python–that should hopefully give you a better idea than my piecemeal instruction here.