Matt and I spent some time last week talking about making more game elements dynamic instead of static pictures. Currently the menu buttons and other “button like” elements, such as the store, are all static images. They also have a bit of a “Picasso” look to them, in that they all have kind of weird angles on the edges.
There are two reasons we were thinking it might be good to make those dynamic. The first was the simple reason that, in the long run, it would save time. While not a ton of time, but it does take Matt time to design the buttons and images and give them that Picasso style border. Once they are dynamic, we just put in the text and a few parameters for the edges and they are drawn on the fly.
The second reason is we felt the game would benefit from some fluidity to the user interface. Not only do these dynamic buttons have different angles from each other, they also each have a different look each time they are generated. So the first time you look at the main menu the buttons will look different than the next time you launch the game. Currently they only change when they are loaded into memory, so the actual main menu buttons don’t change (they never get unloaded), but if you quit the app and run it again they would be different. All other buttons change angles each time the view is created.
Building the code for this was interesting. I wanted a single chunk of code that could generate all the various buttons and edges that we will use. I ended up with quite a few parameters to deal with. Some buttons, like the store buttons, have a picture frame appearance. This means they have a “flat” edge and then another inside edge that appears to slope in. Other elements have a raised “windows 3.0” button appearance. Still others have an inset only appearance, without a flat edge.
That is just for the “up/down” angles of the border, which direction they go and the width of the border. There is also a randomized value to the width of the various borders – or more specifically the corners. The right edge border could have a narrow border up top and grow to a wide border at the bottom. Finally, each element also has a randomized skew value. This skew value tells how far the corner is indented. This keeps (if desired) the corners from being right angles.
After all those options, we end up with a button has an appearance like this. And each time it is put on screen it changes angles slightly. Oh, and a final bonus, because it’s dynamic we don’t need to include 3 or 4 different versions of the image. We don’t even need to include a single version of the image, so this will eventually reduce our binary size a bit.
Based on our talk about putting dynamic elements into the game, we also talked about the possibility of using SVG files for some of the graphics. This would drastically reduce the size of the binary as well as make the zoom-factor a non issue. Since an SVG is pure vector information, it wouldn’t matter what scale we draw it at. It would always be the highest quality possible.
I built a small test library in Objective-C to parse the above store button in it’s SVG form. Because the graphics are so simple, relatively speaking, it didn’t take long to be able to parse the SVG data. Admittedly, the library only supported about 1% of the possible functionality, but it was enough to do some rough testing. In the end this did not work out, but it was some great learning time for me with somewhat lower level graphics API calls.
As I said it didn’t end up working out. My library, at least, was too slow. In truth it probably would be fast enough for what we need, but I don’t know that it is worthwhile just yet. What I really wanted to know is if I could use an SVG to dynamically size stuff (think a slow pinch-to-zoom in a certain war amongst clans game) to get a smooth scale effect without loosing quality. That idea went out the window as soon as I first tried the pinch to zoom ability with a single SVG loaded.
While the SVG scaled just fine on the iPad, the CPU hit 100%. I knew that was pretty much the end of my test but figured I’d try one more thing just to prove it out. I put 10 of the same object on the screen and watched the iPad come to a crawl while zooming. It was painfully slow, and that was with just 10 simple objects. Looking at the profile trace it wasn’t the SVG parsing or the calculations to resize the vector points, it was the actual drawing code. Shortly after a few zooms up and down, it crashed quite spectacularly with an out-of-memory error.
I will probably do some more testing with a few other libraries I found and see how they do. At the very least they might work well for some menu-type rendering where zooming and scaling is not a concern. Might even work for the cards (they don’t zoom often) and that would drastically reduce the binary size.
More specifically, the speed of random. Any card game, or any game for that matter, requires random number generation. Does the computer generated player turn left or turn right? What order are the cards after a shuffle? What angle should the text on the button be drawn at?
One concern I had when I started down this Picasso button path was the speed of the random number generator. See, last time I actually benchmarked a random number generator was about 15 years ago. Back then the CPU just couldn’t keep up and would peak out at around 200 or so random numbers a second. I mean, we were talking about 286 CPUs and such.
With that in mind, knowing that each Picasso element would need about 24 random numbers, and there would easily be around a dozen or more elements on screen that need to be generated, I was worried. I mean, we’re talking about a mobile CPU, not a high-end desktop CPU.
Well, I needn’t have worried. I switched to the arc4random_uniform() method which, compared to rand() and random(), is the best available choice. I then performed a speed test. It didn’t take long to determine that this wasn’t going to be an issue. As soon as I had to start adding 0’s to the number of iterations just to get enough time on the clock to actually make a calculation. The results were thus:
Generate 100,000 random numbers on an 1st generation iPad Mini:
random() = 0.16 seconds
arc4random() = 0.27 seconds
Given that, I don’t think random number generation will be a problem in Mealtime Sabotage.