見出し画像

【Unity】2D多関節キャラ、6本足多足歩行キャラのアルゴリズム

6本足多足歩行の多関節キャラを作ってみたのですが相当作るの苦労したので備忘録として書いておきます。

まずは基本的な考え方として本体と足の根本、足の先端の3つの座標を考えます。

Center_X,Center_Yが本体の中心座標
Beg_X,Beg_Yが足の根本の座標
End_X,End_Yが足の先端の座標
angleが本体の移動方向
width_Begが本体から足の根本までの距離
width_Endが本体から足の先端までの距離
とすると
Beg_X,Beg_Yは

Beg_X= Mathf.Cos(angle + 1.57f) * width_Beg + Center_X;
Beg_Y= Mathf.Sin(angle + 1.57f) * width_Beg + Center_Y;

End_X,End_Yは

End_X= Mathf.Cos(angle + 1.57f) * width_End+ Center_X;
End_Y= Mathf.Sin(angle + 1.57f) * width_End+ Center_Y;

で導けます。angle + 1.57fの部分で足の位置を決めているので、進行方向に対して足の位置の分だけ角度を足しています。

上図のような感じで例えば右下の足の座標が知りたい場合は
下記のように書けば導けます。

Beg_X= Mathf.Cos(angle + 0.785f) * width_Beg + Center_X;
Beg_Y= Mathf.Sin(angle + 0.785f) * width_Beg + Center_Y;

End_X= Mathf.Cos(angle + 0.785f) * width_End+ Center_X;
End_Y= Mathf.Sin(angle + 0.785f) * width_End+ Center_Y;

今回は足が6本あるので6本分の角度の座標を計算しておきます。(6本共に同じ動きならprefab化して回転させても良いかもしれません)
これで本体と足の根本、足の先端の座標の計算は出来ました。
次は歩いているように見せる為に足の先端を動かします。

ただ単に前後に動くだけなら足の先端の座標End_Yに数値を足したり引いたりすれば上下に動いてくれるのですが、それだと本体が回転した時におかしくなるんですよね。

この状態の時は本体の方向に対して上下して欲しいので進行方向も含めて移動座標を考えます。
上下に移動する変数updownを用意します。
上下に移動する座標をUD_X、UD_Yとすると、上下に移動する足の座標は下記のような計算で導けます。

if (ab == 0)
        {
            updown++;
            if (updown> 10)
            {
                ab = 1;
            }
        }
if (ab == 1)
        {
            updown--;
            if updown--< -10)
            {
                ab = 0;
            }
        }

UD_X = Mathf.Cos(angle) * (updown) + End_X;
UD_Y = Mathf.Sin(angle) * (updown) + End_Y;

足の位置によってはangleに足の方向を足したりする必要があります。

これで足の上下の動きは出来ました。
次は足を動かす順番です。
当初6本足をバラバラに動かすのかと思っていたのですが色々と調べたりアドバイスを頂いたりして考えてみると、何本かの足をセットにして動かすと良いようでした。

↑この場合だと8本足がありますが対角線上にある足がセットで動いています。

↑この場合だと3本セットで動いている事がわかると思います。

これを踏まえて3本セットで分けて動かすようにしてみました。

A_groupの足が下に動く時はB_groupは上に動き、A_groupの足が上に動く時はB_groupは下に動かすようにすると歩いているように見えます。

ここまで実装すると真っすぐ歩いている時は違和感ないのですが回転させようとすると足が滑っているように見えて変な感じになります。
なので回転する時は接地している足は移動させずに接地していない足を移動方向に動かしていくと言うような実装が必要になります。

これはちょっと説明が難しいのですが、足が接地している時は座標を固定しておいて足が浮いている時に移動させたい方向に足を移動させるのですが、この時にただ単に本体の移動方向に足を持っていこうとすると足を右回転させるのか左回転させるのか問題が生じます(本体の角度によっては足が逆回転してしまいちゃんと動かない)

この画像の状態だとangle_airは時計回りに回って欲しい

この問題を解決するためには、まず足を移動させる時に右回転か左回転のどちらが良いのかを判断する必要があります。
方法としては
angle = 本体の移動方向
angle_air = 足の実際に移動したい方向
angle_sub = 本体の方向から実際に移動したい方向を引いた数
を用意します。
angleとangle_airの数値 = (自機と敵の位置関係)となるのですが、この数値(自機と敵の位置関係)によってangle_airに足し算をするのか引き算をするのか(右回転なのか左回転なのか)が決まります。

angle = 本体の移動方向  
angle_air = 足の実際に移動したい方向
angle_sub = 本体の方向から実際に移動したい方向を引いた数
        
            if (angle < 0)//マイナスになると計算がややこしいので0~6.28の範囲におさまるようにしている
            {
                angle += 6.28f;
            }

            if (angle_air < 0f)//マイナスになると計算がややこしいので0~6.28の範囲におさまるようにしている
            {
                angle_air += 6.28f;
            }
            if (angle_air > 6.28f)//マイナスになると計算がややこしいので0~6.28の範囲におさまるようにしている
            {
                angle_air -= 6.28f;
            }

            angle_sub = angle - angle_air;//本体の方向から実際に移動したい方向を引いた数。向きたい方向に最小の角度で向くため用

            if (angle_sub >= 0f && angle_sub < 3.14f)
            {
                if (angle <= angle_air) angle_air -= 0.05f;
                if (angle >  angle_air) angle_air += 0.05f;
            }
            if (angle_sub >= 3.14f && angle_sub < 6.28f)
            {
                if (angle <= angle_air) angle_air += 0.05f;
                if (angle >  angle_air) angle_air -= 0.05f;
            }

            if (angle_sub < 0f && angle_sub > -3.14f)
            {
                if (angle <= angle_air) angle_air -= 0.05f;
                if (angle >  angle_air) angle_air += 0.05f;
            }
            if (angle_sub <= -3.14f && angle_sub > -6.28f)
            {
                if (angle <= angle_air) angle_air += 0.05f;
                if (angle >  angle_air) angle_air -= 0.05f;
            }

上記を踏まえて足の動きを作るとこんな感じです。
足が接地している時はangle_groundで足を固定しておき
足が浮いている時はangle_airで足の移動方向を決定している感じです。

angle = 本体の移動方向  
a=ngle_air = 足の実際に移動したい方向
angle_sub = 本体の方向から実際に移動したい方向を引いた数
angle_ground = 足が接地している時の方向
ab = 足が接地しているか浮いているか。0が接地、1が浮いている
updown = 足の上下の動き(高さではない)

        
        if (ab == 1)//足が浮いている時の足先端の座標
        {
            if (angle < 0)//マイナスになると計算がややこしいので0~6.28の範囲におさまるようにしている
            {
                angle += 6.28f;
            }

            if (angle_air < 0f)//マイナスになると計算がややこしいので0~6.28の範囲におさまるようにしている
            {
                angle_air += 6.28f;
            }
            if (angle_air > 6.28f)//マイナスになると計算がややこしいので0~6.28の範囲におさまるようにしている
            {
                angle_air -= 6.28f;
            }

            angle_sub = angle - angle_air;//本体の方向から実際に移動したい方向を引いた数。向きたい方向に最小の角度で向くため用

            if (angle_sub >= 0f && angle_sub < 3.14f)
            {
                if (angle <= angle_air) angle_air -= 0.05f;
                if (angle >  angle_air) angle_air += 0.05f;
            }
            if (angle_sub >= 3.14f && angle_sub < 6.28f)
            {
                if (angle <= angle_air) angle_air += 0.05f;
                if (angle >  angle_air) angle_air -= 0.05f;
            }

            if (angle_sub < 0f && angle_sub > -3.14f)
            {
                if (angle <= angle_air) angle_air -= 0.05f;
                if (angle >  angle_air) angle_air += 0.05f;
            }
            if (angle_sub <= -3.14f && angle_sub > -6.28f)
            {
                if (angle <= angle_air) angle_air += 0.05f;
                if (angle >  angle_air) angle_air -= 0.05f;
            }

            End_X = Mathf.Cos(angle_air + 1.57f) * width_End + Center_X;
            End_Y = Mathf.Sin(angle_air + 1.57f) * width_End + Center_Y;
        }

        if (ab == 0)//足が接地している時の足先端の座標
        {
            End_X = Mathf.Cos(angle_ground + 1.57f) * width_End + Center_X;
            End_Y = Mathf.Sin(angle_ground + 1.57f) * width_End + Center_Y;
        }


        if (ab == 0)//足が接地している時
        {
            updown++;
            if (updown > 10)//足が浮き始める時
            {
                ab = 1;
                angle_air = angle_ground;
            }
        }

        if (ab == 1)//足が浮いている時
        {
            updown--;
            if updown-- < -10)//足が接地する時
            {
                ab = 0;
                angle_ground = angle;
            }
        }

これを6本分(angle_air + 1.57fの1.57fを足の向きによって変更する)計算して表示すると本体が回転する時も上手く表示してくれました。

足の中間のキャラ表示についてですが、足根本から先端までの方向と距離を計算しておき、表示させたい方向と距離にキャラ表示するようにしました。

方向はAtan2、距離はSqrtで求めることが出来るので
Beg_X,Beg_Yが足の根本の座標
End_X,End_Yが足の先端の座標
とすると下記の計算で導けます。

Beg_X= Mathf.Cos(angle + 1.57f) * width_Beg + Center_X;
Beg_Y= Mathf.Sin(angle + 1.57f) * width_Beg + Center_Y;

End_X= Mathf.Cos(angle + 1.57f) * width_End+ Center_X;
End_Y= Mathf.Sin(angle + 1.57f) * width_End+ Center_Y;

houkou = Mathf.Atan2(End_Y - Beg_Y, End_X - Beg_X);//足の向き
kyori = Mathf.Sqrt((Beg_X - End_X) * (Beg_X - End_X) + (Beg_Y - End_Y) * (Beg_Y - End_Y));

Middle00_X = Mathf.Cos(houkou) * (kyori / 3f) + Beg_X;
Middle00_Y = Mathf.Sin(houkou) * (kyori / 3f) + Beg_X;

Middle01_X = Mathf.Cos(houkou) * (kyori / 6f) + Beg_X;
Middle01_Y = Mathf.Sin(houkou) * (kyori / 6f) + Beg_X;

距離が短ければ短くなった用のキャラを、距離が長い場合は長くなった用のキャラをアニメ表示させています。

いいなと思ったら応援しよう!