Target selection

Greetings,

I am fairly new to Panda3d and I’ve just finished going through all of astelix’s “Panda3d Collisions made simple” examples. I have used them to setup an environment/floor&wall collisions/basic actor movement/camera movement. I create an “enemy” on the map, but I’ve come to a problem when I tried to make a target selection system.

My first idea was to create a semicircular CollisionPlane centered on the player extending out using the camera’s heading and then pivoting it clockwise until it hit an enemy CollisionSphere. Being able to define the shape/size of the plane I could restrict the range with which a player could target objects. After I messed around with adding the CollisionPlane for a while, I came to the conclusion that the plane will always be a rectangle of the same size. I then abandoned this notion.

Next, I tried using isInView. I envisioned grabbing all of the “enemyTargetNodes” that were in the camera’s view and then (just to have a simple target selection) select the closest enemy then when nextTarget() is called the next closest enemy is selected, and on…
I attached a CollisionNode with a generic ‘TargetCollider’ tag to the “enemy.” I wanted to be able to have different types of enemies all using the same target tag name instead of having to check to see if it saw EnemyType1 or EnemyType2… or EnemyType 9001. Using one “enemy” as a test, I had a "Target: " onscreentext update when I would execute the target selection. It displayed “Target: TargetCollider” which was a start, but now I cannot figure out/think up a way to access the attributes/methods within the Enemy class. getParent() returns the ActorNode’s Armature which wasn’t really any help in my situation.

Is there an easier way of going about making a targeting system?
Are there any real “tutorials” for Panda3d out there or should I just continue to deconstruct the examples of others to get a greater understanding?

Edit: Tutorials aside from the manual, that is. More “hands- on” type examples.

I decided to scrap all of my previous coding attempts and start over using a CollisionPolygon and pivot it around the player clockwise for nextTarget() and counterclockwise for prevTarget().

I’m currently planning the pseudo-code, but I am still uncertain how to access the attributes of the target after a successful collision of my targeting system.

I guess I’ll try to explain my stupid question as simply as I can to avoid any confusion that my lack of experience with Panda3d might pose:
The player class has the CollisionPolygon that will be checking for Into collisions. The NPC class has the CollisionSphere that will be detected. When the NPC target collision occurs, how do I go about accessing attributes from the NPC class via the enemy CollisionSphere?

I have a World class, a Player class, and an NPC class. Does the World class need to be a conduit for the access or can the Player class directly query/alter attributes of the NPC class?

tldr: How do I go about accessing attributes from the NPC class via the enemy CollisionSphere?

Hello,
There are many ways to achieve what you want to do. Try looking at the clicking on 3d objects section of the manual. That particular method uses NodePath tags to identify typical nodepaths and in turn you can use those identifications to call the correct class or look the correct class up in a dictionary that you have access too.

Again, this is just one way to doing it. Hope that helps.

First of all, thank you for the response. This looks like the answer to my dilemma

A few quick questions:
So I will need to set a tag on each instance of my NPC class and then when a target collision occurs search through the node paths via findNetTag(‘targetTag’) to gain direct access to the target NPC instance handle?

Then I can use the “pickedObj”(from clicking on 3d objects section of the manual) handle to directly access the NPC instance variables/methods from functions within my Player class instance… in theory?

You pretty much got it. However once you do a findNetTag it should return the NodePath that has that tag already. To gain access to the NPC class that you have all you need do is look it up in a dictionary that you keep around somewhere.

Python’s dictionary lookups are quite fast. So you can basically have something like in the NPC creation phase:

self.myNPCsDictionary[uniqueID] = myNPCInstanceClass
myNPCNodePath.setTag("NPC",uniqueID)

This way in your collider handle you can just have something like:

currentIstance = self.myNPCsDictionary[pickedObj.findNetTag("NPC").getTag("NPC")]

Or you can just even use the NodePath itself as a key so then it would be

self.myNPCDictionary[NPCNodePath] = myNPCClassInstance

and

currentInstance = self.myNPCsDictionary[pickedObj.findNetTag("NPC")]

I’m learning python as I go as well. I was a CSC major for the first two years of my collegiate career, but I’m still a bit rusty since I haven’t programmed much aside from macro bots in the past 5 years. I was not aware of python’s dictionary mapping.

So the dictionary would just keep track of each individual NPC instance and give them a unique ID as a point of reference… That makes sense.

I would probably want to keep the dictionary in the instance of my World class (game environment) and then have it assign/reuse unique ID’s as a new NPC instance is spawned, and have it “pop” them out as they are destroyed.

Thank you again for your help.

Am I correct in assuming that I will not be able to properly implement a CollisionPolygon pivoting around the player looking for collisions with NPC CollisionSpheres?

within NPC class:

        #Used to target the enemy
        self.targetNode = self.chaser.attachNewNode(CollisionNode("targetNode"))
        self.targetNode.node().addSolid(CollisionSphere(0, 0, 10, 1))
        self.targetNode.node().setFromCollideMask(BitMask32.allOff())
        self.targetNode.node().setIntoCollideMask(Game.world.enemyMask)

within Player class:

...
        #Used as collision solid for target detection
        self.targetPolygon = self.floater.attachNewNode(CollisionNode("targetPolygon"))
        self.targetPolygon.node().addSolid(CollisionPolygon(Point3(0, 0, -60), Point3(0, 0, 60), Point3(0, -60, 60), Point3(0, -60, -60)))
        self.targetPolygon.node().setFromCollideMask(Game.world.enemyMask)
        self.targetPolygon.node().setIntoCollideMask(BitMask32.allOff())
...
base.cTrav.addCollider(self.targetPolygon, self.targetHandler)
...
    def targetTask(self, task):
        #No target and not searching for a target
        #The targetPolygon will lookAt(targetFloater)
        #keeping the targetPolygon in line with the view and opposite the player
        #ready to pivot clockwise or counterclockwise from the viewer's perspective
        if (self.hasTarget == False and self.nextTargetSearch == False and self.prevTargetSearch == False):
            self.targetFloater.setPos(base.camera.getPos())
            self.targetFloater.setZ(self.floater.getZ())
            self.targetPolygon.lookAt(self.targetFloater)
        
        
        #Player has a target
        #The targetPolygon will lookAt(targetFloater)
        #keeping the targetPolygon in line between the player and the target
        #ready to pivot clockwise or counterclockwise with respect to the targets position
        elif (self.hasTarget == True):
            self.targetFloater.setPos(self.target.getPos())
            self.targetFloater.setZ(self.floater.getZ())
            self.targetPolygon.lookAt(self.targetFloater)
        
        
        #Player is searching for the next target (clockwise)
        elif (self.nextTargetSearch == True):
            if (self.i < 36):
                #pivot targetPolygon 10 degrees
                self.targetPolygon.setH(self.targetPolygon.getH() - 10)
                for x in range(self.targetHandler.getNumEntries()):
                    entry = self.targetHandler.getEntry(x)
                    print str(entry.getIntoNode().getName())
                    if (entry.getIntoNode().getName() == "targetNode"):
                        print "Target Collision"
                        self.hasTarget = True
                        return task.cont
                self.i = self.i + 1
            #targetPolygon has pivoted 360 degrees and found nothing, flag search is over
            if (self.i == 36):
                self.nextTargetSearch = False
        
        
        #Player is searching for the previous target (counterclockwise)
        elif (self.prevTargetSearch == True):
            if (self.i < 36):
                #pivot targetPolygon 10 degrees
                self.targetPolygon.setH(self.targetPolygon.getH() + 10)
                for x in range(self.targetHandler.getNumEntries()):
                    entry = self.targetHandler.getEntry(x)
                    print str(entry.getIntoNode().getName())
                    if (entry.getIntoNode().getName() == "targetNode"):
                        print "Target Collision"
                        self.hasTarget = True
                        return task.cont
                self.i = self.i + 1
            #targetPolygon has pivoted 360 degrees and found nothing, flag search is over
            if (self.i == 36):
                self.prevTargetSearch = False
        
        
        #Reset targetPolygon heading so it doesn't continue to increase to stupid amounts
        if (self.targetPolygon.getH() > 360 or self.targetPolygon.getH() < -360):
                self.targetPolygon.setH(0)
                
        return task.cont

The idea was to easily restrict the distance that a player could target objects/NPCs by creating the targeting collision solid as a polygon of x length,width, height and at the same time being able to select objects/NPCs based on their spatial relationship to each other with respect to the player’s character model.

My other idea is to create a lens and a CollisionLineSegment and attaching it to the player and pivoting that around. I would use CollisionTubes aligned vertically(so height doesn’t play too much of a role) on objects/NPCs.

Is it possible to salvage my current system or should I augment it to work with Lenses, LineSegments, and CollisionTubes?

I went ahead and scrapped the idea of using a CollisionPolygon and started implementing the CollisionSegment approach.

I have been able to set a target from my npcDictionary and query it for attributes.

Now to tweak it for efficiency before moving on…