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

yumeの日記: Unity学習 #10 2D Visibility 完成

日記 by yume

2D Visibility

●可視領域多角形の角度をとる。角度を基準に頂点をソートする

必要な頂点の座標はすべて取れたが、
プレイヤーを原点としたこの座標に対する角度を得る必要がある。
その前に、まずそのためのクラスを作っておく。

public class VisibleVertex
{
        //可視多角形領域の頂点とプレイヤーからの角度
        public float Radian;
        public Vector2 EndPoint;

        public VisibleVertex(float radian, Vector2 endPoint)
        {
                Radian = radian;
                EndPoint = endPoint;
        }
}

これで、可視領域に必要な座標と角度を保持する型ができた。
これまでは単にVector2リストに入れていた終端点リストを、このリストに置き換える。
そして、終端点を求めるタイミングで、角度も同時に求めているので、その角度も同時に入れておく。

for (int i = 0; i < polyVertexs.Count; i++)
{
        float goalRadian = GetRadian(position2D, polyVertexs[i]);
        RaycastHit2D myHitPoly;
        Vector2 checkRay;

        //-0.00001度のチェック
        checkRay = RadianToPoint(goalRadian - 0.00001f, viewRange);
        myHitPoly = Physics2D.Linecast(position2D, checkRay, polyWallLayer);
        visibleVertex.Add(new VisibleVertex(goalRadian - 0.00001f, myHitPoly.point));

        //+-0度のチェック
        checkRay = RadianToPoint(goalRadian, viewRange);
        myHitPoly = Physics2D.Linecast(position2D, checkRay, polyWallLayer);
        visibleVertex.Add(new VisibleVertex(goalRadian, myHitPoly.point));

        //+0.00001度のチェック
        checkRay = RadianToPoint(goalRadian + 0.00001f, viewRange);
        myHitPoly = Physics2D.Linecast(position2D, checkRay, polyWallLayer);
        visibleVertex.Add(new VisibleVertex(goalRadian + 0.00001f, myHitPoly.point));
}

次に、visibleVertexのリストを、radianを基準に並べ替える。
smdn曰く、
・複数型のソートは普通の.Sort()ではダメ
・Comparisonデリゲートの形でSortメソッドに渡すとよい。
>デリゲートってなんだ。
e-Wards曰く、
・デリゲートとは、委任・委譲などを意味する英単語
・オブジェクト指向プログラミングにおいて、あるオブジェクトが一部の処理を他のオブジェクトに任せる仕組みを意味する。
ピンとこないけど、実際のサンプルコードを見てみる:

static int CompareAccount(Account x, Account y)
{
    if (x.ID > y.ID)
        return 1; // xのIDがyのIDより大きい→xはyより大きい
    else if (x.ID < y.ID)
        return -1; // xのIDがyのIDより小さい→xはyより小さい
    else // if (x.ID == y.ID)
        return 0; // xのIDとyのIDが等しい→xはyと等しい
}

Account型は string nameと int IDを持っている。
このメソッドでは、複合型クラスAccountのID xと ID yを比較して結果を返すようだ。
Sortメソッドは、この「比較メソッド」の結果を引数として受け取れるらしい。

list.Sort(CompareAccount);

で、ID順に並ぶそうだ。
俺の場合、radian順に並べばよいわけだから

static int CompareRadian(VisibleVertex x, VisibleVertex y)
{
        //ソート用。Radianで比較
        if (x.Radian > y.Radian)
                return 1;
        else if (x.Radian < y.Radian)
                return -1;
        else
                return 0;
}

を準備して
visibleVertex.Sort(CompareRadian);
を毎フレーム実行すればいいか。

ん~これ、ちゃんと動くけど、ソートできてるかどうかがわかんないな……。
えーと、今はGLで色のついた線を1本ずつ描画してるから、その線を描画するたびにちょっとずつ赤から青へ変化させていけば、
正しくソートできているなら、綺麗なグラデーションになるはず。

……。なってる!

ただ、これではソートの順番が逆だ。これは比較メソッドのReturnの正負を逆にすればOK。

--

●ソートされた頂点リストをもとに、メッシュを描画する。

難解なメッシュづくりに戻ってきた。
昨日の資料によれば:
・どんな多角形でも三角形の組み合わせで作ることができる。
・三角形は3つの頂点と、そのつなげる順番で定義できる。
・正しく時計回りに頂点の順番を設定しないとダメ。

いま、ソートによって、頂点座標はプレイヤーから見て左から時計回りの順に並んでいる。
・0番はプレイヤーの原点としておく。
・1番はリストの[0]番を取る。
・2番はリストの[1]番を取る。
0,1,2の順で繋げると……。時計回りだ。次に:
・3番はリストの[2]番を取る。
0,2,3の順で繋げると……。やはり時計回りだ。
・4番はリストの[3]番を取る。
0,3,4の順で繋げると……。当然時計回りだ。
いけそうな気がしてきた。


private void Update()
{
        //頂点リストをクリア
        meshPoints.Clear();
        meshTriangles.Clear();

        //プレイヤー原点を入力
        meshPoints.Add(myFOV.ownerBody.transform.position);
        meshTriangles.Add(0);

        //最初の三角形の頂点をとる
        meshPoints.Add(myFOV.visibleVertex[0].EndPoint);
        meshTriangles.Add(1);
        meshPoints.Add(myFOV.visibleVertex[1].EndPoint);
        meshTriangles.Add(2);

        //次以降の三角形をとる。
        for (int i = 2; i < myFOV.visibleVertex.Count; i++)
        {
                //三角形の頂点番号を打っておく
                meshTriangles.Add(0);
                meshTriangles.Add(i);

                //頂点をとる。頂点番号を打つ。
                meshPoints.Add(myFOV.visibleVertex[i].EndPoint);
                meshTriangles.Add(i+1);
        }

        //最後の三角形をとる(最初と最後をつなげる三角形)
        meshPoints.Add(myFOV.visibleVertex[0].EndPoint);
        meshTriangles.Add(0);
        meshTriangles.Add(myFOV.visibleVertex.Count);
        meshTriangles.Add(myFOV.visibleVertex.Count+1);

        //作ったリストを実際のMeshに代入して、描画してもらう。
        FOVMesh.vertices = meshPoints.ToArray();
        FOVMesh.triangles = meshTriangles.ToArray();
        FOVMesh.RecalculateNormals();
}

……。動く!!!
できる気がしなかったけど、できたぞ!!

--

●マテリアルを作る

できたはいいが、現状だとピンクのメッシュ(マテリアル未定義だとピンクになる)が視界を表現しているので、
むしろ可視領域がピンクで染まって何も見えない感じになっている。
せめて可視領域は半透明で白っぽくしてやろう。
公式曰く、
Unity上でマテリアルも作ることができるらしい。
YTTMWORK曰く、
・マテリアルとは、色や光沢、テクスチャによって物質の材質を表現する概念
>3DCGとかと同じだよね。
・色はAlbedoをいじると決定できる
・Alpha値もここ。
>とりあえず、白の半透明のマテリアルを定義。
一旦これでいいか。しかしテストするとピンク色ではなくなったものの、めっちゃ暗いグレーになる。
Qiita @ReoNagai曰く、
・マテリアルに光が当たってないので暗くなっている
・シーンにLight -> Directional Lightを入れると全面ライトアップしてくれる。
>白くはなったが、なんかまだグレーっぽいし透明でもないな。
・Directional Lightの位置と向きを調整(画面に垂直に)
>真っ白にはなったが、透明ではない。
KKLOG曰く、
・MaterialのRendering ModeをFadeにすればよい。

そんで、もう仮の線は要らないので消す。
するとこうなる。

たまにメッシュがあらぶってる。
不透明なマテリアルで表示する分にはあまり気にならなかったが、
半透明にするとメッシュ同士が重なったり、隙間が空いたりした部分が見えやすい。

それと、今は半透明の白いマテリアルをメッシュ領域に乗せている感じだが、
本当は、例えば黒い半透明ですべての領域を乗算して暗くして、
可視領域だけを本来の明るさで描画するなどしたい。
座標と多角形の定義はできているから、この辺りはシェーダーにやってもらうといいんだろうか。
あるいは、LightをMeshで定義した形で照らしたりできる?
まぁ、それはおいおいやっていこう。

次は、この要素(動く・視界がある)だけで遊べるミニゲームを作ってみたい。

typodupeerror

ソースを見ろ -- ある4桁UID

読み込み中...