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

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


kuzmich774

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

kuzmich774
(изменено)

Введение

 

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

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

 

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

 

aQhyOAq.jpg

 jshEYzW.jpg

  

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

В основном txt-файлы,содержащиеся в поддиректориях директории Victoria 2 представляют собой структуры данных в виде множества таблиц с содержащимися в них переменными и их значениями.В последних версиях игр серии 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 — это универсальный язык программирования, который применяется в разных сферах: от игр до научных вычислений. Его простота, скорость и легкость интеграции делают его привлекательным для решения множества задач. 

 

Так как в в написании модов для игр Victoria 2 Lua в основном используется в виде структуры данных множества таблиц с переменными и их значениями,логических операторов работы над переменными,то для написания модов Victoria 2 знания основ вполне достаточно,в последних версиях игр серии Paradox встречаются более сложные конструкции.Если вы хотите продолжить изучение языка программирования Lua,который является первым по популярности использования в GameDev(разработка игр) и других областей  применения,указанных выше,для дальнейшего изучения языка рекомендуется:

 

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

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

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

 

 

 

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

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


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


kuzmich774
(изменено)

@woodman ,была и такая задумка.Но без основ языка Lua этого не понять.В следующей статье напишу на примере мода HPM,что можно сделать в Victoria II с помощью языка Lua.

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

думаю ничего, так как, движок из txt фалов читает только информацию, свою логику в них записать нельзя


kuzmich774
(изменено)

Движок игры уже скомпилирован,изменить его не получится,так как исходных файлов нет.Изначально Victoria 2 The Heart of Darkness - ванильная версия.С помощью изменения txt файлов на языке программирования Lua(по сути - это таблицы с переменными и их значениями,которые потом считывает движок) в поддиректориях в директории Victoria 2 The Heart of Darkness,например,таких как параметры файла Victoria 2 The Heart of Darkness\common\defines.lua и графических файлов в директории Victoria 2 The Heart of Darkness\gfx можно менять параметры мода(в шапке темы есть подробная таблица,какие файлы за что отвечают)Некоторые сведения как менять эти параметры уже есть в разделе Victroria 2 -> Моды и модинг -> Учебники по модингу : Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.  ,Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.  , Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.  , 

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

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

 

 

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

kuzmich774
(изменено)

Исходный код движка игры Victoria 2 The Heart of Darkness есть только у компании Paradox Interactive,он является их интеллектуальной собственностью.В 1998 г. группа энтузиастов-программистов из Швеции решили объединиться и открыли компанию под названием Paradox Interactive.Первые игры серии Paradox,такие как Europa universalis  используют движок Europa Engine,написанный на высокоуровневом языке программирования C.В дальнейшем, технология развивалась и теперь многие игры от Paradox Interactive написаны на С++ и Lua.Компания  Paradox Interactive разработала свой игровой движок Clausewitz Engine, который используется начиная с игр Europa Universalis 3,Hearts of Iron 3,Victoria 2.Есть и другие языки программирования,которые используются для создания современных модов Victoria 2 THD - это AMPL(100% мода Crimea mod) - для создания новых алгоритмов ИИ(Искусственного интелеллекта) и HLSL(для создания новых графических элементов).Подробный анализ современных иностранных модов Victoria THD есть в статье Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое. . Руководство по AMPL есть только на английском языке.Когда переведу,выложу сюда же.

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

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

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

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

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

  Only 75 emoji are allowed.

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

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

×   Вы не можете вставлять картинки напрямую. Загрузите или вставьте их через URL.

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

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

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