This is a continuation of Swarm, Lesson 3
By now we should have your red circle chasing your mouse pointer around the
screen, which is all very fine and dandy. However, it seems a little more
like "fetch" instead of "swarm." We want several dots chasing the mouse
pointer.
It would be nicest if we could have any number of dots we wish. This
would be tiresome when writing all those circles to the svg by hand.
What if there were an easier way........
Instead, we'll create the circles in Javascript. We can run a routine when
the document is first loaded to create these circles . The first step would
be to set up our global variables.
First, you should notice that dx and dy are obsolete, so they can be deleted.
Next, you'll want to think about how the script gets to each circle it
creates. We could very easily go through the DOM to get every child of the
group we put the dots in, but that takes time and there's a better shortcut.
Instead, I've created an array whose elements are pointers to the dots in the
SVG document.
var Swarm = new Array(0);
Now when we create our bugs, we just populate this array. Here's a code
snippet which creates one bug and adds it as a child to the "Hive" group
(Note: I named my group "Hive." Name yours what you like):
var NewBug = SVGDocument.createElement('circle');
NewBug.setAttribute('cx', X);
NewBug.setAttribute('cy', Y);
NewBug.setAttribute('r', '1');
NewBug.getStyle().setProperty('fill', 'black');
SVGDocument.getElementById('Hive').appendChild(NewBug]);
This snippet will create the circle at (X,Y). The reason for this is that
we don't want all of the circles to be created at the same point. If we
did, they would all behave exactly the same and you'd never be able to tell
there was more than one. To randomize their position, use the following
code above the point where the bug is created:
var Bounds = SVGDocument.getElementById('BoundingBox')
var Width = Bounds.getAttribute('width') * 1
var Height = Bounds.getAttribute('height') * 1
var X = Math.random() * Width;
var Y = Math.random() * Height;
This picks a random point in our bounding box for the point to appear. Now
all we need to do is add that point to our array of insects
Swarm[Swarm.length] = NewBug
At this point we can wrap this up into a routine and every time it is called
create a bug. Here's what my routine looks like:
function Spawn(NumberToCreate, PresetX, PresetY) // If PresetX = PresetY = -1, use random point
{
var Bounds = SVGDocument.getElementById('BoundingBox')
var Width = Bounds.getAttribute('width') * 1
var Height = Bounds.getAttribute('height') * 1
for (var I = 0; I < NumberToCreate; I++)
{
if ((PresetX == -1) && (PresetY == -1))
{
X = Math.random() * Width;
Y = Math.random() * Height;
}
else
{
X = PresetX
Y = PresetY
}
var NewBug = SVGDocument.createElement('circle');
NewBug.setAttribute('cx', X);
NewBug.setAttribute('cy', Y);
NewBug.setAttribute('r', '1');
NewBug.getStyle().setProperty('fill', 'black');
SVGDocument.getElementById('Hive').appendChild(NewBug);
Swarm[Swarm.length] = NewBug
}
}
I extended it to create all the bugs I want with one call. I also made it able
to place those points at a position of my choosing. You'll see how this can be
handy later.
Now we should take a look at it. Add the routine:
Spawn(4, -1, -1);
To the onload event of the SVG doc and take a look. There should be 4 dots
randomly placed in the bounding box.
Of course we still have that first dot floating around. But none of the new
dots move. To fix this we have to modify the DoWork() routine a bit. First,
all position specific work such as distance to target, and x and y locations
need to be placed in a loop:
for (var I = 0; I < Swarm.length; I++)
{
var X = RedCircle.getAttribute('cx') * 1
var Y = RedCircle.getAttribute('cy') * 1
var Rad = RedCircle.getAttribute('r') * 1
Distance = Math.pow(Math.pow(DestinationX - X, 2) + Math.pow(DestinationY - Y, 2), .5)
dx = ((Speed * DestinationX) - (Speed * X)) / Distance
dy = ((Speed * DestinationY) - (Speed * Y)) / Distance
if ((X + Rad + dx > MaxX) && (dx > 0)) dx *= -1
if ((X - Rad + dx < MinX) && (dx < 0)) dx *= -1
if ((Y + Rad + dy > MaxY) && (dy > 0)) dy *= -1
if ((Y - Rad + dy < MinY) && (dy < 0)) dy *= -1
X += dx
Y += dy
RedCircle.setAttribute('cx', X);
RedCircle.setAttribute('cy', Y);
}
Next replace every existence of "RedCircle" should be replaced with
Swarm[I]. That should do it. Load up the svg doc and you'll see four little
dots that chase you around.
S W A R M !
The most important thing about this process is that you make sure that you not
do any redundant work. Any little piece of code that executes more than it
needs to just burns cycles and possibly disallows slower computers from
running your svg doc. For instance, we don't need to get the bounds more than
once as these are fixed across all dots.
In the next lesson we'll update the algorithm for these bugs. As is they get
the job done. The current method is also efficient. However, it is incredibly
difficult to extend to add a couple of features which mimic bug behaviour. I'll
leave you with the following questions to ponder:
What kind of turning radius does a bug have?
Does that turning radius depend on speed?
Does speed depend on turning radius?
Does a bug always head in a straight line?
Do larger numbers of bugs affect their turning radii?
I'll try to answer at least the first question in the next lesson.