Опыт разработки первой игры на Unity, часть 2
Часть вторая — в которой я сдаюсь и опускаю руки
Ссылка на часть 1
Первая часть была написана 24 ноября, прошло 10 дней…
Оказалось, что вытянуть нужные мне данные — крайне сложная задача. Я пересмотрел кучу роликов, прочитал кучу информации, но все бесполезно. Несколько дней экспериментов — каждый раз одно и то же. Ошибка, ошибка, сплошные ошибки… Им не было конца
Заключение
Итак, я сдался. Вся затея пошла прахом. Постоянные ошибки сломили меня, а полное непонимание того, что делаю — добило окончательно
Так, а это что за текст?
Ахах, сдался — как же! Просто скипнул эту часть и перешел к следующему этапу. Благо, пока что это можно сделать относительно безболезненно. К загрузке данных вернусь как-нибудь потом. Итак, поехали дальше!
Ремарка
Фидбек от первой статьи понятен — нужно больше технической части) В этой по некоторым причинам уже поздно что-то менять, но в следующей обязательно что-нибудь придумаю! Благо из-за кое-какой ситуации там будет сильно больше материала. А так всем спасибо за комменты!
О возникших проблемах
Главная проблема — я вообще не знаю, как работать с таблицей, которая находится где-то там в гугле. Вторая — мой навык проггера. Он крайне низок — что-то около джуна (джун минус минус, ориентировочно). И третья — формат таблицы (о нем чуть ниже)
Нашел такое решение. И оно работает!
Смог адаптировать под моих героев и записать в них данные из таблицы! Проблема пришла внезапно. На первом листе у меня находятся, по сути, ссылки на айдишники из других листов. А cvs, насколько понял, не позволяет работать с другими листами! Штош, ищем другие варианты.
Поиски привели к https://docs.google.com/spreadsheets/d/*/expor? format=cs? edit#gid=0
, где * это id таблицы, а после gid айди листа.
Ура — unity возвращает адрес нужного листа! Но данные все равно берутся с первого листа, агрррх! Я отчаялся. Почти потерял надежду. И тут, неожиданно, нахожу его: https://docs.google.com/spreadsheets/d/{key}/gviz/t? tqx=out:csv&sheet={sheet_name}
. Ура, работает! Берет данные именно с нужного листа. Но вылезли баги — все id обрамлены теперь в » ». Решилось просто — указал, что нужно убирать символ ».
Следующая проблема — как сделать так, чтобы брались характеристики из другого листа в зависимости от того, какой id указан у героя. После долгих часов мучений нашел корень проблемы — в найденном решении при вытаскивании таблиц тут же присваиваются найденные значение. Значит, нужно переписывать — разделять методы так, чтобы появилась нужная гибкость. Отныне и навсегда — отдельно вытягиваем таблицы, отдельно проходимся по ним, ища то, что нужно, отдельно записываем герою нужные характеристики.
Итог вы знаете — пока так и не получилось
Следующий шаг
Удаляем все эксперименты — возвращаемся к самому началу до экспериментов с таблицами.
Теперь по плану мне нужно модифицировать боевую систему — использовать подтянутые из таблицы характеристики героев и противников, и научить героев использовать активируемый скилл. Противников такому научу чуть позже. И вот что получилось
Выдал героям и противникам фейковые характеристики, добавил кнопки для использования скилла. Если мана накопится, то по нажатию герой делает БУМ!
Модернизировал выбор цели героем — теперь есть не только сингл-таргет скилл, но и бьющий несколько противников
Изначальный код по поиску ближайшего объекта взят с Unity
public GameObject FindClosestTarget(GameObject[] objectsArray)
{
//Debug.Log("target");
_debugTarget = null;
GameObject[] gos;
gos = objectsArray;
GameObject closest = null;
float distance = Mathf.Infinity;
Vector3 position = transform.position;
foreach (GameObject go in gos)
{
if (go != null && go.activeSelf)
{
Vector3 diff = go.transform.position - position;
float curDistance = diff.sqrMagnitude;
if (curDistance < distance)
{
closest = go;
distance = curDistance;
}
}
}
return closest;
}
А вот такой код появился для поиска всех объектов, которые находятся на дистанции атаки. Просто взял готовое решение по поиску и переделал чутка
public GameObject FindAllClosestTargets(int index)
{
//Debug.Log("target");
GameObject closest = null;
float distance = GetComponent().Attack_Range;
Vector3 position = transform.position;
if (_enemys[index] != null && _enemys[index].activeSelf)
{
Vector3 diff = _enemys[index].transform.position - position;
float curDistance = diff.sqrMagnitude;
//Debug.Log("curDistance = " + curDistance);
//Debug.Log("distance = " + distance);
if (curDistance <= distance)
{
closest = _enemys[index];
//distance = curDistance;
}
}
return closest;
}
Вызов выглядит так
for (int i = 0; i < _targetAnalyse.Enemys.Length; i++)
{
Attack(_targetAnalyse.FindAllClosestTargets(i), dmg); // dmg принимает мультипликатор урона
}
И все — теперь герои умеют в AoE (атака по области)
Мда, с английским я не слишком заморачивался)
Еще и здоровье теперь не просто отнимается, а делает это красиво и постепенно. Циферки это здоровье — они дублируют полоску здоровья с кнопки скилла. Потом от них избавлюсь
Что вообще происходит на видео
Герой с 1000 здоровья это герой 1, его кнопка Skill Hero 1. По нажатию, если мана заполнилась, он бьет одного ближайшего противника
Герой с 2000 здоровья, соответственно, герой 2. По нажатию на его кнопку, он бьет всех противников, которые находятся на дистанции атаки
Естественно, количество кнопок зависит от количества героев — и у каждого свой скилл
Возникшие проблемы
Первая — архитектура. Уже начинается непонятно что — что-то откуда-то вызывает, что-то при этом происходит. В итоге сделал в confluence кучу страниц с названиями объектов и префабов. В них — страницы со скриптами. И в этих страницах описание того, что они делают со ссылками на те скрипты, которые вызывают. Надеюсь, это немного поможет упорядочить то, что у меня там происходит.
Вторая тоже относится к архитектуре, но немного иначе. Например, изначально был скрипт Fight, в котором есть метод Attack (). В начале все было хорошо —, а сейчас понадобилось использовать умения. Если все оставить как есть, то Attack () будет вызывать UseSkill (), но это скилл должен вызывать атаку! Поэтому пришлось немного переделывать такие моменты. Теперь Fight () только определяет само состояние атаки и ее возможность, а UseSkill () используется в другом месте
Третья — и сейчас самая важная проблема — я понятия не имею как описывать скиллы в коде с учетом предполагаемых данных, которые буду подтягивать с таблицы. Пока стоит заглушка, но нужно будет очень хорошо подумать, как все организовать. Но это по плану будет позже
Немного важностей
Важно делать что-то, что будет выдавать хоть какой-то видимый результат. Например, вот такой код у меня отвечает за то, что циферки красиво уменьшаются при получении урона
private IEnumerator UpdateCurHealth()
{
while (_prevHealth > _curHealth)
{
yield return new WaitForSeconds(.1f);
_prevHealth--;
_healthCount.text = _prevHealth.ToString();
if (_prevHealth <= _curHealth)
{
_prevHealth = _curHealth;
StopCoroutine(UpdateCurHealth());
}
}
}
Маленькая и простая штука, которая позволяет тут же увидеть результат работы. Позволяет сохранить оптимизм, т.к наглядно показывает, что есть прогресс
Следующий этап
А дальше у меня прокачка! Научу героев получать опыт и повышать уровень