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

knockback

Status
Not open for further replies.

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,540
Knockback systems are fairly simple at their core.

First you determine which unit you're knocking back, then you determine the direction it needs to be pushed, then you turn on a timer/loop that will periodically move the unit in that direction. After X seconds you end the knockback for that unit. That's for the most basic of designs of course.

So to get the knockback angle you can use a simple function in GUI:
  • Set Variable Angle = (Angle from SourcePoint to TargetPoint)
An example trigger that could be the start of a Storm Bolt ability that knocks the enemy back:
  • Events
    • Unit - A unit Starts the effect of an ability
  • Conditions
    • (Ability being cast) Equal to Storm Bolt
  • Actions
    • Set Variable SourcePoint = (Position of (Triggering unit))
    • Set Variable TargetPoint = (Position of (Target unit of ability being cast))
    • Set Variable Angle = (Angle from SourcePoint to TargetPoint)
This Angle would push the Target away from the Source.

Now you'd obviously want to store this Angle in a Hashtable or some kind of Array so that it's linked to the unit being knocked back, otherwise your Knockback won't work for multiple units. Then from there you'd have to handle the Movement and what not.


So with that in mind, here's a very basic knockback system I created that does the bare minimum. It REQUIRES a Unit Indexer:
First we determine how often we want our Knockback Timer to run. 0.03 is a great value for smooth knockbacks.
  • Knockback Setup
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- This is how often the Knockback timer will run. A smaller value can look better but can also hurt performance: --------
      • Set VariableSet KB_Interval = 0.03
This trigger is designed to knockback a unit when it gets attacked. You could customize the Event/Actions to work for whatever scenario you want, but I'm going with this one because it's simple and easy to test:
  • Run Knockback
    • Events
      • Unit - A unit Is attacked
    • Conditions
    • Actions
      • -------- Configure the Knockback Source (causes the Knockback) and the Knockback Target (gets Knocked back): --------
      • Set VariableSet KB_Source = (Attacking unit)
      • Set VariableSet KB_Target = (Attacked unit)
      • Set VariableSet CV = (Custom value of KB_Target)
      • -------- --------
      • -------- Configure your Knockback: --------
      • Set VariableSet KB_Source_Point = (Position of KB_Source)
      • Set VariableSet KB_Target_Point = (Position of KB_Target)
      • Set VariableSet KB_Duration[CV] = 0.50
      • Set VariableSet KB_Distance[CV] = 200.00
      • -------- --------
      • -------- Calculate the Angle: --------
      • Set VariableSet KB_Angle[CV] = (Angle from KB_Source_Point to KB_Target_Point)
      • Custom script: call RemoveLocation (udg_KB_Source_Point)
      • Custom script: call RemoveLocation (udg_KB_Target_Point)
      • -------- --------
      • -------- Calculate the Speed: --------
      • Set VariableSet KB_Time = (KB_Duration[CV] / KB_Interval)
      • Set VariableSet KB_Speed[CV] = (KB_Distance[CV] / KB_Time)
      • -------- --------
      • -------- Add Target to Knockback_Group and turn on Knockback_Timer (If necessary) --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (KB_Target is in KB_Group.) Equal to False
        • Then - Actions
          • Unit Group - Add KB_Target to KB_Group
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Knockback Timer <gen> is on) Equal to False
        • Then - Actions
          • Trigger - Turn on Knockback Timer <gen>
          • Countdown Timer - Start KB_Timer as a Repeating timer that will expire in KB_Interval seconds
        • Else - Actions
This is our timer trigger that does the actual Knockback effect. We're looping through all of our units that are currently being Knockedback (KB_Group), reducing the Duration of their Knockback, and checking if the Duration > 0.00. If it's > than 0.00 then we keep pushing them, otherwise we know the Knockback has ended and we remove them from the KB_Group.
  • Knockback Timer
    • Events
      • Time - KB_Timer expires
    • Conditions
    • Actions
      • Unit Group - Pick every unit in KB_Group and do (Actions)
        • Loop - Actions
          • Set VariableSet CV = (Custom value of (Picked unit))
          • Set VariableSet KB_Duration[CV] = (KB_Duration[CV] - KB_Interval)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • KB_Duration[CV] Greater than 0.00
            • Then - Actions
              • -------- Move the Target --------
              • Set VariableSet KB_Target_Point = (Position of (Picked unit))
              • Unit - Move (Picked unit) instantly to (KB_Target_Point offset by KB_Speed[CV] towards KB_Angle[CV] degrees.)
              • Custom script: call RemoveLocation (udg_KB_Target_Point)
            • Else - Actions
              • -------- Knockback Over --------
              • Unit Group - Remove (Picked unit) from KB_Group.
              • -------- --------
              • -------- Turn Off Trigger (If necessary) --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Number of units in KB_Group) Equal to 0
                • Then - Actions
                  • Countdown Timer - Pause KB_Timer
                  • Trigger - Turn off (This trigger)
                • Else - Actions

Now note that Version 1 isn't much of a system in the sense that it handles things nicely for you. So here's how you can adapt these triggers to make it easier on the user to issue Knockbacks for any kind of scenario. Here's Version 2:
  • Knockback Example
    • Events
      • Unit - A unit Is attacked
    • Conditions
    • Actions
      • -------- Configure your Knockback: --------
      • Set VariableSet KB_Source = (Attacking unit)
      • Set VariableSet KB_Target = (Attacked unit)
      • Set VariableSet KB_Source_Point = (Position of KB_Source)
      • Set VariableSet KB_Target_Point = (Position of KB_Target)
      • Set VariableSet KB_Duration[0] = 0.50
      • Set VariableSet KB_Distance[0] = 200.00
      • Set VariableSet KB_Angle[0] = (Angle from KB_Source_Point to KB_Target_Point)
      • Custom script: call RemoveLocation (udg_KB_Source_Point)
      • Custom script: call RemoveLocation (udg_KB_Target_Point)
      • -------- Run this Trigger when you're finished configuration: --------
      • Trigger - Run Add Knockback To System <gen> (ignoring conditions)
  • Add Knockback To System
    • Events
    • Conditions
    • Actions
      • -------- CV is used for taking advantage of the Unit Indexer. It allows us to save Variables directly to a unit: --------
      • Set VariableSet CV = (Custom value of KB_Target)
      • -------- --------
      • -------- Store the Distance/Duration to our Target: --------
      • Set VariableSet KB_Distance[CV] = KB_Distance[0]
      • Set VariableSet KB_Duration[CV] = KB_Duration[0]
      • -------- --------
      • -------- Store the Angle to our Target: --------
      • Set VariableSet KB_Angle[CV] = KB_Angle[0]
      • -------- --------
      • -------- Calculate the Speed and store it to our Target: --------
      • Set VariableSet KB_Time = (KB_Duration[CV] / KB_Interval)
      • Set VariableSet KB_Speed[CV] = (KB_Distance[CV] / KB_Time)
      • -------- --------
      • -------- Add Target to Knockback_Group and turn on Knockback_Timer (If necessary) --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (KB_Target is in KB_Group.) Equal to False
        • Then - Actions
          • Unit Group - Add KB_Target to KB_Group
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Knockback Timer <gen> is on) Equal to False
        • Then - Actions
          • Trigger - Turn on Knockback Timer <gen>
          • Countdown Timer - Start KB_Timer as a Repeating timer that will expire in KB_Interval seconds
        • Else - Actions
So with this setup all you have to do is set the KB_Source, KB_Target, KB_Duration, KB_Distance, and finally run the "Add Knockback To System" trigger and the System will handle the rest.

Also, if your Unit Indexer assigns Index 0 to Units then it could cause problems since I use KB_Duration/Distance/Angle[0] as a shortcut for saving Variables. I know for a fact Bribe's Unit Indexer (the one I'm using) DOESN'T use 0 so this is safe to do.

Note that the design for this system might not be perfect, but it will definitely point you in the right direction.
 

Attachments

  • Basic Knockback System 1.w3m
    22.8 KB · Views: 18
  • Basic Knockback System 2.w3m
    23.5 KB · Views: 18
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,540
That turns off a unit's pathing so that it can move through objects (terrain/doodads/units/buildings). It's not very safe to use since it can push units off the map or get them stuck inside things once it's turned back on.

Often times it's used alongside a Pathing Checking system which is a system that tests the Pathing at a given Point. It would be used in a Knockback system to check if the Point where the unit is going to moved to is "Pathable" (Nothing blocking it). If it is Pathable, then the Knockback system will move the unit, otherwise the unit will stay still and the Knockback will continue to expire.

That being said, it's probably unnecessary to turn off Pathing since you can achieve the same thing using JASS:
vJASS:
SetUnitX(whichUnit, newX)
SetUnitY(whichUnit, newY)
^This will move a unit while ignoring all pathing checks. Obviously it's not GUI friendly so you'll need to use it with Custom script. Also, it uses coordinates instead of a Point, but those are pretty much the same thing since a Point is just a pair of X/Y coordinates.

One last thing, it's important to note that the code above doesn't force the unit to issue a "Stop" order, which Move Unit Instantly does. That's why in my Knockback example the unit is (almost) uncontrollable, because it's being issued a "Stop" order every 0.03 seconds.
 
Last edited:
Status
Not open for further replies.
Top