HOME > BOIDSとは > BOIDSv2.00 ~検知(ミ)ル~


10.02 BOIDSv2.00 ~検知(ミ)ル~

★情報源は眼
このポッドにとっての情報源は「側線」がないので「眼」だけということになります。
今まで動かしてきたポッドはすべてコンピュータ内の位置と速度情報により移動してきたためメモリー上の、これら情報を検索することによって「見えているフリ」をしていたわけです。

実際に画面上のポッドはみることはできないのか? 

色々調査した結果、下記のような方法を思いつきました。

ポッド進行方向にハンマーヘッドシャーク(シュモクザメ)の頭のように突き出した長方形の部分をドット(ピクセル)単位で「色」を検出してみてはどうか。





6.1 絵を描くプログラムを作成する」内の項目「ビットマップ」で説明したように、現在描画しているのはビットマップイメージ上です。
ここを下記のようにして座標を呼んでやると、RGBという色情報を読み取ってくます。

    Color pixcelColor;
    pixcelColor = canvas->GetPixel((int)absPos.x , (int)absPos.y)

このように

    pixcelColor.R
    pixcelColor.G
    pixcelColor.B

のなかに各々0~255の数字が入ってきます。(R:red、G:Green、B:Blue
復習しておきますとビットマップはRGBの色の濃さで決まっており各々0~255までの数値が入っており、GetPixel()で呼ぶと、そこに入っている数値がそのまま出てくるわけです。RGBは組み合わさると様々な種類の色になりますが今回は分かりやすいように背景は白色、枠は緑色、ポッドは青色、サメポッドは赤色にしています。

この配色をRGBで表すと下記のようになります。

  Red Green Blue
背景 255 255 255
0 255 0
ポッド 0 0 255
サメポッド 255 0 0

背景色は「白」なのでRGBがすべて255の状態しかありえないため、255×3=765となり、それ以外は「何か」を検出したということになります。さらに「何か」は色が決まっているため、次にRGBで区分すると検索枠内に何がいるかがわかります。これにより「眼」ができたことになります。

void collisionCheck(int index,Iwashi_str *IwashiThis)
{
  int       AreaPx,AreaPy;
  Vector_str   AreaTempPos;
  Vector_str   absPos,TopPos;
  double     RotAng;
  Color      pixcelColor;
  int       c_judge;
  double     a_sin,a_cos;
  double     diff;
  int       j;

  for(j=0; j<7; j++)
  {
    iwashi[index].CollisionStatus[j] = 0;
  }

  RotAng = atan2(iwashi[index].Vec.y, iwashi[index].Vec.x);
    /* 半径線を算出するための角度算出 */

  a_sin = sin(RotAng);
  a_cos = cos(RotAng);

    /* 接触面回避のための+3 ↓*/
  TopPos.x = (PodHalfSize + 3) * a_cos + iwashi[index].Pos.x;
  TopPos.y = (PodHalfSize + 3) * a_sin + iwashi[index].Pos.y;

  RotAng += _COLLISION_VIEW_ANGLE;
  a_sin = sin(RotAng);
  a_cos = cos(RotAng);

  for (AreaPx=(-1) * (int)(collision_dist+ PodHalfSize); AreaPx <= (int)(collision_dist+ PodHalfSize); AreaPx++)
  {
    for (AreaPy=(-1) * (int)collision_dist*2; AreaPy <= 0; AreaPy++)
    {
      /************************************************************************
      * 進行方向を基準とした検索マトリックス座標算出             *
      ************************************************************************/
      AreaTempPos.x = AreaPx * a_cos - AreaPy * a_sin;
      AreaTempPos.y = AreaPx * a_sin + AreaPy * a_cos;
          /* 進行方向へマトリックス回転 */

      absPos.x = (AreaTempPos.x + TopPos.x);
      absPos.y = (AreaTempPos.y + TopPos.y);
      /************************************************************************
      * 左右の壁の反射                            *
      ************************************************************************/
      if (absPos.x < field.xMin)
      {    /* 範囲はポッド半径の符号に注意 */
        continue;
      }
      else if (absPos.x > field.xMax)
      {
        continue;
      }

      /************************************************************************
      * 上下の壁の反射                            *
      ************************************************************************/
      if (absPos.y < field.yMin)
      {
        continue;
      }
      else if (absPos.y > field.yMax)
      {
        continue;
      }

      /************************************************************************
      * 検索対象ピクセルの色をみる                      *
      ************************************************************************/
      try{
        pixcelColor = canvas->GetPixel((int)absPos.x , (int)absPos.y);
        c_judge = pixcelColor.R + pixcelColor.G + pixcelColor.B;
      }
      catch (Exception^)
      {
        c_judge = 0;
      }

      /************************************************************************
      * 検索対象ピクセルが白以外を探す                    *
      ************************************************************************/
      if (c_judge != 765)
      {
        /************************************************************************
        * 検索対象全表示                            *
        ************************************************************************/
        if (checkBox17->Checked == false)
        {
          gf->DrawLine(YellowPen, (int)TopPos.x, (int)TopPos.y, (int)absPos.x, (int)absPos.y);
        }

        /************************************************************************
        * 検索対象距離測定                           *
        ************************************************************************/
        diff = fabs(distanceTo((Vector_str *)&TopPos.x , (Vector_str *)&absPos.x));

        /************************************************************************
        * 検索対象が壁(緑色)の検索                      *
        ************************************************************************/
        if ((pixcelColor.R == 0)&&(pixcelColor.G != 0)&&(pixcelColor.B == 0))
        { /* 壁を判定する */

        }

        /************************************************************************
        * 検索対象が物体(青色)の検索                     *
        ************************************************************************/
        else if ((pixcelColor.R == 0)&&(pixcelColor.G == 0)&&(pixcelColor.B != 0))
        {

        }

        /************************************************************************
        * 検索対象が物体(赤色)の検索                     *
        ************************************************************************/
        else if ((pixcelColor.R != 0)&&(pixcelColor.G == 0)&&(pixcelColor.B == 0))
        {

        }

        /************************************************************************
        * 検索対象ピクセルを点で表示                      *
        ************************************************************************/
        if (checkBox10->Checked == false)
        {
          canvas->SetPixel((int)absPos.x , (int)absPos.y, Color::Black);
            /* 検索領域ピクセルを描画する */
        }
      }
    }

  }

}



★プログラム解説
1)ヘッド部
TopPos.x,y は進行方向矢印の算出方法と同じです。
        TopPos.x = (PodHalfSize + 3) * a_cos + iwashi[index].Pos.x;
        TopPos.y = (PodHalfSize + 3) * a_sin + iwashi[index].Pos.y;



ここでちょっと問題になるのは「半径(PodHalfSize)+3」の「+3」であります。この「+3」は上記図にあるようにハンマーヘッドシャークのような検索領域を進行方向へ回転させて領域内ドットの色を検索していますが、回転した際の誤差により方向によって自分自身の青色外周と検索領域が被さって、他のポッドと誤認する事態が発生したため、少し間を置くことによって回避しています。

ちなみに ヘッド部は以前行列の回転で行った下記の方法によって回転できます。
cos θ 2 - sin θ 2 sin θ 2 cos θ 2 x y = x cos θ 2 - y sin θ 2 x sin θ 2 + y cos θ 2

ここで注意しないといけないのはヘッドは進行方向にY軸、左右をX軸にしようとしているため上記の写真を「90度だけ右に回転させた状態と同じ」ことになり、θ=0の時ヘッド部は90度回転した状態にあります。



2)検知範囲

下の動画は検索しているドット部を黒い点で表現し移動させたときの動画です。
次に気を付けないといけないことは、以前座標系のお話をした時に使用している座標系はすべてプラスの第一象限のみを使用するというお話をしましたが、ドットの色検索座標系も同じことが言えます。しかし先頭に出た大きなヘッド部を持った状態で回転すると、計算値がマイナスになったり最大値を超える場合がでてきます。値がピクチャーボックスの範囲を逸脱し特にマイナス値になると、プログラムは途中で例外処理となり処理を中断してしまいます。このためピクチャーボックス範囲のチェックを実施して範囲外の場合は以降の処理を実行しないようにしています。
(ちなみに「continue」という命令は、以降を実施せず”For”に戻るという命令です)

【BOIDS NEW MODEL Ver02 00 ~検知(ミ)ル~】





HOME   > BOIDSv2.00 ~検知(ミ)ル~