Scenario Optimization - EE-modders/Empire-Earth-toolbox GitHub Wiki
This Appendix is intended to offer advice on creating performance-friendly scenarios. Consider the variety of different computer systems out there. What kind of system might a potential player of your scenario have: top of the line or barely reaching the minimum system requirements? It is always safer to assume the latter and tailor your scenario appropriately so that it should run well on any system. Keep the following guidelines in mind and try, whenever possible, to test your scenarios on more than one computer, especially if you intend to release your creation to the world. A little forethought and careful design can prevent possible performance problems for players with slower computers, who you want to enjoy your scenarios just as much as players with cutting edge systems.
A performance hit is when something in a scenario, under certain conditions, causes a momentary pause or game lag. When something is said to be “expensive” it means it might cause a performance hit in some circumstances. The following are things to keep in mind when creating a scenario. Note that this doesn’t mean you can’t do (or not do) these things, rather that you should be mindful when doing them that it could result in a performance hit on some computers, under certain circumstances.
-
Map Size – Larger maps are more expensive than smaller maps. Keep your maps as small as possible for your scenario.
-
Fog of War/Revealed Map – Doppelgangers (i.e., representations of buildings, tress, etc. in the fog of war) cost memory. If many are on the map, it can be memory intensive and therefore cause a performance hit. Consider removing line of sight to an area if the player doesn’t need to see it.
-
Total Players – Every additional player in your scenario takes up more RAM. It is best to keep the number of players to a minimum.
-
Number of Civs – Each additional civilization in your scenario takes up more RAM (in other words it’s better from a memory perspective to have two players each be England than for one to be England and one to be the Franks). Try to use as few different civilizations as possible in your scenario.
-
Advancing Ages – If you allow players to advance Ages in your scenario, there can be a short pause in play while all the new artwork is being loaded for the new Age. This is especially true going from the Imperial Age to WWI. If Age advancement is not necessary for your scenario, consider restricting Age advancement.
-
Total Units – For best results, try to keep the total number of units in your scenario below about 2700. This includes buildings, trees, and ambient objects like crates and flags.
-
Physics Units – To help ensure good performance on even minimum systems, try to keep the total number of “physics units” below 600 at any given time. “Physics units” are those units that are mobile and currently on the map… even if they’re standing still!
-
Unit Types – The number of types of units should be minimized as well. Every additional type of unit (e.g., Sherman Tank, Crossbowman, Trebuchet, Submarine, P-51, etc.) uses up more RAM.
-
Pathing – When units move from one location to another, their paths must be computed and this takes up CPU resources which can result in lag if: -> here is no open path between the start point and the destination (such as if the units are blocked by a wall or deep water). -> Units are constantly being moved (and therefore re-pathed) to new locations. -> A large number of units are tasked to move at the same time.
-
Graphical Effects – Creating multiple special effects like fire, smoke, explosions, calamities, footprints, etc. may cause a performance hit, especially when created en masse.
-
Large Scale Battles – A large battle with many units may slow down your scenario on low-end computers. Projectiles, different animations, damage effects, target selection and pathing, etc. all contribute to possible lag.
-
Many Active Triggers – Having lots of active (ON) Triggers can be expensive! Keep Triggers inactive (OFF) until they are needed, then have another Trigger turn them on.
-
Area-Based Computations – The following Trigger tests can be expensive, especially if there are a lot of them: -> Object LOS/Range/Near ->Object in Area, especially for large areas or broadly defined Objects (see below)
-
Conversions – Converting many units at once (or just transferring units from one player to another in general) can be expensive, especially if the units are going from one civilization to a different one.
-
Broad Object References – Referenced (i.e., not selected on the map) Objects must scan every object that exists to identify the ones to use. It is best to narrowly define Object Specifications as much as possible. For example: -> Any Player – Avoid using this for specifying players. There may be over a thousand ambient units, including trees, on a map. Use Any Player Exclude World instead. -> Family Vs. Class – Whenever possible, be specific about the kind of object you are looking for. For example, it’s better to indicate a specific unit type using “Class” rather than a more general type using “Family.” Also, avoid using the Not checkbox when specifying a Class or Family when possible as it is less expensive to say what object you are looking for instead of what you are not looking for.
-
Nested Objects – Having Objects nested in other Objects can impact performance, especially if there are many such nested Objects and/or when used in conjunction with Object LOS/Range/Near or Object In Area.
The following are tips and tricks to keep in mind when creating Triggers and their components. For more tips, see the Optimizing Scenarios section, below.
-
In Object Specifications, use VISIBLE BY a player instead of LOS of an Object with many different units, whenever possible.
-
Use a specific HAS ATTRIBUTE in your Object Specifications. While LOS and IN AREA are expensive, selecting Total MaxVelocity or Total Attack, for example, can rule out certain things—such as units that don’t move (e.g., buildings) or that do not attack (e.g., fishing boats, scouts)—and therefore narrow down the search that the game engine has to do. This improves performance versus not specifying an attribute.
-
When spawning units on the map, set a unit attribute such as hit points in the Object Specification with HAS ATTRIBUTE, so that you don’t have to add another Effect that sets that attribute right afterward.
-
Use non-dynamic Objects whenever possible. By unchecking the Dynamic checkbox, the Object Specification is evaluated only once and keeps using the same unit(s), rather than evaluating the Specification multiple times.
-
Use unit variables (available under HAS ATTRIBUTE in an Object Specification). Every type of unit in the game has three unit variables. You can set each of these variables equal to a positive integer value (0-999999) just as you would set any other unit attribute. By “tagging” expensive Objects with a value in this manner, you can evaluate an expensive Object Specification just once instead of multiple times in each Condition and Effect in which that Object appears.
-
Tag spawned units with a unit variable when they spawn, then use the unit variable to identify them in subsequent Triggers.
-
Tag units in an area, or that pass through an area, with a unit variable so they can be easily identified later.
-
For a specific example, see the Optimizing section: 4. Conditions, e. Combine Multiple Triggers with the same Condition.
-
When creating multiple Task-Area Effects for the same units, queue the tasks together in one Trigger instead. Create multiple Effects that task the Object to each Area in turn, and be sure to check the Queue box. This condenses multiple Triggers into one and saves the game from constantly evaluating Object in Area Conditions. For example:
If "Always True" then “Task Guard to Area1" AND "Task Guard to Area2" (queued) AND "Task Guard to Area3" (queued) AND "Task Guard to Area1" (queued)
- Another way to path units is with Patrol or Explore. Use Patrol to move a unit back and forth between two locations or Explore if you just want the unit to wander around. For Example:
If "Always True" then "Task Guard to Area2 (Patrol)"
If "Always True" then "Set Guard Stance (Explore)"
- Break up a complicated or potentially expensive move order into several moves. For example, imagine a large land mass that’s walled off except for one gate. Instead of tasking units to a location beyond the wall, task them (queued) up to the gate, through the gate, then to the final destination. This decreases one large pathfinding task into three much simpler ones.
- For Triggers with expensive Conditions that don’t need to be on a “hair trigger” to fire, set the test interval higher than the default 500ms (game time). That way, they are evaluated less frequently.
When it comes time to optimize your scenario, here are some things to try or at least keep in mind:
1. Testing and Assessing Scenario Performance
A. Play through the scenario (preferably on a low-end machine) and use the F11 hot key (to see Frames per Second) to find any performance trouble spots.
B. Remember: When playing your scenario, try all kinds of strange things – don’t just “play it straight.” You’re looking not just for all possible performance issues, but also to make sure the scenario works as intended!
C. Use the tips listed earlier to step through every Trigger, making all the scenario and Trigger optimizations that you can.
2. Saving RAM with the Map, Units, and Physics
A. Remove as many trees and ambient objects that you can.
B. Reduce the overall number of units, especially mobile units.
C. Task all units that are not yet needed off map.
D. Do not reveal map at game-start.
E. Set regions from previous stages of the scenario (e.g., earlier objectives) to “No LOS” to reduce the number of doppelgangers on-map.
3. Triggers
A. Only necessary Triggers should be on!
- Remember: every Trigger that is active is automatically evaluated over and over until it fires. This can cause a performance hit if there are a lot of Triggers in a scenario (~100 or more).
- As a general rule, all Triggers that don’t need to be active for the entire scenario should be turned on only when they are needed, and turned off when they are not needed. Especially looping Triggers!
B. Looping Triggers should have delays!
- Looping Triggers that have no delay for their Effects will fire “constantly,” so it’s best to include a delay time (the longer the better).
- If a Trigger is always true, or has a Condition that will often be true for a long period of time, setting “Conditions True For” to 1 second (or more) can improve performance.
4. Conditions
A. Conditions are often more expensive than Effects. Conditions, especially area-computation based Conditions, are evaluated constantly, whereas Effects are only evaluated each time their Trigger fires. So optimizing Conditions can have a much greater impact on improving scenario performance.
B. Optimize Condition Order
- Remember that Trigger Conditions are evaluated in order.
- Whenever possible, place low-cost Conditions before (ahead of) expensive ones. Often this can turn an expensive Trigger into an inexpensive one.
- Set the Trigger Update Interval to a larger time interval for any necessary but expensive Triggers, when you can. (Remember the interval is rounded to the nearest half-second of game time.)
C. No qualifiers if not needed!
- Unnecessary qualifiers may make an Effect not do anything, and it just adds evaluating time to a Condition.
- If you see the same Object being used as both the Condition and the Effect of a Trigger, check to see if you can safely remove the qualifier(s).
- Examples:
- < create soldier near fortress > won’t create a soldier if the fortress isn’t there. So maybe there’s no need to check.
- < task any player2 soldier to any player1 soldier in area > if there’s no player 1 soldier in the area, the player 2 unit won’t go there. Either the player1 qualifier is unneeded or, if it is, perhaps the Area is not important.
- < remove any player1 flare 2 in area > same reasoning as number 2 - if it isn’t in the area, it will not be removed.
D. Replace Areas with unit-visible “triplines”
- Replacement example: < Player1 Any Unit in Tripzone Area >
- Place some complex ambients (e.g., road sign, complex granite, crate) down so they cover the tripzone area.
- Select them all as an Object then select VISIBLE BY Player1. Now you don’t need the Area.
- Note that Airplanes or explorers might trip this Trigger too early, so use it wisely!
E. Combine Multiple Triggers with the same Condition
- Try this if you have many (say, 3 or more) Triggers that reference the same Condition (especially if they are looping Triggers).
- Create one Trigger to evaluate an expensive Condition and then set a player variable (HAS ATTRIBUTE) to indicate whether that Condition is true.
Example:
-
Trigger 1:
IF < Player1 Any Unit in Area > THEN < Set Player1 Variable1 = 1 > AND < Turn Trigger2 ON > -
Trigger 2:
IF NOT < Player1 Any Unit in Area > THEN < Set Player1 Variable1 = 0 > AND < Turn Trigger1 ON >
Trigger3, 4, etc.:
IF < Player1 Variable1 = 1 > AND < other Conditions > THEN…
5. Effects
A. Name Banners
- Name banners can cause a performance hit. More than 8 (or so) could impact performance, so use them only where necessary.
B. Conversion (transfer of ownership)
- Convert as few units/buildings as necessary.
-
Especially avoid converting wall units.
-
If possible, delay changes of ownership by 15-20 seconds (hide this delay with e.g., a message or voiceover).
- Change ownership to player with the same civ when possible.
- Last of unit – If there are no copies of a unit anywhere on the map, the art for it is unloaded from RAM. In some cases, it could help to keep a copy of the unit on-map somewhere to prevent the art from having to unload then reload. On the flipside, it’s best to remove units you no longer need for the scenario.
C. Graphics Effects
- Creating many graphical effects at once can cause a performance hit.
- Minimize the number of graphical effects going off at once. Either reduce their number or try staggering them (for example, with delays) so that they do not all play at once.