Перейти к контенту

Язык Lua. Общие вопросы программирования


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

С чего начинать и где взять.

 

Установка Lua:
http://www.amk-team.ru/forum/index.php?showtopic=11584&p=629106

 

Руководство «Программирование на языке Lua», третье издание:
http://www.amk-team.ru/forum/index.php?showtopic=11584&p=905308

Изменено пользователем Kirgudu
Ссылка на комментарий
xStream, по поводу классов - да ты права, в той схеме, что я выложил, он вообще не нужен, но! это ведь никак не влияет на игру или работу скрипта, да и к тому же лично мне, как-то приятней видеть все в под одним классом, нежели видеть "голые функции", пусть даже каждая схема - отдельный скрипт... Такие схемы бросаются в глаза как сборник функций..., а объединяя эти функции под одним классом, сразу становится видно какие функции связаны между собой, ну это мое ИМХО, это уже кому как... Я так привык, мне так удобней, потому и пользуюсь таким методом, но, еще раз повторюсь, я с тобой полностью согласен - класс здесь не нужен... Потому я и спрашивал влияет ли такой метод на что то или нет...
Ссылка на комментарий

Примерно так :

--[[
P.S.
1. Функции save и load нужно переписать для сохранения в пакете
2. Защитами "от дурака" я не заморачивался. Оставил "на потом" 
3. Естественно все числа секунд для сталкера нужно перевести в миллисекунды.
   Само собой и функции типа os.time , io...
]]

-----------------------------------
-- типа экшены по завершению работы таймеров в каких-то файлах или в глобалке. Удалить нах.
function Action1()
    print('Action1')
end

function Action2()
    print('Action2')
end

function Action3()
    print('Action3')
end

function Action4()
    print('Action4')
end
-----------------------------------------
--[[
В данном варианте подразумевается использование таймеров в глобальном контексте.
Поэтому модуль таймеров таймеров можно записать в любой файл, расположенный в любом месте с произвольным расширением кроме script.
В файле _g.script нужно добавить загрузку модуля :

local f = loadfile('путь в модулю таймеров')
setfenv(f, _G)
f()

'путь в модулю таймеров' - можно указать либо посредством FS, либо напрямую.
]]
----------------------------------------
-- Модуль таймеров
_timer = {}
local updateTime = 0

function _timer:_init()
    self.__index = self
    self.timers  = {}
    setmetatable(self.timers, _timer)
end

_timer:_init()

function _timer:sort()
    table.sort(self.timers, function (a,b) return a.complete < b.complete end)
    self:updateID()
end

function _timer:stop()
    local ostime  = os.time()
    self.remain   = (self.complete or ostime) - ostime
    self.complete = math.huge
    _timer:sort()
end

function _timer:isRunning()
    return self.remain == nil
end

function _timer:start()
    self.complete = os.time() + (self.remain or 0)
    self.remain   = nil
    _timer:sort()
end

function _timer:save()
    local file = io.open('C:\\save_test.ini','w')
    local tab  = self.timers
    local w    = {}
    for i = 1, #tab do
        local t = {}
        for k,v in pairs(tab[i]) do
            local pos
            if     k == 'name'     then pos = 1
            elseif k == 'time'     then pos = 2
            elseif k == 'act'      then pos = 3
            elseif k == 'period'   then pos = 4
            elseif k == 'complete' then pos = 5
            elseif k == 'remain'   then pos = 6
            else pos = nil
            end
            if pos then t[pos]=v end
        end
        if t[3] == nil then t[3] = '@' end
        table.insert(w,table.concat(t,' '))
    end
    local s = table.concat(w,'|')
    file:write(s)
    file:close()
end

function _timer:load()
    self.timers = {}
    local file  = io.open('C:\\save_test.ini','r')
    local s     = file:read()
    for w in s:gmatch('[^|]+') do
        local t = {}
        for c in w:gmatch('%S+') do
            table.insert(t,c)
        end
        if t[5] == '1.#INF' then t[5] = math.huge end
        if t[3] == '@' then t[3] = nil end
        local timer = self:create(t[1], t[2] , t[3], t[4], tonumber(t[5]))
        if t[6] then timer.remain = t[6] end
    end
    file:close()
end
-- пока выключить. Нужно сохранить первоначальную функцию (act для функций changeAction и changeActionTime).
-- Проще пересоздать снова 
--[[function _timer:restart()
    local name = self.name
    local sec  = self.time
    local act  = self.act
    local per  = self.period
    self:kill(self.id)
    self:create(name, sec, act, per)
end]]

function _timer:getRemainTime()
    return self.complete - os.time()
end

function _timer:changeAction(act)
    self.act = act
end

function _timer:changeActionTime(time)
    self.complete = self.complete + time
    if self.remain then self.remain = self.remain + time end
    self:sort()
end

function _timer:setUpdatePeriod(per)
    self.period = per
end

function _timer:kill(n)
    local pos = n or self.id
    if self.name then _G[self.name] = nil end
    table.remove(self.timers, pos)
    self:updateID(pos)
end

function _timer:updateID(start_pos)
    start_pos = start_pos or 1
    local t   = self.timers
    for i = start_pos, #t do
        t[i].id = i
    end
end

function _timer:runAction()
    local action = self.timers[1].act
    return action and _G[action]()
end

function _timer:timerCreate(name, time, act, per, comp)
    if type(name) == 'number' then
        per  = act
        act  = time
        time = name
    elseif type(act) == 'number' then
        per = act
        act = nil
    end
    time = time or 0
    per  = per or 0 -- тут в сталке 0 изменить на 1000 (мс - т.е. 1 сек. по умолчанию)
    local complete = comp or os.time() + (time or -1)
    return  {   handler =
                    function ()
                        local ostime = os.time()
                        local t = self.timers[1]
                        if ostime >= t.complete then
                            self:runAction()
                            self:kill(1)
                            return true
                        end
                        updateTime = ostime + t.period
                        return false
                    end            ,
                name     = name    ,
                time     = time    ,
                complete = complete,
                period   = per     ,
                act      = act
            }
end

function _timer:timerAdd(...)
    local timer    = self:timerCreate(...)
    table.insert(self.timers, timer)
    _timer:sort()
    return timer
end

function _timer:create(...)
    local obj = _timer:timerAdd(...)
    _G[obj.name] = obj
    setmetatable(obj, _timer)
    return obj
end

function _timer:action()
    return self.id == 1 and self.handler()
end

function _timer:update() -- Эту функцию на апдейт в binder actor-а.
    if os.time() >= updateTime then
        local t = self.timers[1]
        if t and not t.remain then
            t:action()
        end
    end
end
---------------------------------------
-- Тест

_timer:create('t1', 4)
_timer:create('t2', 3,1)
_timer:create('t3', 6,'Action2')
_timer:create('t4', 1,'Action3',1)
_timer:create(2,'Action1',1)
_timer:create(5,'Action4')
------------------------------------------
-- Эмуляция апдейта
while _timer.timers[1] and not _timer.timers[1].remain do
_timer:update()
end

Ссылка на комментарий
Gun12, а что, если несколько таймеров запущено одновременно? Изменено пользователем Viнt@rь
Ссылка на комментарий

Обрабатывается только один. Тот у которого ближайшее время срабатывания.

Это и есть главная фишка. Любой другой можно проверить при необходимости (если он динамический, т.е. имеет имя, а значит и поле в таблице _G)

Изменено пользователем Gun12
Ссылка на комментарий

а если запущено 2 таймера в одно и то же время и одинаковой частотой апдейта и с одинаковым временем срабатывания?(вообще конечно, такие случаи редкость или легче создать 1 функцию и запихнуть туда все, что надо, дабы не создавать кучу таймеров, но всетаки...) первый таймер, по идее, проиграется во время, а второй с опозданием...

Изменено пользователем Viнt@rь
Ссылка на комментарий

Viнt@rь, вот я об этом и писала - "работает же" :) Конечно, переделывать глупо. Надо просто сразу делать с умом.

Еще раз - приятней видеть одним классом. В чем приятность? В именах функций видеть имя класса? :) Сомнительный аргумент, на мой взгляд. Код можно грамотно оформить и без класса, обрамить комментариями, организовать блоки, сделать приватные функции и т.п.

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

Ты сам ведь понимаешь, что дело именно привычки. Но для меня главное, что ты понимаешь, что такое псевдо-ООП. Смею на это надеяться.

 

Gun12, вопрос сразу - это ты делаешь ООП-like, или пользуешься свойством оператора ":" для доступа к "экземпляру"? Поясню, откуда этот вопрос. На самом деле "объект" у тебя тут всего лишь один, а остальное "настройки", которые хранятся в таблице. Поэтому такое решение претендовать на ООП никак не может.

В целом "нарекания" вызывает функция timerCreate - много параметров, которые могут еще и меняться местами (разный набор настроек, я понимаю). Я бы порекомендовала либо использовать один параметр options (хеш-таблица) или использовать chain-calls, несколько методов для настройки, каждый из которых возвращает self. То есть сделать пошаговое создание, так сказать. Но для не объектов (в контексте именно инстансов - у тебя их нет) это не очень удобно. Если кратко - сделай "говорящий" интерфейс: local t = timer():setAction(xxx):setPeriod(xxx):run() примерно так. Запись длиннее, но зато гораздо нагляднее, что происходит именно тут. К такому варианту еще провоцирует даже не количество параметров, а то, что они могут обозначать разное при разных наборах. Ну и, мне кажется, такой подход избавил бы от функций типа changeAction, что привело бы к упрощению интерфейса, а это всегда хорошо. :)

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

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий
а второй с опозданием...
Да я думал над этим...

Когда будет большая необходимость изменю.

На самом деле, на данном этапе, время срабатывания одного будет позже другого только на 1 апдейт.

Это настолько критично? Если да, то буду исправлять.

 

xStream

Спасибо за совет.

На ООП особо не претендую. Изначально хотел сделать меньше записей.

Для того и рискнул выложить чтобы учиться (как максимум - всё-таки научиться :)).

Изменено пользователем Gun12
Ссылка на комментарий

Вдогонку - ты вполне можешь использовать замыкания для имитации "экземпляров объектов". Я за пару страниц до этого писала, что это за фигулина. В твоем случае, можно за селф принять как раз ту самую локальную переменную и этим пользоваться, аля:

function createTimer(options)
    local self = ... тут создаем как у тебя набор настроек ... 
    self.setAction = function()
        ... что-то делаем и возвращаем селф...
        return self
    end
    ... тут еще интерфейсные функции ...
    return self
end

То есть обернули твою таблицу настроек в интерфейс. Это просто как вариант. Пользуемся, как ты любишь, только "чистым" ЛУА

 

Добавлено через 2 мин.:

На ООП особо не претендую. Изначально хотел сделать меньше записей.

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

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

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий

xStream

О замыкания знаю.

Изначально функция timerCreate (и та, что сейчас под ключём handler в таблице) и была простым замыканием, но потом переделал в таблицу...Ну так вышло уж...Простите :)

------------------

Хорошо. Буду пытаться. Ещё раз спасибо за примеры и советы. Если будут ещё - "очень" не откажусь.

Изменено пользователем Gun12
Ссылка на комментарий

xStream,

Но для меня главное, что ты понимаешь, что такое псевдо-ООП. Смею на это надеяться.
благодаря тебе - уже понимаю :) и да, все дело в уже приевшихся привычках :)

 

Gun12, как я заметил(по "своим" таймерам) будет довольно таки весомым... Смотри пример:

код простенький, но тут суть не столько в коде, как хоть каком-то наглядном примере...
ЗЫ подправил код заметил, что пример фиговый)))
local TimerTrigger=time_global()+1000
function aaa()
    if TimerTrigger-time_global()==0 then
        ...
    end
end

вот в таком виде, походу, все что после проверки условия "проиграется" только в 10% случаев, а то вообще не "проиграется", а это значит. что всегда гарантировано опоздание вызова, по примеру таймеров тех, что я выкладывал, я не спроста поставил вывод в лог разници между секундами после условия, а именно c целью отслеживать на сколько опаздывает функция, и это время всегда разное... в игровых секундах я получаю примерно такие значения -0,15*; - 0,25; к тому же не исключены фризы в игре при этом вызов опаздывает примерно на столько -7,00015* и тп и это все на 1 апдейте, если же еще считать, то что таймер вызовится на 1 апдейт позже, то смело можно умножать эти цифры на 2, ато и больше...

 

ЗЫЫ пример приведен на основе глобального времени, цифры приведены на выводах основаных на игровом времени... но это не так важно, так как что так что так опоздание будет всеравно....в глобальном времени например к 1000 мс прибавляется до 100мс при работе без подвисаний, если было подвисание, то может быть и такое к 1000 мс около еще 3000 мс

Изменено пользователем Viнt@rь
Ссылка на комментарий

Gun12

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

В твоем коде это сделать довольно просто - принудительный вызов опять же _timer:update(). Минусы - чревато рекурсией, если таймеры таки сработают одновременно. Но учитывая реальне игровые условия, это маловероятно, а если и произойдет, то 1-3 одновременно (цифры с потолка, просто прикинула игровой процесс) максимум. Ну или для перестраховки ограничить количество рекурсий на один апдейт. Например, максимум 5 таймеров могут сработать. Остальные ждут следующего апдейта, судьба у них такая. :)

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий

xStream, вот в таких случаях, ИМХО, и лучше использовать цикл, так как все прогоняется на 1 апдейте, НО! "голый" цикл на апдейте может быть причиной тех же фризов и тп, мое предожение сделать тип функций, в смысле "важная/неважная" функция, тоесть, если был запущен таймер функции с флагом "важная", то постоянно проверять истечение таймера для нее, если же неважная, то тогда всеравно, как часто ее проверять, можно хоть и раз в 5 сек(глобального времени, к примеру)

 

ЗЫ идею берег для себя, но всетаки решил поделиться, заодно и узнаю, насколько она хороша)

Изменено пользователем Viнt@rь
Ссылка на комментарий
По идее, правильно делать так, чтоб при срабатывании одного таймера, тут же, помимо перестраивания таблицы, делалась снова проверка - а вдруг еще какой-то таймер сработать должен?
Блин, хотел же так и сделать, да подумал что вероятность ... Забыл? Лень? Не знаю. Сначала помнил что нужно это сделать, потом... как всегда.

В общем я не и тестировал толком. Иначе по-любому обнаружил бы.

Ссылка на комментарий

Viнt@rь, ну у меня и используется цикл в той реализации, что я выкладывала. При срабатывании таймера, не только находим тот, который будет выполнен в следующий раз, но еще и смотрим, есть ли еще такие, что должны сработать сейчас? Если есть - отрабатываем их. Однако в реализации Gun12 это чревато нагромождением кода, лучше уж рекурсию допустить.

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

Идея с флагом по сути оптимизация, но преждевременная. Надо стараться оценивать реальные ситуации, в которых этот код будет работать. В случае появления проблем делаются тесты, замеры и тогда уже делается оптимизация. Практика показывает, что оптимизировать надо зачастую совсем не там, где ты ожидаешь :)

 

Добавлено через 4 мин.:

Тема, кстати, тоже хорошая - циклы vs рекурсия. Выбор должен определяться условиями и окружением твоего кода, а не личными предпочтениями. Имхо.

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

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий

И правильно думал, что вероятность... :) Она действительно мала в реальных условиях. Учитывая архитектуру твоего кода, это можно делать через рекурсию.

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий
При срабатывании таймера, не только находим тот, который будет выполнен в следующий раз, но еще и смотрим, есть ли еще такие, что должны сработать сейчас

кстати было такое в мыслях, но как-то отложил "на потом" вместе с идеей о флаге...

Ссылка на комментарий
Gun12, для меня, алгоритмы реконструпровать - это долго - просто скажи, я правильно понял: у тебя конвейрный алгоритм? Или другое? Мог-бы ты просто (упрощённо) алгоритм описать(изобразить)? Понятно , если не в лом, конечно. Изменено пользователем 7.9

всё легко

Ссылка на комментарий

Здравствуйте люди!

Пытаюсь подключить к сталку библу.

Через луа.(пакеты\модули)

Вроде не выпилено как debug.

Ну по крайней мере не вылетает ;)

Чего то никак не пойму как это сделать.

Для чего используются пути и тд...

Что за С загрузчик...

Мне бы примерчик по подключению и вызову функций из сторонней библы.

 

 

МЯСО!
Ссылка на комментарий

7.9

После рекомендаций xStream и Vint@rь код конечно сильно измениться, но принцип думаю не очень.

На данный момент примерно всё выглядит примерно так :

 

1. Существует модуль таймеров. Это одна большая таблица.

2. Все данные в этой таблице как бы разделены на две части.

Первая часть - таблица с созданными таймерами (которые также являются таблицами).

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

3. Каждый вызов функции create (из второй части) создаёт таблицу с таймером и помещает в таблицу всех таймеров (первая часть)

4. В каждой созданной таблице с новым таймером устанавливается поле complete в котором записывается время срабатывания.

5. При добавлении(удалении) нового таймера происходит сортировка таблиц всех таймеров по полю complete,

и таймер с ближайшим временем срабатывания занимает поле с индексом 1. Это поле (таблицу таймера под индексом 1) и обрабатывает функция update.

6. Для каждого из таймеров устанавливается метатаблица - сам модуль таймеров.

Поэтому возможен вызов методов из втоорой части, как то stop() и.т.д.

7. ...что-то много писать...может по ходу будешь спрашивать?

 

Добавлено через 199 мин.:

xStream

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

timer = {}

function timer:_init()
    self.__index    = self
    self.timers     = {}
    self.updateTime = 0
    setmetatable(self, self)
end

timer:_init()

function timer:__call()
    local obj = {k = 'I love lua'}
    setmetatable(obj, self)
    return obj
end

function timer:setName(name)
    name = type(name) == 'string' and name
    self.name = name
    return self
end

function timer:setTime(time)
    time = type(tonumber(time)) == 'number' and time or 0
    self.time = time
    return self
end

t = timer():setName('Super'):setTime(21)
print(t.k)
print(t.name)
print(t.time)

Я просто в восторге. Аж дух захватывает.

Изменено пользователем Gun12
Ссылка на комментарий
7.9 ... 7. ...что-то много писать...может по ходу будешь спрашивать?

Того - достаточно...

"По ходу" - это если что-то законченное будет... хотя-бы прототип... надеюсь :)

всё легко

Ссылка на комментарий

Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

Создать аккаунт

Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!

Зарегистрировать новый аккаунт

Войти

Есть аккаунт? Войти.

Войти
  • Недавно просматривали   0 пользователей

    • Ни один зарегистрированный пользователь не просматривает эту страницу.
×
×
  • Создать...