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

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

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

Сообщение SweetyMorgan 07 апр 2019, 18:31

Всем привет. Собственно сабж: при старте игры я хочу сгенерировать случайную дорогу, пересекающую весь terrain, с помощью построения кривой Безье и последующего построения меша дороги. Получился вот такой скрипт:
Синтаксис:
Используется csharp
[SerializeField]
    private Terrain _terrain;

    private Vector3 _startPointBezier;
    private Vector3 _endPointBezier;
    private Vector3 _topLine;
    private Vector3 _bottomLine;
    private Random _rnd = new Random();
    private Vector3[] _points = new Vector3[11];
    private Vector3[] _tangents = new Vector3[11];
    void Start()
    {
        int iswidth = _rnd.Next(0, 1);
        if (iswidth == 0)
        {
            _startPointBezier = new Vector3(0,0, (float)_rnd.NextDouble());
            _endPointBezier = new Vector3(1, 0, (float)_rnd.NextDouble());
            if (_startPointBezier.z > _endPointBezier.z)
            {
                Vector3 tmp = _endPointBezier;
                _endPointBezier = _startPointBezier;
                _startPointBezier = tmp;
            }
            _topLine = new Vector3(1, 0, _startPointBezier.z);
            _bottomLine = new Vector3(1, 0, _endPointBezier.z);
            for (int i = 1; i < _points.Length - 1; i++)
            {
                _points[i] = new Vector3((float)(1) / 10 * i, 0, (float)(_rnd.Next((int)(_startPointBezier.z * 10), (int)(_endPointBezier.z * 10))) / 10);
            }
        }
        else
        {
            _startPointBezier = new Vector3((float)_rnd.NextDouble(), 0, 0);
            _endPointBezier = new Vector3((float)_rnd.NextDouble(), 0, 1);
            if (_startPointBezier.x > _endPointBezier.x)
            {
                Vector3 tmp = _endPointBezier;
                _endPointBezier = _startPointBezier;
                _startPointBezier = tmp;
            }
            _topLine = new Vector3(_startPointBezier.x, 0, 1);
            _bottomLine = new Vector3(_endPointBezier.x, 0, 1);
            for (int i = 1; i < _points.Length - 1; i++)
            {
                _points[i] = new Vector3((float)(_rnd.Next((int)(_startPointBezier.x * 10), (int)(_endPointBezier.x * 10)) / 10), 0, (float)(1) / 10 * i);
            }
        }
        _points[0] = _startPointBezier;
        _points[_points.Length - 1] = _endPointBezier;
        for (int i = 0; i < _tangents.Length - 1; i++)
        {
            _tangents[i] = new Vector3(_points[i + 1].x - _points[i].x, 0, _points[i + 1].z - _points[i].z);
        }
        _tangents[_tangents.Length - 1] = new Vector3(0.1f, 0, _endPointBezier.z);
        for (int i = 0; i < _points.Length - 1; i++)
        {
            Handles.color = Color.white;
            Handles.DrawBezier(_points[i], _points[i + 1], _tangents[i], _tangents[i + 1], Color.white, null, 10f);
        }
    }

Но при запуске вылетает ошибка: NullReferenceException: Object reference not set to an instance of an object
UnityEditor.Handles.DrawBezier (UnityEngine.Vector3 startPosition, UnityEngine.Vector3 endPosition, UnityEngine.Vector3 startTangent, UnityEngine.Vector3 endTangent, UnityEngine.Color color, UnityEngine.Texture2D texture, System.Single width) для строки 66, это строка с непосредственным вызовом метода Handles.DrawBezier(). Насколько я понимаю, Unity ругается на то, что я обращаюсь к чему-то через reference, а не напрямую, но покопавшись в спецификациях не нашел ни слова о том, что для этого метода нужен вообще какой-либо объект. Пока я дошел до того момента, что я ведь рисую на terrain'e, а значит у него должно быть место в парметрах, но такого пункта нет, а terrain, собственно передаётся из Unity в скрипт из сцены. Может кто-то чем-то помочь? Хотя направление "лопаты" указать куда копать дальше?
SweetyMorgan
UNец
 
Сообщения: 18
Зарегистрирован: 05 апр 2019, 01:23

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

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

Может _points заполнен не полностью?
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51

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

Сообщение SweetyMorgan 07 апр 2019, 19:05

1max1 писал(а):Может _points заполнен не полностью?

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

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

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

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

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

Сообщение SweetyMorgan 07 апр 2019, 21:36

1max1 писал(а):Запусти в режиме дебага и посмотри что именно там null.

Все параметры метода заполнены. Ошибка ссылается на 2 места: строка, которую я указал и 1060ая строка в Handle.cs, там такой кусок:
Синтаксис:
Используется csharp
///   <para>Draw a dot handle. Pass this into handle functions.</para>
    /// </summary>
    /// <param name="controlID">The control ID for the handle.</param>
    /// <param name="position">The position of the handle in the space of Handles.matrix.</param>
    /// <param name="rotation">The rotation of the handle in the space of Handles.matrix.</param>
    /// <param name="size">The size of the handle in the space of Handles.matrix. Use HandleUtility.GetHandleSize if you want a constant screen-space size.</param>
    /// <param name="eventType">Event type for the handle to act upon. By design it handles EventType.Layout and EventType.Repaint events.</param>
    public static void DotHandleCap(
      int controlID,
      Vector3 position,
      Quaternion rotation,
      float size,
      EventType eventType)
    {
      switch (eventType)
      {
        case EventType.Repaint:
          position = Handles.matrix.MultiplyPoint(position);
          Vector3 vector3_1 = (!((UnityEngine.Object) Camera.current == (UnityEngine.Object) null) ? Camera.current.transform.right : Vector3.right) * size;
          Vector3 vector3_2 = (!((UnityEngine.Object) Camera.current == (UnityEngine.Object) null) ? Camera.current.transform.up : Vector3.up) * size;
          Color c = Handles.color * new Color(1f, 1f, 1f, 0.99f);
          HandleUtility.ApplyWireMaterial(Handles.zTest);
          GL.Begin(7);
          GL.Color(c);
          GL.Vertex(position + vector3_1 + vector3_2);
          GL.Vertex(position + vector3_1 - vector3_2);
          GL.Vertex(position - vector3_1 - vector3_2);
          GL.Vertex(position - vector3_1 + vector3_2);
          GL.End();
          break;
        case EventType.Layout:
          HandleUtility.AddControl(controlID, HandleUtility.DistanceToRectangle(position, rotation, size));
          break;
      }
    }

Конкретная строка - int controlID.
Насколько я понимаю, ControlID является как раз null, но как туда что-то поместить? Ведь метод совсем другой.
SweetyMorgan
UNец
 
Сообщения: 18
Зарегистрирован: 05 апр 2019, 01:23

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

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

Скорее всего нужно делать через Editor как здесь https://docs.unity3d.com/ScriptReferenc ... ezier.html
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51

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

Сообщение Cr0c 08 апр 2019, 00:16

Как минимум, Handles - это исключительно эдитор. А из нульрефа тут только Texture2D ссылочный.
Аватара пользователя
Cr0c
Адепт
 
Сообщения: 3035
Зарегистрирован: 19 июн 2015, 13:50
Skype: cr0c81

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

Сообщение SweetyMorgan 09 апр 2019, 00:31

Спасибо всем за ответы, немного покопавшись и сделав по образу и подобию получились 2 вот таких скрипта:
1. Скрипт привязан к terrain и выполняет расчёты векторов для рисования кривых Безье:
Синтаксис:
Используется csharp
[SerializeField]
    private Terrain _terrain;

    public Vector3 StartPointBezier;
    public Vector3 EndPointBezier;
    private Random _rnd = new Random();
    public Vector3[] Points = new Vector3[11];
    public Vector3[] Tangents = new Vector3[11];
    void Start()
    {
        double iswidth = _rnd.NextDouble();
        if (iswidth >= 0.5)
        {
            StartPointBezier = new Vector3(0, 1, _rnd.Next(_terrain.terrainData.detailHeight * 2));
            EndPointBezier = new Vector3(_terrain.terrainData.detailWidth * 2, 1, _rnd.Next(_terrain.terrainData.detailHeight * 2));
            if (StartPointBezier.z > EndPointBezier.z)
            {
                Vector3 tmp = EndPointBezier;
                EndPointBezier = StartPointBezier;
                StartPointBezier = tmp;
                StartPointBezier.x = 0;
                EndPointBezier.x = _terrain.terrainData.detailWidth * 2;
            }
            for (int i = 1; i < Points.Length - 1; i++)
            {
                Points[i] = new Vector3((float)(_terrain.terrainData.detailWidth * 2) / 10 * i, 1, _rnd.Next((int)StartPointBezier.z, (int)EndPointBezier.z));
            }
        }
        else
        {
            StartPointBezier = new Vector3(_rnd.Next(_terrain.terrainData.detailHeight * 2), 1, 0);
            EndPointBezier = new Vector3(_rnd.Next(_terrain.terrainData.detailHeight * 2), 1, _terrain.terrainData.detailHeight * 2);
            if (StartPointBezier.x > EndPointBezier.x)
            {
                Vector3 tmp = EndPointBezier;
                EndPointBezier = StartPointBezier;
                StartPointBezier = tmp;
                StartPointBezier.z = 0;
                EndPointBezier.z = _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), 1, (float)(_terrain.terrainData.detailHeight * 2) / 10 * i);
            }
        }
        Points[0] = StartPointBezier;
        Points[Points.Length - 1] = EndPointBezier;
        for (int i = 0; i < Tangents.Length - 1; i++)
        {
            Tangents[i] = new Vector3(Points[i + 1].x - Points[i].x, 1, (_terrain.terrainData.detailHeight * 2) - Points[i].z);
        }
        Tangents[Tangents.Length - 1] = new Vector3((float)(_terrain.terrainData.detailHeight * 2) / 10, 1, EndPointBezier.z);
    }

2. Скрипт ни к чему не привязан и выполняется при клике на terrain'e в редакторе, при постановки PlayMod'a на паузу линии становится видно:
Синтаксис:
Используется csharp
[CustomEditor(typeof(RoadGenerator))]
    public class DrawBezierExample : Editor
    {
        private void OnSceneViewGUI(SceneView sv)
        {
            RoadGenerator rg = target as RoadGenerator;
            for (int i = 0; i < rg.Points.Length - 2; i++)
            {
                rg.Points[i] = Handles.PositionHandle(rg.Points[i], Quaternion.identity);
                rg.Points[i + 1] = Handles.PositionHandle(rg.Points[i + 1], Quaternion.identity);
                rg.Tangents[i] = Handles.PositionHandle(rg.Tangents[i], Quaternion.identity);
                rg.Tangents[i + 1] = Handles.PositionHandle(rg.Tangents[i + 1], Quaternion.identity);
                Handles.DrawBezier(rg.Points[i], rg.Points[i + 1], rg.Tangents[i], rg.Tangents[i + 1], Color.white, null, 10f);
            }
        }

        void OnEnable()
        {
            SceneView.onSceneGUIDelegate += OnSceneViewGUI;
        }

        void OnDisable()
        {
            SceneView.onSceneGUIDelegate -= OnSceneViewGUI;
        }
    }

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

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

Сообщение SweetyMorgan 09 апр 2019, 17:23

Если кому-то вдруг интересно, то вот получившиеся скрипты для генерации "хороших" линий, под "хорошими" я подразумеваю без разрывов и сильных отклонений значений, другими словами своего рода интерполированных линий.
Класс генерации значений:
Синтаксис:
Используется csharp
public class RoadGenerator : MonoBehaviour
{
    [SerializeField]
    private Terrain _terrain;

    public Vector3 StartPointBezier;
    public Vector3 EndPointBezier;
    private Random _rnd = new Random();
    public Vector3[] Points = new Vector3[11];
    public Vector3[] Tangents = new Vector3[20];
    void Start()
    {
        double iswidth = _rnd.NextDouble();
        if (iswidth >= 0.5)
        {
            StartPointBezier = new Vector3(0, 1, _rnd.Next(_terrain.terrainData.detailHeight * 2));
            EndPointBezier = new Vector3(_terrain.terrainData.detailWidth * 2, 1, _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, 1, _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)), 1,
                                 _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)), 1,
                                 _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), 1,
                        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), 1,
                    _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), 1,
                    _rnd.Next((int)StartPointBezier.z, (int)EndPointBezier.z));
        }
        else
        {
            StartPointBezier = new Vector3(_rnd.Next(_terrain.terrainData.detailWidth * 2), 1, 0);
            EndPointBezier = new Vector3(_rnd.Next((int)StartPointBezier.x, _terrain.terrainData.detailWidth * 2), 1, _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), 1, (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), 1,
                    _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), 1,
                            _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), 1,
                        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), 1,
                    _rnd.Next((int)Points[0].z, (int)Points[1].z));
            Tangents[Tangents.Length - 1] = new Vector3(
                _rnd.Next((int)StartPointBezier.x, (int)EndPointBezier.x), 1,
                    _rnd.Next((int)Points[Points.Length - 2].z, (int)EndPointBezier.z));
        }
    }
}


Класс непосредственно рисования линий на terrain:
Синтаксис:
Используется csharp
[CustomEditor(typeof(RoadGenerator))]
    public class DrawBezierExample : Editor
    {
        private void OnSceneViewGUI(SceneView sv)
        {
            RoadGenerator rg = target as RoadGenerator;
            for (int i = 0; i < rg.Points.Length - 1; i++)
            {
                rg.Points[i] = Handles.PositionHandle(rg.Points[i], Quaternion.identity);
                rg.Points[i + 1] = Handles.PositionHandle(rg.Points[i + 1], Quaternion.identity);
                rg.Tangents[(i * 2)] = Handles.PositionHandle(rg.Tangents[(i * 2)], Quaternion.identity);
                rg.Tangents[(i * 2) + 1] = Handles.PositionHandle(rg.Tangents[(i * 2) + 1], Quaternion.identity);
                Handles.DrawBezier(rg.Points[i], rg.Points[i + 1], rg.Tangents[(i * 2)], rg.Tangents[(i * 2) + 1], Color.white, null, 2f);
            }
        }

        void OnEnable()
        {
            SceneView.onSceneGUIDelegate += OnSceneViewGUI;
        }

        void OnDisable()
        {
            SceneView.onSceneGUIDelegate -= OnSceneViewGUI;
        }
    }

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

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

Сообщение lawson 09 апр 2019, 18:38

Если у кого-то есть замечания, комментарии или советы по чему-либо, то с радостью выслушаю.

Синтаксис:
Используется csharp
     void OnEnable()
        {
            SceneView.onSceneGUIDelegate += OnSceneViewGUI;
        }

        void OnDisable()
        {
            SceneView.onSceneGUIDelegate -= OnSceneViewGUI;
        }

Можно без делегата сразу рисовать на сцене через специальный метод редактора OnSceneGUI
lawson
UNIверсал
 
Сообщения: 481
Зарегистрирован: 14 сен 2012, 21:20

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

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

lawson писал(а):
Если у кого-то есть замечания, комментарии или советы по чему-либо, то с радостью выслушаю.

Синтаксис:
Используется csharp
     void OnEnable()
        {
            SceneView.onSceneGUIDelegate += OnSceneViewGUI;
        }

        void OnDisable()
        {
            SceneView.onSceneGUIDelegate -= OnSceneViewGUI;
        }

Можно без делегата сразу рисовать на сцене через специальный метод редактора OnSceneGUI

Это хорошая идея, но в данный момент используя OnSceneGUI или OnSceneViewGUI, неважно, скрипт выполняется каждый кадр и это как минимум нагружает ЦП, к тому же я не могу вынести работу с Handles за пределы стандартных методов Editor'a, потому что ему требуется SceneView и GUI, где рисовать при помощи Handles. Есть атрибут InitializeOnLoad, но, кажется, он для кастомных редакторов, однако, опять же я не понимаю куда этот атрибут вешать. Вопрос я бы сформулировал так: есть ли способ, и каков он, чтобы выполнять скрипт редактора не каждый кадр, а единожды при запуске сцены? При этом скрипт, который выполняет расчёты точек должен быть выполнен до скрипта редактора.
SweetyMorgan
UNец
 
Сообщения: 18
Зарегистрирован: 05 апр 2019, 01:23

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

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

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

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

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

1max1 писал(а):У скрипта редактора есть метод Awake и OnEnable такой же как и у монобеха.

В Awake и OnEnable нельзя использовать Handles, но Handles можно использовать в делегате, но когда в любом из этих методов я пишу присвоение делегата
Синтаксис:
Используется csharp
void OnEnable()
        {
            SceneView.onSceneGUIDelegate += OnSceneViewGUI;
        }

, то сам делегат выполняется опять же постоянно. Уж не знаю в чем конкретно проблема: в том, что OnEnable срабатывает каждый кадр или в том, что скрипт написан неверно, в любом случае, работает он постоянно. Вот весь скрипт:
Синтаксис:
Используется csharp
using UnityEngine;
using UnityEditor;

namespace Assets.Scripts
{
    [CustomEditor(typeof(RoadGenerator))]
    public class DrawRoad : Editor
    {
        private Vector3[] bp;
        private void OnSceneViewGUI(SceneView sv)
        {
            RoadGenerator rg = target as RoadGenerator;
            for (int i = 0; i < rg.Points.Length - 1; i++)
            {
                //Устанавливаю позиции Handles, получаю точки из кривой Безье и запускаю метод из целевого скрипта RoadGenerator
            }
        }
        void OnEnable()
        {
            SceneView.onSceneGUIDelegate += OnSceneViewGUI;
        }
        void OnDisable()
        {
            SceneView.onSceneGUIDelegate -= OnSceneViewGUI;
        }
    }
}
SweetyMorgan
UNец
 
Сообщения: 18
Зарегистрирован: 05 апр 2019, 01:23

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

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

Ну сделай булевую переменную в качестве флага да и все.
Синтаксис:
Используется csharp
if (!flag) return;
flag = false;
// код
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51

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

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

1max1 писал(а):Ну сделай булевую переменную в качестве флага да и все.
Синтаксис:
Используется csharp
if (!flag) return;
flag = false;
// код

Походит на "костыль", думал, может есть какой атрибут или свойство, чтобы делегат не выполнялся каждый кадр. И в таком случае другой вопрос: игра ведь будет запускаться без редактора, в таком случае когда срабатывает OnEnable()? Игра, имеется ввиду конечный продукт, запускаемый через exe файл.
SweetyMorgan
UNец
 
Сообщения: 18
Зарегистрирован: 05 апр 2019, 01:23

След.

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

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

Сейчас этот форум просматривают: Yandex [Bot] и гости: 6