XGM Forum
Сайт - Статьи - Проекты - Ресурсы - Блоги

Форуме в режиме ТОЛЬКО ЧТЕНИЕ. Вы можете задать вопросы в Q/A на сайте, либо создать свой проект или ресурс.
Вернуться   XGM Forum > Warcraft> Академия: форум для вопросов> Jass
Ник
Пароль
Войти через VK в один клик
Сайт использует только имя.

Закрытая тема
 
J
expert
offline
Опыт: 48,447
Активность:
[Task] Свободный индекс
Задача решена Markiz-ом, ему перечисленно 300 опыта.

Допустим, нам не нравится Кеш, и мы хотим перейти на массивы, конечно полностью заменить Кеш массивами нереально, или реально, но уже не оптимально, но частично - вполне возможно.
Единственное что можно сделать, это минимизировать количество обращений к Кешу, а именно только к одному обращению.

как это будет работать?
Расскажу на примере движения прожектила на таймере:

На начало у нас имеется несколько глобальных параллельных массивов
(параллельных это значит, что у всех этих массивов под одним и тем же номером ячейки находится информация, сопоставленная одному объекту)

Так же у нас будет массив булеонов (udg_Free), который обозначает – занят(true) или свободен(false) данный слот. И целочисленная переменная(udg_FreeIndex ), которая обозначает самый ближайший свободный слот, это нужно для уменьшения итераций цикла при поиске свободного слота. И вот как это будет выглядеть:
Код:
globals
    boolean array udg_Free
    integer udg_FreeIndex = 0
    integer udg_Size = 3 // Константа, определяет размер области массива, выделяемый на один обьект.
endglobals

function FreeIndex takes nothing returns nothing
    loop
        exitwhen not udg_Free[udg_FreeIndex]
        set udg_FreeIndex = udg_FreeIndex + udg_Size
    endloop
endfunction

function ClearIndex takes integer index returns nothing
    set udg_Free[index] = false
    if index < udg_FreeIndex then  
        set udg_FreeIndex = index
    endif
endfunction

function GetIndex takes nothing returns integer
    return udg_FreeIndex
endfunction

function SetIndexObject takes handle Object, integer Index returns nothing 
    set udg_Free[Index] = true
    call StoreInteger(udg_cache, "Index", I2S(H2I(Object)), Index)
endfunction

function GetIndexObject takes handle Object returns integer
    return GetStoredInteger(udg_cache, "Index", I2S(H2I(Object)))
endfunction

function RemoveIndexObject takes handle Object returns nothing
    call FlushStoredInteger(udg_cache, "Index", I2S(H2I(Object)))
endfunction

FreeIndex() – устанавливает переменную udg_FreeIndex на значение самого ближайшего свободного слота, методом перебора.
ClearIndex() – Очищает занятый слот, и после этого он снова свободный, и если он находится ближе чем текущее значение переменной udg_FreeIndex то присвоить его этой переменной как ближайший свободный слот. Т.е. применять ее когда таймер закончился.
SetIndexObject() – Сначала обозначает этот слот как занятый, а потом через Кеш сопоставялет обьекту (в нашем случае таймеру) Номер этого слота.
GetIndexObject() – Применяется в функции таймера и возвращает сопоставленный объекту индекс, чтобы брать всю информацию с параллельных массивов под тем же индексом.
RemoveIndexObject() – Стирает информацию из Кеша для данного объекта, нужно когда таймер уже закончился.
Вот пример использования:
Код:
function DistanceBetweenCord takes real AX, real AY, real BX, real BY returns real
    local real dx = BX - AX
    local real dy = BY - AY
    return SquareRoot(dx * dx + dy * dy)
endfunction

function AngleBetweenCord takes real AX, real AY, real BX, real BY returns real
    return bj_RADTODEG * Atan2(BY - AY, BX - AX)
endfunction 
function Projectile_Move_Timer takes nothing returns nothing
    local timer ProjectileMove = GetExpiredTimer()
    local integer index = GetIndexObject(ProjectileMove)
    local real Xs = udg_Real[index  ]
    local real Ys = udg_Real[index+1]
    local real Xp = GetUnitX(udg_Unit[index+1])
    local real Yp = GetUnitY(udg_Unit[index+1])
    local real Xe = GetUnitX(udg_Unit[index  ])
    local real Ye = GetUnitY(udg_Unit[index  ])      
    local real S     = DistanceBetweenCord(udg_Real[index], udg_Real[index+1], Xp, Yp)           
    local real len   = DistanceBetweenCord(udg_Real[index], udg_Real[index+1], Xe, Ye)    
    local real Angle = AngleBetweenCord   (Xp, Yp                            , Xe, Ye)
    local real hig   = len*0.4
    if (len-S) > 30 then    
        call SetUnitX(udg_Unit[index+1], Xp+udg_Real[index+2]*Cos(Angle*bj_DEGTORAD))
        call SetUnitY(udg_Unit[index+1], Yp+udg_Real[index+2]*Sin(Angle*bj_DEGTORAD))  
        call SetUnitFlyHeight(udg_Unit[index+1], 4*S*hig*(1-S/len)/len, 0.0 )
        call SetUnitFacing(udg_Unit[index+1], Angle)
    else
        call RemoveUnit(udg_Unit[index+1]) 
        call UnitDamageTargetBJ( udg_Unit[index+2], udg_Unit[index], 20, ATTACK_TYPE_PIERCE, DAMAGE_TYPE_NORMAL )
        call RemoveIndexObject (ProjectileMove)  
        call ClearIndex (index)              
        call DestroyTimer(ProjectileMove) 
    endif
endfunction

function Projectile_Move takes unit Unit1, unit Unit2 returns nothing    
    local timer ProjectileMove = CreateTimer()   
    local integer index = GetIndex()
    set udg_Unit[index  ] = Unit1
    set udg_Unit[index+2] = Unit2
    set udg_Real[index  ] = GetUnitX(Unit2)
    set udg_Real[index+1] = GetUnitY(Unit2)
    set udg_Real[index+2] = 10 
    set udg_Unit[index+1] = CreateUnit(GetOwningPlayer(Unit1), 'e000', udg_Real[index], udg_Real[index+1], AngleBetweenCord(udg_Real[index], udg_Real[index+1], GetUnitX(Unit1), GetUnitY(Unit1)))   
    call TimerStart(ProjectileMove, 0.01, true, function Projectile_Move_Timer)
    call SetIndexObject(ProjectileMove, index)  
    call FreeIndex()
    set ProjectileMove = null
endfunction

И серьезно, получается несколько быстрее чем если все делать через кеш, однако весь вид портит цикл при поиске слота... это... так сказать.. не по трукодерски
Теперь Задача:
Нужно сделать так чтобы в функции FreeIndex() не было цикла, т.е. находить свободный слот не перебором, в сразуже.
Условия:
Не трогать функции по работе с таймером, также можно добавить и свои функции… но только чтобы этот код с таймером работал…
Разрешается использовать любой набор глобальных переменный.

Отредактировано ShadoW DaemoN, 07.08.2008 в 00:24.
Старый 02.08.2007, 17:34
Alex_Hell
Mapmaker 'N' Programmer
offline
Опыт: 6,885
Активность:
Как я думаю, нужно при удалении таймера изменить переменную udg_FreeIndex на
udg_FreeIndex - udg_Size, тогда не нужна будет глобалка udg_Free...просто сразу в udg_FreeIndex будет храниться номер ближайшей свободной ячейки...Jon, я правильно думаю...или нет?
Старый 04.08.2007, 17:10
exAres
I love magic :)
offline
Опыт: 7,788
Активность:
Alex_Hell нет! т.к. разные "снаряды" могут "лететь" разное время т.е. они не обязательно будут освобождатся по-порядку.
Старый 04.08.2007, 17:53
Alex_Hell
Mapmaker 'N' Programmer
offline
Опыт: 6,885
Активность:
Ну неправильно, так неправильно...вообщето я не очень то и сильно разбирался в этом коде...
Старый 04.08.2007, 18:16
YasonDelAlt

offline
Опыт: 862
Активность:
Jon, вот мое решение, по теории должно работать:
Код:
globals
    boolean array udg_Free
    // integer udg_FreeIndex = 0 // Не нужна!
    integer udg_Size = 3

    integer array udg_FreeStack
    integer udg_FreeStackTop = -1
    integer udg_FreeTop = -udg_Size
endglobals

function ClearIndex takes integer index returns nothing
    set udg_Free[index] = false
    set udg_FreeStackTop = udg_FreeStackTop + 1
    set udg_FreeStack[udg_FreeStackTop] = index
endfunction

function GetIndex takes nothing returns integer
    if udg_FreeStackTop >= 0 then
        set udg_FreeStackTop = udg_FreeStackTop - 1
        return udg_FreeStack[udg_FreeStackTop]
    else
        set udg_FreeTop = udg_FreeTop + udg_Size
        return udg_FreeTop
    endif
endfunction

function SetIndexObject takes handle Object, integer Index returns nothing 
    set udg_Free[Index] = true
    call StoreInteger(udg_cache, "Index", I2S(H2I(Object)), Index)
endfunction

function GetIndexObject takes handle Object returns integer
    return GetStoredInteger(udg_cache, "Index", I2S(H2I(Object)))
endfunction

function RemoveIndexObject takes handle Object returns nothing
    call FlushStoredInteger(udg_cache, "Index", I2S(H2I(Object)))
endfunction


function Projectile_Move_Timer takes nothing returns nothing
    local timer ProjectileMove = GetExpiredTimer()
    local integer index = GetIndexObject(ProjectileMove)
    local real Xs = udg_Real[index  ]
    local real Ys = udg_Real[index+1]
    local real Xp = GetUnitX(udg_Unit[index+1])
    local real Yp = GetUnitY(udg_Unit[index+1])
    local real Xe = GetUnitX(udg_Unit[index  ])
    local real Ye = GetUnitY(udg_Unit[index  ])      
    local real S     = DistanceBetweenCord(udg_Real[index], udg_Real[index+1], Xp, Yp)           
    local real len   = DistanceBetweenCord(udg_Real[index], udg_Real[index+1], Xe, Ye)    
    local real Angle = AngleBetweenCord   (Xp, Yp                            , Xe, Ye)
    local real hig   = len*0.4
    if (len-S) > 30 then    
        call SetUnitX(udg_Unit[index+1], Xp+udg_Real[index+2]*Cos(Angle*bj_DEGTORAD))
        call SetUnitY(udg_Unit[index+1], Yp+udg_Real[index+2]*Sin(Angle*bj_DEGTORAD))  
        call SetUnitFlyHeight(udg_Unit[index+1], 4*S*hig*(1-S/len)/len, 0.0 )
        call SetUnitFacing(udg_Unit[index+1], Angle)
    else
        call RemoveUnit(udg_Unit[index+1]) 
        call UnitDamageTargetBJ( udg_Unit[index+2], udg_Unit[index], 20, ATTACK_TYPE_PIERCE, DAMAGE_TYPE_NORMAL )
        call RemoveIndexObject (ProjectileMove)  
        call ClearIndex (index)              
        call DestroyTimer(ProjectileMove) 
    endif
endfunction

function Projectile_Move takes unit Unit1, unit Unit2 returns nothing    
    local timer ProjectileMove = CreateTimer()   
    local integer index = GetIndex()
    set udg_Unit[index  ] = Unit1
    set udg_Unit[index+2] = Unit2
    set udg_Real[index  ] = GetUnitX(Unit2)
    set udg_Real[index+1] = GetUnitY(Unit2)
    set udg_Real[index+2] = 10 
    set udg_Unit[index+1] = CreateUnit(GetOwningPlayer(Unit1), 'e000', udg_Real[index], udg_Real[index+1], AngleBetweenCord(udg_Real[index], udg_Real[index+1], GetUnitX(Unit1), GetUnitY(Unit1)))   
    call TimerStart(ProjectileMove, 0.01, true, function Projectile_Move_Timer)
    call SetIndexObject(ProjectileMove, index)
    //call FreeIndex()// !!! Пришлось избавится, она нафиг не нужна, надеюсь ты не против
    set ProjectileMove = null
endfunction

Как видишь, Projectile_Move не трогать не удалось, и пришлось удалить кое-что. Никаких циклов. Свободный слот вычисляется сразу в GetIndex().
Старый 05.08.2007, 16:31
J
expert
offline
Опыт: 48,447
Активность:
YasonDelAlt эм... код очень похож на мой
как будет время проверю...

и че? все? только одын?
Старый 06.08.2007, 20:40
YasonDelAlt

offline
Опыт: 862
Активность:
Jon, особых конструкционных особенностей нет, только добавился стек освобожденных слотов и исчезла функция с циклом (FreeIndex()).
Старый 06.08.2007, 21:29
Markiz

offline
Опыт: 11,432
Активность:
мой вариант (чтобы затестить - выберите мк и заюзайте стормболт на вражеском лолодине)
кастом код карты:
Код:
function H2I takes handle h returns integer
  return h
  return 0
endfunction


function InitIndexes takes integer MaxIndex returns nothing
local integer i=0
  loop
    exitwhen i>MaxIndex
    set udg_Indexes[i]=i+1
    set i=i+1
  endloop
endfunction

function FreeIndex takes nothing returns nothing
  //This function is not needed :)
endfunction

function ClearIndex takes integer index returns nothing
  set index=index/udg_Size
  set udg_Indexes[index]=udg_FreeIndex
  set udg_FreeIndex=index
endfunction

function GetIndex takes nothing returns integer
  return udg_FreeIndex*udg_Size
endfunction

function SetIndexObject takes handle Object, integer Index returns nothing 
  call StoreInteger(udg_cache, "Index", I2S(H2I(Object)), Index)
  set Index=Index/udg_Size
  set udg_FreeIndex=udg_Indexes[Index]
endfunction

function GetIndexObject takes handle Object returns integer
    return GetStoredInteger(udg_cache, "Index", I2S(H2I(Object)))
endfunction

function RemoveIndexObject takes handle Object returns nothing
    call FlushStoredInteger(udg_cache, "Index", I2S(H2I(Object)))
endfunction 

 //=======Ðàñòðîÿíèå ìåæäó òî÷êàìè (AX; AY) è (BX; BY)===========

function DistanceBetweenCord takes real AX, real AY, real BX, real BY returns real
    local real dx = BX - AX
    local real dy = BY - AY
    return SquareRoot(dx * dx + dy * dy)
endfunction          
   
//=======Óãîë ìåæäó òî÷êàìè (AX; AY) è (BX; BY)===========

function AngleBetweenCord takes real AX, real AY, real BX, real BY returns real
    return bj_RADTODEG * Atan2(BY - AY, BX - AX)
endfunction 

function Projectile_Move_Timer takes nothing returns nothing
    local timer ProjectileMove = GetExpiredTimer()
    local integer index = GetIndexObject(ProjectileMove)
    local real Xs = udg_Real[index  ]
    local real Ys = udg_Real[index+1]
    local real Xp = GetUnitX(udg_Unit[index+1])
    local real Yp = GetUnitY(udg_Unit[index+1])
    local real Xe = GetUnitX(udg_Unit[index  ])
    local real Ye = GetUnitY(udg_Unit[index  ])      
    local real S     = DistanceBetweenCord(udg_Real[index], udg_Real[index+1], Xp, Yp)           
    local real len   = DistanceBetweenCord(udg_Real[index], udg_Real[index+1], Xe, Ye)    
    local real Angle = AngleBetweenCord   (Xp, Yp                            , Xe, Ye)
    local real hig   = len*0.4
    if (len-S) > 30 then    
        call SetUnitX(udg_Unit[index+1], Xp+udg_Real[index+2]*Cos(Angle*bj_DEGTORAD))
        call SetUnitY(udg_Unit[index+1], Yp+udg_Real[index+2]*Sin(Angle*bj_DEGTORAD))  
        call SetUnitFlyHeight(udg_Unit[index+1], 4*S*hig*(1-S/len)/len, 0.0 )
        call SetUnitFacing(udg_Unit[index+1], Angle)
    else
        call RemoveUnit(udg_Unit[index+1]) 
        call UnitDamageTargetBJ( udg_Unit[index+2], udg_Unit[index], 20, ATTACK_TYPE_PIERCE, DAMAGE_TYPE_NORMAL )
        call RemoveIndexObject (ProjectileMove)  
        call ClearIndex (index)              
        call DestroyTimer(ProjectileMove) 
    endif
endfunction

function Projectile_Move takes unit Unit1, unit Unit2 returns nothing    
    local timer ProjectileMove = CreateTimer()   
    local integer index = GetIndex()
    set udg_Unit[index  ] = Unit1
    set udg_Unit[index+2] = Unit2
    set udg_Real[index  ] = GetUnitX(Unit2)
    set udg_Real[index+1] = GetUnitY(Unit2)
    set udg_Real[index+2] = 10 
    set udg_Unit[index+1] = CreateUnit(GetOwningPlayer(Unit1), 'ewsp', udg_Real[index], udg_Real[index+1], AngleBetweenCord(udg_Real[index], udg_Real[index+1], GetUnitX(Unit1), GetUnitY(Unit1)))   
    call TimerStart(ProjectileMove, 0.01, true, function Projectile_Move_Timer)
    call SetIndexObject(ProjectileMove, index)  
    call FreeIndex()
    set ProjectileMove = null
endfunction
Прикрепленные файлы
Тип файла: w3x for_joneg_arrays.w3x (19.1 Кбайт, 27 просмотров )
Старый 07.08.2007, 00:32
J
expert
offline
Опыт: 48,447
Активность:
хм... работает...:):
у меня 2 переменые и один масив.. у YasonDelAlt 2 масива и 2 обычные, а сдесь вроде одной переменой меньше.. это +
P.S.
хотя нет.. у YasonDelAlt и у меня не похожи...

Отредактировано Jon, 11.07.2008 в 21:39.
Старый 07.08.2007, 00:48
YasonDelAlt

offline
Опыт: 862
Активность:
Markiz, можешь объяснить, как это работает? Я что-то смотрю, смотрю и ни как не въеду.
Старый 09.08.2007, 08:53
Markiz

offline
Опыт: 11,432
Активность:
YasonDelAlt
В массиве Indexes в ячейке i хранится следующий свободный индекс. В переменной FreeIndex хранится индекс первой незаполненной ячейки.
При инициализации мы в каждую i ячейку пишем i+1. FreeIndex присваивается 0 по умолчанию.
При добавлении индекса FreeIndex'у присваивается значение из ячейки массива.
При удалении индекса K в массив в ячейку К пишется значение FreeIndex, а FreeIndex присваивается значение К

Примечание: под первой незаполненной ячейкой мы подразумеваем не обязательно первую по порядку, но совершенно точно последнюю из освобожденных, поэтому "дырок" в массиве не возникнет.
Старый 09.08.2007, 16:22
YasonDelAlt

offline
Опыт: 862
Активность:
Markiz, понял, интересный метод.
Старый 09.08.2007, 16:38
J
expert
offline
Опыт: 48,447
Активность:
YasonDelAlt я сделал также, но несколько иначе

Jon добавил:
но к сожелению у меня есть дырки в масиве, но работе это немешает, потому ща пока доминирует вариант марзика, если никто в ближайшее время ничего не предложит еще он победит
Старый 09.08.2007, 16:41
YasonDelAlt

offline
Опыт: 862
Активность:
Jon, а у меня вообще два массива, так что получается его вариант - лучший. Только я не пойму, почему он делит и умножает индексы, лучше же использовать сложение и вычитание - быстрее работает.
Старый 10.08.2007, 20:56
Markiz

offline
Опыт: 11,432
Активность:
YasonDelAlt
Это ты как деление заменишь вычитанием? о_О
Старый 11.08.2007, 08:46
YasonDelAlt

offline
Опыт: 862
Активность:
Markiz, если я правильно понимаю код, то все set index=index/udg_Size можно заменить на set index=index-udg_Size, а set index=index*udg_Size, на set index=index+udg_Size.

YasonDelAlt добавил:
Что-то я торможу. До меня только-что дошло, что массив udg_Free использовался Jon'ом только для поиска свободных слотов. Вот мой исправленный вариант:
Код:
globals
    integer udg_Size = 3
    integer array udg_FreeStack
    integer udg_FreeStackTop = -1
    integer udg_FreeTop = -udg_Size
endglobals

function ClearIndex takes integer index returns nothing
    set udg_FreeStackTop = udg_FreeStackTop + 1
    set udg_FreeStack[udg_FreeStackTop] = index
endfunction

function GetIndex takes nothing returns integer
    if udg_FreeStackTop >= 0 then
        set udg_FreeStackTop = udg_FreeStackTop - 1
        return udg_FreeStack[udg_FreeStackTop]
    else
        set udg_FreeTop = udg_FreeTop + udg_Size
        return udg_FreeTop
    endif
endfunction

function SetIndexObject takes handle Object, integer Index returns nothing 
    call StoreInteger(udg_cache, "Index", I2S(H2I(Object)), Index)
endfunction

function GetIndexObject takes handle Object returns integer
    return GetStoredInteger(udg_cache, "Index", I2S(H2I(Object)))
endfunction

function RemoveIndexObject takes handle Object returns nothing
    call FlushStoredInteger(udg_cache, "Index", I2S(H2I(Object)))
endfunction


function Projectile_Move_Timer takes nothing returns nothing
    local timer ProjectileMove = GetExpiredTimer()
    local integer index = GetIndexObject(ProjectileMove)
    local real Xs = udg_Real[index  ]
    local real Ys = udg_Real[index+1]
    local real Xp = GetUnitX(udg_Unit[index+1])
    local real Yp = GetUnitY(udg_Unit[index+1])
    local real Xe = GetUnitX(udg_Unit[index  ])
    local real Ye = GetUnitY(udg_Unit[index  ])      
    local real S     = DistanceBetweenCord(udg_Real[index], udg_Real[index+1], Xp, Yp)           
    local real len   = DistanceBetweenCord(udg_Real[index], udg_Real[index+1], Xe, Ye)    
    local real Angle = AngleBetweenCord   (Xp, Yp                            , Xe, Ye)
    local real hig   = len*0.4
    if (len-S) > 30 then    
        call SetUnitX(udg_Unit[index+1], Xp+udg_Real[index+2]*Cos(Angle*bj_DEGTORAD))
        call SetUnitY(udg_Unit[index+1], Yp+udg_Real[index+2]*Sin(Angle*bj_DEGTORAD))  
        call SetUnitFlyHeight(udg_Unit[index+1], 4*S*hig*(1-S/len)/len, 0.0 )
        call SetUnitFacing(udg_Unit[index+1], Angle)
    else
        call RemoveUnit(udg_Unit[index+1]) 
        call UnitDamageTargetBJ( udg_Unit[index+2], udg_Unit[index], 20, ATTACK_TYPE_PIERCE, DAMAGE_TYPE_NORMAL )
        call RemoveIndexObject (ProjectileMove)  
        call ClearIndex (index)              
        call DestroyTimer(ProjectileMove) 
    endif
endfunction

function Projectile_Move takes unit Unit1, unit Unit2 returns nothing    
    local timer ProjectileMove = CreateTimer()   
    local integer index = GetIndex()
    set udg_Unit[index  ] = Unit1
    set udg_Unit[index+2] = Unit2
    set udg_Real[index  ] = GetUnitX(Unit2)
    set udg_Real[index+1] = GetUnitY(Unit2)
    set udg_Real[index+2] = 10 
    set udg_Unit[index+1] = CreateUnit(GetOwningPlayer(Unit1), 'e000', udg_Real[index], udg_Real[index+1], AngleBetweenCord(udg_Real[index], udg_Real[index+1], GetUnitX(Unit1), GetUnitY(Unit1)))   
    call TimerStart(ProjectileMove, 0.01, true, function Projectile_Move_Timer)
    call SetIndexObject(ProjectileMove, index)
    //call FreeIndex()// !!! Пришлось избавится, она нафиг не нужна, надеюсь ты не против
    set ProjectileMove = null
endfunction
теперь я голосую за свой вариант =). В нем так же как и Markiz'а используется только один массив, но в отличие от его варианта, здесь не требуется начальная инициализация массива, что несомненно плюс.
Старый 11.08.2007, 15:06
Закрытая тема

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы можете скачивать файлы

BB-коды Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход



Часовой пояс GMT +3, время: 08:57.