HOME > BOIDSとは > BOIDSv1.00 ~爺になってからのベクトル~


09.14 BOIDSv1.00 ~爺になってからのベクトル~

★ベクトルとは

「現レル」の次は「避ケル」になるはずなのに なぜか、ここで「ベクトル」が登場します。
今までスムーズに進行しているように見えますが実は、このプログラム作成は昨年より地道に行っておりまして前回までは去年の3月ぐらいまでの進捗でありますが、次の「避ケル」が完成するまでに数か月を要してしまいました。

要するに非常に難しかったということです。

何がそんなに難しいかというと、よく考えてみてください!

完全に避けられる」という行為ができると、もしそれが自動車なら「無人走行車」になるじゃないですかぁ!

さらに今回は魚を対象にしているため「視界に入ったものを回避する」まさに「自動運転技術」と、そんなには変わらないのです。
今回特にいわしをポッドにし丸も大きいのでネットに蔓延っている単純なBOIDS理論だとNGなのです。そこでどうした良いかの案をいろいろなところから探してくるのに非常に時間がかかり、さらにそれが複数個ある「群れ」の場合はさらに難しいということです。

一番参考にあったのは以外にもシューティングゲーム理論でした。ゲーム理論というのは以外にも非常に高度な数学・物理の塊ですが、偏見があるのか活動拠点である図書館には本がなく、需要が少ないのか自費で購入するには高価すぎるほど高価なのであり中古などあるはずもありませんでした。要するに自分で勉強して考えるしかないということです。



では、はじめましょう!
下記のようにピクチャーボックス内に展開したポッド同士が連立する中、一番左が自ポッドの場合SF的に言うならポッド中心からレーザーを発射して各々のポッドの中心までの距離を正確に計測します。そして自局進行方向に対し何度の位置にいるかという情報を収集します。 つまりこの際に必要な情報= 「距離(大きさ) と 角度(方向)を持つもの」、これが「ベクトル」です。



ベクトルAは一般的に、 A のように表します。
またベクトルAの大きさは正式には| A |と表します。




★ドット積(内積)とクロス積(外積)
次に必要なのは「視野」という概念を、どのように数学で表現するかです。
上の赤矢印の距離は前回行いましたように点P1,P2の場合 ( Px 2 - Px 1 ) 2 + ( Py 2 - Py 1 ) 2 のようにして計算します。これはピクチャーボックス上の相対座標値でも差分を求めればいいわけです。しかし「視野」とう概念はどうでしょうか?

この場合の「視野」というのは、

 「視野」=「現在進もうとしている方向()を基準とした方向

次に

 「視界」=「視野から左右に移動できる範囲

下のような場合、左の自ポッド半径分伸びた方向用矢印が視野中心となり、隣のポッド中心へ伸びた矢印が「視線」となり「視線」は視界内のみ有効であると考えるなら、青色矢印間の角度を計算し、それが範囲内なら「視界」に入ったと認識できるわけです。 ではどう計算するか?

・ 自ポッドと他ポッドの差分により、その間のベクトルを求めることができます。
・ 自ポッドの進行方向は、速度の成分がそのまま方向ベクトルになります。



この2つを仮にベクトルA(x0、xy)とベクトルB(x1、y1)のとした場合、
これに対し昔高校の数学で習った記憶が少しだけしかない「内積」と「外積」というものを用います。
下の三角関数を見るとわかるのですが、三角関数の基礎をちょっといじくると次のようになるのですけど、理屈はやめておきます。
どうやらゲームなどに使用するDirectXというものの中に、この手のパッケージが既にあるようですので、同じような名称で自分で作ってみました

<内積>
(A・B)のように表され下記の計算式が成り立ちます。
  A・B =   x0 * y0 + X1 * y1 = |A| |B| Cosθ

<外積>
(A×B)のように表され下記の計算式が成り立ちます。
  A×B =   x0 * y1 - X1 * y0 = |A| |B| Sinθ




簡単に言うと三角関数の基礎でやったように変な長さの関係から、sinθ・cosθ・tanθを求めましたが、今知りたいのは斜めの辺(斜辺)の長さと角度θの関係を、角度を知りたい辺の座標から求められるということです。それが上のような内積・外積ということです。

しかし本当にそうか?
前回作成した「現レル」をちょっといじって下記を入れてみましょう。

/************************************************************************
* ベクトルv1、v2の距離を算出する                    *
************************************************************************/
void Vector_Diff(Vector_str *diff,Vector_str *v1,Vector_str *v2)
{
  diff->x = v1->x - v2->x;
  diff->y = v1->y - v2->y;
}


/************************************************************************
* ベクトルv1、v2の内積(v1・v2)を返す                  *
************************************************************************/
double Vector_DotProduct(Vector_str *v1,Vector_str *v2)
{
  return (v1->x * v2->x + v1->y * v2->y);
}


/************************************************************************
* ベクトルv1、v2の外積(v1×v2)を返す                  *
************************************************************************/
double Vector_CrossProduct(Vector_str *v1,Vector_str *v2)
{
  return (v1->x * v2->y - v1->y * v2->x);
}


/************************************************************************
* 二点間の距離算出                           *
************************************************************************/
double distanceTo(Vector_str *v1,Vector_str *v2)
{
  Vector_str   dPos;

  Vector_Diff(&dPos, v1, v2);

  return sqrt(dPos.x * dPos.x + dPos.y * dPos.y);/* 距離を算出する */
}


/************************************************************************
* 2つのベクトルのなす角度と方向の算出                 *
************************************************************************/
void isCrosDot(Iwashi_str *IwashiThis , Iwashi_str *IwashiOther , double *isAngle , int *isLeft)
{
  Vector_str   cq; /* ベクトル C→Q */
  double     s; /* 外積:(C→P)×(C→Q) */
  double     t; /* 内積:(C→P)・(C→Q) */


  Vector_Diff(&cq, &IwashiOther->Pos, &IwashiThis->Pos);
  s = Vector_CrossProduct( &IwashiThis->Vec , &cq );
  t = Vector_DotProduct ( &IwashiThis->Vec , &cq );
  *isAngle = atan2( s , t );

  if (s == 0)
  {
    *isLeft = 0;
  }
  else if (s > 0)
  {
    *isLeft = 1;
  }
  else
  {
    *isLeft = -1;
  }

}


呼ぶところはこんな感じです。

void CrosDottest(int index,Iwashi_str *IwashiThis)
{
  int     nearIwashi;
  double   isAngle;
  int     isLeft;

  nearIwashi = 0;

  for (int i = 1; i < iwashi_kazu; i++)
  {
    if (i != index)
    {
      isCrosDot(IwashiThis , &iwashi[i] , &isAngle , &isLeft);
        /* 自分以外との角度・回転方向算出 */
      if (i == 1)
      {
        int   temp_i;
        temp_i = _RadianToDegree(isAngle);
        textBox3->Text = temp_i.ToString();
        textBox4->Text = isLeft.ToString();
        if (isLeft >= 0)
        {
          radioButton1->Checked = true;
        }
        else
        {
          radioButton2->Checked = true;
        }
      }
    }
  }
}






ちなみに間の角度は tanθ=(対辺/鱗辺) でしたので、θ = aran2(対辺/鱗辺) で計算できます。

本当にそうなったのかテストしてみましょう。
実際にはこういうテストを「単体テスト」といいます。

単体テストは面倒だったのでポッド数を二個にして数値と、ラジオボタンによって左右区別してみました。
テストしたい項目は

1)自ポッドの進行方向を0とした場合、他ポッドの方向を角度で表示できるか? (isAngle)

2)自ポッドの進行方向を0とした場合、他ポッドの方向を(-1、0、1)で表現できるか?(isLeft)


画面を大きくして見て頂くと左のテキストボックスに止まっているポッドとの角度と方向、上記のラジオボタンによって進行方向向かって右なのか左なのかを表示します。どうでしょうか?

これが成功したら次へいきましょう!!


【BOIDS MODELV1.00~爺のためのベクトル~】






HOME > BOIDSv1.00 ~現レル~  > BOIDSv1.00 ~爺になってからのベクトル~