Вот есть мой старый код, ну скажем алгоритм на Jass, который вполне себе исправно работает
Jass код
//передаточная функция я просто вызываю её далее по коду если хочу что либо куда либо передвинуть ForceUnit( юнит, угол ,дистанция, скорость за тик 0,04, флаг (дополнительный параметр))
function ForceUnit takes unit u,real a,real d,real s,integer flag returns nothing
if GetUnitMoveSpeed(u) >= 1 and IsUnitType(u, UNIT_TYPE_STRUCTURE) == false then
call GroupAddUnit(gforce, u)
call SaveReal(udg_hash, GetHandleId(u), StringHash("a"), a)
call SaveReal(udg_hash, GetHandleId(u), StringHash("d"), d)
call SaveReal(udg_hash, GetHandleId(u), StringHash("s"), s)
call SaveInteger(udg_hash, GetHandleId(u), StringHash("flag"), flag)
endif
endfunction

//далее просто перебирается группа и делаются действия
function MainForce takes nothing returns nothing
local unit u=GetEnumUnit()
local real a=LoadReal(udg_hash, GetHandleId(u), StringHash("a"))
local real d=LoadReal(udg_hash, GetHandleId(u), StringHash("d"))
local real s=LoadReal(udg_hash, GetHandleId(u), StringHash("s"))
local integer flag=LoadInteger(udg_hash, GetHandleId(u), StringHash("flag"))
local real x=GetUnitX(u)
local real y=GetUnitY(u)
call SaveReal(udg_hash, GetHandleId(u), StringHash("d"), d - s)
//call BJDebugMsg(R2S(s))
//////ДОПОЛНИТЕЛЬНЫЕ флаги при движении
if flag == 1 then // босс эффект воды
//call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl",GetUnitX(u),GetUnitY(u)))
endif

if flag == 2 then // стоп
call IssueImmediateOrder(u, "stop")
call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl", GetUnitX(u), GetUnitY(u)))
//call BJDebugMsg("2")
endif

////Условия прекращения движения
if d <= 0 or Out(MoveX(x , s * 2 , a) , MoveY(y , s * 2 , a)) == false or GetPointZ(x , y) <= GetPointZ(MoveX(x , s * 2 , a) , MoveY(y , s * 2 , a)) - 30 then
if IsUnitType(u, UNIT_TYPE_HERO) == false then
call FlushChildHashtable(udg_hash, GetHandleId(u))
//call BJDebugMsg("BREAK")
endif

call GroupRemoveUnit(gforce, u)
endif //конец условий прекращения движения

// движение выполняется всегда за исключением условий прекращения движения
call SetUnitX(u, MoveX(x , s , a))
call SetUnitY(u, MoveY(y , s , a))
//call killtree(u,x,y,70)
//call BJDebugMsg("3")
set u=null
endfunction
Код можно не смотреть особо там крутится группа на таймере, в группе у юнита извлекаются на хештаблицах параметры для движения, но ОНB висят НА ЮНИТЕ
Как сделать что-то подобное на LUA?
Как сделать каждое толкание на отдельном таймере я понял, а как крутить группу на 1 таймере? (это тоже в принципе понятно)
Но что щас выступает аналогом load*handle?

Принятый ответ


А еще у меня там местами xpcall отладочный висит, там где я ловил косяки и не убрал его потом, без него код чуть проще становится, он нужен только для отлова ошибок и в моем случае вывода их на экран.

Это печально, новые костыли, а я уже обрадовался, что ничего обнулять и чистить не нужно =(
Не нужно обнулять локалки в функциях и все что ограниченного срока жизни, но всякие системы хранения данных они же либо глобальные либо в "локальном" скопе всей карты и, соответственно, живут пока загружена карта - там вручную подчищать надо, естественно. Можно делать это полу-автоматически как у меня, можно вручную когда становится известно что данные уже не нужны. Можно было бы использовать таблицу со слабыми ключами, но тогда возникает вопрос что использовать в качестве ключей - можно прямо на самого юнита вешать, чтобы когда игра его удалит из памяти, таблица почистилась сборщиком мусора, но нет гарантии что это будет работать и не будет вызывать десинки т.к. сборщику мусора синхронизация не указ.
Bergi_Bear:
но мне можно себе такой вариант забрать или надо ещё что-то дополнительно записать?
У меня библиотека отгорожена только ради того чтобы автоматизировать сборку мусора и создание новых таблиц при обращении по хендлу на который ничего нет.
Минимальный вариант, наверно, такой:
local HandleData = {} -- я использую локальные переменные в скопе карты, при этом важен порядок но доступ к ним идет чуть быстрее чем в глобальном скопе

function ForceUnit (u,a,d,s,flag)
  local data = HandleData[GetHandleId(u)]
  if (data==nil) then data = {} HandleData[GetHandleId(u)] = data end
  data.a = a
  data.d = d
  data.s = s
  data.flag = flag
  GroupAddUnit(gforce, u)
end
-- --------------
ForGroup(gforce, function()
  local u=GetEnumUnit()
  local h=GetHandleId(u)
  local data = HandleData[h]
  local a=data.a
  local d=data.d
  local s=data.s
  local flag=data.flag
  ...
end
`
ОЖИДАНИЕ РЕКЛАМЫ...
1
24
5 лет назад
Отредактирован prog
1
Два варианта
1 - массив-таблица с индексами по хендлу и еще одной таблицей в качестве значений
2 - передача переменных через кложуры (не твой случай ведь тебе на юнита данные вязать надо т.к. таймер один на всех)
Оба способа есть в примере ниже, если сможеш разобраться в том что там происходит.
немного моего сырого кода из тестовой карты
--========== Common Utils start ====================

local function RAW(id) --есть более эффективный вариант, но пока и так сойдет
  return id:byte(1) * 0x1000000 + 
         id:byte(2) * 0x10000 + 
         id:byte(3) * 0x100 +
         id:byte(4)
end

local function ehandler( err )
  print( "ERROR:", err )
end

--========== Common Utils end ====================
--========= Libraries start ==============

local Libraries = {libs = {}}

function Libraries:Init()
  print("Libraries Init")
  for id,lib in ipairs(self.libs) do
    if (lib.Init) then
      print("Pre Init "..lib.name)
--      lib:Init()
      xpcall(function() lib:Init() end,ehandler)
      print("Post Init "..lib.name)
    else
      print("Skip Init "..lib.name)
    end
  end
  for id,lib in ipairs(self.libs) do
    if (lib.Final) then
      print("Pre Final "..lib.name)
--    lib:Final()
      xpcall(function() lib:Final() end,ehandler)
      print("Post Final "..lib.name)
    else
      print("Skip Final "..lib.name)
    end
  end
end

function Libraries:new(name,dep)
  local lib = {}
  lib.name = name
  lib.dep = dep
  table.insert( self.libs, lib )
  self[name] = lib
  return lib
end

--========= Libraries end ==============
--============ CustomDataLibrary start ============

local DataStorage = Libraries:new("DataStorage",{})
function DataStorage:Reset(h)
  print("data reset "..h)
  self[x] = {}
  return self[x]
end
function DataStorage:Purge(h)
  print("data purge "..h)
  self[h] = nil
end
function DataStorage:new (o)
  o = o or {}
  o.Reset = DataStorage.Reset
  o.Purge = DataStorage.Purge
  setmetatable(o, DataStorage)
  DataStorage.__index = function (table, key) table[key] = {} return table[key] end
  return o
end

local HandleData
local ProtoData

function DataStorage:Init()
    HandleData = DataStorage:new()
    ProtoData = DataStorage:new()
end

function DataStorage:Final()
  local clean = Libraries.CleanUp
  table.insert( clean.DeathHandlers,function(u,h) HandleData:Purge(h) end)
  table.insert( clean.RemoveHandlers,function(u,h) HandleData:Purge(h) end)
end

--============ CustomDataLibrary end ==============
--============ CleanUpLib start ===============

local CleanUp =  Libraries:new("CleanUp",{})
CleanUp.DeathHandlers={}
CleanUp.RemoveHandlers={}
CleanUp.RemoveUnitBase=nil

function CleanUp.OnDeath()
  local unit u = GetTriggerUnit()
  local id = GetHandleId(u)
  for n,h in ipairs(CleanUp.DeathHandlers) do h(u,id) end
end

function CleanUp.OnRemove(u)
  local id = GetHandleId(u)
  for n,h in ipairs(CleanUp.RemoveHandlers) do h(u,id) end
  return CleanUp.RemoveUnitBase(u)
end

function CleanUp:Init()
  local trig = CreateTrigger()
  TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_DEATH )
  TriggerAddAction( trig, CleanUp.OnDeath )

  self.RemoveUnitBase=RemoveUnit
  RemoveUnit = self.OnRemove
end

function CleanUp:Final()
  table.insert( self.DeathHandlers,function(u,h) print("Final Unit Death Handler "..GetUnitName(u)) end)
  table.insert( self.RemoveHandlers,function(u,h) print("Final Unit Remove Handler "..GetUnitName(u)) end)
end

--============ CleanUpLib end ===============
--============ TestLib start ===============

local TestLib = Libraries:new("TestLib",{})

function TestLib.TestGroup(g)
  print("group test start")
  xpcall(function()
    local a = 42
    local i = 0
    ForGroup(g, function()
      xpcall(function()
        local u = GetEnumUnit()
        print('i='..i..' a='..a.." "..GetHandleId(u).." "..GetUnitName(u))
        i = i +1
      end,ehandler)
    end)
  end,ehandler)
  print("group test end")
end

TestLib.TimerCounter=0
function TestLib.TestTimers()
  local t1 = CreateTimer()
  local i = 0
  local a = 42
  local x = TestLib.TimerCounter
  print('start timer '..x)
  TimerStart(t1, 1, true, function()
    print('timer '..x..' tick '..i..' a='..a)
    i = i +1
  end)
  TestLib.TimerCounter = TestLib.TimerCounter+1
end

function TestLib.TestUnit(u)
  print("unit test start")
  print(u)
  local h = GetHandleId(u)
  print("handle "..h)
  local data = HandleData[h]
  if (data.num==nil)then
    data.num=0
  end
  data.num = data.num+1
  BlzSetUnitRealField(u, UNIT_RF_DEFENSE, data.num)

  print("unit test end")
end

--============ TestLib end ===============
0
32
5 лет назад
0
prog, спасибо сам тоже только что до этого дошёл, даже всё работает
prog:
1 - массив-таблица с индексами по хендлу и еще одной таблицей в качестве значений
примерно так сделал:
------ новая толкалка
do
af={}
df={}
sf={}
flagf={}
gforce=CreateGroup()

function ForceUnit (u,a,d,s,flag)
GroupAddUnit(gforce, u)
af[GetHandleId(u)]=a
df[GetHandleId(u)]=d
sf[GetHandleId(u)]=s
flagf[GetHandleId(u)]=flag
end


TimerStart(CreateTimer(), 0.04, true, function()
ForGroup(gforce, function()
local u=GetEnumUnit()
local h=GetHandleId(u)
local a=af[h]
local d=df[h]
local s=sf[h]
local flag=flagf[h]
--action
local x=GetUnitX(u)
local y=GetUnitY(u)


df[h]=d-s
--Условия прекращения движения
if d <= 0 or Out(MoveX(x , s * 2 , a) , MoveY(y , s * 2 , a)) == false or IsUnitDeadBJ(u) then --or GetPointZ(x , y) <= GetPointZ(MoveX(x , s * 2 , a) , MoveY(y , s * 2 , a)) - 30
 GroupRemoveUnit(gforce, u)
if flag==0 then
KillUnit(u)
end
end 

-- движение выполняется всегда за исключением условий прекращения движения
SetUnitX(u, MoveX(x , s , a))
SetUnitY(u, MoveY(y , s , a))



---------
end)--group    
end)--timer
end
0
24
5 лет назад
0
Bergi_Bear, у тебя по таблице под каждое значение, а в моем варианте одна таблица в которой внутри по таблице на каждый хендл на который что-то прикручено. Мой вариант удобней в использовании т.к. все что связано с одним юнитом собрано в одном месте.
0
32
5 лет назад
0
prog, в любом случае свой вариант я хотя бы понимаю и это его главный плюс, но в понял тебя, можно же сделать многомерный массив
prog:
одна таблица в которой внутри по таблице на каждый хендл
Насчет удобства я пока не могу оценить ибо вообще ничего не понимаю, что происходит, но очищать ничего не надо? юнит умер, его записи остались в таблице, сборщик мусора все сделает?
тот самый сборщик о котором все говорят?
0
24
5 лет назад
0
На примере твоего кода - в моем варианте работа с данными будет выглядеть примерно так:
function ForceUnit (u,a,d,s,flag)
  local data = HandleData[GetHandleId(u)]
  data.a = a
  data.d = d
  data.s = s
  data.flag = flag
  GroupAddUnit(gforce, u)
end
-- --------------
ForGroup(gforce, function()
  local u=GetEnumUnit()
  local h=GetHandleId(u)
  local data = HandleData[h]
  local a=data.a
  local d=data.d
  local s=data.s
  local flag=data.flag
  ...
end

Насчет удобства я пока не могу оценить ибо вообще ничего не понимаю, что происходит, но очищать ничего не надо? юнит умер, его записи остались в таблице, сборщик мусора все сделает?
Сборщик сам не настолько крут, поэтому у меня там есть функции Purge и Reset для сноса данных из таблицы, а также целая либа CleanUp для перехвата смертей и удаления юнита, на которую либа хранения данных вешает свой хук в котором и подчищаются данные для таких юнитов.
0
32
5 лет назад
0
prog, ну да, через точку писать более удобней, чем через [скобочки], но мне можно себе такой вариант забрать или надо ещё что-то дополнительно записать?
пошёл пробовать
prog:
Сборщик сам не настолько крут, поэтому у меня там есть функции Purge и Reset для сноса данных из таблицы, а также целая либа CleanUp для перехвата смертей и удаления юнита, на которую либа хранения данных вешает свой хук в котором и подчищаются данные для таких юнитов.
Это печально, новые костыли, а я уже обрадовался, что ничего обнулять и чистить не нужно =(
2
24
5 лет назад
Отредактирован prog
2

А еще у меня там местами xpcall отладочный висит, там где я ловил косяки и не убрал его потом, без него код чуть проще становится, он нужен только для отлова ошибок и в моем случае вывода их на экран.

Это печально, новые костыли, а я уже обрадовался, что ничего обнулять и чистить не нужно =(
Не нужно обнулять локалки в функциях и все что ограниченного срока жизни, но всякие системы хранения данных они же либо глобальные либо в "локальном" скопе всей карты и, соответственно, живут пока загружена карта - там вручную подчищать надо, естественно. Можно делать это полу-автоматически как у меня, можно вручную когда становится известно что данные уже не нужны. Можно было бы использовать таблицу со слабыми ключами, но тогда возникает вопрос что использовать в качестве ключей - можно прямо на самого юнита вешать, чтобы когда игра его удалит из памяти, таблица почистилась сборщиком мусора, но нет гарантии что это будет работать и не будет вызывать десинки т.к. сборщику мусора синхронизация не указ.
Bergi_Bear:
но мне можно себе такой вариант забрать или надо ещё что-то дополнительно записать?
У меня библиотека отгорожена только ради того чтобы автоматизировать сборку мусора и создание новых таблиц при обращении по хендлу на который ничего нет.
Минимальный вариант, наверно, такой:
local HandleData = {} -- я использую локальные переменные в скопе карты, при этом важен порядок но доступ к ним идет чуть быстрее чем в глобальном скопе

function ForceUnit (u,a,d,s,flag)
  local data = HandleData[GetHandleId(u)]
  if (data==nil) then data = {} HandleData[GetHandleId(u)] = data end
  data.a = a
  data.d = d
  data.s = s
  data.flag = flag
  GroupAddUnit(gforce, u)
end
-- --------------
ForGroup(gforce, function()
  local u=GetEnumUnit()
  local h=GetHandleId(u)
  local data = HandleData[h]
  local a=data.a
  local d=data.d
  local s=data.s
  local flag=data.flag
  ...
end
Принятый ответ
0
32
5 лет назад
0
гарантии что это будет работать и не будет вызывать десинки т.к. сборщику мусора синхронизация не указ
я конечно лишь примерно представляю как это всё работает, но скорее всего не должно быть десинхов, если данные не использются больше, вроде бы как даже это логично, но будет десинх, если кривым кодом куда-то не туда обратится и попытаться получить какие либо данные
Такс, prog, не получается =(
HandleData={}
gforce=CreateGroup()

function ForceUnit (u,a,d,s,flag)
    print("start")
GroupAddUnit(gforce, u)
local datan = HandleData[GetHandleId(u)]
datan.a = a
datan.d = d
datan.s = s
datan.flag = flag
end


TimerStart(CreateTimer(), 0.04, true, function()
ForGroup(gforce, function()
local u=GetEnumUnit()
local h=GetHandleId(u)
local data = HandleData[h]
local a=data.a
local d=data.d
local s=data.s
local flag=data.flag
print("period")
Дебага с надписью период нет, значит даже таймер не стартует =(
0
24
5 лет назад
0
Bergi_Bear, смотри выше - я в последнем комменте дописал "минимальный" вариант замены моих библиотек, тебе не хватает одной важной строки при работе с данными
if (data==nil) then data = {} HandleData[GetHandleId(u)] = data end
0
32
5 лет назад
0
а вот это ?
  if (data==nil) then data = {} HandleData[GetHandleId(u)] = data end
Чтобы повторно не создавать запись для юнита, а извлечь текущую?
prog, просто я раньше начал писать чем ты отправил и я не видел твоего того сообщения =)
0
24
5 лет назад
0
Рекомендую освоить xpcall для отладки, он принимает два аргумента - функцию которую надо выполнить, можно анонимную и функцию в которую будет передана ошибка если что-то пойдет не так при выполнении функции из первого аргумента. В моем первом комменте много вызовов xpcall, посмотри на примере.
0
32
5 лет назад
0
всё работает, полёт нормальный =)
0
24
5 лет назад
Отредактирован prog
0
Чтобы повторно не создавать запись для юнита, а извлечь текущую?
Поскольку я исхожу из того что в одном месте хранится вся привязанная к юниту информация, то да, извлекается старая запись если такая была.
Но не только - еще это нужно чтобы создать запись для юнита если её еще нет. И именно поэтому сыпался твой вариант без этой строки - по хендлу мы вынимали nil (он же null если по джасовски) если на хендл еще ничего не было записано и на этом все падало, естественно, т.к. nil это ни разу не таблица. У меня без этой строки работало потому как у меня в библиотеке переопределена операция получения значения из таблицы HandleData - либа сама создает новые таблицы-записи если на хендл еще ничего не записано.
0
32
5 лет назад
0
prog, так, тока я не понял как чистить, смотрю в твой код и вижу фигу, мне нужно будет в каждую ячейку таблицы записать nill ?
А вот наверное понял
function CleanUp.OnDeath()
  local unit u = GetTriggerUnit()
  local id = GetHandleId(u)
 ** for n,h in ipairs(CleanUp.DeathHandlers) do h(u,id) end**
end
а как это для меня в минимальном варианте будет выглядеть?
2
24
5 лет назад
Отредактирован prog
2
То что ты нашел - это я храню список функций которые вызываются триггером отслеживающим смерти юнитов - чтобы не плодить множество таких триггеров без необходимости и заодно контролировать порядок срабатывания обработчиков смерти когда это понадобится.
В твоем случае - смотря что ты чистить будеш.
Если данные связаные с системой - обнуляеш те переменные в таблице-записи которые использовал и которые не нужны в других местах. data.s = nil data.a=nil и так далее.
Если все данные на юнита (юнит умер или удаляется) - записываем nil по хендлу. HandleData[GetHandleId(u)] = nil.
У меня в либе для ручной чистки я вызвал бы HandleData:Purge(GetHandleId(u)), если бы не полагался на автоматическую чистку по какой-то причине, но там внутри пока все то-же самое присвоение nil.

Для особо тяжелых случаев можно заводить еще дочерние записи в таблице-записи - если какая-то система хранит много данных на одного юнита, особенно если все эти данные временные, то можно ложить их целой таблицей и потом всей таблицей и убивать
data.somebulkdata = { a=42 b=42 c=42}
data.somebulkdata = nil

Еще есть нюанс - для критичных по производительности мест стоит использовать массивы вместо таблиц. По сути те-же таблицы, но с целочисленной нумерацией элементов вместо обращения по имени. Внутренная реализация таблиц оптимизирует такую ситуацию и обращение к элементам происходит быстрее.
0
32
5 лет назад
0
Как сложно... к этому в пту меня не готовили
но вроде понял, спасибо ещё раз
думаю мне достаточно писать nil по хендлу, это ближайший аналог к FlushChildHashatable
надо ковырять, в любом случае не плохо для первого дня знакомства с lua, я очень доволен, весьма удобно
Чтобы оставить комментарий, пожалуйста, войдите на сайт.