I'm guessing it is a result of your algorithm's complexity. When you create an instance, you add that unit to a group. Let's say you have N instances of boids. In each timer tick, you end up looping N times (that is fine). In each loop iteration, you iterate through all N - 1 other boids (within the ForGroup). So you end up with N^2 order of growth. To put that into perspective, 12 boids will end up doing 12*11=132 iterations per tick whereas 15 boids will do 15*14=210 iterations. And it gets worse as you add more--even adding just one more will add an extra 30 iterations.

If you can try to do all the group management within a single iteration, that'll make things a lot better. As for minimal optimizations--you may want to consider using natives like `IsUnitInRange`

and `IsUnitInRangeXY`

, no need to make your own functions.

And getId() should use a hashtable instead of doing a linear search. As it is, the algorithm may even be approaching N^3, which is a scary thing that no cpu should have to experience.