Тема: Алгоритм руху точок по кривій

Є структура "Coordinates", яка містить інформацію про точку в двовимірному просторі

struct Coordinates
{
    public float x;
    public float y;
}

Створюється об'єкт будь-якої кривої (точки по яким можна з'єднати криву).
Приклад, крива що нагадує квадрат

    square = new Coordinates[4];
    square[0].x = -2.5f;
    square[0].y = 2.5f;

    square[1].x = 2.5f;
    square[1].y = 2.5f;

    square[2].x = 2.5f;
    square[2].y = -2.5f;

    square[3].x = -2.5f;
    square[3].y = -2.5f;

Потрібно зробити рух цих точок по уявній кривій.
square[0] рухається до square[1], square[1] рухається до square[2] і т.д.

http://не-дійсний-домен/i9/7c9499e2f264bd6576a1c2a526840d5f/1516267281/27014/1214016/Untitled.jpg
Червоним кольором показаний рух за часовою стрілкою, зеленим - проти.

1) Рух має бути справжнім, тобто не можна зробити так, що коли одна точка дійшла до наступної (наприклад, square[0] до позиції де була точка square[1]), ця точка миттєво поверталася до своєї початкової позиції, і починала рух спочатку до square[1]. Згодом буде додана можливість видаляти якість точки, тому ілюзія руху не підійде.

2) Якщо одна з точок добралася до наступної позиції раніше інших, то ця точка має чекати поки всі інші точки не доберуться до свої наступних позицій, і тільки тоді продовжувати свій рух. (звичайно, у кривій яка схожа на квадрат, всі точки будуть рухатися рівномірно, але як я казав, можна створити будь яку криву)

3) Швидкість: виклик функції прив'язаний до часу, тому можна зробити додаткову змінну (наприклад speedMoveAlongCurve) яка буде відповідати за швидкість

4) Напрямок руху: по часовій стрілці, або проти. Як напрямок руху краще задавати я не знаю, можна зробити додатну і від'ємну швидкість: "+" це по часовій стрілці, "-" проти часової стрілки. Можливо зробити окрему булеву змінну...

Допоможіть скласти алгоритм, або код на C#.

Дальше краще не читайте, бо там все дуже погано ... , легше почати все спочатку.

Створюю цикл де b відповідає за кількість ітерації (b < всі_точок)
i - точка яка буде рухатися, j точка до якої потрібно рухатися
moveAlongCurveInd - глобальний індекс (поза функцією), що перемикає точки

for(
    ushort b = 0, 
    i = moveAlongCurveInd, 
    j = (ushort)(moveAlongCurveInd + 1);
        b < points.Length;
            b++, i++, j++
    )

Дізнаюся, точка до якої потрібно рухатися має значення зі знаком "-" чи "+"

if (points[j].x < 0)
{
    speedMoveAlongCurve = -(Mathf.Abs( speedMoveAlongCurve ));
}
else
{
    speedMoveAlongCurve = +(Mathf.Abs(speedMoveAlongCurve));
}

Якщо точки, яка рухається і до якої потрібно рухатися, не однакові, то збільшую її на інтервал швидкість speedMoveAlongCurve.

float _X = _PointArr[i].transform.position.x;

if (points[j].x != _PointArr[i].transform.position.x)
{
    _X = _PointArr[i].transform.position.x + speedMoveAlongCurve;
}

те ж саме роблю з "У" точками.

Перевіряю чи всі точки дійшли до запланованих позицій

nextInd[j] = false;
if (points[j].x < 0)
{
    //-
    if (_X <= points[j].x)
    {
        nextInd[j] = true;
    }
    else
    {
        nextInd[j] = false;
    }
}
else
{
    //+
    if (_X >= points[j].x)
    {
        nextInd[j] = true;
    }
    else
    {
        nextInd[j] = false;
    }
}
//
if (points[j].y < 0)
{
    //-
    if (_Y <= points[j].y)
    {
        nextInd[j] = nextInd[j] && true;
    }
    else
    {
        nextInd[j] = nextInd[j] && false;
    }
}
else
{
    //+
    if (_Y >= points[j].y)
    {
        nextInd[j] = nextInd[j] && true;
    }
    else
    {
        nextInd[j] = nextInd[j] && false;
    }
}
bool indSum = nextInd[0];
for (int k = 1; k < points.Length; k++)
    indSum = nextInd[k] && indSum;

Збільшую глобальний індекс, якщо всі точки добралися до запланованих позицій

if (indSum && moveAlongCurveInd + 1 < points.Length)
{ moveAlongCurveInd += 1; }
else if (indSum && moveAlongCurveInd >= points.Length)
{ moveAlongCurveInd = 0; }

Весь код

/*Move along the Curve*/
private ushort moveAlongCurveInd;
private bool[] nextInd;
private void moveAlongCurve(Coordinates[] points, bool direction/*0 - left, 1 - right*/)
{        
    for (
        ushort b = 0, 
        i = moveAlongCurveInd, 
        j = (ushort)(moveAlongCurveInd + 1);
            b < points.Length;
                b++, i++, j++
         )
    {
        if (i == 4) i = 0;
        if (j == 4) j = 0;
        if (points[j].x < 0)
        {
            speedMoveAlongCurve = -(Mathf.Abs( speedMoveAlongCurve ));
        }
        else
        {
            speedMoveAlongCurve = +(Mathf.Abs(speedMoveAlongCurve));
        }
        
        float _X = _PointArr[i].transform.position.x;

        if (points[j].x != _PointArr[i].transform.position.x)
        {
            _X = _PointArr[i].transform.position.x + speedMoveAlongCurve;
        }


        if (points[j].y < 0)
        {
            speedMoveAlongCurve = -(Mathf.Abs(speedMoveAlongCurve));
        }
        else
        {
            speedMoveAlongCurve = +(Mathf.Abs(speedMoveAlongCurve));
        }

        float _Y = _PointArr[i].transform.position.y;

        if (points[j].y != _PointArr[i].transform.position.y)
        {
            _Y = _PointArr[i].transform.position.y + speedMoveAlongCurve;
        }

        nextInd[j] = false;
        if (points[j].x < 0)
        {
            //-
            if (_X <= points[j].x)
            {
                nextInd[j] = true;
            }
            else
            {
                nextInd[j] = false;
            }
        }
        else
        {
            //+
            if (_X >= points[j].x)
            {
                nextInd[j] = true;
            }
            else
            {
                nextInd[j] = false;
            }
        }
        //
        if (points[j].y < 0)
        {
            //-
            if (_Y <= points[j].y)
            {
                nextInd[j] = nextInd[j] && true;
            }
            else
            {
                nextInd[j] = nextInd[j] && false;
            }
        }
        else
        {
            //+
            if (_Y >= points[j].y)
            {
                nextInd[j] = nextInd[j] && true;
            }
            else
            {
                nextInd[j] = nextInd[j] && false;
            }
        }
        //
        bool indSum = nextInd[0];
        for (int k = 1; k < points.Length; k++)
            indSum = nextInd[k] && indSum;
        if (indSum && moveAlongCurveInd + 1 < points.Length)
        { moveAlongCurveInd += 1; }
        else if (indSum && moveAlongCurveInd >= points.Length)
        { moveAlongCurveInd = 0; }

        _PointArr[i].transform.position = new Vector3(
            _X,
            _Y,
            0
            );
    }
}
Подякували: NaharD, 0xDADA11C7, koala3

2

Re: Алгоритм руху точок по кривій

У вас крива чи все ж ламана? Точки рухаються по відрізках прямих чи по дугах, сплайнах і т.д.? Чи виключно паралельно осям координат?
Точки рухаються одна до одної, чи є два набори точок - нерухомий маршрут і рухомі точки, що починають і закінчують рух у вершинах цього маршруту?

Подякували: Betterthanyou1

3 Востаннє редагувалося Betterthanyou (18.01.2018 15:36:30)

Re: Алгоритм руху точок по кривій

koala написав:

У вас крива чи все ж ламана? Точки рухаються по відрізках прямих чи по дугах, сплайнах і т.д.? Чи виключно паралельно осям координат?

Я хочу реалізувати можливість руху по прямих і сплайнах. Наприклад, якщо вказати восьмикутник, і рухатися по прямих - це буде восьмикутник, якщо по сплайнах це буде коло.

koala написав:

Точки рухаються одна до одної, чи є два набори точок - нерухомий маршрут і рухомі точки, що починають і закінчують рух у вершинах цього маршруту?

Є два набори точок, рухомі і не рухомі
рухомі - відображаються у вікні програми і все що з ними відбувається видно у вікні програми, нерухомі потрібні лише для того що не втратити початкові координати, вони не відображаються.


Рухома (видима точка) рухається до не рухомої...
Наприклад
        1 Перша точка
        square[0].x = -2.5f;
        square[0].y = 2.5f;

        1 Копія
        _PointArr[0].x = -2.5f;
        _PointArr[0].y = 2.5f;



        2 Друга точка
        square[1].x = 2.5f;
        square[1].y = 2.5f;

        2 Копія
        _PointArr[0].x = -2.5f;
        _PointArr[0].y = 2.5f;



        3 Третя точка
        square[2].x = 2.5f;
        square[2].y = -2.5f;

        3 Копія
        _PointArr[0].x = -2.5f;
        _PointArr[0].y = 2.5f;



        4 Четверта точка
        square[3].x = -2.5f;
        square[3].y = -2.5f;

        4 Копія
        _PointArr[0].x = -2.5f;
        _PointArr[0].y = 2.5f;


1 Копія   Рухається До  2 Точка
2 Копія   До  3 Точка
3 Копія   До  4 Точка
4 Копія   До  1 Точка

коли рух завершений (  1 Точка ==  2 Копія, 2 Точка ==  3 Копія і т.д. )
починається рух до нової точки

1 Копія   До  3 Точка
2 Копія   До  4 Точка
3 Копія   До  1 Точка
4 Копія   До  2 Точка

і т.д.

4 Востаннє редагувалося koala (18.01.2018 16:13:59)

Re: Алгоритм руху точок по кривій

Ок, в такому разі не називайте їх копіями, тільки все заплутуєте.
Отже, є маршрут, по якому рухається кожна точка. Довжина маршрута відома (про це далі), швидкість теж - значить, треба поділити довжину на ряд сегментів однакової довжини, і на кожній ітерації переносити точку в кінець відповідного сегменту. Коли маршрут закінчується, точка "перемикається" на наступний маршрут. Є якісь проблеми це запрограмувати?
Тепер - про довжину. Якщо у нас відрізки прямих, то довжина обчислюється за Евклідом (або теоремою Піфагора, це для нас те саме). Якщо треба перейти з (x0,y0) в (x1,y1) за n кроків, то на i-му кроці об'єкт буде в
((x1-x0)*i/n, (y1-y0)*i/n). Але якщо це сплайн, то доведеться брати інтеграл.

Подякували: Betterthanyou, leofun012

5

Re: Алгоритм руху точок по кривій

Вийшло зробити рух по ламаній, зараз буду з інтегралом робити

/*Move along the Curve*/
    public ushort segmentNumberAlongCurve;// Number of segments
    private ushort moveAlongCurveSwitcher; // Switcher
    private ushort moveAlongCurveCurrentSegment; // Current segment
    /*
     Current Point go to the Gold Point
    */
    private void moveAlongCurve(Coordinates[] points, bool direction/*0 - left, 1 - right*/)
    {
        bool nextPoint = false; //switch points
        moveAlongCurveCurrentSegment++;//Increase the Current segment to one
        ushort i = 0, /*i - Current point for the cycle*/
            j = 0 /*j - Gold point for the cycle*/;

        //choose direction
        if (direction)
        { i = (ushort)(moveAlongCurveSwitcher - 1); j = moveAlongCurveSwitcher; }
        else
        { i = moveAlongCurveSwitcher; j = (ushort)(moveAlongCurveSwitcher - 1); }


        for (ushort b = 0; b < points.Length; i++, j++, b++)
        {
            //reset the variable (avoid the range out of array)
            if (i == points.Length) i = 0;
            if (j == points.Length) j = 0;

            //the goal achieved
            if (moveAlongCurveCurrentSegment == segmentNumberAlongCurve)
            { moveAlongCurveCurrentSegment = 0; nextPoint = true; }

            //Get points
            float _X = MyMath.linspacePoint(
                points[i].x,
                points[j].x,
                segmentNumberAlongCurve,
                moveAlongCurveCurrentSegment
                );
            float _Y = MyMath.linspacePoint(
                points[i].y,
                points[j].y,
                segmentNumberAlongCurve,
                moveAlongCurveCurrentSegment
                );            

            //Set points to screen
            _PointArr[i].transform.position = new Vector3(
                _X,
                _Y,
                0
                );

            //switch points
            if (nextPoint && moveAlongCurveSwitcher != points.Length)
            { moveAlongCurveSwitcher++; nextPoint = false; }
            else if (nextPoint && moveAlongCurveSwitcher == points.Length)
            { moveAlongCurveSwitcher = 1; nextPoint = false; }
        }
    }
public static class MyMath
{
    public static float linspacePoint(float min, float max, ushort points, ushort ind)
    {
        return min + ind * (max - min) / (points - 1);        
    }
}
Подякували: koala, sensei2

6

Re: Алгоритм руху точок по кривій

Betterthanyou написав:

якщо по сплайнах це буде коло.

Чому це буде коло?

7 Востаннє редагувалося Betterthanyou (02.02.2018 17:42:55)

Re: Алгоритм руху точок по кривій

Бо лінія згладжується..., Сплайн — функція, область визначення якої розбита на шматки, на кожному зі шматків функція є деяким поліномом (многочленом).

https://replace.org.ua/extensions/om_images/img/5a7486ffa7405/uw8oyy89.png

8 Востаннє редагувалося Vyachek Astrofizuk (17.02.2018 15:39:40)

Re: Алгоритм руху точок по кривій

Betterthanyou Рівняння багатокутника P.S. Розділ геометрія. А якщо хочеш коло, тоді рівняння кола. :|
Зараз я коло згадаю: sqr(x0-a)+sqr(y0-b)=sqr(R)