global proc float[] flockObj(string $objName) { // tweakable variables float $avgSpeed = 0.1; float $minSpeed = 0.05; float $maxSpeed = 0.125; float $tooClose = 6.0; float $weightCap = 0.1; float $repulseForce = 0.01; float $weightFactor = 10.0; float $posInLineValue = 0.25; float $influenceDistance = 6.0; float $stopDistance = 3.3; float $maxRadius = 2.0; int $dampen = 1; // if dampen is set, then dampen the velocity. Dampen is turned off in the unaligned collision avoidance // rotate Y corresponds to the direction that the object is facing. float $rotateY = `getAttr ($objName + ".rotateY")`; // the X and Z components of the direction are determined float $directionX = sind($rotateY); float $directionZ = cosd($rotateY); float $velocity = `getAttr ($objName + ".Velocity")`; // the world space position of the object float $positionX = `getAttr ($objName + ".tx")`; float $positionZ = `getAttr ($objName + ".tz")`; float $status = `getAttr ($objName + ".ib")`; float $tempPosX, $tempPosZ,$tempVelocity; string $target = `getAttr ($objName + ".tg")`; // positionInLine keeps track of whether there are more units in front or behind // the current object float $positionInLine = 0.0; // closestDistance is the min distance to an enemy // closestGuy is the name of the closest enemy float $closestDistance = 10000.0; string $closestEnemy = "Alex Trebek"; string $temp; float $tempRotateY; // these track the influences on X and Z from varies inputs float $dirXCentering,$dirZCentering,$dirXVelocity,$dirZVelocity,$dirXAvoid,$dirZAvoid,$dirXApproach,$dirZApproach; $dirXCentering = 0; $dirZCentering = 0; $dirXVelocity = 0; $dirZVelocity = 0; $dirXAvoid = 0; $dirZAvoid = 0; $dirXApproach = 0; $dirZApproach = 0; float $tempDirX, $tempDirZ; string $objects[] = returnNearbyObjects($objName); for($i=0;$i $weightCap) { $weight = $weightCap; } float $dirToFriendX = $tempPosX - $positionX; float $dirToFriendZ = $tempPosZ - $positionZ; float $tempMag = sqrt(($dirToFriendX * $dirToFriendX) + ($dirToFriendZ * $dirToFriendZ)); if($tempMag == 0) { $dirToFriendX = 0; $dirToFriendZ = 0; } else { $dirToFriendX /= $tempMag; $dirToFriendZ /= $tempMag; } // find out if the other guy is an unattached enemy if(substring($objName,1,4) != substring($temp,1,4)) { if($tempStatus == 0) { if($distance < $closestDistance) { $closestDistance = $distance; $closestEnemy = $temp; } } } float $dotProduct = $dirToFriendX * $directionX + $dirToFriendZ * $directionZ; // the behaviors seperation, group centering, velocity matching should only be done if the // guys are in the same army. if(substring($objName,1,4) == substring($temp,1,4) && $tempStatus < 2) { // -------------------- separation ------------------------------------------------ // separation once objects get too close they are repulsed if($distance < $tooClose) { float $dotProductDirs = $tempDirX * $directionX + $tempDirZ * $directionZ; // the repulsive force works if the objects are facing the same direction if($dotProductDirs > 0.0) { float $dirFromFriendX, $dirFromFriendZ; $dirFromFriendX = $positionX - $tempPosX; $dirFromFriendZ = $positionZ - $tempPosZ; float $mag = sqrt(($dirFromFriendX * $dirFromFriendX) + ($dirFromFriendZ * $dirFromFriendZ)); if($mag == 0) { $dirFromFriendX = 0; $dirFromFriendZ = 0; } else { $dirFromFriendX /= $mag; $dirFromFriendZ /= $mag; } // linear // quadratic $dirFromFriendX *= $repulseForce * ($distance - $tooClose) * ($distance - $tooClose); $dirFromFriendZ *= $repulseForce * ($distance - $tooClose) * ($distance - $tooClose); move -r $dirFromFriendX 0.0 $dirFromFriendZ ($objName); } } // ------------------------ group centering -------------------------------------------- // group Centering $dirXCentering = 0.0; $dirZCentering = 0.0; // if dot product between current direction and // this new direction is negative, then disregard input // because the object is behind it. if($dotProduct > 0.0) { // instead of aiming for the dude, aim for a spot next to him. float $crossProduct[] = crossProduct({$directionX,0,$directionZ},{$dirToFriendX,0,$dirToFriendZ},0,1); float $perpAway[] = crossProduct({$directionX,0,$directionZ},{$crossProduct[0],$crossProduct[1],$crossProduct[2]},0,1); float $pointAwayX = $tempPosX + $perpAway[0] * $tooClose; float $pointAwayZ = $tempPosZ + $perpAway[2] * $tooClose; $dirToFriendX = ($pointAwayX - $positionX); $dirToFriendZ = ($pointAwayZ - $positionZ); float $tempMag = sqrt(($dirToFriendX * $dirToFriendX) + ($dirToFriendZ * $dirToFriendZ)); if($tempMag == 0) { $dirToFriendX = 0; $dirToFriendZ = 0; } else { $dirToFriendX /= $tempMag; $dirToFriendZ /= $tempMag; } $dirXCentering = $dirToFriendX * $weight; $dirZCentering = $dirToFriendZ * $weight; $positionInLine += $posInLineValue; } else { $positionInLine -= $posInLineValue; } // ------------------------- velocity matching -------------------------------------------- // velocity matching if($dotProduct > 0.0) { $dirXVelocity = sind($tempRotateY) * $weight; $dirZVelocity = cosd($tempRotateY) * $weight; } // apply directional changes } // --------------------------- unaligned collision avoidance ------------------------------- // unaligned collision avoidance if($dotProduct > 0.0 && $temp != $target) { float $timeDistance[]; $timeDistance = nearestApproach($positionX,$positionZ,$tempPosX,$tempPosZ,$velocity,$tempVelocity,$directionX,$directionZ,$tempDirX,$tempDirZ); float $time, $ucdistance; $time = $timeDistance[0]; $ucdistance = $timeDistance[1]; if($time > 0.0) // if the nearest arrival is behind them, then it doesn't matter { if($ucdistance < ($tooClose * 2)) { float $pointAvoidX,$pointAvoidZ,$futurePointX,$futurePointZ; // pointAvoid is the position the other unit will be at in the future $pointAvoidX = $tempPosX + $time * $tempVelocity * $tempDirX; $pointAvoidZ = $tempPosZ + $time * $tempVelocity * $tempDirZ; $futurePointX = $positionX + $time * $velocity * $directionX; $futurePointZ = $positionZ + $time * $velocity * $directionZ; float $distance1, $tempDistance; $distanceToImpact = sqrt(($futurePointX * $futurePointX) + ($futurePointZ * $futurePointZ)); $tempDistance = sqrt(($pointAvoidX * $pointAvoidX) + ($pointAvoidZ * $pointAvoidZ)); // the direction is the direction from the object to the point the other object will be float $dirToAvoidX,$dirToAvoidZ; $dirToAvoidX = $pointAvoidX - $positionX; $dirToAvoidZ = $pointAvoidZ - $positionZ; float $crossProduct[3]; $crossProduct = crossProduct({$directionX,0,$directionZ},{$dirToAvoidX,0,$dirToAvoidZ},0,1); float $perpAway[3]; $perpAway = crossProduct({$directionX,0,$directionZ},$crossProduct,0,1); // the distance from the collision the object moves away decreases float $distAway = $distance; if($distance >= $tooClose)// / 2.0) { $distAway = $tooClose;// / 2.0; } $pointAvoidX = $pointAvoidX + $perpAway[0] * $distAway; $pointAvoidZ = $pointAvoidZ + $perpAway[2] * $distAway; $dirToAvoidX = $pointAvoidX - $positionX; $dirToAvoidZ = $pointAvoidZ - $positionZ; float $tempMag = sqrt(($pointAvoidX * $pointAvoidX) + ($pointAvoidZ * $pointAvoidZ)); if($tempMag == 0) { $dirToAvoidX = 0; $dirToAvoidZ = 0; } else { $dirToAvoidX /= $tempMag; $dirToAvoidZ /= $tempMag; } $dirXAvoid = $dirToAvoidX; $dirZAvoid = $dirToAvoidZ; // adjust the velocity based on the future point of collision // distanceToImpact is the distance objName has to travel until it will collide with temp // tempDistance is the distance temp has to travel until it will collide with objName if(($distanceToImpact < $tempDistance) || ($distanceToImpact <= $tooClose)) { $velocity += ($minSpeed - $velocity) * $weight * 100.0; } else { $velocity += ($maxSpeed - $velocity) * $weight * 100.0; } $dampen = 0; } } } $directionX += ($dirXCentering + $dirXVelocity + $dirXAvoid); $directionZ += ($dirZCentering + $dirZVelocity + $dirZAvoid); } } // for every guy // -------------------------- approach ------------------------------------------- // approach attached dude if($status == 1) { string $enemy = `getAttr ($objName + ".tg")`; float $sphereRadius; float $enemyDirX, $enemyDirZ, $enemyPosX, $enemyPosZ, $enemyRotateY, $enemyVelocity; $enemyPosX = `getAttr ($enemy + ".tx")`; $enemyPosZ = `getAttr ($enemy + ".tz")`; $enemyRotateY = `getAttr ($enemy + ".ry")`; $enemyVelocity = `getAttr ($enemy + ".Velocity")`; $enemyDirX = sind($enemyRotateY); $enemyDirZ = cosd($enemyRotateY); float $futureEnemyPosX, $futureEnemyPosZ, $dirToEnemyX, $dirToEnemyZ; // don't approach the position the object is currently at, but at a future position. $futureEnemyPosX = $enemyPosX + $enemyDirX * $enemyVelocity; $futureEnemyPosZ = $enemyPosZ + $enemyDirZ * $enemyVelocity; $dirToEnemyX = $futureEnemyPosX - $positionX; $dirToEnemyZ = $futureEnemyPosZ - $positionZ; float $distance = calcDistance($positionX,$positionZ,$enemyPosX,$enemyPosZ); $dirToEnemyX /= $distance; $dirToEnemyZ /= $distance; if($distance > $influenceDistance) { // break apart the two objects // remove the obstacle sphere string $sphereName = "obstacle"; if(substring($objName,4,4) == "B") { $sphereName += $objName + $enemy; } else { $sphereName += $enemy + $objName; } delete $sphereName; // remove targets $closestEnemy = "Alex Trebek"; // set in battle attribute to zero setAttr ($objName + ".ib") 0; setAttr ($enemy + ".ib") 0; } else { $weight = 1.0 / pow($distance,2.0); if($distance < ($influenceDistance / 2.0) ) { $velocity += (0.0 - $velocity) * $weight * 100.0; } else { $velocity += ($maxSpeed - $velocity) * $weight * 10.0; } $dampen = 0; if($velocity < 0.0) { $velocity = 0.0; } float $distTime[] = nearestApproach($positionX, $positionZ, $enemyPosX, $enemyPosZ, $velocity, $enemyVelocity, $directionX, $directionZ, $enemyDirX, $enemyDirZ); float $time = $distTime[0]; float $forwardDistance; $forwardDistance = $time * ($velocity + $enemyVelocity); float $slope = ($maxRadius / ($stopDistance - $influenceDistance)); float $yInt = -1.0 * $slope * $influenceDistance; $sphereRadius = ($slope * $distance) + $yInt; float $futurePosX, $futurePosZ, $futureEnemyPosX, $futureEnemyPosZ; $futurePosX = $positionX + $time * $velocity * $directionX; $futurePosZ = $positionZ + $time * $velocity * $directionZ; $futureEnemyPosX = $enemyPosX + $time * $enemyVelocity * $enemyDirX; $futureEnemyPosZ = $enemyPosZ + $time * $enemyVelocity * $enemyDirZ; float $averagePosX, $averagePosZ; $averagePosX = ($futurePosX + $futureEnemyPosX) / 2.0; $averagePosZ = ($futurePosZ + $futureEnemyPosZ) / 2.0; if($sphereRadius > $maxRadius) { $sphereRadius = $maxRadius; } if($sphereRadius < 0.001) { $sphereRadius = 0.001; } string $sphereName = "obstacle"; if(substring($objName,4,4) == "B") { $sphereName += $objName + $enemy; } else { $sphereName += $enemy + $objName; } if(substring($objName,4,4) == "B") { //print("sphere radius before resizing = " + $sphereRadius + "\n"); setAttr ($sphereName + ".sx") $sphereRadius; setAttr ($sphereName + ".sy") $sphereRadius; setAttr ($sphereName + ".sz") $sphereRadius; setAttr ($sphereName + ".tx") $averagePosX; setAttr ($sphereName + ".tz") $averagePosZ; } // apply directional changes $dirXApproach = $dirToEnemyX * $weight; $dirZApproach = $dirToEnemyZ * $weight; float $mag = sqrt(($dirXApproach * $dirXApproach) + ($dirZApproach * $dirZApproach)); if($mag == 0.0) { $dirXApproach = 0; $dirZApproach = 0; } else { $dirXApproach /= $mag; $dirZApproach /= $mag; } // if they have arrived, then have them fight. float $dotProduct = ($directionX * $dirToEnemyX) + ($directionZ * $dirToEnemyZ); string $selectedguys[] = `ls -sl`; if($selectedguys[0] == $objName) { print("dot product = " + $dotProduct + "\n"); print("my direction = " + $directionX + "," + $directionZ + "\n"); print("dir to enemy = " + $dirToEnemyX + "," + $dirToEnemyZ + "\n"); } if(($distance < $stopDistance + .001) && $dotProduct > 0.95) { setAttr ($objName + ".ib") 2; setAttr ($enemy + ".ib") 2; float $newY = atan2d($dirXApproach,$dirZApproach); setAttr ($objName + ".rotateY") $newY; $newY = atan2d(-1.0 * $dirXApproach,-1.0 * $dirZApproach); setAttr ($enemy + ".rotateY") $newY; // set the collision sphere to exactly between the two. $averagePosX = ($positionX + $enemyPosX) / 2.0; $averagePosZ = ($positionZ + $enemyPosZ) / 2.0; setAttr ($sphereName + ".tx") $averagePosX; setAttr ($sphereName + ".tz") $averagePosZ; //************* CHRIS CODE FOR WALKING ******************************** string $Cchar = `getAttr ($objName + ".char")`; string $Ccurrentclip = `getAttr ($Cchar + ".currentclip")`; int $Ctotallen = (`currentTime -q`) - `getAttr ($Ccurrentclip + ".startFrame")`; float $val = (($Ctotallen-32.0)/32.0); if ($val < 0.0) { $val = 0.0; } setAttr ($Ccurrentclip + ".postCycle") $val; $Cchar = `getAttr ($enemy + ".char")`; $Ccurrentclip = `getAttr ($Cchar + ".currentclip")`; $Ctotallen = (`currentTime -q`) - `getAttr ($Ccurrentclip + ".startFrame")`; $val = (($Ctotallen-32.0)/32.0); if ($val < 0.0) { $val = 0.0; } setAttr ($Ccurrentclip + ".postCycle") $val; //************************ END ***************************************** } } } // Add the closest enemy to the current objects target attribute and create the // obstacle sphere that will be between them. if($status == 0 && $closestEnemy != "Alex Trebek" && $closestDistance <= $influenceDistance) { setAttr ($objName + ".tg") -type "string" $closestEnemy; setAttr ($objName + ".ib") 1; setAttr ($closestEnemy + ".tg") -type "string" $objName; setAttr ($closestEnemy + ".ib") 1; // make an obstacle sphere between them with their names concatenated together as its name string $sphereName = "obstacle"; if(substring($objName,4,4) == "B") { $sphereName += $objName + $closestEnemy; } else { $sphereName += $closestEnemy + $objName; } sphere -name $sphereName; xform -s 0.001 0.001 0.001; setAttr ($sphereName + ".visibility") 0; } // add in the approach weight to the directionX $directionX += $dirXApproach; $directionZ += $dirZApproach; float $mag = sqrt(($directionX * $directionX) + ($directionZ * $directionZ)); if($mag == 0) { $directionX = 0; $directionZ = 0; } else { $directionX /= $mag; $directionZ /= $mag; } float $newY = atan2d($directionX,$directionZ); // velocity is set above in the unaligned collision avoidance behavior if($dampen) // no collision so dampen velocity { //print($objName + " is being dampened\n"); $velocity += ($avgSpeed - $velocity) * 0.1; } if($velocity < $minSpeed) { $velocity = $minSpeed; } if($velocity > $maxSpeed) { $velocity = $maxSpeed; } float $returnValue[3]; $returnValue[0] = $directionX; $returnValue[1] = $directionZ; $returnValue[2] = $velocity; return $returnValue; }