Генерация линии DrawBezier

Программирование на Юнити.

Re: Генерация линии DrawBezier

Сообщение 1max1 11 апр 2019, 21:23

Игра не запускает скрипты редактора, на то они и скрипты редактора.
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51

Re: Генерация линии DrawBezier

Сообщение SweetyMorgan 11 апр 2019, 21:46

1max1 писал(а):Игра не запускает скрипты редактора, на то они и скрипты редактора.

Правильно ли я понимаю, что этот скрипт редактора в конечном продукте никогда не будет запущен? Суть всего скрипта в том, что он на основе массива точек и касательных формирует массив точек на линии Безье, которые получаются через метод Handles.MakeBezierPoints. если он не будет запущен, то в скрипт для генерации меша ничего не будет передано. Значит, нужно искать другой способ получения этих точек? Если это так, то есть ли подобные Handles классы, из которых можно получить массив этих точке Безье?
SweetyMorgan
UNец
 
Сообщения: 18
Зарегистрирован: 05 апр 2019, 01:23

Re: Генерация линии DrawBezier

Сообщение 1max1 11 апр 2019, 22:03

У тебя же 2 скрипта, один монобех, другой для редактора. Скрипт, который для редактора будет менять переменные в скрипте монобеха, вот как раз монобех и будет участвовать в игре.
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51

Re: Генерация линии DrawBezier

Сообщение SweetyMorgan 12 апр 2019, 13:34

1max1 писал(а):У тебя же 2 скрипта, один монобех, другой для редактора. Скрипт, который для редактора будет менять переменные в скрипте монобеха, вот как раз монобех и будет участвовать в игре.

То есть получается, что мне из монобеха нужно вызывать скрипт редактора? Потому что события редактора не будут никогда возникать. Но если так, то SceneView не будет существовать, тогда и Handles класс работать не будет, если я всё правильно понимаю.
SweetyMorgan
UNец
 
Сообщения: 18
Зарегистрирован: 05 апр 2019, 01:23

Re: Генерация линии DrawBezier

Сообщение lawson 12 апр 2019, 13:37

SweetyMorgan писал(а):
1max1 писал(а):У тебя же 2 скрипта, один монобех, другой для редактора. Скрипт, который для редактора будет менять переменные в скрипте монобеха, вот как раз монобех и будет участвовать в игре.

То есть получается, что мне из монобеха нужно вызывать скрипт редактора? Потому что события редактора не будут никогда возникать. Но если так, то SceneView не будет существовать, тогда и Handles класс работать не будет, если я всё правильно понимаю.

Все объекты с которыми вы работаете из пространства UnityEditor в сборке будут отсутствовать. Handles это UnityEditor, SceneView тоже, поэтому стройте логику так чтобы эти объекты не использовались в рантайме.
lawson
UNIверсал
 
Сообщения: 481
Зарегистрирован: 14 сен 2012, 21:20

Re: Генерация линии DrawBezier

Сообщение SweetyMorgan 12 апр 2019, 13:48

Теперь окончательно понял, спасибо!
Насколько я понимаю, самым простым решением (кроме написания собственной MakeBezierPoints) будет создать несколько массивов в редакторе, выгрузить их в csv или xml файл, а монобехом на старте игры брать любой из выгруженных массивов и генерировать меш. Или тогда вообще проще создать несколько сцен с готовым мешем?
SweetyMorgan
UNец
 
Сообщения: 18
Зарегистрирован: 05 апр 2019, 01:23

Re: Генерация линии DrawBezier

Сообщение lawson 12 апр 2019, 14:12

выгрузить их в csv или xml файл, а монобехом на старте игры брать любой из выгруженных массивов и генерировать меш

Да сохраняйте сколько угодно массивов в MonoBehaviour классе, ну или можете использовать ScriptableObejct для хранения.
lawson
UNIверсал
 
Сообщения: 481
Зарегистрирован: 14 сен 2012, 21:20

Re: Генерация линии DrawBezier

Сообщение samana 12 апр 2019, 15:10

Помню плохо, но как-то можно было легко реализовать кривые по точками прямо в игре, используя Vector3.Lerp. :-?
Аватара пользователя
samana
Адепт
 
Сообщения: 4738
Зарегистрирован: 21 фев 2015, 13:00
Откуда: Днепропетровск

Re: Генерация линии DrawBezier

Сообщение SweetyMorgan 12 апр 2019, 22:02

samana писал(а):Помню плохо, но как-то можно было легко реализовать кривые по точками прямо в игре, используя Vector3.Lerp. :-?

Проблема не в построении кривых, скорее в получении точек с шагом в 1пиксель. При помощи Handles.MakeBezierPoints можно получить множество точек на полученной кривой Безье. Аналогов не нашел, по крайней мере пока что, поэтому пока отложил это дело, но когда вернусь, то либо сделаю пресеты из редактора, которые будет использовать монобех, либо напишу функцию, похожую на MakeBezierPoints.
SweetyMorgan
UNец
 
Сообщения: 18
Зарегистрирован: 05 апр 2019, 01:23

Re: Генерация линии DrawBezier

Сообщение samana 12 апр 2019, 22:13

SweetyMorgan писал(а):При помощи Handles.MakeBezierPoints можно получить множество точек на полученной кривой Безье.

Но ведь это всё математика, имея три точки, можно "построить кривую" из бесконечного числа точек. А может я вас неправильно понимаю..
Аватара пользователя
samana
Адепт
 
Сообщения: 4738
Зарегистрирован: 21 фев 2015, 13:00
Откуда: Днепропетровск

Re: Генерация линии DrawBezier

Сообщение SweetyMorgan 13 апр 2019, 17:40

samana писал(а):
SweetyMorgan писал(а):При помощи Handles.MakeBezierPoints можно получить множество точек на полученной кривой Безье.

Но ведь это всё математика, имея три точки, можно "построить кривую" из бесконечного числа точек. А может я вас неправильно понимаю..

Как оказалось, это намного проще, чем я думал, гуглив подобные функции ;)
Синтаксис:
Используется csharp
private Vector3[] MakeBezierPoints(Vector3 p1, Vector3 p2, Vector3 t1, Vector3 t2)
    {
        Vector3[] vec = new Vector3[_terrain.terrainData.detailWidth * 2 / (_points.Length - 1) + 1];
        for (int i = 0; i <= _terrain.terrainData.detailWidth * 2 / (_points.Length - 1); i++)
        {
            float t = (float)i / (_terrain.terrainData.detailWidth * 2 / (_points.Length - 1));
            vec[i] =  (float)Math.Pow((1 - t), 3) * p1 + 3 * t * (float)Math.Pow((1 - t), 2) * t1 + 3 * (float)Math.Pow(t, 2) * (1 - t) * t2 + (float)Math.Pow(t, 3) * p2;
        }
        return vec;
    }
Правда теперь,имея эти точки мне нужно сделать 4 равноудалённые, составляющие плоскость, чтобы из них создать меш. пока хорошим способом кажется делать так:
1. Найти вектор от второй точки до первой.
2. Повернуть вектор на 90 градусов.
3. Найти точку на этом векторе на нужном расстоянии.
4. Найти остальные 3 точки, зеркально отражая их от центральной (первой) по осям.
Если кто-то предложит более компактное решение, то буду благодарен.
SweetyMorgan
UNец
 
Сообщения: 18
Зарегистрирован: 05 апр 2019, 01:23

Re: Генерация линии DrawBezier

Сообщение SweetyMorgan 15 апр 2019, 03:22

И снова здравствуйте. И снова вопрос, который, увы, не могу решить самостоятельно. Суть: скрипт генерации точке Безье есть выше, есть функция генерирования меша. Поскольку кривая Безье разбита на 10 частей, то и функция вызывается 10 раз. Если использовать:
Синтаксис:
Используется csharp
GetComponent<MeshFilter>().mesh = GenerateMesh();
то всё отлично, меш генерируется и прекрасно отображается в игре.
Теперь суть проблемы: поскольку частей 10, то эти части нужно объединить. Покопавшись в классе Mesh и Интернете, нашел функцию CombineMeshes(), соответственно пишу так:
Синтаксис:
Используется csharp
CombineInstance[] comb = new CombineInstance[_bp.Count];
        GetComponent<MeshFilter>().mesh = new Mesh();
        for (int i = 0; i < _bp.Count; i++)
        {
            comb[i].mesh = GenerateMesh(_bp[i]);
        }
        GetComponent<MeshFilter>().mesh.CombineMeshes(comb);
, запускаю игру, а на экране меша нет :(( Может кто знает что я не так делаю? Не очень хочется переписывать метод для общего генерирования для всех 10 частей в 1 меш. Уж слишком много вершин и треугольников там получается.
SweetyMorgan
UNец
 
Сообщения: 18
Зарегистрирован: 05 апр 2019, 01:23

Re: Генерация линии DrawBezier

Сообщение samana 15 апр 2019, 10:46

Метод CombineMeshes имеет параметры, попробуйте выставить второй в true.
Аватара пользователя
samana
Адепт
 
Сообщения: 4738
Зарегистрирован: 21 фев 2015, 13:00
Откуда: Днепропетровск

Re: Генерация линии DrawBezier

Сообщение SweetyMorgan 15 апр 2019, 15:20

samana писал(а):Метод CombineMeshes имеет параметры, попробуйте выставить второй в true.

Выставление параметра не помогло, насколько я помню, они по умолчанию bool mergeSubMeshes = true, bool useMatrices = true, bool hasLightmapData = false - взято из спецификации Unity.

Решил проблему. Оказывается, каждому мешу нужно было назначать transform.
Синтаксис:
Используется csharp
comb[i].transform = GetComponent<MeshFilter>().transform.localToWorldMatrix;


Сейчас скрипт генерации отрабатывает немного плохо и между мешей видны прорехи, как исправлю - выложу весь скрипт, вдруг кому будет полезно. На форумах видел кучу подобных тем.
SweetyMorgan
UNец
 
Сообщения: 18
Зарегистрирован: 05 апр 2019, 01:23

Re: Генерация линии DrawBezier

Сообщение SweetyMorgan 15 апр 2019, 20:05

Как и говорил, выкладываю полноценный скрипт, который устроил лично меня для моих целей.
Синтаксис:
Используется csharp
using System.Collections.Generic;
using UnityEngine;
using Random = System.Random;
using System;
using System.Numerics;
using UnityEngine.AI;
using UnityEngine.Experimental.AI;
using Quaternion = UnityEngine.Quaternion;
using Vector2 = UnityEngine.Vector2;
using Vector3 = UnityEngine.Vector3;

public class RoadGenerator : MonoBehaviour
{
    [SerializeField]
    private Terrain _terrain;

    [SerializeField]
    private GameObject _spawnarea;

    private double _iswidth;
    private Vector3 _startPointBezier;
    private Vector3 _endPointBezier;
    private Random _rnd = new Random();
    private Vector3[] _points = new Vector3[11];
    private Vector3[] _tangents = new Vector3[20];
    private List<Vector3[]> _bp = new List<Vector3[]>();
    public void Start()
    {
        _iswidth = _rnd.NextDouble();
        if (_iswidth >= 0.5)
        {
            _startPointBezier = new Vector3(0, 0, _rnd.Next(_terrain.terrainData.detailHeight * 2));
            _endPointBezier = new Vector3(_terrain.terrainData.detailWidth * 2, 0, _rnd.Next((int)_startPointBezier.z, _terrain.terrainData.detailHeight * 2));
            for (int i = 1; i < _points.Length - 1; i++)
            {
                _points[i] = new Vector3((float)(_terrain.terrainData.detailWidth * 2) / 10 * i, 0, _rnd.Next((int)_startPointBezier.z, (int)_endPointBezier.z));
            }
            _points[0] = _startPointBezier;
            _points[_points.Length - 1] = _endPointBezier;
            for (int i = 1; i < _tangents.Length - 1; i = i + 2)
            {
                if (_points[(i - 1) / 2].z < _points[(i + 1) / 2].z)
                    _tangents[i] =
                        new Vector3(
                            _rnd.Next((int)_points[(i - 1) / 2].x,
                                (int) (_points[(i - 1) / 2].x + (_points[(i + 1) / 2].x - _points[(i - 1) / 2].x) / 2)), 0,
                                 _rnd.Next((int)_points[(i - 1) / 2].z, (int)_points[(i + 1) / 2].z));
                else
                    _tangents[i] =
                        new Vector3(
                            _rnd.Next((int)_points[(i - 1) / 2].x,
                                (int)(_points[(i - 1) / 2].x + (_points[(i + 1) / 2].x - _points[(i - 1) / 2].x) / 2)), 0,
                                 _rnd.Next((int)_points[(i + 1) / 2].z, (int)_points[(i - 1) / 2].z));
                _tangents[i + 1] = new Vector3(
                    _points[(i + 1) / 2].x + (_points[(i + 1) / 2].x - _tangents[i].x), 0,
                    _points[(i + 1) / 2].z + (_points[(i + 1) / 2].z - _tangents[i].z));
            }
            _tangents[0] = new Vector3(
                _rnd.Next((int)_points[0].x, (int)_points[1].x), 0,
                    _rnd.Next((int)_startPointBezier.z, (int)_endPointBezier.z));
            _tangents[_tangents.Length - 1] = new Vector3(
                _rnd.Next((int)_points[_points.Length - 2].x, (int)_endPointBezier.x), 0,
                    _rnd.Next((int)_startPointBezier.z, (int)_endPointBezier.z));
        }
        else
        {
            _startPointBezier = new Vector3(_rnd.Next(_terrain.terrainData.detailWidth * 2), 0, 0);
            _endPointBezier = new Vector3(_rnd.Next((int)_startPointBezier.x, _terrain.terrainData.detailWidth * 2), 0, _terrain.terrainData.detailHeight * 2);
            for (int i = 1; i < _points.Length - 1; i++)
            {
                _points[i] = new Vector3(_rnd.Next((int)_startPointBezier.x, (int)_endPointBezier.x), 0, (float)(_terrain.terrainData.detailHeight * 2) / 10 * i);
            }
            _points[0] = _startPointBezier;
            _points[_points.Length - 1] = _endPointBezier;
            for (int i = 1; i < _tangents.Length - 1; i = i + 2)
            {
                if (_points[(i - 1) / 2].x < _points[(i + 1) / 2].x)
                    _tangents[i] = new Vector3(_rnd.Next((int)_points[(i - 1) / 2].x, (int)_points[(i + 1) / 2].x), 0,
                    _rnd.Next((int)_points[(i - 1) / 2].z,
                        (int) (_points[(i - 1) / 2].z + (_points[(i + 1) / 2].z - _points[(i - 1) / 2].z) / 2)));
                else
                    _tangents[i] =
                    new Vector3(
                        _rnd.Next((int)_points[(i + 1) / 2].x, (int)_points[(i - 1) / 2].x), 0,
                            _rnd.Next((int)_points[(i - 1) / 2].z,
                                (int) (_points[(i - 1) / 2].z + (_points[(i + 1) / 2].z - _points[(i - 1) / 2].z) / 2)));
                _tangents[i + 1] = new Vector3(
                    _points[(i + 1) / 2].x + (_points[(i + 1) / 2].x - _tangents[i].x), 0,
                    _points[(i + 1) / 2].z + (_points[(i + 1) / 2].z - _tangents[i].z));
            }
            _tangents[0] = new Vector3(
                _rnd.Next((int)_startPointBezier.x, (int)_endPointBezier.x), 0,
                    _rnd.Next((int)_points[0].z, (int)_points[1].z));
            _tangents[_tangents.Length - 1] = new Vector3(
                _rnd.Next((int)_startPointBezier.x, (int)_endPointBezier.x), 0,
                    _rnd.Next((int)_points[_points.Length - 2].z, (int)_endPointBezier.z));
        }
        Instantiate(_spawnarea, _endPointBezier, Quaternion.identity);
        Instantiate(_spawnarea, _startPointBezier, Quaternion.identity);
        _bp.Add(new Vector3[] {});
        for (int i = 0; i < _points.Length - 1; i++)
        {
           _bp.Add(MakeBezierPoints(_points[i], _points[i + 1], _tangents[(i * 2)], _tangents[(i * 2) + 1]));
        }

        _bp[0] = new Vector3[] { (_bp[1][0] + (_bp[1][0] - _bp[1][1]) * 300), _startPointBezier};
        _bp.Add(new Vector3[] { _endPointBezier,
            (_bp[_points.Length - 1][_bp[_points.Length - 1].Length - 1] +
                _bp[_points.Length - 1][_bp[_points.Length - 1].Length - 1] -
                    _bp[_points.Length - 1][_bp[_points.Length - 1].Length - 2]) * 300 });
        CombineInstance[] comb = new CombineInstance[_bp.Count];
        GetComponent<MeshFilter>().mesh = new Mesh();
        for (int i = 0; i < _bp.Count - 1; i++)
        {
            comb[i].mesh = GenerateMesh(_bp[i], i + 1);
            comb[i].transform = GetComponent<MeshFilter>().transform.localToWorldMatrix;
        }
        GetComponent<MeshFilter>().mesh.CombineMeshes(comb);
        GetComponent<MeshFilter>().mesh.RecalculateNormals();
        GetComponent<MeshFilter>().mesh.RecalculateBounds();
    }

    private Vector3[] MakeBezierPoints(Vector3 p1, Vector3 p2, Vector3 t1, Vector3 t2)
    {
        Vector3[] vec = new Vector3[_terrain.terrainData.detailWidth * 2 / (_points.Length - 1) + 1];
        for (int i = 0; i <= _terrain.terrainData.detailWidth * 2 / (_points.Length - 1); i++)
        {
            float t = (float)i / (_terrain.terrainData.detailWidth * 2 / (_points.Length - 1));
            vec[i] =  (float)Math.Pow((1 - t), 3) * p1 + 3 * t * (float)Math.Pow((1 - t), 2) * t1 + 3 * (float)Math.Pow(t, 2) * (1 - t) * t2 + (float)Math.Pow(t, 3) * p2;
        }
        return vec;
    }

    private Mesh GenerateMesh(Vector3[] bp, int k)
    {
        Mesh mesh = new Mesh();
        Vector3[] newbp = new Vector3[bp.Length * 4];
        int <img src="./images/smilies/unmarked.gif" alt="[]" title="Запланировано" /> triangles = new int[bp.Length * 24];
        Vector2[] uv = new Vector2[newbp.Length];
        for (int i = 0; i < bp.Length; i++)
        {
            Vector3 vector;
            if (i == bp.Length - 1)
                vector = new Vector3(-(_bp[k][1].z - bp[i].z), _bp[k][1].y - bp[i].y, _bp[k][1].x - bp[i].x).normalized;
            else
                vector = new Vector3(-(bp[i + 1].z - bp[i].z), bp[i + 1].y - bp[i].y, bp[i + 1].x - bp[i].x).normalized;
            newbp[i * 4] =  new Vector3(vector.x * -6f, vector.y + 0.2f, vector.z * -6f) + bp[i];
            newbp[i * 4 + 1] = new Vector3(vector.x * -6f, vector.y - 0.2f, vector.z * -6f) + bp[i];
            newbp[i * 4 + 2] = new Vector3(vector.x * 6f, vector.y - 0.2f, vector.z * 6f) + bp[i];
            newbp[i * 4 + 3] = new Vector3(vector.x * 6f, vector.y + 0.2f, vector.z * 6f) + bp[i];
            if (i < bp.Length - 1)
            for (int j = 0; j < 4; j++)
            {
                triangles[i * 24 + j * 6] = i * 4 + j;
                triangles[i * 24 + j * 6 + 1] = (i + 1) * 4 + j;
                if (j == 3)
                {
                    triangles[i * 24 + j * 6 + 2] = (i + 1) * 4 + 1 + j - 4;
                    triangles[i * 24 + j * 6 + 3] = (i + 1) * 4 + 1 + j - 4;
                    triangles[i * 24 + j * 6 + 4] = i * 4 + 1 + j - 4;
                }
                else
                {
                    triangles[i * 24 + j * 6 + 2] = (i + 1) * 4 + 1 + j;
                    triangles[i * 24 + j * 6 + 3] = (i + 1) * 4 + 1 + j;
                    triangles[i * 24 + j * 6 + 4] = i * 4 + 1 + j;
                }

                triangles[i * 24 + j * 6 + 5] = i * 4 + j;
            }
        }

        for (int i = 0; i < uv.Length; i++)
        {
            uv[i] = new Vector2(newbp[i].x, newbp[i].z);
        }

        Vector3[] normals = new Vector3[newbp.Length];
        for (int i = 0; i < bp.Length - 1; i++)
        {
            normals[i * 4] = Vector3.Cross((newbp[i * 4 + 1] - newbp[i * 4]), (newbp[i * 4 + 3] - newbp[i * 4])).normalized;
            normals[i * 4 + 1] = Vector3.Cross((newbp[i * 4] - newbp[i * 4 + 1]), (newbp[i * 4 + 2] - newbp[i * 4 + 1])).normalized;
            normals[i * 4 + 2] = Vector3.Cross((newbp[i * 4 + 1] - newbp[i * 4 + 2]), (newbp[i * 4 + 3] - newbp[i * 4 + 2])).normalized;
            normals[i * 4 + 3] = Vector3.Cross((newbp[i * 4] - newbp[i * 4 + 3]), (newbp[i * 4 + 2] - newbp[i * 4 + 3])).normalized;
        }

        mesh.vertices = newbp;
        mesh.uv = uv;
        mesh.triangles = triangles;
        mesh.normals = normals;
        mesh.RecalculateNormals();
        mesh.RecalculateBounds();
        return mesh;
    }
}
 

В результате получаются вот такие дороги на terrain'e
Изображение
Изображение
Изображение
Примечания по скрипту так же приветствуются, кусок меша вне terrain'a не артефакт, это как бы продолжение дороги вне игровой области, дабы наша дорога не обрывалась на краю terrain'a, сделать такой же симметричный кусок мне не понадобилось, но имея код, всё возможно. Извиняюсь, что не присутствуют комментарии в коде. Так же количеством полигонов и отрезков можно "играть", изменяя размер массива _points. Всме спасибо за ваши ответы, это очень помогло. :ymparty:
SweetyMorgan
UNец
 
Сообщения: 18
Зарегистрирован: 05 апр 2019, 01:23

Пред.След.

Вернуться в Скрипты

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 5