boid准则 :
在这个模型中,每个个体的行为只和它周围邻近个体的行为有关,每个个体只需遵循以下3条规则:
避免碰撞(Collision Avoidance): 避免和邻近的个体相碰撞。
速度一致(Velocity Matching): 和邻近的个体的平均速度保持一致。
向中心聚集(Flock Centering): 向邻近个体的平均位置移动。
编程方法:
首先每个bird之间都要检测一距离。
然后从所有鸟中找出在自己视野中的几个。
A在这几只鸟中找出离自己最近的,然后避免和它相撞。
B计算这几只鸟的平均速度然后向这个方向移动。
C计算这几只鸟的平均位置然后向这个位置移动。
D检查视野中的障碍物然后避免与绕过障碍。
对ABCD的效果进行叠加
对群体里面的每只鸟都要做上述的检测。
下面是一个法国的网站上的代码加了点注释
Vector2d.java
/**
* Classe representant un vecteur dans un espace a 2 dimensions.
*
* Pour exemple: Creation d'un vecteur de coordonnee ( 4.0 , 3.5 ).
* <pre>
* Vector2d vector = new Vector2d(4.0,3.5);
* </pre>
*
*
* Copyright (C) 2000 Gildas Cadin <gildas.cadin@enst-bretagne.fr>
* Lionel Deglise
* Pierre Thebaud
*
* School: E.N.S.T. Bretagne
* Technop鬺e de Brest Iroise, BP832, 29285 Brest CEDEX, FRANCE
* http://www.Enst-bretagne.fr
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
import java.lang.Math;
public class Vector2d
{
/**
* La coordonnee X du vecteur.
*/
public double x;
/**
* La coordonnee Y du vecteur.
*/
public double y;
/**
* Constructeur. Le vecteur construit est initialise a zero (x=0 et y=0).
*/
public Vector2d()
{
x = 0;
y = 0;
}
/**
* Constructeur. Le nouveau vecteur est initialise avec les parametres.
* @param double xVal : valeur d'initialisation de la coordonnee x du vecteur.
* @param double yVal : valeur d'initialisation de la coordonnee y du vecteur.
*/
public Vector2d(double xVal, double yVal)
{
x = xVal;
y = yVal;
}
/**
* Constructeur. Le nouveau vecteur est initialise par recopie
* @param Vector2d vect : vecteur a recopier.
* 构造拷贝
*/
public Vector2d(Vector2d vect)
{
x = vect.x;
y = vect.y;
}
/**
* Initialise le vecteur a zero (x=0 et y=0).
* 重制
*/
public void setZero()
{
x = 0;
y = 0;
}
/**
* Initialise le vecteur en fonction des parametres.
* @param double xVal : valeur d'initialisation de la coordonnee x du vecteur.
* @param double yVal : valeur d'initialisation de la coordonnee y du vecteur.
* 设置值
*/
public void setXY(double xVal, double yVal)
{
x = xVal;
y = yVal;
}
/**
* Initialise le vecteur par recopie
* @param Vector2d vect : vecteur a recopier.
* 赋值
*/
public void setXY(Vector2d vect)
{
x = vect.x;
y = vect.y;
}
/**
* Longueur du vecteur
* @return Retourne la longueur du vecteur.
* 取长度
*/
public double length()
{
return Math.sqrt(x*x + y*y);
}
/**
* Norme le vecteur a 1.
* Si la longueur est nulle, le vecteur reste tel quel.
* 如果长度小到一定程度(1E-9)认为是0
* 否则缩放x,y目的使向量长度保持为1。
*/
public void normaliser()
{
double zero = 1E-9;
double len = length();
// si longueur nulle, on ne peut rien faire...
if ( len*len < zero ) return;
fois( 1 / len );
}
/**
* Angle du vecteur
* @return Retourne l'angle du vecteur. C'est un valeur comprise entre -PI et PI.
*/
public double getAngle()
{
double zero = 1E-9;
if ( (x*x) < zero )
{
if ( y >= 0 ) return (Math.PI / 2);
return (-1 * Math.PI / 2);
}
if ( x >= 0 ) return Math.atan(y/x);
if ( y >= 0 ) return ( Math.PI + Math.atan(y/x) );
return ( Math.atan(y/x) - Math.PI );
}
/**
* Addition d'un vecteur.
* @param Vector2d vect : vecteur a additioner.
*/
public void plus(Vector2d vect)
{
x += vect.x;
y += vect.y;
}
/**
* Soustraction d'un vecteur.
* @param Vector2d vect : vecteur a soustraire.
*/
public void moins(Vector2d vect)
{
x -= vect.x;
y -= vect.y;
}
/**
* Homothetie d'un vecteur.
* @param double nbr : facteur de l'homothetie.
*/
public void fois(double nbr)
{
x *= nbr;
y *= nbr;
}
/**
* Produit scalaire d'un vecteur.
* @param Vector2d vect : vecteur sur lequel se projeter.
* @return Retourne le produit scalaire du vecteur avec le parametre.
*/
public double point(Vector2d vect)
{
return ( x*vect.x + y*vect.y );
}
}
Monde.java
/**
* Classe representant le monde contenant les Boids.
*
*
* Copyright (C) 2000 Gildas Cadin <gildas.cadin@enst-bretagne.fr>
* Lionel Deglise
* Pierre Thebaud
*
* School: E.N.S.T. Bretagne
* Technop鬺e de Brest Iroise, BP832, 29285 Brest CEDEX, FRANCE
* http://www.Enst-bretagne.fr
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.lang.Runnable;
//import java.lang.String;
public class Monde extends Canvas implements Runnable
{
/**
* Le nombre de Boids verts.
*/
private int nbGreen;
/**
* Le nombre de Boids rouges.
*/
private int nbRed;
/**
* Le tableau des Boids du monde.
*/
public Boid[] myBoids;
/**
* Le nombre de Boids du monde.
*/
public int nbBoids;
/**
* La demi-largeur du monde.
*/
public int largeur;
/**
* La demi-hauteur du monde.
*/
public int hauteur;
/**
* La zone d'affichage graphique a l'ecran.
*/
private Graphics myGraphics;
/**
* La zone de travail (gestion du double-buffering pour l'affichage).
*/
private Graphics myCanvas;
/**
* L'image de la zone de travail (gestion du double-buffering pour l'affichage).
*/
private Image myImage;
/**
* Le Thread de l'applet.
*/
private Thread update;
/**
* Variable permettant de savoir si l'on est en mode applet ou application.
*/
public boolean isApplet = true;
/**
* Constucteur.
*/
public Monde()
{
myBoids = new Boid[200];
nbBoids = 0;
}
/**
* definie le nombre de vert et de rouge.
* @param int nbVert: le nombre de Boids vert
* @param int nbRouge: le nombre de Boids rouge
*/
public void setVR(int nbVert, int nbRouge)
{
nbGreen = nbVert;
nbRed = nbRouge;
}
/**
* Initialisation de l'interface
*/
public void initGraphique()
{
// initialisation des variables largeur et hauteur.
largeur = (int) (getSize().width / 2);
hauteur = (int) (getSize().height / 2);
// mise en place du double buffering pour l'affichage.
myImage = createImage(largeur * 2, hauteur * 2);
myCanvas = myImage.getGraphics();
myGraphics = getGraphics();
}
/**
* Taille minimale du Monde
* @return La dimension du Monde
*/
public Dimension getMinimumSize()
{
return getPreferredSize();
}
/**
* Taille maximale du Monde
* @return La dimension du Monde
*/
public Dimension getMaximumSize()
{
return getPreferredSize();
}
/**
* Taille preferree du Monde
* @return La dimension du Monde
*/
public Dimension getPreferredSize()
{
return new Dimension(400,320);
}
/**
* Creation des habitants du monde: les Boids.
*/
public void init()
{
nbBoids = 0;
// creation des Boids verts.
for (int i = 0; i < nbGreen; i++)
{
myBoids[nbBoids++] = new Boid(this, Color.green);
}
// creation des Boids rouges.
for (int i = 0; i < nbRed; i++)
{
myBoids[nbBoids++] = new Boid(this, Color.red);
}
}
/**
* Debut du traitement.
*/
public void start()
{
if (update == null)
{
update = new Thread(this);
update.start();
}
}
/**
* Arret du traitement.
*/
public void stop()
{
if (update != null && update.isAlive())
{
//update.stop();
update = null;
}
}
/**
* Corps du traitement.
*/
public void run()
{
while (update != null)
{
repaint();
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
}
}
}
/**
* Affichage.
* @param Graphics g: le contexte d'affichage du Monde.
*/
public void paint(Graphics g)
{
int nb;
Color bgColor;
// effacement de la zone de travail
bgColor = new Color(0.6F, 0.6F, 0.6F);
myCanvas.setColor(bgColor);
myCanvas.fillRect(0, 0, largeur * 2 - 1, hauteur * 2 - 1);
myCanvas.setColor(Color.black);
myCanvas.drawRect(0, 0, largeur * 2 - 1, hauteur * 2 - 1);
// vie des Boids
for (nb = 0; nb < nbBoids; nb++)
myBoids[nb].update();
// affichage des Boids
for (nb = 0; nb < nbBoids; nb++)
myBoids[nb].paint(myCanvas);
// affichage de la zone de travail
myGraphics.drawImage(myImage, 0, 0, this);
}
/**
* Reactualisation de l'affichage.
* @param Graphics g: le contexte d'affichage du Monde.
*/
public void update(Graphics g)
{
paint(g);
}
}
Interface.java
/**
* Classe representant L'interface.
*
* Classe principale, utilisation:
* <pre>
* java Interface
* </pre>
* ou dans une page HTML:
* <pre>
* <APPLET code="Interface.class" width=400 height=320>
* Utilisez un navigateur compatible Java.
* </APPLET>
* </pre>
*
*
* Copyright (C) 2000 Gildas Cadin <gildas.cadin@enst-bretagne.fr>
* Lionel Deglise
* Pierre Thebaud
*
* School: E.N.S.T. Bretagne
* Technop鬺e de Brest Iroise, BP832, 29285 Brest CEDEX, FRANCE
* http://www.Enst-bretagne.fr
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.GridLayout;
import java.awt.Checkbox;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.lang.String;
public class Interface extends Applet implements ActionListener, WindowListener, ItemListener
{
/**
* Le Monde
*/
private Monde leMonde;
private Button startButton;
private Button stopButton;
private Button resetButton;
private TextField nbVertText;
private TextField nbRougeText;
private Checkbox regleSeparation;//避免碰撞规则
private Checkbox regleCohesion;
private Checkbox regleAllignement;
private Checkbox regleRepulsion;
private Checkbox avecMurs;
private Checkbox avecMursPlein;
/**
* Initialisation de l'interface et creation des habitants du monde: les Boids.
*/
public void init()
{
super.init();
Panel panel = new Panel(new BorderLayout());
Panel simulation= new Panel(new BorderLayout());
leMonde = new Monde();
simulation.add(leMonde, BorderLayout.CENTER);
Panel boutons = new Panel(new FlowLayout());
startButton = new Button("Start");
startButton.setActionCommand("start");
startButton.addActionListener(this);
startButton.setEnabled(false);
stopButton = new Button("Stop");
stopButton.setActionCommand("stop");
stopButton.addActionListener(this);
stopButton.setEnabled(false);
resetButton = new Button("Reset");
resetButton.setActionCommand("reset");
resetButton.addActionListener(this);
boutons.add(startButton);
boutons.add(stopButton);
boutons.add(resetButton);
simulation.add(boutons, BorderLayout.SOUTH);
panel.add(simulation,BorderLayout.WEST);
Panel parametres= new Panel(new GridLayout(8,1));
Panel greenOne = new Panel(new FlowLayout());
//greenOne.add( new Label("Nombre de Boids vert:"));
greenOne.add( new Label("绿色的数量:"));
nbVertText = new TextField("20",4);
greenOne.add(nbVertText);
parametres.add(greenOne);
Panel redOne = new Panel(new FlowLayout());
//redOne.add( new Label("Nombre de Boids rouge:"));
redOne.add( new Label("红色的数量:"));
nbRougeText = new TextField("2",4);
redOne.add(nbRougeText);
parametres.add(redOne);
//regleSeparation = new Checkbox("Regle de separation :" , true);
regleSeparation = new Checkbox("避免同类碰撞 :" , true);
regleSeparation.setName("regleSeparation");
regleSeparation.addItemListener(this);
parametres.add(regleSeparation);
//regleCohesion = new Checkbox("Regle de cohesion :" , true);
regleCohesion = new Checkbox("向中心对齐 :" , true);
regleCohesion.setName("regleCohesion");
regleCohesion.addItemListener(this);
parametres.add(regleCohesion);
//regleAllignement= new Checkbox("Regle de allignement :" , true);
regleAllignement= new Checkbox("对齐速度矢量 :" , true);
regleAllignement.setName("regleAllignement");
regleAllignement.addItemListener(this);
parametres.add(regleAllignement);
//regleRepulsion = new Checkbox("Regle de repulsion :" , true);
regleRepulsion = new Checkbox("避免异类碰撞:" , true);
regleRepulsion.setName("regleRepulsion");
regleRepulsion.addItemListener(this);
parametres.add(regleRepulsion);
//avecMurs = new Checkbox("Presence de murs :" , true);
avecMurs = new Checkbox("避免边界碰撞 :" , true);
avecMurs.setName("avecMurs");
avecMurs.addItemListener(this);
parametres.add(avecMurs);
avecMursPlein = new Checkbox("Murs /"pleins/" :" , false);
avecMursPlein.setName("avecMursPlein");
avecMursPlein.addItemListener(this);
parametres.add(avecMursPlein);
panel.add(parametres,BorderLayout.EAST);
add(panel);
}
/**
* Methode appele a l'appel de l'application.
*/
public static void main(String args[])
{
// creation des fenetres et du monde
Frame fenetre = new Frame("Boids");
Interface inter = new Interface();
fenetre.addWindowListener(inter);
// mise en place de l'interface
fenetre.add(inter);
fenetre.setSize(400, 320);
// initialisation de l'interface.
inter.init();
fenetre.pack();
fenetre.show();
inter.leMonde.initGraphique();
}
/**
* Pour debuter l'applet
*/
public void start()
{
leMonde.initGraphique();
}
/**
* Methode de gestion des evenements
*/
public void actionPerformed(ActionEvent e)
{
if (e.getActionCommand().equals("start"))
{
stopButton.setEnabled(true);
leMonde.start();
startButton.setEnabled(false);
resetButton.setEnabled(false);
}
if (e.getActionCommand().equals("stop"))
{
startButton.setEnabled(true);
resetButton.setEnabled(true);
leMonde.stop();
stopButton.setEnabled(false);
}
if (e.getActionCommand().equals("reset"))
{
startButton.setEnabled(true);
leMonde.setVR( new Integer(nbVertText.getText()).intValue(),
new Integer(nbRougeText.getText()).intValue() );
leMonde.init();
leMonde.repaint();
}
}
public void windowOpened(WindowEvent e) {}
public void windowClosing(WindowEvent e)
{
leMonde.stop();
System.exit(0);
}
public void windowClosed(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
public void itemStateChanged(ItemEvent e)
{
if ( ! (e.getItemSelectable() instanceof Checkbox) )
return;
String regle = ((Checkbox)e.getItemSelectable()).getName() ;
if (regle.equals("regleSeparation"))
Boid.regleSeparation = regleSeparation.getState();
if (regle.equals("regleCohesion"))
Boid.regleCohesion = regleCohesion.getState();
if (regle.equals("regleAllignement"))
Boid.regleAlignement = regleAllignement.getState();
if (regle.equals("regleRepulsion"))
Boid.regleRepulsion = regleRepulsion.getState();
if (regle.equals("avecMurs"))
{
Boid.avecMurs = avecMurs.getState();
if (avecMurs.getState())
avecMursPlein.setEnabled(true);
else
{
avecMursPlein.setEnabled(false);
avecMursPlein.setState(false);
Boid.avecMursPlein = false;
}
}
if (regle.equals("avecMursPlein"))
Boid.avecMursPlein = avecMursPlein.getState();
}
}
Boid.java
/**
* Classe representant un Boid dans un espace a 2 dimensions.
*
* Pour exemple: Creation d'un Boid dans le Monde monMonde, de couleur rouge.
* <pre>
* Boid boid = new Boid(monMonde, Color.Red);
* </pre>
* @see Monde
*
*
* Copyright (C) 2000 Gildas Cadin <gildas.cadin@enst-bretagne.fr>
* Lionel Deglise
* Pierre Thebaud
*
* School: E.N.S.T. Bretagne
* Technop鬺e de Brest Iroise, BP832, 29285 Brest CEDEX, FRANCE
* http://www.Enst-bretagne.fr
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
import java.awt.Color;
import java.awt.Graphics;
public class Boid
{
/**
* Le vecteur position.
*/
public Vector2d position;
/**
* Le vecteur vitesse
*/
public Vector2d vitesse;
/**
* La couleur
*/
public Color myColor;
/**
* Le monde ou l'on est.
*/
private Monde leMonde;
/**
* Le vecteur representant la force s'appliquant.
*/
private Vector2d laForce;
/**
* Le vecteur acceleration.
*/
private Vector2d acceleration;
//避免同类碰撞
public static boolean regleSeparation = true;
//向中心对齐
public static boolean regleCohesion = true;
//速度对齐
public static boolean regleAlignement = true;
//避免异类碰撞
public static boolean regleRepulsion = true;
//避免边界碰撞
public static boolean avecMurs = true;
//?
public static boolean avecMursPlein = true;
/**
* Vitesse maximale des Boids.
*/
public static double maxSpeed = 2.5;
/**
* Force maximale applicable a un Boid.
*/
public static double maxForce = 1.7;
/**
* Distance utilise pour la separation.
*/
public static double distSeparation = 10.0;
/**
* Distance utilise pour la cohesion.
*/
public static double distCohesion = 100.0;
/**
* Distance utilise pour l'alignement.
*/
public static double distAlignement = 100.0;
/**
* Distance utilise pour la repulsion.
*/
public static double distRepulsion = 100.0;
/**
* Distance utilise pour les obstacles.
*/
public static double distObstacles = 20;
/**
* Angle de visibilite du Boid
*/
public static double visibleAngle = 90.0;
/**
* Facteur correctif de la force de separation.
*/
public static double separationForce = 1.0;
/**
* Facteur correctif de la force de cohesion.
*/
public static double cohesionForce = 0.0001;
/**
* Facteur correctif de la force d'allignement.
*/
public static double alignementForce = 1.0;
/**
* Facteur correctif de la force de repulsion.
*/
public static double repulsionForce = 5.0;
/**
* Facteur correctif de la force due aux obstacles.
*/
public static double obstaclesForce = 80.0;
/**
* Masse du Boid.
*/
public static double masse = 1.0;
/**
* Facteur de regulation de l'acceleration.
*/
public static double vecteurAccel = 0.85;
/**
* Cosinus de l'angle utilise pour l'alignement.
*/
private static double visibleAngleCos;
/**
* Cosinus de l'angle utilise pour la cohesion.
*/
private static double coheAngleCos;
static
{
visibleAngleCos = Math.cos(visibleAngle);
}
/**
* @param Monde leM : le Mmonde dans lequel on est.
* @param Color color : la couleur du Boid.
*/
public Boid(Monde leM, Color color)
{
leMonde = leM;
myColor = color;
position = new Vector2d();
vitesse = new Vector2d();
laForce = new Vector2d();
acceleration = new Vector2d();
// initialisation de la position
position.setXY( (java.lang.Math.random() - 0.5) * leMonde.largeur,
(java.lang.Math.random() - 0.5) * leMonde.hauteur );
// initialisation de la vitesse
vitesse.setXY( java.lang.Math.random() - 0.5,
java.lang.Math.random() - 0.5 );
vitesse.normaliser();
vitesse.fois(0.25);
vitesse.plus(new Vector2d(0,0.75));
vitesse.fois(Boid.maxSpeed);
}
/**
* @param Boid otherBoid : le Boid dont on est peut-etre proche.
* @param double distance : la ditance de decision.
* return TRUE si on est proche, FALSE sinon.
*/
private boolean proche(Boid otherBoid, double distance)
{
Vector2d tmp;
tmp = new Vector2d(position);
tmp.moins(otherBoid.position);
// si on est trop loin tand-pis.
if ( tmp.length() > distance )
return false;
return true;
}
/**
* @param Boid otherBoid : le Boid que l'on voit peut-etre.
* @param double distance : la ditance de decision.
* return TRUE si on le voit, FALSE sinon.
*/
private boolean visible(Boid otherBoid, double distance)
{
double deltaAngle;
Vector2d tmp;
Vector2d tmp2;
tmp = new Vector2d(otherBoid.position);
tmp.moins(position);
// si on est trop loin tand-pis.
if ( tmp.length() > distance )
return false;
tmp2 = new Vector2d(vitesse);
tmp2.normaliser();
// on regarde le produit scalaire...
if ( tmp2.point(tmp) < Boid.visibleAngleCos)
return false;
return true;
}
/**
* @return Vector2d force : retourne la force du a la separation.
*/
private void separation(Vector2d force)
{
int nbr;
Boid otherBoid;
Vector2d tmp;
double len;
tmp = new Vector2d();
force.setZero();
for (nbr = 0; nbr < leMonde.nbBoids; nbr++)
{
otherBoid = leMonde.myBoids[nbr];
if ( (otherBoid != this) && (visible(otherBoid,Boid.distSeparation)) )
{
tmp.setXY(position);
tmp.moins(otherBoid.position);
len = tmp.length();
// force en 1/r
tmp.fois( 1 / (len*len) );
force.plus(tmp);
}
}
}
/**
* @return Vector2d force : retourne la force du a la cohesion.
*/
private void cohesion(Vector2d force)
{
int nbr;
int nbTot;
Boid otherBoid;
force.setZero();
nbTot = 0;
for (nbr = 0; nbr < leMonde.nbBoids; nbr++)
{
otherBoid = leMonde.myBoids[nbr];
if ( (otherBoid != this)
&& (otherBoid.myColor == myColor)
&& (visible(otherBoid,Boid.distCohesion)) )
{
nbTot++;
force.plus(otherBoid.position);
}
}
// calcul du barycentre...
if (nbTot > 0)
{
force.fois(1 / nbTot);
force.moins(position);
}
}
/**
* @return Vector2d force : retourne la force du a l'alignement.
*/
private void alignement(Vector2d force)
{
int nbr;
int nbTot;
Boid otherBoid;
Vector2d tmp;
tmp = new Vector2d();
force.setZero();
nbTot = 0;
for (nbr = 0; nbr < leMonde.nbBoids; nbr++)
{
otherBoid = leMonde.myBoids[nbr];
if ( (otherBoid != this)
&& (otherBoid.myColor == myColor)
&& (visible(otherBoid,Boid.distAlignement)) )
{
nbTot++;
tmp.setXY(otherBoid.vitesse);
tmp.fois( 1 / tmp.length() );
force.plus(tmp);
}
}
if (nbTot > 0)
{
force.fois( 1 / nbTot );
}
}
/**
* @return Vector2d force : retourne la force du a la repulsion.
*/
private void repulsion(Vector2d force)
{
int nbr;
int nbTot;
int nbCopain;
Boid otherBoid;
Vector2d tmp;
double len;
tmp = new Vector2d();
force.setZero();
nbTot = 0;
nbCopain = 1;
for (nbr = 0; nbr < leMonde.nbBoids; nbr++)
{
otherBoid = leMonde.myBoids[nbr];
if ( (otherBoid != this)
&& (otherBoid.myColor != myColor)
&& (proche(otherBoid,Boid.distRepulsion)) )
{
nbTot++;
tmp.setXY(position);
tmp.moins(otherBoid.position);
len = tmp.length();
tmp.fois( 1 / (len*len) );
force.plus(tmp);
}
if ( (otherBoid != this)
&& (otherBoid.myColor == myColor)
&& (proche(otherBoid,Boid.distRepulsion)) )
{
nbCopain++;
}
}
if ( nbCopain > (nbTot*2) )
force.fois( -1 );
}
/**
* @return Vector2d force : retourne la force due aux obtacles (murs).
*/
private void obstacles(Vector2d force)
{
Vector2d tmp= new Vector2d();
force.setZero();
if (((leMonde.largeur-position.x)<distObstacles) && vitesse.x>0)
{
tmp.setXY(-vitesse.x/(leMonde.largeur-position.x),0);
force.plus(tmp);
}
if ((position.x<-leMonde.largeur+distObstacles) && vitesse.x<0)
{
tmp.setXY(-vitesse.x/(leMonde.largeur+position.x),0);
force.plus(tmp);
}
if ((position.y>leMonde.hauteur-distObstacles) && vitesse.y>0)
{
tmp.setXY(0,-vitesse.y/(leMonde.hauteur-position.y));
force.plus(tmp);
}
if ((position.y<-leMonde.hauteur+distObstacles) && vitesse.y<0)
{
tmp.setXY(0,-vitesse.y/(leMonde.hauteur+position.y));
force.plus(tmp);
}
force.fois(masse);
}
/**
* Reajuste la position pour etre dans la fenetre de vue du Monde.
*/
private void ajusteAuMonde()
{
double posX;
double posY;
posX = position.x;
posY = position.y;
if (!Boid.avecMursPlein)
{//穿越
if ( posX > leMonde.largeur ) posX -= 2 * leMonde.largeur;
if ( posX < ( -1 * leMonde.largeur ) ) posX += 2 * leMonde.largeur;
if ( posY > leMonde.hauteur ) posY -= 2 * leMonde.hauteur;
if ( posY < ( -1 * leMonde.hauteur ) ) posY += 2 * leMonde.hauteur;
}
else
{//反弹
if ( posX > leMonde.largeur ) posX = leMonde.largeur-0.1;
if ( posX < ( -1 * leMonde.largeur ) ) posX = -leMonde.largeur+0.1;
if ( posY > leMonde.hauteur ) posY = leMonde.hauteur-0.1;
if ( posY < ( -1 * leMonde.hauteur ) ) posY = -leMonde.hauteur+0.1;
}
position.setXY(posX,posY);
}
/**
* Applique les forces calculees et fait avancer le Boid.
*/
public void appliquerForce()
{
// on borne la force appliquee.
if (laForce.length() > Boid.maxForce)
{
laForce.normaliser();
laForce.fois(Boid.maxForce);
}
// contribution de la masse.
laForce.fois( 1 / Boid.masse );
// mise a jour de l'acceleration et de la vitesse.
acceleration.setXY(laForce);
vitesse.plus(acceleration);
// on borne la vitesse.
if (vitesse.length() > Boid.maxSpeed)
{
vitesse.normaliser();
vitesse.fois(Boid.maxSpeed);
}
// on met a jour la position
position.plus(vitesse);
// on s'ajuste en fonction des dimension du Monde.
ajusteAuMonde();
}
/**
* Un instant de vie du Boid.
*/
public void update()
{
Vector2d force;
force = new Vector2d();
laForce.setZero();
if (Boid.regleSeparation)
{
separation(force);
force.fois(Boid.separationForce);
laForce.plus(force);
}
if (Boid.regleCohesion)
{
cohesion(force);
force.fois(Boid.cohesionForce);
laForce.plus(force);
}
if (Boid.regleAlignement)
{
alignement(force);
force.fois(Boid.alignementForce);
laForce.plus(force);
}
if (Boid.regleRepulsion)
{
repulsion(force);
force.fois(Boid.repulsionForce);
laForce.plus(force);
}
if (Boid.avecMurs)
{
obstacles(force);
force.fois(Boid.obstaclesForce);
laForce.plus(force);
}
appliquerForce();
}
/**
* Affichage du Boid.
* @param Graphics g: le contexte d'affichage du Monde.
*/
public void paint(Graphics g)
{
int posX;
int posY;
double direction;
double cos;
double sin;
posX = leMonde.largeur + new Double(position.x).intValue();
posY = leMonde.hauteur + new Double(position.y).intValue();
direction = vitesse.getAngle();
cos = Math.cos(direction);
sin = Math.sin(direction);
g.setColor(myColor);
g.drawLine( posX + (int) ( 5 * cos ),
posY + (int) ( 5 * sin ),
posX - (int) ( 2 * cos + 2 * sin ),
posY - (int) ( 2 * sin - 2 * cos ) );
g.drawLine( posX + (int) ( 5 * cos ),
posY + (int) ( 5 * sin ),
posX - (int) ( 2 * cos - 2 * sin ),
posY - (int) ( 2 * sin + 2 * cos ) );
g.drawLine( posX - (int) ( 2 * cos + 2 * sin ),
posY - (int) ( 2 * sin - 2 * cos ),
posX - (int) ( 2 * cos - 2 * sin ),
posY - (int) ( 2 * sin + 2 * cos ) );
}
}