Python Script: Track to nearest object with prop:enemy

Here’s 2 scripts that track to the nearest object with property:enemy, useful for homing missles and whatever else you can come up with.

1.Give each enemy a property:enemy
2.Give the tracking object these logic bricks:
Always—>python:trackToClosest—>EditObject:TrackTo, named “trackTo”
--------------------------------------------------->Any Positive-Y motion, set to Local
3. Use what ever method you prefer for creating projectiles and damage. I’m currently using a missle with no collision and a ray that checks in front for prop:enemy. If the ray is positive, it ends the missle object and inflicts damage on the target.

Demo file:
http://christopherschons.com/temp/blender/trackToNearestEnemy.blend

  1. Create a list of all the enemies within range 100 of the player.

import GameLogic as g
cont = g.getCurrentController()
own = cont.getOwner()
scene = g.getCurrentScene()
objList = scene.getObjectList()

#create a list of all enemies within range 100
#change this number to best fit your game area
g.enemyList=[]
for i in range(0,len(objList)):
    obj = objList[i]
    if hasattr(obj,"enemy"):
        if own.getDistanceTo(obj) <100:
            g.enemyList.append((obj.name))
    
  1. Create a list of distances to enemies, relative to the script owner, then sorts the list. enemyRanges[0] is the enemy with the smallest distance from the object.

import GameLogic as g
cont = g.getCurrentController()
own = cont.getOwner() 
objList = g.getCurrentScene().getObjectList()

track = cont.getActuator("trackTo")
motion = cont.getActuator("motion")


#check to see if there are any enemies within range
if len(g.enemyList) > 0:

    #create a list of the enemies within range and their distances
    #each list item has 2 parts: (distance, OBname)
    enemyRanges = []        
    for i in range(0,len(g.enemyList)):
        enemy = objList[g.enemyList[i]]
        dist = own.getDistanceTo(enemy)
        enemyRanges.append((dist,enemy.name))

    #sort the list. the first object will be the one with the shortest distance
    enemyRanges.sort()

    #get the second item (OBname) of the first list item, then strips 'OB' from the string
    nearestEnemy = enemyRanges[0][1][2:]

    #set your object to track to the nearestEnemy
    #the name returned is "OBenemy", but the trackTo actuator just needs "enemy"
    #the [2:] removes the "OB" from the object name for proper tracking
    track.setObject(nearestEnemy)
    
    #activate the actuators
    g.addActiveActuator(track,1)
    g.addActiveActuator(motion,1)    
    
else:
    #if there are no enemies, activate only the motion actuator
    g.addActiveActuator(track,0)
    g.addActiveActuator(motion,1)        

Thanks, man! That’s good stuff. Did you write it your self?

Nice script! Thank you very much for sharing this = )

Glad you like it. I was testing it and it runs much better with g.enemyList moved to a separate globals script. Also, it’s much smoother if you set the always pulse to f:8 or so, so it’s not constantly firing. There’s still some errors I have to work out, and I’m going to tweak it for my game so it only creates a list of the objects on screen, to help cut down on overhead.

If there wasn’t for this pesky “de-perent” bug in 2.43, you could just use my sonar method:

http://socialstorage.googlepages.com/AI_Coin.blend

^
Only works with 2.42 now.

For once, I would like to download a new blender version, with things just added on, and everything else left working as before.

It’s really getting annoying.

I’m having problems connecting to the site with the blendfile on… thanks for the script! did you see the modified version I made of your alpha fade script?

http://blenderartists.org/forum/showthread.php?t=90773

Nice script Skullbunny :slight_smile:

got it now… dunno why but it was just hanging earlier! thanks

Social:
I definitely agree with you on the bugs in new versions. I don’t mind it much when I compare the performance of my games from 2.42 to 2.43, because it’s a drastic improvement, but I’m really annoyed at some of the add object bugs I’ve been coming across. I was originally going to use your sonar setup as a basis for homing, but now that I know python I try and get rid of as much logic as physically possible, while still keeping things modular.

Kirado:
Glad you got the alpha script working. That method is what i thought of trying next, but never got around to it.

I optimized the tracking script to run better. It’s split into to scripts, one that declares a global enemy list, and one that goes on any object you want to track to enemies. I also move the motion actuator into the script.

I got it working well with multiple tracking missles and multiple enemies. The missles still behave a bit wonky if they are equal distances from enemies. I’m not sure what to do about that, but I’ll look into it in the future. Also, the script doesn’t work if your adding enemies with an addObject actuator. The enemies have to be in the scene from the start. I’ll probably make a separate script some time that deals with that at some point.

  1. Create a list of all the enemies on the screen. Change “20” to a bigger number if you have a bigger game area.

import GameLogic as g
cont = g.getCurrentController()
own = cont.getOwner()
scene = g.getCurrentScene()
objList = scene.getObjectList()

g.enemyList=[]
for i in range(0,len(objList)):
    obj = objList[i]
    if hasattr(obj,"enemy"):
        if own.getDistanceTo(obj) <20:
            g.enemyList.append((obj.name))
  1. Create a list of distances to enemies, relative to the script owner, then sorts the list. enemyRanges[0] is the enemy with the smallest distance from the object.

import GameLogic as g
cont = g.getCurrentController()
own = cont.getOwner() 
objList = g.getCurrentScene().getObjectList()

track = cont.getActuator("trackTo")
motion = cont.getActuator("motion")

enemyRanges = []        
for i in range(0,len(g.enemyList)):
    enemy = objList[g.enemyList[i]]
    dist = own.getDistanceTo(enemy)
    enemyRanges.append((dist,enemy.name))
    
if len(g.enemyList) > 0:
    enemyRanges.sort()
    nearestEnemy = enemyRanges[0][1][2:]
    track.setObject(nearestEnemy)
    g.addActiveActuator(track,1)
    g.addActiveActuator(motion,1)    
else:
    g.addActiveActuator(track,0)
    g.addActiveActuator(motion,1)        

Thank, this is really cool. It seems to work to, this is great! It is just what I needed for my game.

I did notice one error. If the script is used, and the object you are homing is destroyed, blender crashes. Not a big deal. I can fix it with more python to turn the script off.

Yeah, I found that bug too, but I think the newer script I posted fixes it. I had to make sure the pulse was set to 0 so there aren’t times where it tries to track to an object that’s not there. I’ll have to update the first post and demo file.

I updated the script, which should now be bug free. I also added a better demo file if anyone wants to check it out. There’s a simple damage script in there if anyone needs it.

Thank you!!! I love seein you wizards do this stuff and then figuring out other uses. My son just asked for a directional arrow on screen to show the nearest exit/teleporter. This script gives me the method to find the nearest exit/teleporter and I would never have been able to code this - Thank you!!! (pause, feel the love, relish, resume).

Big piece of the puzzle solved. Now all I need to do is hunt for code that updates an arrow’s TrackTo constraint (the arrow is floating just in front of the camera, bottom left hand corner) and let blender spin it to point to it. I guess I need to be able to set the Ob: field in the TrackTo constraint for the arrow to …hmmm…nearestEnemy.name()? So fun figuring this out!!! Thanks again. And in the else set arrow.alpha? to be 0, and then if there is a nearest enemy in the len(list)>0, set the alpha based on distance/100 or whatever the max is, so the arrow gets “clearer” as you get closer to the teleporter. is that the way you wizards would do it?

I was planning on making an arrow that fades out as you near a point, so I gave it a shot. This example has the arrow above the player. Not sure how you’d move it to the side because there’s a tracking/parenting bug.

http://christopherschons.com/temp/blender/arrowPointerFadeOut.blend

Sorry to report that my game still crashes when an enemy is destroyed. I also found that no matter what I did, I couldn’t turn the script “off” after it is activated.

That’s odd. Does the new demo file crash, or is it your own game incorporating the script? Also, do you have the pulse on the script set to something besides 0? That’s the only thing that I’ve found causes a crash.

I tested it by essentially killing objects as they were being homed in on, and it worked fine, even with a lot of missles active. Can you post a blend with the problem?

Your blend worked well, but when I tried to apply it in my game it would crash. I had the pulse set at 0, I’ll try it now to see if that helps.

No, it still crashes. I think it has something to do with the way your bullets are created. I am not applying the script to missles but to the character itself, this could be one problem. I am also using blender 2.42a.

hi skullbunny ! do you how I could use your code to get the nearest object but also the one which have a property equal to 1 ?

thanks

Can you make the scirpt updated to 2.49b?

Shouldn’t the script already work in 2.49b?
Warnings are only warnings, you should worry when you get errors.

Anway, the below is a condensed version for 2.49b, just put this script onto the object that you want to do the tracking and make sure there are some objects with an ‘enemy’ property in the scene.

import GameLogic

cont = GameLogic.getCurrentController()
own = cont.owner

# Construct a list containing all objects in the scene with an 'enemy' property
obj_list = []
for obj in GameLogic.getCurrentScene().objects:
    if obj.has_key('enemy'):
        obj_list.append(obj)

# Sort the list and select closest object
if len(obj_list) != 0:
    target = sorted(obj_list, key=lambda obj: obj.getDistanceTo(own))[0]

    # Align object to closest enemy
    own.alignAxisToVect(own.getVectTo(target)[1], 1, 0.1)

Also, its generally forum etiquette to not bump threads that are really old.