Pull to refresh

Comments 31

Вот если серьёзно, зачем использовать функциональный язык для программирования игр? Игры оперируют некими сущностями, обладающими поведением — как в реальном мире. Само собой напрашивается объединение данных и способов взаимодействия с ними в какие-то структуры — то есть объектно-ориентированный подход.

Примечание
Если уточнять — мы имеем дело со сложными объектами, свойства которых можно группировать по определённым признаками — графика, физика, логика, и.т.д — поэтому тут даже не ООП, а ПОП, то есть прототип-ориентированное программирование подойдёт.


Отсюда вопрос: зачем идти поперёк естественного способа представления предметной области?

П.С.: Погуглил немного. Оказалось, что на лиспе кто-то сайты умудряется писать. Ещё почитал вот это обсуждение, и понял что не один имею приведённую выше точку зрения (хотя, стоит заметить, голоса «за» там тоже раздавались).
Common Lisp обладает довольно мощной объектной системой. Вообще, его сложно назвать функциональным. Он поддерживают несколько парадигм, и функциональная далеко не главная.

В частности в последующих туториалах используются классы, в сущности делающие то же самое, что и в оригинальном туториале.
Хм… Но ведь, как я понимаю, оригинальный lisp создавался как функциональный язык, с соответствующими для этого подхода целями. Объектное программирование и в прологе есть… Но зачем? Lisp, насколько я слышал, хорошо подходит для разбора текста, для обработки иерархических данных, для программирования искусственного интеллекта (как иерархической структуры). В эту сторону он развивается, в этом его задача. И использовать его для других целей — всё равно что шуруп молотком забивать.

В данный момент мне кажется, что создавать на нём игры — это всё равно, что, например, на С++ сайты писать или даже всё равно, что на visual basic для excel тетрис делать. Если я ошибаюсь — взглянул бы на примеры сколь-нибудь серьёзных проектов на lisp, интересно как сложную игровую механику возможно в скобочки упаковать.
Лисп лиспу рознь. Лисп это не какой-то конкретный язык, а целое семейство довольно разных языков объединенных некоторыми общими свойствами.
Скажем тот-же Scheme гораздо более функциональный чем Common Lisp.

Вообще насколько я знаю сейчас самый популярный диалект лиспа это Clojure.
И чего только на нем не пишут: github.com/search?utf8=%E2%9C%93&q=stars%3A%3E1+language%3AClojure&type=Repositories&ref=advsearch&l=Clojure&l=
На lisp с помощью макросов можно запилить DSL для игровой механики. Почему никто не использует для меня самого загадка, видимо думают, что сложно.
И, да, lisp не функциональный, а мультипарадигменный, причем Common Lisp вообще больше императивным тянет назвать. Насчет остальных не знаю.
Сообщество очень маленькое, популяризаторов нет, вот и не пишут. (http://lispgames.org/)

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

Про игры на лиспе есть целая книжка, кстати: landoflisp.com
А как, кстати, обстоят дела с установкой игр, написанных на CL, на пользовательские машины?
Насколько я знаю, раньше тот же SBCL тянул в бинарный файл весь образ лисп-машины, от чего бинарники, выводящие «Hello, World», получались размером с мегабайт 40. Конечно, в современных компьютерных играх 40мб — это мало, но всё же…
Сейчас все обстоит так же. Разве что теперь есть roswell который упрощает процедуру.

Если размер бинарника важен, можно воспользоваться другой реализацией. Кажется, CLISP умеет делать маленькие бинарники.

Впрочем, лично мне кажется что статическая линковка вполне себе хорошая идея для дистрибуции.
Можно и не на лиспе запилить DSL для игровой механики, беда в том что и этого никто не делает, или мне не попадалось, если есть хорошие примеры с радостью бы на них поглядел, потому что мы уже пол года пилим свой DSL для таких целей и хотелось бы посмотреть как люди решают вопросы которые у нас возникают, если надо могу рассказать свой опыт. Пример того что на данный момент достигли. Решение фаториала. Пусть вас не пугает слово quest изначально планировалось на нем квесты писать, хорошо бы заменить на superblock или state
block Step(current, value)
{
exit mul
{
condition: current!=0
action: add(current, -1) as newCurrent, mul(value, current ) as newValue
return: newCurrent, newValue
}

exit finish
{
condition: current == 0
return: value
}
}

block ShowResult(result)
{
exit show
{
action: print(«result:»), print(result)
}
}


quest Begin()
{
start: Step(5, 1 )

exit finish

Step
{
mul -> Step(::newCurrent, ::newValue)
finish -> ShowResult(::value)
}

ShowResult
{
show->finish
}
}
Вы правильно сказали. Игры отражают реальный мир. А реальный мир всегда находится в движении, всегда что-то меняется и влияет на всё окружающее. Прямо эффект бабочки. А для таких случаев отлично подходит реактивное программирование, которое очень удобно и лаконично реализуется на ФЯП.
Да. Но игры итеративны как правило, существуют в рамках потока событий, обновляясь от кадра до кадра… Впрочем, возможно, я совсем не понимаю в функциональном программировании. Исходя из универских лекций (да, я только оттуда знаю и из общения с однокурсником), мне всегда казалось что оно не очень-то дружит с циклами и с хранением каких-либо состояний в течение обработки. С передачей дальше на обработку — да. А вот с интеративной обработкой и событиями — не очень.
Зато хорошо дружит с рекурсиями ;)
Вы имеете в виду имитацию цикла с помощью рекурсии? Если да — то ведь это и есть эмуляция одного подхода с помощью другого подхода непонятно зачем.
При чем тут функциональные подходы в программирование и Lisp? Сейчас на любом мейнстрим языке можно писать в функциональной парадигме. Лямбда функции и замыкания появились нынче даже в с++, от этого же вы не скажете, что с++ язык не для разработки игр?

Вам выше уже указали, что лисп (конкретно я говорю про Common Lisp) не является чистым функциональным языком программирования, как например Хаскелл. На CL вы можете писать полностью в императивном стиле, со всеми вытекающими.
Ну, dezconnect упомянул рекурсию на мой комментарий про итеративное обновление состояния игрового мира. Вот я и подумал, что он её упомянул к тому, что можно эмулировать циклы — поэтому заметил, что это странно с моей точки зрения эмулировать императивный подход в рамках функционального.

По поводу того, что lisp уже не является чисто функциональным я понял, комментарий читал. Но, тем не менее, мне непонятно зачем вводить разработку игр в перечень задач для языка (ладно, пусть языка, расширенного поддержкой других парадигм), который на это не было ориентирован изначально и у которого, как мне кажется, исторически сложился другой перечень задач… Нет, как, догадка есть: видимо потому, что есть программисты, которые знают lisp и хотят писать игры. Но:
— во-первых, нужно смотреть сколько таких программистов.
— во-вторых, нужно понять почему этим программистам трудно будет выучить какой-то из языков, который исторически использовался для разработки игр.
— в-третьих, нужно оценить сколько кода придётся портировать (байндить, скорее всего) на lisp с других языков.
— ну и, наконец, последнее: мне кажется что программисту на lisp, который привык в основном использовать функциональную парадигму (иначе не ясно зачем он использовал именно lisp) всё равно придётся переходить на другие парадигмы в рамках того же lisp — что, как мне кажется, по усилиям не намного сложнее изучения какого-нибудь несложного языка из используемых для разработки игр раньше.

Исходя из этих размышлений, использование lisp как языка для программирования игр, как на меня, не самая лучшая идея. Эти усилия было бы эффективные потратить на какие-то улучшения lisp для задач, на которые он ориентирован.
А если про геймдев — тот же C# выучить можно за неделю — и сразу делать игры на Unity. Это точно намного легче, чем специально делать Unity на lisp.
Common Lisp как и C#, как и C++ языки общего назначения. А потому говорить о «сложившемся перечне задач» некорректно.
Все равно что говорить: «На си нужно писать только ядра операционных систем».
Я не говорил «только». Я перечислил четыре пункта, исходя из которых сделал для себя вывод, что lisp не особо походит для написания игр.
Каждый преследуют свои цели при выборе языка разработки. Куда лучше тратить свои усилия тоже думаю каждый сможет определить для себя сам.

А выбор лиспа в качестве языка программирования в общем (в том числе и игр), может вас весьма удивить тем что вы начинаете видеть решения задач в новом свете.

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

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


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

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


Верно, но движки имеют разную степень развития для разных языков.

Но в любом случае материалы про разработке игр на лиспе могут разжечь в ком то интерес к изучению лиспа именно сегодня :)


Это да. Если статья писалась с такой целью — конечно. Я, например, чуть больше узнал благодаря этой статье и чтению вики в процессе дискуссии.
Вот если серьёзно, зачем использовать функциональный язык для программирования игр?

Затем, что в ряде случаев состояние следующей итерации игрового цикла можно представить как функцию от текущей итерации. А дальше — радостно использовать все прелести функционального программирования для декомпозиции этой функции.

(что характерно, это не мешает декомпозиции игры на сущности с поведением)
А почему вы называете Common Lisp функциональным языком? Мне вот это не кажется верным — скорее уж Scheme с более функциональным уклоном, а CL как раз с более императивным.

Помимо прочего, «стандартная» ООП-система без множественного наследования не очень хорошо позволяет описывать сущности во многих играх, и поэтому в частности OpenRA использует систему из паттерна «поведение» и композиции этих «поведений» в игровых объектах. На функциональном языке это также прекрасно можно смоделировать без какого-либо онтологического кризиса.
А можете привести пример, как бы мог выглядеть код с использованием (имитацией) прототипного программирования для описания игровых классов в lisp? Интересуют такие вещи, как реализация полиморфизма (так как в играх объекты очень разнотипные бывают, и при этом все существуют на сцене), инкапсуляция (так как объекты бывают огромные и хочется чтобы сам язык разделял данные на слои в смысле прав на работу с ними)… ну, наследование должно быть очевидно, если заявляется объектная парадигма.

Спрашиваю потом, что, как мне кажется, получится код, универсальный для любого объектного языка и потому в данном случае не более удобный для работы конкретно в рамках common lisp.
Если под «прототипным программированием» вы имеете в виду модель ООП наподобие той, которая используется в JS — то нет, CLOS такое не поддерживает напрямую (конечно же, можно что-то эдакое кастомное изобрести, но мне не видится практического смысла в подобных экзерсисах).

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

Да, объектно-ориентированный код на CL не отличается радикально (на мой взгляд) от других языков, поддерживаемых множественное наследование — например, от Clojure или Scala. Но никаких сложностей с реализацией, например, двойной диспетчеризации вы не испытаете — а в то же время в мейнстримных компилируемых языках это целая проблема.
Туповатый пример на коленке
;;; http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html

(defpackage #:ultra-game
  (:use :common-lisp)
  (:export :say-my-name :ship))

(in-package :ultra-game)

(defclass point ()
  ((x
    :initform 0
    :accessor point-x)
   (y
    :initform 0
    :accessor point-y)))

(defclass entity (point)
  ((name
    :initarg :name
    :initform (error "Must supply a name")
    :accessor entity-name)))

(defgeneric say-my-name (entity)
  (:documentation "Print entity name to the *standard-output*"))

(defmethod say-my-name ((entity entity))
  (format *standard-output* "My name is ~a~%" (entity-name entity)))


(defclass engine ()
  ((angle
    :initform 0
    :accessor engine-angle)
   (acceleration
    :initform 9000
    :accessor engine-acceleration)))

(defclass ship (entity engine)
  ((cost
   :initform 0)))

(defmethod say-my-name ((ship ship))
  (with-slots (name acceleration) ship
    (format *standard-output* "Colonial spaceship ~a; Acceleration: ~a~%" name acceleration)))

;;; usage example

(in-package :cl-user)

(let ((ship (make-instance 'ultra-game:ship :name "Foo")))
  (ultra-game:say-my-name ship))

;; output:
;; Colonial spaceship Foo; Acceleration: 9000
;; My name is Alarm!


;; note '::'
(let ((entity (make-instance 'ultra-game::entity :name "Alarm!")))
  (ultra-game:say-my-name entity))

;; output:
;; My name is Alarm!


Спасибо. Здесь можно уже предметно говорить. По порядку:

1. В примере вообще не понятно как в lisp задавать агрегацию (у меня этот вопрос не звучал — но всё равно непонятно). Из-за этого point является базовым классом для entity, или это просто кривой пример? Как сделать так, чтобы entity мог содержать несколько point, задающих векторные характеристики entity — ускорение, скорость, и.т.д.
Такое же странное место вот тут — defclass ship (entity engine). Почему корабль наследует двигатель и сущность? Сущность-то правильно — но двигатель? Должна быть агрегация, а не наследование.

2. В приведённом примере не ясно как будут выглядеть сколь угодно сложные алгоритмы в реализациях методов — с проверками и ветвлением алгоритмов. Как я понял, в скобках при defclass за именем класса указывают базовые классы (defclass ship (entity engine)), а метод объявляются вне классов. Вот пример метода (отсюда), с одним ветвлением:

Код на common lisp
(defmethod initialize-instance :after ((account bank-account) &key)
(let ((balance (slot-value account 'balance)))
(setf (slot-value account 'account-type)
(cond
((>= balance 100000) :gold)
((>= balance 50000) :silver)
(t :bronze)))))


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

Код на С++
// Как я понял, &key — это нечто, содержащее все параметры метода… тогда это будет словарь
typedef std::map <std::string, void *> KeyType;

initialize-instance::after(KeyType &inKey) {
if(balance >= 100000)
account-type = gold;
else if(balance >= 50000)
account-type = silver;
else
account-type = bronze;
}

// Запись через тернарные операторы
// Тернарный оператор задаёт проверку в следующем формате:
// (condition)? (return-if-true): (return-if-false);
//
initialize-instance::after(KeyType &inKey) {
balance = (balance >= 50000)? (balance >= 100000)? gold: silver: bronze;
}


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

П.С.: Нашёл в интернете тетрис, написанный на lisp. Там есть несколько методов с логикой больше двух строк. Например:

Метод, рисующий блочок
(defun draw-block (block x y)
«Print a block on (x,y) coordonates.»
(loop for part in (car block)
as x0 = (+ (* (+ x (car part)) 20) +game-left-corner-x+)
as y0 = (+ (* (+ y (cdr part)) 20) +game-left-corner-y+)
as r = (first (cdr block))
as g = (second (cdr block))
as b = (third (cdr block))
do (draw-square x0 y0 r g b)))


Выглядит читабельно. Достаточно ясно что происходит, но… записано словно на С-подобном языке. А если можно записать на нём — зачем мучится со скобками и стилем оформления кода, унаследованным от функционального прошлого? Чем именно common lisp лучше чем С-подобные языки для написания игр, чтобы менять свой стиль программирования под околофункциональный и писать игры на нём?
Нравится писать в императивном стиле — пишите.
И снова вы пытаетесь высказывать какие-то, простите, маргинальные идеи: «функционального прошлого», «мучится со скобками». Риторический вопрос: а зачем мучиться с C-подобными фигурными скобками и стилем оформления, унаследованным от императивного прошлого?

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

Так вот, это неверно. CL — вполне современный язык программирования, а функциональное прошлое (я, честно говоря, не понимаю, где вы его в посте и примерах увидели, потому как CL — это императивный по большей части язык) — это скорее плюс, и я бы даже предпочёл побольше функциональных возможностей как в стандартной библиотеке CL, так и в языках, на которых я пишу каждый день.

Ну и, да, программисты на CL пишут на лиспе именно потому, что он им нравится. Нет тут никаких «мучений» и «изменения стиля программирования на околофункциональный».
Вы как-то эмоционально восприняли то, что я написал… Под функциональным прошлым я не подразумевал что функциональный подход это нечто из прошлого, а имел в виду что сам язык common lisp образовался из функционального языка lisp (так же как, например, С++ образовался от С) и потому унаследовал часть его особенностей организации кода. Про «мучения» я говорил в контексте людей, которые переходят на common lisp с других языков с целью, например, написания игр — людей вроде меня. Для меня было бы мучительно (возможно, только в начале — но тем не менее) парсить глазами концы выражений вроде: (t :bronze))))) с шестью скобками в конце при наличии логики двух проверок в рамках этих выражений.

У меня не было желания как-то унизить язык common lisp или людей, пишущих на нём. Я просто считаю, что на этом языке неудобно писать конкретно игры, и потому попросил указать на причины, по которым, например, я мог бы выбрать этот язык в качестве такового… Не буду тут повторять свои тезисы про сложившиеся специализации для разных языков.

Думаю, эту дискуссию стоит закончить… В общем и целом, я не особо имел право её начинать. У меня не было серьёзного работы с common lisp (кроме того, что я успел почитать в течение дискуссии и лабораторных в ВУЗе). Ко всему, я, кажется, единственный, кто имеет подобную точку зрения среди комментаторов статьи — поэтому трудно оценить объективность моих взглядов. Ещё раз просите если что не так написал.
Sign up to leave a comment.

Articles