Всем привет! Мой вопрос собственно заключается в том, что правильно ли я сделал, если допустим есть способность кровотечение, которая вызывает кровотечение на юнитах. Суть в том, что допустим 10 юнитов получили кровотечение и получают урон, на 5 из них допустим через 2 секунды опять оказало действие кровотечение, а на тех, что не получили изначально получат через 1 секунду. Собственно сделать через один таймер кровотечение на группу юнитов с разными таймера их действия, чтоб кровотечение обновлялось, а не тупо стакалось на 100500 таймеров... Я понимаю, что нужно через переменную проверять и собсвтенно это сделал..Но вопрос в том, пойдет ли данная схема, если допустим несколько игроков запустят таймеры для себя, не будут ли сбиваться переменные передаваемые через функцию и вообще рабочая ли эта схема ? У меня вроде все работает...но хотелось бы узнать мнение экспертов )))
	local PS_DMG_PERC=.25

	local PS_GROUP={}
	local PS_TIMER_BLEED={}
	local PS_TIME_UNIT={}
	
PS_Opgh_FUNC=function(caster,caster_uid,target,target_uid)

		if PS_GROUP[caster_uid]==nil then

			local size
			local fg
			local id
			local damage

			PS_GROUP[caster_uid]=CreateGroup()
			PS_TIMER_BLEED[caster_uid]=CreateTimer()
			PS_TIME_UNIT[caster_uid]={}

			TimerStart(PS_TIMER_BLEED[caster_uid],1,true,function()

				damage=GetHeroStr(caster,true)*PS_DMG_PERC

				size=BlzGroupGetSize(PS_GROUP[caster_uid])

				print("7")
				if size==0 then
					DestroyGroup(PS_GROUP[caster_uid])
					PS_GROUP[caster_uid]=nil
					DestroyTimer(PS_TIMER_BLEED[caster_uid])
					PS_TIMER_BLEED[caster_uid]=nil
					PS_TIME_UNIT[caster_uid]=nil
					return
				elseif size>=1 then
					for i=size-1,0,-1 do

						fg=BlzGroupUnitAt(PS_GROUP[caster_uid],i)
						id=GetHandleId(fg)
						if UnitAlive(fg) and not BlzIsUnitInvulnerable(fg) then

							UnitDamageTarget(caster,fg,damage,false,false,ATTACK_PHYSICAL,DAMAGE_PHYSICAL,nil)

							PS_TIME_UNIT[caster_uid][id]=PS_TIME_UNIT[caster_uid][id]-1

							if PS_TIME_UNIT[caster_uid][id]==0 then
								GroupRemoveUnit(PS_GROUP[caster_uid],fg)
								PS_TIME_UNIT[caster_uid][id]=nil
							end
						else
							GroupRemoveUnit(PS_GROUP[caster_uid],fg)
							PS_TIME_UNIT[caster_uid][id]=nil
						end
					end
				end

				size=BlzGroupGetSize(PS_GROUP[caster_uid])
				if size==0 then
					DestroyGroup(PS_GROUP[caster_uid])
					PS_GROUP[caster_uid]=nil
					DestroyTimer(PS_TIMER_BLEED[caster_uid])
					PS_TIMER_BLEED[caster_uid]=nil
					PS_TIME_UNIT[caster_uid]=nil
				end
			end)
		end

		if PS_GROUP[caster_uid]~=nil then
			if not IsUnitInGroup(target,PS_GROUP[caster_uid])then
				GroupAddUnit(PS_GROUP[caster_uid],target)
				PS_TIME_UNIT[caster_uid][target_uid]=3
			else
				PS_TIME_UNIT[caster_uid][target_uid]=3
			end
		end
	end

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

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

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
0
8
5 лет назад
Отредактирован Castiel
0
prog:
Castiel, если что, пример выше это только очень простой пример того как можно записать данные. Его нужно дополнять и расширять для практического использования.
Есть альтернативный способ - вместо записи последовательно, записывать по хендлу кастера и перебирать потом через pairs.
function BleedBitch(caster,target,duration,basedamage)
	-- добавляем новое кровотечение к юниту по хендлу кастера
	local data = bleeding_data[GetHandleId(target)]
	data[GetHandleId(caster)] = {caster=caster, duration=duration, damage=GetHeroStr(caster,true)*basedamage}
	GroupAddUnit(BleedGroup,target)
end

function BleedTickUnit (target)
	local data = bleeding_data[GetHandleId(target)]
	for cid, bdat in pairs(data)
		Damage(bdat.caster, target, bdat.damage)
	end
end
Перебор через pairs медленней перебора по индексам, но и мороки с ним меньше, плюс проверка на то есть ли на цели кровотечение от кастера проще. Но есть нюанс - тогда в перебираемой таблице не должно быть лишних записей, чтобы не париться проверками нужная ли это запись или что-то левое.
А если добавлять одного и того же юнита в группу ? Не получится ли, что один и тот же юнит в группе несколько раз состоит ? Может стоит сделать проверку ?
2
28
5 лет назад
Отредактирован PT153
2
А если добавлять одного и того же юнита в группу ? Не получится ли, что один и тот же юнит в группе несколько раз состоит ? Может стоит сделать проверку ?
Проверка уже делается внутри. В 1.31 функции добавления и удаления в\из группу\ы возвращают правду, если юнит был действительно добавлен или удалён. Ложь, если не был.
1
24
5 лет назад
Отредактирован prog
1
Castiel, еще раз повторюсь - это не более чем пример. Для практического использования, естественно, нужны дополнительные проверки.

Для группы проверка это не более чем экономия одного вызова нативки т.к. вар не дает два раза добавить одного юнита в группу, а вот проверка на то есть ли уже кровотечение от этого кастера имеет смысл, чтобы модифицировать данные о кровотечении, а не добавлять еще одно.
0
8
5 лет назад
Отредактирован Castiel
0
PT153:
А если добавлять одного и того же юнита в группу ? Не получится ли, что один и тот же юнит в группе несколько раз состоит ? Может стоит сделать проверку ?
Проверка уже делается внутри. В 1.31 функции добавления и удаления в\из группу\ы возвращают правду, если юнит был действительно добавлен или удалён. Ложь, если не был.
Если я правильно понял, то если добавлять одного и того же, то ничего не произойдет более 1 раза ?И проверку на наличие можно не делать ?
2
28
5 лет назад
2
И проверку на наличие можно не делать ?
Не нужно делать, ибо это бессмысленно.
1
28
5 лет назад
1
prog, Castiel, а зачем вам хэндл юнита вообще?
можно просто хранить всех юнитов и данные в массиве и в таймере пробегать циклом по массиву
1
24
5 лет назад
1
nvc123, можно, конечно. Но, например, у себя в картах я обычно храню намного больше информации привязаной к юнитам - если бы каждая система хранила свои данные в массиве, то удаление этих данных превратилось бы в ад, как и доступ к этим данным вне перебора. Вот когда удаление и доступ к данным просходит только внутри одной системы и только внутри перебора, то можно и в массив без хендлов сложить, конечно.
0
8
5 лет назад
Отредактирован Castiel
0
nvc123:
prog, Castiel, а зачем вам хэндл юнита вообще?
можно просто хранить всех юнитов и данные в массиве и в таймере пробегать циклом по массиву
не бро, тут не получиться просто так все сделать. По большому счету, нужно кровотечение не складывать,а обновлять от одного и того же источника. Я уже пробовал много разных вариантов и все равно самый удобный получился мой первый вариант, о котором я спрашивал, но немного переработанный и без лишних телодвижений. Через один таймер как советовали, то получается один таймер обрабатывает 1 и ту же группу по несколько раз за секунду. Там при одном таймере слишком много проверок получается при добавлении или обновлении. И перебор через паирс мне не понравился. Сделал таймер для несколих групп для каждого источника, и удалять их когда нет целей, и с таймерами точно так же...Через именную функцию для таймера и сохранением таймер и хендл таймера на кастера и кастера с целями на хендл таймера, короче все получилось нормально и рабочее. Хотя не читаемо для того кто это увидит )))
1
24
5 лет назад
Отредактирован prog
1
Castiel, создание-удаление таймеров и групп намного дороже обходится чем pairs, если что...
Ну и еще один нюанс - что ты будеш делать когда захочеш снимать кровотечение с цели каким-нибудь бинтом или очищением или рассеиванием или еще чем-нибудь, до истечения длительности кровотечения? В моей реализации данные о кровотечении хранятся по хендлу цели поэтому до них очень легко добраться зная только цель, в твоей же реализации тебе надо обязательно знать кастера прежде чем ты сможеш добраться до данных о кровотечении на цели.
0
8
5 лет назад
0
prog:
Castiel, создание-удаление таймеров и групп намного дороже обходится чем pairs, если что...
Ну и еще один нюанс - что ты будеш делать когда захочеш снимать кровотечение с цели каким-нибудь бинтом или очищением или рассеиванием или еще чем-нибудь, до истечения длительности кровотечения? В моей реализации данные о кровотечении хранятся по хендлу цели поэтому до них очень легко добраться зная только цель, в твоей же реализации тебе надо обязательно знать кастера прежде чем ты сможеш добраться до данных о кровотечении на цели.
Хмммм....Я думал, что паирс слишком уж тугой) попробую все переделать, но я вчера бился над этой проблемой 4 часа ))так и не сделал вернулся к своему старому способу, видать я тугой в этом деле! Просто когда начинал делать всплывало много нюансов по реализации данной системы, по этому я способа 4 перебрал и ниодин не то, что не подходил, и даже не был рабочим, а то что получается цикл перебора через паирс в цикле перебора группы....Хотя попробую..Кстати вот, как я сделал, код не очень то и читаем но рабочий !
	local PS_TIME=4
	local PS_DMG_PERC=.25
	local PS_TIMER_ID={}
	local PS_TIMER={}
	local PS_UNIT_BLEED={}

local PS_Opgh_TIMER_FUNC=function()

		local t=GetExpiredTimer()
		local id=GetHandleId(t)

		local fg
		local fg_uid
		local group=PS_TIMER[id].group

		local size=BlzGroupGetSize(group)

		for i=size-1,0,-1 do

			fg=BlzGroupUnitAt(group,i)
			fg_uid=GetHandleId(fg)
	
			if UnitAlive(fg) and not BlzIsUnitInvulnerable(fg) then

				UnitDamageTarget(PS_TIMER[id].caster,fg,PS_UNIT_BLEED[id][fg_uid].damage,false,false,ATTACK_PHYSICAL,DAMAGE_PHYSICAL,nil)

				PS_UNIT_BLEED[id][fg_uid].duration=PS_UNIT_BLEED[id][fg_uid].duration-1

				if PS_UNIT_BLEED[id][fg_uid].duration==0 then
					PS_UNIT_BLEED[id][fg_uid]=nil
					GroupRemoveUnit(group,fg)
				end
			else
				PS_UNIT_BLEED[id][fg_uid]=nil
				GroupRemoveUnit(group,fg)
			end

		end

		if BlzGroupGetSize(group)==0 then
			PauseTimer(t)
		end
	end


	PS_Opgh_FUNC=function(caster,caster_uid,target,target_uid)

		local id
		
		if PS_TIMER_ID[caster_uid]==nil then
		
			local timer=CreateTimer()
			PS_TIMER_ID[caster_uid]={timer=timer,id=GetHandleId(timer)}
			PS_TIMER[PS_TIMER_ID[caster_uid].id]={caster=caster,caster_uid=caster_uid,group=CreateGroup()}
			PS_UNIT_BLEED[PS_TIMER_ID[caster_uid].id]={}
			
		end
		
		PS_UNIT_BLEED[PS_TIMER_ID[caster_uid].id][target_uid]={duration=PS_TIME,damage=GetHeroStr(caster,true)*PS_DMG_PERC}
		GroupAddUnit(PS_TIMER[PS_TIMER_ID[caster_uid].id].group,target)
		
		if BlzGroupGetSize(PS_TIMER[PS_TIMER_ID[caster_uid].id].group)==1  then
			TimerStart(PS_TIMER_ID[caster_uid].timer,1,true,PS_Opgh_TIMER_FUNC)
		end
		
	end
1
24
5 лет назад
1
Castiel, цикл в цикле это нормально, когда оправдано. И уж намного лучше, чем когда одного юнита дергает по несколько таймеров, если на нем больше одного кровотечения.
А что касается pairs - он определенно дешевле создания-удаления групп и таймеров. Плюс у тебя же не сотни кровотечений от разных кастеров на одной цели будут, на таком кол-ве не особо важно как перебор реализован.
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.