Язык программирования для игр

e5315b09881172c6e47a1259cdbb995a

Сейчас мне не известен язык, на котором было бы удобно разрабатывать игры. Поэтому я пишу Кедр.

Предыдущие статьи здесь и здесь, но они частично устарели, лучше смотреть документацию.

Новые возможности

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

Внедрение зависимостей

type TextBlock =
    let get_font : String -> Font @auto

let main () =
    let text_block = TextBlock.new

let get_font @publish = { path -> font_directory.get path }

main

Частая ситуация — необходимость передавать вспомогательные объекты. Мы не хотим делать это явно, но также не хотим создавать глобальные данные. С помощью атрибутов @publish и @auto значение привязки get_font из корня файла будет передано в одноимённое поле объекта text_block. Совпадать должны как имена привязок, так и их типы.

Дерево объектов

let start_button = Button.new
start_button.text = "start"
start_button.on_press = { start_calculation }

let stop_button = Button.new
stop_button.text = "stop"
stop_button.on_press = { stop_calculation }

let stack = Stack.new
stack.items.add start_button
stack.items.add stop_button

stack

Так могло бы выглядеть создание элементов управления на условном классическом языке. Подобный код, очевидно, не желателен, поэтому существуют системы разметки, например XAML для WPF. Но для большей гибкости и простоты желательно всё же ограничиться кодом.

Stack
    Button
        text = "start"
        on_press = { start_calculation }
    Button
        text = "stop"
        on_press = { stop_calculation }

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

Поле Stack.items помечено атрибутом @dst, поэтому в него добавляются кнопки. Если бы тип содержал два @dst поля типа Option — то соответствующие по порядку кнопки были бы присвоены им.

on_press имеет тип Option, т.е. может содержать либо функцию, которая ничего не принимает и не возвращает, либо значение None. Во время присваивания замыкание типа Unit -> Unit неявно приводится к типу Option::Some.

let start_button = Button.new
let stop_button = Button.new
let stack = Stack.new

stack
    start_button
        text = "start"
        on_press = { start_calculation }
    stop_button
        text = "stop"
        on_press = { stop_calculation }

Нам могут понадобиться ссылки на объекты, которые создаются внутри выражения. Для этого мы создаём их снаружи, и используем имена привязок вместо типов внутри.

Разделённый конструктор

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

type Control
    var maybe_drawable : Option @mut = None

Мы добавляем поле к типу Control в ящике drawer, использующем конкретный отрисовщик, частью которого является Drawable.

type Rectangle
    let materials : Materials @auto

    val drawable = Drawable
        material = materials.ui
        mesh = Mesh.from_memory memory@atom

    maybe_drawable = drawable

Объект Drawable создаётся в конструкторе типа Rectangle, который неявно получает необходимый ему объект materials.

let materials @publish = app.drawer.materials

let rectangle = Rectangle.new

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

Игры

Основные языки для разработки игр это C++ и C#. Кто-то ещё добавит JavaScript, но это совсем другие игры с другими требованиями, о них не будем.

C++ был создан давно, нужна замена, обладающая всеми его возможностями и учитывающая современные достижения в дизайне языков. Кедр является такой заменой.

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

Создание временных объектов даже без сборщика мусора может быть нежелательным, поэтому в Кедре переиспользование памяти будет реализовано на уровне языка.

Концепция

Одна из особенностей Кедра — это единообразие кода в разных контекстах. Код внутри функции содержит привязки, выражения и вложенные функции. Код внутри типа — всё те же привязки, выражения и функции. При этом привязки становятся полями, а функции методами. В корне файла к ним добавляются типы и модули.

Код

Здесь есть код среды разработки. В ней пока можно редактировать код с подсветкой синтаксиса, запускать приложение, видеть ошибки компиляции. В соседнем репозитории привязка к Vulkan и отрисовщик. Всего вместе со стандартной библиотекой около 20к строк.

© Habrahabr.ru