• 🏆 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!

Simple Angular Jump System v1.04

  • Like
Reactions: xorkatoss and Galdr
Hiho,

this is a simple jumping system which allows the user to let units jump with variable angle, speed and to a certain point.

Special thanks goes to peq who analysed the system and gave me tips on how to improve the parabel function.

Note this system requires:
- TimerUtils

Here is the systems code:
JASS:
/**
 * Simple Angular Jump System - by Robbepop
 *
 * Uses the following libraries:
 *     - Unit Indexer : http://www.hiveworkshop.com/forums/jass-resources-412/system-unit-indexer-172090/
 *     - Timer Utils : http://www.wc3c.net/showthread.php?t=101322
 * 
 * This system can be used to let units jump to a certain point with variable speed and a starting angle.
 * The speed is adjusted to the wc3-Engine and so values of 1 to 2000 are normal.
 * The system is similar to a throwing system so only angles within the bounds of [0, 90) are valid.
 * You should use the API function "UnitJumpToPoint()" to use this system.
 * 
 * Special thanks goes to peq who analysed the system and gave me tips on how to improve the parabel function.
 * 
 * I hope I documented the system well enough to give a slight understanding of how it works
 * it should be even usable by GUI users because it has got only one API function which should be okay.
 * 
 * If there are any questions about it please leave me a comment in hiveworkshop.com, [email protected] or at goh-rpg.de.to.
 * And please give me credits if you use this simple jump system in your map.
 * 
 * API:
 * function UnitJumpToPosition takes unit u, real a, real s, real x, real y returns nothing
 * function UnitJumpToPositionLoc takes unit u, real a, real s, location l returns nothing
 * 
 * Takes the unit 'u' to jump with a starting angle of 'a' and a speed of 's' to the coordinates of ('x'/'y').
 * A possbile usage example would be the following:
 *     call UnitJumpToPoint(udg_myUnit, 45, 600, 0, 0)
 * Which would make the unit udg_myUnit jump to point (0,0) with 600 speed at a starting angle of 45°.
 * 
 * function IsUnitJumping takes unit u returns boolean
 *
 * Can determine if a unit is currently jumping.
 * A possible usage example would be the following:
 *     if IsUnitJumping(udg_myUnit) then
 *         call DoNothing()
 *     endif
 * 
 * The system does not check for pathability so you should do this by yourself if you need it in some cases!
 *
 * The functions AngleBetweenCoords and DistanceBetweenCoords are helper functions.
 * You may use them in your map's header (or utils) file if you want to but note that you then have to remove them from this script.
 */
library SimpleAngularJump requires TimerUtils, UnitIndexer

private function AngleBetweenCoords takes real ax, real ay, real bx, real by returns real
    return Atan2(by - ay, bx - ax)
endfunction

private function DistanceBetweenCoords takes real ax, real ay, real bx, real by returns real
    local real dx = bx - ax
    local real dy = by - ay
    return SquareRoot(dx * dx + dy * dy)
endfunction

struct SimpleAngularJump extends array
    // The interval of the periodic timer.
    private static constant real INTERVAL = 0.03125

    // Can be adjusted to set the global expansion of height.
    private static constant real HEIGHT_EXPANSION = 90.

    private unit jumper
    private real startAngle
    private real destAngle
    private real speed
    private real curDist
    private real maxDist
    private real moveX
    private real moveY
    private real endX
    private real endY
    private real oldFlyHeight
    private boolean active

    public method isJumping takes nothing returns boolean
        return .active
    endmethod

    private method destroy takes nothing returns nothing
        call SetUnitFlyHeight(.jumper, .oldFlyHeight, 0.) // just for debug purpose to make the unit land properly always
        call SetUnitX(.jumper, .endX)
        call SetUnitY(.jumper, .endY)
        set .active = false
        set .jumper = null
    endmethod

    private static method periodic takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local thistype this = GetTimerData(t)
        local real percentageDist
        local real curAngle

        set .curDist = .curDist + RAbsBJ(.speed)
        set percentageDist = .curDist / .maxDist
        set curAngle = .startAngle + percentageDist * .destAngle * 2.
        
        call SetUnitX(.jumper, GetUnitX(.jumper) + .moveX)
        call SetUnitY(.jumper, GetUnitY(.jumper) + .moveY)
        call SetUnitFlyHeight(.jumper, GetUnitFlyHeight(.jumper) + thistype.HEIGHT_EXPANSION * curAngle / 60., 0.)

        if .curDist >= .maxDist then
            call ReleaseTimer(t)
            call .destroy()
        endif

        set t = null
    endmethod

    public static method create takes unit u, real a, real s, real x, real y returns thistype
        local timer t
        local thistype this = GetUnitUserData(u)
        local real angle
        local real posX
        local real posY

        // Case: Unit was not already jumping.
        if not .active then
            set .jumper = u
            set .startAngle = a
            set .destAngle = - .startAngle
            set .oldFlyHeight = GetUnitFlyHeight(u)

            set t = NewTimer()
            call SetTimerData(t, this)
            call TimerStart(t, thistype.INTERVAL, true, function thistype.periodic)
        endif

        set .speed = s * thistype.INTERVAL
        set posX = GetUnitX(u)
        set posY = GetUnitY(u)
        set angle = AngleBetweenCoords(posX, posY, x, y)
        set .moveX = .speed * Cos(angle)
        set .moveY = .speed * Sin(angle)
        set .curDist = 0.
        set .maxDist = DistanceBetweenCoords(posX, posY, x, y)
        if .speed > 0 then
            set .endX = GetUnitX(u) + .maxDist * Cos(angle)
            set .endY = GetUnitY(u) + .maxDist * Sin(angle)
        else
            set .endX = GetUnitX(u) - .maxDist * Cos(angle)
            set .endY = GetUnitY(u) - .maxDist * Sin(angle)
        endif
        set .active = true

        return this
    endmethod
endstruct

function IsUnitJumping takes unit u returns boolean
    local SimpleAngularJump data = GetUnitUserData(u)
    return data.isJumping()
endfunction

function UnitJumpToPosition takes unit u, real a, real s, real x, real y returns nothing
    if a < 0 or a >= 90 then
        debug call BJDebugMsg("ERROR: Angle (a) parameter is not within the bounds of [0, 90)")
        return
    elseif s == 0 then
        debug call BJDebugMsg("ERROR: Speed (s) parameter is 0!")
        return
    elseif u == null then
        debug call BJDebugMsg("ERROR: Unit (u) must be a unit!")
        return
    elseif IsUnitJumping(u) then
        debug call BJDebugMsg("ERROR: Unit (u) can not jump while jumping!")
        return
    endif
    if UnitAddAbility(u, 'Arav') then
        call UnitRemoveAbility(u, 'Arav')
    endif
    call SimpleAngularJump.create(u, a, s, x, y)
endfunction

function UnitJumpToPositionLoc takes unit u, real a, real s, location l returns nothing
    call UnitJumpToPosition(u, a, s, GetLocationX(l), GetLocationY(l))
endfunction

endlibrary

And here is how you use it:
  • Example
    • Events
      • Unit - A unit starts the effect of an Ability
    • Conditions
      • (Ability being cast) Equals (==) Jump
    • Actions
      • Custom script: call UnitJumpToPosition(GetTriggerUnit(), 45., 900., GetSpellTargetX(), GetSpellTargetY())
I hope even some GUI users will have a use of this as this system is really simple to use. ;-)

Robbepop


simpleangularjumpsystem.jpg




v1.04
  • Added UnitIndexer to the requirements.
  • Removed an unused check for .active.
    Changes are only made in the code posted here - the downloadable map is still not updated, however will follow as soon as I've time to do so.

v1.03
  • Once again a unit no longer is able to jump while jumping ... (It's not a bug, it's a feature!)
  • Extended the system with another library "UnitIndexer" to keep further simplicity of this system ...

v1.02
  • Jumps on the same unit can stack now.
  • Extended the API with a new function IsUnitJumping(unit u).
  • It is now possible to jump with a negative speed.
  • A jumping unit will no longer lose its base fly height after jumping.
  • A unit will now land exactly at the target position after landing.

v1.01
  • Changed the Jass test-trigger to GUI.
  • Remove forth-back converting of angles.
  • Renamed UnitJumpToPoint to UnitJumpToPosition.
  • Added a new API function UnitJumpToPositionLoc.
  • Inlined a few parts of the code.

v1.0
  • Initial Release


And sorry for the horrible preview pic - my paint skills are the worst ... :-/

Keywords:
jump, simple, vjass, system
Contents

Simple Angular Jump System (Map)

Reviews
22:51, 22nd Aug 2012 Magtheridon96: Approved. The code is decent and the system is bug-free according to my tests. Here's one nice improvement though: set t = NewTimer() call SetTimerData(t, this) call TimerStart(t, thistype.INTERVAL...

Moderator

M

Moderator

22:51, 22nd Aug 2012
Magtheridon96:

Approved.
The code is decent and the system is bug-free according to my tests.

Here's one nice improvement though:
JASS:
set t = NewTimer()
call SetTimerData(t, this)
call TimerStart(t, thistype.INTERVAL, true, function thistype.periodic)
->
call TimerStart(NewTimerEx(this), thistype.INTERVAL, true, function thistype.periodic)


And this way, you don't need to declare a local timer t and null it at the end ^_^
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
Learn to use Radians. Now you are converting back and forth.

Also simple things as
set x = GetUnitX(.jumper) + .moveX
call SetUnitX(.jumper, x)

----->
call SetUnitX(.jumper, GetUnitX(.jumper) + .moveX)

Why not remove the destroy method and just put those things where you call .destroy?

Another thing, why is it called "jumptoPoint" when it takes coordinates? ^^

Please let it only run on one timer like T32 (as an example)

Short, neat, useful yet some improvements can be done.

EDIT:

JASS:
    if a < 0 or a >= 90 then
        debug call BJDebugMsg("ERROR: Angle (a) parameter is not within the bounds of [0, 90)!")
        return
    elseif s <= 0 then
        debug call BJDebugMsg("ERROR: Speed (s) parameter is 0 or smaller than 0!")
        return
    elseif u == null then
        debug call BJDebugMsg("ERROR: Unit (u) must be a unit!")
        return
    endif

Debug the whole if ^^

EDIT2:

call SetUnitFlyHeight(.jumper, GetUnitFlyHeight(.jumper) + thistype.HEIGHT_EXPANSION * curAngle / 60., 9999.)

Use 0. instead of 9999. at rate - 0. is instant as well
 
Level 11
Joined
Mar 6, 2008
Messages
898
Hiho,

thank your for your comment I did some improvements to my system based on some of your points.
However, I think that the code is more clear if I leave the destroy method.
And putting the whole if in a debug-field would cause missbehaviour of the system if used incorrectly.
For example using a speed of 0 would cause an endless loop and as this system is supposed to be used to GUI'lers it should be stable.

Robbepop
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
I think you should inline the destroy method, GUIers never read vJASS code anyway (99.99999999999%)

there are more that you can inline, like the angle/distance functions, curangle can be merged into the setunitheight call

True about the if but I'd debug it out anyway, if they are stupid enough to put 0. speed then let them suffer ^^
 
Level 3
Joined
Aug 25, 2012
Messages
30
I have a little problem, i copied the script and all needed snippets, but when i clicked save, editor throws for about 150 errors in those scripts (for example 'excepting end of line', etc.)

Please help, i really wanna to use this system
 
Level 3
Joined
Apr 21, 2012
Messages
33
I am going to use this, thanks.

Also, I read all the vJass code. Because I always hope that someday I will become skillful enough to write down all code and system that I want by myself. :)

EDIT: Wait, while testing I notice that I can jump into the border of the map that normally unit can't walk in. Does this supposed to happen?
 
Top