• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[JASS] Smoother lightning?

Status
Not open for further replies.
Level 1
Joined
Aug 27, 2012
Messages
2
Hi guys, I'm fairly new to JASS (started learning about a couple days ago), and to this forum, since I want to migrate from making GUI spells to JASS coded ones, complete with MUI.

Here's a code that I made (again, I'm quite new) that creates a lightning between units. The lightning moves between units and will break if the units are within 2000 units apart.
JASS:
function Trig_lightning_2_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A001'
endfunction

function fastremoveloc takes location a, location b returns nothing
    call RemoveLocation(a)
    call RemoveLocation(b)
endfunction

function clearall takes handle a, handle b, handle c, handle d, handle e returns nothing
    set a = null
    set b = null
    set c = null
    set d = null
    set e = null
endfunction

function Trig_lightning_2_Actions takes nothing returns nothing
    local unit source = GetSpellAbilityUnit()
    local unit target = GetSpellTargetUnit()
    local location sourceloc = GetUnitLoc(source)
    local location targetloc = GetUnitLoc(target)
    local lightning link
    local real distancecheck
    call AddLightningLoc("HWSB", sourceloc, targetloc)
    set link = GetLastCreatedLightningBJ()
    call fastremoveloc(sourceloc, targetloc)
    loop
        set sourceloc = GetUnitLoc(source)
        set targetloc = GetUnitLoc(target)
        set distancecheck = DistanceBetweenPoints(sourceloc, targetloc)
        exitwhen distancecheck > 2000.00
        call MoveLightningLoc(link, sourceloc, targetloc)
        call fastremoveloc(sourceloc, targetloc)
        call TriggerSleepAction(0.01)
    endloop
    call fastremoveloc(sourceloc, targetloc)
    call DestroyLightningBJ(link)
    //set source = null
    //set target = null
    //set sourceloc = null
    //set targetloc = null
    //set link = null
    //set linktimer = null
    call clearall(source, target, sourceloc, targetloc, link)
endfunction

//===========================================================================
function InitTrig_lightning_2 takes nothing returns nothing
    set gg_trg_lightning_2 = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_lightning_2, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_lightning_2, Condition( function Trig_lightning_2_Conditions ) )
    call TriggerAddAction( gg_trg_lightning_2, function Trig_lightning_2_Actions )
endfunction
Knowing that I triggered something in JASS that's finally MUI, I'd say I accomplished something but there's one problem. Since I'm using TriggerSleepAction (which I think is the Wait action in GUI), the minimum amount is about 0.1 seconds, which means the lightning updates its position every 0.1 seconds. This results in the lightning looking really choppy, as opposed to updating every 0.01 seconds which is very smooth. I tried starting another timer that lasts 0.01 seconds inside the loop, then making another loop after the timer which will exit when the mini timer is over, which then goes back to the first loop, updating the lightning positions (since I assumed loops will not trigger any other action below it until the exitwhen is met).


Here's what I mean:
JASS:
    loop
        set sourceloc = GetUnitLoc(source)
        set targetloc = GetUnitLoc(target)
        set distancecheck = DistanceBetweenPoints(sourceloc, targetloc)
        exitwhen distancecheck > 2000.00
        call StartTimerBJ(updatetimer, false, 0.01) //updatetimer is the minitimer
        loop
             exitwhen TimerGetRemaining(updatetimer) <= 0.00
        endloop
        call MoveLightningLoc(link, sourceloc, targetloc)
        call fastremoveloc(sourceloc, targetloc) //fastremove is a function that I made
        //call TriggerSleepAction(0.01)

Is there someway to refine this trigger? Or am I gonna delve further into the world of Hashtables, which I am also learning at the moment? And also while I'm it, can you guys help me out and point which leaks I need to clean up in my trigger?
 
Level 22
Joined
Dec 31, 2006
Messages
2,216
JASS:
function fastremoveloc takes location a, location b returns nothing
    call RemoveLocation(a)
    call RemoveLocation(b)
endfunction

Why do you have a function for this? Just put those 2 lines inside your code where they should be. Having unnecessary functions causes unneeded overhead and it's slower in execution.

JASS:
function clearall takes handle a, handle b, handle c, handle d, handle e returns nothing
    set a = null
    set b = null
    set c = null
    set d = null
    set e = null
endfunction
This doesn't even work. The parameters are passed by value, not by reference. This function will not null the variables that you pass it. And, as with the function above, just put the lines inside the code instead of creating a function for it.

JASS:
function Trig_lightning_2_Actions takes nothing returns nothing
    local unit source = GetSpellAbilityUnit()
    local unit target = GetSpellTargetUnit()
    local location sourceloc = GetUnitLoc(source)
    local location targetloc = GetUnitLoc(target)
    local lightning link
    local real distancecheck
    call AddLightningLoc("HWSB", sourceloc, targetloc)
    set link = GetLastCreatedLightningBJ()
    call fastremoveloc(sourceloc, targetloc)
    loop
        set sourceloc = GetUnitLoc(source)
        set targetloc = GetUnitLoc(target)
        set distancecheck = DistanceBetweenPoints(sourceloc, targetloc)
        exitwhen distancecheck > 2000.00
        call MoveLightningLoc(link, sourceloc, targetloc)
        call fastremoveloc(sourceloc, targetloc)
        call TriggerSleepAction(0.01)
    endloop
    call fastremoveloc(sourceloc, targetloc)
    call DestroyLightningBJ(link)
    //set source = null
    //set target = null
    //set sourceloc = null
    //set targetloc = null
    //set link = null
    //set linktimer = null
    call clearall(source, target, sourceloc, targetloc, link)
endfunction
You should stay away from BJ functions. Most of them can be removed easily without the need of extra lines.

For example, instead of this:
JASS:
    call AddLightningLoc("HWSB", sourceloc, targetloc)
    set link = GetLastCreatedLightningBJ()
Do this:
JASS:
    call AddLightningLoc("HWSB", sourceloc, targetloc)
    //Then use bj_lastCreatedLightning directly instead of link
However, it would be more preferable to do this:
JASS:
    set link = AddLightningEx("HWSB", true, GetLocationX(sourceloc), GetLocationY(sourceloc), GetLocationZ(sourceloc), GetLocationX(targetloc), GetLocationY(targetloc), GetLocationZ(targetloc))
(The most preferable would be to abandon locations entirely and use coordinates.

And instead of this: call DestroyLightningBJ(link), do this: call DestroyLightning(link).
Don't ask me why some of these useless BJs exist. Some of them are okay to use, like TriggerRegisterAnyUnitEventBJ but most of them should be avoided.

As for your questions. Yes, you should delve into hashtables, however it's not necessary for this particular code. Simple arrays are enough.

Btw, this is how you're supposed to use timers:

JASS:
//This function will run periodically every 0.03 second. Assuming 'SomeOtherFunction' is called
function SomeFunction takes nothing returns nothing
endfunction

function SomeOtherFunction takes nothing returns nothing
    local timer t = CreateTimer()
    call TimerStart(t, 0.03, true, function SomeFunction)
    set t = null
endfunction

As for leaks, if you follow the very first things I pointed out in this post, then it shouldn't be an issue.
 
Level 1
Joined
Aug 27, 2012
Messages
2
Wow, thanks for the help, Reborn Devil! Learned a lot of things from your post.

As for the timer one you provided me, is SomeOtherFunction a different function from the main action trigger (Trig_lightning_2_actions)? And that I should call SomeOtherFunction somwhere in Trig_lightning_2_actions?
 
Level 22
Joined
Dec 31, 2006
Messages
2,216
As for the timer one you provided me, is SomeOtherFunction a different function from the main action trigger (Trig_lightning_2_actions)? And that I should call SomeOtherFunction somwhere in Trig_lightning_2_actions?
No, SomeOtherFunction is basically your main action function, and SomeFunction should be called from it.

Basically, this is how your trigger should be laid out:
JASS:
// I've renamed this function for better readability
function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A001'
endfunction

function Run takes nothing returns nothing
    //local timer t = GetExpiredTimer() (Uncomment this if you're going for hashtables, otherwise, remove it)
    // You need to get "source", "target" and "link" either through hashtables or indexing (arrays)
    local location sourceloc = GetUnitLoc(source)
    local location targetloc = GetUnitLoc(target)
    if (DistanceBetweenPoints(sourceloc, targetloc) < 2000) then //I can tolerate this BJ (any function that lights up as red is a BJ function)
        call MoveLightning(link, GetLocationX(sourceloc), GetLocationY(sourceloc), GetLocationZ(sourceloc), GetLocationX(targetloc), GetLocationY(targetloc), GetLocationZ(targetloc))
    else
        call DestroyLightning(link)
        // You need to stop (this is important) and then destroy the timer here if you're using hashtables.
        // If you're using indexing you would need to decrement an integer and stop (only stop) the timer if the integer reaches 0.
    endif
    call RemoveLocation(sourceloc)
    call RemoveLocation(targetloc)
    set sourceloc = null
    set targetloc = null
    // Null "source", "target" and "link" as well
endfunction

// I've renamed this function for better readability
function Actions takes nothing returns nothing
    local unit source = GetSpellAbilityUnit() // This should be replaced by GetTriggerUnit() which is slightly faster.
    // GetTriggerUnit() gets the unit that triggered your event and therefore works with many different events.
    local unit target = GetSpellTargetUnit()
    local location sourceloc = GetUnitLoc(source) // Normally we do not use locations because they are slower, and you get more function calls.
    // However, we need to account for terrain height when dealing with lightning effects.
    // Hence the GetLocationZ() call.
    local location targetloc = GetUnitLoc(target)
    local lightning link = AddLightningEx("HWSB", true, GetLocationX(sourceloc), GetLocationY(sourceloc), GetLocationZ(sourceloc), GetLocationX(targetloc), GetLocationY(targetloc), GetLocationZ(targetloc))
    local timer t = CreateTimer()

    // Using an interval of 0.03 is less laggy than 0.01, and it's not necessary to use anything lower than 0.03 as it will not be noticeably different to our eye
    call TimerStart(t, 0.03, true, function Run)

    call RemoveLocation(sourceloc)
    call RemoveLocation(targetloc)

    set source = null
    set target = null
    set sourceloc = null
    set targetloc = null
    set link = null
    set t = null
endfunction

//===========================================================================
function InitTrig_lightning_2 takes nothing returns nothing
    local trigger t = CreateTrigger() // I doubt it's necessary to ever disable this trigger from somewhere else, so we make it local.
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function Conditions)) // Some will say that you should put your actions inside your conditions, however it's not really necessary
    // Also, one thing at a time :D
    call TriggerAddAction(t, function Actions)
    set t = null
endfunction

Now, as for the hashtables/indexing, that's something you should try to learn yourself by looking at tutorials and trying stuff out. Experimenting with a language is the best way to learn.
 
Status
Not open for further replies.
Top