Tuesday, September 8, 2009

Tweaker Tutorial: Modifying the World Map

Tweaker Version: 1.1.0+

In an earlier post on New Addon Design Concepts, a reader commented that he'd be interested in trying my MapTweak addon. I really enjoyed writing MapTweak. One might argue that it was the predecessor to Tweaker and this whole project. But as such, there's nothing that MapTweak did that can't be easily achieved in Tweaker, so that addon won't be released. Instead, I bring you this Tweaker Tutorial. (Sorry it took so long.)

For this tutorial, I found two addons to compare Tweaker's performance with: Cartographer (specifically, the LookNFeel module) and tekMapEnhancer.

Cartographer is a huge framework for creating addons that interact with the World Map. It does many more things then what I'm going to demonstrate in this tutorial, so I've removed all the other standard Cartographer modules so that we can focus purely on Cartographer itself and the LookNFeel module (which is what actually tweaks the World Map to look and behave differently). If you want those other features, you're probably best off sticking to the whole package. If you're really only concerned with making the map smaller and a bit transparent and being able to type and move and generally interact with the game while the World Map is open, then this tutorial is for you.

Tekkub, the author of tekMapEnhancer, is someone of whom I'm a big fan. She writes small, efficient addons that serve a legitimate purpose, and lots of them. That said, tekMapEnhancer is great, but it's always left me wanting more. With 7 lines of code, she's duplicated all the finer points of the LookNFeel module (which is 1352 lines long). However, tekMapEnhancer doesn't allow you to move the map, it doesn't allow you to open other UI Panels like the Character and Social panes while the map is open open, and there are no options -- you're stuck with the default 0.75 scale, the fully opaque window, the normal player arrow, and whatever else you may want to be different.

In this Tweaker tutorial, I will show you how to duplicate all the settings of LookNFeel, except overlay transparency -- I feel that it's a neat idea that only looks good half the time and requires way too much work to execute. I'll also show you how to modify how the area name and description are displayed. The description is rarely ever used by the default UI. Wintergrasp occasionally uses it, directly underneath the area name. I believe maps with nodes on them like the pvp objectives in Hellfire Peninsula may also use it.

But before we get into the tutorial, let's talk performance. As I've said before, it's important, and understanding what you're gaining from using Tweaker and these tutorials is what will make all the effort seem worthwhile.

Addon Base Startup During Configuration Map Open Map Closed
Cartographer 1702.48 KB, 110.67 ms 1723.53 KB, 1065.14 ms 1704.70 KB, 613.48 ms 1704.70 KB, 32.54 ms
LookNFeel 62.92 KB, 0.46 ms 62.92 KB, 10.85 ms 62.88 KB, 5.83 ms 62.88 KB, 3.58 ms
tekMapEnhancer 0.61 KB, 0.00 ms - 0.61 KB, 0.00 ms 0.61 KB, 0.00 ms
Tweaker 19.31 KB, 0.08 ms - 19.31 KB, 0.00 ms 19.31 KB, 0.00 ms

Keep in mind, I was polling memory and CPU usage every minute for several minutes, and Cartographer's numbers were frequently jumping up and down. The numbers above are just a close approximation of what they were most frequently at. Overall, I was seeing a steady rise in the amount of memory Cartographer was using (LookNFeel stayed steady), but it seemed to eventually plateau around 1704-1705. I can't fathom what the cause of this would be. CPU seemed to steadily decline so long as the map was closed. For both Cartographer and its module, CPU usage spiked heavily during configuration, which leads me to believe the settings were jumping through multiple hoops to eventually change the values that needed to be changed. They both had higher CPU usage while the map was open. For our purposes here, there's no need for that, but I imagine since Cartographer is a framework for the world map, there are things that it's doing and checking on behind the scenes for its other functionalities, and that's just the price you pay when using these big framework apps.

tekMapEnhancer was most impressive. The 0.61 KB seems a bit high to me, but that's still exremely minimal compared even to Tweaker. And it didn't use even as much as 1/100th of a millisecond to start up (obviously, it used some amount of CPU time, it just wasn't measurable here and thus deemed insignificant).

Tweaker, at under 20 KB, is still an impressively small addon -- especially when compared to Cartographer. It is a good bit bigger than tekMapEnhancer, but that's the price you pay for the framework (yes, like Cartographer, Tweaker is a framework for addon development, it just has a different purpose and a much lower cost associated with it). Something to quickly note, while tekMapEnhancer and Tweaker aren't consistently doing anything, they do take action when the map is opened. Excessively opening the map will use the CPU. For both addons, opening the map 20 times in a minute used about 112 ms of CPU time on my computer (very minimal, but I thought it worthwhile to mention it). If you use Tweaker to enable dragging the map, there will also be some CPU time being consumed when the map is dragged. Excessively dragging the map over the course of a minute (ie. constantly dragging and stopping and dragging and stopping and ...) used a second of CPU time. Worth noting, but generally speaking, not a concern and still significantly less than Cartographer's CPU usage (it would have this same effect on top of the CPU usage reported already).

For fun and to demonstrate the cost of the Tweaker framework, I decided to benchmark MapTweak -- it started up in 0.00 ms with a 0.76 KB memory footprint and maintained those numbers. I did this to demonstrate a point here, if you really want to squeeze the most out of your system, you'll create an addon of your own, have a lua file, and do all custom code. I find the cost of Tweaker to be minimal and worth the ease that it provides me. In this case, it's costing me roughly 19 KB of memory and 8 hundredths of a millisecond in startup time. This does not mean that Tweaker will always cost this much. As I've shown before, at its base, with no Tweaker_Data defined, Tweaker uses 0.05 KB of memory and 0.00 ms of CPU at startup. The "cost" of Tweaker comes in relation to what you're doing with it.

The Code

The Explanation
There are a number of things we're modifying here in addition to the WorldMapFrame widget. You could hook to these all individually and modify them. Since the modifications were minimal -- hiding this, scaling that -- I decided to create just a hook and make the modifications from the OnLoad event. The method you chose is up to you. Hooking to them each individually may be a bit more costly and, in my opinion, it looks bloated.

First, we hook to the WorldMapFrame. Since we know it exists, we know we're hooking to it here and not creating it. Alpha determines how transparent the frame is -- 1.0 means fully opaque while 0.0 means fully invisible. I like 0.7, it's enough to see everything clearly and still notice movement behind it. We set Movable = true so that the frame can be moved.

By default, the WorldMapFrame has the keyboard enabled. What this means is that any key you press on your keyboard sends the signal to the WorldMapFrame. This is what prevents you from chatting or moving your tune or, really, doing anything while the map is open. Very important, disable this by setting it to false.

FontStrings, this is one thing that Tweaker easily brings to the table that LookNFeel does not have an option to do. I like the values that I used above, obviously. Feel free to use any font you want and adjust any other settings that you'd like. Move them, scale them, change the color, whatever.

SecureHooks, this is new in 1.1.0. WorldMapFrame.Show is a function that's called to show the WorldMapFrame (who'd have guessed?). For whatever reason, this function sets the map's scale to 1.0 so we need to create a hook to reapply the scale that we want. That said, apply whatever scale you like within this function call. Change the 0.75 to any number you'd like. If you set it higher than 1.00, it'll be bigger than your screen. I find that 0.75 gives me plenty of room to see around the map, including my chat frame. Much smaller makes it difficult to see some things on the map, but experiment for yourself.

Next up, we have the OnLoad event. OnLoad is a fickle pickle. In order to set the OnLoad event, the widget needs to already be loaded, so it really only works when being defined in an xml file. For this reason, Tweaker will call any OnLoad event that you set once Tweaker is finished "loading" the widget. The WorldMapFrame already has an OnLoad event, but I looked around and found that it wasn't being called after load so overwriting the default one with this one doesn't really hurt anything. In the future (Tweaker 1.2.0?), I'll likely create an easy way to hook these events so you don't lose the actual code, or perhaps define an area to include code that'll execute when OnLoad is currently executing (that may be a safer approach).

Anyhow, UIPanelWindows is a collection of names of, well, Panel Windows. tekMapEnhaner keeps the world map frame in this collection, which is why you can type into chat but can't open any other Panels while the map is open (character pane, social pane, bags, etc). It may seem strange, but sometimes I like to open my bags or look at my equipment or see who's online in my guild while my map is open, so I remove the world map from the collection by setting it to nil.

UISpecialFrames is similar. It's a collection of names of frames that aren't Panel Windows but should be closed when the user presses Escape (all panel windows are closed when Escape is pressed). Since my map is no longer a panel window, it needs to be a special frame since I want it to continue to close when I press escape.

This may not be too noticeable in a 4:3 monitor, but when the world map is open, the rest of the screen is blacked out. The BlackoutWorld frame is a frame that's anchored to the WorldMapFrame and has a black texture to hide everything. Simply hide BlackoutWorld in order to remove this "feature" of the world map.

WorldMapMagnifyingGlassButton is the magnifying glass on the world map. When you click it, the map zooms out. It also has some instructional text anchored to it. I assume this is for people with only a single button on their mouse (ie. no right click). Mine has a right-click so I never use this button, so I'm hiding it.

The Player arrow on the map isn't that big to begin with. When you scale the map smaller, it gets even smaller, so I like to make it a bit bigger in order to compensate -- that's what PlayerArrowFrame:SetModelScale(1.2) does. It scales it up to 1.2 times it's normal size (20% bigger). The "Effect" frame should be the same scale since they overlap each other.

And lastly, the OnDragStart and OnDragStop events. These are what allow the map to be moved. Now that I think about it, these events are pretty simplistic and I could probably create a special property for auto generating them both. But for now, if you want the frame to be movable, just define them. If not, delete the 6 lines.

That's all! Enjoy your beautiful new map!

No comments:

Post a Comment