Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Library: Swarm

Apotheus Silverman
I write code.
Join date: 17 Nov 2003
Posts: 416
02-14-2004 17:28
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.


CODE

// 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();
}
}
_____________________
Apotheus Silverman
Shop SL on the web - SLExchange.com

Visit Abbotts Aerodrome for gobs of flying fun.
Moleculor Satyr
Fireflies!
Join date: 5 Jan 2004
Posts: 2,650
02-16-2004 19:11
Right, could you explain to non swarmy people what exactly a kind of 'swarm' this makes?
_____________________
</sarcasm>
Apotheus Silverman
I write code.
Join date: 17 Nov 2003
Posts: 416
02-18-2004 06:03
Sure Moleculor.

The swarm algorithm in its purest sense creates any number of things that are attracted to each other. Any objects you tie this script to (after modifying the configurable parameters according to your object size, desired speed, etc) behave like social animals. That is, they swarm/flock/school... whatever you want to call it. None of generated movement is random, though it may appear to be.

Swarm rules for each swarm object to follow:
1. Accelerate toward the halfway point between your two nearest neighbors.
2. Upon collision, momentarily be repulsed by whatever object or ground was collided with.
3. If no neighbors are detected, just keep moving in a straight line at a constant speed. If I go off-world, then so be it.

The traditional swarm algorithm actually has a few more rules than this, but those rules are rather specific to a single application handling the entire swarm rather than multiple state engines working together.


The best way of using this script that I have found is to create your swarm object... let's say you want to make a school of fish. Create your fish model. Add the script into your fish.

Now comes the tricky part: getting the parameters just right. The swarming behavior does not occur until there are at least 3 of your swarm object within 96 meters of each other. When you have the parameters adjusted and are ready to test your swarm, you must rez at least 3 of the current object. An easy way to do this is to be in edit mode and hit ctrl+d twice. Make sure you take a copy of your object BEFORE you start testing, as incorrectly configured objects tend to fly away very fast! You can also set sandbox to TRUE so they don't get too far away.

Once you have your swarm behaving the way you want it to, make sure you have a timeout set (I generally use 5 minutes) on the swarm object, then create another object that will be the main center point of your swarm. This object will rez new swarm objects every N seconds. This ensures you always have a swarm that is relatively nearby the rezzing object, and in conjunction with the swarm object timeout, ensures you always have a relatively constant number of swarm objects rezzed. The script code for your rezzing object should be something like this (ignore errors... i'm at work right now and don't have access to SL to test this):

CODE

default {
state_entry() {
llSetTimerEvent(20);
}
timer() {
// Rez in a random location within a 10m cube. Don't use 5 in lieu of 5.0 or you could end up with slightly odd results due to the automatic conversion to integer instead of float.
vector mypos = llGetPos();
mypos.x += llFrand(10) - 5.0;
mypos.y += llFrand(10) - 5.0;
mypos.z += llFrand(10) - 5.0;
llRezObject("swarm object", mypos, <0,0,0,1>,<0,0,0>);
}
}


Once you apply this script to your rezzing object, add your swarm object to its contents and let it go. Congratulations, you now have a completely self-maintaining swarm. :)

Oh and please please please please please do not create huge swarms with a ton of swarm objects and just let it stay that way. You'll dump the sim's script and physics performance right in the toilet and end up making all your neighbors angry. Just as an FYI I had a swarm of 25 going in Cordova and did not see any slowdown or performance loss at all, but your mileage may vary.

Since it is quite obvious that creating a swarm is not something to be tackled by a non-scripter or even someone who doesn't have much patience, I will be selling my swarm creations soon at my new home in Abbots for a small fee. Since the code is open-source, I will also allow full copy/mod/transfer perms to purchasers.
_____________________
Apotheus Silverman
Shop SL on the web - SLExchange.com

Visit Abbotts Aerodrome for gobs of flying fun.
Riptide Ramos
Junior Member
Join date: 25 Jan 2004
Posts: 9
02-19-2004 13:31
Apotheus,

This is an excellent behavior script! Thanks for sharing. I hope to see some fish and bird schools out there soon.

You may be interested in a simpler sort for your sensor; instead of using the list functionality which seems like overkill for a 2-deep sort. Try something like this:

CODE

sensor(...)
...
vector v1;
vector v2;
float d1 = 100;
float d2 = 100;
...

for (i = 0; i < total_number; i++) {
vector current_pos = llDetectedPos(i);
float cur_dist = llVecDist(mypos, current_pos)
if ( cur_dist < d1 ) {
// Shift list down, take over top slot.
d2 = d1;
v2 = v1;
d1 = cur_dist;
v1 = current_pos;
} else if ( cur_dist < d2 ) {
// Replace second slot only
d2 = cur_dist;
v2 = current_pos;
}
}
...


--Riptide
Apotheus Silverman
I write code.
Join date: 17 Nov 2003
Posts: 416
04-22-2004 18:53
I updated the code in the original post to the most recent version of this script, with a couple months' worth of bug fixes and optimization. Overall behavior is much better and more optimized, much easier to work with, etc. Enjoy!
_____________________
Apotheus Silverman
Shop SL on the web - SLExchange.com

Visit Abbotts Aerodrome for gobs of flying fun.
Chris Byrne
Broccoli Chef
Join date: 21 Mar 2004
Posts: 57
05-07-2004 11:18
Great post! I was able to try this out in minutes flat. Thanks!
Aaron Levy
Medicated Lately?
Join date: 3 Jun 2004
Posts: 2,147
06-24-2004 22:41
Help!

Ok... there's a rotate variable I can set, but the fish I made never turn around and swim the opposite direction or anything. They just keep steadily moving in one direction until they are long gone... I need them to stay within the tank I'm making! Unless I call them superfish, it won't be believable. Ha!

Second, how can I alter this script so that they NEVER de-spawn?

Finally, even though I set the variable about how far I want the fish to stray from where they spawned, the fish always slowly make it all the way to the floor where they go outside the sandbox and stop.

Any help would be greatly appreciated as I learn this languange.

Thanks!!
Apotheus Silverman
I write code.
Join date: 17 Nov 2003
Posts: 416
10-14-2004 17:03
I have updated the main swarm script to add an optional schooling behavior so the swarm can wander around if you so desire. This allows schools of fish especially to appear much more realistic.
_____________________
Apotheus Silverman
Shop SL on the web - SLExchange.com

Visit Abbotts Aerodrome for gobs of flying fun.
winston Moseley
Registered User
Join date: 16 Sep 2004
Posts: 4
Super Stuff
10-18-2004 12:06
Cheers for this script, I have always wanted my own flock of small alien flying saucers

and it was relatively easy to get going considering that I am completely crap when it comes to scripts or code of any sort


Thanks again
:)
Foster Virgo
Registered User
Join date: 16 Jun 2004
Posts: 175
11-11-2004 17:42
This script is hilarious, I put six toilets out and had a little toilet farm for a few minutes. They mingled about bumping into each other making flush sounds.

I noticed that objects had physics enabled but my prim pusher gun wouldn't work on them, I wanted to make like a group of aliens or animals to blast with this as a target shooting game.
_____________________
"An official Red Ryder carbine action two-hundred shot range model air rifle with a compass in the stock and this thing that tells time!" Ralphie
Apotheus Silverman
I write code.
Join date: 17 Nov 2003
Posts: 416
11-14-2004 06:13
From: Foster Virgo
I noticed that objects had physics enabled but my prim pusher gun wouldn't work on them, I wanted to make like a group of aliens or animals to blast with this as a target shooting game.


The swarm objects are set as volume detect because that was what allowed me to produce the most realistic looking behavior. You can change it so they are not phantom or volume detect if you want. The script is written to handle physical collisions already if you want to try that.
_____________________
Apotheus Silverman
Shop SL on the web - SLExchange.com

Visit Abbotts Aerodrome for gobs of flying fun.
Araiya Bomazi
A. Bomazi-Tomba. :)
Join date: 3 Dec 2004
Posts: 51
12-21-2004 06:39
I'm not very good with code, so I'd just like to pass this question:

In theory, could I use this script to have a cloud of tiny little silver balls floating around me (within one half of one meter), and have them attracted to, say... a small ball I hide inside my chest?

Just really curious. :)
Wheel Fizz
Registered User
Join date: 26 May 2005
Posts: 36
06-14-2005 08:36
Hi,I got the Swarm script working fine,but can't seem to get the second small Swarm rez script to compile....I'm not really sure why :/ If any one can take a quick look at it,it is much appreciated :)
Burke Prefect
Cafe Owner, Superhero
Join date: 29 Oct 2004
Posts: 2,785
07-10-2005 21:28
I don't suppose this would work with AVs wearing a special attachment, that could possibly follow a 'leader'?

I'm asking because I need to be able to take around 6 - 12 people into the air and I'd like to keep them somewhat together.
Apotheus Silverman
I write code.
Join date: 17 Nov 2003
Posts: 416
07-11-2005 16:09
From: Burke Prefect
I don't suppose this would work with AVs wearing a special attachment, that could possibly follow a 'leader'?

I'm asking because I need to be able to take around 6 - 12 people into the air and I'd like to keep them somewhat together.


Now there is a novel concept. :)

I think this could be done with minimal modification if the avatars were sitting on the objects. It would probably take a bit more work to make it work as attachments.
_____________________
Apotheus Silverman
Shop SL on the web - SLExchange.com

Visit Abbotts Aerodrome for gobs of flying fun.
Ariaruil Stygian
Registered User
Join date: 14 Jun 2004
Posts: 27
07-12-2005 00:07
It's been done already, with those silly follower things that were modelled off that Sesame Street creature.... big head, tendril like threads below. Someone rezzed a bunch (maybe a dozen of these things) and we all sat on one each. Then the "leader"/"bunch of rexxed objects owner" began to fly and we bobbed and weaved our way along behind him like a pack of rabid cogs on crack. We even crossed sim boraders with minimal losses - and this was roughly nine months ago.

It was cool and fun and everyone had a good time.

I wish I could remember the name of the follower or the Sesame Street creature it was modelled on, I'd name it if I could.

But it IS doable.
Burke Prefect
Cafe Owner, Superhero
Join date: 29 Oct 2004
Posts: 2,785
07-12-2005 17:02
Okay. The problem I'm having now is that the follower code doesn't ways... um... follow. Or if it follows, it doesn't give proper feedback. Or.... when it's turned off, you can't move at all. SOOO..... I guess some kind of swarm will work. :D I'm trying not to use vehicles as sim-border crossings have been.... more pooched than ever.
twistedtwoubles Biggles
Registered User
Join date: 23 Sep 2004
Posts: 2
What is a swarm
09-14-2005 13:13
Here is a better way of explaining heard of a flock of segulls or even better a school of fish theres the concept lol :p
RyeDin Meiji
Reluctant Entrepeneur
Join date: 15 Mar 2005
Posts: 124
09-16-2005 06:50
From: Ariaruil Stygian
like a pack of rabid cogs on crack


Were they Cogswell cogs? And were there Spacely sprockets on speed?
_____________________
if (!you)
{
who();
}
Lana Tomba
Cheap,Fast or Good Pick 1
Join date: 5 Aug 2004
Posts: 746
eeerrrm
09-18-2005 19:13
Im probably missing something here..I got the swarm script to compile however neither of the rezzer scripts want to compile..am I doing something wrong?
Ariane Brodie
Registered User
Join date: 22 Apr 2004
Posts: 28
09-19-2005 13:30
I have a small shoreline property and used your script with almost no modification (except to shorten the life span to 150 so it does not kill my prim count). The fish are single prims, just a cylinder stretched into a fish shape (long x short y), colored fluorescent orange (256, 110, 0) material "light". The result is a school of goldfish.

Here is the script I made for the dispenser box, which I plopped down at the bottom of the water. It does not spawn fish at random locations (I determined it really wasnt necessary).
But it does add an on and off switch via command so as to not tick off the neighbors whose property the fish like to wander into.

CODE

//say "fish" to spawn three fish and start a school, repeated commands spawn 3 more.
//say "nofish" to stop the spawning
default
{
state_entry()
{
llListen(0, "", NULL_KEY, "");
vector mypos = llGetPos();
mypos = mypos + <0.0, 0.0, 1.0>;
llRezObject("fishy", mypos, ZERO_VECTOR, ZERO_ROTATION, 0);
mypos = mypos + <0.0, 0.0, 1.0>;
llRezObject("fishy", mypos, ZERO_VECTOR, ZERO_ROTATION, 0);
mypos = mypos + <0.0, 0.0, 1.0>;
llRezObject("fishy", mypos, ZERO_VECTOR, ZERO_ROTATION, 0);
llSetTimerEvent(20);
}

touch_start(integer total_number)
{
llResetScript();
}

listen(integer channel, string name, key id, string message) {
if (message == "nofish")
llSetTimerEvent(0.0);
if (message == "fish")
llResetScript();
}

timer()
{
vector mypos = llGetPos();
mypos = mypos + <0.0, 0.0, 1.0>;
llRezObject("fishy", mypos, ZERO_VECTOR, ZERO_ROTATION, 0);
}
}


One other idea I have, create a "predator" fish that when it collides with another fish it "eats" that fish which causes the fish to llDie and causes the predator to grow in size and slow its velocity. The predator dies if it does not eat a fish each minute.

Even more ambitious is have multiple predators, male and female. If two predators touch and they are the same gender, they fight and the biggest one wins. If two are of different genders, the biggest predator shrinks down to baby size (symbolizing the death of the big predator and the birth of the next generation).
Fafnir Fauna
Downy Cat
Join date: 23 Jun 2004
Posts: 34
10-25-2005 09:43
As of patch 1.7, this script no longer seems to function properly. God knows why.

The swarm objects just clump together in one spot and swirl around eachother like atomic particles!
Exchange Street
Registered User
Join date: 6 Sep 2004
Posts: 69
10-25-2005 10:30
From: Fafnir Fauna
As of patch 1.7, this script no longer seems to function properly. God knows why.

The swarm objects just clump together in one spot and swirl around eachother like atomic particles!

I think that's because volume detect objects were changed to phantom during the upgrade. If you change your objects back to volume detect they will hopefully work again.
Kyuubi David
Registered User
Join date: 30 Sep 2005
Posts: 49
10-27-2005 10:11
My dragonflys seem to be working ok. I changed a couple of the settings but nothing major.
Have a look at them in Achemon (62, 180) and see if they are doing the same thing yours are.
TXGorilla Falcone
KWKAT Planetfurry DJ
Join date: 4 May 2004
Posts: 176
10-28-2005 16:43
From: Ariaruil Stygian
It's been done already, with those silly follower things that were modelled off that Sesame Street creature.... big head, tendril like threads below. Someone rezzed a bunch (maybe a dozen of these things) and we all sat on one each. Then the "leader"/"bunch of rexxed objects owner" began to fly and we bobbed and weaved our way along behind him like a pack of rabid cogs on crack. We even crossed sim boraders with minimal losses - and this was roughly nine months ago.

It was cool and fun and everyone had a good time.

I wish I could remember the name of the follower or the Sesame Street creature it was modelled on, I'd name it if I could.

But it IS doable.


Leme guess... multible colors and have a sound file that repeats saying "Yeeep Yip Yip Yip Yip Yip Uhh Huh Uhh Huh"

I rember thouse when they spawned and started to follow people in the sandbox...

I still have my Yip Repeller in standby dew to thouse...
Sorry no idea how to fix just tossin my 2 cence in on that post
_____________________
Drunken Monkeys danceing on the tables.
Falling from the sky just like in a fable.
1 2