09.18 BOIDSv1.00 ~鮫現ル~

名古屋港水族館の都市伝説のようにネットに出回っている話の1つに、こんなのがあります。
元々水槽の中はイワシとまぎれて入ってしまった魚だけの水槽だったそうですが、単調な餌やりと外敵がいないことから段々イワシが慣れてきて群れで泳がなくなってきたとか。そこで飼育員の方が考え出した秘策が「サメを入れること」だったそうです。実際朝の餌やり説明の中で「イワシがひとりだけ群れからはぐれると、サメが食べることがありますが自然の食物連鎖だと思って勘弁してください」という説明があります。

っということで、今回作成したBOIDSモデルも完全に動きというのは1つとしてありませんが、ぐるぐる廻っているいう点では同じ動きで長いこと見ているとつまらなくなってしまいますので、ここでサメを投入してみましょう。


★ サメ仕様
まずどんな動きなのか動画を見ていただきます。

【緊急時の回避行動】


1)サメは単独行動です。

2)サメはイワシを避けませんが、いわしはサメを避けます。

3)サメは避けませんので、壁以外はまっすぐ進み壁でUターンします。

4)イワシはサメの進行方向に対し下記のような緊急回避行動をとります
  サメポッド(赤〇青進行方向矢印)に対し、現在進行している方向とは無関係に最大限の速度で直角方向へ回避します。
  多分短い区間で考えると少しずつ回避しているのでしょうけど、今回は90度回避とします。




★プログラムにしてみましょう。

【データ宣言部】


/* サメに関するパラメータ */
#define _SAME_COUNT_MAX   1
    /* サメの数(配列決定用最大値) */
#define _SAME_DRAW_SIZE   20
    /* サメの大きさ デフォルト値 */
#define _SAME_SPEEDRATE   1
    /* 高速化のためのsin/cos */
#define _SAME_SPEEDRATE360 (360 * _SAME_SPEEDRATE)

#define _SAME_VIEW_ANGLE  70
    /* サメ視認視野の長さ */
#define _SAME_TURN_ANGLE (135.0 * M_PI / 180)
    /* サメ回避角度 */


Iwashi_str same[_SAME_COUNT_MAX];
/* サメの情報 */






【初期設定部】

System::Drawing::Pen ^ SamePodPen;
      /* 鮫用のペン */
double   cos_antisame_angle;
double   sin_antisame_angle;
      /* サメ遭遇時移動角度sin cos */
double   SamePodSize;
      /* サメサイズ */
double   SamePodHalfSize;
      /* サメ1/2サイズ */


  sinAngle = gcnew array<double>(_SAME_SPEEDRATE360);
  cosAngle = gcnew array<double>(_SAME_SPEEDRATE360);
  for (int i = 0; i < _SAME_SPEEDRATE360; i++)
  {
    sinAngle[i] = sin(_DegreeToRadian((double)i / _SAME_SPEEDRATE));
    cosAngle[i] = cos(_DegreeToRadian((double)i / _SAME_SPEEDRATE));
  }
  SamePodPen = gcnew Pen(Color::Red, 3);

  cos_antisame_angle = cos(_SAME_TURN_ANGLE);
  sin_antisame_angle = sin(_SAME_TURN_ANGLE);




【回避処理部】


/************************************************************************
* 位置のセッター                            *
************************************************************************/
void setPosision(int index, double x, double y)
{
  if (index < 10000) /* Iwashi<10000 else サメ集合体 */
  {
    iwashi[index].Pos.x = x;
    iwashi[index].Pos.y = y;
  }
  else
  {
    same[index-10000].Pos.x = x;
    same[index-10000].Pos.y = y;
  }
}

/************************************************************************
* 速度のセッター                            *
************************************************************************/
void setVelocity(int index, double vx, double vy)
{
  if (index < 10000) /* Iwashi<10000 else サメ集合体 */
  {
    iwashi[index].Vec.x = vx;
    iwashi[index].Vec.y = vy;
  }
  else
  {
    same[index-10000].Vec.x = vx;
    same[index-10000].Vec.y = vy;
  }
}

/************************************************************************
* サメ回避                               *
* 前方視野角度内で一定距離ない個体を規定角度で避ける          *
* 相手の進行方向に関係なく、相手の位置と反対に逃げる          *
************************************************************************/
void SameSakeru(int index,Iwashi_str *IwashiThis)
{
  int       nearSame;
  double     isAngle;
  int       isLeft;
  Vector_str   Vec;
  double     dist;

  nearSame = 0;

  for (int i = 0; i < _SAME_COUNT_MAX; i++)
  {
    isCrosDot(&same[i], IwashiThis, &isAngle, &isLeft);  /* サメ主体の視野分析に変更 */
    if (fabs(isAngle) <= _SAME_TURN_ANGLE)
    {    /* サメ主体視野内の検出 */
      dist = distanceTo(&same[i].Pos, &IwashiThis->Pos);
      if ( dist <= ((PodHalfSize+SamePodHalfSize) + _SAME_VIEW_ANGLE))
      {
        if (nearSame != 0)
        {
          if (dist <= distanceTo(&same[nearSame-1].Pos , &IwashiThis->Pos))
          {
            nearSame = i+1;
          }
        }
        else
        {
          nearSame = i+1;
        }
      }
    }
  }

  if (nearSame != 0)
  {
    isCrosDot(&same[nearSame - 1], IwashiThis, &isAngle, &isLeft);/* サメ進行方向に対し90度直角に変針する*/
    if (isLeft == 1) /* サメから見たIwashiの方向性を示す */
    {
      Vec.x = same[nearSame - 1].Vec.x * cos_antisame_angle - same[nearSame - 1].Vec.y * sin_antisame_angle;
      Vec.y = same[nearSame - 1].Vec.x * sin_antisame_angle + same[nearSame - 1].Vec.y * cos_antisame_angle;
    }
    else
    {
      Vec.x = same[nearSame - 1].Vec.x * cos_antisame_angle + same[nearSame - 1].Vec.y * sin_antisame_angle;
      Vec.y = -same[nearSame - 1].Vec.x * sin_antisame_angle + same[nearSame - 1].Vec.y * cos_antisame_angle;
    }

    IwashiThis->Vec.x = Vec.x;
    IwashiThis->Vec.y = Vec.y;
  }
}

/************************************************************************
* 移動方向の矢印付きポッドを指定位置に描画する             *
************************************************************************/
void moveSame(int index,Iwashi_str *SameThis)
{
  double     detectLEN;
  double     newV;
  static int   theta=0;

  detectLEN = (SamePodHalfSize + _KABE_REPUL_DIST);/* 間合いは必ずポッド半径を加味する */

/************************************************************************
* 左右の壁の反射                            *
************************************************************************/
  if ((SameThis->Pos.x - detectLEN) < field.xMin)
  { /* 範囲はポッド半径の符号に注意 */
    SameThis->Pos.x = detectLEN;
    SameThis->Vec.x = fabs(SameThis->Vec.x);
  }
  else if ((SameThis->Pos.x + detectLEN) > field.xMax)
  {
    SameThis->Pos.x = (field.xMax - detectLEN);
    SameThis->Vec.x = (-1) * fabs(SameThis->Vec.x);
  }

/************************************************************************
* 上下の壁の反射                            *
************************************************************************/
  if ((SameThis->Pos.y - detectLEN) < field.yMin)
  {
    SameThis->Pos.y = detectLEN;
    SameThis->Vec.y = fabs(SameThis->Vec.y);
  }
  else if ((SameThis->Pos.y + detectLEN)> field.yMax)
  {
    SameThis->Pos.y = (field.yMax - detectLEN);
    SameThis->Vec.y = (-1) * fabs(SameThis->Vec.y);
  }

/************************************************************************
* 速さを最大に調整                           *
************************************************************************/
  newV = sqrt(SameThis->Vec.x * SameThis->Vec.x + SameThis->Vec.y * SameThis->Vec.y);
  SameThis->Vec.x *= _MAX_VELOCITY / newV;
  SameThis->Vec.y *= _MAX_VELOCITY / newV; /* 最大速度で調整する */

  SameThis->Pos.x += (SameThis->Vec.x * _INTERVAL_SEC);
  SameThis->Pos.y += (SameThis->Vec.y * _INTERVAL_SEC);
}

/************************************************************************
* サメポッドの表示する                         *
************************************************************************/
void CreateSame(void)
{
  SamePodSize = _SAME_DRAW_SIZE;
  /* サメサイズ */
  SamePodHalfSize = (double)(SamePodSize / 2);


  for (int i = 0; i<_SAME_COUNT_MAX; i++)
  {
    same[i].Pos.x = _CANVAS_WIDTH / 2;
    same[i].Pos.y = _CANVAS_HEIGHT / 2;

    same[i].a = 0;
    same[i].v = 0;
    same[i].Vec.x = 0.0;
    same[i].Vec.y = 0.0;
    same[i].OrgVec.x = 0.0;
    same[i].OrgVec.y = 0.0;
  }

  Random^ rr;
  double x, y;

  rr = gcnew Random();

  for (int i = 0; i<_SAME_COUNT_MAX; i++)
  {
    x = (double)rr->Next(0, (int)(field.xMax- (SamePodSize+MAXSpeed)*2)) + SamePodSize;
    y = (double)rr->Next(0, (int)(field.yMax- (SamePodSize+MAXSpeed)*2)) + SamePodSize;
      /* ランダムな位置を作成 */
    setPosision(10000+i, x, y);

    same[i].v = (double)rr->Next(0, (int)(MAXSpeed / 4)) + MAXSpeed / 4.0;
    same[i].a = _DegreeToRadian(rr->Next(0, 360));

    setVelocity(10000+i, same[i].v * cos(same[i].a), same[i].v * sin(same[i].a));
  }
}




だいたい今までのプログラム構造から推測は付くと思いますが、setPosition、setVelocityはiwashiのを流用しているためサメはすべてインデックスが10000上乗せして使用しています。また処理はイワシもサメも同じとなりますが、緊急回避用優先順位は処理を最後に持ってきてそれ以前に行った処理をキャンセルするという方法をとります。

さあ!これが最後です。
じゃあ!やってみましょう。

【BOIDS MODEL V1 00 ~鮫現ル~】