I have something vaguely similar. In my game I have red ghosts (PacMan-style) who try and pursue the player, and I have a functionning pathfinding algorithm for this. However, at the moment the ghosts don’t stop when they reach the player; they run straight through, then turn around and run through the player again (stopping isn’t important, because eventually the player is supposed to die immediately if touched by a ghost). However, the ghosts only charge the player if the player is within their detection range, which I represent with a collision sphere. I’ll post the relevant code, though mind that the indentation is off, because the code is within a class’ method.
First, this is the collision sphere:
#This is the collision sphere that will detect the player,
#and help the ghost run away if the player gets too close.
self.hunterHunt = self.hunter.attachNewNode(CollisionNode("hunterHunt"))
self.hunterHunt.node().addSolid(CollisionSphere(0, 0, 0, 25))
#Here we enable both From and Into collision masks on the ghosts, because, on the one hand,
#they have to be stopped by walls; on the other hand, they also have to be collided into
#by blasts, which are meant to destroy them.
self.hunterHunt.node().setFromCollideMask(world.huntMask)
self.hunterHunt.node().setIntoCollideMask(BitMask32.allOff())
[...]
self.hunterFleeing = CollisionHandlerQueue()
base.cTrav.addCollider(self.hunterHunt, self.hunterFleeing)
This is the code, in a separate Move task that is constantly run, which makes the ghost turn to face the player. This will probably be of the most use to you. I use trigonometry to calculate the heading from the ghost to the player, then give the ghost that heading.
if self.isHunting == False:
for i in range(self.hunterFleeing.getNumEntries()):
entry = self.hunterFleeing.getEntry(i)
if (entry.getIntoNode().getName() == "pacmanHunt"):
self.isHunting = True
self.pacPos = entry.getSurfacePoint(self.hunter)
self.ghostPos = self.hunter.getPos(self.hunter)
self.huntVec = self.pacPos - self.ghostPos
self.huntHeading = math.degrees(math.atan2(self.huntVec.getY(), self.huntVec.getX())) + 90
if self.huntHeading < 0:
self.huntHeading += 360
self.hunter.setH(self.hunter, self.huntHeading)
taskMgr.doMethodLater(0.5, self.keepHunting, "keepHunting")
This is the “keep hunting” task, which reenables the pursuing behavior after a half second.
def keepHunting(self, task):
self.isHunting = False
And then I use this line in the task to move the ghost forwards:
self.hunter.setY(self.hunter, -10 * globalClock.getDt())
There are a lot of other elements in the method for movement, such as randomly changing directions every 5 seconds, and avoiding walls, but I figure that this is most interesting for you. If you want, I can just post the entire class, but a lot of the code might be tailored to the specific game I have in mind, and might not be suitable to your needs.
I hope this helps!