9

Пишем игру "Змейка"

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

О движке

Пишу я его на с++ с использованием графической библиотеке SDL, в качестве скриптового языка использую Питон.

Прошу прощения за скриншоты, но не понял как вставлять "правильный код", а вставлять цитатами было очень страшно и не красиво.

Для лиги лени сразу прикрепляю ссылку с готовым архивом:

Скачать архив

И демонстрация игры

Заранее спасибо. И так начнем.

Создаем пустой проект

Структура каталогов пустого проекта:

Редактируем config.xml

config.xml - это файл который читается в движке первым.

имеет структуру xml

Основная строка которая нас интересует

<AppMain value = "..\Assets\ScriptsP\AppMain.py" />

Application.Script.AppMain - это путь к скрипту с которого стартует приложение

Все конфиг нам больше не нужен.

Создаем Тайлы

В каталог /Assets/Tiles/ - добавляем следующие картинки (все имеют формат 120 на 120)

  1. snakeApple.png - яблоко

2. snakeBody.png - часть тела змеи (кружочек зеленый)

3. snakeGrid.png - квадратик с полупрозрачными черными линиями(из него мы будем делать сетку)

4. snakeHeadDown.png - голова которая смотри вниз

5. snakeHeadLeft.png - голова которая смотри влево

6. snakeHeadRight.png - голова которая смотри вправо

7. snakeHeadUp.png - голова которая смотри вверх

Создаем UI

Элементы ГУИ можно создавать как програмно так и задавать расположение в xml файлах.

Получается что то типа "Окна" - к которому прикреплены все элементы как дочерние.

создаем файл UISnake.xml

К основному объекту типа xdCanvas прикрепляем

Объект типа xdText с именем tFPS который будет счетчиком фпс

Аналогично добавляем

Объект типа xdText с именем tObjects который будет счетчиком количества объектов на экране

Объект типа xdText с именем tScore который будет отображать текущий счет.

Ниже полный текст файла UISnake.xml

<?xml version="1.0"?>

<object type="xdCanvas">

<position x="0" y="0" />

<scale value="1" />

<order value="999998" />

<name value="UISnake" />

<relative value="XD_UI" />

<alignment value="XD_UI_ALI_NONE" />

<anchorVertical value="XD_UI_ANC_V_TOP" />

<anchorHorizontal value="XD_UI_ANC_H_LEFT" />

<childs>

<object type="xdText">

<position x="-100" y="-20" />

<text value="FPS:" />

<font name="..\Assets\Fonts\Comfortaa.ttf" size = "10" style = "1" colorR = "93" colorG = "86" colorB = "79" />

<name value="tFPS" />

<anchorVertical value="XD_UI_ANC_V_DOWN" />

<anchorHorizontal value="XD_UI_ANC_H_RIGHT" />

</object>

<object type="xdText">

<position x="-100" y="-10" />

<text value="Objects:" />

<font name="..\Assets\Fonts\Comfortaa.ttf" size = "10" style = "1" colorR = "93" colorG = "86" colorB = "79" />

<name value="tObjects" />

<anchorVertical value="XD_UI_ANC_V_DOWN" />

<anchorHorizontal value="XD_UI_ANC_H_RIGHT" />

</object>

<object type="xdText">

<position x="20" y="20" />

<text value="Счет:" />

<font name="..\Assets\Fonts\Comfortaa.ttf" size = "30" style = "1" colorR = "100" colorG = "50" colorB = "50" />

<name value="tScore" />

<anchorVertical value="XD_UI_ANC_V_TOP" />

<anchorHorizontal value="XD_UI_ANC_H_LEFT" />

</object>

</childs>

</object>

Создаем файл UISnakeMenu.xml

Который будет отвечать за меню начала игры и отображения результатов

Иерархия нашей меню будет следующая

  1. UISnakeMenu(xdCanvas) - основной канвас окна

    1. uiPanelCenterMenu(xdPanel) - панелька на которой все нарисуем

    2. tCaption(xdText) - Заголовок "Пиратская Змейка"

    3. tGameOver(xdText) - Надпись "Начните новую игру", или "Game over" (значение будем задавать скриптом)

    4. tScoreEnd(xdText) - Счет, если был проигрыш (значение будем задавать скриптом)

    5. btnNewGame(xdButton) - Кнопка новая игра (при нажатии которой игра начинается сначала)

    6. btnExit(xdButton) - Кнопка выход из игры

Из непонятного у нас только xdPanel и xdButton

это по сути те-же элементы UI, что и xdText - описанные выше, только с небольшими отличиями

xdPanel - элемент интерфейса отражает физическую панельку, на которой мы можем что то рисовать.

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

Принципиально не отличается от текста вся логика будет задаваться на стороне скрипта.

Ниже полный текст файла UISnakeMenu.xml

<?xml version="1.0"?>

<object type="xdCanvas">

<position x="0" y="0" />

<scale value="1" />

<order value="999998" />

<name value="UISnakeMenu" />

<relative value="XD_UI" />

<alignment value="XD_UI_ALI_NONE" />

<anchorVertical value="XD_UI_ANC_V_TOP" />

<anchorHorizontal value="XD_UI_ANC_H_LEFT" />

<childs>

<object type="xdPanel">

<position x="0" y="0" />

<sizePrecent x="20" y="50" />

<name value="uiPanelCenterMenu" />

<anchorVertical value="XD_UI_ANC_V_CENTER" />

<anchorHorizontal value="XD_UI_ANC_H_CENTER" />

<childs>

<object type="xdText">

<position x="0" y="+30" />

<text value="Пиратская Змейка" />

<font name="..\\Assets\\Fonts\\Comfortaa.ttf" size = "20" style = "1" colorR = "93" colorG = "86" colorB = "79" />

<name value="tCaption" />

<anchorVertical value="XD_UI_ANC_V_TOP" />

<anchorHorizontal value="XD_UI_ANC_H_CENTER" />

</object>

<object type="xdText">

<position x="0" y="80" />

<text value="Начните новую игру" />

<font name="..\\Assets\\Fonts\\Comfortaa.ttf" size = "18" style = "1" colorR = "190" colorG = "86" colorB = "79" />

<name value="tGameOver" />

<anchorVertical value="XD_UI_ANC_V_TOP" />

<anchorHorizontal value="XD_UI_ANC_H_CENTER" />

</object>

<object type="xdText">

<position x="0" y="100" />

<text value="Счет:" />

<font name="..\\Assets\\Fonts\\Comfortaa.ttf" size = "18" style = "1" colorR = "93" colorG = "86" colorB = "79" />

<name value="tScoreEnd" />

<anchorVertical value="XD_UI_ANC_V_TOP" />

<anchorHorizontal value="XD_UI_ANC_H_CENTER" />

</object>

<object type="xdButton">

<position x="0" y="0" />

<size x="130" y="50" />

<name value="btnNewGame" />

<text value="Новая Игра" />

<anchorVertical value="XD_UI_ANC_V_CENTER" />

<anchorHorizontal value="XD_UI_ANC_H_CENTER" />

</object>

<object type="xdButton">

<position x="0" y="70" />

<size x="130" y="50" />

<name value="btnExit" />

<text value="Выход" />

<anchorVertical value="XD_UI_ANC_V_CENTER" />

<anchorHorizontal value="XD_UI_ANC_H_CENTER" />

</object>

</childs>

</object>

</childs>

</object>

```

Скрипты

Теперь переходим к самому интересному к логике.

Файл globals.py - вспомогательный скрипт который в данной игре не имеет смысла большого, можно обойтись без него.

но по замыслу тут находятся какие-то глобальные константы и методы

например есть метод Debug который позволяет отлаживать скрипты в режиме реального времени в Visual Studio Code ооооочень удобно, но об этом в другой раз.

Создаем файл AppMain.py

Импортируем пространство имен xd

import xd # Импорт в питон методов движка. почти все находится в пространстве имен xd

добавляем самый первый метод OnStart

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

Application.OnStart - будет вызываться при старте сцены.

application - экземпляр класса приложение.

в глобальный метод **OnStart** добавляем вызов:

Минимальный скрипт готов.

Далее создаем пустую сцену, а именно файл SceneGame.py

Сцена это объект порождённый от объекта xd.GameScene со следующими методами:

OnLoad - вызывается после загрузки сцены

OnStart - вызывается при старте сцены

OnFrame - вызывается каждый кадр.

Unload - метод где будем удалять все, что создали.

Текст файла SceneGame.py с минимальной сценой.

Далее в файле AppMain.py добавляем следующие строки

где в

xd.GameScene.LoadScene("SceneGame.SceneGame", self.OnLoadScene)

Вызываем метод движка xd.GameScene.LoadScene для загрузки сцены.

1. "SceneGame.SceneGame" - строка с указанием класса сцены в питон скрипте. В формате [Имя файла скрипта].[Имя класса в файле]

2. self.OnLoadScene - обработчик вызываемый после загрузки сцены.

Реализация метода SceneGame.OnLoadScene

На данный момент полный текст файла AppMain.py:

```python

import xd # type: ignore

import globals

globals.Debug(False)

class Application:

""" Глобальный класс сцена"""

def OnStart(self):

xd.GameScene.LoadScene("SceneGame.SceneGame", self.OnLoadScene)

def OnLoadScene(self, scene):

self.scene = scene

xd.App.SetGame(scene)

pass

#Экземпляр класса сцены

application = Application()

def OnStart():

"""Обработчик при старте сцены (вызывается движком)"""

application.OnStart()

return 1

После запуска должно быть пустое окно с синеватым фоном. Если это так, от значит мы все делаем правильно.

Файл AppMain.py - мы больше не меняем. Он принял конечный вид.

В методе SceneGame.OnStart добавляем следующий код

На любой сцене есть корневой объект с именем rootObject куда можно прикреплять объекты. Не прикрепленные объекты не отображаются на сцене (игнорируются циклом рендера)

Метод xd.BaseObject.FindByNameS это метод FindByNameS - ищет игровой объект на сцене с по имени. и возвращает ссылку на объект.

Метод xd.Panel.to - преобразовывает ссылку в Объект типа xdPanel есть аналогичные методы для любых предопределенных объектов xd.Text.to , xd.Button.to и др.

После запуска мы увидим Окно игровое с отображаемой меню.

Далее добавляем на сцену окно из файла UISnake.xml для отображения текущих игровых данных а запоминаем ссылки на элементы интерфейса см которыми после будем работать

Первый обработчик событий. Начнем с простого, сделаем обработчик, который выходит из игры.

У нас есть кнопка для выходи из игры с именем btnExit

Первая сцена и обработчик готовы

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

Далее добавим в проект вспомогательный класс GameData

Основная особенность которого хранить данные между сценами, аля глобальный объект. В текущей реализации это не обязательно но предлагается его использовать, потому, что при развитии может быть у нас появятся новые сцены, и как между ними передавать данные?

С высоты птичьего полета класс GameData выглядит так:

Методы:

  1. GameData.Unload - метод где описываются удаления элементов аля деструктор

  2. GameData.OnStart - обработчик который вызываем при старте приложения

  3. GameData.OnTick - обработчик таймера (обсудим позже)

  4. GameData.OnKeyPressed - обработчик нажатия клавиши

  5. GameData.GameOver - Метод проигрыша

  6. GameData.NewGame - метод новая игра

  7. GameData.IsPause - проверить на паузе ли игра

  8. GameData.NewApple - добавить новое яблоко на сцену

  9. GameData.DrawGrid - нарисовать сетку

Реализация этих методов будет позже и постепенно. Сейчас сосредоточимся на следующем.

....

Часть первая закончена (лимит картинок в посте закончился)

Спасибо, кто дочитал, и прокомментировал.

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества