Xatari

На Пикабу
169 рейтинг 3 подписчика 1 подписка 2 поста 1 в горячем
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 - нарисовать сетку

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

....

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

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

Показать полностью 24 1
67

Как я сделал Платформер Pixel Quest

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

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

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

Но, как говорится, сдаются только трусы! Мы учли ошибки, сделали выводы и принялись за новый проект. Так как наработки в части платформера уже были, выбрали стратегию “меньше кода, больше проработки” и начали удалять все сложное с точки зрения управления, например, лестницы и лазанье по цепям.

Если честно, я немного слукавил говоря, что принятие решения идет по принципу, что хочу то и делаю. Самое сложное в геймдеве – придумать контент и найти хорошего художника, который будет готов работать на голом энтузиазме и за доброе слово. Я встречал много таких людей, обычно они активно начинают работу и быстро пропадают, приходится искать другого, а он уже не может рисовать в том же стиле. Хорошо, что есть магазины, где люди продают свой нарисованный контент за деньги.

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

Подобрали замечательные сеты уровней и главного героя в стиле pixel art

Купили тайлы, нарисовали в редакторе Tiled первые версии уровней,

получилось красиво. Игру в черновом варианте обозвали Pixel Rogue. Все логично – игра из пикселей, а вторым словом должно быть действие, так пусть игрок будет искателем приключений и ворует сокровища.

Когда появился прототип и главный герой, мы начали обсуждать разные варианты названий: Pixel Rogue, Pixel World, Pixel Man, Pixel Knight, Pixel Vixel, Pixel Adventure, Pixel Quest. Долгими дебатами выбрали “Pixel Quest” – это название хранит в себе какую-то загадочность и отражение в реальности вымышленного мира.

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

Подачу сюжета решили сделать в простом для понимания и реализации виде.

На уровне раскиданы три вида предметов:

1) Огонь – это обращение создателя к главному герою

2) Указатели – для знакомства игрока с миром, чтобы он не показался сухим и населенным роботами.

3) Свитки – это мысли самого персонажа

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

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

Любой уровень проходит в своем взрослении следующие жизненные этапы:

1. ЧВ (Черновой вариант) – это уровень нарисованный на листочке.

2. РЧВ (Реализованный черновой вариант) – это уровень, который можно "пробежать" в игре

3. ПВ 1 (Проверочный вариант 1) – это уровень, в котором есть примерные враги, монеты и сундуки

4. ПВ 2 (Проверочный вариант 1) – это уровень в котором уже меньше свободного и пустынного места, так сказать, ПВ 1 с устраненными замечаниями

5. ПВ 3 (Проверочный вариант 1) – это уже уровень, который на своем месте в порядке уровней. Имеет сложность адекватную своему местоположению. Ни один уровень не может стать ПВ 3, пока все уровни не прошли ПВ 2 проверку. Имеет чекпоинты

6. ПВ 4 (Проверочный вариант 1) – окончательный вариант уровня, у которого выровнен баланс, встроены диалоги. Устранены все замечания версии ПВ 3

Примерно по этой схеме мы старались провести каждый уровень, прогоняя его и тестируя много и много раз. Но самое интересное, что уровень на любой стадии может не быть включен в финальную версию игры. Для Pixel quest мы реализовали 25 уровней на разной стадии, а в релиз пошло только 18,самых отборных и лучших на наш взгляд.

Вот, например, один из последних уровней на разных стадиях:

Как я уже написал, подачу сюжета решили делать через некоторые объекты на карте. Для упорядочивания сюжетной линии расписали содержание в виде текста, который потом конвертировали в конкретные сообщения, а после расставили объекты на уровне. Вот пример одной из глав:

и как в виде текстовых сообщений:

Как это выглядит на уровне:

Игру, разумеется, всегда хочется сделать качественную, с наименьшим количеством глюков, но каждый раз перепроходить уровни, начиная с первого, долго, поэтому использовали стандартный метод “читов”. В специальном билде на главном экране появляется кнопка которой можно активировать “админку”.

По итогу можно выбрать любой уровень, и с помощью кнопок во время игры:

1. Пройти все предыдущие уровни полностью и зафиксировать это;

2. Пройти текущий уровень, собрав все предметы;

3. Включить режим “имбы”;

4. Выпить зелье здоровья;

5. Надеть шкуру снежного человека;

6. Вернуться в шкуру главного героя;

7. Пройти уровень, собрав все пиксели “Силы”;

8. Пройти уровень, собрав все пиксели “Интеллекта”;

9. Пройти уровень, собрав все пиксели “Выносливости”;

10. Пройти игру с концовкой номер “1”;

11. Пройти игру с концовкой номер “2”;

12. Пройти игру с концовкой номер “3”.

А вот как мы старались сделать "красиво" на примере главного меню.

Музыкальное сопровождение оставили напоследок, когда вся игра уже была почти готова, потому что музыканту надо поиграть, погрузиться в процесс, а не абстрактно сочинять, что попало. Благодаря такому подходу у нас появились уникальные композиции, написанные для Pixel quest. На каждом уровне и для каждого босса – своя мелодия. Музыка занимает больше половины всего веса – 55 мегабайт из 80, но мы думаем, оно того стоит.



[b]Игра бесплатная[/b]. Скачайте, поиграйте, получите удовольствие. Буду очень благодарен за положительные отзывы и пятерки.


Вот ссылочка на видео:

https://www.youtube.com/watch?v=TDKoU7YZBTk


Вот ссылочка на приложение

https://play.google.com/store/apps/details?id=com.xatari.Pix...

Показать полностью 13 1
Отличная работа, все прочитано!