Разработка движка для roguelike libtcod-duktape
Если кому интересно — я создал: https://github.com/Crutchmaster/libtcod-duktape
Что это?
Обёртка из js движка duktape над библиотекой libtcod. Последний умеет рендерить в opengl/sdl тайлы/псевдографику, там есть a*, fov (field of view), генератор подземелий, карт высот и еще кое что по мелочи
Что и этого получится?
Простой движок, на котором можно легко клепать игрульки на 7DRL или просто поиграться.
Киллер фичи?
Stand-alone движок, код для которого не нужно компилять, а для начала разработки достаточно текстового редактора. Duktape реализует функции изоленты, всё остальное, сколько-нибудь тяжелое, допиливается на си.
Почему не lua/v8/python/etc
Луа мерзкий, v8 избыточен, обёртка для питона уже есть, питон тоже мерзкий.
Это мой первый былдокод на си, учтите это, когда будете строчить свои язвительные комменты. Ну и всё еще только в разработке, но я постараюсь пилить и не забрасывать.
crutch_master ★★★★★
17.07.17 09:56:37 MSK
Поправил Makefile прими pull, картинки перемести в data исходники в src, добавь README, include файлы либо вместе с исходниками в одном каталоге src или все в include каталог положи.
Dron ★★★★★
( 17.07.17 10:08:48 MSK )
duktape.c у тебя бинарный, исходник не похерил?
Краткое введение
Добро пожаловать к нам в учебник. Как вы, возможно, догадываетесь, наша цель состоит в том, чтобы выдать вам всю необходимую информацию для создания хорошего рогалика с самого нуля. Надеюсь, наш учебник будет вам полезен! Но, для начала, небольшой FAQ.
Почему именно Python?
Большинство людей, знакомых с этим языком, находят его очень интересным. Python стремится быть простым, но мощным, и крайне понятным для новичков. Проходить данный учебник без знания этого языка будет трудновато. Поэтому мы рекомендуем вам установить Python 2.7 и освоить хотя бы первые главы Python Tutorial (Примечание для пользователей Windows 7 x64: устанавливайте 32-битную версию, так как, по-видимому, 64-битный Python не дружит с libtcod). Если вы хоть немного поэкспериментируете с языком, то понять учебник вам будет гораздо проще. Не забывайте, что Справка Python Library — ваш лучший друг в этом деле. В стандартной библиотеке есть всё, что вам нужно для работы. И будьте готовы к тому, что в процессе разработки вам придётся искать в ней справку по любой неизвестной функции.
Почему именно libtcod?
Если вы ещё не видели её в деле, почитайте о её возможностях, посмотрите на готовые проекты с её использованием. Эта библиотека исключительно проста в использовании и содержит множество полезных для рогаликов функций.
Начало учебника
Начните с первой главы!
- Глава 1: Графика Начнём создание игры с настройки окна. Нарисуем классическую @ и заставим её перемещаться нажатием клавиш стрелок.
- Глава 2: Объект и карта Здесь мы освоим две новых идеи: это система универсального объекта, которая станет основой всей игры; и это общий объект карты, в котором будет храниться наше подземелье.
- Глава 3: Подземелье Узнаем как написать небольшой и понятный генератор подземелья.
- Глава 4: Поле зрения и исследование Отображение поля зрения игрока (FOV) и постепенное исследование подземелья (также известное как «туман войны»).
- Глава 5: Подготовка к бою Разместим в подземелье несколько орков и троллей (ненадолго!). А ещё, разберёмся с блокирующими объектами и состояниями игры. Всё это нам понадобится в следующей главе.
- Глава 6: Впадаем в ярость берсерка! Охота на монстров, сражения, крошилово — о чём тут ещё говорить?
- Глава 7: Интерфейс Вкусный интерфейс с полосками состояния и разноцветный журнал сообщений, дающий глазу наибольшую усладу. Кроме того, старая-добрая команда «обзор» с небольшой доработкой: можно рассматривать объекты мышью.
- Глава 8: Вещи и инвентарь Игрок сможет поднимать вещи с пола и использовать их через удобный экран инвентаря. Больше вещей мы добавим в следующей главе.
- Глава 9: Заклинания и дистанционный бой Варианты действий игрока вырастут экспоненциально с добавлением нескольких свитков магии. Создадим заклинания урона и разума, не забудем и про дистанционный бой.
- Глава 10: Главное меню и сохранение Готовое главное меню с фоновой картинкой и возможностью сохранять и загружать игру.
- В 11-ой главе будет разобран переход между этажами подземелья, прокачка игрока по уровням и его характеристики.
Приложения
Разные необязательные для реализации штуки. Можете почитать этот раздел, когда разберётесь с учебником и захотите улучшить игру. Некоторые из советов просты, другие же гораздо сложнее.
- Удобный ярлык Python для Notepad++ Настройка ярлыка для пользователей программы Notepad++. Удобно для отладки.
- Олдскульные тайлы стен и полаs Использование символов в тайлах без получения странных глюков с графикой. По сути, метод очень простой.
- Бой в реальном времени Система скорости, меняющая пошаговый бой из учебника на бой в реальном времени!
- Прокрутка карты Код, реализующий прокрутку карты на экране при движении игрока
- Создание исполняемого файла Легко и просто упаковываем игру!
Автор и благодарности
Код и учебник написаны João F. Henriques (a.k.a. Jotaf). Спасибо: George Oliver, за помощь с форматированием, сортировкой глав и подсветкой синтаксиса; Teddy Leach за его рецензии; и всем форумчанам с форума libtcod за их неоценимую поддержку!
Спокойно заходите на libtcod/Python forum если у вас возникли проблемы, если вам хочется показать свой проект или просто пообщаться! Всегда приятно получить обратную связь по этому учебнику и узнать о разработке других рогаликов.
Примечание переводчика
Текст учебника будет адаптироваться под самую свежую версию libtcod. На текущий момент, это версия 1.5.1rc1.
Автор: João F. Henriques (a.k.a. Jotaf)
Источник: Complete Roguelike Tutorial, using Python+libtcod
Перевод: Sanja, 25.03.2012
Saved searches
Use saved searches to filter your results more quickly
Cancel Create saved search
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
Simple roguelike game
dsaveliev/Simple-Roguelike
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Switch branches/tags
Branches Tags
Could not load branches
Nothing to show
Could not load tags
Nothing to show
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Cancel Create
- Local
- Codespaces
HTTPS GitHub CLI
Use Git or checkout with SVN using the web URL.
Work fast with our official CLI. Learn more about the CLI.
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
Git stats
Files
Failed to load latest commit information.
Latest commit message
Commit time
README.md
Rogue-like игра «Tombs of the Ancient Kings»
Игра создана при помощи:
Откуда есть пошла рогалик-игра (Материалы по рогаликам)
- Статья на википедии.
- Roguebasin
- Отечественный форум рогаликолюбов
Запуск игры
Для запуска, на машине должен быть установлен Python 2.X, и в папке с игрой должны присутствовать библиотеки:
- libSDL.so (Различные версии этой библиотки можно найти в папке ./Vendor/libtcod/)
- libtcod.so (Под конкретную машину библиотеку можно скомпилировать из исходников — см. /Vendor/libtcod/cmake/QuickStart.txt)
Игра запускается через консоль:
python start.py
Значение некоторых символов в игре:
@ - Главный герой (Вы) ! - Какое-либо зелье # - Какой-либо свиток (а так же стены) D, T, o, g - Монстры Подробнее, о значении символа в игре можно узнать наведя на него мышью.
Управление:
y k u \ | / h - @ - l Перемещение (Традиционный Vi стиль) / | \ b j n i - Открыть инвентарь g - Поднять вещь с земли, под игроком d - Выкинуть вещь из инвентаря f - Атаковать (при дистанционной атаке) Esc - Выход в меню Изменить клавиши можно в файле ./Config/game.py
Глава 7: Интерфейс [ править ]
Куча вещей происходит под капотом игры, которые не всегда ценятся игроком по достоинству, например, боевая механика, описанная в предыдущих главах. Но теперь мы будем работать над кое-чем более видимым — Графическим Интерфейсом Пользователя (Graphical User Interface)! Со всей мощью полноцветных консолей libtcod’а, и с кучкой креативности вы сможете создать по-настоящему потрясную графику. Вы можете поспорить, что ограничения консоли на самом деле позволяют проще сделать игру конфеткой, чем если бы у вас была свобода в пиксельной графике, как в большинстве игр.
Мы начнем с создания GUI панели внизу экрана. Конечно же вы можете свободно поместить ее там где вам нравится. Для начала она будет содержать в себе здоровье игрока и цветной лог сообщений.
Гораздо проще работать с GUI окнами и панелями если создать по отдельной консоли (off-screen console) для них перед началом главного цикла (main loop):
panel = libtcod.console_new(SCREEN_WIDTH, PANEL_HEIGHT)
Константа PANEL_HEIGHT определяется позже, вместе с остальными. Давайте перепрыгнем сразу к коду отображения полоски состояния («status bar»)! Он обобщен и может использоваться для полосок опыта, маны, времени перезарядки, уровня подземелья, всего чего угодно.
Полоска состоит из двух частей: один прямоугольник изменяется в размере как пропорция между значением и максимальным значением, а второй прямоугольник нужен как фон. Нужна только простая формула для расчета размера и несколько вызовов функции libtcod’а console_rect для прямоугольников.
def render_bar(x, y, total_width, name, value, maximum, bar_color, back_color): #render a bar (HP, experience, etc). first calculate the width of the bar bar_width = int(float(value) / maximum * total_width) #render the background first libtcod.console_set_default_background(panel, back_color) libtcod.console_rect(panel, x, y, total_width, 1, False, libtcod.BKGND_SCREEN) #now render the bar on top libtcod.console_set_default_background(panel, bar_color) if bar_width > 0: libtcod.console_rect(panel, x, y, bar_width, 1, False, libtcod.BKGND_SCREEN)
Чтобы было еще понятнее — значение и максимум отображаются текстом поверх полоски, вместе с подписью («Здоровье», «Мана» и т.п.).
#finally, some centered text with the values libtcod.console_set_default_foreground(panel, libtcod.white) libtcod.console_print_ex(panel, x + total_width / 2, y, libtcod.BKGND_NONE, libtcod.CENTER, name + ': ' + str(value) + '/' + str(maximum))
Теперь мы изменим главную функцию отрисовки, чтобы использовать это. Сначала определим несколько констант: высоту панели, ее положение на экране (панель у нас внизу, так что понадобится нам только Y) и размер полоски здоровья.
#sizes and coordinates relevant for the GUI BAR_WIDTH = 20 PANEL_HEIGHT = 7 PANEL_Y = SCREEN_HEIGHT - PANEL_HEIGHT
Я также изменил MAP_HEIGHT на 43, чтобы у панели было больше пространства. В конце функции render_all, замените код, который показывает состояние игрока, на код, приведенный ниже. Он переинициализирует панель в черный, вызовет нашу функцию render_bar для отображения здоровья игрока, а потом покажет панель на главной консоли (root console).
#prepare to render the GUI panel libtcod.console_set_default_background(panel, libtcod.black) libtcod.console_clear(panel) #show the player's stats render_bar(1, 1, BAR_WIDTH, 'HP', player.fighter.hp, player.fighter.max_hp, libtcod.light_red, libtcod.darker_red) #blit the contents of "panel" to the root console libtcod.console_blit(panel, 0, 0, SCREEN_WIDTH, PANEL_HEIGHT, 0, 0, PANEL_Y)
Время проверить — наша полоска здоровья выглядит суперски! And you can easily make more like it with different colors and all.
Маленький нюанс: консоль, где отрисовывается карта (con) должна быть размера карты, а не размера экрана. Теперь видно что панель занимает довольно много места. Поменяйте SCREEN_WIDTH и SCREEN_HEIGHT на MAP_WIDTH и MAP_HEIGHT когда создаете эту консоль и заполняете (blitting) ее. Это строкаcon = libtcod.console_new(. ) перед главным циклом (main loop), и первый console_blit вrender_all.
Журнал сообщений [ править ]
До сейчас все наши боевые сообщения бросались в стандартную консоль — не очень-то удобно. Давайте сделаем маленький симпатичный лог сообщений с прокруткой и встроим его в GUI панель, а сообщения сделаем цветными, чтобы игрок мог понять что вообще произошло с первого взгляда. А еще мы сделаем перенос слов!
Константы для положения и размера полоски сообщений:
MSG_X = BAR_WIDTH + 2 MSG_WIDTH = SCREEN_WIDTH - BAR_WIDTH - 2 MSG_HEIGHT = PANEL_HEIGHT - 1
Он будет правее нашей полоски здоровья и заполнит все оставшееся пространство. Сообщения будут храниться в списке, чтобы с ними было проще работать. Каждое сообщение это пара с двумя полями: текст сообщения и его цвет.
#create the list of game messages and their colors, starts empty game_msgs = []
Простенькая функция будет добавлять сообщения в список. Она будет использовать модуль textwrap из Python’а чтобы разбивать сообщение на несколько строк, если оно слишком длинное! Для этого внесем import textwrap в начало файла и создадим функцию:
def message(new_msg, color = libtcod.white): #split the message if necessary, among multiple lines new_msg_lines = textwrap.wrap(new_msg, MSG_WIDTH) for line in new_msg_lines: #if the buffer is full, remove the first line to make room for the new one if len(game_msgs) == MSG_HEIGHT: del game_msgs[0] #add the new line as a tuple, with the text and the color game_msgs.append( (line, color) )
После того, как она получила разбитое сообщение и список строк, она начинает добавлять их по очереди в лог сообщений. Таким образом, что если лог переполнится, первая строка удалится и даст место новой строчке. Работая с каждой строкой по отдельности можно легко убедиться, что лог сообщений никогда не превысит максимальную высоту.
Код отображения лога сообщений гораздо проще. Просто пробежимся по всем строкам и напечатаем их нужными цветами (прямо перед отрисовкой полоски здоровья). Посмотрите как мы получаем значения пар прямо в цикле for; эта особенность Python’а (называемая распаковка (unpacking)) позволяет писать очень cжатый код.
#print the game messages, one line at a time y = 1 for (line, color) in game_msgs: libtcod.console_set_default_foreground(panel, color) libtcod.console_print_ex(panel, MSG_X, y, libtcod.BKGND_NONE, libtcod.LEFT, line) y += 1
Время потестить! Давайте напечатаем дружественное сообщение перед главным циклом (main loop) и поприветствуем игрока в нашем подземелье судьбы:
#a warm welcoming message! message('Welcome stranger! Prepare to perish in the Tombs of the Ancient Kings.', libtcod.red)
Длинное сообщение позволит нам проверить перенос слов. Можете заменить все вызовы стандартной функции print на вызовы нашей новой функции message (все 4 раза). Я сделал сообщение о смерти игрока красным (libtcod.red), а смерти монстра — оранжевым (libtcod.orange), остальные оставил по-умолчанию. Кстати, вот список стандартных цветов libtcod. Очень удобный, если вы не против использовать уже определенную палитру цветов!
Вот и есть та красота, которой не хватало нашей игре. Теперь играть в нее гораздо приятнее даже обычным игрокам. Не дайте хардкорным игрокам обмануть вас — все любят немножко красивостей!
Обзор при помощи мыши [ править ]
Теперь добавим немного интерактивности в наш графический интерфейс (GUI). У рогаликов издавна есть традиция использовать только клавиатуру, и это конечно хорошо; но для некоторых действий, таких как выбор тайла, использование мыши гораздо удобнее. Поэтому добавим нечто вроде команды «осмотреть» («look»), которая будет показывать имя обьекта, на который игрок навел мышь! Также можно использовать ее для выбора целей для заклинаний или стрелкового оружия. Конечно, это всего лишь урок, который показывает что можно сделать, а вы можете остановиться на команде «осмотреть» («look»).
С помощью libtcod очень просто узнать положение мыши, а также клики: функция sys_check_for_event возвращает информацию как про действия клавиатуры, так и мыши: (прочитайте документацию здесь, чтобы узнать больше, и здесь, чтобы узнать больше о мыши).
Нам нужно немного переструктурировать нашу программу, чтобы использовать клавиатуру и мышь. Прямо перед главным циклом добавьте
mouse = libtcod.Mouse() key = libtcod.Key()
и сразу же после начала главного цикла , но до вызова render_all, добавьте
libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS|libtcod.EVENT_MOUSE,key,mouse)
Теперь, определим keys как глобальную переменную в handle_keys, и уберем строчку, которая вызывает libtcod.console_check_for_keypress.
Мы будем работать с полями cx и cy структуры мыши, которые обозначают координаты тайла (или клетки), над которой находится мышь. Будем использовать это в новой функции, которая будет возвращать строку с именами обьектов под мышью:
def get_names_under_mouse(): global mouse
#return a string with the names of all objects under the mouse (x, y) = (mouse.cx, mouse.cy)
Теперь нам нужно собрать список имен обьектов, которые удовлетворяют несколько условий: они под мышью, и внутри поля зрения игрока (FOV). (Иначе игрок сможет опознавать врагов сквозь стены!). Это можно сделать с помощью списковых выражений, используя условие «if».
#create a list with the names of all objects at the mouse's coordinates and in FOV names = [obj.name for obj in objects if obj.x == x and obj.y == y and libtcod.map_is_in_fov(fov_map, obj.x, obj.y)]
Теперь нужно просто соединить имена в одну строку через запятые. Python имеет клёвые функции для этого. Можем даже сделать заглавными первые буквы:
names = ', '.join(names) #join the names, separated by commas return names.capitalize()
Функция render_all может вызвать это после отрисовки полоски здоровья (health bar), чтобы получить строку, которая зависит от позиции мыши:
#display names of objects under the mouse libtcod.console_set_default_foreground(panel, libtcod.light_gray) libtcod.console_print_ex(panel, 1, 0, libtcod.BKGND_NONE, libtcod.LEFT, get_names_under_mouse())
Но стойте! Если вы еще не забыли, это пошаговая игра, и отрисовка происходит лишь раз на ход, остальное время игра заблокирована на console_wait_for_keypress. В это время (а оно почти всегда) код, написанный выше просто не будет выполняться! Мы переключимся на отрисовку в реальном врмени (real-time rendering) заменив вызов console_wait_for_keypress в handle_keys на sys_check_for_event в главном цикле .
Но разве тогда наша игра не перестанет быть пошаговой? Удивительно, но нет! Прежде чем вы начнете копаться в этом вопросе, позвольте мне сказать что мы сделали несколько изменений, которые косвенно повлияли на это.
Когда игрок не делает ход (не нажимает кнопку передвижения/атаки), handle_keys возвращает специальную строку ( ‘didnt-take-turn’ ). Вы можете заметить, что главный цикл позволяет врагам совершать свои ходы только если значение от handle_keys не ‘didnt-take-turn’ ! То есть главный цикл работает, но монстры стоят на месте. Основное различие между игрой в реальном времени и пошаговой игрой в том, что в пошаговой игре монстры ждут пока игрок сделает ход, чтобы походить самим. Логично!
Если вы раньше не сделали, вам нужно вызвать libtcod.sys_set_fps(LIMIT_FPS) перед главным циклом, чтобы ограничить скорость игры. Вам также можете посчитать, что клавиши движения реагируют слишком быстро как для пошаговой игры, где каждое нажатие должно считаться только как один ход (почитайте обьяснение раннее в Добавим движение). Просто замените вызовы console_is_key_pressed в handle_keys на вызовы вроде:
if key.vk == libtcod.KEY_UP:
И аналогично для других клавиш движения. Вот и все! Теперь вы можете с помощью мыши быстро узнать имя каждого обьекта в поле видимости.
Здесь готовый к запуску код.