Перейти к основному содержимому

Raylib в Pharo

Разработчику

Raylib в Pharo

См. также: Morphic · Pharo · SmallShooter · SmallPong · Справочник §16 — FFI · Игры на C++ · Pygame — Ping Pong · Практикум игр


Что такое Raylib?

Raylib — лёгкая C-библиотека для 2D/3D-игр и мультимедиа. Она даёт:

  • создание OS-окна;
  • отрисовку примитивов, текста, текстур;
  • загрузку звуков и музыки;
  • опрос клавиатуры и мыши.

Девиз проекта — "just code": минимум настроек, один нативный файл raylib.dll / libraylib.so. Сайт: raylib.com.

Важно для Smalltalk: Raylib не входит в стандартный образ Pharo. Подключение идёт через FFI (Foreign Function Interface) — механизм вызова C-функций из Smalltalk. Pharo вызывает, например, InitWindow, DrawText, CloseWindow из загруженной DLL.

Термины:

  • FFI / uFFI — Unified FFI в Pharo; переносимый слой поверх libffi; см. §16 справочника.
  • Call-out — вызов из Smalltalk в C (наружу из image).
  • Binding (биндинг) — Smalltalk-обёртка с методами для функций C-библиотеки.
  • pharo-cig — генератор биндингов из заголовков .h через libclang.
Когда читать эту статью

После хотя бы одного Morphic-практикума (крестики-нолики, SmallPong). Иначе сначала непонятна разница между step и C-циклом while.


Зачем Raylib, если есть Morphic?

КритерийMorphicRaylib
ОкноВнутри среды Pharo (World)Отдельное OS-окно
ГрафикаdrawOn:, цветные прямоугольникиТекстуры, шейдеры, 3D
ЗвукОграниченно / сторонние libsВстроенный audio API
ЗависимостиНетНативный raylib
Учебная цельООП, MVC, live UIFFI, нативный game loop
Практикумы разделаSmallPong, SmallShooter, TTTРасширение для "настоящего" рендера

Morphic — лучший первый шаг: весь код в image, Debugger доступен между кадрами, модель игры отделена от UI (SmallPong).

Raylib — когда нужна производительная 2D/3D-графика или осознанное сравнение с:


uFFI — основа вызовов

Pharo использует Unified FFI (uFFI). Минимальный call-out к Raylib:

Object subclass: #RaylibTutorial
classVariableNames: ''
package: 'RaylibTutorial'

RaylibTutorial class >> initWindow: width height: height title: title
^ self ffiCall: #(void InitWindow(int width, int height, char *title))
library: (self raylibLibrary)

RaylibTutorial class >> raylibLibrary
^ 'raylib'

Разбор:

ФрагментСмысл
ffiCall:Сообщение uFFI; аргумент — литерал с сигнатурой C-функции
#(void InitWindow(...))Тип возврата void, имя функции, типы параметров
char *titleУказатель на C-строку; Smalltalk передаёт ByteString / ExternalString
library: (self raylibLibrary)Имя DLL/SO; VM подставит .dll, .so, .dylib
'raylib'Базовое имя файла raylib.dll на Windows

Подробная книга: Calling Foreign Functions from Pharo (uFFI booklet).

На практике сотни ffiCall вручную не пишут — берут готовый пакет или генерируют код через pharo-cig.


Проекты биндингов в Pharo

ПроектОписаниеСсылка
pharo-raylibБиндинги сообщества pharo-ciggithub.com/pharo-cig/pharo-raylib
pharoRaylibРучные обёртки, RaylibConstantsgithub.com/Zenchess/pharoRaylib
pharo-cigГенератор FFI из C/C++github.com/pharo-cig/pharo-cig

pharo-cig читает raylib.h и создаёт пакет Raylib с тысячами функций. Для графических библиотек включают useMainThread, потому что OpenGL/OS-окно ожидают главный поток процесса.

Пример генерации (упрощённо)

(CigCLibraryGenerator new)
prefix: 'Ray';
packageName: 'Raylib';
libraryName: 'raylib';
import: '/path/to/raylib/src/raylib.h';
cIncludePath: '/path/to/raylib/src';
useMainThread;
generate

После generate в image появляются классы вроде RaylibLibrary с методами для каждой экспортированной C-функции. Поверх них можно писать Smalltalk-обёртки с удобными именами (initWindowWidth:height:title:).


Установка Raylib и DLL

Windows

  1. Скачайте 64-bit MSVC сборку Raylib 5.x с GitHub releases.
  2. Положите raylib.dll рядом с .image Pharo или в каталог из PATH.
  3. Загрузите пакет биндингов через Metacello (скрипт в README репозитория).

Linux

sudo apt install libraylib-dev

Проверьте RaylibLibrary>>unixLibraryName — обычно ^ 'libraylib.so'.

macOS

brew install raylib

Укажите путь к .dylib в finder-классе FFILibrary, если VM не находит библиотеку автоматически.

Версии должны совпадать

Биндинги привязаны к версии заголовков (например Raylib 5.0). Несовпадение raylib.dll и сгенерированных сигнатур рушит VM без Smalltalk-исключения — всегда сверяйте README проекта.


Игровой цикл в Pharo и Raylib

Raylib ожидает классический цикл на C:

InitWindow(800, 600, "Pharo Raylib");
while (!WindowShouldClose()) {
BeginDrawing();
ClearBackground(RAYWHITE);
DrawText("Hello", 190, 200, 20, LIGHTGRAY);
EndDrawing();
}
CloseWindow();

Разбор C-цикла:

ШагНазначение
InitWindowСоздать окно 800×600
WindowShouldClosetrue, если пользователь закрыл окно
BeginDrawing / EndDrawingПара для кадра
ClearBackgroundЗаливка фона цветом RAYWHITE
DrawTextТекст на экране
CloseWindowОсвободить ресурсы

В Smalltalk те же шаги — сообщения к FFI-классу. Константы RAYWHITE, LIGHTGRAY — в shared pool RaylibConstants (пакет pharoRaylib).

Обёртка в Pharo (псевдоструктура)

RayGame class >> run
[ self initWindow.
[ self update.
self draw.
self shouldClose ] whileFalse.
self closeWindow ] ensure: [ self cleanup ]

Сравнение с Morphic:

MorphicRaylib
startStepping + stepwhile + Draw*
stepTime = 16SetTargetFPS(60) в Raylib
changedEndDrawing

Потоки: UI Pharo исторически однопоточный; Raylib-окно на main thread. Отладка "внутри" DrawText может блокировать морфы Pharo. Логику игры держите в отдельных объектах (как ShooterGame в SmallShooter) — меняется только слой отрисовки.


Сравнение с практикумом SmallShooter

SmallShooter — тот же жанр без Raylib:

АспектSmallShooter (Morphic)Raylib
ЦиклShooterGameMorph>>stepwhile !WindowShouldClose
СпрайтыRectangleMorph, drawOn:DrawRectangle, текстуры
ОтладчикМежду кадрами в PharoСложнее при FFI-crash
ЗависимостиНетDLL + биндинги

Портирование: класс ShooterGame (модель) можно оставить; заменить только морф на FFI-адаптер, который в draw читает координаты из модели и вызывает DrawRectangle.

Raylib даёт больше FPS и эффектов на тяжёлых сценах; Morphic глубже учит Smalltalk. Оба подхода укладываются в игровой практикум как разные слои одной дисциплины.


Загрузка пакета через Metacello

Точный скрипт — в README репозитория. Общий шаблон:

Metacello new
baseline: 'Raylib';
repository: 'github://pharo-cig/pharo-raylib:main';
load

После загрузки:

  1. Smoke test из документации пакета (открыть окно, нарисовать текст).
  2. Свой класс MyRayGame с методами update, draw, run.
  3. Save as копии image перед экспериментами.

Подробнее о Metacello — статья Pharo.


Ограничения и риски

РискПояснение
Краш VMНеверная сигнатура FFI → падение всего образа, не Error
ПотокиRaylib + Morphic UI — согласовать main thread
DeployVM + image + raylib.dll, не один .exe
ДокументацияМеньше, чем у Morphic-практикумов раздела
ОтладкаC-level crash не попадает в Smalltalk Debugger

Рекомендация: чистый образ + копия .image перед FFI; для учёбы достаточно Morphic.


Связь с другими темами


Что дальше

ШагДействие
1Пройти SmallPong на Morphic
2Установить Raylib + один пакет биндингов
3Запустить demo из репозитория
4Портировать модель из SmallShooter на FFI-отрисовку