path follow random

Here is what i copied and pasted off youtube.
How would i make the npc pathfollow to each waypoint for a certain amount of time?

import bge
import random
def main():
    cont = bge.logic.getCurrentController()
    own = cont.owner
    scene = bge.logic.getCurrentScene()
    one = scene.objects["one"]
    two = scene.objects["two"]
    three = scene.objects["three"]
    four = scene.objects["four"]
    listObjects = [one,two,three,four]
    pathFollow = cont.actuators["pathFollow"]
    choice = random.choice(listObjects)
    pathFollow.target = choice
    cont.activate(pathFollow)
main()


from random import randint

#get navmesh from connected steering actuator 
If "navMesh" not in own:
    own["navMesh"] = cont.actuators["Steering"].navmesh


#if you do not have a destination get one
if "Path" not in own:
    Possible =[] 
    #lookup possible destinations
    for object in scene.objects:
        if "destination" in object:
            Possible.append(object)
    #choose a random destination 
    X = randint(0,len(Possible) - 1)
    Target = Possible[X] 
    
    #This is used if recalculate path failed 
    if "Target" in own:
         
        Target = own["Target"]
    #get path
    P = own["navMesh"].findPath(Target.worldPosition, own.worldPosition) 
    #make sure there is a valid path
    if P! = None:
        if len(P)>0:
            # save patb and target
            own["Path"] = P
            own['Target'] = Target


#navigate to destination

else:
    if bge.logic.getRandomFloat()>.875:
         #randomly recalculate path 
         P = own["navMesh"].findPath(Target.worldPosition, own.worldPosition) 
        #make sure there is a valid path
        if P! = None:
            if len(P)>0:
                # save patb and target
                own["Path"] = P
                own['Target'] = Target
            else:
                 del own["Path"] 
    if len(own['Path']) >=1:
        P = own["Path"] [0].copy()
        P.z = own.worldPosition.z
        V2 = own.getVectTo(P)
        if V2[0]>2:
           own.alignAxisToVect(V2[1],0, .1)
           own.alignAxisToVect([0,0,1],2,1)

            if own.localLinearVelocity.x<5:
                own.applyForce(V2[1]*70)
            else:
                own.localLinearVelocity.x*=.95
            own.localLinearVelocity.y*=.5

        else:
             own["Path"].pop(0)
    else:
        del own['Path'] 
        del own["Target"] 

This code is supposed to main.

This code searches for objects, places them in a list and picks a random object out of the list. Then it sets it as target of an actuator (like steering actuator) and activates the actuator.

This is a one time operation. You do it once every time you want to set a new target.

This is outside of this code. There are many solutions. Here is one:

After running the code (triggered the python controller) you set a timer property to whatever time you want (e.g. -5 seconds). As timer property it get s updated automatically at each single frame. Therefore you do not need to do that by yourself.

Sense if the property value is above zero (means 5 seconds since random selection passed). Then you trigger the code again. Do not forget to set the timer property too.

This all can be done with logic bricks very easily.

The code is not complete. As it is it is supposed to run once with an always or delay sensor. To be used in general it needs to check the sensor states. If it does not it will run at least twice: the first time when a sensor evaluates positive and again when a sensor evaluates not positive (after it was positive).


import bge
import random

controller = bge.logic.getCurrentController()
if all(sensor.positive for sensor in controller.sensors):
    scene = bge.logic.getCurrentScene()

    objectNames = ["one", "two", "three", "four"]
    selectedObjectName =  random.choice(objectNames)

    selectedObject = scene.objects[selectedObjectName]

    pathFollowActuator = controller.actuators["pathFollow"]
    pathFollowActuator .target = selectedObject 

    controller.activate(pathFollowActuator )


  • The code behaves like an AND controller.
  • You do not need the owner of the python controller. It is nor used anywhere.
  • You do not need four separate searches for objects as you want just one. So this is reduced to a single search.
  • “main” is the most useless name for a bge function ever. [sub]It should not be used within the BGE. Functions called “main” are conventional reserved names in other programming languages (such as Java, C, C++ and more). Such functions are used in a complete different context which does not apply to Blender, the BGE or Python at all. To avoid confusion this name should not be used. Better use a name that describes what the code is supposed to do (e.g. selectTarget). This helps understanding the purpose of the code.[/sub]
  • As this runs in script mode you do not need a function at all. [sub]The script mode forces you to write the function first and call it later (very later). This is like reading a book, jump to the end (or somewhere inbetween and jump to the first section. This is hard to read. [/sub]

As you are not that firm with coding here is a version that randomly selects from objects with the property “target”:


import bge
import random

controller = bge.logic.getCurrentController()
if all(sensor.positive for sensor in controller.sensors):
    scene = bge.logic.getCurrentScene()

    availableTargets = [object for object in scene.objects
                        if "target" in object]
                        
    selectedTarget =  random.choice(availableTargets)

    pathFollowActuator = controller.actuators["pathFollow"]
    pathFollowActuator.target = selectedTarget 

    controller.activate(pathFollowActuator )
    print("target is", pathFollowActuator.target)

ask, and ye shall receive.



import bge


from random import randint


def main():
    
    cont = bge.logic.getCurrentController()
    own = cont.owner
    
    #get navmesh from connected steering actuator 
    if "navMesh" not in own:
        own["navMesh"] = cont.actuators["Steering"].navmesh




    #if you do not have a destination get one
    if "Path" not in own:
        Possible =[] 
        #lookup possible destinations
        for object in own.scene.objects:
            if "destination" in object:
                Possible.append(object)
        #choose a random destination 
        X = randint(0,len(Possible) - 1)
        Target = Possible[X] 
        
        #This is used if random recalculate path failed last time
        if "Target" in own:
            Target = own["Target"]
        #get path
        P = own["navMesh"].findPath(Target.worldPosition, own.worldPosition) 
        #make sure there is a valid path
        if P!= None:
            if len(P)>0:
                # save patb and target
                own["Path"] = P
                own['Target'] = Target




    #navigate to destination


    else:
        if bge.logic.getRandomFloat()>.975:
            #randomly recalculate path
            P = own["navMesh"].findPath(own['Target'].worldPosition, own.worldPosition) 
            #make sure there is a valid path
            if P!= None:
                if len(P)>0:
                    # save patb and target
                    own["Path"] = P
                    
                else:
                     del own["Path"] 
        index = 0
        #Debug Path
        if own['Debug']==True:
            for point in own['Path']:
                if index==0:
                    p1 = own['Target'].worldPosition.copy()
                    p1.z =own.worldPosition.z
                else:
                    p1 = own['Path'][index-1].copy()
                    p1.z = own.worldPosition.z
                p2 = own['Path'][index].copy()
                p2.z = own.worldPosition.z
                bge.render.drawLine(p1,p2,(1,0,0)) 
                index+=1   
                                 
        if len(own['Path'])>=1:
            P = own["Path"][-1].copy()
            P.z = own.worldPosition.z
            V2 = own.getVectTo(P)
            
            if V2[0]>1:
                own.alignAxisToVect(V2[1],0, .1)
                own.alignAxisToVect([0,0,1],2,1)
                if own.localLinearVelocity.x<2:
                    own.applyForce(V2[1]*20)
                else:
                    own.localLinearVelocity.x*=.5
                own.localLinearVelocity.y*=.25


            else:
                
                own["Path"].pop(-1)
        else:
            del own['Path'] 
            del own["Target"] 
main()
        

Attachments

Traveler.blend (457 KB)

1 Like

advanced AI with obstacle avoidance

edit : noticed if the agent was too close to a target, the path was always None in the recalculate path section
fixed it with a if None path -> if too close -> del target and del path


import bge
from mathutils import Vector
from random import randint


def main():
    
    cont = bge.logic.getCurrentController()
    own = cont.owner
    
    #get navmesh from connected steering actuator 
    if "navMesh" not in own:
        own["navMesh"] = cont.actuators["Steering"].navmesh




    #if you do not have a destination get one
    if "Path" not in own:
        Possible =[] 
        #lookup possible destinations
        for object in own.scene.objects:
            if "destination" in object:
                Possible.append(object)
        #choose a random destination 
        Target =None
        
        #This is used if recalculate path failed 
        if "Target" in own:
             
            Target = own["Target"]
        else:
            X = randint(0,len(Possible) - 1)
            Target = Possible[X]     
        #get path
        P = own["navMesh"].findPath(Target.worldPosition, own.worldPosition) 
        #make sure there is a valid path
        if P!= None:
            if len(P)>0:
                # save patb and target
                own["Path"] = P
                own['Target'] = Target
                




    #navigate to destination


    else:
        
        if own.getDistanceTo(own['Target'])>2.5:
            if bge.logic.getRandomFloat()>.975:
                
                P = own["navMesh"].findPath(own['Target'].worldPosition, own.worldPosition) 
                #make sure there is a valid path
                if P!= None:
                    if len(P)>0:
                        # save patb and target
                        own["Path"] = P
                        
                    else:
                         del own["Path"] 
                else:
                     if own.getDistanceTo(own['Target'])<2.5:
                         
                         del own['Target']
                         del own['Path']
                             
                         
            index = 0
            #Debug Path
            if own['Debug']==True:
                for point in own['Path']:
                    if index==0:
                        p1 = own['Target'].worldPosition.copy()
                        p1.z =own.worldPosition.z
                    else:
                        p1 = own['Path'][index-1].copy()
                        p1.z = own.worldPosition.z
                    p2 = own['Path'][index].copy()
                    p2.z = own.worldPosition.z
                    bge.render.drawLine(p1,p2,(1,0,0)) 
                    index+=1   
                             
                             
                             
                                     
            if len(own['Path'])>=1:
                #we have a path point to navigate to
                rayE = own.worldPosition+own.worldOrientation.col[0]*1.75
                
                ray = own.rayCast(rayE,own.worldPosition,0,'Team',0,0,0)
                # check for obstructions
                
                if ray[0]:
                    local2 = own.worldPosition - ray[0].worldPosition
                    local2 = ray[0].worldOrientation.inverted()*local2
                    
                    if local2.x>0:
                        # if obstructions are ahead and facing you check to see if there 
                        # is ground to your left
                        ray2E = own.worldPosition+(own.worldOrientation*Vector([0,1.5,-1.5]))
                        ray2S = own.worldPosition+(own.worldOrientation*Vector([0,1.5, 1 ]))
                        ray2 = own.rayCast(ray2E,ray2S,0,'',0,0,0)
                        if ray2[0]:
                            #if there is ground side step
                            if own.localLinearVelocity.y<1.5:
                                own.applyForce((0,100,0),1)
                        else:
                            #Don't fall off edge
                            own.localLinearVelocity.y*=.5
                    else:
                        #Back up and let other person go
                        own.applyForce((-100,0,0),1)     
                
                
                
                    
                P = own["Path"][-1].copy()
                P.z += 1
                
                local = P-own.worldPosition
                local = own.worldOrientation.inverted()*local
                V2 = own.getVectTo(P)
                
                own['Read']=str(local)
                
                
                
                #if target is ahead of you
                if local.x>0:
                    #rotate while moving
                    if local.y>.05:
                        own.applyRotation((0,0,.025),1)
                    elif local.y<-.05:
                        own.applyRotation((0,0,-.025),1)
                        
                
                    V2 = own.getVectTo(P)
                    
                    
                    if V2[0]>1:
                        
                        if (abs(local.y)-local.x )<0:
                            #if path in front of you in a cone
                            if own.localLinearVelocity.x<5:
                                #servo motion forward
                                own.applyForce((50,0,0),1)
                                
                            else:
                                #Stop if the path is not in fron
                                own.localLinearVelocity.x*=.5
                            #Stop sideways sliding    
                            own.localLinearVelocity.y*=.25
                        else:
                            #Stop you are not facting correctly
                            own.localLinearVelocity.x*=.25
                                


                    else:
                        #at destinatino
                        own["Path"].pop(-1)
                else:
                    
                    if abs(local.magnitude)>2:
                        own.localLinearVelocity.x*= .9
                        own.localLinearVelocity.y*= .9
                        
                        #rotate in place
                        if local.y>0.1:
                            own.applyRotation((0,0, .05),1)
                        elif local.y<-0.1:
                            own.applyRotation((0,0,-.05),1)
                            
                    else:
                        own["Path"].pop(-1)
            else:
                #Reached target
                del own['Path']
                del own['Target']                        
                        
        else:
            #Reached target
            del own['Path'] 
            del own["Target"] 
main()
        
        

edit: new file update has _update in file name

Attachments

Traveler2x_update.blend (454 KB)

I notice one thing that blueprintrandom code causes the npcs to do.They get stuck and cannot move if they are moving close one behind the other near the waypoint.Sometimes the npcs get stuck on a waypoint and stop pathfinding.For no reason at all.You have to let it run long enough to see it happen.

Does anyone know how to fix that?

Is the player the same height as the way point?

If the player cannot get within 2.5 blender units to a target he will get stuck

I have a better version of this code I will post tonight.
We will see if it fixes your issue.

I will wait.



import bge
from mathutils import Vector
from random import randint


def main():
    
    cont = bge.logic.getCurrentController()
    own = cont.owner
    
    
            
    #get navmesh from connected steering actuator 
    if "navMesh" not in own:
        own["navMesh"] = cont.actuators["Steering"].navmesh




    #if you do not have a destination get one
    if "Path" not in own:
        Possible =[] 
        #lookup possible destinations
        for object in own.scene.objects:
            if "destination" in object:
                Possible.append(object)
        #choose a random destination 
        Target =None
        
        #This is used if recalculate path failed 
        if "Target" in own:
             
            Target = own["Target"]
        else:
            X = randint(0,len(Possible) - 1)
            Target = Possible[X]     
        #get path
        P = own["navMesh"].findPath(Target.worldPosition, own.worldPosition) 
        #make sure there is a valid path
        if P!= None:
            if len(P)>0:
                # save patb and target
                own["Path"] = P
                own['Target'] = Target
                




    #navigate to destination


    else:
        
        if own.getDistanceTo(own['Target'])>2.5:
            if bge.logic.getRandomFloat()>.995:
                
                P = own["navMesh"].findPath(own['Target'].worldPosition, own.worldPosition) 
                #make sure there is a valid path
                if P!= None:
                    if len(P)>0:
                        # save patb and target
                        own["Path"] = P
                        
                    else:
                         del own["Path"] 
                else:
                     if own.getDistanceTo(own['Target'])<2.5:
                         
                         del own['Target']
                         del own['Path']
                             
                         
            index = 0
            #Debug Path
            if own['Debug']==True:
                for point in own['Path']:
                    if index==0:
                        p1 = own['Target'].worldPosition.copy()
                        p1.z =own.worldPosition.z
                    else:
                        p1 = own['Path'][index-1].copy()
                        p1.z = own.worldPosition.z
                    p2 = own['Path'][index].copy()
                    p2.z = own.worldPosition.z
                    bge.render.drawLine(p1,p2,(1,0,0)) 
                    index+=1   
                             
                             
                             
                                     
            if len(own['Path'])>=1:
                #we have a path point to navigate to
                rayE = own.worldPosition+own.worldOrientation.col[0]*1.75
                
                ray = own.rayCast(rayE,own.worldPosition,0,'Team',0,0,0)
                # check for obstructions
                
                if ray[0]:
                    
                    local2 = own.worldPosition - ray[0].worldPosition
                    local2 = ray[0].worldOrientation.inverted()*local2
                    
                    if local2.x>0:
                        if bge.logic.getRandomFloat()>.5:
                            own.localLinearVelocity.x*=.9
                        # if obstructions are ahead and facing you check to see if there 
                        # is ground to your left
                        ray2E = own.worldPosition+(own.worldOrientation*Vector([0,1.5,-1.5]))
                        ray2S = own.worldPosition+(own.worldOrientation*Vector([0,1.5, 1 ]))
                        ray2 = own.rayCast(ray2E,ray2S,0,'',0,0,0)
                        if ray2[0]:
                            #if there is ground side step
                            if own.localLinearVelocity.y<1.5:
                                own.applyForce((0,100,0),1)
                        else:
                            #Don't fall off edge
                            own.localLinearVelocity.y*=.5
                    else:
                        #Back up and let other person go
                        own.applyForce((-100,0,0),1)     
                
                
                
                    
                P = own["Path"][-1].copy()
                P.z += 1
                #Create a left lane / right lane
                P = P+(own.worldOrientation.col[1]*1.5)
                #if len(own['Path'])==1:
                   # P+=own.worldOrientation.col[0]*4
                
                local = P-own.worldPosition
                local = own.worldOrientation.inverted()*local
                V2 = own.getVectTo(P)
                
                own['Read']=str(local)
                
                
                
                #if target is ahead of you
                if local.x>0:
                    #rotate while moving
                    if local.y>.05:
                        own.applyRotation((0,0,abs(local.y)*1/(3.14*6)),1)
                    elif local.y<-.05:
                        own.applyRotation((0,0,-abs(local.y)*1/(3.14*6)),1)
                        
                
                    V2 = own.getVectTo(P)
                    
                    
                    if V2[0]>1:
                        
                        if (abs(local.y)-local.x )<0:
                            #if path in front of you in a cone
                            if own.localLinearVelocity.x<15:
                                #servo motion forward
                                own.applyForce((50,0,0),1)
                                
                            else:
                                #Stop if the path is not in fron
                                own.localLinearVelocity.x*=.5
                            #Stop sideways sliding    
                            own.localLinearVelocity.y*=.25
                        else:
                            #Stop you are not facting correctly
                            own.localLinearVelocity.x*=.05
                            own.localLinearVelocity.y*=.05
                            
                                


                    else:
                        #at destination
                        own["Path"].pop(-1)
                else:
                    
                    if abs(local.magnitude)>2:
                        own.localLinearVelocity.x*= .9
                        own.localLinearVelocity.y*= .9
                        
                        #rotate in place
                        if local.y>0.1:
                            own.applyRotation((0,0, .05),1)
                        elif local.y<-0.1:
                            own.applyRotation((0,0,-.05),1)
                            
                    else:
                        own["Path"].pop(-1)
            else:
                #Reached target
                del own['Path']
                del own['Target']                        
                        
        else:
            #Reached target
            del own['Path'] 
            del own["Target"] 
main()
        

They go so fast they fall off the ground.

The npcs stops still.Here is the blend.I had to put a plane underneath but the npcs still stopped.

Attachments

Traveler2x_update.blend (526 KB)

I will have to see the scene.

Chop everything out you can except the npc and navmesh and ground physics.
One thing you have to consider is the origin of the npc must be within 2.5 units of the target.

If the target origin is too far off the nav mesh he can’t reach it.

Here it is.It just has one big plane for ground and the navmesh is bigger.

Attachments

Traveler2x_update.blend (506 KB)