Добавлен ArhiMEN
Пытаюсь сделать споосбнось, как у Катарины Танцующий клинок, но столкнулся с рядом проблем в анимации: то, одно клинок улетает в неизвестность, невзирая на условия, то летит нормально; то клинок приземляется куда нужно, но оказывается, что только под определённым углом, то сильно мимо, можете помочь с тем, как правильно сделать анимацию?
Код способности
function MoveBlade takes nothing returns nothing
local timer Timer = GetExpiredTimer()
local integer TimerId = GetHandleId(Timer)
local unit Target = LoadUnitHandle(udg_hash, TimerId, StringHash("Target"))
local effect Blade = LoadEffectHandle(udg_hash, TimerId, StringHash("Blade"))
local real a = LoadReal(udg_hash, TimerId, StringHash("a"))
local real TargetX = GetEffectX(Blade)
local real TargetY = GetEffectY(Blade)
local real FallBladeX = GetUnitX(Target) + 300 * Cos(a * bj_DEGTORAD)
local real FallBladeY = GetUnitY(Target) + 300 * Sin(a * bj_DEGTORAD)
local integer AngleBlade = LoadInteger(udg_hash, TimerId, StringHash("Blade Pitch Angle"))
if SquareRoot((FallBladeX - TargetX) * (FallBladeX - TargetX) + (FallBladeY - TargetY) * (FallBladeY - TargetY)) <= 0 then
call DestroyTimer(Timer)
call FlushChildHashtable(udg_hash, TimerId)
call SetEffectPitch(Blade, 90)
call SetEffectY(Blade, GetEffectY(Blade) - 10)
elseif SquareRoot((FallBladeX - TargetX) * (FallBladeX - TargetX) + (FallBladeY - TargetY) * (FallBladeY - TargetY)) >= 150 then
call SetEffectPosition(Blade, TargetX + 2 * Cos(a * bj_DEGTORAD), TargetY + 2 * Sin(a * bj_DEGTORAD), GetEffectZ(Blade) + 5)
elseif SquareRoot((FallBladeX - TargetX) * (FallBladeX - TargetX) + (FallBladeY - TargetY) * (FallBladeY - TargetY)) <= 150 then
call SetEffectPosition(Blade, TargetX + 2 * Cos(a * bj_DEGTORAD), TargetY + 2 * Sin(a * bj_DEGTORAD), GetEffectZ(Blade) - 4)
endif
if AngleBlade == 60 then
set AngleBlade = 0
else
set AngleBlade = AngleBlade + 1
endif
call SaveInteger(udg_hash, TimerId, StringHash("Blade Pitch Angle"), AngleBlade)
endfunction
function CreateBlade takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
local unit Caster = LoadUnitHandle(udg_hash, id, StringHash("Caster"))
local unit Target = LoadUnitHandle(udg_hash, id, StringHash("Target"))
local real CasterX = GetUnitX(Caster)
local real CasterY = GetUnitY(Caster)
local real TargetX = GetUnitX(Target)
local real TargetY = GetUnitY(Target)
local real a = bj_RADTODEG * Atan2(TargetY - CasterY, TargetX - CasterX)
local effect Blade = AddSpecialEffect("Weapon_2.mdl", TargetX, TargetY)
local effect Alarm = AddSpecialEffect("Alarm.mdl", TargetX + 235 * Cos(a * bj_DEGTORAD), TargetY + 235 * Sin(a * bj_DEGTORAD))
local timer Timer = CreateTimer()
local integer TimerId = GetHandleId(Timer)
call SetEffectColourR(Alarm, 255)
call SetEffectColourG(Alarm, 0)
call SetEffectColourB(Alarm, 0)
call SaveEffectHandle(udg_hash, GetHandleId(Caster), StringHash("Alarm Voracity"), Alarm)
call SaveEffectHandle(udg_hash, GetHandleId(Caster), StringHash("Blade"), Blade)
call TimerStart(Timer, 0.01, true, function MoveBlade)
call SaveUnitHandle(udg_hash, TimerId, StringHash("Target"), Target)
call SaveEffectHandle(udg_hash, TimerId, StringHash("Blade"), Blade)
call SaveReal(udg_hash, TimerId, StringHash("a"), a)
set t = null
set Caster = null
set Target = null
set Blade = null
set Alarm = null
set Timer = null
endfunction
function Trig_Bouncing_Blade_Actions takes nothing returns nothing
local unit Caster = GetTriggerUnit()
local unit Target = GetSpellTargetUnit()
local real CasterX = GetUnitX(Caster)
local real CasterY = GetUnitY(Caster)
local real TargetX = GetUnitX(Target)
local real TargetY = GetUnitY(Target)
local real a = bj_RADTODEG * Atan2(TargetY - CasterY, TargetX - CasterX)
local trigger Voracity = CreateTrigger()
local timer t = CreateTimer()
local integer id = GetHandleId(t)
local real time = SquareRoot((CasterX - TargetX) * (CasterX - TargetX) + (CasterY - TargetY) * (CasterY - TargetY)) / 1500
if GetSpellAbilityId() == 'A001' then
call TimerStart(t, time, false, function CreateBlade)
call SaveUnitHandle(udg_hash, id, StringHash("Caster"), Caster)
call SaveUnitHandle(udg_hash, id, StringHash("Target"), Target)
call TriggerRegisterEnterRectSimple( Voracity, Rect( TargetX + 235 * Cos(a * bj_DEGTORAD) - 100 * 0.5, TargetY + 235 * Sin(a * bj_DEGTORAD) - 100 * 0.5, TargetX + 235 * Cos(a * bj_DEGTORAD) + 100 * 0.5, TargetY + 235 * Sin(a * bj_DEGTORAD) + 100 * 0.5 ))
call TriggerAddAction(Voracity, function VoracityAction)
call SaveTriggerHandle(udg_hash, GetHandleId(Caster), StringHash("Destroy Voracity"), Voracity)
endif
set Caster = null
set Target = null
set Voracity = null
set t = null
endfunction
//===========================================================================
function InitTrig_Bouncing_Blade takes nothing returns nothing
set gg_trg_Bouncing_Blade = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_Bouncing_Blade, EVENT_PLAYER_UNIT_SPELL_CAST )
call TriggerAddAction( gg_trg_Bouncing_Blade, function Trig_Bouncing_Blade_Actions )
endfunction
Принятый ответ
ArhiMEN, работаю над этим, немного осталось
в общем я поленился и не добавил все настройки, но оставил комменты по которым ты быстро сориентируешься что к чему и сам доделаешь как надо
то что я бы посоветовал переделать, это выбирать юнитов вокруг точки падения, которая регистрируется после первого пораженного юнита, а не вокруг последней пораженной цели, в видео ты можешь посмотреть пример как глупо полёт смотрелся (0:24)
то что я бы посоветовал переделать, это выбирать юнитов вокруг точки падения, которая регистрируется после первого пораженного юнита, а не вокруг последней пораженной цели, в видео ты можешь посмотреть пример как глупо полёт смотрелся (0:24)
раскрыть
library mylib
globals
constant hashtable H = InitHashtable()
private group TempG = CreateGroup()
private group TempG1
private unit bj_lastFilterUnit
private unit TempUnit
private unit CUEX
private real TempReal
private real MaxX
private real MinX
private real MaxY
private real MinY
endglobals
// воспомогательные фукнции
native UnitAlive takes unit id returns boolean
private function SetUnitPositionEx takes unit u, real x, real y returns nothing
if x > MaxX then
set x = MaxX
elseif x < MinX then
set x = MinX
endif
if y > MaxY then
set y = MaxY
elseif y < MinY then
set y = MinY
endif
call SetUnitX(u,x)
call SetUnitY(u,y)
endfunction
private function CreateUnitEx takes player id, integer unitid, real x, real y, real face returns unit
set CUEX = CreateUnit(id,unitid,x,y,face)
call UnitAddAbility(CUEX,'Arav')
call SetUnitPathing(CUEX,false)
call SetUnitPositionEx(CUEX,x,y)
return CUEX
endfunction
private function ParabolaZ takes real h, real d, real x returns real
return (4 * h / d) * (d - x) * (x / d)
endfunction
private struct Spell
unit caster
unit target
unit dummy
unit dummy1
real speed
real angle
real dmg
real fly
real fly1
real time
real dist
real dist1
real radius
real x
real y
integer dummyid
boolean b = true
integer count
group g
endstruct
private function cond takes nothing returns boolean // выбор ближайших целей для кинжала
set bj_lastFilterUnit = GetFilterUnit()
return UnitAlive(bj_lastFilterUnit) and IsUnitEnemy(bj_lastFilterUnit,GetOwningPlayer(TempUnit)) and not IsUnitInGroup(bj_lastFilterUnit,TempG1)
endfunction
private function DamageCond takes nothing returns boolean // кому наносить урон
set bj_lastFilterUnit = GetFilterUnit()
if UnitAlive(bj_lastFilterUnit) and IsUnitEnemy(bj_lastFilterUnit,GetOwningPlayer(TempUnit)) then
call DestroyEffect(AddSpecialEffectTarget("Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl",bj_lastFilterUnit,"chest"))
call UnitDamageTarget(TempUnit,bj_lastFilterUnit,TempReal,false,false,null,null,null)
endif
return false
endfunction
private function casterr takes nothing returns boolean // это для кинжала, выбор кастера когда кинжал упал
return GetFilterUnit() == TempUnit
endfunction
private function move1 takes nothing returns nothing //это для эффекта кругового удара
local timer t = GetExpiredTimer()
local integer i = GetHandleId(t)
local unit u = LoadUnitHandle(H,i,0)
local unit u1 = LoadUnitHandle(H,i,1)
local real tm = LoadReal(H,i,2)-.01
call SetUnitPositionEx(u1,GetUnitX(u),GetUnitY(u))
if tm <= 0. then
call KillUnit(u1)
call PauseTimer(t)
call DestroyTimer(t)
call FlushChildHashtable(H,i)
else
call SaveReal(H,i,2,tm)
endif
set t = null
set u = null
set u1 = null
endfunction
// периодическая проверка кастера у кинжала
private function checkcaster takes nothing returns nothing
local Spell A = LoadInteger(H,GetHandleId(GetExpiredTimer()),0)
local timer t
set A.time = A.time-.01
set TempUnit = A.caster
call GroupEnumUnitsInRange(TempG,A.x,A.y,100,Condition(function casterr))
if A.time <= 0. or FirstOfGroup(TempG) != null then
if FirstOfGroup(TempG) != null then
set t = CreateTimer()
call SaveUnitHandle(H,GetHandleId(t),0,A.caster)
call SaveUnitHandle(H,GetHandleId(t),1,CreateUnitEx(Player(0x0F),'u002',GetUnitX(A.caster),GetUnitY(A.caster),GetRandomReal(0,360)))
call SaveReal(H,GetHandleId(t),2,.10)
call SetUnitFlyHeight(CUEX,50,0)
call SetUnitScale(CUEX,2,2,2)
call TimerStart(t,.01,true,function move1)
set t = null
call GroupClear(TempG)
set TempReal = A.dmg
call GroupEnumUnitsInRange(TempG,GetUnitX(A.caster),GetUnitY(A.caster),A.radius*0.8,Condition(function DamageCond))
endif
call KillUnit(A.dummy)
call KillUnit(A.dummy1)
call RemoveUnit(A.dummy1)
call DestroyGroup(A.g)
set A.g = null
set A.caster = null
set A.dummy = null
call A.destroy()
call FlushChildHashtable(H,GetHandleId(GetExpiredTimer()))
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
endif
endfunction
// перемещение кинжала когда он падает
private function setpos takes nothing returns nothing
local Spell A = LoadInteger(H,GetHandleId(GetExpiredTimer()),0)
local real x = GetUnitX(A.dummy)+A.speed*Cos(A.angle)
local real y = GetUnitY(A.dummy)+A.speed*Sin(A.angle)
set A.dist = A.dist-A.speed
call SetUnitPositionEx(A.dummy,x,y)
call SetUnitFlyHeight(A.dummy,ParabolaZ(A.fly,A.dist1,A.dist)+A.fly1,0)
if A.dist <= 0. then
set A.x = x
set A.y = y
set A.dummy1 = CreateUnitEx(GetOwningPlayer(A.caster),A.dummyid,x,y,GetRandomReal(0,360))
call SetUnitScale(A.dummy1,.5,.5,.5)
call TimerStart(GetExpiredTimer(),.01,true,function checkcaster)
endif
endfunction
private function move takes nothing returns nothing
local Spell A = LoadInteger(H,GetHandleId(GetExpiredTimer()),0)
local real x = GetUnitX(A.dummy)+A.speed*Cos(A.angle)
local real y = GetUnitY(A.dummy)+A.speed*Sin(A.angle)
local real x1 = GetUnitX(A.target)
local real y1 = GetUnitY(A.target)
local unit u
local real r
local real r1
set A.angle = Atan2(y1-y,x1-x)
call SetUnitPositionEx(A.dummy,x,y)
if SquareRoot((x-x1)*(x-x1)+(y-y1)*(y-y1)) <= 50. then
call SetUnitX(A.dummy,x1)
call SetUnitY(A.dummy,y1)
call UnitDamageTarget(A.caster,A.target,A.dmg,false,false,null,null,null)
if A.b then // установление дистанции падения кинжала после первой цели
set A.x = x1+A.radius*Cos(A.angle)
set A.y = y1+A.radius*Sin(A.angle)
set A.b = false
endif
set A.count = A.count-1
set TempUnit = A.dummy
set TempG1 = A.g
call GroupEnumUnitsInRange(TempG,x1,y1,A.radius,Condition(function cond))
if A.count <= 0 or FirstOfGroup(TempG) == null then
// настройки для падения кинжала
set A.dist = SquareRoot((x-A.x)*(x-A.x)+(y-A.y)*(y-A.y))
set A.dist1 = A.dist // (для параболы)
set A.angle = Atan2(A.y-y1,A.x-x1)
set A.speed = SquareRoot((x-x1)*(x-x1)+(y-y1)*(y-y1))*.1
call TimerStart(GetExpiredTimer(),.01,true,function setpos)
else
// выбор ближней цели
set r = A.radius*2
loop
set u = FirstOfGroup(TempG)
exitwhen u == null
set r1 = SquareRoot((GetUnitX(u)-x1)*(GetUnitX(u)-x1)+(GetUnitY(u)-y1)*(GetUnitY(u)-y1))
if r1 < r then
set r = r1
set A.target = u
endif
call GroupRemoveUnit(TempG,u)
endloop
call GroupAddUnit(A.g,A.target)
endif
endif
endfunction
private function Trig_Spell_Actions takes nothing returns nothing
local timer t = CreateTimer()
local Spell A = Spell.create()
local integer lvl
set A.caster = GetTriggerUnit()
set A.target = GetSpellTargetUnit()
set A.angle = Atan2(GetUnitY(A.target)-GetUnitY(A.caster),GetUnitX(A.target)-GetUnitX(A.caster))
set A.dummy = CreateUnitEx(GetOwningPlayer(A.caster),'u000',GetUnitX(A.caster)+50*Cos(A.angle),GetUnitY(A.caster)+50*Sin(A.angle),A.angle*bj_RADTODEG)
set A.g = CreateGroup()
set lvl = GetUnitAbilityLevel(A.caster,'A000')
if lvl == 1 then
set A.dmg = 100.00
set A.count = 3
set A.speed = 25.00
set A.radius = 300.00 // радиус поиска целей а так же дистанция падения кинжала от первой пораженной цели
set A.fly = 300.00 // максимальная высота кинжала
set A.fly1 = 50.00 // постоянная высота кинжала
set A.time = 5.00 // время существования кинжала
set A.dummyid = 'u001' // какого юнита спавнить для отображения радиуса подбора кинжала
//elseif lvl == 2 then
endif
call GroupAddUnit(A.g,A.target)
call SetUnitFlyHeight(A.dummy,A.fly1,0)
call SaveInteger(H,GetHandleId(t),0,A)
call TimerStart(t,.03125,true,function move)
set t = null
endfunction
//===========================================================================
private function mycond takes nothing returns boolean
if GetSpellAbilityId() == 'A000' then
call Trig_Spell_Actions()
endif
return false
endfunction
function InitTrig_Spell takes nothing returns nothing
set gg_trg_Spell = CreateTrigger( )
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x00), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x01), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x02), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x03), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x04), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x05), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x06), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x07), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x08), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x09), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x0A), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x0B), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x0C), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x0D), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x0E), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(gg_trg_Spell, Player(0x0F), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerAddCondition( gg_trg_Spell, Condition(function Trig_Spell_Actions) )
set MaxX = GetRectMaxX(GetWorldBounds())
set MinX = GetRectMinX(GetWorldBounds())
set MaxY = GetRectMaxY(GetWorldBounds())
set MinY = GetRectMinY(GetWorldBounds())
endfunction
endlibrary
а насчёт вращения юнита, если мемхак не помогает, можешь найти дамми модель 360 градусов и прикрепить к ней кинжал как эффект, устанавливая нужную анимацию модели в соответствии с таким-то градусом, костыльно, но работает без проблем
Загруженные файлы
`
ОЖИДАНИЕ РЕКЛАМЫ...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.
он спрашивает почему кинжал себя так странно ведёт)
Ред. rsfghd
то что я бы посоветовал переделать, это выбирать юнитов вокруг точки падения, которая регистрируется после первого пораженного юнита, а не вокруг последней пораженной цели, в видео ты можешь посмотреть пример как глупо полёт смотрелся (0:24)
Ред. rsfghd