Snippet orbiteffect

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
An orbiteffect is basically an eyecandy effect that happens to rotate in an circular type of movement (on a sphere's surface).

uses/requires: xefx

The api is really simple:

Create:
[ljass]orbiteffect orbiteffect.add_to_unit(unit u, real radius, integer axis_of_rotation, real angle_in_degrees, real orbits_per_second, string path_to_effect)[/ljass]

Where the orbiteffect will orbit the unit "u" from a distance of "radius", in a path determent by the "axis_of_rotation" and the "angle_in_degrees", with a speed of "orbits_per_second", using the model of "path_to_effect".

Destroy:
[ljass]nothing <orbiteffect_instance>.destroy()[/ljass]


The most important and tricky arguments in the add_to_unit method are the axis_of_rotation which can take only the following values (ORBIT_X_AXIS, ORBIT_Y_AXIS and ORBIT_Z_AXIS).
I am finding it difficult for explaining them so I tried to make a "silly" pic that only describes the ORBIT_Z_AXIS (see the attachments section).

Yeah... not much of explanation but... there's always the demo map in which there are examples which hopefully would make sense.

JASS:
library orbiteffect uses xefx

globals
    private real PERIOD = 0.03125
endglobals

globals
    // exported globals used for the axis_of_rotation argument in the add_to_unit method
    integer ORBIT_X_AXIS = 0
    integer ORBIT_Y_AXIS = 1
    integer ORBIT_Z_AXIS = 2

    // private global(s)
    private location g_loc = Location(0, 0)
endglobals

struct orbiteffect
    private static  timer             graviton = CreateTimer()

    private static  orbiteffect array instances
    private static  integer           instance_count = 0

    private unit    center_unit

    public  real    radius

    private integer rotation_axis
    private real    rotation_angle

    public  xefx    xefx

    private real    az = 0
    private real    az_inc

    public  real    x_offset
    public  real    y_offset
    public  real    z_offset

    static method looop takes nothing returns nothing
        local orbiteffect this
        local integer     i
        local real        ox
        local real        oy
        local real        oz
        local real        cux
        local real        cuy
        local real        cuz
        // local real        cuf

        if instance_count == 0 then // the destroy method was called on all orbiteffect instances
            call PauseTimer(graviton)
            return
        endif

        set i = 0
        loop
            exitwhen i &gt;= instance_count       

            set this = instances<i>
            set i = i + 1

            set cux = GetUnitX(center_unit)
            set cuy = GetUnitY(center_unit)
            call MoveLocation(g_loc, cux, cuy)
            set cuz = GetUnitFlyHeight(center_unit) + GetLocationZ(g_loc)

            // set cuf = GetUnitFacing(center_unit) * bj_DEGTORAD

            if     ORBIT_X_AXIS == rotation_axis then
                set ox = cuz + radius * Sin(az) * Cos(rotation_angle) + z_offset
                set oy = cuy + radius * Sin(az) * Sin(rotation_angle) + y_offset //* Sin(cuf)
                set oz = cux + radius * Cos(az)                       + x_offset //* Cos(cuf)
                set xefx.x = oz
                set xefx.y = oy
                call MoveLocation(g_loc, oz, oy)
                set xefx.z = ox - GetLocationZ(g_loc)

            elseif ORBIT_Y_AXIS == rotation_axis then
                set ox = cux + radius * Sin(az) * Cos(rotation_angle) + x_offset //* Cos(cuf)
                set oy = cuz + radius * Sin(az) * Sin(rotation_angle) + z_offset
                set oz = cuy + radius * Cos(az)                       + y_offset //* Sin(cuf)
                set xefx.x = ox
                set xefx.y = oz
                call MoveLocation(g_loc, ox, oz)
                set xefx.z = oy - GetLocationZ(g_loc)

            elseif ORBIT_Z_AXIS == rotation_axis then
                set ox = cux + radius * Sin(az) * Cos(rotation_angle) + x_offset //* Cos(cuf)
                set oy = cuy + radius * Sin(az) * Sin(rotation_angle) + y_offset //* Sin(cuf)
                set oz = cuz + radius * Cos(az)                       + z_offset
                set xefx.x = ox
                set xefx.y = oy
                call MoveLocation(g_loc, ox, oy)
                set xefx.z = oz - GetLocationZ(g_loc)

            endif

            set az = az + az_inc
        endloop
    endmethod

    static method add_to_unit takes unit u, real radius, integer axis_of_rotation, real angle_in_degrees, real orbits_per_second, string path_to_effect returns orbiteffect
        local orbiteffect this = orbiteffect.allocate()

        set center_unit    = u
        set this.radius    = radius
        set rotation_axis  = axis_of_rotation
        set rotation_angle = angle_in_degrees * bj_DEGTORAD

        // get a dummy 
        // and attach the effect to it
        set xefx = xefx.create(0, 0, 0)
        set xefx.fxpath = path_to_effect

        set x_offset = 0
        set y_offset = 0
        set z_offset = 0

        set az_inc = ((360.0 / (1 / PERIOD)) * orbits_per_second) * bj_DEGTORAD

        set instances[instance_count] = this
        set instance_count = instance_count + 1

        if instance_count == 1 then
            call TimerStart(graviton, PERIOD, true, function orbiteffect.looop)
        endif

        return this
    endmethod       
        
    method destroy takes nothing returns nothing
        set instance_count  = instance_count - 1       
        set instances[this - 1] = instances[instance_count]

        call this.xefx.destroy()

        call deallocate()
    endmethod

endstruct
    
endlibrary

</i>
 

Attachments

  • orbiteffect_demo.w3x
    33.2 KB · Views: 427
  • z_axis_of_roation.png
    z_axis_of_roation.png
    95.5 KB · Views: 448
I've checked the test map, and hey bro, xe 0.9 is out! You should d/l it.

A linked list might be slightly faster than the current stack approach (WC3 match is kinda slow, and with a linked list you don't have to use any kind of math; and I also like linked lists much more, however it doesn't affect your code in any way).

Also, even tho' I like C++ and its syntax, it's still WC3 which has its own naming conventions, I wouldn't mix them, it can be confusing.

Other than that, the recreated Blood Mage spheres were good, I only realized that the mage has its own model-based effect after checked the sample trigger. Looks cool so far.

EDIT: Here's how mine would look like with proper API and a linked list:

JASS:
library orbiteffect uses xefx

globals
    private real PERIOD=.03125
endglobals

globals
    integer ORBIT_X_AXIS=0
    integer ORBIT_Y_AXIS=1
    integer ORBIT_Z_AXIS=2

    // private global(s)
    private location zLoc=Location(0,0)
endglobals

struct OrbitEffect
    private static  timer             graviton=CreateTimer()

    private thistype next
    private thistype prev

    private unit    centerUnit

    public  real    radius

    private integer rotationAxis
    private real    rotationAngle

    public  xefx    sfx

    private real    az
    private real    azInc

    public  real    offsetX
    public  real    offsetY
    public  real    offsetZ

    static method callback takes nothing returns nothing
        local thistype    this=thistype(0)
        local real        ox
        local real        oy
        local real        oz
        local real        cux
        local real        cuy
        local real        cuz
        // local real        cuf

        if this.next==0 then // the destroy method was called on all orbiteffect instances
            call PauseTimer(thistype.graviton)
            return
        endif

        loop
            set this=this.next
            exitwhen this==0  

            set cux=GetUnitX(this.centerUnit)
            set cuy=GetUnitY(this.centerUnit)
            call MoveLocation(zLoc,cux,cuy)
            set cuz=GetUnitFlyHeight(this.centerUnit)+GetLocationZ(zLoc)

            // set cuf=GetUnitFacing(center_unit)*bj_DEGTORAD

            if     ORBIT_X_AXIS==this.rotationAxis then
                set ox=cuz+this.radius*Sin(this.az)*Cos(this.rotationAngle)+this.offsetZ
                set oy=cuy+this.radius*Sin(this.az)*Sin(this.rotationAngle)+this.offsetY //* Sin(cuf)
                set oz=cux+this.radius*Cos(this.az)                        +this.offsetX //* Cos(cuf)
                set this.sfx.x=oz
                set this.sfx.y=oy
                call MoveLocation(zLoc,oz,oy)
                set this.sfx.z=ox-GetLocationZ(zLoc)

            elseif ORBIT_Y_AXIS==this.rotationAxis then
                set ox=cux+this.radius*Sin(this.az)*Cos(this.rotationAngle)+this.offsetX //* Cos(cuf)
                set oy=cuz+this.radius*Sin(this.az)*Sin(this.rotationAngle)+this.offsetZ
                set oz=cuy+this.radius*Cos(this.az)                        +this.offsetY //* Sin(cuf)
                set this.sfx.x=ox
                set this.sfx.y=oz
                call MoveLocation(zLoc,ox,oz)
                set this.sfx.z=oy-GetLocationZ(zLoc)

            elseif ORBIT_Z_AXIS==this.rotationAxis then
                set ox=cux+this.radius*Sin(this.az)*Cos(this.rotationAngle)+this.offsetX //* Cos(cuf)
                set oy=cuy+this.radius*Sin(this.az)*Sin(this.rotationAngle)+this.offsetY //* Sin(cuf)
                set oz=cuz+this.radius*Cos(this.az)                        +this.offsetZ
                set this.sfx.x=ox
                set this.sfx.y=oy
                call MoveLocation(zLoc,ox,oy)
                set this.sfx.z=oz-GetLocationZ(zLoc)

            endif

            set this.az=this.az+this.azInc
        endloop
    endmethod

    static method addToUnit takes unit u,real radius,integer rotationAxis,real angle,real orbitsPerSecond,string effectPath returns thistype
        local thistype this=thistype.allocate()

        set this.centerUnit   =u
        set this.radius       =radius
        set this.rotationAxis =rotationAxis
        set this.rotationAngle=angle*bj_DEGTORAD

        // get a dummy 
        // and attach the effect to it
        set this.sfx=xefx.create(0,0,0)
        set this.sfx.fxpath=effectPath

        set this.offsetX=0.
        set this.offsetX=0.
        set this.offsetX=0.
        set this.az      =0.

        set this.azInc=((360.0/(1/PERIOD))*orbitsPerSecond)*bj_DEGTORAD

        if thistype(0).next==0 then
            call TimerStart(thistype.graviton,PERIOD,true,function thistype.callback)
        endif
        
        set thistype(0).next.prev=this
        set this.next=thistype(0).next
        set thistype(0).next=this
        set this.prev=thistype(0)

        return this
    endmethod       
        
    method destroy takes nothing returns nothing
        set this.prev.next=this.next
        set this.next.prev=this.prev

        call this.sfx.destroy()

        call this.deallocate()
    endmethod

endstruct
    
endlibrary


Do not use default values in structs because they'll increase your loading time. Declare them in the constructor.
 
> I've checked the test map, and hey bro, xe 0.9 is out!

Oh... Hah =), I hope it doesn't break under xe v0.9 then

> Also, even tho' I like C++ and its syntax, it's still WC3 which has its own naming conventions, I wouldn't mix them, it can be confusing.

Well let's just say that I am using the common.ai's wc3's conventions =), not the Blizzard.j.

About the stack vs the list, well... in my opinion the stack is easier to write and "grasp" and it only adds a single addition in the looop so it's not much of an "optimization" that the list implementation give you.

@Switch33

Well, if 2006- computers could handle gamecache + return bug shenanigans then I don't think that some array lookups and little trigonometry would slow maps down. Tnx for posting though, I guess users would now have two choices then =).

Edit:
@luorax: "Do not use default values in structs because they'll increase your loading time. Declare them in the constructor."

Not sure what you mean by that, but setting a struct variable member in the declaration simply means that in the allocation method it will be assigned that value, so what difference would it make if it's assigned there or in the constructor?
Ex:
JASS:
struct STRUCT
    real VAR1 = 1
    real VAR2 = 2
    real VAR3

    static method create takes nothing returns STRUCT
        local STRUCT s = STRUCT.allocate()
        set s.VAR3 = 3 // no point, could simply let jasshelper do it in the allocate method
        return s
    endmethod
endstruct


// the output allocate method
//Generated allocator of STRUCT
function s__STRUCT__allocate takes nothing returns integer
 local integer this=si__STRUCT_F
    if (this!=0) then
        set si__STRUCT_F=si__STRUCT_V[this]
    else
        set si__STRUCT_I=si__STRUCT_I+1
        set this=si__STRUCT_I
    endif
    if (this&gt;8190) then
        return 0
    endif

   set s__STRUCT_VAR1[this]= 1 // jasshelper saves some typing and &quot;effort&quot; =)
   set s__STRUCT_VAR2[this]= 2
    set si__STRUCT_V[this]=-1
 return this
endfunction
 
If you assign an initial value to a struct member, it'll fill the whole array with that value, whilst if you don't, it'll leave it untouched as "0/0./false". It's minor things, just like that linked list vs stack thing, but it's extra speed for free, it won't break readibility.

It works perfectly with xe 0.9, I've already tested it :) And well, I'd still stick to the common conventions, that's what I get used to. If a moderator finds this snipper useful, I'm almost completely sure that he'll suggest you to change it tho' :)

EDIT:
I feel like this would have almost no lag on crud computers if you just used an single unit that was set periodically and had attachment points using a model like this:http://www.hiveworkshop.com/forums/models-530/defence-matrix-90990/
http://www.hiveworkshop.com/forums/745234-post3.html <-- only attachment version

The reason for using a model with attachment points? It's much less calculation intensive.

That's true, although for people like me this snippet is better, because I like to play with values (like XYZ offset, speed, angle) in real time, in the game. It makes your map much better IMO.
 
if you could add an option to create an orbit affect of a unit that is currently existing around another unit based on its current position, i could think of a trillion ways i could use this for spell effects if you did that...

and i would suggest using T32 for this, but meh, thats just a preference of mine
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • The Helper The Helper:
    News portal has been retired. Main page of site goes to Headline News forum now
  • The Helper The Helper:
    I am working on getting access to the old news portal under a different URL for those that would rather use that for news before we get a different news view.
  • Ghan Ghan:
    Easily done
    +1
  • The Helper The Helper:
    https://www.thehelper.net/pages/news/ is a link to the old news portal - i will integrate it into the interface somewhere when i figure it out
  • Ghan Ghan:
    Need to try something
  • Ghan Ghan:
    Hopefully this won't cause problems.
  • Ghan Ghan:
    Hmm
  • Ghan Ghan:
    I have converted the Headline News forum to an Article type forum. It will now show the top 20 threads with more detail of each thread.
  • Ghan Ghan:
    See how we like that.
  • The Helper The Helper:
    I do not see a way to go past the 1st page of posts on the forum though
  • The Helper The Helper:
    It is OK though for the main page to open up on the forum in the view it was before. As long as the portal has its own URL so it can be viewed that way I do want to try it as a regular forum view for a while
  • Ghan Ghan:
    Yeah I'm not sure what the deal is with the pagination.
  • Ghan Ghan:
    It SHOULD be there so I think it might just be an artifact of having an older style.
  • Ghan Ghan:
    I switched it to a "Standard" article forum. This will show the thread list like normal, but the threads themselves will have the first post set up above the rest of the "comments"
  • The Helper The Helper:
    I don't really get that article forum but I think it is because I have never really seen it used on a multi post thread
  • Ghan Ghan:
    RpNation makes more use of it right now as an example: https://www.rpnation.com/news/
  • The Helper The Helper:
  • The Helper The Helper:
    What do you think Tom?
  • tom_mai78101 tom_mai78101:
    I will have to get used to this.
  • tom_mai78101 tom_mai78101:
    The latest news feed looks good
  • The Helper The Helper:
    I would like to see it again like Ghan had it the first time with pagination though - without the pagination that view will not work but with pagination it just might...
  • The Helper The Helper:
    This drink recipe I have had more than a few times back in the day! Mind Eraser https://www.thehelper.net/threads/cocktail-mind-eraser.194720/

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top