Всем привет! Мой вопрос собственно заключается в том, что правильно ли я сделал, если допустим есть способность кровотечение, которая вызывает кровотечение на юнитах. Суть в том, что допустим 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
19
5 лет назад
0
Похожие вопросы:

ответ
Audes, просто написать
angle-=180
ответ
Нашёл систему, где звук передаётся в таймер, который истекает через 0 секунд. В результате один и тот же звук может проигрываться несколько раз без всяких проблем. В инетике пишут мол это такой баг движка.
ответ
ClotPh:
Ige, т. е. как-то так (при инициализации)?
call Preload (war3mapImported\SingerOfDeath1.wav)
call Preload("war3mapImported\\SingerOfDeath1.wav")
ответ
Есть стандартная способность "Бродячий". Она задает рандомное движение всяким птицам, жабам и прочим нейтральным зверюшкам, которых в то же время можно убить. Я бы попробовал ее использовать, но не уверен, что она не только для пассивных юнитов.

2
24
5 лет назад
Отредактирован prog
2
Прежде всего, зачем по таймеру для каждого игрока, а тем более на каждого кастера, если можно обойтись одним таймером на всех?
Группы я бы тоже не хранил для каждого игрока, а вместо этого хранил бы по хендлу цели сколько осталось тактов кровотечения и от чьего имени наносить урон, а всех юнитов с кровотечением складывал бы в одну группу для всех.
Постоянное создание-удаление групп, в принципе, тоже не лучшая идея, лучше их чистить и повторно использовать.
Ну и я бы не стал использовать анонимную функцию в таймере - она тут не нужна, все отлично передается глобалками.
Принятый ответ
0
8
5 лет назад
Отредактирован Castiel
0
prog:
Прежде всего, зачем по таймеру для каждого игрока, а тем более на каждого кастера, если можно обойтись одним таймером на всех?
Группы я бы тоже не хранил для каждого игрока, а вместо этого хранил бы по хендлу цели сколько осталось тактов кровотечения и от чьего имени наносить урон, а всех юнитов с кровотечением складывал бы в одну группу для всех.
Постоянное создание-удаление групп, в принципе, тоже не лучшая идея, лучше их чистить и повторно использовать.
Ну и я бы не стал использовать анонимную функцию в таймере - она тут не нужна, все отлично передается глобалками.
Так по хендлу кастера и хендлу цели я и храню их и создаю. Все группы и таймер создаются именно по хендлу юнита. caster_uid=GetHandleId(caster) target_uid=GetHandleId(target)...Просто хотелось бы узнать рабочая ли эта схема, если допустим штук 10 героев будут вешать кровотечение через эту функцию....
1
24
5 лет назад
Отредактирован prog
1
Castiel, ты храниш все по хендлу кастера, а я предлагаю хранить данные по хендлу цели. Ты создаеш по одному таймеру для каждого кастера и по одной группе на каждого кастера и еще и удаляеш их каждый раз когда у кастера заканчиваются цели, я предлагаю использовать один таймер и одну группу на всех, причем без создания-удаления. разве что таймер можно стопать и рестартовать если очень хочется оптимизации, но, опять-же, удалять его нет смысла.

Это может выглядеть примерно так:
  • функция ДобавитьКровотечение (кастер, цель, длительность)
    • проверяет если ли на юните кровотечение от кастера, если есть, то продлевает его, а если нет, то добавляет новое кровотечение в список записаный по хендлу цели (в данных о кровотечении хранится, как минимум, кастер и оставшаяся длительность, но можно хранить и больше данных, например урон за такт чтобы кровотечение не усиливалось магическим образом если кастер прокачался пока идет кровотечение)
    • добавляет цель в группу для перебора
    • если таймер остановлен, то запускает его заново
  • Функция таймера (желательно отдельной именованой функцией, а не анонимной пересоздаваемой при каждом старте таймера)
    • перебирает группу раз в секунду, для каждого юнита в группе перебирает все повешеные на него кровотечения, наносит урон и уменьшает оставшиеся такты кровотечения на 1
    • если прошли все такты кровотечения, то кровотечение убирается из данных записаных по хендлу юнита
    • если по хендлу юнита больше не записано кровотечений, то юнит убирается из группы перебора
    • если в группе перебора не осталось юнитов, то таймер стопается, но не удаляется, группа тоже не удаляется
0
8
5 лет назад
0
как отдельная именная функция узнает юнита который нанносит урон кровотечением ? Допустим через цикл i=0,#GroupCaster.. А если таких юнитов которые вешают кровотечение 5-6 рыл, то получается цикл в цикле....Мне кажется те же яйца только в профиль ! Или так более оптимизированее и быстрее ?
1
24
5 лет назад
1
Castiel, еще раз перечитай мой комментарий выше - там есть и основные отличия и то где хранится информация о кастере. Главное преимущество такого способа - нет жонглирования множеством групп и таймеров, можно даже полностью без групп обойтись, а юнитов для перебора в массиве хранить. Второе ключевое отличие - более гибкая система, позволяющая хранить дополнительную информацию о кровотечении индивидуально для каждой цели и получать эту информацию зная только юнита-цель.
2
8
5 лет назад
Отредактирован Castiel
2
prog:
Castiel, еще раз перечитай мой комментарий выше - там есть и основные отличия и то где хранится информация о кастере. Главное преимущество такого способа - нет жонглирования множеством групп и таймеров, можно даже полностью без групп обойтись, а юнитов для перебора в массиве хранить. Второе ключевое отличие - более гибкая система, позволяющая хранить дополнительную информацию о кровотечении индивидуально для каждой цели и получать эту информацию зная только юнита-цель.
Хорошо я понял! Но вот не могу понять, если записывать GetHandleId(unit), то как обратно найти юнита или можно прям по хендлу и наносить урон ? Дело ведь происходит в именной функции отдельно не вписываемой в сам таймер! а значения в функции таймеров нельзя передать. И как перебрать группу разных id если один 14325432 другой 43223545 ! каким способом ? и узнать размер этого же массива таблицы! или хранить id цели под ключами [1],[2],[3]..... ?Или записывать в ключи самих юнитов ?
1
24
5 лет назад
Отредактирован prog
1
Castiel, но никто ведь не мешает записать самого юнита.
function BleedBitch(caster,target,duration,basedamage)
	-- добавляем новое кровотечение к юниту
	local data = bleeding_data[GetHandleId(target)]
	local num_bleed = data.num
	data.num = num_bleed+1
	data[num_bleed] = {caster=caster, duration=duration, damage=GetHeroStr(caster,true)*basedamage}
	GroupAddUnit(BleedGroup,target)
end
В коде выше большая часть кода пропущена для наглядности.
0
8
5 лет назад
0
BleedBitch
BleedBitch хорошее название функции! Типа адресовано мне я так понял!
2
24
5 лет назад
2
Адресовано юниту который будет истекать кровью, я не кидаюсь необоснованными оскорблениями в собеседников.
2
8
5 лет назад
2
prog:
Адресовано юниту который будет истекать кровью, я не кидаюсь необоснованными оскорблениями в собеседников.
Ну вообще то я воспринял это как шутку! Ладно спасибо что подсказал как лучше!+
1
24
5 лет назад
Отредактирован prog
1
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 медленней перебора по индексам, но и мороки с ним меньше, плюс проверка на то есть ли на цели кровотечение от кастера проще. Но есть нюанс - тогда в перебираемой таблице не должно быть лишних записей, чтобы не париться проверками нужная ли это запись или что-то левое.
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 - он определенно дешевле создания-удаления групп и таймеров. Плюс у тебя же не сотни кровотечений от разных кастеров на одной цели будут, на таком кол-ве не особо важно как перебор реализован.
0
8
5 лет назад
0
prog:
Castiel, цикл в цикле это нормально, когда оправдано. И уж намного лучше, чем когда одного юнита дергает по несколько таймеров, если на нем больше одного кровотечения.
А что касается pairs - он определенно дешевле создания-удаления групп и таймеров. Плюс у тебя же не сотни кровотечений от разных кастеров на одной цели будут, на таком кол-ве не особо важно как перебор реализован.
спасибо бро! )))
Чтобы оставить комментарий, пожалуйста, войдите на сайт.