- Joined
- Mar 29, 2016
- Messages
- 688
This library is intended to deal with common types of triggered unit motions so you don't have to write separate periodic functions per spell/system. This library will handle them internally.
I want to get feedback from you guys what things could still be included in this library. Currently, the library supports:
- Linear motion
- Constantly changing speed (but not direction)
- Circular motion (constantly changing direction)
- Spiral-in motion (like due to a spinning black hole) as well as Spiral-out motion
Perhaps there are many things you have in mind that I haven't considered. If so, please tell them. Feedback is appreciated.
Code
I want to get feedback from you guys what things could still be included in this library. Currently, the library supports:
- Linear motion
- Constantly changing speed (but not direction)
- Circular motion (constantly changing direction)
- Spiral-in motion (like due to a spinning black hole) as well as Spiral-out motion
Perhaps there are many things you have in mind that I haven't considered. If so, please tell them. Feedback is appreciated.
Code
JASS:
library UnitMovement /*
*/uses /*
*/ErrorMessage /* https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage/main.j
*/Vector /* http://www.wc3c.net/showthread.php?t=101322
*/TimerUtils /* http://www.wc3c.net/showthread.php?t=87027
*/Table /* https://www.hiveworkshop.com/threads/snippet-new-table.188084/
*/UnitDex /* https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
*/optional AutoFly /* https://www.hiveworkshop.com/threads/snippet-autofly.249422/
*///! novjass
/*
Description:
This system is used to handle if possible, all unit movements, internally. You can use this
to easily create knockback movements, throws, and jumps whether it be uniform or accelerated
motion. By default, this system also takes into consideration the units' acceleration due to
gravity as well as the side effect of gravity, i.e., ground friction. But if you don't need
these additional features, you can just set their values to 0 in the configuration section
below.
*/
|=========|
| CREDITS |
|=========|
Author:
- AGD
Dependencies:
- Anitarf (Vector)
- Vexorian (TimerUtils)
- Bribe (Table)
- TriggerHappy (UnitDex, AutoFly)
|-----|
| API |
|-----|
struct UnitMovement/*
*/real x /* The x-component of the UnitMovement
*/real y /* The y-component of the UnitMovement
*/real z /* The z-component of the UnitMovement
*/real coefficientOfFriction /* The coefficient of friction of the unit
*/readonly real friction /* The magnitude of the ground friction vector acting on the unit
*/readonly static UnitMovement triggerInstance /* Use this to refer to the UnitMovement instance of
the moving unit inside a handler function
*/static method operator [] takes unit whichUnit returns UnitMovement/*
- Reftrieves the UnitMovement instance corresponding to the input unit
*/method destroy takes nothing returns nothing/*
- Destroys a UnitMovement instance
*/method addVelocity takes vector whichVector returns UnitMovement/*
- Adds a velocity to the UnitMovement instance
*/method addAcceleration takes vector whichVector returns UnitMovement/*
- Adds a permanent acceleration to the UnitMovement instance
- You can use this for example to negate the gravity for a specific unit by applying
a vector of the same magnitude with the gravity but in the opposite direction like
for example: call UnitMovement[unit].addAcceleration(0.00, 0.00, UnitVector.GRAVITY)
*/method addAccelerationTimed takes vector whichVector, real duration returns UnitMovement/*
- Adds a timed acceleration to the UnitMovement instance
*/method addTorque takes vector axisOfRotation, real magnitude returns UnitMovement/*
*/method addTorqueEx takes vector axisOfRotation, real radius returns UnitMovement/*
- Adds a rotational acceleration to the UnitMovement instance
- Positive magnitude causes a clockwise rotation when viewing through the axis head-on
while a negative magnitude does the inverse
*/method addTorqueIncrement takes vector axisOfRotation, real magnitude returns UnitMovement/*
- Adds an increment in the torque's magnitude per period
*/method isFreeFalling takes nothing returns boolean/*
- Checks if the unit owner of the UnitMovement instance is in free-fall
*/method registerHandler takes code c returns triggercondition/*
*/method unregisterHandler takes triggercondition tc returns nothing/*
- Registers/Unregisters a code that runs when the unit moves due to a vector applied
to the unit using this system
*///! endnovjass
private keyword Init
struct UnitMovement extends array
/****************************************/
/* SYSTEM CONFIGURATION */
/****************************************/
/*
The number of frames per second in which
the periodic function operates.
Default Value: 32 */
static constant real FPS = 32.00
/*
The 'acceleration' due to gravity.
Default Value: 981.00 */
static constant real GRAVITY = 981.00
/*
The coefficient of the ground friction.
Default Value: 0.70 */
static constant real GROUND_FRICTION = 0.70
/*
The default value for the coefficient of
friction for units. This value is applied
upon UnitMovement creation.
Default Value: 0.60 */
private static constant real DEFAULT_UNIT_FRICTION = 0.60
/*
Determines if newly created units are
automatically registered into the
system. */
private static constant boolean AUTO_REGISTER_UNITS = true
/****************************************/
/* END OF CONFIGURATION */
/****************************************/
/*======================================*/
/* Do not change anything below this */
/* line if you're not so sure on what */
/* you're doing. */
/*======================================*/
private real deltaFriction
private boolean flag
private thistype prev
private thistype next
private real unitFriction
private vector velocity
private vector acceleration
private vector torqueAxis
private vector torqueIncrement
private vector timerForce
private integer codeCount
private trigger handler
readonly static thistype triggerInstance = 0
static constant real FRAME_RATE = 1.00/FPS
private static code periodicCode
private static triggercondition condition
private static timer timer = CreateTimer()
private static group enumerator = CreateGroup()
private static Table table
private static TableArray tableArray
private static method max takes real a, real b returns real
if a > b then
return a
endif
return b
endmethod
static method operator [] takes unit u returns thistype
local thistype this = GetUnitId(u)
if not .flag then
set .flag = true
set .velocity = vector.create(0.00, 0.00, 0.00)
set .acceleration = vector.create(0.00, 0.00, -GRAVITY*FRAME_RATE)
set .torqueAxis = vector.create(0.00, 0.00, 0.00)
set .torqueIncrement = vector.create(0.00, 0.00, 0.00)
set .unitFriction = DEFAULT_UNIT_FRICTION
set .deltaFriction = GRAVITY*GROUND_FRICTION*DEFAULT_UNIT_FRICTION*FRAME_RATE
static if not LIBRARY_AutoFly then
if UnitAddAbility(u, 'Arav') and UnitRemoveAbility(u, 'Arav') then
endif
endif
endif
return this
endmethod
method destroy takes nothing returns nothing
if .flag then
call .velocity.destroy()
call .acceleration.destroy()
call .torqueAxis.destroy()
call .torqueIncrement.destroy()
call DestroyTrigger(.handler)
set .handler = null
set .velocity = 0
set .acceleration = 0
set .torqueAxis = 0
set .torqueIncrement = 0
set .deltaFriction = 0.00
set .unitFriction = 0.00
set .codeCount = 0
set .flag = false
endif
endmethod
method operator unit takes nothing returns unit
return GetUnitById(this)
endmethod
method operator x= takes real x returns nothing
set .velocity.x = x
endmethod
method operator x takes nothing returns real
return .velocity.x
endmethod
method operator y= takes real y returns nothing
set .velocity.y = y
endmethod
method operator y takes nothing returns real
return .velocity.y
endmethod
method operator z= takes real z returns nothing
set .velocity.z = z
endmethod
method operator z takes nothing returns real
return .velocity.z
endmethod
method operator coefficientOfFriction= takes real frictionFactor returns nothing
set .unitFriction = frictionFactor
set .deltaFriction = max(-.acceleration.z*GROUND_FRICTION*frictionFactor, 0.00)
endmethod
method operator coefficientOfFriction takes nothing returns real
return .unitFriction
endmethod
method operator friction takes nothing returns real
return .deltaFriction*FPS
endmethod
private method insertNode takes nothing returns nothing
local thistype last = thistype(0).prev
set .next = 0
set .prev = last
set last.next = this
set thistype(0).prev = this
if .prev == 0 then
call TimerStart(timer, FRAME_RATE, true, periodicCode)
endif
endmethod
private method removeNode takes nothing returns nothing
local thistype prev = .prev
local thistype next = .next
set next.prev = prev
set prev.next = next
if thistype(0).next == 0 then
call PauseTimer(timer)
endif
endmethod
method isFreeFalling takes nothing returns boolean
local vector acceleration = .acceleration
return acceleration.x == 0.00 and acceleration.y == 0.00 and acceleration.z == -GRAVITY*FRAME_RATE
endmethod
method addVelocity takes vector whichVector returns thistype
local boolean freeFalling = .isFreeFalling()
local vector velocity = .velocity
local boolean prevState = velocity.getLength() == 0.00 and freeFalling
call velocity.add(whichVector)
if prevState != (velocity.getLength() == 0.00) then
if velocity.getLength() > 0.00 then
call .insertNode()
elseif freeFalling then
call .removeNode()
endif
endif
return this
endmethod
method addAcceleration takes vector whichVector returns thistype
local vector acceleration = .acceleration
local boolean prevState = .velocity.getLength() == 0.00 and .isFreeFalling()
local boolean freeFalling
call whichVector.scale(FRAME_RATE)
call acceleration.add(whichVector)
call whichVector.scale(FPS)
set .deltaFriction = max(-acceleration.z*GROUND_FRICTION*.unitFriction, 0.00)
set freeFalling = .isFreeFalling()
if prevState != freeFalling then
if not freeFalling then
call .insertNode()
elseif .velocity.getLength() == 0.00 then
call .removeNode()
endif
endif
return this
endmethod
private static method onExpire takes nothing returns nothing
local timer expired = GetExpiredTimer()
local vector timerVector = table[GetHandleId(expired)]
call thistype(GetTimerData(expired)).acceleration.subtract(timerVector)
call timerVector.destroy()
call ReleaseTimer(expired)
set expired = null
endmethod
method addAccelerationTimed takes vector whichVector, real duration returns thistype
local timer newTimer = NewTimerEx(this)
local vector timerVector = vector.create(0.00, 0.00, 0.00)
call timerVector.add(whichVector)
set table[GetHandleId(newTimer)] = timerVector
call .addAcceleration(timerVector)
call TimerStart(newTimer, duration, false, function thistype.onExpire)
set newTimer = null
return this
endmethod
method addTorque takes vector axis, real magnitude returns thistype
local real prevLength = axis.getLength()
call axis.setLength(magnitude*FRAME_RATE)
call .torqueAxis.add(axis)
call axis.setLength(prevLength)
return this
endmethod
method addTorqueEx takes vector axis, real radius returns thistype
local real speed = .velocity.getLength()
return .addTorque(axis, speed*speed/radius)
endmethod
method addTorqueIncrement takes vector axis, real magnitude returns thistype
local real prevLength = axis.getLength()
call axis.setLength(magnitude*FRAME_RATE)
call .torqueIncrement.add(axis)
call axis.setLength(prevLength)
return this
endmethod
method registerHandler takes code c returns triggercondition
debug call ThrowError(c == null, "UnitVector", "registerHandler", "thistype", this, "Attemped to register a null code")
if .codeCount == 0 then
set .handler = CreateTrigger()
endif
set .codeCount = .codeCount + 1
set condition = TriggerAddCondition(.handler, Filter(c))
set tableArray[this][GetHandleId(condition)] = 1
return condition
endmethod
method unregisterHandler takes triggercondition tc returns nothing
debug call ThrowError(tc == null, "UnitVector", "unregisterHandler", "thistype", this, "Attemped to unregister a null triggercondition")
debug call ThrowError(not tableArray[this].has(GetHandleId(tc)), "UnitVector", "unregisterHandler", "thistype", this, "Attemped to unregister a triggercondition twice")
set .codeCount = .codeCount - 1
if .codeCount == 0 then
call tableArray[this].flush()
call DestroyTrigger(.handler)
set .handler = null
else
call tableArray[this].remove(GetHandleId(tc))
call TriggerRemoveCondition(.handler, tc)
endif
endmethod
private static method periodic takes nothing returns nothing
local thistype this = thistype(0).next
local thistype prev
local thistype next
local unit u
local real xVel
local real yVel
local real zVel
local real unitZ
local real friction
local vector velocity
local vector acceleration
local vector torqueAxis
local vector torqueIncrement
loop
exitwhen this == 0
set u = GetUnitById(this)
set velocity = .velocity
set acceleration = .acceleration
set unitZ = GetUnitFlyHeight(u)
if acceleration.x != 0.00 or acceleration.y != 0.00 or acceleration.z != 0.00 then
call velocity.add(acceleration)
set .deltaFriction = max(-acceleration.z*GROUND_FRICTION*.unitFriction, 0.00)
endif
set friction = .deltaFriction
if unitZ <= GetUnitDefaultFlyHeight(u) + 0.10 then
call SetUnitPropWindow(u, GetUnitDefaultPropWindow(u))
set zVel = velocity.z
if zVel < 0.00 then
set velocity.z = 0.00
endif
if friction > 0.00 then
set xVel = velocity.x
set yVel = velocity.y
if friction*friction > xVel*xVel + yVel*yVel then
set velocity.x = 0.00
set velocity.y = 0.00
if acceleration.x == 0.00 and acceleration.y == 0.00 and acceleration.z <= 0.00 and zVel <= 0.00 then
set prev = .prev
set next = .next
set next.prev = prev
set prev.next = next
endif
else
call velocity.setLength(max(velocity.getLength() - friction, 0.00))
endif
endif
else
call SetUnitPropWindow(u, 0.00)
endif
if velocity.x != 0.00 or velocity.y != 0.00 or velocity.z != 0.00 then
set torqueAxis = .torqueAxis
set torqueIncrement = .torqueIncrement
if torqueIncrement.x != 0.00 or torqueIncrement.y != 0.00 or torqueIncrement.z != 0 then
call torqueAxis.add(torqueIncrement)
endif
if torqueAxis.x != 0.00 or torqueAxis.y != 0.00 or torqueAxis.z != 0 then
call velocity.rotate(torqueAxis, torqueAxis.getLength()/velocity.getLength())
endif
endif
call SetUnitX(u, GetUnitX(u) + velocity.x*FRAME_RATE)
call SetUnitY(u, GetUnitY(u) + velocity.y*FRAME_RATE)
call SetUnitFlyHeight(u, unitZ + velocity.z*FRAME_RATE, 0.00)
set triggerInstance = this
call TriggerEvaluate(handler)
set triggerInstance = 0
set this = .next
endloop
set u = null
endmethod
static if thistype.AUTO_REGISTER_UNITS then
private static method onIndex takes nothing returns nothing
local thistype this = thistype[GetIndexedUnit()]
endmethod
private static method onDeindex takes nothing returns nothing
call thistype[GetIndexedUnit()].destroy()
endmethod
endif
private static method init takes nothing returns nothing
static if thistype.AUTO_REGISTER_UNITS then
local code onIndex = function thistype.onIndex
local code onDeindex = function thistype.onDeindex
call OnUnitIndex(onIndex)
call OnUnitDeindex(onDeindex)
endif
set periodicCode = function thistype.periodic
set tableArray = TableArray[0x2000]
set table = tableArray[0]
endmethod
implement Init
endstruct
private module Init
private static method onInit takes nothing returns nothing
call init()
endmethod
endmodule
endlibrary
Last edited: