群体模拟方法——biod

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>
 * &lt;APPLET code="Interface.class" width=400 height=320>
 *  Utilisez un navigateur compatible Java.
 * &lt;/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 ) );
 }
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值