Найти тему
ElandGames

Основы языка C# для создания игр #4: Цикл с условием, генератор случайных чисел и округление

Оглавление

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

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

Генератор случайных чисел в C# и Unity

В языке C# есть специальная функция для генерации случайных чисел из заданного диапазона Random.Range(). Случайный урон можно легко получить вписав в эту функцию минимальное и максимальное значение урона через запятую.

Метод Attack() вычисляет случайный урон и сохраняет его в переменную damage
Метод Attack() вычисляет случайный урон и сохраняет его в переменную damage

Но у этой функции есть интересная особенность. Когда функция Random.Range работает с целыми числами типа int, верхнюю границу она не включает в диапазон возможных значений. А вот если будет работать с дробными числами, то включает.

-2

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

-3

Математическая функция: Округление

Частенько нам, при работе с дробными числами, может понадобиться функция округления. Например, мы можем не прибавлять единицу, а просто пользоваться дробными значениями, которые потом будем округлять до целых.

Функции можно записывать в качестве аргументов внутри других функций. Значение полученное из первой функции автоматически применится в качестве аргумента для второй функции. Главное, чтобы типы данных этих значений подходили.
Функции можно записывать в качестве аргументов внутри других функций. Значение полученное из первой функции автоматически применится в качестве аргумента для второй функции. Главное, чтобы типы данных этих значений подходили.

Когда мы в первой статье говорили о типах данных, то использовали приведение типа float к типу int. Его тоже можно использовать для округления, но тогда мы максимального значения опять не получим. Приведения числа типа float к числу типа int просто отбрасывает дробную часть.

-5

Есть ещё функция округления Mathf.Round(), которая на выходе сохраняет тип данных, поэтому с дробными числами её использовать не всегда удобно - придётся самостоятельно делать приведение к int.

-6

Но, что если, мы захотим сделать урон в виде дробных значений, а нам не нужна такая точность в 6 знаков? Есть в библиотеке System функция округления до определенного порядка Math.Round(), но она работает со значениями типа double. Придётся приводить float к double (это произойдёт автоматически), а потом обратно во float.

Выглядит довольно мудрёно!
Выглядит довольно мудрёно!

Я предпочёл бы, в данном случае, умножить на 100, округлить и просто поделить на 100. Результат такой же. Выглядит проще. :)

Тип данных double, который является более точным аналогом типа float и занимает больше места в памяти, мы как правило и не используем никогда.

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

Цикл с условием while

Мы уже рассмотрели условный оператор if и цикл for. В цикле for можно зациклить проверку какого-либо условия на любое желаемое количество раз и по сути тоже получится "цикл с условием". Но, что если мы хотим непременно дождаться выполнения условия, сколько бы повторений цикла нам не потребовалось? Или вообще хотим, чтобы цикл выполнялся бесконечно. Тут то нам и пригодится цикл while ().

В качестве аргумента в операторе цикла while указывается условное выражение или переменная типа bool. While переводится как "до тех пор", "пока". Соответственно, пока аргумент цикла while имеет значение true, будет выполняться тело цикла бесконечное количество раз, каждый раз проверяя - не стал ли аргумент false.

Пока здоровье игрока больше нуля, будет бесконечно выполняться тело цикла.
Пока здоровье игрока больше нуля, будет бесконечно выполняться тело цикла.

Главная проблема и опасность цикла while кроется в бесконечности выполнения. Если не предусмотреть, чтобы условие цикла рано или поздно стало false, то мы получим бесконечный цикл, который приведёт к зависанию программы. Поскольку этот цикл никогда не закончится, то программа никогда не перейдёт к следующему шагу и зависнет на текущем шаге.

Вот так уже ничего не зависнет - через 100 повторений цикла, здоровье станет равным нулю, условие станет равным false, что приведёт к выходу из цикла
Вот так уже ничего не зависнет - через 100 повторений цикла, здоровье станет равным нулю, условие станет равным false, что приведёт к выходу из цикла

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

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

Цикл с условием в обычном методе нужно использовать аккуратно и всегда предусматривать условие выхода из него. Это можно сделать, например, с помощью оператора break.

Программа будет спаунить врагов столько раз, сколько задаст пользователь. Но если количество врагов достигнет лимита, то оператор break прервёт цикл.
Программа будет спаунить врагов столько раз, сколько задаст пользователь. Но если количество врагов достигнет лимита, то оператор break прервёт цикл.

Есть ещё полезный оператор continue, который прерывает не весь цикл, а только данную его итерацию, но он применяется довольно редко.

Как правило цикл while применяется в асинхронных циклах, либо в корутинах. В большинстве случаев хватает и циклов for / foreach. Но знать про него и про его подводные камни, при использовании в обычных методах, надо обязательно - это база! :)

А вот тут вся серия статей по базе C# для Unity:

Основы языка С# для создания игр на Unity | Сергей Эланд | Дзен