うにてぃブログ

主にUnityとC#に関する記事を書いていきます

【Unity】ジグソーパズルの自動生成 ~3~

ピースの厚み

今回は ~2~で作成したメッシュに厚みをつけるために
生成したMeshの輪郭線のZ座標をずらして複製し、複製した線と元の線の間にポリゴンを作成することで厚みをつけます

背面に関しては、表面の頂点座標を押し出し頂点の順番を反転してやれば作成できます
※正しい言い方があるかと思いますが、わからなかった

f:id:hacchi_man:20200109233520p:plain

外枠の線の抽出

外枠の線は、2頂点で構成されているポリゴンが1つしかないため
こちらの条件を満たす頂点を探していきます。

f:id:hacchi_man:20200109234544p:plain

上記図を見ると頂点3を含むポリゴンはA,B,C,Dすべてであり、1-2, 2-5, 1-4, 4-5 を含むポリゴンは一つしか存在しない
そのため、1-2, 2-5, 1-4, 4-5は外枠の線であることが分かる

ポリゴンの押出コード

こちらがポリゴンの押出処理のコードになります

GetOutlineEdge で取得した外枠の頂点をz方向に押し出し、側面のポリゴンを生成しています

public static class MeshUtil
{
    public static void PushMesh(Mesh mesh, float thickness)
    {
        var vertexLength = mesh.vertices.Length;
        var vertices = new List<Vector3>(mesh.vertices);
        foreach (var vertex in mesh.vertices)
        {
            var cache = vertex;  
            cache.z += thickness;
            vertices.Add(cache);
        }
        
        var triangles = new List<int>(mesh.triangles);
        // 背面のポリゴン
        triangles.Reverse();
        for (var i = 0; i < triangles.Count(); i++)
            triangles[i] += vertexLength;
        
        triangles.AddRange(mesh.triangles);

        // 側面のポリゴン生成
        foreach (var edge in GetOutlineEdge(mesh.triangles))
        {
            triangles.Add(edge.y + vertexLength);
            triangles.Add(edge.x);
            triangles.Add(edge.x + vertexLength);
                
            triangles.Add(edge.y);
            triangles.Add(edge.x);
            triangles.Add(edge.y + vertexLength);
        }
        
        mesh.vertices = vertices.ToArray();
        mesh.triangles = triangles.ToArray();
    }
    
    private static Vector2Int[] GetOutlineEdge(int[] triangles)
    {
        var dic = new Dictionary<Vector3Int, int>();
        var loopIndexes = new[]
        {
            new Vector2Int(0, 1),
            new Vector2Int(1, 2),
            new Vector2Int(2, 0),
        };
        for (var i = 0; i < triangles.Length; i += 3)
        {
            foreach (var index in loopIndexes)
            {
                // 反転判定用にzを利用する
                var key = triangles[i + index.x] > triangles[i + index.y]
                    ? new Vector3Int(triangles[i + index.y], triangles[i + index.x], 1)
                    : new Vector3Int(triangles[i + index.x], triangles[i + index.y], 0);
                var find = dic.Keys.FirstOrDefault(k => k.x == key.x && k.y == key.y);
                if (find == default(Vector3Int))
                    dic.Add(key, 1);
                else
                    dic[find] += 1;
            }
        }
        return dic.Where(p => p.Value == 1)
            .Select(p => p.Key.z >= 1 ? new Vector2Int(p.Key.y, p.Key.x) : new Vector2Int(p.Key.x, p.Key.y)).
            ToArray();
    }
}

結果

~2~の コードに MeshUtil.PushMesh を追加すると下図のようになります
f:id:hacchi_man:20200111015604p:plain

ピースの色がおかしいのは mesh の再生成後に RecalculateNormals を用いて 法線を再構築しており、それが正しくないようです が正しい対応がわからないので、とりあえず Unlit を使わない方向でいきます