• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Knock-Back Lite

Level 18
Joined
Jan 21, 2006
Messages
2,552
This is an extremely lite system for creating the effect of a knock-back on a specific unit. The knock-back uses GetUnitX/GetUnitY so a single unit can be applied multiple knock-backs and not have it act weird. It uses a single timer to update a stack of data making it very efficient. The amount of function calls is kept to a bear minimum, which should yield quick results.

The aim of this library is for users who seek out a very very basic knock-back. The user must specify a distance a unit will be knocked back, and the time it will take.

Its as easy to use as:

JASS:
call knockback.create( unitVar, angleInRadians, distance, time )

Requirements
  • JassHelper (to compile)

Knock Back Lite
JASS:
library Knockback


globals
//*********************
//* Configuration
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯
//*
    private constant real       LOOP_REF        = 0.03              
//*
//*
//*****************************************************************************
endglobals
    

struct knockback
    
    readonly    unit    subject         = null
    readonly    real    velocity
    readonly    real    rotation
    private     real    rotationCos
    private     real    rotationSin
    
    readonly    real    friction        
    
    private static  real            checkItemX          = GetRectMaxX(bj_mapInitialPlayableArea)
    private static  real            checkItemY          = GetRectMaxY(bj_mapInitialPlayableArea)
    private static  item            checkItem         
    
    private static  timer           priv_stackLoop      = CreateTimer()
    private static  constant real   priv_stackLoopRef   = LOOP_REF
    private         real            priv_time          
    private         boolean         priv_stopped        = false

    private         integer         priv_index 
    private static  thistype array  priv_stack
    private static  integer         priv_stackN         = 0

    
    method stop takes nothing returns nothing
        // Instead of directly destroying an instance from knockback, the user can tell the system
        // that he/she wants the knockback to be stopped. If the knockback is to be stopped, it will
        // be on the next iteration.
        set .priv_stopped = true
    endmethod
    private method onDestroy takes nothing returns nothing
        set thistype.priv_stackN = thistype.priv_stackN-1
        set thistype.priv_stack[.priv_index] = thistype.priv_stack[thistype.priv_stackN]
        set thistype.priv_stack[.priv_index].priv_index = .priv_index
        if (.priv_stackN == 0) then
            call PauseTimer(thistype.priv_stackLoop)
        endif
    endmethod
    
    private static method onRef takes nothing returns nothing
        local integer i = .priv_stackN-1
        local thistype kb
        local real x
        local real y
        local real iX
        local real iY
        loop
            exitwhen(i < 0)
            set kb = .priv_stack[i]
            if (kb != 0) then
                set x  = GetWidgetX(kb.subject) + kb.velocity * kb.rotationCos
                set y  = GetWidgetY(kb.subject) + kb.velocity * kb.rotationSin
                //use an item to check the pathability of the new coordinate and
                //move the unit if the point is available.
                call SetItemPosition(checkItem, x, y)
                set iX = GetWidgetX(checkItem)
                set iY = GetWidgetY(checkItem)
                if ((iX-x)*(iX-x) + (iY-y)*(iY-y) <= 32) then
                    call SetUnitX(kb.subject, x)
                    call SetUnitY(kb.subject, y)
                endif
                
                set kb.velocity  = kb.velocity + kb.friction
                set kb.priv_time = kb.priv_time - .priv_stackLoopRef
                if (kb.priv_time <= 0.00) or kb.priv_stopped then
                    call kb.destroy()
                endif
            endif
            set i = i - 1
        endloop
        call SetItemPosition(checkItem, checkItemX, checkItemY)
        call SetItemVisible(checkItem, false)
    endmethod
    
    static method create takes unit u, real angle, real distance, real time returns thistype
        local thistype kb   = .allocate()
        set kb.subject      = u
        set kb.velocity     = 2*distance/time
        set kb.rotation     = angle
        set kb.rotationCos  = Cos(angle)
        set kb.rotationSin  = Sin(angle)
        set kb.friction     = -kb.velocity/time
        set kb.priv_index   = .priv_stackN
        set kb.priv_time    = time
        
        set kb.velocity = kb.velocity * .priv_stackLoopRef
        set kb.friction = kb.friction * .priv_stackLoopRef * .priv_stackLoopRef
        if (.priv_stackN == 0) then
            call TimerStart(.priv_stackLoop, .priv_stackLoopRef, true, function thistype.onRef)
        endif
        set .priv_stack[.priv_stackN] = kb
        set .priv_stackN = .priv_stackN + 1
        return kb
    endmethod
    
    static method onInit takes nothing returns nothing
        //the item will be have maintained invisibility as to not interfere with
        //in-game actions, such as a unit picking the item up.
        set checkItem=CreateItem('afac', checkItemX, checkItemY)
        call SetItemVisible(checkItem, false)
    endmethod
    
endstruct

endlibrary
 

Attachments

  • THW_KB_demo.w3x
    62.1 KB · Views: 299
Last edited:
Level 8
Joined
Oct 3, 2008
Messages
367
Might want to privatise onDestroy and onRef. To protect users from themselves.

Move that constant to the top of the script, and document it.

You could eliminate those local x/y variables in the onRef method.

JASS:
set x = GetUnitX(kb.subject) + kb.velocity * kb.rotationCos
set y = GetUnitY(kb.subject) + kb.velocity * kb.rotationSin
call SetUnitX(kb.subject, x)
call SetUnitY(kb.subject, y)
->
JASS:
call SetUnitX(kb.subject, GetUnitX(kb.subject) + kb.velocity * kb.rotationCos)
call SetUnitY(kb.subject, GetUnitY(kb.subject) + kb.velocity * kb.rotationSin)
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
azlier said:
Might want to privatise onDestroy and onRef. To protect users from themselves.

There is no real reason to privatize onDestroy because if users want to stop the knock-back half-way through the unit being knocked back they need to be able to. For now I've privatized the onDestroy method and added a stop method that basically flags the knock-back to be destroyed.

azlier said:
Move that constant to the top of the script, and document it.

Done.

azlier said:
You could eliminate those local x/y variables in the onRef method.

Also done. I was anticipating having to use these values more than once so I mindlessly used them. There really is no need for them, though, you're right.

CHA_Owner said:
Could you please post example use trigger?

It is pretty straight-forward, and I included how to use it in the first post (a template-use of the function) but I suppose I could attach a map-file to show you how its done in more detail.

I've updated the script accordingly with all of the suggestions, and added a configuration option to use SetUnitPosition instead of SetUnitX and SetUnitY.

I've updated the first post --it now includes a test-map with an implementation of this library (within the Bolt trigger). It also uses the projectiles library of mine but you can just ignore that :p
 
Last edited:
Level 18
Joined
Jan 21, 2006
Messages
2,552
Because that's the point of it, and it doesn't matter how long it would take another person to do it.

I could code TimerUtils in less than 15 minutes but that doesn't stop it from being a commonly used resource. There are not hundreds of knock-back systems, either, not to mention that this is a script not a system. It doesn't require anything but a small amount of vJass knowledge to use.

The difference between this and others (because differentiation is obviously needed) is that this is exactly as the thread states, Lite. Other knock-back systems are a lot more interwoven with other systems and have a lot of access functionality. The purpose of this would be as light-weight as you can possibly get while still keeping necessary functionality.

Even though it shouldn't be, this script also seems to be one of the only ones that allows the user to specify distance/time, rather than velocity/acceleration (which leaves the calculation up to the user) not that this is alone is a good enough reason for the approval of this script.

YourNameHere said:
Every half decent vJass coder could do this in about 15 minutes, and there are already 100 of knockback systems.

Whats funny is that if you look in the JASS scripts section of this web-site almost every single script there is trivial and more primitive than this. I don't know why you would even mention how "easy it is" to re-code because that isn't good logic to follow on any reasoning. This is the most basic knock-back a user could get, and sometimes that's exactly what users are looking for. If this resource is so unnecessary then why is it people are still having troubles with knock-back systems that they are trying to make.

Just so that my point is clear, here is the perfect example of a script that not only is trivial, but is actually inefficient for the primitive nature of its purpose. I shouldn't have to compare my scripts to other submissions, though, it just seems strange how you're questioning this library while leaving the gross inefficiencies of the script I linked to alone.
 
Level 11
Joined
Apr 29, 2007
Messages
826
I shouldn't have to compare my scripts to other submissions, though, it just seems strange how you're questioning this library while leaving the gross inefficiencies of the script I linked to alone.

I just like to fuck around with you :)

Now seriously, all the stuff you were writing is true, but a knockback system should NOT be made that simple.
You can't compare TimerUtils with this, it's like comparing french fries with mashed potatoes - they're made of the same but used for totally different things.

I would never want to use a knockback script that doesn't even support pathability. I'm not talking about too advanced features aswell, but it should atleast have some more than what it has now.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Well if you're super-super concerned about pathing, then there is the option of using SetUnitPosition, which takes into account pathing. I wasn't comparing this script to TimerUtils, I was just saying that scripts do not need to be hard to code for them to be resources.

Other than that, what bugs are you expecting? There really isn't that much room for error.

Rising_Dusk said:
JASS:
            //* Propagate the knockback in space and time
            set xf = xi + n.Displace*n.CosA
            set yf = yi + n.Displace*n.SinA
            call SetUnitPosition(u, xf, yf)

This is taken from Dusk's knock-back system. As you can see, the actual unit-movement is very simple, and would be equivalent to mine given PAUSE_UNIT was set to true.

YourNameHere said:
I would never want to use a knockback script that doesn't even support pathability.

So seeing as how the implementation of pathability in both this Lite version and Dusk's "Heavy" version are the same, I am interested in seeing this knock-back script that you use.
 
You can probably use this to remain to the standard of "lite" yet more functional:
JASS:
if not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
endif

Although, that would mean using the local variables x and y again. =P However, since this is a "lite" version this should cover most of pathing (except for pathing blockers and map bounds afaik).

Otherwise, nice job. Also nice job in the fact that this doesn't require other systems.
 
Level 8
Joined
Oct 3, 2008
Messages
367
SetUnitPosition is really a bad way to handle pathing because units slid in such a matter can't go through other units. Spammed SetUnitPosition also makes the game laggier over time for some reason due to the spammed stop orders it gives.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
azlier said:
SetUnitPosition is really a bad way to handle pathing

Tell that to Dusk, lol.

Perhaps, but its all we've got. The best approach would be using a physics engine to handle all of your knock-back needs. I could add a check to see whether or not the terrain is walkable, but that is an ambiguous condition. Not walkable means it could be water, it could be a cliff (up/down), or a variety of other things.

I can add the IsTerrainPathable check to the SetUnitX and SetUnitY commands, if it is necessary/requested --which would pretty much just do SetUnitPosition without the weird glitchy movement (around units, at least).
 
Level 8
Joined
Oct 3, 2008
Messages
367
>which would pretty much just do SetUnitPosition without the weird glitchy movement (around units, at least).

Exactly the point.

The absolute best (although not most efficient) method is to use items. Some resource on WC3C does that. Can't find it at the moment.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Yea, I know what method you're talking about. It is a lot more bulky though. It also isn't the best method, as it fails in certain scenarios --requiring even less efficiency to avoid. I think that in this case IsTerrainPathable will do fine. Of course, I should really make it an available option for users to have on/off --default would be on.

The method outlined in the case you suggested uses an item's creation coordinates to check whether or not the item was created where it should have been. If the item is in the same spot as it was created then it determines the pathing at that coordinate is fine. If the item is not in the same position, it checks to see whether an item is already there using EnumItemsInRect. If there are no items there, then the terrain is not "pathable". If there is an item there, then the terrain is "pathable".

I would prefer to avoid using all of the necessary code on each iteration simply to check for pathing.

Updated.
 
Last edited:
Level 18
Joined
Jan 21, 2006
Messages
2,552
Thanks for reading my post. Also, if both items are in the same spot it could mean that its in the midst of trees, or anything else that is forcing the item to overlap the other.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Well, for the sake of learning how to use this trick properly and the benefit of the submission, I will include this method of pathability checking. I realized that buildings were not adapted into the check and since I've been reading through implementations of the item trick. It isn't very complicated, it should be updated soon.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
I will have to re-read some of the stuff, its quite late right now and I'm dozing off, so I have to get some sleep. I'll keep working on it tomorrow.

Okay, I read the thread again. Basically what is going on (this is what I thought before, just wasn't clear on the details) is you're creating a widget (unit or item) and using the default pathing that is factored into those functions to determine whether or not a position is pathable to have a unit/item created on it (thus, "pathable"). It would be a similar effect to having a dummy-unit use "Blink" and obtaining the GetSpellTargetX() and GetSpellTargetY() --though CreateUnit/CreateItem may actually be more accurate.

YourNameHere said:
Well it still detects unpathable spots in all conditions.

I'm not sure what you mean, it is used to differentiate between a "pathable" and "unpathable" status given a set of coordinates. The initial item-creation forces the implementation of a built-in pathing check. The only situation in which an item is not created where it is specified is because there is either an item already there, or because the terrain beneath is classified as "unpathable". In order to determine whether or not this is the case, you enumerate the items in a small rect --determining whether the item has been displaced due to pathing, or due to an item block.

Apparently there is some other strange bug that will force two items to be in the same spot even though they should not be able to be created on-top of each other, though I'm not sure how credible this problem really is. In any situation it would at most require another if-statement to determine if that is the case.

Again, its late and I'm tired --I'll be at University tomorrow but I will have my laptop so I'll play around with this idea a bit more. Good night.

Okay, so I got it working. When I use an item (for constant collision size, not dependent on the unit) it works just about the same as IsTerrainPathable with the added bonus that it detects trees, buildings, and other miscellaneous stuff that isn't by the pathability native.

I've updated it with a working version of the item-pathability trick --though I've read about some problems that can occur. Right now it assumes that there are no problems, but obviously as I continue to read about this method of pathability (and practice it) bugs will surely arise. I will fix them as they come. First post updated.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Yeah I was going to write something about recycling the item (recycling; a concept I am learning and liking very much). I can't Benchmark test because when I update my grimoire newgen/maps won't even open, but why don't you make it a constant item and use SetItemPosition?

At the very least, the variable could be a global so that you don't have to null it each time? Or are locals faster than globals?
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
azlier said:
Why don't you recycle that item that you use to check pathing?

This update is long over due. I've updated the code included in the first post to use an item that is placed near the top right limits of your map (using GetWorldBounds()-100) so it should not interfere with anybody's map in any way.

I also hide the item on map initialization as to eliminate the chance of any run-time interaction.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
They are static members being referenced from a static method.

There was a problem with the code, however. In the iteration function I declare a local item and null it afterward but never actually use it. It has been removed from the code. I am also thinking about taking away the options for INCLUDE_PATHING and PAUSE_UNIT since they really aren't very necessary. The user should be controlling whether or not the unit is "stunned" outside of the knock-back and just leave the actual moving/pathing checking up to the knock-back as to keep it as basic and "lite" as possible.

Oh, I found another mistake too. It would be considered an abuse the amount of times SetItemPosition is unnecessarily used because it is set twice per iteration. It only needs to be set once, and then reset once the loop is complete. I'll update that too, and post some code on how a system without the unnecessary options would look.

What are your thoughts?

****
Here's a copy of the updated script, I will post the appropriate fixes to the script on the first page as well.

JASS:
library Knockback

globals
//*********************
//* Configuration
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯
//*
    private constant real       LOOP_REF        = 0.03              
//*
//*
//*****************************************************************************
endglobals


struct knockback
    
    readonly    unit    subject         = null
    readonly    real    velocity
    readonly    real    rotation
    private     real    rotationCos
    private     real    rotationSin
    
    readonly    real    friction        
    
    private static  constant real   checkItemX          = GetRectMaxX(GetWorldBounds())-100
    private static  constant real   checkItemY          = GetRectMaxY(GetWorldBounds())-100
    private static  item            checkItem           = CreateItem('afac', checkItemX, checkItemY)
    
    private static  timer           priv_stackLoop      = CreateTimer()
    private static  constant real   priv_stackLoopRef   = LOOP_REF
    private         real            priv_time          
    private         boolean         priv_stopped        = false

    private         integer         priv_index 
    private static  thistype array  priv_stack
    private static  integer         priv_stackN         = 0
    
    
    method stop takes nothing returns nothing
        // Instead of directly destroying an instance from knockback, the user can tell the system
        // that he/she wants the knockback to be stopped. If the knockback is to be stopped, it will
        // be on the next iteration.
        set .priv_stopped = true
    endmethod
    private method onDestroy takes nothing returns nothing
        set thistype.priv_stackN = thistype.priv_stackN-1
        set thistype.priv_stack[.priv_index] = thistype.priv_stack[thistype.priv_stackN]
        set thistype.priv_stack[.priv_index].priv_index = .priv_index
        if (.priv_stackN == 0) then
            call PauseTimer(thistype.priv_stackLoop)
        endif
    endmethod
    
    private static method onRef takes nothing returns nothing
        local integer i = .priv_stackN-1
        local thistype kb
        local real x
        local real y
        local real iX
        local real iY
        loop
            exitwhen(i < 0)
            set kb = .priv_stack[i]
            if (kb!=0) then
                set x = GetUnitX(kb.subject) + kb.velocity * kb.rotationCos
                set y = GetUnitY(kb.subject) + kb.velocity * kb.rotationSin
                
                //use an item to check the pathability of the new coordinate and
                //move the unit if the point is available.
                call SetItemPosition(checkItem, x, y)
                set iX=GetItemX(checkItem)
                set iY=GetItemY(checkItem)
                if ((iX-x)*(iX-x) + (iY-y)*(iY-y)) <= (32*32) then
                    call SetUnitX(kb.subject, x)
                    call SetUnitY(kb.subject, y)
                endif
                
                set kb.velocity = kb.velocity + kb.friction
                set kb.priv_time = kb.priv_time - .priv_stackLoopRef
                if (kb.priv_time <= 0.00) or kb.priv_stopped then
                    call kb.destroy()
                endif
            endif
            set i=i-1
        endloop
        call SetItemPosition(checkItem, checkItemX, checkItemY)
    endmethod
    
    static method create takes unit u, real angle, real distance, real time returns thistype
        local thistype kb   = .allocate()
        set kb.subject      = u
        set kb.velocity     = 2*distance/time
        set kb.rotation     = angle
        set kb.rotationCos  = Cos(angle)
        set kb.rotationSin  = Sin(angle)
        set kb.friction     = -kb.velocity/time
        set kb.priv_index   = .priv_stackN
        set kb.priv_time    = time
        
        set kb.velocity = kb.velocity * .priv_stackLoopRef
        set kb.friction = kb.friction * .priv_stackLoopRef * .priv_stackLoopRef
        if (.priv_stackN == 0) then
            call TimerStart(.priv_stackLoop, .priv_stackLoopRef, true, function thistype.onRef)
        endif
        set .priv_stack[.priv_stackN] = kb
        set .priv_stackN = .priv_stackN + 1
        return kb
    endmethod
    
    //hide the check item on initialization so that it is completely unaccessible to
    //any in-game interaction.
    static method onInit takes nothing returns nothing
        call SetItemVisible(checkItem, false)
    endmethod
    
endstruct

endlibrary
 
Level 11
Joined
Sep 30, 2009
Messages
697
You sure you posted the right code here? The code in the testmap is working but the one you posted above is wrong o_O Heres the code from testmap :D

JASS:
library Knockback

globals
//*********************
//* Configuration
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯
    private constant real       LOOP_REF        = 0.03                      //* Defines the rate (in seconds) by which the knockback will
                                                                            //* iterate through each instance (and refresh the stack).
    private constant boolean    PAUSE_SUBJECT   = false                     //* Determines whether to use SetUnitPosition or SetUnitX/Y to
                                                                            //* update the subject's position. SetUnitPosition not only 
                                                                            //* pauses the unit briefly but it also factors pathing into
                                                                            //* the movement.
//*
//*****************************************************************************
endglobals


struct knockback
    
    readonly    unit    subject         = null
    readonly    real    velocity
    readonly    real    rotation
    private     real    rotationCos
    private     real    rotationSin
    
    readonly    real    friction        
    
    // --------------------------------------------------------------------------------------------------
    private static  timer           priv_stackLoop      = CreateTimer()
    private static  constant real   priv_stackLoopRef   = LOOP_REF
    private         real            priv_time          
    private         boolean         priv_stopped        = false
    // --------------------------------------------------------------------------------------------------
    private         integer         priv_index 
    private static  thistype array  priv_stack
    private static  integer         priv_stackN         = 0
    // --------------------------------------------------------------------------------------------------
    
    method stop takes nothing returns nothing
        // Instead of directly destroying an instance from knockback, the user can tell the system
        // that he/she wants the knockback to be stopped. If the knockback is to be stopped, it will
        // be on the next iteration.
        set .priv_stopped = true
    endmethod
    
    private method onDestroy takes nothing returns nothing
        set thistype.priv_stackN = thistype.priv_stackN-1
        set thistype.priv_stack[.priv_index] = thistype.priv_stack[thistype.priv_stackN]
        set thistype.priv_stack[.priv_index].priv_index = .priv_index
        if (.priv_stackN == 0) then
            call PauseTimer(thistype.priv_stackLoop)
        endif
    endmethod
    
    private static method onRef takes nothing returns nothing
        local integer i = .priv_stackN-1
        local thistype kb
        loop
            exitwhen(i < 0)
            set kb = .priv_stack[i]
            if (kb!=0) then
                static if (PAUSE_SUBJECT) then
                    call SetUnitPosition(kb.subject, GetUnitX(kb.subject) + kb.velocity * kb.rotationCos, GetUnitY(kb.subject) + kb.velocity * kb.rotationSin)
                else
                    call SetUnitX(kb.subject, GetUnitX(kb.subject) + kb.velocity * kb.rotationCos)
                    call SetUnitY(kb.subject, GetUnitY(kb.subject) + kb.velocity * kb.rotationSin)
                endif
                
                set kb.velocity = kb.velocity + kb.friction
                set kb.priv_time = kb.priv_time - .priv_stackLoopRef
                if (kb.priv_time <= 0.00) or kb.priv_stopped then
                    call kb.destroy()
                endif
            endif
            set i=i-1
        endloop
    endmethod
    
    static method create takes unit u, real angle, real distance, real time returns thistype
        local thistype kb   = .allocate()
        set kb.subject      = u
        set kb.velocity     = 2*distance/time
        set kb.rotation     = angle
        set kb.rotationCos  = Cos(angle)
        set kb.rotationSin  = Sin(angle)
        set kb.friction     = -kb.velocity/time
        set kb.priv_index   = .priv_stackN
        set kb.priv_time    = time
        
        set kb.velocity = kb.velocity * .priv_stackLoopRef
        set kb.friction = kb.friction * .priv_stackLoopRef * .priv_stackLoopRef
        if (.priv_stackN == 0) then
            call TimerStart(.priv_stackLoop, .priv_stackLoopRef, true, function thistype.onRef)
        endif
        set .priv_stack[.priv_stackN] = kb
        set .priv_stackN = .priv_stackN + 1
        return kb
    endmethod
    
endstruct

endlibrary
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
The test-map code is extremely old, but you were right the static declarations associated with the item were causing some problems. The create item call was causing the game to crash,

I was using constant real variables and initializing them to GetRectMaxX and GetRectMaxY which are not constant natives, which caused a small problem. I've fixed the code and updated the test-map with the fixed updated code. Also CreateItem cannot be used to initialize a global variable.
 
Level 8
Joined
Oct 3, 2008
Messages
367
JASS:
                if ((iX-x)*(iX-x) + (iY-y)*(iY-y)) <= (32*32) then
Why don't you just put in 1024? Other than that, it's pretty basic, but that's what it's made for. Does it's job well. :p

I have a better idea.

JASS:
if GetWidgetX(checkItem) + GetWidgetY(checkItem) - x - y <= 32 then
Yes, I think that's valid. It also eliminates two variables.

You might also want to eliminate GetUnitX/Y and replace those with some struct members.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
It would probably be quicker in this case to not use variables for GetWidgetX() and GetWidgetY() since they are only used once in the distance calculation. It would have probably been faster for me to use the function calls instead of the variables before too, but whatever.

By the way, is GetWidgetX() faster than GetItemX()? Can you post a benchmark?

I updated the code. I'll update the demo-map too.
 
Level 8
Joined
Oct 3, 2008
Messages
367
Benchmarks in the past have proven GetWidgetAnything to be faster than the widget type specific equivalent. Which ones, who did them, and when, I don't remember.

The specific equivalents probably just pass the arguments to GetWidgetSomething.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
So then GetItemX would be faster.

I really need to setup Reinventing the Craft so I can use these benchmark natives.

azlier said:
Benchmarks in the past have proven GetWidgetAnything to be faster than the widget type specific equivalent

Perhaps in the past, but blizzard has changed quite a few things. Bribe's recent benchmark suggests GetItemX() to be faster than GetWidgetX(). If there is nothing shown to contradict this result then I'm going to leave it as it is.

Bribe said:
Benchmark has widgetX at 9. and itemX at 8., so there's some input.
 
Level 8
Joined
Oct 3, 2008
Messages
367
>So then GetItemX would be faster.

That can't be proven by Bribe's post. We're not sure what he even means. For all we know, he could mean times executed per millisecond or something, which would make GetWidgetX faster.

>Perhaps in the past, but blizzard has changed quite a few things.

Past being after the latest patch.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I'm using the new benchmark natives, and yes I have to nod a bit towards azlier because I've had some weird results from it. The first day I tried it, locals were operating at 2x faster than globals, while the next day I did the exact same test, total controlled environment, got almost 1-1 with globals actually slightly faster. Since then, I haven't relied on benchmarking :p
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Sorry I changed my wording from "proven" to "suggests", obviously it does not prove it that would be a radical jump to conclusion. I'm allowing you to present evidence that suggests otherwise, but from what I gather the same amount of executions that took GetWidgetX() 9 seconds (or perhaps .9 seconds) took GetItemX() 8 seconds (or perhaps .8 seconds). It is a little unclear what he means, but in a matter of time there really isn't much room for misinterpretation since bigger numbers typically mean more time is taken. We'll wait to see.

Bribe said:
I'm using the new benchmark natives, and yes I have to nod a bit towards azlier because I've had some weird results from it. The first day I tried it, locals were operating at 2x faster than globals, while the next day I did the exact same test, total controlled environment, got almost 1-1 with globals actually slightly faster. Since then, I haven't relied on benchmarking :p

Oh, so then perhaps that is not a good benchmark option. I would still like to see some consistent evidence that suggests one is faster than the other. I did not change anything because of Bribe's claims (as it was supporting what I had) and I would not like to change it until some clarification is given.

Though, if GetWidgetX is actually faster then it would probably be quicker to use that instead of GetUnitX as well.

azlier said:
You might also want to eliminate GetUnitX/Y and replace those with some struct members.

I don't use struct members to control the knock-back. I purposely use the coordinates of the unit so that it is always synchronized with unit movement, and it allows for the combination of multiple knock-backs without having to worry about combining the values and searching the stack for the matching instance.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
JASS:
library Benchmark initializer OnInit
    ///////////////////////////////////////////////
    // Native declarations for stopwatch natives //
    //  - Requires no modified common.j import   //
    ///////////////////////////////////////////////
    native StopWatchCreate  takes nothing returns integer
    native StopWatchMark    takes integer stopwatch returns real
    native StopWatchDestroy takes integer stopwatch returns nothing
    
    /////////////////////////
    // Benchmarking script //
    /////////////////////////
    
    // Initialisation
    globals
        // ...
        item thing
    endglobals
    ////////////
    private function Init takes nothing returns nothing
        // things required to be performed once before your test
        set thing = CreateItem('phea',0.,0.)
    endfunction
    // Tests
    globals
    private constant string TITLE_A="Widget X"
    //! textmacro Benchmark__TestA
        call GetWidgetX(thing)
        call GetWidgetX(thing)
        call GetWidgetX(thing)
        call GetWidgetX(thing)
        call GetWidgetX(thing)
        call GetWidgetX(thing)
        call GetWidgetX(thing)
        call GetWidgetX(thing)
        call GetWidgetX(thing)
        call GetWidgetX(thing)
    //! endtextmacro
    private constant string TITLE_B="Item X"
    //! textmacro Benchmark__TestB
        call GetItemX(thing)
        call GetItemX(thing)
        call GetItemX(thing)
        call GetItemX(thing)
        call GetItemX(thing)
        call GetItemX(thing)
        call GetItemX(thing)
        call GetItemX(thing)
        call GetItemX(thing)
        call GetItemX(thing)
    //! endtextmacro
    endglobals
    
    // execution
    private function TestA1000 takes nothing returns nothing
        local integer i = 100 // hence 1,000 execs
        loop
            exitwhen(i == 0)
            set i = i - 1
            // Repeat x10
            //! runtextmacro Benchmark__TestA() // 1
            //! runtextmacro Benchmark__TestA() // 2
            //! runtextmacro Benchmark__TestA() // 3
            //! runtextmacro Benchmark__TestA() // 4
            //! runtextmacro Benchmark__TestA() // 5
            //! runtextmacro Benchmark__TestA() // 6
            //! runtextmacro Benchmark__TestA() // 7
            //! runtextmacro Benchmark__TestA() // 8
            //! runtextmacro Benchmark__TestA() // 9
            //! runtextmacro Benchmark__TestA() // 10
        endloop
    endfunction
    private function TestB1000 takes nothing returns nothing
        local integer i = 100
        loop
            exitwhen(i == 0) // hence 1,000 execs
            set i = i - 1
            // Repeat x10
            //! runtextmacro Benchmark__TestB() // 1
            //! runtextmacro Benchmark__TestB() // 2
            //! runtextmacro Benchmark__TestB() // 3
            //! runtextmacro Benchmark__TestB() // 4
            //! runtextmacro Benchmark__TestB() // 5
            //! runtextmacro Benchmark__TestB() // 6
            //! runtextmacro Benchmark__TestB() // 7
            //! runtextmacro Benchmark__TestB() // 8
            //! runtextmacro Benchmark__TestB() // 9
            //! runtextmacro Benchmark__TestB() // 10
        endloop
    endfunction
    
    private function OnEsc takes nothing returns nothing
        local integer sw = StopWatchCreate()
        local integer i = 0
        
        loop
            set i = i + 1
            call TestA1000.evaluate() // x10 - 10,000 executions altogether.
            exitwhen(i == 10)
        endloop
        call BJDebugMsg("|cff66ddff"+TITLE_A+" : "+R2S(StopWatchMark(sw)*100)+"|r")
        call StopWatchDestroy(sw)
        
        set i = 0
        set sw = StopWatchCreate()
        loop
            set i = i + 1
            call TestB1000.evaluate() // x10 - 10,000 executions altogether.
            exitwhen(i == 10)
        endloop
        call BJDebugMsg("|cffddff66"+TITLE_B+" : "+R2S(StopWatchMark(sw)*100)+"|r")
        call StopWatchDestroy(sw)
    endfunction
    
    ///////////////////////////////
    // Registers the OnEsc event //
    ///////////////////////////////
    private function OnInit takes nothing returns nothing
        local trigger t=CreateTrigger()
        call TriggerRegisterPlayerEvent(t,Player(0),EVENT_PLAYER_END_CINEMATIC)
        call TriggerAddAction(t,function OnEsc)
        call Init()
    endfunction
endlibrary
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
azlier said:
But apparently you changed that.

Yes, but I forgot to remove the declaration of iX and iY; by the time you read this it will be updated :p

Also for the hell of it I changed the getter functions to their parent-type equivalent. Hope you like :D
 
Level 9
Joined
Oct 11, 2009
Messages
477
Or, instead of creating that whole library, just use "PolarProjectionBJ", and use formula for the speed and distance. Cause you used the function SetUnitPosition which can be used also in GUI.


Aside from that I think it's efficient, no leaks...:thumbs_up:
 
Top