Pull to refresh

Common Lisp SDL2 туториал

Reading time 4 min
Views 15K
SDL2 — отличная библиотека, но туториалов по ней не очень много.
Common Lisp — отличный язык, но статей по нему катастрофически мало.
Думаю этого вполне достаточно, для того чтобы написать этот цикл статей.

Почему я выбрал общелисп? Что ж, на вкус и цвет как говорится.
Впрочем, причины есть:



Фактически эта серия — адаптация туториалов от Lazy Foo:
lazyfoo.net/tutorials/SDL/index.php

В этой статье я не буду детально объяснять как ставить все необходимое ПО, но
если это кому-то действительно интересно, пожалуйста, напишите об этом
в комментариях.

Для установки SDL2, воспользуйтесь советами Фу: lazyfoo.net/tutorials/SDL/01_hello_SDL/index.php.

Что касается лиспа, я пользуюсь реализацией www.sbcl.org и www.quicklisp.org.
На данный момент работоспособность проверялась только в ней.

Дальше я использую библиотеку cl-sdl2: github.com/lispgames/cl-sdl2.
Так уж вышло, что поддержка некоторого функционала из SDL2, например
surfaces, в ней реализована не полностью. Но это не беда, я дописываю
нужный функционал по мере продвижения по туториалам. Поэтому не
ставьте версию из quicklisp’а, а сразу клонируйте мастер ветку в ~/quicklisp/local-projects.

Предполагается, что вы минимально понимаете синтаксис лиспа и не пугаетесь от обилия скобок.
В качестве основного средства взаимодействия с лисповым окружениям я буду использовать slime.
Вы еще не пробовали spacemacs? Тогда мы идем к вам!

Тем кому не нужна лирика, а нужен код: github.com/TatriX/cl-sdl2-tutorial

Белый экран жизни


Начнем с того, что просто заставим SDL2 показать нам окошко, залитое белым цветом.

Очевидно, нам понадобится сам библиотека cl-sdl2. Подключим её:
CL-USER> (ql:quickload :sdl2)

Не будем усложнять себе задачу и создадим окошко фиксированного размера.
Для этого объявим глобальные (и более того специальные) переменные для ширины и высоты нашего окошка, ведь магические числа — зло.
(defparameter *screen-width* 640)
(defparameter *screen-height* 480)

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

Незатейливо обзовем нашу функцию main и добавим ей опциональный
именованный параметр, чтобы мы могли регулировать время через которое
окно будет закрываться. По умолчанию двух секунд нам за глаза.
(defun main (&key (delay 2000))

Затем нам нужно инициализировать sdl2, сказав библиотеки какие
подсистемы мы хотим использовать. Для этого cl-sdl2 предоставляем нам удобный макрос:
  (sdl2:with-init (:video)

Мы хотим использовать только подсистему вывода графики, поэтому
передадим символ :video. Вы спросите: «как я должен был догадаться,
что передать нужно именно :video?» Отвечаем: cl-sdl2 преобразовывает
SDL_ константы в соответствующие символы. Например,
мы можем открыть документацию по методу SDL_Init и посмотреть
доступные флаги: wiki.libsdl.org/SDL_Init#Remarks
Чтобы получить необходимый символ отрежем от имени константы SDL_INIT_ и
преобразуем флаг в нижний регистр.

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

Отлично, дальше создадим окно:
 (sdl2:with-window (window :title "SDL2 Window" :w *screen-width* :h *screen-height*)

Это очередной with макрос. В этот раз первым элементом списка
аргументов макроса будет символ window, через который в теле
макроса мы и будем обращаться к созданному окну. Именованные
параметры :title, :w, :h думаю вполне очевидны и не нуждаются в
объяснения.

С окном все понятно, но теперь мы хотим залить получившееся окно белым
цветом. Одним из вариантов реализации задуманного будет использование
«поверхностей», они же surfaces. По сути, поверхность это структура
содержащая пиксели некоторой области, используемая при программном
рендеринге. Например, мы можем получить поверхность нашего окна:
      (let ((screen-surface (sdl2:get-window-surface window)))

и залить его белым цветом:
        (sdl2:fill-rect screen-surface
                        nil
                        (sdl2:map-rgb (sdl2:surface-format screen-surface) 255 255 255))

Первый аргумент — прямоугольник, который мы хотим залить. Если его не
передать, будем заливать всю область. Зачем несчастный #fff
записывать таким сложным образом? Все дело в разнообразии форматов
пикселей, экранов и тому подобного. А так как SDL2 библиотека
кросcплатформенная, для применения всех необходимых преобразований
используются различные функции, как например map-rgb в данном случае.

Залить окно залили, но этого недостаточно. Теперь нам нужно вежливо попросить библиотеку обновить наше окошко:
        (sdl2:update-window window)

Учитывая что весь так долго описанный процесс пройдет за доли секунды,
а мы все-таки хотим насладится полученным результатом, попросим sdl подождать немного:
(sdl2:delay delay)
Ну и самое главное:
)))


Вот собственно и все. Осталось запустить наше творение, и надеятся, что мы не вылетим в отладчик:
CL-USER> (main)




На всякий случай,
исходник полностью
(defparameter *screen-width* 640)
(defparameter *screen-height* 480)

(defun main (&key (delay 2000))
  (sdl2:with-init (:video)
    (sdl2:with-window (window :title "SDL2 Window" :w *screen-width* :h *screen-height*)
      (let ((screen-surface (sdl2:get-window-surface window)))
        (sdl2:fill-rect screen-surface
                        nil
                        (sdl2:map-rgb (sdl2:surface-format screen-surface) 255 255 255))
        (sdl2:update-window window)
        (sdl2:delay delay)))))



Честно говоря, я планировал в одной статье рассказать сразу про первых три туториала, но как-то уже и так много текста вышло.

Для нетерпеливых, еще раз ссылка на код туториалов (на момент написания статьи 16 штук):
github.com/TatriX/cl-sdl2-tutorial

Надеюсь это было интересно и познавательно.
Only registered users can participate in poll. Log in, please.
Нужно ли больше лиспа на хабре?
78.65% Конечно нужно! 151
13.02% Унеси свои скобки туда, откуда ты их выкопал 25
8.33% Кто здесь? 16
192 users voted. 28 users abstained.
Tags:
Hubs:
+13
Comments 31
Comments Comments 31

Articles