Artificial intelligence opensource middleware

Hi, i was searching for information about artificial intelligence middleware to use in the BGE. Does anyone tried it anytime?

-What i have fount:
seems to exist to opensource middlewares
http://www.simbionic.com/games.htm simbionic is a opensource AI development tool, but seems quite old, maybe from 10 years ago. Anyway it provides a graphic logic blocs interface for non programers.

http://opensteer.sourceforge.net/ provides steer behaviours.
i didnt have tried it , dont know if they cam be used whith the BGE, did amybody tried?
please post more AI tools, there is a big lake about this.

pybrain.org

even more libraries:
https://wiki.python.org/moin/PythonForArtificialIntelligence

If I need an AI for my computer game, I code it myself. Computer game AI’s are so specialized for there game, that generic AI libraries are pretty much useless.
Just my opinion thouh.

There’s also commonly a misunderstanding about what video game AI is/does. Concepts like machine learning usually aren’t that valuable in making a basic computer opponent that’s fun to play.

Many people seem to be completely out there when they think they need AI. Most of them just need a function that picks one out of three on more or less arbitrary basis. Take it from something very simple and start refining if you feel like it’s still too dumb.

Keeping this short:

AI stands for Artificial Intelligence. As demonstrated by a number of species (and humans themselves), intelligence is a very relative concept.

All forms of decision making approximate intelligence. Depending upon how serious the application may be, you will choose a different solution.

Most of the aforementioned libraries are skewed towards offline intelligence used in interpreting data, with use cases aligned towards robotics / obscure forms of mathematics.

Better describe what capabilities your NPC characters (assumed) require, and then we can look at proposing methods (not libraries) to achieve them.

Regards, Angus.

Now someone could code a “bge_ai” and sell it on the blender market.

The problem with an AI middleware, as mentioned already, is the broad set of algorithms that fall under the AI category. Often times an AI middleware focuses on a particular aspect of AI, such as decision making, path finding (Recast), steering (Detour), machine learning (PyBrain), etc. In other words, simply asking for an AI middleware without first explaining what you need, will yield many results, and not all of them useful.

As for a bge AI library, I feel like there may be a few user created libraries for AI floating around these forums. I have also written my own simple libraries for decision making (using a state machine) and steering behaviors in a couple of personal projects. I could clean up the most recent one and put it on GitHub if anyone is interested in it. It is very simple, but could be used as a starting point for game specific AI.

That’s actually exactly what AI is most of the time. AI doesn’t refer to any particular class of intelligent algorithms, it just means a procedure that can make decisions (even if they aren’t necessarily smart).

Sure, this will be a good learning material.

So making a brief about this topic…
what an average BGE user need is an standalone program or script capable of doing at least:

  • path tracer, a way to locate the enemies in the game and find a good path to arrive there.
  • Steer behaviours, what does the bot when not in “hunting mode”.
  • Select between a few list of options when some events happen in game.
  • Not exactly AI related but i think that could be very usefull for this kind of problems…is to have some kind of scripts like the ones knowed in second life as ANIMATION OVERRIDERS (are scripts that load automatically between a list of loaded animation loops when an event hapens or simply automatically.
    Anyone have developed things like this, or we are still on the “only for coders” stage?

About the “pathtracer”, if there’s some sort of library for it, you’d have to find out a way to pass geometry data to it (write an exporter for with it’s geometry format), which can significantly slow down the load time of your game.
About steering behaviour, it’s already very basic to script it given the position, radius of the obstacle, player position and player’s direction vector.
About the last one, this is a very gameplay specific feature, just create those animations and specify the events that run them.

All of that is way too game-specific to be a plug-and-play asset. Stuff like that everyone (including myself at least) implements personally for each of their games.

Pathfinding you can already find built-in in BGE. Look for tutorials on how to use it.

import bge
import math

def main():

    cont = bge.logic.getCurrentController()
    own = cont.owner
    on = cont.sensors['On']
    
    if own['Health']<=1:
        own.scene.addObject('Explode',own,30)
        for child in own.childrenRecursive:
            if not child.invalid:
                child.endObject()
        own.endObject()
    else:            
        if cont.sensors['Near'].positive:
            d=100
            for hit in cont.sensors['Near'].hitObjectList:
                dist = own.getDistanceTo(hit)
                if dist<d:
                    own['Target']=hit
                    own['Reset']=60
                    own['On']=True
                    d = dist
                
        if 'parts' not in own:
            p = []
            c = []
            for child in own.childrenRecursive:
                if 'B_Tag' in child:
                    p.append( [ child, own.children[child['B_Tag'] ] ] )
                if 'c' in child:
                    c.append(child)
                    child.color = [.25,.25,.25,1]
                    
                        
            own['parts']=p        
            own['colorParts']=c
            
        else:
            for p in own['parts']:
                p[1].removeParent()
                p[1].worldPosition =p[0].worldPosition
                p[1].worldOrientation = p[0].worldOrientation
                p[1].setParent(own,1,0)
                
        if on.positive:            
            if own['init']<=34:
                cont.activate(cont.actuators['Wake'])
                own['init']+=1
            else:
                cont.deactivate(cont.actuators['Wake'])
                if own['BombTime']<15:
                    if own.localAngularVelocity.z < own.localLinearVelocity.magnitude:
                        if own.localLinearVelocity.magnitude>.15:
                            if abs(own.localLinearVelocity.x)>abs(own.localLinearVelocity.y):
                                own.children['Spider']['W']+=own.localLinearVelocity.x*.25
                                if own.children['Spider']['W']>40:
                                    own.children['Spider']['W']=0
                                elif own.children['Spider']['W']<0:
                                    own.children['Spider']['W']=40
                                        
                                
                                cont.activate(cont.actuators['Walk'])
                                cont.deactivate(cont.actuators['Idle'])
                                cont.deactivate(cont.actuators['Wake'])
                                
                            else:
                                #crab walk state
                                cont.deactivate(cont.actuators['Walk'])
                                cont.deactivate(cont.actuators['Idle'])
                                    
                        
                        else:
                            #idle state
                            own['W']=0
                            cont.deactivate(cont.actuators['Walk'])
                            cont.activate(cont.actuators['Idle'])
                        
                    elif abs(own.localAngularVelocity.z) >.25 :
                        #rotate in place state
                        pass             
                    
                
            if 'Target' in own and own['init']>14:
                if 'Path' not in own:
                    if own.getDistanceTo(own['Target'])>3:
                        own['Path'] = cont.actuators['Steering'].navmesh.findPath(own.worldPosition,own['Target'].worldPosition)
                    else:
                        v2 = own.getVectTo(own['Target'])
                        own.applyForce(v2[1] *( 5 + (15*v2[0]*v2[0]*v2[0])))
                        own.localLinearVelocity.y*=.25
                        own.localLinearVelocity.x*=.25
                        cont.activate(cont.actuators['Blow'])
                        cont.deactivate(cont.actuators['Walk'])
                        cont.deactivate(cont.actuators['Idle'])
                        cont.deactivate(cont.actuators['Wake'])
                        own.applyForce((0,0,9.9),0)
                        own.applyTorque((0,0,2),0)
                        if own['BombTime']<=99:
                            
                            own['BombTime']+=1
                        for part in own['colorParts']:
                            part.color = [ .5+ math.cos((own['BombTime']*own['BombTime'])*.0125),0,0,1 ]
                            
                            
                                          
                      
                elif len(own['Path'])>=1:
                    if own.getDistanceTo(own['Target'])>=4:
                        p1 = own['Path'][0].copy()
                        p1.z = own.worldPosition.z
                        local = own.worldOrientation.inverted()*(p1 - own.worldPosition)
                        if local.magnitude>.75:
                            if local.x>0:
                                own.localLinearVelocity.y*=.75
                                own.alignAxisToVect(own.getVectTo(p1)[1],0,.125)
                                if abs(local.y) < local.x:
                                    if own.localLinearVelocity.x<5:
                                        own.applyForce((25,0,0),1)
                            else:
                                if local.y>0:
                                    own.applyRotation((0,0,.1),0)
                                else:
                                    own.applyRotation((0,0,-.1),0)
                        else:
                            own['Path'].pop(0)            
                    else:
                        del own['Path']
                                    
                else:
                    del own['Path']
                                                            
            if own['Reset']>=1:
                own['Reset']-=1
                
            elif 'Target' in own:
                del own['Target'] 
            if 'Target' in own:
                if own.getDistanceTo(own['Target'])>6 and own['BombTime']>=1:
                    cont.deactivate(cont.actuators['Blow'])
                    own['BombTime']-=1
                    for p in own['colorParts']:
                        p.color = p.color.lerp([.25,.25,.25,1],.1)
                elif own.getDistanceTo(own['Target'])<=2:
                    own.scene.addObject('Explode',own,15)
                    own.endObject()   
                                
                           
            if own['BombTime']>=100:
                own.scene.addObject('Explode',own,15)
                own.endObject()    
main()

This is my ai for our recent bgmc 28 Lost star tribe

nothing fancey

turn in place if next point is behind -> run to path -> get to player -> explode.

#commented code

import bge
import math


# spider bot ai controller by jacob merrill
def main():

    cont = bge.logic.getCurrentController()
    own = cont.owner
    on = cont.sensors['On']
    
    
    # am I dead yet?
    if own['Health']<=1:
        # yeah I am dead lets explode
        own.scene.addObject('Explode',own,30)
        for child in own.childrenRecursive:
            if not child.invalid:
                child.endObject()
        own.endObject()
        
    else:            
        
        # if I am not dead and something is near me set my target
        # to the closest on the list of hitobjects
        # later we could cast rays and only go at something we see
        if cont.sensors['Near'].positive:
            d=100
            for hit in cont.sensors['Near'].hitObjectList:
                dist = own.getDistanceTo(hit)
                if dist<d:
                    own['Target']=hit
                    own['Reset']=60
                    own['On']=True
                    d = dist
                    
                    
        # this is used for compound phyiscs 
        # tags are parented to the armature 
        # child shapes are stored in conjunction with their tags
        # we also gather parts we will be flashing later with object color        
        
        #have we stored the list of parts?
        if 'parts' not in own:
            #no we need to store it
            p = []
            c = []
            #roll through all child shapes
            for child in own.childrenRecursive:
                
                if 'B_Tag' in child:
                    #child is a armature bone tag
                    p.append( [ child, own.children[child['B_Tag'] ] ] )
                    
                if 'c' in child:
                    #child will get object color animated
                    c.append(child)
                    child.color = [.25,.25,.25,1]
                    
            #store the data            
            own['parts']=p        
            own['colorParts']=c
            
        #data is found use it    
        else:
            
            #this updates the child shapes to match the armature
            for p in own['parts']:
                p[1].removeParent()
                # move child shape to match bone position and rot
                p[1].worldPosition =p[0].worldPosition
                p[1].worldOrientation = p[0].worldOrientation
                p[1].setParent(own,1,0)
                
                
        #if we activate play wake up animation        
        if on.positive:            
            if own['init']<=34:
                cont.activate(cont.actuators['Wake'])
                own['init']+=1
            else:
                #if we are  awake animate actor based on linV
                cont.deactivate(cont.actuators['Wake'])
                if own['BombTime']<15:
                    if own.localAngularVelocity.z < own.localLinearVelocity.magnitude:
                        if own.localLinearVelocity.magnitude>.15:
                            #if we are moving animate actor
                            if abs(own.localLinearVelocity.x)>abs(own.localLinearVelocity.y):
                                #if we move forward / backward play walk
                                own.children['Spider']['W']+=own.localLinearVelocity.x*.25
                                if own.children['Spider']['W']>40:
                                    #this is setting the animation frames in the armature
                                    own.children['Spider']['W']=0
                                elif own.children['Spider']['W']<0:
                                    own.children['Spider']['W']=40
                                        
                                
                                cont.activate(cont.actuators['Walk'])
                                cont.deactivate(cont.actuators['Idle'])
                                cont.deactivate(cont.actuators['Wake'])
                                
                            else:
                                #I did not add a crab walk animation yet
                                #crab walk state
                                cont.deactivate(cont.actuators['Walk'])
                                cont.deactivate(cont.actuators['Idle'])
                                    
                        
                        else:
                            #idle state
                            own['W']=0
                            cont.deactivate(cont.actuators['Walk'])
                            cont.activate(cont.actuators['Idle'])
                        
                    elif abs(own.localAngularVelocity.z) >.25 :
                        #rotate in place state
                        #I did not add a rotate in place animation yet
                        pass             
                    
                    
                    
            #if we have a target and we are awake check to see if we have a path    
            if 'Target' in own and own['init']>14:
                
                if 'Path' not in own:
                    #we don't have a path
                    if own.getDistanceTo(own['Target'])>3:
                        #if we don't have a path grab one if the target is far away
                        own['Path'] = cont.actuators['Steering'].navmesh.findPath(own.worldPosition,own['Target'].worldPosition)
                    else:
                        #if we are really close we should probably kill it
                        v2 = own.getVectTo(own['Target'])
                        
                        own.applyForce(v2[1] *( 5 + (15*v2[0]*v2[0]*v2[0])))
                        own.localLinearVelocity.y*=.25
                        own.localLinearVelocity.x*=.25
                        cont.activate(cont.actuators['Blow'])
                        cont.deactivate(cont.actuators['Walk'])
                        cont.deactivate(cont.actuators['Idle'])
                        cont.deactivate(cont.actuators['Wake'])
                        own.applyForce((0,0,9.9),0)
                        own.applyTorque((0,0,2),0)
                        if own['BombTime']<=99:
                            
                            own['BombTime']+=1
                            #set self to explode at 100
                        for part in own['colorParts']:
                            #this makes the parts flash faster and faster as boom boom time approaches
                            part.color = [ .5+ math.cos((own['BombTime']*own['BombTime'])*.0125),0,0,1 ]
                            
                            
                                          
                # this is what to do if we have a path and are not too close to target      
                elif len(own['Path'])>=1:
                    #check to see if we are close enough to target to explode on them
                    if own.getDistanceTo(own['Target'])>=4:
                        #if not continue pathfinding
                        p1 = own['Path'][0].copy()
                        p1.z = own.worldPosition.z
                        #place point in local space so we can decide what to do next.
                        local = own.worldOrientation.inverted()*(p1 - own.worldPosition)
                        #are we at the point on the path?
                        if local.magnitude>.75:
                            #is the next point ahead of me?
                            if local.x>0:
                                #the point is ahead of me seek it
                                own.localLinearVelocity.y*=.75
                                own.alignAxisToVect(own.getVectTo(p1)[1],0,.125)
                                #if point inside cone apply forces
                                #this math defines a cone 
                                if abs(local.y) < local.x:
                                    if own.localLinearVelocity.x<5:
                                        own.applyForce((25,0,0),1)
                            else:
                                #the point is behind me turn in place
                                if local.y>0:
                                    own.applyRotation((0,0,.1),0)
                                else:
                                    own.applyRotation((0,0,-.1),0)
                        else:
                            #remove point from path as we visit it
                            own['Path'].pop(0)            
                    else:
                        #I am near target, better rethink things
                        del own['Path']
                                    
                else:
                    #my path is empty time to rethink things
                    del own['Path']
            
            #if I don't see anything this clock ticks down                                                
            if own['Reset']>=1:
                own['Reset']-=1
                
                
            elif 'Target' in own:
                #forget target if they run too far away and I don't see them in near
                del own['Target'] 
                
                
            if 'Target' in own:
                #if we have a target and they run away before we can explode reset our boom clock
                if own.getDistanceTo(own['Target'])>6 and own['BombTime']>=1:
                    cont.deactivate(cont.actuators['Blow'])
                    own['BombTime']-=1
                    for p in own['colorParts']:
                        p.color = p.color.lerp([.25,.25,.25,1],.1)
                elif own.getDistanceTo(own['Target'])<=2:
                    own.scene.addObject('Explode',own,15)
                    own.endObject()   
                                
            #if our boom clock is high enough exlplode               
            if own['BombTime']>=100:
                own.scene.addObject('Explode',own,15)
                #clean up phyiscs shapes
                for child in own.childrenRecursive:
                    if not child.invalid:
                        child.endObject()
                
                own.endObject()    
main()