Добавлен
Столкнулся с такой проблемой, как ООП в Lua. Никаких статей по этому поводу не нашёл, пока что их очень мало на сайте. На Хайве попытался найти наработки на Луа, но не нашёл реализацию систем через классы (таблицы). Подскажите, где можно достать какой-нибудь Lua-скрипт в Варкрафте с реализацией класса, или может быть зарубежная статья по этому поводу? Очень непривычно работать с этими таблицами как классами и пока непонятно, как лучше оформлять код, неизвестны многие нюансы. Как говорил кто-то, у Lua своя парадигма программирования — в чём её суть и действительно ли удобна она?
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
0
24
4 года назад
Отредактирован prog
0
Я не спец, но разве не у obj нужно ставить переменную damage?
Именно так.

Почему-то в цикле он пробегает только по последнему созданному объекту, причём сколько всего объектов, столько раз по нему и проходит.
Все правильно оно проходит. Просто у тебя значения damage нет в экземплярах, ты его задаешь только для метатаблицы, которая одновременно является индексной таблицей. Вот и берется это значение для экземпляров у которых нет такой переменной.
1
16
4 года назад
Отредактирован Drulia_san
1
NazarPunk:
Почему все носятся с ООП как с писанной торбой и пытаются применить её ко всем ЯП?
Потому что это удобно.
Не читал что вы понаписывали, объясню ООП в луа на пальцах. Объяснения с теориями всякими, объяснением что такое метатаблицы - всосная муть, которая только усложняет.
Короче. В луа есть переменные и функции. В луа все функции это еще и переменные, а переменные могут хранить и функции. Они могут хранить в себе другие переменные и функции если присваивать их через оператор точку ( . ), например var.a = 4. Мета-таблицы создают завершенность этого механизма, позволяя эту магию использовать в качестве ООП.
Ты хочешь создать класс, допустим класс Projectile. Класс снаряда, которому ты дашь параметры, а потом запустишь и он полетит себе так как ты прописал.
Для начала объявим его глобально:
Projectile = {}
Тут мы просто создали пустой массив, он необходим чтоб хранить объекты со всей их инфой о нашем классе, мы к нему никогда обращаться напрямую не будем, просто ну вот так надо, не задумывайся. Не думай вообще что такое мета-таблицы, такие объекты и так далее, просто работай с этим и всё, потом осознание что это такое придет само (или почувствуешь сам что готов про это почитать отдельно). А пока не засоряй себя и идем дальше.
У класса должен быть конструктор, то с чего всё начинается.
Объявляем:
-- У нас конструктор принимает путь к эффекту effectStr и две локации, откуда лететь и куда лететь (locA, locB)
function Projectile:new(effectStr, locA, locB) 
    local this = {} -- Собственно наш новый объект, текущий экземпляр, просто массив-локальная переменная которая хранит наши члены и методы
    
    -- Сначала объявим методы, я не буду писать реализацию снаряда, главное понять приницп

    function this.Move() -- Метод Move, двигает наш снаряд, если он достиг цели - возвращаем false, иначе - true
         ...........
    end

   function this.destroy() -- Деструктор. Луа не умеет их вызывать самостоятельно, вызываем вручную где надо
   ...
   end

    function this.Launch() -- Метод запуска, вызывает в таймере метод Move() пока он не вернет false
        local t = CreateTimer()
        save(GetHandleId(t), 1, this)
        TimerStart(t, 0.01, true, function()
                local t = GetExpiredTimer()
                local This = load(GetHandleId(t), 1)
                if not This.Move() then
                        This.destroy()
                        DestroyTimer(t)
                end
          end)
    end

    --А теперь код самого конструктора, напомню что мы снова пишем в скоупе функции Projectile:new, нашего "конструктора"
   this.Dummy = CreateDummy(bla bla bla)
   this.effect = CreateEffect(bla bla bla)
   ... -- И так далее
   
    -- Это должно быть обязательно
    setmetatable(this, self)
	self.__index = self
	return this
end
Итак, у нас есть свой класс, который что-то делать, пройдемся еще раз, начинку класса объявляем внутри конструктора function Projectile:new(effectStr, locA, locB) , локальная переменная this в нём и есть объект класса, который мы потом вернём. Объявление функций внутри конструктора с префиксом this. это и есть объявление методов, внутри них все манипуляции с классовыми членами и методами делаются строго через написание перед ними this. , потом уже сам код конструктора, инициализируем нужные члены, ресурсы, подготовим все и в конце вот это:
    setmetatable(this, self)
	self.__index = self
	return this
Не думай вообще что это такое, знание этого тебе ничего не даст, просто скопируй, оно вернет в конце рабочий this в место где ты создаешь объект.
Как пользоваться? Изи.
proj = Projectile.Create("war3imported\\myEffect.mdl", myloc, dest)
proj.launch()
Ничего сложного
Вот еще пример с очень простым классом, скопируй и тестируй тут: www.lua.org/cgi-bin/demo
Код для теста под катом
-- Пример класса суммирующего 2 переменные
Sum = {}

function Sum:new(a, b)
    local this = {}
    
    function this.Calc()
        print( this.a + this.b )
    end

    this.a = a
    this.b = b
    setmetatable(this, self)
    self.__index = self
    return this
end

-- Test
mySum = Sum:new(2, 5)
mySum.Calc()
0
24
4 года назад
Отредактирован prog
0
Drulia_san, фигню понаписал, у тебя методы создаются для каждого экземпляра, а должны для индексной таблицы создаваться один раз, иначе зачем тебе метатаблица и индексная таблица тогда...
2
16
4 года назад
Отредактирован Drulia_san
2
prog:
Drulia_san, фигню понаписал, у тебя методы создаются для каждого экземпляра, а должны для индексной таблицы создаваться один раз, иначе зачем тебе метатаблица и индексная таблица тогда...
Для начала и так сойдет, пусть с этим сначала разберется
0
29
4 года назад
0
Потому что это удобно.
ООП не серебрянная пуля, чтоб пихать его всюду.
Не думай вообще что это такое, знание этого тебе ничего не даст, просто скопируй
И правильно, зачем думать? Просто копируй, потом копируй скопированное, потом на 100500 итерации копирования беги в QA и задавай вопросы в стиле "нииирабоотаит".
3
16
4 года назад
3
NazarPunk:
И правильно, зачем думать?
Избыточность информации усложняет и делает невозможным усваивание материала, нельзя начинать учить что-то, давая сразу всю инфу, нужно абстрагировать от глаз всё что происходит за кулисами и скормить лишь суть, как этим воспользоваться. Потом уже сам узнаешь постепенно что и как работает.
Я не говорю, что надо всю жизнь копировать, в конечном итоге нужно это будет узнать, но в начале от всего что происходит за кулисами лучше абстрагироваться.
0
29
4 года назад
0
Избыточность информации усложняет и делает невозможным усваивание материала, нельзя начинать учить что-то, давая сразу всю инфу, нужно абстрагировать от глаз всё что происходит за кулисами и скормить лишь суть, как этим воспользоваться.
А потом пропускать мимо ушей критику вооружившись аргументом "работает же"
prog:
Drulia_san, фигню понаписал, у тебя методы создаются для каждого экземпляра, а должны для индексной таблицы создаваться один раз, иначе зачем тебе метатаблица и индексная таблица тогда...
0
28
4 года назад
0
Drulia_san, поэтому лучше погуглить решения от спецов в Lua, которые ерунду не понапишут, а потом на основе их сделать своё решение, или использовать их, если оно подходит под задачи. Чем больше погуглишь, тем лучше.
0
29
4 года назад
0
поэтому лучше погуглить решения от спецов в Lua, которые ерунду не понапишут
Я конечно не спец, но ерунду напишу. Простейший пример метатаблиц
Unit = { x = 0, y = 0 }
function Unit:new (id, unitid, x, y, face)
	local data = {}
	setmetatable(data, self)
	self.__index = self
	data.unit    = CreateUnit(id, unitid, x, y, face)
	return data
end
function Unit:x(x)
	if x == nil then return GetUnitX(self.unit) end
	SetUnitX(self.unit, x)
	return self
end
function Unit:y(y)
	if y == nil then return GetUnitY(self.unit) end
	SetUnitY(self.unit, y)
	return self
end
function Unit:xy(x, y)
	if x == nil and y == nil then return GetUnitX(self.unit), GetUnitY(self.unit) end
	SetUnitX(self.unit, x)
	SetUnitY(self.unit, y)
	return self
end
function Unit:name(name)
	if name == nil then return GetUnitName(self.unit) end
	BlzSetUnitName(self.unit, name)
	return self
end

local u = Unit:new(Player(0), FourCC('hfoo'), 0, 0, 0)
u:x(13):y(3):name('OOP mazafaka')
0
15
4 года назад
0
PT153:
Я не спец, но разве не у obj нужно ставить переменную damage?
Ох, действительно, спасибо!
prog:
Drulia_san, фигню понаписал, у тебя методы создаются для каждого экземпляра, а должны для индексной таблицы создаваться один раз, иначе зачем тебе метатаблица и индексная таблица тогда...
Да, сразу это заметил. Но это ведь для наглядности, ничего не мешает вынести эти методы.
В общем, всем спасибо, практически разобрался в принципе работы. Отдельная благодарность к Drulia_san.
Единственное, нужно уточнить один нюанс. Как реализовать деструктор? Имеется список skillshotObjectList, содержащий все создаваемые экземпляры. Необходимо реализовать удаление экземпляра из памяти и соотвественно очистку его из skillshotObjectList.
function Skillshot:Create (...)
    local this = {}
    
    skillshotIndex = skillshotIndex + 1
    skillshotObjectList[skillshotIndex] = this
    this.id = skillshotIndex

	...
end

function Skillshot.Destroy (this)
    ???
    skillshotIndex = skillshotIndex - 1
end
3
24
4 года назад
3
JackFastGame, если не важен порядок, то практически в любом языке один из самых быстрых способов удаления чего-либо из списка это поменять его местами с последним элементом списка, занулить последний элемент и сократить длину списка на 1.
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.