Unity3Dschool

обучение разработке игр

Варианты перемещения группы юнитов в одну локацию

Варианты перемещения группы юнитов в одну локацию

Суть проблемы

При разработке игры типа  RTS, когда мы имеем дело с большим количеством юнитов, которые нужно отправить в одну локацию/точку, может возникнуть проблема, так как все юниты не могут занимать одну и ту же точку. К примеру, есть настройка, с помощью которой можно выбрать несколько единиц и задать порядок действий для всех одновременно. Мы выбираем точку, и даем нашим юнитам команду двигаться к ней,  используя проект A * Pathfinding. И при попытке «попасть в пункт назначения», мы обнаружим, что ВСЕ юниты не смогут попасть в указанную точку. Какие варианты решения проблемы?

Поиски решений от разных разработчиков

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

Но есть ли более «правильные» варианты решения подобной задачи?

При попытке решения данной задачи многие упоминают о возможности использования рулевого управления (Steering behaviors). А именно – с помощью  Flee и Arrival, а также  «Follow Leader, Pursuit and Evade» и т. д. Цель последнего  – заставить юнитов двигаться реалистично, используя локальную информацию о силах противника.  Либо говорят о том, что управление большим количеством юнитов также можно реализовать через управление типом поля потока. Хотя этот метод - это просто альтернативный способ создания пути, который лучше работает для более крупных групп единиц. Но вам все равно придется разрабатывать решение для формации вокруг пунктов назначения.

Вариант обхода

Часть разработчиков предлагают решать проблему – «не создавая ее», попросту - не отправлять юниты в одну и ту же точку. То есть, нажимая на пункт назначения, вы отправляете первого юнита в эту точку, а затем смещаете остальные пункты назначения.  Это можно сделать несколькими способами, например - компенсировать их расстояниями относительно их начальных позиций и радиуса. Более сложные решения - создавать формации вокруг щелкнутого местоположения. Пример такой реализации можно увидеть в игре Rise of Nations, где после нажатия на пункт назначения вы можете удерживать нажатой кнопку мыши и перемещать курсор, при этом появляются маленькие значки круга, куда будут перемещаться остальные юниты.

Но рассмотрев наиболее популярные решения разработчиков, мы нашли такое, которое можно считать практически «идеальным».

Идеальное решение!

Когда объект/юнит попадает в место назначения (или рядом с ним), установите для некоторого типа логическое значение значение true, чтобы подтвердить, что он достиг точки назначения. Остановите движение юнита и заморозьте его. Когда другой юнит, перемещающийся в тот же пункт назначения, сталкивается с этим остановленным юнитом, также остановите его и установите для него то же логического значения значение true, чтобы определить, что он уже прибыл в пункт назначения. Когда у вас целая группа единиц ютов, то первый юнит, который достиг точки назначения, вызовет цепную реакцию, останавливающую  всех остальных юнитов.

Рассмотрим этот пример:
  • using UnityEngine;
  • using System.Collections;
  • public class GameManager : MonoBehaviour {
  • public bool TargetReached = false;
  • Vector3 destination;
  • float StoppingDistance;
  • void Update()
  • {
  • StoppingDistance = collider.bounds.extents.z;
  • if (Vector3.distance (transform.position, destination) < StoppingDistance)
  •    {
  •        TargetReached = true;
  •    }
  • if (!TargetReached)
  •    {
  •        //MoveToTarget
  •    }
  • else{
  •        rigidBody.isKinematic = true;
  •    }
  • }
  • void OnCollisionStay (Collision col)
  • {
  •    if (!TargetReached)
  •    if (col.gameObject.GetComponent<RTS_Unit>().TargetReached == true)
  •        TargetReached = true;
  • }
  • }
 

С помощью этого метода, Вам нет нужды в использовании сложного поведения поля потока. Но например, если отправить группы юнитов навстречу друг другу, возникнет другая проблема – они будут «ждать вечно». Чтобы решить ее, вы можете создать переменную с названием «StoppingDistance», которая увеличивается в зависимости от группы команд, в которой находится юнит, задайте ей определенное значение (т. е. Один элемент будет перемещаться почти точно в направлении цели, в то время как главное подразделение остановится на расстоянии к примеру 3 единиц от точки.)

Поделитесь с нами, сталкивались ли Вы с подобными задачей и какими были Ваши методы для ее решения?