うにてぃブログ

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

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

ピースの Mesh 生成

~1~で作成したピースの境界を4つ繋げればピースができる

前回は線を描画するために new PathProperties() を利用していたが、今回は線では無く面を作成するため new SolidFill(), を設定する。

各境界を作成しているのが GetBorder メソッドで、凸と凹がランダムででるようになっている。

実行した結果が以下になる。

f:id:hacchi_man:20200109224929p:plain:w400

見ての通り凸と凹がランダムで生成されている。

しかし SolidFill で生成した Mesh は面なのでこれに厚みをつける必要があるため
次回の記事で厚みのつけかたについて記述する

f:id:hacchi_man:20200109225113p:plain:h300

ソースコード

using System;
using System.Collections.Generic;
using Unity.VectorGraphics;
using UnityEngine;

[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshFilter))]
public class Piece : MonoBehaviour
{
    private void Awake()
    {
        var shape = new Shape
        {
            Contours = new[]
            {
                new BezierContour
                {
                    Segments = GetSegment(new[]
                    {
                        Vector2.zero, 
                        Vector2.right,
                        Vector2.one,
                        Vector2.up
                    }).ToArray(),
                    Closed = true,
                }
            },
            Fill = new SolidFill(),
        };

        var scene = new Scene {Root = new SceneNode {Shapes = new List<Shape> {shape}}};
        
        var options = new VectorUtils.TessellationOptions
        {
            StepDistance = 100,
            MaxCordDeviation = 0.05f,
            MaxTanAngleDeviation = 0.05f,
            SamplingStepSize = 0.01f
        };

        var mesh = new Mesh();
        var geometries = VectorUtils.TessellateScene(scene, options);
        VectorUtils.FillMesh(mesh, geometries, 1f);
        GetComponent<MeshFilter>().mesh = mesh;
        
    }

    private List<BezierPathSegment> GetSegment(Vector2[] positions)
    {
        List<BezierPathSegment> segments = new List<BezierPathSegment>();
        for (var i = 0; i < positions.Length; i++)
            segments.AddRange(GetBorder(positions[i], positions[i + 1 >= positions.Length ? 0 : i + 1]));
        
        return segments;
    }

    private List<BezierPathSegment> GetBorder(Vector2 begin, Vector2 end)
    {
        var segments = new List<BezierPathSegment>();
        var isVertical = Math.Abs(begin.x - end.x) < 0.001f;
        var reverse = UnityEngine.Random.Range(0, 2) == 0 ? 1 : -1;
        var height = 0.08f;
        var positions = new[]
        {
            0f, 0.1f, 0.3f, 
            0.4f, 0.4f, 0.3f, 
            0.3f, 0.3f, 0.7f,
            0.7f, 0.7f, 0.6f, 
            0.6f, 0.7f, 1f,
        };
        var heights = new[]
        {
            0f, 0f, 0f, 
            0f, height, height, 
            height * 2, height * 3, height * 3,
            height * 2, height, height, 
            0f, 0f, 0f,
        };

        var heightBase = (isVertical ? Vector2.right : Vector2.up) * reverse;
        for (var i = 0; i < positions.Length; i+= 3)
        {
            segments.Add(new BezierPathSegment
            {
                P0 = Vector2.Lerp(begin, end, positions[i]) + heightBase * heights[i],
                P1 = Vector2.Lerp(begin, end, positions[i + 1]) + heightBase * heights[i + 1],
                P2 = Vector2.Lerp(begin, end, positions[i + 2]) + heightBase * heights[i + 2],
            });
        }
        return segments;
    }
}