• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Solved] Leaks and improvement

Level 4
Joined
Apr 4, 2020
Messages
13
Hey all i played warcraft for a looong time. always loved making maps etc but they always started to lag a little after some time. Its been years now that i made a map or even played warcraft 3 so i thought this could be fun :D. So i'm making a single player map based on the steam game "Bounty of one". And for some reason (could be reforged or my pc) the moment more then 60+ units are on map with the order attack herounit the game starts lagging, wich is kinda weird because my pc isn't weak. I can remember this never beeing a problem before reforged. Could also be that my triggers have leaks like i can't even imagine. I have read alot of the pages on here about leaks and how to remove them. Read alot on forums how to make what leak proof so i really did my best. Annyone here kind enough to check it out for me :D ? (its not finished but i want it to be playable before i move on to complete it)
 

Attachments

  • SurviveFighter Weather Works.w3m
    225.6 KB · Views: 4

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,877
I don't think it has anything to do with lag or Reforged or anything like that. This sounds like an issue that has existed forever which is issuing Orders to large amounts of units, usually around 60+, that are all owned by the same Player. This will cause problems for the pathfinder and cause units to "jitter", where they stop moving for a few seconds and then resume what they were doing. The fix to this problem would be to distribute the units amongst multiple Computer players, so let's say Players 12 -> 18 in the case of your map. This is pretty simple to do, just change Player 12-18 to all use the same Color, the same Name, and in your Spawn system add the logic for determining which Computer gets a unit next.
  • Events
    • Time - Elapsed game time is 0.00 seconds
  • Conditions
  • Actions
    • For each (Integer A) from 1 to 6 do (Actions)
      • Loop - Actions
        • Set Variable Computers[(Integer A)] = (Player((Integer A) + 11))
        • Player - Set color of Computers[(Integer A)] to Brown
        • Player - Set name of Computers[(Integer A)] to Enemies
  • Set Variable Computer_Index = (Computer_Index + 1)
  • If Computer_Index Greater than 6 then Set Variable Computer_Index = 1 Else do nothing
  • Set Variable Computer_Player = Computers[Computer_Index]
  • Unit - Create 1 Enemy for Computer_Player...
Then proceed to fix any cases where you were checking for Player 12, like "If a unit owned by Player 12 died", and replace it with some more generic logic like "If level of Enemy Classification for (Dying unit) greater than 0", where Enemy Classification is a hidden passive ability that all enemy units are given.


Now for some random things that I would change after glossing over all of your triggers:

Swap this Event with the "Starts The Effect Of An Ability" Event:
  • Unit - A unit Begins casting an ability
"Begins" casting occurs very early in the casting process. The Mana is unspent, the Cooldown hasn't started, and the Effects haven't launched yet. This stage can be interrupted with a simple "Stop" order or any kind of normal interruption effect.

As a result, I could abuse this Pet Dog trigger to spawn an endless supply of dogs, or I could just get stunned by an enemy while prepping the ability and get a free dog by accident:
  • PetDogSpawn
    • Events
      • Unit - A unit Begins casting an ability
    • Conditions
      • (Ability being cast) Equal to Guard Dog
    • Actions
      • Set VariableSet PetDogUnitPoint = ((Position of PlayerHeroUnit) offset by 75.00 towards (Facing of PlayerHeroUnit) degrees.)
      • Unit - Create 1 Pet Dog for Player 1 (Red) at PetDogUnitPoint facing (Position of PlayerHeroUnit)
      • Set VariableSet PetDogUnit = (Last created unit)
This DestroyTrigger() stuff is having no impact on your map's performance, at least where I see it being used:
  • Custom script: call DestroyTrigger( GetTriggeringTrigger() )
I am also concerned with using it this way, but that could be superstition.

You could store these Trees in a Destructible Array variable and Loop over them using a For Loop:
  • Tree Swaying Fast
    • Events
      • Time - Elapsed game time is 0.01 seconds
      • Time - Every 2.57 seconds of game time
    • Conditions
    • Actions
      • Destructible - Pick every destructible in (Playable map area) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Destructible-type of (Picked destructible)) Equal to Summer Tree Wall
            • Then - Actions
              • Animation - Change (Picked destructible)'s animation speed to 10.00% of its original speed
              • Animation - Play (Picked destructible)'s stand hit animation
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Destructible-type of (Picked destructible)) Equal to Fall Tree Wall
            • Then - Actions
              • Animation - Change (Picked destructible)'s animation speed to 10.00% of its original speed
              • Animation - Play (Picked destructible)'s stand hit animation
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Destructible-type of (Picked destructible)) Equal to Snowy Tree Wall
            • Then - Actions
              • Animation - Change (Picked destructible)'s animation speed to 10.00% of its original speed
              • Animation - Play (Picked destructible)'s stand hit animation
            • Else - Actions
You can space out the timing as well so that you aren't running logic on 500 Destructibles of any type every X seconds, and instead running logic on those types of Tree Destructibles over the course of let's say 0.40 seconds every X seconds. It might even look more natural to have the trees play these animations in an offset manner. If the trees can die then it could complicate things, so keep in mind that this solution isn't perfect without extending it further. It's also probably low on the priority list and something you could ignore.

The Weather Effects trigger is leaking Points like WeatherDestructiblePoint, although this trigger only runs once and it's a handful of leaks so it's extremely low priority:
  • Then - Actions
    • Set VariableSet WeatherDestructiblePoint = (Position of (Picked destructible))
    • Destructible - Remove (Picked destructible)
    • Destructible - Create a Snowy Tree Wall at WeatherDestructiblePoint facing (Random angle) with scale (Random real number between 0.85 and 1.00) and variation (Random integer number between 0 and 9)
You Set this variable yet I don't see you using it everywhere that you could:
  • Set VariableSet MAPfull = (Entire map)
Let's optimize your Damage Taken trigger - A big source of performance issues:
  • Damage Numbers
    • Events
      • Unit - A unit Takes damage
    • Conditions
      • (Owner of (Triggering unit)) Equal to Player 12 (Brown)
      • (Damage taken) Greater than 0.99
    • Actions
      • Set VariableSet DamagedUnitPosition = (Position of (Triggering unit))
      • Floating Text - Create floating text that reads (String((Integer((Damage taken))))) at DamagedUnitPosition with Z offset 0.00, using font size 6.00, color (95.00%, 80.00%, 0.00%), and 0.00% transparency
      • Custom script: call RemoveLocation (udg_DamagedUnitPosition)
      • Set VariableSet FloatingText = (Last created floating text)
      • Floating Text - Change FloatingText: Disable permanence
      • Floating Text - Change the lifespan of FloatingText to 2.00 seconds
      • Floating Text - Set the velocity of FloatingText to 64.00 towards 90.00 degrees
This Event chooses a random number once and ONLY once and reuses it for the rest of the game, seems weird that some sessions you'll get a tip every 20 seconds and other sessions you'll get a tip every 160 seconds:
  • Hints
    • Events
      • Time - Every (Random real number between 20.00 and 160.00) seconds of game time
    • Conditions
    • Actions
      • ...
Becareful with the reliance on these Events, they aren't necessarily guaranteed (particularly if you mess with time of day settings):
  • Enemy Spawn And Diff Setup
    • Events
      • Game - The in-game time of day becomes Equal to 18.00
      • Game - The in-game time of day becomes Equal to 6.00
    • Conditions
    • Actions
      • ...
Stuff like this is similar to Destroying the trigger, very unnecessary:
  • Custom script: set udg_EnemySpawnedUnit = null
You would null a local variable to avoid a memory leak but this is global.

When possible, use Event based approaches to update specific things at the exact moment they change, rather than this "lazy" method that tries to correct things and most of the time is being redundant:
  • Multiboard and Leaderboard Updating
    • Events
      • Time - Every 0.25 seconds of game time
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Difficulty Equal to 2
        • Then - Actions
          • Leaderboard - Change the label for Player 4 (Purple) in LeaderBoardPlayer to (Enemy's Alive : + ((String(EnemyCheckupAlive)) + /150))
          • Leaderboard - Change the color of the label for Player 4 (Purple) in LeaderBoardPlayer to (100.00%, 100.00%, 100.00%) with 0.00% transparency
        • Else - Actions
          • Leaderboard - Change the label for Player 4 (Purple) in LeaderBoardPlayer to (Enemy's Alive : + (String(EnemyCheckupAlive)))
      • Leaderboard - Change the label for Player 3 (Teal) in LeaderBoardPlayer to (Total Game Score : + (String(TotalGameScore)))
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • SkillHpBoost Greater than or equal to 1
        • Then - Actions
          • Multiboard - Set the text for MultiboardPlayerInfo item in column 3, row 2 to (String(SkillHpBoost))
        • Else - Actions
      • etc...
This trigger uses the wrong casting Event, leaks 8 Points, constantly sets RangeCheckerPoint to the same place for no reason, has unnecessary Custom Script for destroying a Special Effect, and could be solved nicely with some simple For Loop arithmetic -> (Towards 45.00 x (Integer A) degrees):
  • Range Checker Skill
    • Events
      • Unit - A unit Begins casting an ability
    • Conditions
      • (Ability being cast) Equal to Magnet Range Checker
    • Actions
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 0.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[1] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 45.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[2] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 90.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[3] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 135.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[4] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 180.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[5] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 225.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[6] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 270.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[7] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 315.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[8] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Wait 2.00 seconds
      • For each (Integer A) from 1 to 8, do (Actions)
        • Loop - Actions
          • Special Effect - Destroy RangeCheckerSpecialEffect[(Integer A)]
          • Custom script: call DestroyEffectBJ( udg_RangeCheckerSpecialEffect[GetForLoopIndexA()] )
Get rid of this, it's identical to the Destroy action above it:
  • Custom script: call DestroyEffectBJ( udg_RangeCheckerSpecialEffect[GetForLoopIndexA()] )
I've heard issues related to using (Integer A) and (Integer B) instead of your own Integer variable, although I cannot confirm these to be true. The only place I would avoid using these is in triggers that rely on the "A unit Dies" event since this Event likes to break the trigger queue rules. In "Death" event triggers I suggest using unique variables for anything that could get overwritten.
 
Last edited:
Level 5
Joined
Apr 15, 2019
Messages
33
Looks like "Item Magnet Function" trigger is the main fps dropper here. It leaks a ton of locations per second. I tried to disable it and fps drops pretty much gone.

1st of all you did set SkillMagnetHeroPosition and SkillMagnetItemPosition variables to some locations, but then in actions below sometimes you still used "get position of..." instead of them.

Also "point with polar offset" requires 2 temp locations instead of 1. Beacause it spawns 2 locations under the hood.

What you used is:
set tempPoint1 = position of picked item
move item to tempPoint1 offset by 5 towards x degrees
remove location tempPoint1

What it needs to be
set tempPoint1 = position of picked item
set tempPoint2 = tempPoint1 offset by 5 towards x degrees
move item to tempPoint2
remove location tempPoint1
remove location tempPoint2
 
Last edited:
Level 4
Joined
Apr 4, 2020
Messages
13
I don't think it has anything to do with lag or Reforged or anything like that. This sounds like an issue that has existed forever which is issuing Orders to large amounts of units, usually around 60+, that are all owned by the same Player. This will cause problems for the pathfinder and cause units to "jitter", where they stop moving for a few seconds and then resume what they were doing. The fix to this problem would be to distribute the units amongst multiple Computer players, so let's say Players 12 -> 18 in the case of your map. This is pretty simple to do, just change Player 12-18 to all use the same Color, the same Name, and in your Spawn system add the logic for determining which Computer gets a unit next.
  • Events
    • Time - Elapsed game time is 0.00 seconds
  • Conditions
  • Actions
    • For each (Integer A) from 1 to 6 do (Actions)
      • Loop - Actions
        • Set Variable Computers[(Integer A)] = (Player((Integer A) + 11))
        • Player - Set color of Computers[(Integer A)] to Brown
        • Player - Set name of Computers[(Integer A)] to Enemies
  • Set Variable Computer_Index = (Computer_Index + 1)
  • If Computer_Index Greater than 6 then Set Variable Computer_Index = 1 Else do nothing
  • Set Variable Computer_Player = Computers[Computer_Index]
  • Unit - Create 1 Enemy for Computer_Player...
Then proceed to fix any cases where you were checking for Player 12, like "If a unit owned by Player 12 died", and replace it with some more generic logic like "If level of Enemy Classification for (Dying unit) greater than 0", where Enemy Classification is a hidden passive ability that all enemy units are given.


Now for some random things that I would change after glossing over all of your triggers:

Swap this Event with the "Starts The Effect Of An Ability" Event:
  • Unit - A unit Begins casting an ability
"Begins" casting occurs very early in the casting process. The Mana is unspent, the Cooldown hasn't started, and the Effects haven't launched yet. This stage can be interrupted with a simple "Stop" order or any kind of normal interruption effect.

As a result, I could abuse this Pet Dog trigger to spawn an endless supply of dogs, or I could just get stunned by an enemy while prepping the ability and get a free dog by accident:
  • PetDogSpawn
    • Events
      • Unit - A unit Begins casting an ability
    • Conditions
      • (Ability being cast) Equal to Guard Dog
    • Actions
      • Set VariableSet PetDogUnitPoint = ((Position of PlayerHeroUnit) offset by 75.00 towards (Facing of PlayerHeroUnit) degrees.)
      • Unit - Create 1 Pet Dog for Player 1 (Red) at PetDogUnitPoint facing (Position of PlayerHeroUnit)
      • Set VariableSet PetDogUnit = (Last created unit)
This DestroyTrigger() stuff is having no impact on your map's performance, at least where I see it being used:
  • Custom script: call DestroyTrigger( GetTriggeringTrigger() )
I am also concerned with using it this way, but that could be superstition.

You could store these Trees in a Destructible Array variable and Loop over them using a For Loop:
  • Tree Swaying Fast
    • Events
      • Time - Elapsed game time is 0.01 seconds
      • Time - Every 2.57 seconds of game time
    • Conditions
    • Actions
      • Destructible - Pick every destructible in (Playable map area) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Destructible-type of (Picked destructible)) Equal to Summer Tree Wall
            • Then - Actions
              • Animation - Change (Picked destructible)'s animation speed to 10.00% of its original speed
              • Animation - Play (Picked destructible)'s stand hit animation
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Destructible-type of (Picked destructible)) Equal to Fall Tree Wall
            • Then - Actions
              • Animation - Change (Picked destructible)'s animation speed to 10.00% of its original speed
              • Animation - Play (Picked destructible)'s stand hit animation
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Destructible-type of (Picked destructible)) Equal to Snowy Tree Wall
            • Then - Actions
              • Animation - Change (Picked destructible)'s animation speed to 10.00% of its original speed
              • Animation - Play (Picked destructible)'s stand hit animation
            • Else - Actions
You can space out the timing as well so that you aren't running logic on 500 Destructibles of any type every X seconds, and instead running logic on those types of Tree Destructibles over the course of let's say 0.40 seconds every X seconds. It might even look more natural to have the trees play these animations in an offset manner. If the trees can die then it could complicate things, so keep in mind that this solution isn't perfect without extending it further. It's also probably low on the priority list and something you could ignore.

The Weather Effects trigger is leaking Points like WeatherDestructiblePoint, although this trigger only runs once and it's a handful of leaks so it's extremely low priority:
  • Then - Actions
    • Set VariableSet WeatherDestructiblePoint = (Position of (Picked destructible))
    • Destructible - Remove (Picked destructible)
    • Destructible - Create a Snowy Tree Wall at WeatherDestructiblePoint facing (Random angle) with scale (Random real number between 0.85 and 1.00) and variation (Random integer number between 0 and 9)
You Set this variable yet I don't see you using it everywhere that you could:
  • Set VariableSet MAPfull = (Entire map)
Let's optimize your Damage Taken trigger - A big source of performance issues:
  • Damage Numbers
    • Events
      • Unit - A unit Takes damage
    • Conditions
      • (Owner of (Triggering unit)) Equal to Player 12 (Brown)
      • (Damage taken) Greater than 0.99
    • Actions
      • Set VariableSet DamagedUnitPosition = (Position of (Triggering unit))
      • Floating Text - Create floating text that reads (String((Integer((Damage taken))))) at DamagedUnitPosition with Z offset 0.00, using font size 6.00, color (95.00%, 80.00%, 0.00%), and 0.00% transparency
      • Custom script: call RemoveLocation (udg_DamagedUnitPosition)
      • Set VariableSet FloatingText = (Last created floating text)
      • Floating Text - Change FloatingText: Disable permanence
      • Floating Text - Change the lifespan of FloatingText to 2.00 seconds
      • Floating Text - Set the velocity of FloatingText to 64.00 towards 90.00 degrees
This Event chooses a random number once and ONLY once and reuses it for the rest of the game, seems weird that some sessions you'll get a tip every 20 seconds and other sessions you'll get a tip every 160 seconds:
  • Hints
    • Events
      • Time - Every (Random real number between 20.00 and 160.00) seconds of game time
    • Conditions
    • Actions
      • ...
Becareful with the reliance on these Events, they aren't necessarily guaranteed (particularly if you mess with time of day settings):
  • Enemy Spawn And Diff Setup
    • Events
      • Game - The in-game time of day becomes Equal to 18.00
      • Game - The in-game time of day becomes Equal to 6.00
    • Conditions
    • Actions
      • ...
Stuff like this is similar to Destroying the trigger, very unnecessary:
  • Custom script: set udg_EnemySpawnedUnit = null
You would null a local variable to avoid a memory leak but this is global.

When possible, use Event based approaches to update specific things at the exact moment they change, rather than this "lazy" method that tries to correct things and most of the time is being redundant:
  • Multiboard and Leaderboard Updating
    • Events
      • Time - Every 0.25 seconds of game time
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Difficulty Equal to 2
        • Then - Actions
          • Leaderboard - Change the label for Player 4 (Purple) in LeaderBoardPlayer to (Enemy's Alive : + ((String(EnemyCheckupAlive)) + /150))
          • Leaderboard - Change the color of the label for Player 4 (Purple) in LeaderBoardPlayer to (100.00%, 100.00%, 100.00%) with 0.00% transparency
        • Else - Actions
          • Leaderboard - Change the label for Player 4 (Purple) in LeaderBoardPlayer to (Enemy's Alive : + (String(EnemyCheckupAlive)))
      • Leaderboard - Change the label for Player 3 (Teal) in LeaderBoardPlayer to (Total Game Score : + (String(TotalGameScore)))
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • SkillHpBoost Greater than or equal to 1
        • Then - Actions
          • Multiboard - Set the text for MultiboardPlayerInfo item in column 3, row 2 to (String(SkillHpBoost))
        • Else - Actions
      • etc...
This trigger uses the wrong casting Event, leaks 8 Points, constantly sets RangeCheckerPoint to the same place for no reason, has unnecessary Custom Script for destroying a Special Effect, and could be solved nicely with some simple For Loop arithmetic -> (Towards 45.00 x (Integer A) degrees):
  • Range Checker Skill
    • Events
      • Unit - A unit Begins casting an ability
    • Conditions
      • (Ability being cast) Equal to Magnet Range Checker
    • Actions
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 0.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[1] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 45.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[2] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 90.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[3] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 135.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[4] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 180.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[5] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 225.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[6] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 270.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[7] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Set VariableSet RangeCheckerPoint = (Position of PlayerHeroUnit)
      • Special Effect - Create a special effect at (RangeCheckerPoint offset by (Real(RangeCheckerRange)) towards 315.00 degrees.) using Abilities\Spells\Human\ManaFlare\ManaFlareTarget.mdl
      • Set VariableSet RangeCheckerSpecialEffect[8] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_RangeCheckerPoint)
      • Wait 2.00 seconds
      • For each (Integer A) from 1 to 8, do (Actions)
        • Loop - Actions
          • Special Effect - Destroy RangeCheckerSpecialEffect[(Integer A)]
          • Custom script: call DestroyEffectBJ( udg_RangeCheckerSpecialEffect[GetForLoopIndexA()] )
Get rid of this, it's identical to the Destroy action above it:
  • Custom script: call DestroyEffectBJ( udg_RangeCheckerSpecialEffect[GetForLoopIndexA()] )
I've heard issues related to using (Integer A) and (Integer B) instead of your own Integer variable, although I cannot confirm these to be true. The only place I would avoid using these is in triggers that rely on the "A unit Dies" event since this Event likes to break the trigger queue rules. In "Death" event triggers I suggest using unique variables for anything that could get overwritten.
Thanks you so much so i tried to change all the points u told me and i hope they are ok now :D. The only thing i do not understand is the tree swaying how to make it like u say i should using a destructible array. Maybe an example :p ??? haha sorry i also added the changed version of the map if u liek to check if i "solved" the problems and made the triggers u sayd i should change correctly. Thanks alot in advance !! every hint/tip/help is a like and if i know how + rep :D
 

Attachments

  • SurviveFighter Weather Works.w3m
    236.1 KB · Views: 2
Level 4
Joined
Apr 4, 2020
Messages
13
Looks like "Item Magnet Function" trigger is the main fps dropper here. It leaks a ton of locations per second. I tried to disable it and fps drops pretty much gone.

1st of all you did set SkillMagnetHeroPosition and SkillMagnetItemPosition variables to some locations, but then in actions below sometimes you still use "get position of..." instead of them.

Also "point with polar offset" requires 2 temp locations instead of 1. Beacause it spawns 2 location under the hood.

What you used is:
set tempPoint1 = position of picked item
move item to tempPoint1 offset by 5 towards x degrees
remove location tempPoint1

What it needs to be
set tempPoint1 = position of picked item
set tempPoint2 = tempPoint1 offset by 5 towards x degrees
move item to tempPoint2
remove location tempPoint1
remove location tempPoint2
Ok so i changed it is this better ?
 

Attachments

  • Schermafbeelding 2025-01-25 162033.png
    Schermafbeelding 2025-01-25 162033.png
    86.9 KB · Views: 7

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,877
You should not re-Set data that won't change. In this case the position of the Hero does not need to be recalculated for every single Item. It's not like the Hero can move during this process, it all happens at once.

So you would calculate the position ONCE before the Loop and use it throughout the rest of the trigger. Then Destroy it at the very end of the trigger:
  • Actions
    • Set Position = (Position of Hero)
    • Item - Pick every item in Region and do (Actions)
      • Loop - Actions
        • ... do stuff
    • Custom script: call RemoveLocation( udg_Position)
Also, Distance checks are expensive, you don't want to calculate them twice. You can store the Distance in a variable and reference that, or organize the If Then Elses so that you only use the Distance condition after you've confirmed it was an appropriate Item.

Remember that whenever you see these parenthesis -> (Do something) there's a high chance that you're calling a function. A function runs code "under the hood", which means it could be doing complicated math in order to get your desired outcome. A Variable can be used to store the results of that function, allowing you to access the final results of that complicated process without having to do the process all over again.

Bad:
  • If (Distance between PointA and PointB) Greater than 100.00) then...
  • If (Distance between PointA and PointB) Less than 1000.00) then...
Good:
  • Set Variable Distance = (Distance between PointA and PointB)
  • If (Distance Greater than 100.00) then...
  • If (Distance Less than 1000.00) then...
The Bad trigger calculates the Distance twice, which means it's doing the complicated math two times. It's redundant, we know that both times will have the same exact outcome so why go through the whole process again? On the contrary, the Good trigger calculates the Distance once, then refers to the results of that calculation twice using the Variable - which is efficient and fast. Now this doesn't mean that you need to use variables EVERYWHERE, but triggers that run very often are a great place to make these optimizations.

Edit:
You don't really have to worry about the tree swaying thing, but I'll give you an example of how you could achieve it:
  • Store Trees In Array
    • Events
      • Time - Elapsed game time is 0.00 seconds
    • Conditions
    • Actions
      • Set Variable Tree_Count = 0
      • Set Variable Tree_Speed = 10.00 // calculate this based on difficulty
      • Destructible - Pick every destructible in (Playable map area) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Or - Multiple conditions
                • (Destructible-type of (Picked destructible)) Equal to Summer Tree Wall
                • (Destructible-type of (Picked destructible)) Equal to Fall Tree Wall
                • (Destructible-type of (Picked destructible)) Equal to Snowy Tree Wall
            • Then - Actions
              • Set Variable Tree_Count = (Tree_Count + 1)
              • Set Variable Tree_Destructibles[Tree_Count] = (Picked destructible)
              • Animation - Change Tree_Destructibles[Tree_Count]'s animation speed to Tree_Speed% of its original speed
            • Else - Actions
  • Animate Trees In Array
    • Events
      • Time - Every 5.00 seconds
    • Conditions
    • Actions
      • For each integer (Tree_Loop) from 1 to Tree_Count do (Actions)
        • Loop - Actions
          • Animation - Play Tree_Destructibles[Tree_Loop]'s stand hit animation
Variables:
Tree_Count = Integer
Tree_Loop = Integer
Tree_Speed = Real
Tree_Destructibles = Destructible (Array)

Note that this same logic could be applied to your Item Magnet trigger, using an Item Array instead of a Destructible Array. You would add these Items to the Array at the time of creating them rather than constantly searching for them. Keep in mind that removing elements from an Array can be complicated, so a concept like Dynamic Indexing would be nice to use here.
 
Last edited:
Level 4
Joined
Apr 4, 2020
Messages
13
You should not re-Set data that won't change. In this case the position of the Hero does not need to be recalculated for every single Item. It's not like the Hero can move during this process, it all happens at once.

So you would calculate the position ONCE before the Loop and use it throughout the rest of the trigger. Then Destroy it at the very end of the trigger:
  • Actions
    • Set Position = (Position of Hero)
    • Item - Pick every item in Region and do (Actions)
      • Loop - Actions
        • ... do stuff
    • Custom script: call RemoveLocation( udg_Position)
Also, Distance checks are expensive, you don't want to calculate them twice. You can store the Distance in a variable and reference that, or organize the If Then Elses so that you only use the Distance condition after you've confirmed it was an appropriate Item.

Remember that whenever you see these parenthesis -> (Do something) there's a high chance that you're calling a function. A function runs code "under the hood", which means it could be doing complicated math in order to get your desired outcome. A Variable can be used to store the results of that function, allowing you to access the final results of that complicated process without having to do the process all over again.

Bad:
  • If (Distance between PointA and PointB) Greater than 100.00) then...
  • If (Distance between PointA and PointB) Less than 1000.00) then...
Good:
  • Set Variable Distance = (Distance between PointA and PointB)
  • If (Distance Greater than 100.00) then...
  • If (Distance Less than 1000.00) then...
The Bad trigger calculates the Distance twice, which means it's doing the complicated math two times. It's redundant, we know that both times will have the same exact outcome so why go through the whole process again? On the contrary, the Good trigger calculates the Distance once, then refers to the results of that calculation twice using the Variable - which is efficient and fast. Now this doesn't mean that you need to use variables EVERYWHERE, but triggers that run very often are a great place to make these optimizations.

Edit:
You don't really have to worry about the tree swaying thing, but I'll give you an example of how you could achieve it:
  • Store Trees In Array
    • Events
      • Time - Elapsed game time is 0.00 seconds
    • Conditions
    • Actions
      • Set Variable Tree_Count = 0
      • Set Variable Tree_Speed = 10.00 // calculate this based on difficulty
      • Destructible - Pick every destructible in (Playable map area) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Or - Multiple conditions
                • (Destructible-type of (Picked destructible)) Equal to Summer Tree Wall
                • (Destructible-type of (Picked destructible)) Equal to Fall Tree Wall
                • (Destructible-type of (Picked destructible)) Equal to Snowy Tree Wall
            • Then - Actions
              • Set Variable Tree_Count = (Tree_Count + 1)
              • Set Variable Tree_Destructibles[Tree_Count] = (Picked destructible)
              • Animation - Change Tree_Destructibles[Tree_Count]'s animation speed to Tree_Speed% of its original speed
            • Else - Actions
  • Animate Trees In Array
    • Events
      • Time - Every 5.00 seconds
    • Conditions
    • Actions
      • For each integer (Tree_Loop) from 1 to Tree_Count do (Actions)
        • Loop - Actions
          • Animation - Play Tree_Destructibles[Tree_Loop]'s stand hit animation
Variables:
Tree_Count = Integer
Tree_Loop = Integer
Tree_Speed = Real
Tree_Destructibles = Destructible (Array)

Note that this same logic could be applied to your Item Magnet trigger, using an Item Array instead of a Destructible Array. You would add these Items to the Array at the time of creating them rather than constantly searching for them. Keep in mind that removing elements from an Array can be complicated, so a concept like Dynamic Indexing would be nice to use here.
i did it like u sayd and it works great again :D u'r really good at this haha :p how to implement them not moving the same together ? bc u'r right like this they all move in synch
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,877
i did it like u sayd and it works great again :D u'r really good at this haha :p how to implement them not moving the same together ? bc u'r right like this they all move in synch
You could try something like this:
  • Animate Trees In Array
    • Events
      • Time - Every 5.00 seconds
    • Conditions
    • Actions
      • Set Variable Tree_Size_A = (Tree_Count / 4)
      • For each integer (Tree_Loop) from 1 to Tree_Size_A do (Actions)
        • Loop - Actions
          • Animation - Play Tree_Destructibles[Tree_Loop]'s stand hit animation
      • If Tree_Size_A Less than 4 Then (Skip remaining actions) Else (Do nothing)
      • Set Variable Tree_Size_B = (Tree_Size_A + Tree_Size_A)
      • Set Variable Tree_Size_C = (Tree_Size_A + Tree_Size_B)
      • Set Variable Tree_Size_D = Tree_Count
      • Wait 0.10 seconds
      • For each integer (Tree_Loop) from (Tree_Size_A + 1) to Tree_Size_B do (Actions)
        • Loop - Actions
          • Animation - Play Tree_Destructibles[Tree_Loop]'s stand hit animation
      • Wait 0.10 seconds
      • For each integer (Tree_Loop) from (Tree_Size_B + 1) to Tree_Size_C do (Actions)
        • Loop - Actions
          • Animation - Play Tree_Destructibles[Tree_Loop]'s stand hit animation
      • Wait 0.10 seconds
      • For each integer (Tree_Loop) from (Tree_Size_C + 1) to Tree_Size_D do (Actions)
        • Loop - Actions
          • Animation - Play Tree_Destructibles[Tree_Loop]'s stand hit animation
 
Top