Swarm
De DigiWiki.
Reproduced from this SL Forum thread with permission of the Author, Apotheus Silverman.
This implements the widely-available swarm algorithm in SL. A good way to use this is to have an object that rezzes a new swarming object every N seconds and set the swarming object to die after X seconds. These coupled with the force and friction values you set ensure your swarm will never get out of hand.
This works absolutely awesome for creating flocks of birds and schools of fish.
Edit: A couple months' worth of testing and optimization goes a long way :-) This new version has a lot of bug fixes and better overall behavior.
Edit 10/14/2004: Added optional schooling behavior so the swarm wanders realistically.
// Swarm script // by Apotheus Silverman // This script is my implementation of the well-known swarm algorithm // which can be found in numerous open-source programs. // Due to the specifics of the SL environment, I have strayed from some // of the traditional rules slightly. Regardless, the end effect is // indistiguishable from the original algorithm. // Configurable parameters // Determines whether or not to enable STATUS_SANDBOX. integer sandbox = FALSE; // Timer length float timer_length = 0.5; // Die after this many seconds integer kill_time = 300; // Enables or disables schooling behavior integer school = TRUE; // Schooling comm channel integer school_comm_channel = 9284; // Schooling behavior update interval (should be a multiple of timer_length) float school_update_interval = 2.0; // How much force to apply with each impulse float force_modifier = 0.7; // How much force to apply when repulsed by another like me float repulse_force_modifier = 0.86; // How much friction to use on a scale from 0 to 1. // Note that friction takes effect each timer cycle, so the lower the timer length, // the more the friction you specify here will take effect, thereby increasing actual // friction applied. float friction = 0.45; // How much to modify the rotation strength. Higher numbers produce greater strength // Note that if the modifier is too small, the object may not rotate at all. float rotation_strength_modifier = 2.8; // How much to modify rotation damping. Higher numbers produce slower rotation. float rotation_damping_modifier = 5000000.0; // Does this object "swim" in air or water? // 2 = air // 1 = water // 0 = both integer flight_mode = 1; // Maximum distance from spawn point float max_distance = 15.0; // How far away to scan for others like me float sensor_distance = 30.0; // *** Don't change anything below unless you *really* know what you're doing *** float mass; vector spawn_location; float school_timer = 0.0; vector school_modifier = <0,0,0>; // Update rotation function do_rotation(vector mypos, vector myvel) { llLookAt(mypos + myvel, mass * rotation_strength_modifier, mass * rotation_damping_modifier); } // Collision function collide(vector loc) { vector mypos = llGetPos(); // Apply repulse force vector impulse = llVecNorm(mypos - loc); llApplyImpulse(impulse * repulse_force_modifier * mass, FALSE); //llSay(0, "collide() - impulse " + (string)impulse + " applied."); // Update rotation do_rotation(mypos, llGetVel()); } // This function is called whether the sensor senses anything or not sensor_any() { // Die after reaching kill_time if (kill_time != 0 && llGetTime() >= kill_time) { llDie(); } // Get my velocity vector myvel = llGetVel(); // Apply friction llApplyImpulse(-(myvel * friction * mass), FALSE); // Schooling behavior if (school && llGetTime() - school_timer > school_update_interval) { llSay(school_comm_channel, (string)myvel); school_timer = llGetTime(); } // Get my position vector mypos = llGetPos(); // Check for air/water breach if (flight_mode == 1) { // water if (mypos.z >= llWater(mypos) - llVecMag(llGetScale())) { //llSay(0, "collide() called due to air/water breach."); collide(<mypos.x, mypos.y, mypos.z + 0.3>); } } else if (flight_mode == 2) { // air if (mypos.z <= llWater(mypos) + llVecMag(llGetScale())) { //llSay(0, "collide() called due to air/water breach."); collide(<mypos.x, mypos.y, mypos.z - 0.3>); } } // Stay near spawn location if (llVecDist(mypos, spawn_location) > max_distance) { // Compensate for being near sim border if (spawn_location.x - mypos.x > 100) { mypos.x += 255; } //llSay(0, "collide() called due to too much distance from my spawn point. mypos=" + (string)mypos + ", spawn_location = " + (string)spawn_location); collide(mypos - llVecNorm(spawn_location - mypos)); } // Stay above ground level if (mypos.z <= llGround(ZERO_VECTOR)) { collide(mypos - llGroundNormal(ZERO_VECTOR)); } } default { state_entry() { llResetTime(); llSay(0, "Fishy spawned."); // School if (school) { llListen(school_comm_channel, "", NULL_KEY, ""); } // Sandbox llSetStatus(STATUS_SANDBOX, sandbox); llSetStatus(STATUS_BLOCK_GRAB, FALSE); spawn_location = llGetPos(); // Initialize physics behavior mass = llGetMass(); llSetBuoyancy(1.0); llSetStatus(STATUS_PHYSICS, TRUE); llVolumeDetect(TRUE); // Initialize sensor llSensorRepeat(llGetObjectName(), NULL_KEY, ACTIVE|SCRIPTED, sensor_distance, PI, timer_length); } collision_start(integer total_number) { //llSay(0, "collide() called due to physical object collision."); collide(llDetectedPos(0)); } no_sensor() { sensor_any(); } sensor(integer total_number) { sensor_any(); // Populate neighbors with the positions of the two nearest neighbors. vector mypos = llGetPos(); list neighbors = []; integer i; for (i = 0; i < total_number; i++) { vector current_pos = llDetectedPos(i); if (llGetListLength(neighbors) < 2) { // Add to list neighbors = llListInsertList(neighbors, [current_pos], llGetListLength(neighbors)); } else { // Check to see if the current vector is closer than the list // vector which is furthest away. if (llVecDist(mypos, llList2Vector(neighbors, 0)) > llVecDist(mypos, llList2Vector(neighbors, 1))) { // check against first list item if (llVecDist(mypos, llList2Vector(neighbors, 0)) > llVecDist(mypos, current_pos)) { llListInsertList(neighbors, [current_pos], 0); } } else { // check against second list item if (llVecDist(mypos, llList2Vector(neighbors, 1)) > llVecDist(mypos, current_pos)) { llListInsertList(neighbors, [current_pos], 1); } } } } // Process movement // Apply force if (llGetListLength(neighbors) == 2) { vector neighbor1 = llList2Vector(neighbors, 0); vector neighbor2 = llList2Vector(neighbors, 1); vector target = neighbor2 + ((neighbor1 - neighbor2) * 0.5); vector impulse = <0,0,0>; if (school) { impulse = llVecNorm(target + school_modifier - mypos); } else { impulse = llVecNorm(target - mypos); } //llSay(0, "setforce " + (string)(impulse * force_modifier * mass)); llSetForce(impulse * force_modifier * mass, FALSE); } // Update rotation do_rotation(llGetPos(), llGetVel()); } listen(integer channel, string name, key id, string message) { list myList = llCSV2List(llGetSubString(message, 1, llStringLength(message) - 2)); if (llGetListLength(myList) == 3) { school_modifier = <llList2Float(myList, 0), llList2Float(myList, 1), llList2Float(myList, 2)>; school_timer = llGetTime(); } } on_rez(integer start_param) { llResetScript(); // spawn_location = llGetPos(); // llResetTime(); } }