Wml optimisation

From The Battle for Wesnoth Wiki
Revision as of 21:42, 28 October 2013 by Dunno (talk | contribs) (Created page with "''Work in progress...'' ==WML Optimisation== ===For programming beginners:=== ====What you should know first:==== *Macros *Eve...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Work in progress...

WML Optimisation

For programming beginners:

What you should know first:

How do I optimise my code?

So, you've written your campaign, scenario or era and it works. Congratulations, but your work is far from over! If your campaign or scenario is big enough, you'll often notice that it runs very slowly, and you need to do something about it. Or maybe you have a powerful computer and you'd like to make sure that other, weaker machines won't have troubles with running your content. Or perhaps, you just want to learn how to write code that not only works, but is also efficient.

The first thing to know, since apparently it's not stressed enough, macros do not improve performance! Macros only make your code more readable and easier to maintain. Try to refrain from using big macros, and more importantly, avoid using macros inside macros (inside macros, inside macros... yes, I've seen it happen, and yes I've tried doing it myself when I was new to wml). Under the covers, WML macros are replaced by the code they represent, and if there are macros inside macros (especially big ones), lines of code multiply horribly fast.

What's a better way to write code that appears often? Custom events' and while loops! By defining a custom event, and firing it when needed, you'll save a lot of code lines, make your code nicer to read and much more efficient. Same goes for the while loop: instead of copy/pasting your code, try to use a loop, whenever you can. Let's take a look at this example problem: whenever side 1 unit enters a village, you want to place 5 walking corpses around this village. Let's say there are 6 villages in question with coordinates (1,1), (2,2), (3,3), (4,4), (5,5), (6,6). Here's the bad way to do it:

   #WARNING: this code was written on the run, and may not be 100% accurate
   #NOTE: remember, this should work alright, but it is not efficient! Imagine if you have much more villages in mind, or you want to spawn more zombies
   
   #def SPAWN_ZOMBIES X Y SIDE
   [unit]
   side={SIDE}
   type=Walking Corpse
   x,y={X},{Y}
   placement=map_passable
   [unit]
   [unit]
   side={SIDE}
   type=Walking Corpse
   x,y={X},{Y}
   placement=map_passable
   [unit]
   [unit]
   side={SIDE}
   type=Walking Corpse
   x,y={X},{Y}
   placement=map_passable
   [unit]
   [unit]
   side={SIDE}
   type=Walking Corpse
   x,y={X},{Y}
   placement=map_passable
   [unit]
   [unit]
   side={SIDE}
   type=Walking Corpse
   x,y={X},{Y}
   placement=map_passable
   [unit]
   #enddef
   
   [event]
   name=moveto
       [filter]
       side=1
       x,y=1,1
       [or]
       x,y=2,2
       [/or]
       [or]
       x,y=3,3
       [/or]
       [or]
       x,y=4,4
       [/or]
       [or]
       x,y=5,5
       [/or]
       [or]
       x,y=6,6
       [/or]
       [/filter]
   
       {SPAWN_ZOMBIES $unit.x $unit.y 2}
   [/event]

Looks innocent, right? But imagine that there are more villages. Or better yet, imagine you want to increase the amount of zombies, you'll have to copy/paste even more, and it's easy to make a mistake while doing it. So here's a much nicer and more efficient way:

   #first, let's define our custom event:
   [event]
   name=spawn_zombies
   first_time_only=no
   #it's easy to forget, but you have to set first_time_only in custom events too
   {VARIABLE i 0}
   [while]
       [variable]
       name=i
       less_than_equal_to=5
   #repeating the loop 5 times for 5 zombies, note how easy it is now to change the amount of zombies you want to spawn!
       [/variable]
       [do]
           [unit]
           side=2
           type=Walking Corpse
           x,y=$unit.x,$unit.y
   #unit variable is passed to this event from the parent event. 
           placement=map_passable
           [unit]
           {VARIABLE_OP i add 1}
       [/do]
   [/while]
   {CLEAR_VARIABLE i}
   [/event]
   
   [event]
   name=moveto
       [filter]
       side=1
       x,y=1,1
       [or]
       x,y=2,2
       [/or]
       [or]
       x,y=3,3
       [/or]
       [or]
       x,y=4,4
       [/or]
       [or]
       x,y=5,5
       [/or]
       [or]
       x,y=6,6
       [/or]
       [/filter]
   
       [fire_event]
       name=spawn_zombies
       [/fire_event]
   [/event]

But if fire events are so cool and efficient, what is the advantage of using macros? Good question. As you can see, custom events have one issue: it's harder to set dynamic variables in fired events. When defining macros, you specify arguments and those arguments are declared whenever we're using that macro and can be changed in any way you, and more importantly, someone else wants. Remember, that you might want to start a bigger project and invite someone to help you with the code. Macros are obvious and transparent in usage - when reading a macro definition you always know what is the changing variable and what is a constant. In custom events, it is not so obvious.

Work in progress...