Используйте экземпляр или класс для игровых ресурсов (дерево, железо, золото)

31

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

Теперь мне было интересно, как ресурсы должны создаваться в игре. Я придумал 2 варианта

  1. Создайте класс для каждого ресурса:

    public class ResourceBase {
        private int value;
        // other base properties
    }
    
    public class Gold : ResourceBase {
         public Gold {
             this.value = 40 // or whatever
         }
    }
    
  2. Создать экземпляры класса Resource

    public class Resource {
        string name;
        int value;
    
        public Resource(string name, int value) {
            this.name = name;
            this.value = value;
         }
     }
    
    // later on...
    
    Resource gold = new Resource("Gold",40);

Со вторым вариантом можно заполнить игровые ресурсы из файла resources.json, мне нравится эта структура.

Новые идеи / шаблоны дизайна / структуры всегда приветствуются!

РЕДАКТИРОВАТЬ: Это немного похоже на приложение-компаньон Assassins Creed Black Flag. Смотрите панель ресурсов на изображении ниже

РЕДАКТИРОВАТЬ 2: Я провел еще несколько исследований "загрузки элементов / ресурсов из файла JSON" и нашел этот блог: Сила JSON в разработке игр - Предметы . Он показывает лучшее из варианта 1 и варианта 2. Вы все еще можете добавить функциональность для каждого ресурса :)

введите описание изображения здесь

MDijkstra
источник
1
Minecraft имеет отдельные классы для каждого ресурса (не то, что вы должны)
MCMastery
@MCMastery О, майнкрафт с открытым исходным кодом? Я хотел бы взглянуть на эту структуру. И спасибо за упоминание
MDijkstra
1
Это не так , но она постоянно подвергается де-затемненный для серверов ... см github.com/Wolf-in-a-bukkit/Craftbukkit/tree/master/src/main/...
MCMastery
12
Не используйте такую ​​строку. Слишком просто ошибочно набрать «золото» с «глод» или подобным, и отлаживать это очень сложно. Используйте enumвместо.
Нолонар
Minecraft не является технически открытым исходным кодом, но он почти полностью декомпилирован и деобфусцирован. Вы не должны быть в состоянии найти деобфусцированный источник где-либо в Интернете, но вы можете загрузить и запустить процесс, чтобы сделать это из файла files.minecraftforge.net (вам нужна загрузка MDK)
JamEngulfer

Ответы:

51

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


Редактировать:

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

Давайте возьмем пример консольной игры, упомянутый в комментариях, потому что это дает очень вескую причину того, почему вы не должны жестко кодировать что-либо, если вы действительно не имеете (или не хотите), например, ключи шифрования, ваше собственное имя и т.д.

При выпуске игры на консолях, они обычно должны проходить процесс обзора, при котором собственный QA производителя консоли будет тестировать игру, играть в нее, искать проблемы и т. Д. Это обязательно и стоит денег, больших денег. Я думаю, что однажды прочитал, что выпуск может стоить 30 000-50 000 долларов только за сертификацию.

Теперь представьте, что вы запускаете игру, платите 30 000 долларов и ждете. И вдруг вы замечаете, что некоторые из ваших игровых ценностей буквально нарушены. Допустим, вы можете купить железные слитки за 50 золотых и продать их за 55 золотых.

Чем ты занимаешься? Если вы жестко запрограммировали этот материал, вам придется создать новую версию обновления / выпуска, которая должна будет еще раз пройти проверку, так что в худшем случае вы снова заплатите за повторную сертификацию! Бьюсь об заклад, Ubisoft не будет возражать, но для вашего собственного маленького кармана разработчиков инди-игр ... ой!

Но представьте себе, что игра просто время от времени проверяет наличие обновленных определений игры (например, в форме файла JSON). Ваша игра может загружать и использовать последнюю версию в любое время, не требуя обновления. Вы можете исправить этот дисбаланс / эксплойт / ошибку в любое время, не требуя повторной сертификации или других уплаченных денег. Просто какое-то незначительное дизайнерское решение, вы не думали, что оно того стоило, просто сэкономили вам 5-значную сумму денег! Разве это не круто? :)

Только не пойми меня неправильно. Это также относится к другим типам программного обеспечения и платформ. Загрузка обновленных файлов данных в целом намного проще и выполнима для обычного пользователя, независимо от того, игра это или какое-то приложение / инструмент. Представьте себе игру, установленную в Program Files в Windows. Для обновления вам нужны права администратора, и вы не можете изменять запущенные программы. С DDD ваша программа просто загружает данные и использует их. Плеер может даже не заметить, что произошло обновление.

Марио
источник
6
+1 за DDD. Это очень подходит для таких вещей, как ресурсы.
Кромстер говорит, что поддерживает Монику
@Funnydutchman, если вам помог чей-то ответ, на Stack Exchange принято поднять свой ответ, нажав стрелку над числом слева. Если вам больше всего помог ответ, нажмите на флажок под нижней стрелкой, чтобы принять его ответ. Оба действия дают ответчику бонус в репутации и делают полезные ответы более заметными для сообщества.
Nzall
2
Я не понимаю, как вам пришлось переписывать код первым способом; все, что вам нужно сделать, это добавить класс (если я что-то упустил).
SirPython
4
И в этом заключается разница между объектно-ориентированным кодом и объектно-ориентированным кодом. То, что вы можете создать иерархию объектов, не обязательно означает, что вы должны это делать.
CorsiKa
2
@ AgustínLado Если бы у меня был доллар за каждый раз, когда клиент говорил: «Это никогда не изменится», и это изменилось, я мог бы взять нас обоих в приличный ресторан на ужин (хотя нам пришлось бы немного сэкономить на чаевых, так что, возможно, только посредственный ресторан ... еще достаточно времени для ужина на двоих, хотя.)
corsiKa
29

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

Когда ресурсы имеют различную игровую механику, которая является уникальной для них, может иметь смысл представлять их с помощью классов. Например, если у вас есть Плутоний, который имеет период полураспада, и Кролики, которые размножаются в соответствии с последовательностью Фибоначчи , то может иметь смысл иметь базовый класс Resourceс методом, Updateреализованным как неоперативный, и двумя производными классами, HalfLifeResourceи SelfGrowingResourceкоторый переопределите этот метод, чтобы уменьшить / увеличить значение (и, возможно, также включить логику, чтобы управлять тем, что происходит, когда вы храните оба рядом друг с другом).

Но когда ваши ресурсы на самом деле являются просто глупыми числами, тогда нет смысла реализовывать их с помощью чего-то более фантастического, чем простая переменная.

Philipp
источник
Спасибо @Phillipp за ваш ответ. Это то, о чем я думал. Но ресурсы не будут сильно меняться с точки зрения свойств / методов, поэтому сейчас я
перейду к
14

Я хотел бы добавить, что есть две дополнительные опции:
Интерфейс: вы можете рассмотреть это, если класс ресурсов будет просто «хранилищем» для 5 целых чисел, и каждая сущность будет иметь различную логику в отношении ресурсов (например, тратить / добывать игрока, город производит ресурс). В этом случае вам может вообще не понадобиться класс - вы просто хотели показать, что у некоторой сущности есть переменная, с которой другие могут взаимодействовать:

interface IHasResources {
  int Gold { get; set; }
  int Wood { get; set; }
  int Cloth { get; set; }
  // ....
}

class Player : IHasResources { }
class City: IHasResources { }

Кроме того, у этого есть преимущество: вы можете грабить город с точно таким же кодом, как и с вражеским флотом, способствуя повторному использованию кода.
Недостатком является то, что реализация такого интерфейса может оказаться утомительной, если его реализуют многие классы, так что вы можете использовать интерфейсы, но не исходную проблему, решив ее с помощью опции 1 или 2:

interface IHasResources { 
   IEnumerable<Resource> Resources { get; }
}

Экземпляры (с перечислением): в некоторых случаях было бы желательно использовать перечисление вместо строки при определении типа ресурса:

enum Resource {
   Gold, Wood, Cloth, //...
}

class ResourceStorage {
   public Resource ResourceType { get; set; }
   public int Value { get; set; }
}

это обеспечит некоторую «безопасность», потому что ваша IDE предоставит вам список всех допустимых ресурсов при назначении его после написания точки ResourceType = Resource., кроме того, есть дополнительные «хитрости» с перечислениями, которые вам могут понадобиться, такие как явный тип или композиция / флаги который я могу себе представить полезным при создании:

[Flags]
enum Metal {
 Copper = 0x01/*01*/, Tin = 0x02/*10*/, Bronze = 0x03/*11*/
}
Metal alloy = Metal.Copper; //lets forget Value(s) for sake of simplicity
alloy |= Metal.Tin; //now add a bit of tin
Console.WriteLine(alloy); //will print "Bronze"


Что касается того, что лучше - серебряной пули нет, но лично я склоняюсь к любой форме экземпляров, они могут быть не такими причудливыми, как интерфейс, но они просты, не загромождают дерево наследования и широко поняты.

wondra
источник
2
Спасибо за ваш ответ @wondra Мне нравится опция enum и как вы сделали эту бронзу из меди и олова; p
MDijkstra
2
Я присоединился к этому сообществу специально, чтобы я мог проголосовать за решение enum. Вы также можете изучить использование атрибута enum Description для отображения из некоторого JSON-представления enum ( my-great-enum) в какое-либо C # -образующее представление enum ( MyGreatEnum).
Дэн Форбс
По-видимому, я слишком долго был вне цикла Java. С каких это пор разрешено иметь объявления полей для интерфейсов? Насколько я знаю, они автоматически являются статичными и окончательными, и, таким образом, они мало используются для целей, описанных в вопросе.
М.Херцкамп,
2
@ M.Herzkamp это не поле, это свойство - и Java не поддерживает свойства. Вопрос помечен C #, C # разрешают свойства в интерфейсах - свойства все-таки скрытые методы. Я боюсь, что то же самое касается аннотации флагов, не поддерживается в Java, хотя я не совсем уверен.
Вондра
Спасибо за разъяснение этого! Я пропустил тег C #, и код в вопросе выглядел так чертовски похож на Java: D
М.Херцкамп,
2

Вы должны использовать вариант 1, который имеет 3 преимущества:

  1. Создание экземпляра ресурса проще - вместо того, чтобы передавать тип ресурса в качестве параметра, вы просто сделаете, например, new Gold(50)
  2. Если вам нужно присвоить ресурсу особое поведение, вы можете изменить его класс.
  3. Проще проверить, является ли ресурс определенного типа - resource instanceof Gold
Zac
источник
У всех ответов есть свои плюсы и минусы, сложно выбрать, что лучше. Я внес изменения в вопрос, что вы думаете об этой структуре?
MDijkstra
1

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

Если вам нужно рассмотреть ситуации, например, что произойдет, если вы смешаете два ресурса для крафта, если ресурсы могут иметь совершенно разные свойства (ведро воды сильно отличается от куска угля) или другие сложные экономические взаимодействия, тогда подход 1 - это гораздо более гибкий для двигателя, но не данные.

Кристиан Уильямс
источник