Swipe track controller с увеличением скорости для СпэйсШутер

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

Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение LoveUnity 22 дек 2019, 07:21

Всем привет. Есть задача сделать такое же управление как в этой игре:
https://play.google.com/store/apps/deta ... ssic&hl=ru

Игра называется Falcon Squad. Лучше потыкать самому, чтобы понять какой контроллер нужен.
Суть такая. В любой точке экрана можно провести пальцем и корабль повторит траекторию параллельно, начиная от своей позиции. Проблемы появляются на моменте, когда я хочу реализовать увеличение скорости/чувствительности корабля. Путь получается либо ломанный, либо не точный, либо с багами.
Ввод должен быть через Input.GetMousePosition и GetMouseButton, а движение через методы rigidbody2d.

1. Я брал вектор от места клика, до позиции корабля, затем прибавлял к вектору клика и получал тем самым смещенное положение игрока, которое передавал в MovePosition, тем самым корабль повторяет точные движения пальцем, но при таком вводе нет возможности увеличивать скорость. Конечно можно умножить полученный вектор на скорость, но при каждом клике корабль смещается на вектор кратный скорости, и только после этого летит быстрее.

2. Пробовал брать дельту позиции курсора, тем самым получая направление свайпа и передавая его rigidbody velocity, но тогда при умножении этого вектора на скорость, сам вектор увеличивается и путь получается ломанным. А если вектор нормализовать, тогда при обычном прикосновении к экрану, палец дрожит, получается нормализованный вектор и корабль улетает быстро в сторону, при большой заданной скорости.

3. Пробовал записывать точки свайпа в массив, и потом по этому пути следовать, но так и не получилось корректной работы.

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

Вот скрипт с первым способом:

Синтаксис:
Используется csharp
using System;
using UnityEngine;

public class Player : MonoBehaviour
{
        [SerializeField] private float speed;

        private Camera camera;
        private Vector2 touchPosition;
        private Vector2 offset;
        private Vector2 direction;
        private Rigidbody2D rb2d;
       
        private void Awake()
        {
                rb2d = GetComponent<Rigidbody2D>();
                camera = Camera.main;
               
                Input.multiTouchEnabled = false;

                direction = transform.position;
        }

        private void Update()
        {
                GetInput();
        }

        private void FixedUpdate()
        {
                rb2d.MovePosition(direction);
        }

        private void GetInput()
        {
                if (!Input.GetMouseButton(0)) return;
               
                touchPosition = camera.ScreenToWorldPoint(Input.mousePosition);
               
                if (Input.GetMouseButtonDown(0))
                {
                        offset = (Vector2)transform.position - touchPosition;
                }
               
                if (Input.GetMouseButton(0))
                {
                        direction = touchPosition * speed + offset;
                }
               
                if (Input.GetMouseButtonUp(0))
                {
                        direction = transform.position;
                }
        }
}
 

Тут очевидно, что не так, но зато с с таким ускорением игрок движется плавно и всё работает круто, кроме того что при клике он перемещается в сторону кратно скорости. Может решение простое, и я чего-то не догоняю. А может не всё так очевидно и надо хитрить.
Аватара пользователя
LoveUnity
UNец
 
Сообщения: 13
Зарегистрирован: 22 дек 2019, 07:03

Re: Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение LoveUnity 04 янв 2020, 06:58

Может мне подробнее/понятнее описать проблему? Помогите кто может, пожалуйста!
Аватара пользователя
LoveUnity
UNец
 
Сообщения: 13
Зарегистрирован: 22 дек 2019, 07:03

Re: Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение 1max1 04 янв 2020, 09:54

Синтаксис:
Используется csharp
class XXX : MonoBehaviour
{
    [SerializeField]
    float speedMove;

    Rigidbody2D rb2D;
    new Camera camera;
    Coroutine coroutine;
    Vector3 startPosition;
    Vector3 deltaPosition;
    Vector3 finishPosition;

    void Start()
    {
        camera = Camera.main;
        rb2D = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            startPosition = camera.ScreenToWorldPoint(Input.mousePosition);
        }
        else if (Input.GetMouseButtonUp(0))
        {
            deltaPosition = camera.ScreenToWorldPoint(Input.mousePosition) - startPosition;

            finishPosition = transform.position + deltaPosition;

            if (coroutine != null)
            {
                StopCoroutine(coroutine);
            }

            coroutine = StartCoroutine(c_Move());
        }
    }

    IEnumerator c_Move()
    {
        Vector3 oldDirection = finishPosition - transform.position;

        while (true)
        {
            Vector3 nowDirection = finishPosition - transform.position;

            if (Vector2.Angle(nowDirection, oldDirection) > 90.0f)
            {
                rb2D.velocity = Vector2.zero;

                coroutine = null;
               
                yield break;
            }

            rb2D.velocity = nowDirection.normalized * speedMove;

            yield return new WaitForFixedUpdate();
        }
    }
}


Второй вариант, попроще:

Синтаксис:
Используется csharp
class XXX : MonoBehaviour
{
    [SerializeField]
    float speedMove;

    Rigidbody2D rb2D;
    new Camera camera;

    Vector3 startPosition;
    Vector3 deltaPosition;
    Vector3 finishPosition;

    void Start()
    {
        camera = Camera.main;
        rb2D = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            startPosition = camera.ScreenToWorldPoint(Input.mousePosition);
        }
        else if (Input.GetMouseButtonUp(0))
        {
            deltaPosition = camera.ScreenToWorldPoint(Input.mousePosition) - startPosition;

            finishPosition = transform.position + deltaPosition;
        }
    }

    void FixedUpdate()
    {
        rb2D.MovePosition(Vector2.MoveTowards(rb2D.position, finishPosition, speedMove));
    }
}
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51

Re: Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение LoveUnity 06 янв 2020, 05:00

1max1 писал(а):
Синтаксис:
Используется csharp
class XXX : MonoBehaviour
{
    [SerializeField]
    float speedMove;

    Rigidbody2D rb2D;
    new Camera camera;
    Coroutine coroutine;
    Vector3 startPosition;
    Vector3 deltaPosition;
    Vector3 finishPosition;

    void Start()
    {
        camera = Camera.main;
        rb2D = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            startPosition = camera.ScreenToWorldPoint(Input.mousePosition);
        }
        else if (Input.GetMouseButtonUp(0))
        {
            deltaPosition = camera.ScreenToWorldPoint(Input.mousePosition) - startPosition;

            finishPosition = transform.position + deltaPosition;

            if (coroutine != null)
            {
                StopCoroutine(coroutine);
            }

            coroutine = StartCoroutine(c_Move());
        }
    }

    IEnumerator c_Move()
    {
        Vector3 oldDirection = finishPosition - transform.position;

        while (true)
        {
            Vector3 nowDirection = finishPosition - transform.position;

            if (Vector2.Angle(nowDirection, oldDirection) > 90.0f)
            {
                rb2D.velocity = Vector2.zero;

                coroutine = null;
               
                yield break;
            }

            rb2D.velocity = nowDirection.normalized * speedMove;

            yield return new WaitForFixedUpdate();
        }
    }
}


Второй вариант, попроще:

Синтаксис:
Используется csharp
class XXX : MonoBehaviour
{
    [SerializeField]
    float speedMove;

    Rigidbody2D rb2D;
    new Camera camera;

    Vector3 startPosition;
    Vector3 deltaPosition;
    Vector3 finishPosition;

    void Start()
    {
        camera = Camera.main;
        rb2D = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            startPosition = camera.ScreenToWorldPoint(Input.mousePosition);
        }
        else if (Input.GetMouseButtonUp(0))
        {
            deltaPosition = camera.ScreenToWorldPoint(Input.mousePosition) - startPosition;

            finishPosition = transform.position + deltaPosition;
        }
    }

    void FixedUpdate()
    {
        rb2D.MovePosition(Vector2.MoveTowards(rb2D.position, finishPosition, speedMove));
    }
}


Здравствуйте! Спасибо за ответ, но ваше решение, это совсем не то, что мне нужно. Чтобы понять какой контроллер нужен, можно протестировать мой код или вышеупомянутую игру. Потому что на словах объяснить достаточно не просто.

Нужное поведение следующее - корабль находится в любой точке экрана, игрок жмет на экран в любой точке и начинает рисовать пальцем траекторию, корабль мгновенно из своей позиции повторяет параллельно вводимую траекторию. Проблема с таким вводом возникает на том этапе, когда появляется задача увеличивать скорость, а точнее чувствительность. Это нужно пощупать, что бы понять.
У вас в первом варианте корабль просто повторяет длину и направление свайпа, после того как свайп закончится, а второй вариант тоже самое но с телепортацией, и проблема со скоростью тоже не решена, хотя со свайпами это легко сделать, но мне нужен не такой способ ввода.

Представьте движение, если бы мы просто transform.position задавали бы в точке касания, ведем пальцем и корабль движется под нашим пальцем. Это не весело, поэтому мы идем дальше и хотим нажать в любой точке экрана, начать движение и корабль будет двигаться от своей позиции повторяя наши движения параллельно. Но это требует от нас двигать быстрее пальцем по всему экрану, что бы корабль успел облетать врагов. Это тоже не весело. Поэтому мы хотим делать движения пальцем короче, а корабль бы повторял их с большим радиусом, большей скоростью. Понятно что уже прям точного повторения кораблем траектории не будет, но оно визуально слабо заметно, и движение осуществляется не рывками, по крайней мере в игре по ссылке из первого поста реализовано корректно. К тому же нам не нужно что бы при совсем коротком движении пальцем, корабль улетал бы сильно в сторону.
Всё, что я пробовал, было уже похоже на нужное движение, но корабль двигался рывками, ступенчато.

Попробуйте игру по ссылке. (это не реклама=) ) Просто так будет точно понятно.

Изображение
Картинка иллюстрирует как работает мой неправильный скрипт в данный момент. Корабль движется ускорено, но со смещением. Я думаю, может можно считать расстояния от следующего и предыдущего direction, и прикладывать к текущему трансформу, и уже в эту точку телепортировать через MovePosition, но сам скрипт у меня не получается придумать, там нужно делать таймеры, или корутины, записывать direction'ы в массив. А может всё проще, не знаю. Сейчас то touch position рассчитывается каждый апдейт, а direction передается в метод MovePosition каждый 0,02 секунды. Я пробовал через такие же промежутки времени считать вектора направления, но видимо что-то напорол в алгоритмах, у движение получалось слишком ступенчатое и с задержкой, потому что направления рассчитывались с низкой частотой.

Я думал может можно рассчитать расстояние от transform.position до direction, затем прибавить снова к direction и уже в эту точку двигать корабль, но это не работает, не уверен что это вообще корректно.
О господи, надеюсь хоть что-то понятно :(
Аватара пользователя
LoveUnity
UNец
 
Сообщения: 13
Зарегистрирован: 22 дек 2019, 07:03

Re: Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение LoveUnity 06 янв 2020, 07:49

Синтаксис:
Используется csharp
using System;
using System.Collections;
using UnityEngine;

public class Player : MonoBehaviour
{

        [SerializeField] private float speed;

        private Camera camera;
        private Vector2 touchPosition;
        private Vector2 offsetPos;
        private Vector2 offset;
        private Vector2 offset_2;
        private Vector2 direction;
        private Rigidbody2D rb2d;

        private void Awake()
        {
                rb2d = GetComponent<Rigidbody2D>();
                camera = Camera.main;
               
                Input.multiTouchEnabled = false;

                direction = transform.position;
        }

        private void Update()
        {
                GetInput();
        }

        private void FixedUpdate()
        {
                rb2d.MovePosition(direction);
        }

        private void GetInput()
        {
                if (!Input.GetMouseButton(0)) return;
               
                touchPosition = camera.ScreenToWorldPoint(Input.mousePosition);
               
                if (Input.GetMouseButtonDown(0))
                {
                        offset = (Vector2)transform.position - touchPosition;
                }
               
                if (Input.GetMouseButton(0))
                {
                        offsetPos = touchPosition * speed + offset;
                        offset_2 = offsetPos - (Vector2)transform.position;
                        direction = offsetPos + offset_2;
                }
               
                if (Input.GetMouseButtonUp(0))
                {
                        direction = transform.position;
                }
        }


}

 


Изображение

Думаю решение в таком подходе, только это слишком сильно багует на данном этапе. Сейчас голова не варит, попозже надо подумать.
Помогите кто может.
Аватара пользователя
LoveUnity
UNец
 
Сообщения: 13
Зарегистрирован: 22 дек 2019, 07:03

Re: Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение 1max1 06 янв 2020, 15:18

Игра не запустилась на моем калькуляторе(
Короче, будем гадать на кофейной гуще, вот сделал другой пример, вроде как по твоей схеме.
По поводу рывков... у тебя камера дочерняя к игроку? Попробуй поставить на твердом теле интерполяцию в инспекторе.
На интерфейс не смотри, это тестовый проект)



Синтаксис:
Используется csharp
class XXX : MonoBehaviour
{
    public enum MoveType
    {
        Lerp,
        Towards
    }

    [SerializeField] float range;
    [SerializeField] float moveIntensity;
    [SerializeField] MoveType moveType;
    [SerializeField] LineRenderer lr;

    Rigidbody2D rb2D;
    new Camera camera;

    Vector2 start0, start1;
    Vector2? finish;

    void Start()
    {
        camera = Camera.main;
        rb2D = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            start0 = worldMousePosition;
            start1 = rb2D.position;

            lr.SetPosition(0, start0);
            lr.SetPosition(1, start0);
        }
        else if (Input.GetMouseButton(0))
        {
            var wmp = worldMousePosition;
            var delta = (wmp - start0) * range;

            finish = start1 + delta;

            lr.SetPosition(1, wmp);
        }
        else if (Input.GetMouseButtonUp(0))
        {
            finish = null;

            lr.SetPosition(0, Vector3.zero);
            lr.SetPosition(1, Vector3.zero);
        }
    }

    void FixedUpdate()
    {
        if (finish.HasValue)
        {
            switch (moveType)
            {
                case MoveType.Lerp:
                    rb2D.MovePosition(Vector2.Lerp(rb2D.position, finish.Value, moveIntensity));
                    break;

                case MoveType.Towards:
                    rb2D.MovePosition(Vector2.MoveTowards(rb2D.position, finish.Value, moveIntensity));
                    break;
            }
        }
    }

    Vector2 worldMousePosition => camera.ScreenToWorldPoint(Input.mousePosition);
}
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51

Re: Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение LoveUnity 09 янв 2020, 06:25

Это именно то, что нужно! Гениально, мне просто не хватало навыков, что бы самому сообразить. Но к сожалению работает с багом, если потыкать чаще и быстрее в разных точках экрана, корабль телепортируется на широкие расстояния, не пойму в чём дело. А ещё есть второй баг, у меня статичная камера, а игрок не может вылетать за границы экрана, таким образом если корабль упирается в границу экрана и я продолжаю свайп не отпуская пальца, возникает проблема, когда я хочу вернуть корабль, направляя свайп от стены, он не сразу отлетает от границы, потому что видимо вектор продолжал увеличиваться, а стартовая точка не менялась. Получается такой эффект, будто корабль прилипает к границам экрана. Легко понять, если поместить корабль в периметр коллайдеров и попробовать подвигать его там. Но тут надо подумать, может при достижении границы, менять стартовую точку, но нет идей как реализовать, да и наврятли поможет
1max1 писал(а):По поводу рывков... у тебя камера дочерняя к игроку? Попробуй поставить на твердом теле интерполяцию в инспекторе.

Да нет, это было из-за моих кривых скриптов, точки пути были слишком далеко друг от друга, и движение получалось ломанным, а интерполяция на твёрдом теле не помогала.
Аватара пользователя
LoveUnity
UNец
 
Сообщения: 13
Зарегистрирован: 22 дек 2019, 07:03

Re: Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение 1max1 09 янв 2020, 11:50

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

Re: Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение LoveUnity 11 янв 2020, 13:12

1max1 писал(а):Про первый баг не знаю, нужно это видеть чтобы понять, но у меня всё нормально.

На телефоне особенно заметно, такое ощущение что он не обнуляет finish, хотя должен, пробовал разные варианты, не помогло. Надо быстро кликать через расстояние. Может сниму видео позже.
1max1 писал(а):Про второй баг, да, такое надо исправлять смещением стартовой точки.

Делать её от самого края экрана я так понимаю. Получается не всё так очевидно конечно, я думал такой способ ввода не потребует таких заморочек, эх. Возможно есть и другие варианты реализации. Ну ладно, попробую.
Аватара пользователя
LoveUnity
UNец
 
Сообщения: 13
Зарегистрирован: 22 дек 2019, 07:03

Re: Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение 1max1 11 янв 2020, 14:18

Делать её от самого края экрана я так понимаю. Получается не всё так очевидно конечно, я думал такой способ ввода не потребует таких заморочек, эх. Возможно есть и другие варианты реализации. Ну ладно, попробую.

Ну там скорей всего будет что-то типа:
Синтаксис:
Используется csharp
// void OnCillisionStay2D
start0 = worldMousePosition;

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

Re: Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение LoveUnity 11 янв 2020, 15:36

1max1 писал(а):Ну там скорей всего будет что-то типа:
Синтаксис:
Используется csharp
// void OnCillisionStay2D
start0 = worldMousePosition;


У меня коллайдерами, он тогда при касании коллайдера возвращается на начальную позицию.
Аватара пользователя
LoveUnity
UNец
 
Сообщения: 13
Зарегистрирован: 22 дек 2019, 07:03

Re: Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение 1max1 11 янв 2020, 15:41

А так?
Синтаксис:
Используется csharp
// void OnCillisionStay2D
start0 = worldMousePosition;
start1 = rb2D.position;
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51

Re: Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение LoveUnity 16 янв 2020, 02:48

1max1 писал(а):А так?
Синтаксис:
Используется csharp
// void OnCillisionStay2D
start0 = worldMousePosition;
start1 = rb2D.position;

Да работает, но это всё равно не совсем корректно всё же. В идеале новая стартовая точка должна быть на самом краю экрана в месте соприкосновения. Надо бы подумать как это сделать без багов
Аватара пользователя
LoveUnity
UNец
 
Сообщения: 13
Зарегистрирован: 22 дек 2019, 07:03

Re: Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение LoveUnity 24 янв 2020, 14:00

В общем я нашёл решение, через методы Touch класса, хотя и не хотел через него делать изначально, а зря. Это самый простой и верный вариант, на мой взгляд.
Синтаксис:
Используется csharp
        private Rigidbody2D rb2d;
        public float speedModifier;
        private Touch touch;
        private Vector2 offset;

        private void Awake()
        {
                rb2d = GetComponent<Rigidbody2D>();
        }

        private void Update()
        {
                if (Input.touchCount > 0)
                {
                        touch = Input.GetTouch(0);

                        if (touch.phase == TouchPhase.Moved)
                        {
                                offset = new Vector2(
                                        transform.position.x +  touch.deltaPosition.x * speedModifier,
                                        transform.position.y +  touch.deltaPosition.y * speedModifier);
                        }
                }
        }

        private void FixedUpdate()
        {
                rb2d.MovePosition(offset);
        }
 
Аватара пользователя
LoveUnity
UNец
 
Сообщения: 13
Зарегистрирован: 22 дек 2019, 07:03

Re: Swipe track controller с увеличением скорости для СпэйсШутер

Сообщение 1max1 24 янв 2020, 14:02

хотя и не хотел через него делать изначально

А как еще ты для телефонов собирался делать?)) Там же мышку не подрубишь :)
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51

След.

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

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

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