В какой-то момент движок ДОЛЖЕН специализироваться и знать материал об игре. Я пойду по касательной здесь.
Возьмите ресурсы в РТС. Одна игра может иметь Credits
и Crystal
другую Metal
иPotatoes
Вы должны использовать концепции ОО правильно и идти на макс. кода повторного использования. Понятно, что Resource
здесь существует понятие .
Поэтому мы решили, что ресурсы имеют следующее:
- Крюк в основном цикле, чтобы увеличивать / уменьшать себя
- Способ получить текущую сумму (возвращает
int
)
- Способ вычитать / складывать произвольно (игроки переводят ресурсы, покупают ....)
Обратите внимание, что это понятие Resource
может представлять убийства или очки в игре! Это не очень сильно.
Теперь давайте подумаем об игре. Мы можем иметь валюту, имея дело с копейками и добавляя десятичную точку к выводу. Чего мы не можем сделать, так это «мгновенных» ресурсов. Мол, скажем "генерация электросети"
Допустим, вы добавили InstantResource
класс с похожими методами. Вы теперь (начинаете) загрязнять свой двигатель ресурсами.
Проблема
Давайте снова возьмем пример RTS. Предположим, игрок что-нибудь пожертвует Crystal
другому игроку. Вы хотите сделать что-то вроде:
if(transfer.target == engine.getPlayerId()) {
engine.hud.addIncoming("You got "+transfer.quantity+" of "+
engine.resourceDictionary.getNameOf(transfer.resourceId)+
" from "+engine.getPlayer(transfer.source).name);
}
engine.getPlayer(transfer.target).getResourceById(transfer.resourceId).add(transfer.quantity)
engine.getPlayer(transfer.source).getResourceById(transfer.resourceId).add(-transfer.quantity)
Однако это действительно довольно грязно. Это общее назначение, но грязное. Уже, хотя это налагает, resourceDictionary
что означает, что теперь ваши ресурсы должны иметь имена! И это на игрока, поэтому вы не можете больше иметь командные ресурсы.
Это «слишком большая» абстракция (я не признаю блестящий пример), вместо этого вы должны достичь точки, когда вы признаете, что в вашей игре есть игроки и кристалл, тогда вы можете просто иметь (например)
engine.getPlayer(transfer.target).crystal().receiveDonation(transfer)
engine.getPlayer(transfer.source).crystal().sendDonation(transfer)
С классом Player
и классом , CurrentPlayer
где CurrentPlayer
«S crystal
объект будет автоматически показывать материал на ИЛС для передачи / отправки пожертвований.
Это загрязняет двигатель кристаллом, пожертвованием кристалла, сообщениями в HUD для текущих игроков и всем этим. Это быстрее и проще для чтения / записи / обслуживания (что более важно, так как это не значительно быстрее)
Заключительные замечания
Случай с ресурсами не блестящий. Я надеюсь, что вы все еще можете увидеть суть. Во всяком случае, я продемонстрировал, что «ресурсы не принадлежат движку», поскольку то, что нужно конкретной игре и что применимо ко всем представлениям о ресурсах, это ОЧЕНЬ разные вещи. Обычно вы найдете 3 (или 4) «слоя»
- «Ядро» - это определение движка в учебнике, это граф сцены с перехватчиками событий, он имеет дело с шейдерами и сетевыми пакетами и абстрактным представлением игроков
- «GameCore» - это довольно общий тип игры, но не для всех игр - например, ресурсы в RTS или боеприпасы в FPS. Логика игры начинает просачиваться сюда. Вот где было бы наше раннее представление о ресурсах. Мы добавили эти вещи, которые имеют смысл для большинства ресурсов RTS.
- «GameLogic» ОЧЕНЬ специфичен для самой игры. Вы найдете переменные с именами, такими как
creature
или ship
или squad
. Использование наследования вы получите классы , которые охватывают все 3 слоя (например , Crystal
это Resource
который является GameLoopEventListener
скажет)
- «Активы» бесполезны для любой другой игры. Возьмем, к примеру, комбинированные сценарии ИИ в Half Life 2, они не будут использоваться в RTS с тем же движком.
Создание новой игры из старого движка
Это ОЧЕНЬ распространено. Фаза 1 состоит в том, чтобы вырвать слои 3 и 4 (и 2, если игра ПОЛНОСТЬЮ другого типа). Предположим, мы делаем RTS из старой RTS. У нас все еще есть ресурсы, только не кристаллы и прочее - поэтому базовые классы в слоях 2 и 1 все еще имеют смысл, все кристаллы, на которые есть ссылки в 3 и 4, могут быть отброшены. Так и делаем. Однако мы можем проверить это как ссылку на то, что мы хотим сделать.
Загрязнение в слое 1
Это может случиться Абстракция и производительность - враги. Например, UE4 предоставляет множество оптимизированных вариантов компоновки (поэтому, если вы хотите, чтобы X и Y написали код, который действительно быстро выполняет X и Y - он знает, что выполняет оба), и в результате ДЕЙСТВИТЕЛЬНО довольно большой. Это не плохо, но это отнимает много времени. Уровень 1 будет определять такие вещи, как «как вы передаете данные в шейдеры» и как вы анимируете вещи. Делать это наилучшим образом для вашего проекта ВСЕГДА хорошо. Просто попробуйте и спланируйте будущее, повторное использование кода - ваш друг, наследуйте там, где это имеет смысл.
Классифицирующие слои
ПОСЛЕДНО (обещаю) не надо слишком бояться слоев. Движок - это архаичный термин из старых дней конвейеров с фиксированной функцией, когда двигатели работали в основном графически (и в результате имели много общего), программируемый конвейер перевернул это с ног на голову, и как таковой «слой 1» стал загрязненным с любыми эффектами, которые разработчики хотели достичь. ИИ был отличительной чертой (из-за множества подходов) двигателей, теперь это ИИ и графика.
Ваш код не должен храниться в этих слоях. Даже знаменитый движок Unreal имеет МНОЖЕСТВО разных версий, каждая из которых специфична для отдельной игры. Есть несколько файлов (отличных от структур данных), которые остались бы без изменений. Это отлично! Если вы хотите создать новую игру из другой, это займет больше 30 минут. Ключ в том, чтобы планировать, знать, какие биты копировать и вставлять, а что оставлять.