   Need Help with flocking AI
  Hey, guys. I have a problem with my first AI project. It's about flocking. The problem is, it looks so unnatural. They tend to flock with no more than 2 or 3 boids.SWF : http://megaswf.com/view/03279d52b96a29a5590e45eff91e20a3.htmlYes, I've read the boid's page. I've understood the algorithm ( I think) but I failed to implement it.Dunno whats wrong with my code.Okay, my approach for this AI is :1) Find the "desired" direction based on separation, alignment, and alignment rule2) Steer to "desired" directionHowever, this is my code. It's done in AS 3 (FYI I've just start learning AS 3 few days ago).For simplicity, I set all the boids speed to a same value.So we will find the desired direction :function Flock_Calc(me:MovieClip):void {//begins flocking AIvar Dx:Number = 0; //Resultant Vector for X axisvar Dy:Number = 0; //Resultant Vector for Y axisvar count:Number = 0;var avgDir:Number = 0; //avg direction - for alignment rulefor(var i = 0; i<BodyArray.length; i++) {//if the boid is me, then continue to next iterationif(i==me.id) continue;var dx:Number = (BodyArray.x-me.x); //distance in X axis between me and other boidvar dy:Number = (BodyArray.y-me.y);var dr:Number = Math.sqrt(dx*dx + dy*dy); //distance//if the boid is out of my sight, continue to next iterationif(dr>me.sightRadius) continue;//cohesion rule//the farther the other boid, the stronger the desire to steer to that locationDx += LinearFunction(dx,0,0,me.sightRadius,1); //this function returns 0 when dx = 0Dy += LinearFunction(dy,0,0,me.sightRadius,1); //return 1 for dx = sightradius and -1 for dx = -sightradius//alignmentavgDir += BodyArray.rotation;count++;//separation//the nearer the boid, the stronger desire to steer away from that locationif(dr<=me.sepRadius) {if(dx>0) Dx -= LinearFunction(Math.abs(dx),0,1,me.sepRadius,0); //return 1 if dx = 0 and 0 if dx = sepradiuselse Dx += LinearFunction(Math.abs(dx),0,1,me.sepRadius,0);if(dy>0) Dy -= LinearFunction(Math.abs(dy),0,1,me.sepRadius,0);else Dy += LinearFunction(Math.abs(dy),0,1,me.sepRadius,0);}}if(count > 0) {//computing the final alignment ruleavgDir += me.rotation; //add my directioncount++;avgDir /= count; //get the averageDx += Math.cos(avgDir); //Math.cos returns value between -1 and 1Dy += Math.sin(avgDir);//the final magnitude of Dx and Dy doesnt matter since we just find for the directionme.toRotate = Math.atan2(Dy,Dx)/Math.PI*180;}else me.toRotate = me.rotation;}Here is the steering method://calculate steeringme.rotation += (me.toRotate - me.rotation)*0.1;and here is the linear function, just in case you need it//LinearFunction()//(y-y1)/(y2-y1) = (x-x1)/(x2-x1)//(y-y1) = (x-x1)/(x2-x1)*(y2-y1)// y= [(x-x1)/(x2-x1)*(y2-y1)] + y1//LinearFunction returns y for any given x passed by valx function LinearFunction(valx:Number, x1:Number, y1:Number, x2:Number, y2:Number):Number {return (valx - x1)/(x2-x1) *(y2-y1) + y1;}
  I haven't really looked at your code, but from looking at the SWF, it seems that the "area of influence" is too small. Try increasing the value of sightRadius and see if they form larger flocks.   ;  Hi,//cohesion rule//the farther the other boid, the stronger the desire to steer to that locationDx += LinearFunction(dx,0,0,me.sightRadius,1); Dy += LinearFunction(dy,0,0,me.sightRadius,1); This is the same as:Dx += dx/me.sightRadius + 1Dy += dy/me.sightRadius + 1Is this really what you want?//calculate steeringme.rotation += (me.toRotate - me.rotation)*0.1;So you will never reach the desired angle! You might want to use something along the linesrotationDiff = me.toRotate - me.rotationif rotationDiff > maxRotation:    rotationDiff = maxRotationelif rotationDiff < -maxRotation:    rotationDiff = -maxRotationHope this helps;Quote:This is the same as:Dx += dx/me.sightRadius + 1Dy += dy/me.sightRadius + 1Is this really what you want?nope, actually it's the same as :Dx += dx/me.sightRadiusDy += dy/me.sightRadiusand thats what I want, returning value between -1 and 1.okay I've increased the sight radius and tweaked to this ://calculate steeringvar rotdiff:Number = (me.toRotate - me.rotation);var maxrot:Number = 15;if(rotdiff<-maxrot)  rotdiff = -maxrot;else if(rotdiff>maxrot) rotdiff = maxrot;me.rotation += rotdiff;still not much difference :/By the way other problem that I could spot is the boid tends to leave the flock.you may see it in the SWF I posted earlier.   ;  I am assuming the following locomotion model: velocity is constant in magnitude always parallel to the agent facing, rotations are modified by changing the angular velocity like thisrotation += (deisredRotation - currentRotation) * angVelMagSo, provided there are not bugs (which I have not verified thoroughly) and that I understood the locomotion model correctly, I think you should decrease sepRadius which should be quite smaller than "sightRadius" (half maybe?).Question / considerations1) Are you using the rotation in degrees to draw the agent on the screen?2) I think that the contribution coming from the "alignment" behaviour is too small compared to the others or, at least, their ratio depends on how many agents are in sight.I hope this can help.   ;Quote:I am assuming the following locomotion model: velocity is constant in magnitude always parallel to the agent facing, rotations are modified by changing the angular velocity like thisrotation += (deisredRotation - currentRotation) * angVelMagSo, provided there are not bugs (which I have not verified thoroughly) and that I understood the locomotion model correctly, I think you should decrease sepRadius which should be quite smaller than "sightRadius" (half maybe?).Question / considerations1) Are you using the rotation in degrees to draw the agent on the screen?2) I think that the contribution coming from the "alignment" behaviour is too small compared to the others or, at least, their ratio depends on how many agents are in sight.I hope this can help.thank you for your replyActually, I have rewrite the code, just in case my old code contains bugs (which actually it is) and finished just now.the bug was few math errors.. for example, I forgot to convert from degree to radian. yes, in the new code, I implemented changing rotation technique just like the one you mention. And it gives a smooth turn :)The new code is using a slight different algorithm. The new algorithm makes separation rules run exclusive than the other 2 rules. meaning thatif separation rules work, then alignment and cohesion rules are ignored. And thats slight different, from Reynold's. AFAIK Reynold's does run all rules whenever possible.here is the result : http://www.swfcabin.com/open/1245403988tell me what you think.Honestly I'm not quite happy with it since the boids sometime collide with each other.   ;  Before proceeding just try with 2 or three agents in order to thoroughly understand what's going on.Prioritised weighted truncated sum, that's what you could need.After you have calculated your DX and DY normalised (their range is -1 <-> 1 isn't it?) you just multiply them by a weight (maybe between 0 and 1) (you will need to tune those though) and comparing them against a threshold which will truncate your running total.This will enable a more smooth mixing of the three behaviours having a priority defined.A high weight for the separation behaviour will make overlapping less likely, however if you want to keep them tight you shall have the separation behaviour effect take place only at a close range that is you should keep the "sepRadius" parameter quite smaller than "sightRadius".The locomotion model makes things quite difficult since it is the rotation that defines the vector of the velocity, not the steering force (which there is not in your program). So if two agents are going one towards another, when the separation behaviours comes in, it could be too late since its effect is rotating the agents and not directly changing their velocity. You could try and increase the maximum angular speed and do something more sophisticated like:currentAngularVelocity = 0;if(Math.abs(desiredRotation - rotation) > PI / 2.0){//if you use a smaller angle you can have hard turns more oftencurrentAngularVelocity = MAX_ANG_VEL;}else{currentAngularVelocity = MAX_ANG_VEL * Math.sin(desiredRotation - rotation)}deltaRotation = dt * currentAngularVelocityrotation += deltaRotation;rotation = rotation % (Math.PI * 2.0);//check this since maybe the rotation value should have the same range as desiredRotationThis should avoid trembling and oscillations or make them less frequent.I hope I didn't make too many mistakes :-) and I helped a bit.   