Lua для игр и не только. Часть 3
Беря все лучшее из обоих миров
Мартин Страйчер, главный редактор Linux Magazine о Lua
Сегодня мы подробно остановимся на принадлежности языка Lua, а также на некоторых его особенностях. Многие его называют процедурным, это и так и не так одновременно. А для начала нам нужно разобраться с тем, что же подразумевается под современным понятием высокоуровневых языков, где слабые места в принятых соглашениях.
Основную разницу между компилируемыми и интерпретируемыми языками можно описать простыми словами так: в случае компилируемых языков перевод программы в понятную для машины форму осуществляется сразу же по завершении ее создания, а программа на интерпретируемом языке транслируется в машинные коды лишь при ее выполнении. Каждой команде интерпретируемого языка в коде интерпретатора соответствует подпрограмма, созданная с использованием поддерживаемых аппаратной частью компьютера средств. Когда эта команда встречается в коде, связанная с ней подпрограмма запускается, и необходимые действия выполняются. При этом все работает так, что пользователь (программист) может подумать: команды проделываются напрямую компьютером, хотя на самом деле — интерпретатором или виртуальной машиной (есть такой термин).
Для примера приведем широко известную и любимую всеми технологию Flash. Flash-плеер — виртуальная машина языка ActionScript. При этом есть и понятие флеш-ассемблера, то есть если в коде мы напишем a+b, то это будет соответствовать операциям: добавить в стек первую переменную, прочитать ее значение, добавить в стек вторую переменную, прочитать ее значение, сложить значения в стеке (на флеш-ассемблере это так и происходит). Для пользователя все это скрыто, он просто набирает «a+b». Перед исполнением языки должны быть переведены в понятную для машины форму, для чего производится их трансляция либо на язык ассемблера, либо сразу в машинный код, либо же взаимодействие предусматривает более сложную модель, когда один язык базируется на другом и так далее. Этим и проявляется различие между компилируемыми и интерпретируемыми языками. Хотя универсальные языки, такие как С/С++/Java принято считать низкоуровневыми и, соответственно, компилируемыми, это условное соглашение, особенно для Java. Под низкоуровневыми или компилируемыми языками очень часто понимаются мощные и универсальные.
О софизмах…
Современные интерпретируемые языки, куда входит множество скриптов (языков сценариев) принято считать высокоуровневыми, причем довольно часто ставят знак равенства между понятиями «высокоуровневый» и «объектно-ориентированный». Это сравнение больше напоминает софизм сродни: грузовик — автомобиль, «Мерседес» — автомобиль, значит, грузовик и «Мерседес» — одно и тоже. Иногда так оно и бывает:).
Объектно-ориентированное программирование (ООП) — концепция, пришедшая на смену структурному или процедурному программированию, и получившая широкое распространение только с середины 1980-х гг. с разработкой таких языков, как Smalltalk, C++ и Object Pascal. В качестве объектов в ней рассматриваются структуры данных, являющиеся множеством свойств и методов (проще говоря, переменных и функций). На самом деле в рамках ООП ничего особенно нового придумано не было, просто произошло определенного рода терминологическое, а после и концептуальное «очеловечивание» программирования как такового, помимо этого были решены проблемы с большими по объему разработками, которые уже представлялись на уровне взаимодействующих структур. Появилось понятие классов, а также три основных столпа-принципа ООП: абстракция, инкапсуляция, наследование. В середине-конце 90-х этого оказалось мало. Новой ласточкой явилась компонентная объектная модель COM, которая изначально была придумана для объединения в единую структуру нескольких документов различных типов и происхождения. Но после все переросло на другой уровень, то есть те или иные блоки программы либо программно-аппаратного комплекса могут реализовываться и функционировать в различных средах, писаться на разных языках, самое главное — это реализация общения между ними. Например, в магазине имеется множество кассовых аппаратов, и на самом деле все равно, как реализована их работа, главное — подключить их в сеть и организовать общение с ними (передавать и получать данные). Модель COM породила новую концепцию, которую принято называть интерфейсно-ориентированным программированием (ИОП). То есть подразумевается взаимодействие между интерфейсами.
В середине 2000-х была широко развита новая идея — многоязыковое программирование. Оно является следствием ИОП, а также множества сопутствующих технологий, например, связанных с созданием переносимого кода, который бы мог функционировать на компьютерах с различными процессорами и операционными системами.
Многоязыковое программирование позволяет расширять возможности одного языка программирования за счет другого, или, говоря иначе, тут понимается способность кода, написанного на разных языках, работать совместно. Дело в том, что ситуация с ИОП родила достаточно интересную проблему с необходимостью взаимодействия нескольких платформ, следовательно изначально были необходимы языки с высокой степенью переносимости. Первой ласточкой стал Java, который был создан в 1991 году, причем изначально он предназначался для небольших задач — программирование микроконтроллеров, а на самом деле оказалось, что его можно применять и в большом масштабе — для Интернета, а также для взаимосвязи между компьютерами с различными процессорами и операционными системами. В Java переносимость осуществляется за счет трансляции кода в специальный байт- код, который в свою очередь легко читается и приводится в исполнение виртуальными машинами Java (JVM — Java Virtual Machine). То есть, Java- программы запускаются в любой среде, где функционирует JVM.
Microsoft с конца 90-х стала развивать собственную технологию .NET Farmework, при этом как в ее рамках, так и в Visual Studio была реализована концепция не зависящей от языка среды исполнения (CLR), то есть вам, по существу, сейчас все равно на чем программировать в рамках того же Visual Studio. Выбор остается по функциональным и синтаксическим предпочтениям. В качестве альтернативы байт-коду Java был предложен промежуточный язык-ассемблер MSIL («ассемблер» следует воспринимать как «сборщик», например, мы уже говорили о флеш-ассемблере). MSIL и обладает всеми необходимыми свойствами переносимости, о которых упомянуто выше. MSIL превращается в исполняемый код при использовании JIT-компилятора (JIT — Just-in-time). При выполнении .NET программы система CLR активирует JIT-компилятор, превращающий MSIL в код для данного процессора, но с ключевым ноу-хау — коды частей программы преобразуются по мере того, как в них появляется потребность. Основным языком для реализации платформы .NET Framework является С#. В дополнение к MSIL при компилировании С#-программы вы получаете еще один важный компонент — метаданные, которые описывают данные, используемые вашей программой.
С# своим появлением решал и еще одну проблему, которая в других языках программирования стала камнем преткновения, а именно — утечка памяти. То есть у программистов появилось больше времени для того, чтобы концентрировать внимание на выполнении задач проектов вместо того, чтобы тратиться на реализацию правильного управления ресурсами. В Visual Studio сегодня вы можете в рамках одного кода использовать сразу несколько языков, например, создав проект на Visual C#, вы можете туда вставлять блоки на С++, Visual Basic и т.п. В начале-середине 90-х многоязыковое программирование также имело место, правда, в несколько другом представлении, например, писав программу на Delphi, вы могли вставлять фрагменты на ассемблере.
Таким образом, нынешнее состояние таково:
. Все современные языки являются в той или иной степени высокоуровневыми интерпретируемыми (включая C# и Java).
. Основной плюс интерпретируемых языков — высокая степень переносимости.
. Работа с интерпретируемыми языками подразумевает цикл: написание->>>запуск.
. Современные интерпретируемые языки должны иметь решения в области управления памятью.
В более неформальной среде все новации отображены в языке Lua, который как мы уже говорили, является встраиваемым в какое-либо API (наиболее часто можно встретить варианты его взаимодействия с C, C++, C#, Java, Python), и обладает всеми свойствами высокоуровневых языков, а именно: простой синтаксис, переносимость, управление памятью (мусороуборщик), метаданные, динамический контроль типов.
А сколько их, «ориентированных», бывает?
У нас почему-то очень глобально относятся к понятиям ориентированности того или иного языка. На самом деле, это просто показатель, не более того:
. Объектно-ориентированный.
. Компонентно-ориентированный.
. Интерфейсно-ориентированный.
. Data-oriented.
. Expression-oriented.
. …
Естественно, у каждого варианта есть своя специфика. Внедрение доктрины ООП хорошо объясняет суть появления классов как отдельных структур, но по существу ничего нового изобретено не было.
Так зачем нужен Lua?
Представьте себе сложный игровой проект. Если вы его будете реализовывать на компилируемом языке, то это отнимет много времени, которое будет тратиться не только на написание и отладку, но и на правильное распределение ресурсов и т.п. Эта ошибка достаточно часто встречается у начинающих разработчиков, которые только думают(!), что могут правильно обращаться с ресурсами и алгоритмами. Поэтому, например, никто не может переплюнуть по оптимизации движков Blizzard’вцев, которые являются гуру в этом вопросе.
А в случае, когда за низкоуровневым языком вы закрепите ключевую функцию — реализацию общения с железом, а все остальное опишете сценариями, например, на Lua, программирование в котором гораздо проще, то время разработки существенно сократится. При этом, если уж говорить о создании игр под Windows, то у разработчиков есть еще один козырь в виде низкоуровневого API DirectX, через который и происходит общение с железом. То есть разработка игры может быть и не такой сложной, как это описывается в некоторых книгах трех-(и больше) летней давности.
Если говорить о современных тенденциях и о том же вышеупомянутом MSIL, достаточно указать в качестве яркого примера на Microsoft XNA Game Studio — «игровое расширение» для Visual C#, на базе которого можно делать игры сразу для трех платформ: PC (Windows), игровой приставки Xbox 360 (Xbox) и портативного устройства Zune. Мы уже писали об XNA в «КГ». И вообще, в последнее время практически все среды разработки добавили возможность программирования для мобильных устройств.
На Lua можно возложить очень много задач, не только касающихся искусственного интеллекта. Честно сказать, я наблюдал несколько примеров работы с Lua в рамках игровых проектов. Резюме от увиденного: очень немногие в действительности умеют работать с Lua, используют его возможности на 5- 10%. И хороших специалистов мало, а сама специализация становится все более востребованной.
Важно понимать, что Lua — это прежде всего data-oriented язык, а в какие структуры и обличья объединяются данные — на усмотрение видения разработчика. Эту интересную тему, а также «Lua и ООП» мы и рассмотрим в следующей части материала.
Кристофер christopher@tut.by
Мартин Страйчер, главный редактор Linux Magazine о Lua
Сегодня мы подробно остановимся на принадлежности языка Lua, а также на некоторых его особенностях. Многие его называют процедурным, это и так и не так одновременно. А для начала нам нужно разобраться с тем, что же подразумевается под современным понятием высокоуровневых языков, где слабые места в принятых соглашениях.
Основную разницу между компилируемыми и интерпретируемыми языками можно описать простыми словами так: в случае компилируемых языков перевод программы в понятную для машины форму осуществляется сразу же по завершении ее создания, а программа на интерпретируемом языке транслируется в машинные коды лишь при ее выполнении. Каждой команде интерпретируемого языка в коде интерпретатора соответствует подпрограмма, созданная с использованием поддерживаемых аппаратной частью компьютера средств. Когда эта команда встречается в коде, связанная с ней подпрограмма запускается, и необходимые действия выполняются. При этом все работает так, что пользователь (программист) может подумать: команды проделываются напрямую компьютером, хотя на самом деле — интерпретатором или виртуальной машиной (есть такой термин).
Для примера приведем широко известную и любимую всеми технологию Flash. Flash-плеер — виртуальная машина языка ActionScript. При этом есть и понятие флеш-ассемблера, то есть если в коде мы напишем a+b, то это будет соответствовать операциям: добавить в стек первую переменную, прочитать ее значение, добавить в стек вторую переменную, прочитать ее значение, сложить значения в стеке (на флеш-ассемблере это так и происходит). Для пользователя все это скрыто, он просто набирает «a+b». Перед исполнением языки должны быть переведены в понятную для машины форму, для чего производится их трансляция либо на язык ассемблера, либо сразу в машинный код, либо же взаимодействие предусматривает более сложную модель, когда один язык базируется на другом и так далее. Этим и проявляется различие между компилируемыми и интерпретируемыми языками. Хотя универсальные языки, такие как С/С++/Java принято считать низкоуровневыми и, соответственно, компилируемыми, это условное соглашение, особенно для Java. Под низкоуровневыми или компилируемыми языками очень часто понимаются мощные и универсальные.
О софизмах…
Современные интерпретируемые языки, куда входит множество скриптов (языков сценариев) принято считать высокоуровневыми, причем довольно часто ставят знак равенства между понятиями «высокоуровневый» и «объектно-ориентированный». Это сравнение больше напоминает софизм сродни: грузовик — автомобиль, «Мерседес» — автомобиль, значит, грузовик и «Мерседес» — одно и тоже. Иногда так оно и бывает:).
Объектно-ориентированное программирование (ООП) — концепция, пришедшая на смену структурному или процедурному программированию, и получившая широкое распространение только с середины 1980-х гг. с разработкой таких языков, как Smalltalk, C++ и Object Pascal. В качестве объектов в ней рассматриваются структуры данных, являющиеся множеством свойств и методов (проще говоря, переменных и функций). На самом деле в рамках ООП ничего особенно нового придумано не было, просто произошло определенного рода терминологическое, а после и концептуальное «очеловечивание» программирования как такового, помимо этого были решены проблемы с большими по объему разработками, которые уже представлялись на уровне взаимодействующих структур. Появилось понятие классов, а также три основных столпа-принципа ООП: абстракция, инкапсуляция, наследование. В середине-конце 90-х этого оказалось мало. Новой ласточкой явилась компонентная объектная модель COM, которая изначально была придумана для объединения в единую структуру нескольких документов различных типов и происхождения. Но после все переросло на другой уровень, то есть те или иные блоки программы либо программно-аппаратного комплекса могут реализовываться и функционировать в различных средах, писаться на разных языках, самое главное — это реализация общения между ними. Например, в магазине имеется множество кассовых аппаратов, и на самом деле все равно, как реализована их работа, главное — подключить их в сеть и организовать общение с ними (передавать и получать данные). Модель COM породила новую концепцию, которую принято называть интерфейсно-ориентированным программированием (ИОП). То есть подразумевается взаимодействие между интерфейсами.
В середине 2000-х была широко развита новая идея — многоязыковое программирование. Оно является следствием ИОП, а также множества сопутствующих технологий, например, связанных с созданием переносимого кода, который бы мог функционировать на компьютерах с различными процессорами и операционными системами.
Многоязыковое программирование позволяет расширять возможности одного языка программирования за счет другого, или, говоря иначе, тут понимается способность кода, написанного на разных языках, работать совместно. Дело в том, что ситуация с ИОП родила достаточно интересную проблему с необходимостью взаимодействия нескольких платформ, следовательно изначально были необходимы языки с высокой степенью переносимости. Первой ласточкой стал Java, который был создан в 1991 году, причем изначально он предназначался для небольших задач — программирование микроконтроллеров, а на самом деле оказалось, что его можно применять и в большом масштабе — для Интернета, а также для взаимосвязи между компьютерами с различными процессорами и операционными системами. В Java переносимость осуществляется за счет трансляции кода в специальный байт- код, который в свою очередь легко читается и приводится в исполнение виртуальными машинами Java (JVM — Java Virtual Machine). То есть, Java- программы запускаются в любой среде, где функционирует JVM.
Microsoft с конца 90-х стала развивать собственную технологию .NET Farmework, при этом как в ее рамках, так и в Visual Studio была реализована концепция не зависящей от языка среды исполнения (CLR), то есть вам, по существу, сейчас все равно на чем программировать в рамках того же Visual Studio. Выбор остается по функциональным и синтаксическим предпочтениям. В качестве альтернативы байт-коду Java был предложен промежуточный язык-ассемблер MSIL («ассемблер» следует воспринимать как «сборщик», например, мы уже говорили о флеш-ассемблере). MSIL и обладает всеми необходимыми свойствами переносимости, о которых упомянуто выше. MSIL превращается в исполняемый код при использовании JIT-компилятора (JIT — Just-in-time). При выполнении .NET программы система CLR активирует JIT-компилятор, превращающий MSIL в код для данного процессора, но с ключевым ноу-хау — коды частей программы преобразуются по мере того, как в них появляется потребность. Основным языком для реализации платформы .NET Framework является С#. В дополнение к MSIL при компилировании С#-программы вы получаете еще один важный компонент — метаданные, которые описывают данные, используемые вашей программой.
С# своим появлением решал и еще одну проблему, которая в других языках программирования стала камнем преткновения, а именно — утечка памяти. То есть у программистов появилось больше времени для того, чтобы концентрировать внимание на выполнении задач проектов вместо того, чтобы тратиться на реализацию правильного управления ресурсами. В Visual Studio сегодня вы можете в рамках одного кода использовать сразу несколько языков, например, создав проект на Visual C#, вы можете туда вставлять блоки на С++, Visual Basic и т.п. В начале-середине 90-х многоязыковое программирование также имело место, правда, в несколько другом представлении, например, писав программу на Delphi, вы могли вставлять фрагменты на ассемблере.
Таким образом, нынешнее состояние таково:
. Все современные языки являются в той или иной степени высокоуровневыми интерпретируемыми (включая C# и Java).
. Основной плюс интерпретируемых языков — высокая степень переносимости.
. Работа с интерпретируемыми языками подразумевает цикл: написание->>>запуск.
. Современные интерпретируемые языки должны иметь решения в области управления памятью.
В более неформальной среде все новации отображены в языке Lua, который как мы уже говорили, является встраиваемым в какое-либо API (наиболее часто можно встретить варианты его взаимодействия с C, C++, C#, Java, Python), и обладает всеми свойствами высокоуровневых языков, а именно: простой синтаксис, переносимость, управление памятью (мусороуборщик), метаданные, динамический контроль типов.
А сколько их, «ориентированных», бывает?
У нас почему-то очень глобально относятся к понятиям ориентированности того или иного языка. На самом деле, это просто показатель, не более того:
. Объектно-ориентированный.
. Компонентно-ориентированный.
. Интерфейсно-ориентированный.
. Data-oriented.
. Expression-oriented.
. …
Естественно, у каждого варианта есть своя специфика. Внедрение доктрины ООП хорошо объясняет суть появления классов как отдельных структур, но по существу ничего нового изобретено не было.
Так зачем нужен Lua?
Представьте себе сложный игровой проект. Если вы его будете реализовывать на компилируемом языке, то это отнимет много времени, которое будет тратиться не только на написание и отладку, но и на правильное распределение ресурсов и т.п. Эта ошибка достаточно часто встречается у начинающих разработчиков, которые только думают(!), что могут правильно обращаться с ресурсами и алгоритмами. Поэтому, например, никто не может переплюнуть по оптимизации движков Blizzard’вцев, которые являются гуру в этом вопросе.
А в случае, когда за низкоуровневым языком вы закрепите ключевую функцию — реализацию общения с железом, а все остальное опишете сценариями, например, на Lua, программирование в котором гораздо проще, то время разработки существенно сократится. При этом, если уж говорить о создании игр под Windows, то у разработчиков есть еще один козырь в виде низкоуровневого API DirectX, через который и происходит общение с железом. То есть разработка игры может быть и не такой сложной, как это описывается в некоторых книгах трех-(и больше) летней давности.
Если говорить о современных тенденциях и о том же вышеупомянутом MSIL, достаточно указать в качестве яркого примера на Microsoft XNA Game Studio — «игровое расширение» для Visual C#, на базе которого можно делать игры сразу для трех платформ: PC (Windows), игровой приставки Xbox 360 (Xbox) и портативного устройства Zune. Мы уже писали об XNA в «КГ». И вообще, в последнее время практически все среды разработки добавили возможность программирования для мобильных устройств.
На Lua можно возложить очень много задач, не только касающихся искусственного интеллекта. Честно сказать, я наблюдал несколько примеров работы с Lua в рамках игровых проектов. Резюме от увиденного: очень немногие в действительности умеют работать с Lua, используют его возможности на 5- 10%. И хороших специалистов мало, а сама специализация становится все более востребованной.
Важно понимать, что Lua — это прежде всего data-oriented язык, а в какие структуры и обличья объединяются данные — на усмотрение видения разработчика. Эту интересную тему, а также «Lua и ООП» мы и рассмотрим в следующей части материала.
Кристофер christopher@tut.by
Компьютерная газета. Статья была опубликована в номере 18 за 2009 год в рубрике программирование