У нас в арсенале уже есть основные типы данных, классы, методы, условия, списки и циклы. Из этого можно слепить неплохой функционал для простой игры. Однако, чем сложнее будет становиться игра, тем более изящные решения потребуются, чтобы избежать повторений в коде и сохранить его читаемость. Поэтому нам стоит познакомиться с такими штуками как перечисления, оператор switch и структуры.
Перечисления в языке C#
Перечисления - это уже более сложный тип данных, представляющий из себя список констант и их имён. Благодаря именам этих констант, мы можем сделать код намного более наглядным и читаемым.
Константы, в отличии от переменных, получают свое значение при объявлении и в дальнейшем оно не меняется. В коде если к полю (переменной) добавить const, то переменная станет константой и меняться уже не будет.
В перечислении же ничего указывать не надо - там одни константы. Самый простой пример это перечисление дней недели.
Перечисление объявляется с помощью слова enum, вне класса - отдельно. Внутри через запятую записываются имена констант, а рядом с ними через знак равенства им присваивается значение. Но значение можно и не указывать. По умолчанию первое значение будет 0, а каждое последующее увеличивается на 1. Если же мы явным образом зададим первое значение равным 1, то остальные по умолчанию также будут увеличиваться на 1 и нумерация получится с 1 до 7.
Мы можем задать любые целочисленные значения. Это могут быть коды, удобные нам числа или просто нумерация, но главное, что нам не придётся её запоминать - работать в коде мы будем с именами констант перечисления.
Константы перечислений можно использовать как и любые целочисленными переменные - сравнивать и производить математические действия, например сложение.
Давайте придумаем какой-то пример для создания игры. Например, создадим перечисление с расами игровых персонажей Race. Информация о каждом игровом персонаже будет храниться в отдельном экземпляре класса Unit.
Сделаем метод CreateUnit(), который будет создавать нового юнита и в зависимости от его расы изменять его максимальное количество здоровья. Новый экземпляр класса создаётся с помощью оператора new и скобок () после имени класса.
В качестве входного параметра мы получаем переменную типа Race и присваиваем её в качестве значения для расы нового юнита. Затем, в зависимости от его расы, присваиваем ему нужное значение здоровья.
Всё довольно наглядно и легко читается. Экземпляр класса потом мы сможем сохранить в какой-нибудь список юнитов.
Когда в нашей программе появляются большие ветвления условий, как например тут с разновидностями рас, то эффективнее будет использовать условный оператор switch ().
Оператор сопоставления значений switch
Когда нам нужно сравнивать значения, то вместо конструкции if else будет лучше использовать конструкцию switch. Это и выглядит нагляднее (хотя на мой взгляд это спорно), и работает более оптимизированно.
В скобках оператора switch указываем то, с чем будем сравнивать. Для каждого варианта создаём строчку case со значением. После него пишем код, который нужно выполнить в этом случае. А после него break, который разграничивает код для разных вариантов. В конце можно также добавить вариант default, который будет срабатывать если ни одно из описанных нами значений не совпало (по аналогии с else).
Перечисления enum и оператор switch частенько используют вместе и в случае простых действий все это выглядит нормально. Но, что если нам нужно, при создании юнита, задать ему много разных параметров? Оператор switch разрастётся до огромных масштабов, если после каждого case указывать несколько строчек присвоения значения.
Как одно из решений, можно использовать в этом случае структуру.
Структуры
Структуры struct - это очень интересная штука и может содержать в себе переменные и методы - прямо как класс, но при этом является значимым типом. Это значит, что она копируется, а не передается ссылкой.
Мы можем использовать её для хранения значений множества параметров наших юнитов.
В структурах можно прописать специальный метод (конструктор), с помощью которого будет удобно создавать новые экземпляры с нужными значениями.
Используя этот конструктор, мы можем создать структуры с настройками для каждого класса. Например, в методе Start(). Для новых экземпляров структуры нужно объявить поля, чтобы потом иметь возможность к ним обращаться, ну или иными словами, чтобы их где-то хранить.
Создав экземпляры структуры с настройками для разных классов, мы можем использовать её при создании юнитов и задания им параметров. Для этого в классе юнита объявим поле unitSettings.
Метод создания юнитов по объёму не увеличится, а параметров мы зададим в 4 раза больше.
Теперь мы можем обратится к каждому параметру юнита через структуру, либо разом задать ему одно из значений всей структуры, как мы это сделали выше.
Например, при повышении уровня юнита, мы можем разом задать новые значения всех параметров, подготовив экземпляр с нужными значениями или даже создать внутри структуры метод, который будет по нужной нам формуле высчитывать, как увеличатся параметры. Либо как вариант можно создать метод не в структуре, а в том же классе, где мы создаём юнитов.
Возможно это не самый удачный пример для большого количества параметров, т.к. если их будет под сотню, то конструктор такой структуры будет слишком громоздким, но для небольших пачек переменных этот способ будет вполне неплохим решением. Например, можно сделать в виде структуры цену, которая состоит из 3 разных видов ресурсов (золото, камень и дерево).
Вот только не очень хорошо, что параметры юнитов мы задаём в коде "магическими числами".
Частая ошибка: Магические числа
"Магические числа" - это термин, означающий явно указанные числовые значения в коде. Непонятно откуда взялись и что вообще значат. Они затрудняют чтение и понимание кода. По-хорошему, все значения должны хранится в переменных с говорящими названиями. Например, humanHealth. И тогда инициализация структуры будет выглядеть более читаемо.
В некоторых случаях магические числа можно вынести в отдельную переменную, но когда их много, то делают специальные конфиги, чтобы считывать значения оттуда. До этого мы еще дойдём, а пока стоит научиться не вставлять в свой код числа непонятного происхождения, как бы быстро и удобно это не казалось. Потому-что вернувшись к своему коду спустя месяц есть очень высокая вероятность ничего не понять и долго ломать голову над тем, зачем вообще здесь эти цифры. :)
С каждой статьёй темы всё сложнее и их все сложнее объяснять простыми словами. :) Но все эти штуки реально встречаются и их использование нехило повышает качество нашего кода, поэтому стоит разобраться! :)