Wraith
> Вопрос правильно поставлен: почему "больше кода" == "лучше"?
Люди просто не разбирались в том, какие преимущества может дать метод описанный в этой статье. В итоге я как раз таки напишу меньше кода чем они своими свитчами или конечными автоматами.
Вообщем опишу где это можно применить.
1) переходы между главным меню и игрой - банально и описано в статье
2) большое число экранов что хорошо например для РПГ, в которой начало игры может состоять из нескольких экранов - меню, создания партии, создания героя которое в свою очередь также может состоять из экранов и т.д. Описано в предыдущем посте.
3) бесконечное число экранов. Вот вздумалось издателю казуала собрать все свои игры в один сборник - благо движок у них один, игр допустим штук пятьдесят. Мне для этого надо написать всего сто-двести строчек кода (при условии что все они юзали модули) - помещение сцен в менеджер, и отдельную сцену с выбором игр. Вам надо будет написать новое приложение - лаунчер. У меня можно будет переходить из игры в игру сразу без лаунчера. У вас надо будет выходить в лаунчер из из него только выбирать новую игру. Я могу встроить лаунчер в код игр и у меня не будет этих раздражающих миганий экрана, вы такого вообще не сможете сделать. Я смогу передавать данные из одной игры в другую без записи их в файл, вам скорее всего придется их писать в файлы, потом грузить.
4) дебаг. Вот допустим у вас уже почти готовая игра. Интро, главное меню, игра. И вот вы тестируете, пытаетесь найти какой-то баг который невозможно поймать из дебагера, а только из игры. Сколько времени вы затрачиваете на переход "Интро-Игра"? Раздражает? Меня да. Хорошее решение - убрать интро и главное меню, и запускать сразу игру. Сколько строчек кода вам надо закоментировать или изменить? Мне всего одну. А когда вы найдете баг и надо будет все вернуть, сколько строк? Мне также одну?
5) инструментарий из игры. Я вот для двух своих трешевых игр (у меня в резюме ссылки) писал редакторы, при этом эти игры были не теми, для которых надо писать отдельный редактор. Я его писал в виде отдельного модуля. И как видите модули хорошо мне в этом помогли. Мне не надо писать тяжелый парсер разпознования входных параметров. Я не боюсь что какой-нибудь хакер сможет подобрать нужный ключ для запуска потому что у меня невозможно никаким обрабом в конечной версии запустить редактор (только через перекомпиляцию). Мне не надо комментировать огромные куски кода отвечающие за редактор
6) Подмодули. никто не обратил на это внимание, а зря. Давайте посмотрим на скайрим. В игре вы можете вызвать окно инвентаря, окно квестов, окно навыков, карту. При этом нельзя открыть сразу два окна. Мои модули позволяют такое сделать. В модуле "Игра", создаем новый экземпляр менеджера модулей. Затем создаем наследников IModule для каждого из этих окон. Программируем логику. Все, Теперь просто через менеджер, указываем в нужный момент нужный активный модуль. Все остальное сделает за вас код. Сколько строк кода вам над будет написать только для того чтобы корректно осуществлять переходы в эти окна? Мне все 4 и все они являются вызовом метода SetActiveModule(). Вам надо будет звать всякие Init/Close/Frame/Input, следить за тем чтобы не было вызвано сразу два окна, следить за тем чтобы из окна можно было вызвать только некоторые окна а не все. Мне ничего этого писать не надо - все уже написано
При этом мне дл всего этого не надо писать ни одной строчки кода - текущий модуль и менеджер уже имеют все что нужно (ну скорее всего для 6 придется дописать проверку на пустой модуль, но и то необязательно). так что код получился универсальным и чуть ли не на все случаи жизни:)
Кстати в том и преимущество ООП - один раз написав, код можно применять в множестве ситуаций, даже в тех, для которых он не предназначался. Именно поэтому на движке квайка можно сделать только квайк, медицинскую систему рендера органов человека на нем уже будет сложновато сделать.
И еще момент - я перерос то время когда ООП - это было круто (точнее я его обошел стороной). Сейчас я понял две вещь - код должен быть не красивым, не следовать всем законам какой-то парадигмы - он должен просто работать и все. Не важно - говнокод или идеал, если он решает свою задачу - это хороший код. Второе - чем меньше я вожусь с кодом, тем больше я могу сделать. Данная система модулей написана один раз год назад (с правками конечно). И теперь мне не надо начиная новый проект строчить сотни строк переходов. Да даже банально - мне не надо напрягать мозг тем как сделать этот переход, тупо копипаста достаточно чтобы все работало.
war_zes и что ты так не любишь switch?
if (getch( )==49) // нажал на 1 m_mgr->SetActiveModule( GAME);// переключаем на следующий модуль else if ( getch( )==50) // нажал на 2 return false; // выходим
Это уже switch.
// если пользователь нажмет Esc, вернемся в главное меню if (kbhit( ) && getch( ) == 27) m_mgr->SetActiveModule( MAINMENU);
Это почти switch.
Заметь, это твой код, а не мой.
HumanAPI
> и что ты так не любишь switch?
ты не понял. я не против свитча. Я против архитектурного решения в виде свитча - из-за которого весь код связывается в узел. Я избавлен от такого узла.
HumanAPI
> Это почти switch.
где? две строчки кода - это не кейс свитча
И ты не ответил на мой вопрос. Сделай свитчем (или каким другим способом то что я тебя просил пару постов ранее), просто сравним количество кода которое надо написать (я сделаю после тебя, потому что устал приводить листинги кода в пустую в местных спорах, когда люди не хотят доказывать свою точку зрения а переходят на личности)
Пожалуйста, добавил экран для создания персонажа.
Game.h
#pragma once #include "IModule.h" #include <string> class Game : public IModule { private: std::string m_text; std::string m_name; long m_i; static int st_init (IModule* module); static int st_menu ( IModule* module); static int st_game ( IModule* module); static int st_close ( IModule* module); static int st_person ( IModule* module); public: Game( ) {do_update = st_init;} ~Game( ) {} };
Game.cpp
#include "Game.h" #include <stdio.h> #include <stdlib.h> #include <iostream> #include <limits.h> #include <conio.h> int Game::st_init (IModule* module) { Game* gm = ( Game*)module; gm->m_text = "Hello World"; gm->m_name = "war_zes"; gm->m_i = 0; system( "CLS"); // очищаем экран std::cout << gm->m_text << std::endl; system( "PAUSE"); gm->do_update = st_game; return 1; } int Game::st_menu ( IModule* module) { Game* gm = ( Game*)module; int key; system( "CLS"); // очищаем экран std::cout << "Main Menu\n\n"; std::cout << "Select:\n"; std::cout << "1 - New Game\n"; std::cout << "2 - Exit Game\n"; std::cout << "3 - Set name person\n"; if ( kbhit( )) { key = getch( ); switch( key) { case 49: gm->do_update = st_game; break; case 50: gm->do_update = st_close; break; case 51: gm->do_update = st_person; break; } } return 1; } int Game::st_game ( IModule* module) { Game* gm = ( Game*)module; int key; system( "CLS"); // очищаем экран gm->m_i++; if ( gm->m_i >= LONG_MAX) gm->m_i = 0; std::cout << gm->m_name << " " << gm->m_i; if ( kbhit( )) { key = getch( ); switch( key) { case 27: gm->do_update = st_menu; break; // если пользователь нажмет Esc, вернемся в главное меню case 51: gm->do_update = st_person; break; } } if ( kbhit( ) && getch( ) == 27) gm->do_update = st_menu; return 1; } int Game::st_close ( IModule* module) { Game* gm = ( Game*)module; gm->m_text.clear( ); gm->m_name.clear( ); return 0; } int Game::st_person ( IModule* module) { Game* gm = ( Game*)module; system( "CLS"); // очищаем экран std::cout << "Create person \n\n"; std::cout << "New name - "; if ( kbhit( )) { std::cin >> gm->m_name; gm->do_update = st_game; } return 1; }
Если так дело пойдет к вечеру будет консольный скайрим :))
>> где? две строчки кода - это не кейс свитча
Это пока ты отлавливаешь одно нажатие кнопки, а если несколько то привет switch.
HumanAPI
щас тоже напишу, будет короче, понятей и более расширяемей
ModuleName.h
#pragma once #define HELLOSCREEN "Hello" #define MAINMENU "Menu" #define GAME "Game" #define HEROCREATE "Hero"
HelloScreen.h
#pragma once #include "IModule.h" class HelloScreen : public IModule { public: virtual void doInit(); virtual bool doRun( ); virtual void doClose( ); };
HelloScreen.cpp
#include "HelloScreen.h" #include "ModuleName.h" #include "ModuleMgr.h" void HelloScreen::doInit() { } bool HelloScreen::doRun( ) { m_mgr->SetActiveModule( MAINMENU); return true; } void HelloScreen::doClose( ) { }
MainMenu.h
#pragma once #include "IModule.h" #include <string> class MainMenu : public IModule { public: virtual void doInit(); virtual bool doRun( ); virtual void doClose( ); };
MainMenu.cpp
#include "MainMenu.h" #include "ModuleName.h" #include "ModuleMgr.h" void MainMenu::doInit() { } bool MainMenu::doRun( ) { system( "CLS"); std::cout << "Main Menu\n\n"; std::cout << "Select:\n"; std::cout << "1 - New Game\n"; std::cout << "2 - Exit Game\n"; if ( kbhit( )) { if ( getch( )==49) m_mgr->SetActiveModule( HEROCREATE); else if ( getch( )==50) Exit( ); } return true; } void MainMenu::doClose( ) { }
HeroCreate.h
#pragma once #include "IModule.h" class HeroCreate : public IModule { public: virtual void doInit(); virtual bool doRun( ); virtual void doClose( ); };
HeroCreate.cpp
#include "HeroCreate.h" #include "ModuleName.h" #include "ModuleMgr.h" void HeroCreate::doInit() { } bool HeroCreate::doRun( ) { m_mgr->SetActiveModule( GAME); return true; } void HeroCreate::doClose( ) { }
Game.h
#pragma once #include "IModule.h" #include <string> class Game : public IModule { public: virtual void doInit(); virtual bool doRun( ); virtual void doClose( ); };
Game.cpp
#include "Game.h" #include "ModuleName.h" #include "ModuleMgr.h" void Game::doInit() { } bool Game::doRun( ) { if ( kbhit( )) { if ( getch( ) == 27) m_mgr->SetActiveModule( MAINMENU); else if( getch( ) == 51) m_mgr->SetActiveModule( HEROCREATE); } return true; } void Game::doClose( ) { }
HumanAPI
> static int st_init (IModule* module);
> static int st_menu (IModule* module);
> static int st_game (IModule* module);
> static int st_close (IModule* module);
> static int st_person (IModule* module);
Видишь, ты вставил новую строчку в код. Я в своем примере не изменил ни одной строчки в системе модулей. Вообщем считай как знаешь, но я свою систему написал не ради крутости и не ради лололоООПлололо-я мастер ООП. А ради того чтобы без напряга добавлять новые экраны. Тебе чтобы добавить экран, надо лезть в код, мне надо написать отдельный независимый модуль и прицепить его к менеджеру. А это еще и бонус для командной разработки - я могу писать модуль главного меню, а другой программист модуль создания героя, и мы не будем писать конфликтующий друг с другом код, потому что нет связи.
Можно сказать что типа у меня много кода, но это только поверхностно. обрати внимание - это вообще копипаст, при этом в серьезном проекте ты от него не как не избавишься, любой класс должен инициализироваться, выполняться и завершаться (ну конструкторами, но и у меня можно перенести Init и Close в в конструкторы)
war_zes
> А ради того чтобы без напряга добавлять новые экраны.
я всегда удивлялся с людей которые чтобы не писать 1 строчку пишут 3:)
war_zes
> . А ради того чтобы без напряга добавлять новые экраны
Это называется повторное использование кода ...
Mephistopheles
> я всегда удивлялся с людей которые чтобы не писать 1 строчку пишут 3:)
Эх... семь раз отмерь - один раз отреж
Mephistopheles
> я всегда удивлялся с людей которые чтобы не писать 1 строчку пишут 3:)
Где? Я что, виноват, что в C++, чтобы написать метод класса, надо написать его объявление в объявлении класса, затем строчку с возвращающим типом именем и параметрами (как она зовется?) еще две строчки для фигурных скобок и строчку для ретурна, а из модулей у меня таки одна строчка. И вы никак не поймете, что в реальном проекте напишите те же самые 3 строчки и ваша ранняя экономия вам ровным счетом ничего не даст. Потому что можно сказать вообще так - затем писать 100 тысяч строк кода, если для рабочего приложения достаточно одной строчки: int main(){}
?
Это как раз то что вы и говорите, вы сэкономили 99999 строк но ничего не получили. также и здесь.
Какой то новый модуль супер независимый вообще ни чего не делает и поведение программы не изменилось кажется.
PS Я понял, что бы модуль HeroCreate начал работать его надо связать с модулем Game, но тогда он потеряет свою независимость.
А что же еще не опубликована то статья? Напиши модераторам, посмотрят, покажут что поправить, и опубликуется статья.
Метод вполне хороший, лучше банального свитча. Хотя бы этим:
Любая сцена (экран) игры имеет свою логику, в любом случае придется писать под каждый свой класс/код. Тут уж ни как не обойти. Но в данном случае достаточно будет только одной строчки для регистрации сцены. Связь между переходами сцен описывают сами сцены, что и откуда открыть они должны знать, им и назначается эта задача.
Что имеем, если это свитч? Тоже самое, создаем сцену, и после не регистрируем сцену, а правим блок свитча. Устанавливаем новые флаги, новые константы/переменные. В итоге они нагромождаются и становится не ясна логика а как и что работает. Теперь, что бы переключить экран, надо дергать не имя сцены, а знать какие же там были фалги/константы/переменные в свитч навязаны. А если удалить экран? два, три? Править опять свитч, убирать лишние переменные. А кто этим будет заниматься? Оставим на потом, как оно обычно и бывает. В итоге лишнее нагромождение кода.
Примеры приводят здесь которые, единичные они. Опираться не стоит на них.
кейс прозрачен и понятен любому нубу и легко правится.
говономенеджер сосет уже в статье
"как это работает" - вот этого пункта не должно быть в принципе. Читаем art of readable code.
новичков которые пишут статьи я бы отстреливал. резиновыми пулями (чтоб больнее и дольше)
Тема в архиве.