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: 414
  • z_axis_of_roation.png
    z_axis_of_roation.png
    95.5 KB · Views: 438

luorax

Invasion in Duskwood
Reaction score
67
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.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
> 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
 

luorax

Invasion in Duskwood
Reaction score
67
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.
 

GFreak45

I didnt slap you, i high 5'd your face.
Reaction score
130
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.
  • Varine Varine:
    I ordered like five blocks for 15 dollars. They're just little aluminum blocks with holes drilled into them
  • Varine Varine:
    They are pretty much disposable. I have shitty nozzles though, and I don't think these were designed for how hot I've run them
  • Varine Varine:
    I tried to extract it but the thing is pretty stuck. Idk what else I can use this for
  • Varine Varine:
    I'll throw it into my scrap stuff box, I'm sure can be used for something
  • Varine Varine:
    I have spare parts for like, everything BUT that block lol. Oh well, I'll print this shit next week I guess. Hopefully it fits
  • Varine Varine:
    I see that, despite your insistence to the contrary, we are becoming a recipe website
  • Varine Varine:
    Which is unique I guess.
  • The Helper The Helper:
    Actually I was just playing with having some kind of mention of the food forum and recipes on the main page to test and see if it would engage some of those people to post something. It is just weird to get so much traffic and no engagement
  • The Helper The Helper:
    So what it really is me trying to implement some kind of better site navigation not change the whole theme of the site
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top