Retrieving object or class from node being its attribute or an attribute of its object respectively

So now i have this issue where i try to make simple melee combat. I started with making a swoosh class ( a collider with a kill function) which is triggered from events i send to player char to return the node it hits.
However i want to trace this node smh to get the instance of my box class which has this node as its attribute, to call self.break method (nonexistent so far). I know that i probably could make a manager script storing every class and get the instance from their instances lists by getting name from automatically generated node names as it contains id and class id, but it be really slow and clumsy solution, which would make me rewrite a lot of start. Is there an easier way?
My swoosh script:

from panda3d.core import CollisionNode, CollisionBox, CollideMask, CollisionHandlerQueue
from reference import REFERENCE



class Swoosh():
    NAMEID = 'Swoosh'
    def __init__(self,wielder,ctrav):

        if wielder.NAMEID != 'apple_char':
            print('ya cant attach swoosh to non-player onjects')

        self.wielder = wielder
        self.node = self.wielder.node.attachNewNode(f'{self.NAMEID}_{self.wielder.NAMEID}')
        self.cNode = CollisionNode(f"collision_{self.NAMEID}_{self.wielder.NAMEID}")
        self.cNode.setFromCollideMask(CollideMask.bit(0))
        self.cNodeP = self.node.attachNewNode(self.cNode)
        self.cNode.add_solid(CollisionBox(self.cNodeP.getPos() + (-6, 0, 0), 3, 3, 1.5))
        self.cNodeP.setPos(render,self.wielder.node.getPos())
        self.qq = CollisionHandlerQueue()
        ctrav.addCollider(self.cNodeP, self.qq)

        self.cNodeP.show()


    def YeetEnemy(self):
        if len(self.qq.entries) > 0:
            enemy = self.qq.entries[0]

            for el in REFERENCE.damageble:
                if enemy.getIntoNode().getName().find(el) != -1:
                    print(f'{enemy.getIntoNode().getName()} is yonked')
                    self.qq.clear_entries()







and thats the script above aka the char

from panda3d.bullet import BulletTriangleMeshShape, BulletTriangleMesh
from panda3d.bullet import BulletRigidBodyNode
from panda3d.core import CollisionHandlerPusher, CollideMask, CollisionNode, CollisionSphere, CollisionRay, \
    CollisionHandlerQueue
from reference import REFERENCE
from Swoosh import Swoosh


class Char():
    speed = 20
    NAMEID = 'apple_char'
    ORIGIN = (0,0,0)
    def __init__(self,ctrav,pusher):
        x,y,z = self.ORIGIN
        self.model = loader.loadModel(REFERENCE.loadTexturePath('slime_1.bam'))
        self.node = render.attachNewNode(self.NAMEID)
        self.model.setPos(x,y,z)
        self.node.setPos(x,y,z)
        self.model.reparentTo(self.node)
        self.futurepos = self.node.getPos()
        self.qq = CollisionHandlerQueue()
        self.cNode = CollisionNode(f'{self.NAMEID}')
        self.cNodeP = self.model.attachNewNode(self.cNode)
        self.cNodeP.setPos(x,y,z)
        self.cNode.addSolid(CollisionSphere(center=(x, y, z), radius=2))
        self.cNode.setFromCollideMask(CollideMask.bit(0))
        self.cNode.setIntoCollideMask(CollideMask.bit(0))

        #self.cNodeP.show()

        self.gcNode = CollisionNode(f'Gcollision_{self.NAMEID}')
        self.GroundColl = CollisionRay()
        self.GroundColl.setOrigin(z, y, z)
        self.GroundColl.setDirection(0, 0, -1)

        self.gcNode.add_solid(self.GroundColl)
        self.gcNode.setFromCollideMask(CollideMask.bit(1))
        self.gcNodeP = self.node.attachNewNode(self.gcNode)
        self.gcNodeP.setPos(self.node,0,0,0)
        #self.gcNodeP.show()

        self.floater = self.node.attachNewNode(f'camera_{self.NAMEID}')
        self.floater.setPos(self.node,0,0,2)
        self.floater.show()


        ctrav.addCollider(self.gcNodeP, self.qq)




        self.cNodeP.show()

        pusher.horizontal = True
        pusher.addCollider(self.cNodeP, self.node)
        ctrav.addCollider(self.cNodeP, pusher)

        self.lastDelta = [0,0]
        self.node.setScale(2,2,2)

        self.swoosh = Swoosh(self,ctrav)

        self.cmddict = {
            "up": self.Up,
            "down": self.Down,
            "left": self.Left,
            "right": self.Right,
            "shoot": self.beep,
            'not shoot': self.boop,
            'top' : self.top,
            'bottom' : self.bottom
        }






    def Update_state(self,keypass,dt, tup):
        self.futurepos = self.node.getPos(self.node)
        dx, dy = tup







        for k, v in keypass.items():
            if v:
                self.cmddict[k](dt)

        self.node.setPos(self.node,self.futurepos)

        entries = list(self.qq.entries)

        if len(entries) == 0:
            self.node.setZ(self.node,self.node.getZ()-1)



        for entry in entries:
            if entry.getIntoNode().getName() == "collision_tGround":
                self.node.setZ(entry.getSurfacePoint(render).getZ()+4.5)

        self.qq.clear_entries()





        self.node.setHpr(dx, 0, 0)
        self.floater.setPos(self.node.getPos())




    def Up(self, dt):
        self.futurepos.x -= self.speed * dt


    def Down(self, dt):
        self.futurepos.x += self.speed * dt



    def Left(self, dt):
        self.futurepos.y -= self.speed * dt
        self.lastDelta = [-1,0]


    def Right(self, dt):
        self.futurepos.y += self.speed * dt
        self.lastDelta = [1, 0]

    def top(self, dt):
        self.futurepos.z += self.speed * dt

    def bottom(self, dt):
        self.futurepos.z -= self.speed * dt


    def beep(self,duduh):
        self.swoosh.YeetEnemy()

    def boop(self, b):
        pass



    #DEBUG METHODS
    def changecNode(self,tup):
        x,y,z = tup
        x = x/10
        y = y/10
        z = z/10









and thats the storage of objects in the class and the nodename gen

from panda3d.core import CollisionNode, CollisionPolygon, CollisionHandlerPusher, CollideMask, CollisionHandlerQueue, \
    CollisionBox, CollisionSphere
from reference import REFERENCE


class Box():
    BOXES = {}
    speed = 0
    NAMEID = 'box'

    @classmethod
    def getInstances(cls,id):
        if id == -1:
            return cls.BOXES
        else:
            try:
                return cls.BOXES[id]
            except Exception as e:
                REFERENCE.ReturnExceptionPretty(e)


    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls)
        cls.BOXES[len(cls.BOXES)] = obj
        return obj



    def __init__(self,where,ctrav):
        self.id = self.getId()
        self.model = loader.loadModel(REFERENCE.loadTexturePath('Box_2.bam'))
        self.node = render.attachNewNode(f'{self.NAMEID}[{self.id}]')
        x, y, z = where
        print(x,y,z)
        self.model.setPos(x, y, z)
        self.node.setPos(x, y, z)
        self.model.reparentTo(self.node)
        self.cNode = CollisionNode(f'collision_{self.NAMEID}[{self.id}]')
        self.cNode.setFromCollideMask(CollideMask.bit(0))
        self.cNode.setIntoCollideMask(CollideMask.bit(0))
        self.cNodeP = self.model.attachNewNode(self.cNode)
        self.cNodeP.setPos(render,x,y,z)
        self.cNode.add_solid(CollisionBox((x,y,z), 1, 1, 1))
        self.qq = CollisionHandlerQueue()
        ctrav.addCollider(self.cNodeP, self.qq)
        #self.cNodeP.show()

        self.node.setScale(4,4,4)

        print(self.cNodeP.getPos()-self.model.getPos(),self.cNodeP.getPos()-self.node.getPos())



    def DBG(self,tup):
        x,y,z = tup

        self.node.setHpr(x,y,z)



    def getId(self):
        for k,v in self.BOXES.items():
            if v == self:
                return k



and thats the main loop

from panda3d.core import Vec3, CollisionHandlerPusher, CollisionHandlerQueue, AmbientLight
from panda3d.bullet import BulletWorld
from direct.showbase.ShowBase import ShowBase, CollisionTraverser
from panda3d.core import MouseWatcher
from panda3d.core import WindowProperties
from Char_spherecollision import Char
from dafollowcam import FollowCam
from cone import cone
from map import Map
from test_ground import test_ground
from wall import wall
from Box import Box
import cv2
winder = cv2.namedWindow('oooooooooo')

def noting(nut):
    pass


cv2.createTrackbar('x','oooooooooo',30,100,noting)
cv2.createTrackbar('y','oooooooooo',0,100,noting)
cv2.createTrackbar('z','oooooooooo',10,100,noting)






class MyApp(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)
        self.dx,self.dy = 0,0
        # Showbase augmentations
        self.disable_mouse()
        self.taskMgr.add(self.update, 'update')
        self.setBackgroundColor(0,0,220,1)
        self.ambientLight = AmbientLight("ambientLight")
        self.ambientLight.setColor((15, 3, 3, 7))
        render.setLight(render.attachNewNode(self.ambientLight))


        #Usefull objects monitor etc
        self.cTrav = CollisionTraverser()
        self.pusher = CollisionHandlerPusher()
        self.queue = CollisionHandlerQueue()
        self.rat = MouseWatcher()
        self.winder = WindowProperties()
        self.winder.setCursorHidden(True)
        self.winder.setMouseMode(WindowProperties.M_relative)
        self.win.requestProperties(self.winder)
        self.pusher = CollisionHandlerPusher()


        # events listeners
        self.accept("a", self.UpdateKeyMap, ["left", True])
        self.accept("a-up", self.UpdateKeyMap, ["left", False])
        self.accept("d", self.UpdateKeyMap, ["right", True])
        self.accept("d-up", self.UpdateKeyMap, ["right", False])
        self.accept("w", self.UpdateKeyMap, ["up", True])
        self.accept("w-up", self.UpdateKeyMap, ["up", False])
        self.accept("s", self.UpdateKeyMap, ["down", True])
        self.accept("s-up", self.UpdateKeyMap, ["down", False])
        self.accept("q", self.UpdateKeyMap, ["shoot", True])
        self.accept("q-up", self.UpdateKeyMap, ["shoot", False])
        self.accept("e", self.UpdateKeyMap, ["not shoot", True])
        self.accept("e-up", self.UpdateKeyMap, ["not shoot", False])
        self.accept("z", self.UpdateKeyMap, ["top", True])
        self.accept("z-up", self.UpdateKeyMap, ["top", False])
        self.accept("x", self.UpdateKeyMap, ["bottom", True])
        self.accept("x-up", self.UpdateKeyMap, ["bottom", False])
        self.MsX,self.MsY = None,None

        #keymap
        self.keyMap = {
            "up": False,
            "down": False,
            "left": False,
            "right": False,
            "shoot": False,
            'not shoot': False,
            'top': False,
            'bottom': False
        }



        # object instanciation
        #self.map = Map((0,0,0),self.cTrav,self.queue)
        self.grnd = test_ground(self.cTrav,self.queue)
        #self.cones = []



        #a,b,c = 0,0,0
        #for i in range(5):
        #    a = a + 4
        #    placeholder = cone((a,b,c),self.cTrav)
        #    self.cones.append(placeholder)

        self.boxes = []

        a, b, c = 0, 0, 1
        for i in range(5):
            a = a + 3
            placeholder = Box((a, b, c), self.cTrav)
            self.boxes.append(placeholder)


        self.character = Char(self.cTrav,self.pusher)


        self.FCAM = FollowCam(self.cam,self.character.floater,self.cTrav, self.character.model)


        #print(render.ls())

        #print(self.character.qq,self.cones[0].qq,self.FCAM.qq)


    def UpdateKeyMap(self, key, state):
        self.keyMap[key] = state




    def update(self,task):


        dt = globalClock.getDt()


        if self.mouseWatcherNode.hasMouse():
            self.MsX = ((base.mouseWatcherNode.get_mouse_x() + 1) / 2) * base.win.get_x_size()
            self.MsY = self.mouseWatcherNode.getMouseY()
            self.size = self.get_size()
            self.dx,self.dy = int(self.size[0]/2) - self.MsX, int(self.size[1]/2) - self.MsY



        x,y,z = cv2.getTrackbarPos('x','oooooooooo'),cv2.getTrackbarPos('y','oooooooooo'),cv2.getTrackbarPos('z','oooooooooo')
        if x != 0 and y != 0 and z != 0:
          for box in self.boxes:
              box.DBG((x,y,z))


        self.character.Update_state(self.keyMap, dt, (self.dx,self.dy))
        self.FCAM.update()




        if self.keyMap['shoot']:
            pass
        return task.cont



app = MyApp()
app.run()

Also, yes ik the gravity (collision ray) is broken, please don’t point that out (its like salt in my eyes). i try to figure it out but sadly so far no good.

One way to do this, I believe, is to store a reference to the logical representation of the object (e.g. the player-class instance) in the “Python tag” of the NodePath in question. Something like this:

## When initialising your game-object class:
class MyChar():
    def __init__(self):
        # Initialisation here

        self.colliderNP = #... initialisation of collider node here...

        # Now store a reference to this object--i.e. "self"--
        # in the Python tag of the collider NodePath
        self.colliderNP.setPythonTag("owner", self)


## ELSEWHERE, possibly in another class:

    def onCollisionEvent(self, entry):
        intoNP = entry.getIntoNodePath()

        # We may not know whether we hit something
        # that corresponds to something that has such a tag.
        # Perhaps we just hit a wall! So let's check, first.
        if intoNP.hasPythonTag("owner"):
            
            # Now get the logical representation from the tag.
            gameObj = intoNP.getPythonTag("owner")
            # And then do something with it, presumably.

However, there is a caveat: Since the NodePath is stored in the game-object instance, and the game-object instance is now stored in the NodePath, we’ve created a circular reference. This can interfere with garbage collection, and thus the freeing up of memory. Thus it may be important to remember to clear the Python tag when you’re cleaning up your game-object instances!

Something like this:

        # In your game-object class's cleanup code:
        self.colliderNP.clearPythonTag("owner")

Finally, it can be easy to make little mistakes with repeated strings like “owner”, so I recommend storing the string in an easily-accessible variable (perhaps in a common file), and then just referencing that. Something like this:

OWNER_TAG = "owner"

#ELSEWHERE, perhaps in another file (after importing the above):

        self.colliderNP.setPythonTag(OWNER_TAG, self)

This means that mistakes should be picked up by the interpreter as mistakes, and thus not leave problems lurking in your program!

All that said, if I may, I have a “beginner’s tutorial” that may perhaps be of use to you. It covers a lot of the fundamentals of using the engine (including simple collision and this very topic), building up a simple game from first pretty-much principles all the way to generating a stand-alone distributable.

If you’re interested, you should find it here:

Thanks, that’s really neat of you to give me that guide. Ill try to get as much out of it as i can. Bout the tags though… tags are like forcing an attribute on a node? where first arg is its id and second the attribute?

1 Like

It’s my pleasure; I hope that you find it useful. :slight_smile:

Indeed, yes.

It’s pretty much the same as a Python “dict”: a key-value pair, with the key (“owner” in my example above) being used to access the associated value (the reference to the instance in my example above).

(I wouldn’t say that one is forcing an attribute on the node, just storing some arbitrary data in a way that NodePath is designed to do.)