И вот моя первая попытка выбраться на просторы Хабрахабр.
Недавно в свет вышла моя игра Evasion. Простая аркада, в которой нужно уклоняться от препятствия. Игра сама по себе легка в разработке, но в каждой простоте есть свои подводные камни. О парочке из них я и хочу поговорить в данной статье.
Красный шар движется в точку касания пальца.
Редактор уровней
Уровни в Evasion представлены в виде матрицы спрайтов, которые падают с определенной скоростью. Когда нижний ряд опускается ниже допустимых координат, он перемещается на самый верх. В итоге получается подобие конвейерной ленты. В момент перемещения меняются препятствия, соответственно и изображение на спрайтах. У каждого вида препятствия есть свой id. Информация об уровне хранится в двумерном массиве.
Таким способом меняются препятствия:
public static bool Next_M(int Series) // Получаем номер ряда
{
if (Set < 0)
Set = 0;
if (Set < mLvl) // Если уровень не закончился
{
// xR - количество столбцов в уровне
for (int i = 0; i < xR; i++)
{
M_Load(Series, i, Level[i, Set]); // Меняем препятствия на те, которые содержатся в массиве Level
}
Set++;
}
else // Иначе финиш
{
for (int i = 0; i < xR; i++)
{
M_Load(Series, i, 0);
}
if (Finish)
{
Finish = false;
return true;
}
}
return false;
}
Поначалу, чтобы создавать уровни, приходилось вводить стринговое выражение и его уже конвертировать в целочисленное значение.
Base[0] = "040900080205080202000204050000001500060001001515141314140706000705110704040004020406"; // Первый уровень во всей красе
Но быстро понял, что так дела не делаются. Процесс работы был медленным, муторным, а исправление ошибок каралось углубленным поиском иголки в стоге сена. Итогом стало решение создание быстрого и удобного редактора уровня.
Центральная лента опускается слайдами.
Редактор упростил разработку в несколько раз. Можно быстро построить уровень и там же его протестировать. И если в вашей игре есть возможность облегчить жизнь, создав редактор уровней, не поленитесь потратить на него своё время, оно того стоит. К тому же это можно преподнести, как дополнительную опцию в игре (если ваш редактор удобен в использовании обычным смертным).
Хоть редактор в Evasion присутствует, он только для ознакомления. Хотя и можно сделать сохранение уровней, возможность делится с друзьями и так далее. Но это уже совсем другая история…
Бесконечный режим
Не имея больших познаний в математике, рандоманая генерация уровня стала для меня непостижимой задачей. Единственным выходом было создание небольших блоков длиной в 3 – 5 спрайтов и их повторение. Скучная и нудная реализация, но другого выбора не было. Пока однажды, сидя в БФУ на паре математике, в голову не пришло одно единственное слово: множества!
Все препятствия можно разделить на множества:
- Все виды препятствий
- Все препятствия сверху (Подмножество первого массива)
- Все препятствия снизу
- Слева
- Справа
Те виды, у которых несколько препятствий попадают в несколько массивов сразу. Случайным образом узнаем, где вход, где выход и дистанцию между ними. Всё происходит в пределах одного ряда. В зависимости от дистанции узнаем, где и в каком месте исключить появление непроходимого места.
public static void Next_M(int S)
{
int Di = Math.Abs(In - Ou); // Узнаем дистанцию между входом и выходом
if (Di == 0) // В зависимости от дистанции генерируем препятствие
{
List<int> Cut = Draw(0, 1); // 0 - исключаем препятствие снизу, 1 - сверху
M_Load(S, In, Cut[StaticVolue.Rnd(0, Cut.Count)]);
}
else
{
List<int> Cut = new List<int>();
if (In > Ou)
{
Cut = Draw(0, 2);
M_Load(S, In, Cut[StaticVolue.Rnd(0, Cut.Count)]);
for (int C = In - 1; C >= Ou; C--)
{
if (C == Ou)
{
Cut = Draw(1, 3);
M_Load(S, C, Cut[StaticVolue.Rnd(0, Cut.Count)]);
}
else
{
Cut = Draw(2, 3);
M_Load(S, C, Cut[StaticVolue.Rnd(0, Cut.Count)]);
}
}
}
else
{
Cut = Draw(0, 3);
M_Load(S, In, Cut[StaticVolue.Rnd(0, Cut.Count)]);
for (int C = In + 1; C <= Ou; C++)
{
if (C == Ou)
{
Cut = Draw(1, 2);
M_Load(S, C, Cut[StaticVolue.Rnd(0, Cut.Count)]);
}
else
{
Cut = Draw(2, 3);
M_Load(S, C, Cut[StaticVolue.Rnd(0, Cut.Count)]);
}
}
}
}
In = Ou;
Ou = StaticVolue.Rnd(0, Matrix_Control.xR);
}
static List<int> Draw(int A)
{
List<int> CellSet = new List<int>();
bool ice;
for (int y = 0; y < AllCell.Length; y++)
{
ice = true;
for (int i = 0; i < 9; i++)
{
if (AllCell[y] == EnterCell[A, i]) // Из всех видов исключаем те препятствия, которые содержатся в массиве A
{
ice = false;
}
}
if (ice)
CellSet.Add(AllCell[y]);
}
return CellSet;
}
Вот такая реализация рандомного уровня получилась. Если у вас есть идеи, как это можно было бы сделать лучше, пишите в комментариях.
Музыкальное сопровождение
Хотелось сказать пару слов о музыке в игре.
Что, если ты студент и у тебя нету денег на хорошую музыку, а бесплатные варианты не устраивают? Тут есть два выхода: либо самому написать или же иметь хороших друзей, желательно со своей группой. Так уж получилось, что писать музыку самостоятельно я не умею, а вот с друзьями мне повезло.
Музыку для моей игры любезно предоставила наша местная Калининградская группа «Houston, we've got problems!» Для тех, кто любит post-rock, рекомендую ознакомиться с их творчеством.
На этом всё. Удачи всем в проектах.