Перейти к содержимому
Strategium.ru

Основы языка программирования Lua,на котором написаны все игры серии Paradox(с простыми примерами)


kuzmich774

Рекомендованные сообщения

kuzmich774
(изменено)

Введение

 

Цель статьи - объяснить основы программирования языка Lua на простых примерах.В конце статьи есть ссылки на скачивание литературы для дальнейшего изучения языка.

Статья будет интересна начинающим модерам Victoria 2 и других игр серии Paradox.

 

Рассмотрим содержимое файлов в директории Victoria 2

 

aQhyOAq.jpg

 jshEYzW.jpg

  

 Для того,чтобы понимать код,написанный во всех этих файлах в директориях,содержащихся в Victoria 2 и в дальнейшем менять его(или добавлять) при создании мода по своему усмотрению,необходимо знать основы языка программирования Lua,на котором написаны все игры серии Paradox.

 

Введение в язык программирования Lua

 

Lua — это легкий, высокоуровневый, многоцелевой язык программирования, который был разработан в начале 1990-х годов в Бразилии. Он известен своей простотой, гибкостью и эффективностью. Lua часто используется в сценариях для игр, встраиваемых системах и других приложениях, где важны производительность и компактность кода. Этот язык стал популярным благодаря своей способности легко интегрироваться с другими языками программирования и системами, что делает его идеальным выбором для разработчиков, работающих в различных областях.

Lua имеет минималистичный синтаксис, что делает его легким для изучения и использования. Основные принципы языка включают в себя динамическую типизацию, автоматическое управление памятью и поддержку процедурного, объектно-ориентированного(ООП) и функционального программирования. Эти особенности делают Lua универсальным инструментом, который может быть использован как для простых скриптов, так и для сложных приложений. Важно отметить, что Lua также поддерживает расширяемость через метатаблицы и метаметоды, что позволяет разработчикам создавать более сложные и мощные конструкции.

 

Основные синтаксические конструкции Lua

 

Переменные и типы данных

 

Переменные в Lua не требуют явного указания типа, так как язык использует динамическую типизацию. Это означает, что тип переменной определяется автоматически в момент присваивания значения, что упрощает процесс написания кода и делает его более гибким.

 

local number = 10

local text = "Hello, Lua!"

local isTrue = true

local nothing = nil

 

Числа в Lua могут быть как целыми, так и с плавающей запятой. Строки представляют собой последовательности символов и могут быть заключены в одинарные или двойные кавычки. Булевы значения могут быть либо true, либо false. Таблицы являются основным структурным типом данных и могут содержать элементы различных типов. Функции в Lua являются первоклассными объектами, что означает, что их можно присваивать переменным, передавать в качестве аргументов и возвращать из других функций. Nil используется для обозначения отсутствия значения.

 

Области видимости

 

Переменные бывают глобальные и локальные. При создании все переменные в Lua являются глобальными.

 

Для указания локальной области видимости пишут ключевое слово local:

1
2
local x
local var1, var2 = 5, 3

Не забывайте об этом слове.

 

 Управляющие конструкции

 

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

 

Условные операторы

 

Условные операторы в Lua включают if, elseif и else. Они используются для выполнения различных блоков кода в зависимости от условий.

 

local age = 20

 

if age < 18 then

    print("You are a minor.")

elseif age >= 18 and age < 65 then

    print("You are an adult.")

else

    print("You are a senior.")

end

 

Циклы

 

Циклы в Lua включают while, for и repeat-until. Они используются для повторного выполнения блока кода до тех пор, пока выполняется определенное условие.

 

-- Цикл while

local i = 1

while i <= 5 do

    print(i)

    i = i + 1

end

 

-- Цикл for

for j = 1, 5 do

    print(j)

end

 

--Цикл repeat-until

local i = 1

repeat

print(i)

 i = i+1

until i=5

 

Цикл while выполняет блок кода до тех пор, пока условие истинно. Цикл for используется для итерации по диапазону значений.

Цикл repeat-until используется для выполнения блока команд до тех пор, пока указанное условие не станет истинным. В отличие от других циклов, таких как for или while , repeat-until гарантированно выполнится хотя бы один раз, так как проверка условия происходит после выполнения блока команд.

Циклы позволяют автоматизировать повторяющиеся задачи и делают код более компактным и читаемым.

 

 

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

* присваивание: x = 0
* арифметические: +, -, *, /, % (остаток от деления), ^ (возведение в степень)
* логические: and, or, not
* сравнение: >, <, ==, <=, >=, ~= (не-равно, да-да, вместо привычного «!=»)
* конкатенация строк (оператор «..»), напр.: s1=»hello»; s2=»world»; s3=s1..s2
* длина/размер (оператор #): s=»hello»; a = #s (‘a’ будет равно 5).
* получение элемента по индексу, напр.: s[2]

 

Работа с таблицами в Lua

 

Таблицы являются основным структурным типом данных в Lua. Они могут использоваться для создания массивов, словарей и других сложных структур данных. Таблицы в Lua являются ассоциативными массивами, что означает, что они могут содержать пары ключ-значение, где ключи могут быть любого типа, кроме nil.

 

Создание и использование таблиц

 

Таблицы в Lua создаются с помощью фигурных скобок {}. Они могут содержать элементы различных типов и могут быть динамически изменены.

 

-- Создание таблицы

local fruits = {"apple", "banana", "cherry"}

 

-- Доступ к элементам таблицы

print(fruits[1])  -- Output: apple

 

-- Добавление элементов в таблицу

table.insert(fruits, "date")

print(fruits[4])  -- Output: date

 

В этом примере мы создаем таблицу с тремя элементами и добавляем четвертый элемент с помощью функции table.insert. Таблицы в Lua являются динамическими, что означает, что их размер может изменяться в процессе выполнения программы.

 

Ассоциативные массивы

 

Ассоциативные массивы позволяют использовать строки и другие типы данных в качестве ключей. Это делает таблицы в Lua очень гибкими и мощными.

 

local person = {

    name = "John",

    age = 30,

    isEmployed = true

}

 

print(person.name)  -- Output: John

 

В этом примере мы создаем таблицу с тремя парами ключ-значение и выводим значение ключа name. Ассоциативные массивы позволяют создавать сложные структуры данных, которые могут быть использованы для моделирования различных объектов и сущностей.

 

Функции и метатаблицы

 

Определение и вызов функций

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

 

-- Определение функции

local function greet(name)

    return "Hello, " .. name .. "!"

end

 

-- Вызов функции

print(greet("Lua"))  -- Output: Hello, Lua!

 

Функции могут быть определены с помощью ключевого слова function. Они могут принимать аргументы и возвращать значения. В Lua также поддерживаются анонимные функции, которые могут быть определены без имени и использованы в качестве аргументов или возвращаемых значений.

 

Функции языка позволяют принимать несколько аргументов, и возвращать несколько аргументов. Так аргументы, значения которых не указаны явно, считаются равными nil.

 

 

function swap(a, b)

  return b, a

end

 

x, y = swap(x, y)

-- кстати, это можно сделать и без функции:

x, y = y, x

-- и если уж функция возвращает несколько аргументов,

-- а они вам не нужны - игнорируйте их с помощью

-- специальной переменной-подчеркивания "_"

a, _, _, d = some_function()

Функции могут принимать переменное количество аргументов:

 
-- в прототипе переменное число аргументов записывается как троеточие
function sum(...)
   s = 0
   for _, n in pairs(arg) do -- в функции обращаются к ним, как к таблице "arg"
      s = s + n
   end
   return a
end
sum(1, 2, 3) -- вернет 6
sum(1, 2, 3, 4) -- вернет 10

Поскольку функции — это полноценный тип данных, то можно создавать переменные-функции, а можно передавать функции как аргументы других функций

 

 
a = function(x) return x * 2 end -- функция, умножающая на 2
b = function(x) return x + 1 end -- функция, увеличивающая на 1
 
function apply(table, f)
  result = {}
  for k, v in pairs(table) do
    result[k] = f(v) -- заменяем элемент на какую-то функцию от этого элемента
  end
end
 
-- ПОДУМАЙТЕ: что вернут вызовы
t = {1, 3, 5}
apply(t, a)
apply(t, b)
 

Метатаблицы и метаметоды

 

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

 

local mt = {

    __add = function(a, b)

        return a.value + b.value

    end

}

 

local obj1 = {value = 10}

local obj2 = {value = 20}

 

setmetatable(obj1, mt)

setmetatable(obj2, mt)

 

local result = obj1 + obj2

print(result)  -- Output: 30

 

В этом примере мы создаем метатаблицу с метаметодом __add, который перегружает оператор сложения для объектов obj1 и obj2. Метатаблицы позволяют создавать более сложные и мощные конструкции, которые могут значительно расширить возможности языка.

 

Объекты = функции + таблицы

 

Раз мы можем сохранять функции в переменных, то и в полях таблиц тоже сможем. А это уже получаются как-бы-методы. Для тех, кто не знаком с ООП скажу, что основная его польза (по крайней мере в Lua) в том, что функции и данные, с которыми они работают находятся рядом — в пределах одного объекта. Для тех, кто знаком с ООП скажу, что классов здесь нет, а наследование прототипное.

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

 

lamp = {

  on = false

}

 

function turn_on(l)

  l.on = true

end

 

function turn_off(l)

  l.on = false

end

 

-- это просто функции для работы со структурой

turn_on(lamp)

turn_off(lamp)

А если лампочку сделать объектом, и функции turn_off и turn_on сделать полями объекта, то получится:

 

lamp = {

  on = false

  turn_on = function(l) l.on = true end

  turn_off = function(l) l.on = false end

}

lamp.turn_on(lamp)

lamp.turn_off(lamp)

Мы вынуждены передавать сам объект лампочки в качестве первого аргумента, потому что иначе наша функция не узнает с какой именно лампочкой надо работать, чтобы сменить состояние on/off. Но чтобы не быть многословными, в Lua есть сокращенная запись, которую обычно и используют — lamp:turn_on(). Итого, мы уже знаем несколько таких упрощений синтаксиса:

 

lamp:turn_on() -- самая общепринятая запись

lamp.turn_on(lamp) -- то с точки зрения синтаксиса это тоже правильно

lamp["turn_on"](lamp) -- и это

Продолжая говорить о сокращениях, функции можно описывать не только явно, как поля структуры, но и в более удобной форме:

 

lamp = {

  on = false

}

 

-- через точку, тогда аргумент надо указывать

function lamp.turn_on(l) l.on = true end

 

-- через двоеточкие, тогда аргумент неявно задается сам, как переменная "self"

-- "self" - и есть та лампочка, для которой вызвали метод

function lamp:turn_off() self.on = false end

 

Специальные функции

 

Некоторые имена функций таблиц (методов) зарезервированы, и они несут особый смысл:

* __add(a, b), __sub(a, b), __div(a, b), __mul(a, b), __mod(a, b), __pow(a, b) — вызываются, когда выполняются арифметические операции над таблицей
* __unm(a) — унарная операция «минус» (когда пишут что-то типа «x = -x»)
* __lt(a, b), __le(a, b), __eq(a, b) — вычисляют результат сравнения (<, <=, ==)
* __len(a) — вызывается, когда делается "#a"
* __concat(a, b) — вызывается при "a..b"
* __call(a, …) — вызывается при "a()". Переменные аргументы — это аргументы при вызове
* __index(a, i) — обращение к a, при условии, что такого элемента не существует
* __newindex(a, i, v) — создание "a = v"
* __gc(a) — когда объект удаляется при сборке мусора

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

 

Наследование

 

Для тех, кто не знает ООП, наследование позволяет расширить функциональность уже существующего класса. Например, просто лампочка умеет включаться-выключаться, а супер-ламкочка будет еще и яркость менять. Зачем нам переписывать методы turn_on/turn_off, если можно их повторно использовать?

В Lua для этого есть понятие мета-таблицы, т.е. таблицы-предка. У каждой таблицы есть одна таблица-предок, и дочерняя таблица умеет делать все, что умеет предок.

Допустим, что объект-таблицу lamp мы уже создали. Тогда супер-лампочка будет выглядеть так:

 

superlamp = {

  brightness = 100

}

-- указываем родительскую таблицу

setmetatable(superlamp, lamp)

-- и ее методы теперь доступны

superlamp:turn_on()

superlamp:turn_off()

 

Расширение функциональности

 

Родительские таблицы есть у многих типов (ну у строк и таблиц точно, у чисел и булевых чисел, и у nil их нет). Допустим, мы хотим складывать все строки с помощью оператора "+", а не "..". Для этого надо подменить функцию «+» (__add) для родительской таблицы всех строк:

 

s = getmetatable("") -- получили родительскую таблицу строки

s.__add = function(s1, s2) return s1..s2 end -- подменили метод

 

-- проверяем

a = "hello"

b = "world"

print(a + b) -- напишет "helloworld"

 

Собственно, мы еще можем заменить функцию print с помощью «print = myfunction», да и много других хакерских дел можно сделать.

 

 

Обработка ошибок

 

Часто, если возникают ошибки, надо прекратить выполнение определенной функции. Можно, конечно, сделать множество проверок и вызывать «return», если что-то пошло не так. Но это увеличит объем кода. В Lua используется что-то наподобие исключений (exceptions).

Ошибки порождаются с помощью функции error(x). В качестве аргумента можно передать все, что угодно (то, что имеет отношение к ошибке — строковое описание, числовой код, объект, с которым произошла ошибка и т.д.)

Обычно после этой функции вся программа аварийно завершается. А это надо далеко не всегда. Если вы вызываете функцию, которая может создать ошибку (или ее дочерние функции могут создать ошибку), то вызывайте ее безопасно, с помощью pcall():

 

function f(x, y)

...

  if ... then

    error("failed to do somthing")

  end

...

end

 

status, err = pcall(f, x, y) -- f:функция, x-y: ее аргументы

if not status then

  -- обработать ошибку err. В нашем случае в err находится текст ошибки

end

 

 

Практическое применение и примеры кода

 

Lua широко используется в различных областях, включая разработку игр, встраиваемые системы и веб-программирование. Вот несколько примеров, которые демонстрируют практическое применение Lua.

 

Скрипт для игры

 

Lua часто используется для написания сценариев в играх. Это позволяет разработчикам быстро и легко добавлять новые функции и изменять поведение игры без необходимости перекомпилировать основной код.

 

local player = {

    name = "Hero",

    health = 100,

    attack = function(self, target)

        target.health = target.health – 10

        print(self.name .. " attacks " .. target.name)

    end

}

 

local enemy = {

    name = "Goblin",

    health = 50

}

 

player:attack(enemy)

print(enemy.health)  -- Output: 40

 

В этом примере мы создаем объекты player и enemy и определяем метод attack для игрока. Этот метод уменьшает здоровье цели на 10 и выводит сообщение об атаке. Скрипты на Lua позволяют легко изменять и расширять функциональность игры.

 

Встраивание Lua в C++

 

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

 

#include <lua.hpp>

 

int main() {

    lua_State *L = luaL_newstate();

    luaL_openlibs(L);

 

    luaL_dostring(L, "print('Hello from Lua!')");

 

    lua_close(L);

    return 0;

}

 

В этом примере мы создаем новое состояние Lua, открываем стандартные библиотеки Lua и выполняем строку кода на Lua с помощью функции luaL_dostring. Встраивание Lua в C++ позволяет использовать Lua для написания сценариев и расширения функциональности приложений на C++.

 

Подведём итог

 

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

 

С помощью Lua создано много популярных игр, среди которых Crysis, World of Warcraft, Sim City, Far Cry, Stalker. 

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

опубликовать на Roblox. Это платформа, на которой можно играть, общаться, создавать и выкладывать свои проекты. Так десятки тысяч пользователей смогут увидеть вашу игру. 

У языка Lua есть и другие сферы применения, например: 

·         написание Telegram-ботов; 

·         научные вычисления, например в исследовательских центрах, лабораториях; 

·         разработка серверных сценариев, где требуется высокая производительность; 

·         создание Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.  для обработки, анализа и визуализации данных; 

·         автоматизация задач в различных системах. 

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

 

Для дальнейшего изучения языка рекомендуется:

 

1.Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

2.Книга создателя языка Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое. " 3-е издание

3.Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.  

 

 

 

Изменено пользователем kuzmich774

Сразу видно наш человек!


Присоединиться к обсуждению

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

Гость
Ответить в тему...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Only 75 emoji are allowed.

×   Ваша ссылка автоматически преображена.   Отображать как простую ссылку

×   Предыдущее содержимое было восстановлено..   Очистить текст в редакторе

×   You cannot paste images directly. Upload or insert images from URL.

  • Ответы 1
  • Создано
  • Последний ответ
  • Просмотры 206

Лучшие авторы в этой теме

  • fedottt

    1

  • kuzmich774

    1

Популярные дни

  • Сейчас на странице   0 пользователей

    • Нет пользователей, просматривающих эту страницу
  • Модераторы онлайн

    • alexis
×
×
  • Создать...