Friday, July 10, 2009

Predator-Prey crowd system


The goal of the script was to create a primary state of equality and to add some fields which would tarnsform(change) the boids behaviour. Therefore from simple wanderers (with no leaders) some boids would become either preys or predators.

If a predator would hit a wanderer, the number of predators would increase by one. If the predator would hit a prey, the number of preys would decrease by one and the number of predators would be the same. If a predator would enter the "force" field which changes the boids behaviour to preys, it would become a prey. If a prey would enter the "force" field which changes the boids behaviour to predators, it would become a predator.




import maya.cmds as cmds
import random


# Main
# ____
class Crowd:
def __init__(self):
#def crowdMain():
self.vNumber = 10
self.obst = 0
self.gforces = 0
self.UIelements = {}
cmds.playbackOptions(min= 1, max=500)
self.crowdSystemUI()
#end if
#end def __init__


def crowdSystemUI(self):
self.UIelements["window"] = cmds.window(title="Crowd System",
widthHeight=(300, 200)
)

self.UIelements["form"] = cmds.formLayout()
txt = cmds.text(label="Number of Vehicles")
collection = cmds.radioCollection()
radiob1 = cmds.radioButton(label="10", data=1, changeCommand=self.changeVnumber)
radiob2 = cmds.radioButton(label="20", onCommand="vNumber = 20")
cmds.setParent(upLevel=1)
cmds.setParent(upLevel=1)

txtOB = cmds.text(label="Obstacles")
collection2 = cmds.radioCollection()
radiob3 = cmds.radioButton(label="On", changeCommand=self.changeObst)
radiob4 = cmds.radioButton(label="Off")
cmds.setParent(upLevel=1)
cmds.setParent(upLevel=1)

txtGF = cmds.text( label = "Global Forces")
collection3 = cmds.radioCollection()
radiob5 = cmds.radioButton(label= "On" ,changeCommand =self.changeGforces)
radiob6 = cmds.radioButton(label="Off")
cmds.setParent(upLevel=1)
cmds.setParent(upLevel=1)

cmds.radioCollection(collection, edit=1, select=radiob1)
cmds.radioCollection(collection2, edit=1, select=radiob4)
cmds.radioCollection(collection3, edit=1, select=radiob6)

# Place Vehicle options
form = self.UIelements["form"]
cmds.formLayout(form,
edit=1,
attachForm= [
( txt, "top", 20),
(txt ,"left", 70),
(radiob1,"top", 10),
(radiob1,"left", 20),
(radiob2,"top", 30),
(radiob2,"left", 20)
]
)

# Place environment options
cmds.formLayout(form,
edit=1,
attachForm= [
(txtOB, "top", 80),
(txtOB, "left", 80),
(radiob3, "top", 80),
(radiob3, "left", 150),
(radiob4 ,"top", 80),
(radiob4, "left", 190),
(txtGF, "top", 110),
(txtGF, "left", 63),
(radiob5, "top", 110),
(radiob5, "left", 150),
(radiob6, "top", 110),
(radiob6, "left", 190)
]
)

# Create buttons
button1 = cmds.button(label = "Create")
button2 = cmds.button(label = "Cancel")

# Place buttons in the window
cmds.formLayout(form,
edit=1,
attachForm =[
(button1, "bottom", 10),
(button1, "left", 185),
(button2, "bottom", 10),
(button2, "right", 10)
]
)

# Add the commands to the buttons.

cmds.button(button1, edit=1, command=self.createCrowd )
cmds.button(button2, edit=1, command=self.deleteUI )

cmds.showWindow(self.UIelements["window"])

##############################################################
def createCrowdSolver(self):
self.crowdSolver = cmds.rigidSolver( create=1,
current=1,
name="crowdSolver",
velocityVectorScale=0.5,
displayVelocity=1,
sc=1
)
cmds.setAttr(self.crowdSolver + ".allowDisconnection", 1)

def createVehicle(self, i, vType):
"Creates a Vehicle"
if vType == "F":
w = 2
h = 2.5
d = 2
else:
w=5
h=3.5
d=5
v = cmds.polyCube(name="vehicle%s_%d" % (vType, i), width = w, height = h, depth = d)
#Add a Vehicle force field for follower.
field = cmds.radial( position=(0, 0, 0),
name = "vehicle%sForce_%d" % (vType, i),
magnitude=50,
attenuation=0.3,
maxDistance = 8.0
)
cmds.parent(field[0], v[0])
cmds.hide (field)
'''
if vType == "L":
# Add leader field
lRadial = cmds.radial( position = (0, 0, 0),
name = "vehicleGlobalLeadForce_%d" % i,
magnitude = -1,
attenuation = 0.2,
maxDistance = 50
)
cmds.parent (lRadial[0], v[0])
cmds.hide (lRadial)
'''
# Make the Vehicle a Rigid Body with random placement.
rigid = cmds.rigidBody(v[0],
name="rigidVehicle%s_%d" % (vType, i),
active=1,
mass=1,
bounciness=0,
damping= 1.5 ,
position = (random.uniform(-70,70), random.uniform(-70,70), 0),
impulse =( 0, 0, 0),
standInObject="cube",
solver= self.crowdSolver
)

cmds.disconnectAttr (rigid + "ry.output", v[0] + ".rotateY")
cmds.disconnectAttr (rigid + "rx.output", v[0] + ".rotateX")
cmds.disconnectAttr (rigid + "rz.output", v[0] + ".rotateZ")

# Create expression for VehicleF with a random multiplier added to the expression.
randX = random.uniform(-3,3)
randY = random.uniform(3,-3)
expString = "// Set wander motion of vehicle and add random multipliers.\n"
expString += "%s.impulseX" % rigid
expString += " = sin(time * %f);\n" % randX
expString += "%s.impulseY" % rigid
expString += " = (noise(time) * %f);\n\n" % randY
expString += "// Set orientation of Vehicle according to the velocity direction.\n"
expString += "float $fVel%d[];\n" % i
expString += "$fVel%d = `getAttr %s.velocity`;\n\n" % (i,rigid)
expString += "%s.rotateX = 0;\n" % v[0]
expString += "%s.rotateY = atan2d( $fVel%d[0], $fVel%d[2] );\n" % (v[0],i,i)
expString += "%s.rotateZ = 0;\n" % v[0]
expString += "string $ContactNames = `getAttr %s.cnn`;\n" % rigid
expString += "print($ContactNames);\n"

#print expString
exp = cmds.expression(s=expString )
######################################################################### start w08
if vType == "F":
return [rigid, field]
else:
return [rigid, field, lRadial]

def createCrowd(self, *args) :

self.collectObjects = cmds.ls(sl=1) ###################################### get selected objects

# Bring in ints and string from interface.
# Vehicle creation for crowd system.
# Create a master crowdSolver for all Vehicles.
self.createCrowdSolver()

# Get total number of Vehicles from interface options.
if self.vNumber == 10:
FvNumber = 10 # Followers
#LvNumber = 2 # Leaders
elif self.vNumber == 20:
FvNumber = 20 # Followers
#LvNumber = 2 # Leaders

#if (self.gforces == 1): self.makeGforces() # Check Global Forces option.


# Basic Vehicle model type: follower
#____________________________
followers = []
for i in range(FvNumber):
v = self.createVehicle(i, "F")
followers.append(v)

# Basic Vehicle model type: Leader
#_________________________________
#leaders = []
#for i in range(LvNumber):
# v = self.createVehicle(i, "L")
# leaders.append(v)
##########################
# Connect the fields nested loops.
# ________________________________
'''
for i in range(LvNumber): # Connect Leaders to Followers both fields.
fieldL = leaders[i][1]
fieldLG = leaders[i][2]
for j in range(FvNumber):
rigidF = followers[j][0]
cmds.connectDynamic(rigidF, fields=fieldL)
cmds.connectDynamic(rigidF, fields=fieldLG)





for i in range(FvNumber): # Connect Followers to Leaders
fieldF = followers[i][1]
for j in range(LvNumber):
rigidL = leaders[j][0]
cmds.connectDynamic(rigidL, fields=fieldF)


# Connect same type Vehicles to each other. Disconnect Vehicle from itself.

for i in range(LvNumber): # Connect Leaders to Leaders
rigidL1 = leaders[i][0]
fieldL1 = leaders[i][1]
for j in range(LvNumber):
rigidL2 = leaders[j][0]
fieldL2 = leaders[j][1]
cmds.connectDynamic(rigidL2, fields=fieldL1)

cmds.connectDynamic(rigidL1, delete=1, fields=fieldL1)
'''
for i in range(FvNumber): # Connect Follower to Followers
rigidF1 = followers[i][0]
fieldF1 = followers[i][1]
for j in range(FvNumber):
rigidF2 = followers[j][0]
fieldF2 = followers[j][1]
cmds.connectDynamic(rigidF2, fields=fieldF1)

cmds.connectDynamic(rigidF1, delete=1, fields=fieldF1)

######################################################################################## skip this global forces thing
# Connect the Global Forces to both types of Vehicles if option is on.

if self.gforces:
self.makeGforces()

compass = [ "N", "S", "E", "W" ]
for i in range(LvNumber):
for c in range(len(compass)):
cmds.connectDynamic(leaders[i][0], fields = "Gforce" + compass[c])

for i in range(FvNumber):
for c in range(len(compass)):
cmds.connectDynamic(followers[i][0], fields = "Gforce" + compass[c])


# Build Obstacles if option is selected.
if self.obst: self.collectObstacles()

# Disable warning.

cmds.cycleCheck(e=0)

# End of createCrowd def

def collectObstacles(self):
print "collecting obstacles!!"
if len(self.collectObjects) == 0: ### show on __init__ def
return "No obstacle objects were selected."
else:
print "elseeeee"
for ob in self.collectObjects:
print ob
cmds.rigidBody(ob, passive=1, name= ob + "_RigidB", bounciness=2.0)

#end def collectObstacles


# Make Global Forces with an interface.
#______________________________________


########################################### UI functions (for radio buttons)
def changeVnumber(self, *args):
if args[0] == "true":
self.vNumber = 10
else:
self.vNumber = 20
print "vNumber:",self.vNumber
def changeObst(self, *args):
if args[0] == "true":
self.obst = 1
else:
self.obst = 0
print "Obst:", self.obst
def changeGforces(self, *args):
if args[0] == "true":
self.gforces = 1
else:
self.gforces = 0
print "gforces:", self.gforces
def deleteUI(self, *args):
cmds.deleteUI(self.UIelements["window"])
self.UIelements = {}

############### call the class
c = Crowd()

And part of the second attempt:

import maya.cmds as cmds
import maya.mel as mel
import random
import math

def hit(hit, agent):
### define what to do when it some collision is detected
#all transformations are made only on the agent, as the hit object will perform the same queries on this agent
#if it hit the cube, ignore
if hit == "wall_rigid":
return
agentRole = cmds.getAttr(agent + ".role")
hitRole = cmds.getAttr(hit + ".role")
if hitRole == agentRole:
#check if both are bad
if hitRole == "bad":
#then shrink
cmds.scale(.9,.9,.9, agent, r=1)
else:
#if good, increase size
cmds.scale(1.1,1.1,1.1, agent, r=1)
elif hitRole != agentRole:
if hitRole == "bad":
#means agent role is good
cmds.scale(.90,.90,.90, agent, r=1)
elif hitRole == "good":
#means agent was bad
cmds.scale(1.1,1.1,1.1, agent, r=1)


#function to create shader
def createShader(color, transparency=0, type="lambert"):
#steps to create a new material
shadingNode1 = cmds.shadingNode(type, asShader=1)
shadingNode2 = cmds.sets(renderable=1, noSurfaceShader = 1, empty=1, name=shadingNode1+"SG")
cmds.connectAttr(shadingNode1+".outColor", shadingNode2 + ".surfaceShader", force=1)
#steps to modify material attributes
cmds.setAttr(shadingNode1 + ".color", color[0],color[1], color[2]) #color in r g b values
cmds.setAttr(shadingNode1 + ".transparency", transparency, transparency, transparency)

return shadingNode2

def applyShaderOnObject(obj, shader):
cmds.sets(obj, e=1, forceElement=shader)




class Agent:
def __init__(self, solver, role, position=(0,0,0), size=(3,3,3), color=(0,0,0), objType="cube", rigidType="active", bounciness=.6, collide=1):
#set agent atributes
self.bounciness = bounciness
self.collide = collide
self.rigidType = rigidType
self.solver=solver
self.role = role
self.initialScale = size
#create the agent and scale it
self.object = cmds.polySphere(ax=(0,0,1))
cmds.scale(size[0], size[1], size[2])
#apply a color to the agent
#set the object color
sh = createShader(color, transparency=0, type="lambert")
applyShaderOnObject(self.object[0], sh)

#create the rigid body
self.rigid = cmds.rigidBody(self.object,
name=self.object[0] + "_rigid",
p=position,
b=self.bounciness,
impulse=(0,0,0),
sio=objType, #stand in object
cc = 1, #contact count
cp = 1, #contact position
cn = 1, #contact name
solver=self.solver)

#add attribute to the rigid body to store the type of agent
cmds.select(self.rigid, r=1)
cmds.addAttr(ln="role", dt="string", keyable=1)
cmds.setAttr(self.rigid + ".role", "good", type="string")

if self.rigidType == "active":
cmds.setAttr(self.rigid + ".active", 1)
else:
cmds.setAttr(self.rigid + ".active", 0)

#apply the expression
self.applyExpression()

def applyExpression(self):
"Function to apply the expression to the agent"
#first disconnect the rotation attributes because we want to controll it via expressions
cmds.disconnectAttr (self.rigid + "ry.output", self.object[0] + ".rotateY")
cmds.disconnectAttr (self.rigid + "rx.output", self.object[0] + ".rotateX")
cmds.disconnectAttr (self.rigid + "rz.output", self.object[0] + ".rotateZ")
# Create expression for VehicleF with a random multiplier added to the expression.
randX = random.uniform(-3,3)
randY = random.uniform(3,-3)
####COMPOSE THE EXPRESSION STRING
#this is how it should look like (in MEL)
'''
// Set wander motion of vehicle and add random multipliers.
pCube1_rigid_0.impulseX = sin(time * -2.808801);
pCube1_rigid_0.impulseY = (noise(time) * -1.041035);

// Set orientation of Vehicle according to the velocity direction.
float $fVel[];
$fVel = `getAttr pCube1_rigid_0.velocity`;

pCube1.rotateX = 0;
pCube1.rotateZ = atan2d( $fVel[0], $fVel[1] );
pCube1.rotateY = 0;

//checking bumps on other agents
string $lastHit;
if (pCube1_rigid_0.contactCount > 0){
int $contactCount = `getAttr pCube1_rigid_0.contactCount`;
$lastHit = `getAttr pCube1_rigid_0.contactName[0]`;
string $rigid = "pCube1_rigid_0";
python("hit('"+$lastHit+"', '"+ $rigid +"')");
};//endif
'''
#now put the text above in a python string
#the first lines define how the agent will wander
expString = "// Set wander motion of vehicle and add random multipliers.\n"
expString += "%s.impulseX" % self.rigid
expString += " = sin(time * %f);\n" % randX
expString += "%s.impulseY" % self.rigid
expString += " = (noise(time) * %f);\n\n" % randY
#the second part, how it rotates according to the direction it is going (velocity vector)
expString += "// Set orientation of Vehicle according to the velocity direction.\n"
expString += "float $fVel[];\n"
expString += "$fVel = `getAttr %s.velocity`;\n\n" % (self.rigid)
expString += "%s.rotateX = 0;\n" % self.object[0]
expString += "%s.rotateZ = atan2d( $fVel[0], $fVel[1] );\n" % (self.object[0])
expString += "%s.rotateY = 0;\n\n" % self.object[0]
#the third part, checking bumps on other agents
expString += "//checking bumps on other agents\n"
expString += "string $lastHit;\n"
expString += "if (%s.contactCount > 0){\n" % self.rigid
expString += " int $contactCount = `getAttr %s.contactCount`;\n" % self.rigid
expString += " $lastHit = `getAttr %s.contactName[0]`;\n" %self.rigid
expString += ' string $rigid = "%s";\n' % self.rigid
expString += ' python("hit(\'"+$lastHit+"\', \'"+ $rigid +"\')");\n'
expString += "};//endif\n"

self.expString = expString
cmds.expression(s=expString)


##############################################
class Crowd:
def __init__(self, numAgents=20, minPosition=-70, maxPosition=70, walls=0):
#set the playback options to increase the time range
cmds.playbackOptions(min= 1, max=5000)
self.numAgents = numAgents
self.minPosition = minPosition
self.maxPosition = maxPosition
#get any selected objects
self.wallObjs = cmds.ls(sl=1)
#create the rigid solver
self.solver = self.createSolver()
#create the agents
self.createAgents()
#create the walls
if walls: self.makeWalls()

def createSolver(self):
solver = cmds.rigidSolver( create=1,
current=1,
name="crowdSolver",
velocityVectorScale=0.5,
displayVelocity=1,
sc=1, #showcollision
ctd=1 #contact data
)
cmds.setAttr(solver + ".allowDisconnection", 1)
return solver

def createAgents(self):
type1 = ["good", (3,3,3), (250,100,0)] #name, scale, color
type2 = ["bad", (5,5,5), (100,100,100)]
types = [type1, type2]
self.badAgents = []
self.goodAgents = []
for i in range(self.numAgents):
#get the agent type randomly
at = random.choice(types)
#get random x and y
x = random.uniform(self.minPosition,self.maxPosition)
y = random.uniform(self.minPosition,self.maxPosition)
#create the agents
a = Agent(self.solver, at[0], color=at[2], size=at[1], position=(x,y,0))
if at[0] == "good":
self.goodAgents.append(a)
else:
self.badAgents.append(a)

def makeWalls(self):
#get selected object,
#which should be already with its normals facing to the right directions,
#and convert it to passive rigid bodies
if len(self.wallObjs) == 0:
return "No wall objects were selected."
else:
for w in self.wallObjs:
self.wallRigid = cmds.rigidBody(w, passive=1, name= "wall_rigid", bounciness=.8)

########################################
c=Crowd(numAgents=30, walls=1)

No comments:

Post a Comment