И это все. Никаких наворотов, только то, что необходимо. Следующий класс NewGameMenu. Он создаёт меню, которое видит игрок перед началом и после окончания игры. Итак, что представляет собой меню? Это просто прямоугольник, на который "натянута" текстура с изображение меню. Тут все просто, но сколько нам нужно текстур?
Давайте считать. Существует 4 возможных варианта:
- игрок только что запустил программу, и мы предлагаем начать игру; игрок выиграл предыдущую партию, мы выводим соответствующее сообщение и предлагаем сыграть ещё раз; игрок проиграл предыдущую игру; предыдущая игра закончилась вничью.
Кроме этого я решил реализовать подсветку кнопок меню, т. е. нужно по одной дополнительной текстуре для каждой кнопки (каждое меню в игре имеет две кнопки). Таким образом, получается, что нужно по 3 текстуры на каждый вариант меню. Итого, 4 * 3 = 12 текстур. Посмотрим, как пользоваться нашим классом. При создании меню нам нужно указать положение центра меню на экране, передать указатель на массив с именами текстур, количество текстур в массиве, длину и ширину меню, прямоугольник в котором нужно нарисовать меню. Для прорисовки меню, как вы догадались, используется метод render(). Два дополнительных метода класса – checkSelectedButtons и setCurrentMenu используются вместе. Зачем они нужны? Помните, я писал, что мы будем подсвечивать кнопки меню, так вот, чтобы включить подсветку мы должны проверить, где находится курсор мыши, и установить соответствующую текстуру. Метод checkSelectedButtons в качестве параметра принимает координаты курсора мыши, и возвращает номер выбранной кнопки, а метод setCurrentMenu устанавливает нужное меню. Таким образом, если вызывать эти методы при в обработчике события WM_MOUSEMOVE (возникает при перемещении курсора мыши), то мы получим обычное меню: навели курсор на кнопку, она подсветилась, убрали – подсветка исчезла. Приводить пример использования этого класса я не буду, т. к. он очень тесно связан с остальным кодом проекта (обработкой сообщений, установкой матриц преобразований и т. п.), поэтому будет лучше, если вы посмотрите исходники программы, и почитаете комментарии. Теперь, как я и обещал, вернёмся к классу XZGame, и рассмотрим, как он осуществляет управление ходом игры (т. е. его работу в качестве движка). В первую очередь посмотрим на рис.3. На нем изображена диаграмма состояний класса, на которой изображены все возможные варианты хода игры (ситуации, вроде нажатия на кнопку "Reset" или удара молотком по системному блоку здесь не учитываются). Рис.3. Диаграмма состояний класса Давайте разберем все по порядку. У нас есть шесть основных состояний игры, каждому из которых соответствует своя константа в перечислении states (см. исходный код):
- NEW_GAME_MENU – начало новой игры, пользователь только что запустил программу; NEW_GAME_MENU_WIN – начало новой игры, пользователь выиграл предыдущую игру; NEW_GAME_MENU_LOST – начало новой игры, пользователь проиграл предыдущую игру; NEW_GAME_MENU_DRAW – начало новой игры, предыдущая игра закончилась вничью; PLAYER_MOVE – ожидание хода игрока; COMP_MOVE – ожидание хода компьютера (ждем, пока метод findNextMove класса XZField выберет ход).
Итак, игрок только что запустил программу. Объект класса XZGame находится в состоянии NEW_GAME_MENU, и, соответственно, на экране отображается меню с предложением начать игру. Если игрок нажимает кнопку "нет" – игра завершается (не понятно – зачем вообще он ее запускал? От скуки щелкал по всем ярлыкам подряд:-)). А вот если нажата кнопка "да" – начинаем играть. В первую очередь, программа выбирает, кто первым будет ходить (случайным образом, с помощью функции rand()). Далее, в зависимости от результатов предыдущего этапа, мы попадаем в состояние PLAYER_MOVE или COMP_MOVE. В состоянии PLAYER_MOVE мы просто ждем, пока игрок сделает ход (кстати, игрок всегда играет ноликами). А в состоянии COMP_MOVE мы ждем результатов метода findNextMove. Затем мы проверяем состояние игры (завершена, не завершена, если завершена, то с каким результатом). Если игра не была завершена, то мы переходим либо в состояние PLAYER_MOVE, либо COMP_MOVE, в зависимости от того, кто ходил перед этим. Если игра завершена, то в зависимости от ее результатов, мы переходим в одно из трех состояний: NEW_GAME_MENU_WIN (если победил игрок), NEW_GAME_MENU_LOST (если победил компьютер), NEW_GAME_MENU_DRAW (если игра закончилась вничью). Эти состояния очень похожи. Все три рисуют меню с предложением начать новую игру. Разница только в текстурах, которые используются для меню (в верхней части текстуры нарисована строка с результатами игры). Дальше все просто, если игрок нажмет кнопку "да" – переходим в состояние розыгрыша первого хода, и процесс повторяется, если игрок нажал кнопку "нет" – завершаем работу. Заключение Это очень простая игра, но, тем не менее, здесь используются те же приемы, что и в больших проектах. Проблема с большими проектами в том, что в них, как говорится, "за лесом деревьев не видно" (особенно, если вы только начали изучать DirectX и принципы создания игр). Конечно, моя игрушка написана далеко не идеально. К моменту окончания работы над ней, я видел кучу недостатков, и, если бы я делал ее заново, то возможно она получилась бы лучше и красивее. Но игра работает. В нее можно играть. А это все-таки положительный результат. В общем, надеюсь, эта статья вам поможет. Скачать: игру крестики-нолики (setup. exe – 578кБ); исходный код (xzgame. zip – 475кБ). Постовой Продвижение сайта сделает ваш товар ближе к потребителю