パスワードを忘れた? アカウント作成
14303626 journal
日記

yumeの日記: Unity学習 #6 2D Visibility

日記 by yume

2D Visibility

プレイヤーが惑星軌道を描くのを修正する。
AddForce()を使っていたが、単にvelocity変数を直接いじれば、ややこしいこともなく移動速度と方向を決定できるようだ。
前回はマウス現在地とプレイヤー現在地の差を徐々に速度に追加していったので、綺麗な軌道を描いてしまった。
今回は現在地の差をそのまま速度にする。ただし、毎フレーム取る必要はないのでマウスクリックした瞬間のマウスポイントへ向かう。
そのまま作ると目指した方向へ永久に進み続けてしまうので、目標地点と現在地の差を速度として更新し続ければ、目標地点へ近づくほどスピードが落ちる。
目標地点に到達すれば静止する、ということになるだろう。これでいこう。
マウスクリックはInput.GetMouseButtonDown()。押下したフレームだけ検知する。
Rigidbody2D.positionはVector2でXY座標をとれるので、transform.positionではなくこちらにする。


void Update()
{
        if (Input.GetMouseButtonDown(0)) TowardPoint();
        myPositionText.text = (myBody.position).ToString();
}

private void FixedUpdate()
{
        myBody.velocity = (aimPosition - myBody.position) * speed;
}

void TowardPoint()
{
        aimPosition = new Vector2(Camera.main.ScreenToWorldPoint(Input.mousePosition).x, Camera.main.ScreenToWorldPoint(Input.mousePosition).y);
}

さて次は壁だ。がその前にまずは目標のテキストを読む。

・Playerを中心に、すべてのポリゴンの頂点(と部屋の四隅の点)への角度を求める
・すべての角度に向かって光線を投げる(Raycast)
・光線とポリゴンの辺によってできた、すべての三角形を塗りつぶす

簡単そうに書いてるけど、じゃあ書け、って言われても書けないぞ。
とにかく読み進める。DeepLで翻訳:

壁の追跡

私たちは、特に空間ハッシュを使用してすべての壁との交差を避けるために高速なレイキャスティングアルゴリズムを持っている場合は、そこで止めることができます。しかし、より効率的なアプローチは、レイキャスティングと壁の交差を単一のアルゴリズムに組み合わせることです。ここでは、角度によってソートされたすべてのポイントを打つ、円の周りに線を掃引するアルゴリズムを説明します; それはまた、半径によってソートされたすべてのポイントを打つ、円を外側に展開することが可能ですが、私はそのアプローチを試したことがありません。
連続した光線の間の領域については、最も近い壁を見つけたい。この壁はライトアップされており、他のすべての壁は隠されています。我々の戦略は、360°一周して、すべての壁の端点を処理することです。掃引線と交差する壁を追跡していきます。

さっぱりわからん。
実際に動かせるサンプルを見ながら考えるに:
プレイヤーを中心に、角度0(サンプルの場合は左へまっすぐ)から時計回りに、
・点P(プレイヤー)
・点A(サンプルでは部屋の左上隅の点)
・点B(一つ目の箱の左下点)
の2点をゲットしたら、その2点とPlayerからなる3点の三角形「ではなく」、
Bの角度ポイントの先の辺にぶつかったポイント、B'を使って
P,A,B'からなる三角形を作り、そこが「見える三角形1」とする。

そしてさらに角度を回して
・一つ目の箱の左上点、右上点はスルーして(スルー点a,b)
・点C(二つ目の箱の左上点)
・点C'(点Cへの角度の光線が進んだ先の、辺にぶつかった点)
・P,B,C'からなる三角形を「見える三角形2」とする。

……これを角度が一周するまで続ける。
という具合のようだ。
光線と辺が交差する点を求めれば、辺の途中の点B'やC'を取ることはできると思う。
しかし疑問は山ほどある:
・ある点をスルーするかどうかをどう決めるのか
・ある点がそのまま可視三角形の頂点になるのか、あるいはその先の辺にぶつかるまでを求めるのかをどうやって決めるのか
(どういうアルゴリズムで点Bと点B'を使うと決めているのか)
・(そもそもUnityで)どうやって「辺」とか「頂点」とかを求めるのか

どのような可視三角形も、点AとBは必ず同じ辺の上にある、はずだ。
ということは、点の属する辺さえわかればなんとかなるか。
得るべき可視三角形は:
・P, A, B
・P, A, B'
・P, A', B
・P, A', B'
の4種類だ。
頭の中でアルゴリズムを書いてみよう(絶対ヌケがあると思うけど)

Junbi()
{
        ・まず、シーン上のすべての2Dポリゴンの頂点をなんとかして得る。
        ・さらに、部屋の四隅の点も得る。
        ・それをリストKとして、プレイヤーの現在地「点P」からの角度を求め、角度順にソートする
        ・さらに、三角形リストTを作る。リストTに点Pを入れる。
}

Loop()
{
        ・リストKの次の(または最初の)点「点A」をチェックする。 CheckThisPoint(A);
        ・リストKの最後の点までやる。
}

CheckThisPoint(Vector2 A)
{
        ・点Aまでに、光線が辺でさえぎられるならReturn(スルー)

        ・「三角形リストT」にまだ点Pしかないなら、とにかく点AをリストTに点Bとして加える。

        ・リストTにすでにP以外の点Bが入っているなら、その点Bと点Aが「同じ辺」であるか確認し:
                ・同じ辺なら三角形PABを作る
                ・違う辺なら、CheckOkumade(A);で新たな点A'を求め:
                        ・点A'とBが同じ辺なら、三角形PA'Bを作る。
                        ・点A'とBが違う辺なら、CheckOkumade(B)で新たな点B'を求め:
                                ・点AとB'が同じ辺なら、三角形PAB'を作る。
                                ・違う辺なら、点A'とB'からなる三角形PA'B'を作る。
        ・点AをリストTの点Bに代入しておく。
}

CheckOkumade(Vector2 A)
{
        ・A点の先にさらに光線が続かない(言い換えると四隅点なら)、A点を返す。、
        そうでないなら、光線が続く先の、辺にぶつかった地点を改めて「点A'」として返す。
}

(画像がないとわかりづらいな)
うーん……サンプルを見るかぎり、このアルゴリズムで動く気はするんだが、
肝心のサンプルの方は「近い辺を探す」とかなんとか書いてあるんだよな。このアルゴリズムはそんなことしてない。

どちらにせよ、辺や頂点の取り方がわかってない。もうちょっと読んでみよう。
ncaseがステップごとにアルゴリズムを解説してくれている。
まず最初に
・ポリゴンでできた壁
・プレイヤーから狙ったところへ延びる線
を作ろう。

●ポリゴン壁:
・LineRendererコンポーネントで線を描画できる。複数点からなる線、さらにつなげることもできる。
・PolygonCollider2Dで複数点からなるポリゴン障害物が作れる
LineRendererで描画して、スクリプトで同じポイントにPolygonCollider2Dを入れれば、画面上でポリゴンをいじって、実際その形の壁を作れそうだ。
……がいまいちうまくいかない。逆(ColliderのポリゴンからLineRendererのポイントを指定する)ならうまくいった。

●プレイヤーから延びる線
これはまんまLineRendererでマウスの狙った位置と自分を結ぶ線を描画するだけ。

といったところで今日はここまで。

typodupeerror

皆さんもソースを読むときに、行と行の間を読むような気持ちで見てほしい -- あるハッカー

読み込み中...