System Movement Instances

Darius34

New Member
Reaction score
30
Movement Instances v1.0

Note: this system is far from done, so it's a little lightweight at the moment - I plan to add lots more functionality in future, including stuff like collisions, parabolic paths, etc. I'm submitting this now because I intend to use the system in a public map, so.

Requirements:
-vJass preprocessor (NewGen pack).
-JASS knowledge. This isn't very useful in GUI.

Implementation:
-Copy the code/trigger into a blank text trigger called Movement Instances. Keep the readme for reference, too, in a disabled trigger.

What is it for?
Moving units, over distances or to specific points. The concept's not new, but it also uses the pseudo-OOP capabilities of vJass to implement some (hopefully) friendly syntax, and accessible options. It's also fairly flexible, in that it's usable in most situations without a user having to calculate coordinates and stuff. Finally, it's good for simplifying projectile spells (Elune's Arrow from DotA, or some kind of triggered Shockwave) and jump spells.

JASS:
library MovementInstances initializer Init

//***************************************************************************
//* Movement Instances v1.0, by Darius34
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* <a href="http://www.thehelper.net/forums/showthread.php?p=878606" class="link link--internal">http://www.thehelper.net/forums/showthread.php?p=878606</a>
//*
//* Some utility code for moving units in spells, over distances or to
//* specific points. Utilises the pseudo-OOP capabilities of vJass with
//* struct members to provide a lots of accessible, configurable options.
//*
//* Usage/Syntax
//* ¯¯¯¯¯¯¯¯¯¯¯¯
//* Refer to the list of properties for details.
//*
//*   - User-defined functions for the periodic and end actions must carry
//*     the &quot;MovementPeriodic.&quot; and &quot;MovementEnd.&quot; prefixes, respectively.
//*     The former takes a unit and an integer, in that order, while the
//*     latter takes only a unit.
//*
//* Credits
//* ¯¯¯¯¯¯¯
//* - The people who made vJass possible, in all its pseudo-OOP glory.
//*
//***************************************************************************

globals
    // Configuration
    private constant real TIMER_PERIOD = .03 // Global timer period for this module.
    private constant boolean SHOW_ERROR_MESSAGES = true // Toggles error message displays on and off.
endglobals

// System starts here. Don&#039;t modify stuff below if you&#039;re not sure how to.

function interface MovementPeriodic takes unit subject, integer mvinstance returns nothing
function interface MovementEnd takes unit subject returns nothing

globals
    private timer Timer = null
    private integer Index = 0    
    private moveinstance array MoveInstance
endglobals

struct moveinstance
    unit subject
    real speed

    real dist = 0.
    real angle = 0. // Radians
    real targetx = 0.
    real targety = 0.
    boolean targetCoords = false
    
    unit target = null
    boolean dynamicMovement = false // NYI

    boolean interruptOrders = false
    boolean keepUnitFacing = false
    boolean killSubjectOnEnd = false
    boolean removeSubjectOnEnd = false

    MovementPeriodic periodicFunction = 0
    MovementEnd endFunction = 0
    
    boolean InstanceTerminated = false
    real originaldist
    integer iterations = 0
    
    static method create takes unit subject, real speed returns moveinstance
        local moveinstance m = moveinstance.allocate()
        set m.subject = subject
        set m.speed = speed * TIMER_PERIOD
        return m
    endmethod
    
    method onDestroy takes nothing returns nothing
        if .killSubjectOnEnd then
            call KillUnit(.subject)
        elseif .removeSubjectOnEnd then
            call RemoveUnit(.subject)
        endif
    endmethod
    
    method start takes nothing returns nothing
        local real dx
        local real dy
        set MoveInstance[Index] = this
        set Index = Index + 1

        if .subject == null and SHOW_ERROR_MESSAGES then
            call BJDebugMsg(&quot;Movement Instances - Error: Invalid subject specified.&quot;)
        endif
        if .targetCoords then
            if not .dynamicMovement and .target != null then
                set .targetx = GetUnitX(.target)
                set .targety = GetUnitY(.target)
            endif
            set dx = .targetx - GetUnitX(.subject)
            set dy = .targety - GetUnitY(.subject)
            set .angle = Atan2(dy, dx)
            set .dist = SquareRoot(dx * dx + dy * dy)
        endif
        set .originaldist = .dist
        if .dynamicMovement then
            if .target == null and SHOW_ERROR_MESSAGES then
                call BJDebugMsg(&quot;Movement Instances - Error: Invalid target specified for dynamic movement.&quot;)
            endif
        endif
        set .iterations = R2I(.originaldist/.speed)
    endmethod
endstruct

private function PeriodicMovement takes nothing returns nothing
    local integer a = 0
    local real dx
    local real dy
    local moveinstance m
    local real x
    local real y
    loop
        exitwhen a &gt;= Index
        set m = MoveInstance[a]
        
        set x = GetUnitX(m.subject)
        set y = GetUnitY(m.subject)
        if m.interruptOrders then
            call SetUnitPosition(m.subject, x + m.speed * Cos(m.angle), y + m.speed * Sin(m.angle))
        else
            call SetUnitX(m.subject, x + m.speed * Cos(m.angle))
            call SetUnitY(m.subject, y + m.speed * Sin(m.angle))
        endif
        if m.keepUnitFacing then
            call SetUnitFacing(m.subject, m.angle * bj_RADTODEG)
        endif

        call m.periodicFunction.execute(m.subject, m)

        set m.iterations = m.iterations - 1
        if (m.dynamicMovement and m.dist &lt;= m.speed) or (not m.dynamicMovement and m.iterations &lt;= 0) then
            call m.endFunction.execute(m.subject)
            set m.InstanceTerminated = true
        endif

        if m.InstanceTerminated then
            call m.destroy()
            set MoveInstance[a] = MoveInstance[Index-1]
            set MoveInstance[Index-1] = 0
            set Index = Index - 1
        endif
        set a = a + 1
    endloop
endfunction

private function Init takes nothing returns nothing
    set Timer = CreateTimer()
    call TimerStart(Timer, TIMER_PERIOD, true, function PeriodicMovement)
endfunction
endlibrary


Readme:

JASS:
//***************************************************************************
//* Movement Instances v1.0, by Darius34
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//***************************************************************************

Movement Instances is a utility for moving units in any way, be it over distances or to specific points.
All this is implemented with a struct that is declared and later started with a method call: think of it
as representative of the actual &#039;happening&#039; of movement, the properties and workings of which can be
modified and controlled, and on the fly too, if desired.

To begin using the system, a movement instance has to be declared, like such:

private function Init takes nothing returns nothing
    local moveinstance m = moveinstance.create(subject, 522.)
    ...
endfunction

The method .create() takes two arguments, the subject unit being moved and the speed at which this will happen,
in distance units per second. These are necessary arguments in any situation.

Properties are then assigned, via the struct members of the moveinstance struct.

private function Init takes nothing returns nothing
    local moveinstance m = moveinstance.create(subject, 522.)
    set m.dist = 900.
    set m.angle = GetUnitFacing(GetTriggerUnit()) * bj_DEGTORAD
    ...
endfunction

Here&#039;s a list of the properties available. Properties are initialised with their default values.
Hence, properties can often be omitted, based on frequency of use. Only necessary properties need be specified in
general, in other words.

    real dist = 0.
The distance to be moved by the unit, in total. Is disregarded if .targetCoords is true.

    real angle = 0. // Taken in radians
The angle at which the unit will be moved. Is disregarded if .targetCoords is true.

    boolean targetCoords = false
This flag controls if the system moves a unit to destination coordinates, or just a distance at an angle.

    real targetx = 0.
    real targety = 0.
The coordinates to which the unit will be moved, if .targetCoords is true.
    
    boolean dynamicMovement = false // NYI
Controls if the subject unit moves dynamically towards another unit, like a projectile.

    unit target = null
The target the subject unit will track dynamically, if .dynamicMovement is true. If .dynamicMovement is false and .targetCoords is true,
the coordinates of this unit will be those moved to, as if specified via .targetx/.targety.

    boolean interruptOrders = false
Controls whether SetUnitPosition() or SetUnitX()/SetUnitY() are used to move the subject unit. SetUnitPosition() is slower but does
more checks on pathability and such, and so might be useful for the sake of omitting a manual check.

    boolean keepUnitFacing = false
Controls whether the facing of the target unit is kept throughout.

    boolean killSubjectOnEnd = false
    boolean removeSubjectOnEnd = false
Controls whether the subject unit is disposed of upon the movement conluding, and how.

    MovementPeriodic periodicFunction = 0
    MovementEnd endFunction = 0
User-defined functions that can be specified, executed during the movement and after it, respectively. Functions must be prefixed with their
types. Also, the latter function takes a unit (the subject) and an integer (the instance of movement), while the latter takes a unit.
An example to illustrate:

private function Actions takes unit subject, integer mvinstance returns nothing
    ...
endfunction

private function FinalActions takes unit subject returns nothing
    ...
endfunction

private function Init takes nothing returns nothing
    ...
    set m.periodicFunction = MovementPeriodic.Actions
    set m.endFunction = MovementEnd.FinalActions
    ...
endfunction

Note: MovementPeriodic functions provide the instance of movement itself as an argument, which allows control over all the properties on
the fly. This has its advantages and disadvantages; it&#039;s possible for the end-user to break the working of the function. Be warned, and
change properties cautiously.

Finally, the .start() method begins the movement. It doesn&#039;t take any arguments.

private function Init takes nothing returns nothing
    local moveinstance m = moveinstance.create(subject, 522.)
    set m.dist = 900.
    set m.angle = GetUnitFacing(GetTriggerUnit()) * bj_DEGTORAD
    call m.start()
endfunction

It destroys the struct instance automatically, so a .destroy() later is not required. Once called, the rest is left to the system,
and no more cleanup is required by the end-user.

Examples of usage and instructions can be found in the readme.

Comments are appreciated.

Changelog

v1.0

- Initial release.
 

saw792

Is known to say things. That is all.
Reaction score
280
Hmm add the 'debug' prefix before your BJDebugMsg's. Random messages coming up in game would not look good.
 

Darius34

New Member
Reaction score
30
Added a boolean flag for error message displays, true by default. (I always forget to change that Debug Mode flag before saving final versions.)

Also, a hotfix: I left out a function argument somewhere.
 

Gwypaas

hook DoNothing MakeGUIUsersCrash
Reaction score
50
Instead of doing this
JASS:
 call SetUnitX(m.subject, x + m.speed * Cos(m.angle))

store the m.speed * Cos(m.angle) value in a variable so you don't need to calulate it every time it runs. (Do the same for Sin.)
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • WildTurkey WildTurkey:
    is there a stephen green in the house?
    +1
  • The Helper The Helper:
    What is up WildTurkey?
  • The Helper The Helper:
    Looks like Google fixed whatever mistake that made the recipes on the site go crazy and we are no longer trending towards a recipe site lol - I don't care though because it motivated me to spend alot of time on the site improving it and at least now the content people are looking at is not stupid and embarrassing like it was when I first got back into this like 5 years ago.
  • The Helper The Helper:
    Plus - I have a pretty bad ass recipe collection now! That section of the site is 10 thousand times better than it was before
  • The Helper The Helper:
    We now have a web designer at my job. A legit talented professional! I am going to get him to redesign the site theme. It is time.
  • Varine Varine:
    I got one more day of community service and then I'm free from this nonsense! I polished a cop car today for a funeral or something I guess
  • Varine Varine:
    They also were digging threw old shit at the sheriff's office and I tried to get them to give me the old electronic stuff, but they said no. They can't give it to people because they might use it to impersonate a cop or break into their network or some shit? idk but it was a shame to see them take a whole bunch of radios and shit to get shredded and landfilled
  • The Helper The Helper:
    whatever at least you are free
  • Monovertex Monovertex:
    How are you all? :D
    +1
  • Ghan Ghan:
    Howdy
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top