Использованны vJass и cJass, так что НУЖЕНJNGP с установленным AdicHelper!' Система отталкивает юнитов, при этом учитываеться растояние от юнита до центра отталкивания, скорость их отталкивания, а также при встрече с припятствиями юниты отлетают от них,но уже с меньшей скоростью. Обновления v1.1
Отличный рекошет (сделал 'SirNikolas')
Теперь юниты ещё реагируют на высоту рельефа. Если юнит отталкиваеться на гору то он начинает быстро тормозить, а если с горы то набирает скорость. v1.2
Стали доступны функции OTR_ConvertReal и OTR_Recochet
Система немного оптимизирована.
Значительно увеличина точность рекошета.
Дана возможность отключения спецэфекта, так как из-за него при отталкивание большого количества юнитов (более 30) начинаются небольшие лаги.(true - включить, false - выключить).
Дана возможность настроек параметров step и max. Если их не указывать будут использованны параметры по умолчанию.
Импорт: Для использлвания в своей карте скопируйте триггер OTR. Функции: OTR_Start(unit u,real radius,real maxdist,boolean effecton,real step,real max) - отталкивает юнитов в определённом радиусе от юнита. Параметр maxdist рекомендую ставить 2000, он что то вроде импульса. radius - радиус от x;y координат юнита, выбранные все живые, вражесские юниты в нём будут отталкиваться. Вы можете не указывать параметры step и max тоесть можно также писать call OTR_Start(u,radius,maxdist,e) или только step call OTR_Start(u,radius,maxdist,e,step), при этом они будут установленны по умолчанию. step по умолчанию - .34907 ,а max - 3.1415926536. Анологично можно делать и в функциях OTR_StartXY и в OTR_GrUnit. OTR_StartXY(player p,real x,real y,real radius,real maxdist,boolean effecton,real step,real max)- отталкивает всех от точки, за исключением юнитов указанного игрока. OTR_GrUnit(unit u,real x,real y,real maxdist,real step,real max)- отталкивает определённого юнита. OTR_XYpr(real x,real y)- проверяет точку на проходимость. (Автор следущих двух функций [/b]SirNikolas) OTR_ConvertReal(real r,real min,real max)- возвращает число в указанном диапозоне. OTR_Ricochet(real x,real y,real angle,real dist,real step,real max) - определяет угол (в радианах!) по которому будет продолжать движение объект попадающий в точку ( x;y) и движущийся под углом angle. Также она берёт доп. параметры. dist('Оптимальное значение - 15') - определяет, в каком радиусе нужно искать препятствие. При слишком высоких значениях функция может не заметить не большой объект, а при низких юнит будет рекошировать от малых шерховатостей, и следовательно, совсем в другую сторону. step('Оптимальное значение - .34907') - чем меньше это параметр , тем выше точность рикошета, но и лаги тоже увеличиваються max('Оптимальное значение - 3'(в радианах!)) - Показывает под каким максимальным углом следует искать препятствие. Если оно не будет найдено под этим углом, рикошет будет в сторону, противоположенную движение.
Code
library OTR {
define { //Значения по умолчанию===== private STEP=.34907 private MAX=3.1415926536 //========================== } //Дальше ни чего не трогать. =) private hashtable hash=InitHashtable() private location LocZ=Location(0,0) private player playercast
private struct str { unit u real speed real angle real Z real maxspeed real step real max string efmd boolean flag boolean effecton }
public boolean XYpr(real x,real y) { local item t=CreateItem('texp',x,y) local real x1,y1 SetItemPosition(t,x,y) x1=GetWidgetX(t);y1=GetWidgetY(t);RemoveItem(t);t=null return x==x1 && y==y1 }
public real ConvertReal(real r, real min, real max) { whilenot r<=max { r-=max } whilenot r>=min { r+=max } return r }
public real Ricochet(real x, real y, real angle, real dist, real step, real max) { real r=step,a=angle,x1,x2,y1,y2 do { x1=x+Cos(a)*dist ; y1=y+Sin(a)*dist exitwhen r>=max or OTR_XYpr(x1,y1) r=r+step a=ConvertReal(angle+r,.0,6.283185306) } if a==angle { return angle } r=step do { x2=x+Cos(a)*dist ; y2=y+Sin(a)*dist exitwhen r>= max or OTR_XYpr(x2, y2) a=ConvertReal(angle-r,.0,6.283185306) ; r=r+step } r=ConvertReal(3.1415926536+(Atan2(y1-y,x1-x)+Atan2(y2-y,x2-x))/2,.0,6.283185306) if r>=1.5707963265 and a>=3.1415926536 { return 2*(r+1.5707963265)-a } return 2*(r-1.5707963265)-a }
private nothing GrAc3() { timer t=GetExpiredTimer() str h=LoadInteger(hash,GetHandleId(t),0) real x=GetWidgetX(h.u),y=GetWidgetY(h.u) real x1=x+h.speed*Cos(h.angle),y1=y+h.speed*Sin(h.angle) real angle,Z=h.Z MoveLocation(LocZ,x,y) h.Z=GetLocationZ(LocZ) if h.speed>.0 && GetWidgetLife(h.u) > .0 { angle=Ricochet(x1,y1,h.angle,15.,h.step,h.max) if angle==h.angle { SetUnitX(h.u,x1) SetUnitY(h.u,y1) else h.angle = angle h.speed=h.speed-3. } if h.flag==false { h.flag=true elseif h.Z>Z h.speed=h.speed-(h.Z-Z)/10 else h.speed=h.speed+(Z-h.Z)/10 } if h.speed>h.maxspeed { h.speed=h.maxspeed } if h.effecton { DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl",x1,y1)) } h.speed=h.speed-.3 else FlushChildHashtable(hash,GetHandleId(t)) DestroyTimer(t) h.destroy() } t=null } public nothing GrUnit(unit u,real x,real y,real maxdist,boolean e,real step,real max) { timer t=CreateTimer() real xu=GetUnitX(u),yu=GetUnitY(u) str h=str.create() h.angle=Atan2(yu-y,xu-x) h.speed=maxdist/100-SquareRoot((x-xu)*(x-xu)+(y-yu)*(y-yu))/100 h.maxspeed=h.speed ;h.u=u ;h.effecton=e;h.flag=false;h.step=step;h.max=max SaveInteger(hash,GetHandleId(t),0,h) TimerStart(t,0.02,true,function GrAc3) t=null } public nothing Start(unit u,real radius,real maxdist,boolean effecton,real step,real max) { group g=CreateGroup() unit ugr real x=GetWidgetX(u),y=GetWidgetY(u) boolexpr b=Condition(function Cond) playercast=GetOwningPlayer(u) GroupEnumUnitsInRange(g,x,y,radius,b) do { ugr=FirstOfGroup(g) exitwhen ugr==null GrUnit(ugr,x,y,maxdist,effecton,step,max) GroupRemoveUnit(g,ugr) } DestroyBoolExpr(b);DestroyGroup(g) b=null;g=null } public nothing StartXY(player p,real x,real y,real radius,real maxdist,boolean effecton,real step,real max) { group g=CreateGroup();unit ugr boolexpr b=Condition(function Cond) playercast=p GroupEnumUnitsInRange(g,x,y,radius,b) do { ugr=FirstOfGroup(g) exitwhen ugr==null GrUnit(ugr,x,y,maxdist,effecton,step,max) GroupRemoveUnit(g,ugr) } DestroyBoolExpr(b);DestroyGroup(g) b=null;g=null } define { OTR_Start(u,radius,maxdist,effecton)=OTR_##Start(u,radius,maxdist,effecton,STEP,MAX) OTR_Start(u,radius,maxdist,effecton,step)=OTR_##Start(u,radius,maxdist,effecton,step,MAX) OTR_Start(u,radius,maxdist,effecton,step,max)=OTR_##Start(u,radius,maxdist,effecton,step,max) OTR_StartXY(p,x,y,radius,maxdist,e)=OTR_##StartXY(p,x,y,radius,maxdist,e,STEP,MAX) OTR_StartXY(p,x,y,radius,maxdist,e,step)=OTR_##StartXY(p,x,y,radius,maxdist,e,step,MAX) OTR_StartXY(p,x,y,radius,maxdist,e,step,max)=OTR_##StartXY(p,x,y,radius,maxdist,e,step,max) OTR_GrUnit(u,x,y,maxdist,e)=OTR_Gr##Unit(u,x,y,maxdist,e,STEP,MAX) OTR_GrUnit(u,x,y,maxdist,e,step)=OTR_Gr##Unit(u,x,y,maxdist,e,step,MAX) OTR_GrUnit(u,x,y,maxdist,e,step,max)=OTR_Gr##Unit(u,x,y,maxdist,e,step,max) OTR_Ricochet(x,y,angle,dist)=OTR_##Ricochet(x,y,angle,dist,STEP,MAX) OTR_Ricochet(x,y,angle,dist,step)=OTR_##Ricochet(x,y,angle,dist,step,MAX) OTR_Ricochet(x,y,angle,dist,step,max)=OTR_##Ricochet(x,y,angle,dist,step,max) } }
Сообщение отредактировал rixt7956 - Пятница, 14 Января 2011, 17:09:58
Можно еще модернизировать систему, сделав так, чтобы при столкновении юнит отлетал не всегда против часовой стрелки и не обязательно под углом в 45 градусов.
local real a=bj_RADTODEG*Atan2(y-y2,x-x2) local real BX local real CD local real RB=bj_RADTODEG*Atan2(y2-y1,x2-x1) if a>=180 and RB >=90 then set CD=RB+90 else CD=RB-90 endif set BX=2*CD-a
rixt7956, боюсь, что я тоже тупанул, причем капитально. Я это писал, расчитывая на то, что юнит будет сталкиваться только с другими юнитами. То, что ты написал в RB - это угол не от центра препятствия, а от точки столкновения, т. е., если использовать это, то юнит всегда будет отлетать перпендикулярно движению, что еще хуже. У меня есть мысль, как можно определить центр, главное - ее оформить в виде кода.
library OTR globals private constant hashtable hash=InitHashtable() endglobals private struct str unit u real speed real angle endstruct
public function XYpr takes real x,real y returns boolean local item t=CreateItem('texp',x,y) local real x1 local real y1 call SetItemPosition(t,x,y) set x1=GetItemX(t) set y1=GetItemY(t) call RemoveItem(t) set t=null return x==x1 and y==y1 endfunction
public function ConvertReal takes real r, real min, real max returns real loop exitwhen r <= max set r = r - max endloop loop exitwhen r >= min set r = r + max endloop return r endfunction
public function Ricochet takes real x, real y, real angle, real dist, real step, real max returns real local real r = step local real a = angle local real x1 local real y1 local real x2 local real y2 loop set x1 = x + Cos(a) * dist set y1 = y + Sin(a) * dist exitwhen r >= max or OTR_XYpr(x1, y1) set r = r + step set a = OTR_ConvertReal(angle + r, .0, 360.) endloop if a == angle then return angle endif set r = step loop set x2 = x + Cos(a) * dist set y2 = y + Sin(a) * dist exitwhen r >= max or OTR_XYpr(x2, y2) set r = r + step set a = OTR_ConvertReal(angle - r, .0, 360.) endloop set r = OTR_ConvertReal(3.1415926536 + (Atan2(y1 - y, x1 - x) + Atan2(y2 - y, x2 - x)) / 2, .0, 6.283185306) if r >= 1.5707963265 and a >= 3.1415926536 then return 2 * (r + 1.5707963265) - a endif return 2 * (r - 1.5707963265) - a endfunction
private function GrAc3 takes nothing returns nothing local timer t=GetExpiredTimer() local str h=LoadInteger(hash,GetHandleId(t),0) local real x=GetWidgetX(h.u) local real y=GetWidgetY(h.u) local real x1=x+h.speed*Cos(h.angle*.0174532) local real y1=y+h.speed*Sin(h.angle*.0174532) local real angle if h.speed>.0 then set angle = OTR_Ricochet(x1, y1, h.angle, 15., .3490658502, 3.1415926536) if angle == h.angle then call SetUnitX(h.u,x1) call SetUnitY(h.u,y1) else set h.angle = angle set h.speed=h.speed-1. endif call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl",x1,y1)) set h.speed=h.speed-.3 else call FlushChildHashtable(hash,GetHandleId(t)) call DestroyTimer(t) call h.destroy() endif set t=null endfunction
public function GrUnit takes unit u,real x,real y,real maxdist returns nothing local timer t=CreateTimer() local real xu=GetUnitX(u) local real yu=GetUnitY(u) local real angle=bj_RADTODEG*Atan2(yu-y,xu-x) local real speed=maxdist/100-SquareRoot((x-xu)*(x-xu)+(y-yu)*(y-yu))/100 local str h=str.create() set h.u=u set h.speed=speed set h.angle=angle call SaveInteger(hash,GetHandleId(t),0,h) call TimerStart(t,0.02,true,function GrAc3) set t=null endfunction
public function Start takes unit u,real radius,real maxdist returns nothing local group g=CreateGroup() local unit ugr local real x=GetWidgetX(u) local real y=GetWidgetY(u) call GroupEnumUnitsInRange(g,x,y,radius,null) loop set ugr=FirstOfGroup(g) exitwhen ugr==null if IsUnitEnemy(ugr,GetOwningPlayer(u)) and GetWidgetLife(ugr)>.0 then call GrUnit(ugr,x,y,maxdist) endif call GroupRemoveUnit(g,ugr) endloop call DestroyGroup(g) set g=null endfunction
public function StartXY takes player p,real x,real y,real radius,real maxdist returns nothing local group g=CreateGroup() local unit ugr call GroupEnumUnitsInRange(g,x,y,radius,null) loop set ugr=FirstOfGroup(g) exitwhen ugr==null if IsUnitEnemy(ugr,p) and GetWidgetLife(ugr)>.0 then call GrUnit(ugr,x,y,maxdist) endif call GroupRemoveUnit(g,ugr) endloop call DestroyGroup(g) set g=null endfunction
endlibrary
Функция OTR_Ricochet определяет угол (в радианах!), по которому будет продолжать движение объект, попадающий в точку (x; y) и движущийся под углом angle. Также она берет дополнительные параметры. dist определяет, в каком радиусе нужно искать препятствие. При слишком высоких значениях она может не заметить небольшой объект (например, овцу), а при низких юнит будет рикошетировать от малых шероховатостей, и следовательно, совсем в другую сторону. Оптимальное значение - 15. Чем меньше параметр step, тем выше точность рикошета, но и лаги тоже увеличиваются. Оптимальное значение - .34907. Измеряется в радианах! Параметр max показывает, под каким максимальным углом следует искать препятствие. Оптимальное значение - 3.141592. Измеряется в радианах!
Добавлено (15-01-2011, 07:42) --------------------------------------------- Само собой разумеется, существует погрешность, и, я думаю, немалая, но в таких случаях ей можно пренебречь. P. S. Функцию не тестировал.
private struct str unit u real speed real angle endstruct
public function XYpr takes real x,real y returns boolean local item t=CreateItem('texp',x,y) local real x1 local real y1 call SetItemPosition(t,x,y) set x1=GetWidgetX(t) set y1=GetWidgetY(t) call RemoveItem(t) set t=null return x==x1 and y==y1 endfunction
public function ConvertReal takes real r, real min, real max returns real loop exitwhen r <= max set r = r - max endloop loop exitwhen r >= min set r = r + max endloop return r endfunction
public function Ricochet takes real x, real y, real angle, real dist, real step, real max returns real local real r = step local real a = angle local real x1 local real y1 local real x2 local real y2 loop set x1 = x + Cos(a) * dist set y1 = y + Sin(a) * dist exitwhen r >= max or OTR_XYpr(x1, y1) set r = r + step set a = OTR_ConvertReal(angle + r, .0, 6.283185306) endloop if a == angle then return angle endif set r = step loop set x2 = x + Cos(a) * dist set y2 = y + Sin(a) * dist exitwhen r >= max or OTR_XYpr(x2, y2) set r = r + step set a = OTR_ConvertReal(angle - r, .0, 6.283185306) endloop set r = OTR_ConvertReal(3.1415926536 + (Atan2(y1 - y, x1 - x) + Atan2(y2 - y, x2 - x)) / 2, .0, 6.283185306) if r >= 1.5707963265 and a >= 3.1415926536 then return 2 * (r + 1.5707963265) - a endif return 2 * (r - 1.5707963265) - a endfunction
private function GrAc3 takes nothing returns nothing local timer t=GetExpiredTimer() local str h=LoadInteger(hash,GetHandleId(t),0) local real x=GetWidgetX(h.u) local real y=GetWidgetY(h.u) local real x1=x+h.speed*Cos(h.angle) local real y1=y+h.speed*Sin(h.angle) local real angle if h.speed>.0 and GetWidgetLife(h.u) > .0 then set angle = OTR_Ricochet(x1, y1, h.angle, 15., .3490658502, 3.1415926536) if angle == h.angle then call SetUnitX(h.u,x1) call SetUnitY(h.u,y1) else set h.angle = angle set h.speed=h.speed-1. endif call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl",x1,y1)) set h.speed=h.speed-.3 else call FlushChildHashtable(hash,GetHandleId(t)) call DestroyTimer(t) call h.destroy() endif set t=null endfunction
public function GrUnit takes unit u,real x,real y,real maxdist returns nothing local timer t=CreateTimer() local real xu=GetUnitX(u) local real yu=GetUnitY(u) local real angle=Atan2(yu-y,xu-x) local real speed=maxdist/100-SquareRoot((x-xu)*(x-xu)+(y-yu)*(y-yu))/100 local str h=str.create() set h.u=u set h.speed=speed set h.angle=angle call SaveInteger(hash,GetHandleId(t),0,h) call TimerStart(t,0.02,true,function GrAc3) set t=null endfunction
public function Start takes unit u,real radius,real maxdist returns nothing local group g=CreateGroup() local unit ugr local real x=GetWidgetX(u) local real y=GetWidgetY(u) call GroupEnumUnitsInRange(g,x,y,radius,null) loop set ugr=FirstOfGroup(g) exitwhen ugr==null if IsUnitEnemy(ugr,GetOwningPlayer(u)) and GetWidgetLife(ugr)>.0 then call GrUnit(ugr,x,y,maxdist) endif call GroupRemoveUnit(g,ugr) endloop call DestroyGroup(g) set g=null endfunction
public function StartXY takes player p,real x,real y,real radius,real maxdist returns nothing local group g=CreateGroup() local unit ugr call GroupEnumUnitsInRange(g,x,y,radius,null) loop set ugr=FirstOfGroup(g) exitwhen ugr==null if IsUnitEnemy(ugr,p) and GetWidgetLife(ugr)>.0 then call GrUnit(ugr,x,y,maxdist) endif call GroupRemoveUnit(g,ugr) endloop call DestroyGroup(g) set g=null endfunction endlibrary
private struct str unit u real speed real angle endstruct
Структура - это своеобразная "связка" переменных. Здесь она используется, чтобы уменьшить кол-во обращений к хэшу, т. к. гораздо быстрее использовать внутриструктурные переменные, чем выгружать их из хэш-таблицы.