Don't click here unless you want to be banned.

LSL Wiki : LibrarySwarm

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are crawl338.us.archive.org
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();
    }
}
There are 2 comments on this page. [Display comments/form]