Or put another way: It’s time for a post about problems with time-related programming.
From the Y2K bug (which I worked on) to the Year 2038 Problem to leap years (it’s more complicated than year/4) and leap seconds, there are a lot of potential pitfalls when you’re trying to get a computer to work with dates and times. This kind of code isn’t the most glamourous or even always necessary… One of the first games I worked on was for the original PlayStation which didn’t even have a clock. Modern devices have them though and they can be useful, so here are a couple of issues we ran into recently.
In Empire Casino, we added a “deals” feature, so we have a server where we can say things like, “People will get 50% extra stuff for the same price, from now until Monday.” Steve worked on the client and server code for this while I implemented some SDKs.
When it came time to test it on iOS and Android, I created an hour-long sale. I could see it in iOS but not in Android. Steve used UTC on the server and client, so time zones weren’t the issue, but I did test using other time zones to be careful. After printing the times I was getting from the server and the device’s time, and doing some other tests, I noticed that tm_isdst in struct tm was coming out as nonzero on Android, meaning that Daylight Savings Time was in effect. Remember what I said a few entries ago about initialization? Making sure that it’s zero everywhere (we don’t care about DST) fixed the issue.
Moral of the story: Initialize your data!The next one was pretty sneaky. As a retention mechanic, Empire Casino awards you a minigame token every day that you open the app. Back in April, we were getting reports of people not receiving them every day. I noticed that we only had a check for awarding the daily token when the user enters the main menu. If the app is still running in the background and the user was in game when they came back to it, it wouldn’t do the check. So I added the check to applicationWillEnterForeground, and another place or two to be safe, and figured that was it.
Nope. It was an improvement, but didn’t ultimately fix the issue. Since it didn’t affect everyone, and our playerbase was smaller back then, some people probably gave up on the app rather than (re-)reporting it. We got slammed, relatively speaking, with bug reports on June 2nd of people not getting their tokens. We all dutifully opened the apps on our devices to test, and got the tokens, so what could it be?
I started checking over the token awarding code, and thinking, “What happened on June 2nd?” Well, WWDC started on June 2nd, but as much as I’d like to blame my problems on Apple, the issue was occurring on Android as well.
I noticed that our userPlayedToday check was testing if lastDayPlayed is less than today. This would mean if someone set their device’s date to the future and ran the game, the future date would get saved, and when they changed it back, the future date isn’t less than today so they wouldn’t get a token. There couldn’t be that many people setting their device dates in the future, could there?
After running out of options, I started running dates through our date code. I should have tried this earlier, but it’s code that’s supposed to “just work”. (Famous last words) Anyway, we were using some date code called CCDate. When I ran dates through it using setLastDatePlayed, I got the following:sLDP args 2014 6 1 // values passed in
sLDP 1 2014 5 31 // object before change
sLDP 2 2014 7 1 // object after change
Uh oh, 2014-05-31 changed to 2014-07-01 instead of 2014-06-01. When looking through the code, I saw that for whatever reason, CCDate doesn’t let you set year, month, and day all at once when using ints — you have to set them individually. (I didn’t write the original date handling code, or add CCDate, so I had to get familiar with it) This causes problems, due to the implementation.
As above, let’s say we have an existing date of 2014-05-31. Then we try to set the date to the next day. We first set the year to 2014. No effect. We then set the month to 6, but internally, CCDate is a struct tm and a time_t value. What the void month(const int nmon) and other int setters do is update the appropriate entry in the struct tm, and then call mktime to update the time_t. Now what’s this we see in the documentation for mktime?
the values of the other members are interpreted even if out of their valid ranges. For example, tm_mday may contain values above 31, which are interpreted accordingly as the days that follow the last day of the selected month. A call to this function automatically adjusts the values of the members of timeptr if they are off-range
So setting the month to 6 gives us 2014-06-31, but that’s an invalid date, so when we give it to mktime, it happily changes it to 2014-07-01. Then setting the day to 1 doesn’t do anything, and the date is saved. We’ve already given the player today’s token, though, so the issue doesn’t show up until the next day when we try to see if 2014-07-01 is less than 2014-06-02. So the problem was equivalent to people setting their device dates to the future, but it was our own dumb fault.
To get a fix out quickly, the first thing that came to mind is to set the day before the month, but you can’t do that. Changing 2014-06-01 to 2014-07-31 will do:
Set year: 2014-06-01
Set day: 2014-06-31 (invalid)
Mktime changes it to 2014-07-01
Set month: 2014-07-01
So you can either set the month twice, to account for a possible mktime correction, or set the day to 1, then set the month and day. Ultimately we should change CCDate to allow setting YMD all at once as ints, but this (properly commented) fix is okay for the time being. Now we check if the two dates are not equal, so people with future lastPlayedDates can get their tokens.
Moral of the story: Beware of side effects!
As a final note, we did notice a spike in our Chartboost bootup stats on June 2nd and were happy about the number of users, but in retrospect, it was probably people starting the game multiple times trying to get their token. Based on the calendar, this bug would also have manifested on Feb 2nd and Apr 2nd, and we can see spikes on those dates as well, followed by drops in traffic.
Git commit message of the month: “blah blah deprecated blah”
Chat message of the month: “if only it were a shooter” http://www.doki.ca/pics/work/EGMSeanbaby-Friends.jpg
(I worked on this game many moons ago. It was a short project, the client was happy. Yay.)
Glitch of the month: Cocos2d-x does what it can to be cross-platform, but not all platforms are created equal. I found this bug in the word wrap code on Mac, when printing more than two lines of text. I’ve been meaning to submit a pull request, if it hasn’t been fixed already, but haven’t yet.
Our Empire Casino games have been updated to include promotions, redeem codes (try BIGBUCKS) and free chips for following our accounts!
As the title implies, I’ll be talking about CCLabelTTF, not CCLabelBMFont or CCLabelAtlas, since that’s what we’re using in Empire Casino. First of all, using TTF fonts is a little different between iOS and Android, and tripped us up. As described in the Cocos2d-x wiki, on iOS you have to add the TTF to your Info.plist, and you pass the font name to CCLabelTTF::create. The font name, however, may be different than the filename. Open the font in whatever font preview tool you have on your system to get the font name. On Android, however, you have to pass in the font filename. So you could end up having something like “Cool Font” on iOS and “fonts/myfontfile.ttf” on Android.
So because of a font referencing bug, our rules screen in Three Card Poker was coming out in a Helvetica-ish font — whatever the default is. After changing it to the font we’re using everywhere else, I noticed that the text overflowed the dialog box. Line spacing isn’t a huge issue when you have one-line button text, or just a couple of lines in a dialog box, but it becomes obvious with large blocks of text.
ftxdumperfuser -t hhea -A d font.ttf # export font header to XML [edit XML] ftxdumperfuser -t hhea -A f font.ttf # import XML to font header
When dumping the header XML from my font, “lineGap” was 0 so I decreased the “ascender” value and increased the “descender” value until I liked the result. When you’re doing this, it’s important to test by displaying every character in the font you’ll ever want to display, since you don’t want to cut off any tops or bottoms.
I copied the new font over to Android, and didn’t see any change. After searching for information on Cocos2d-x line spacing, I did find a forum thread complaining about it looking different on iOS and Android so I started looking in the library. I traced through the code on iOS just to see how it was implemented, but line spacing seems to be handled by the OS, inside drawInRect:withFont:lineBreakMode:alignment: in CCImage.mm.
When searching for font in Cocos2d-x’s Java files, it only shows up in Cocos2dxBitmap.java, so I started looking there. In computeTextProperty and splitString we see the following:
final FontMetricsInt fm = pPaint.getFontMetricsInt(); final int heightPerLine = (int) Math.ceil(fm.bottom - fm.top);
(It’s simply “final int h” in computeTextProperty)
If we look in FontMetrics to see what it gives us, you see that as well as “top” and “bottom”, it also has “ascent” and “descent”. So let’s try changing top to ascent, and bottom to descent.
That looks much better, but if you look carefully, some of the descenders (eg: p, g, y) are cut off. Going back to Cocos2dxBitmap.java, we see that “top” is also referred to four times in computeY, so we change those to “ascent”. And now our line spacing in Android matches up to iOS. Apparently the extents (top and bottom) of some fonts include extra whitespace, and by changing the ascender and descender values, we can essentially crop it out, but Cocos2d-x on Android doesn’t use these values. I checked, and top/bottom still appear to be used in Cocos2d-x 3.0. I can’t guarantee that these changes won’t look bad with the particular font you’re dealing with, but they helped in my case.
Looking back in FontMetrics, however, there’s one more value: “leading”, or “the recommended additional space to add between lines of text”. It seems like this value should affect the line spacing, but this value isn’t used in Cocos2dxBitmap.java. My font had a lineGap of 0, so as a test, I set it to a large value. Opening it in OSX’s font previewer showed a larger line spacing. I tried it in the iOS simulator, however, and there was no change. After searching a little, it appears that iOS7 ignores the lineGap property. To maintain compatibility with iOS6, it seems it would be safest to take any custom TTFs you’re using, ensure that lineGap is 0, and adjust the ascender and descender to compensate. Once lineGap is 0, it’s probably not an issue that Cocos2d-x Android isn’t using “leading”.
While I’m talking about text rendering anyway, I ran into another small problem. There seems to be an issue with font stroke in iOS7 when using Cocos2d-x 2.2. Here’s a fix.
Looking back, my first post on this blog was on 2013/05/03, and tomorrow is 2014/05/03… Happy birthday, blog!
Git commit message of the month: “Reload custom shaders on Android” Thanks to pix2d for this tip!
Chat message of the month: “If I could just get a platform with iOS’ debugging, and Android’s liberal install rules, I would be sooooooooo happy.”
Glitch of the month: Somehow, opening this popup didn’t take the altered Z values of the wheel’s pegs and flipper into account. This one made it to the App Store.
Office Attacks is now free worldwide on the App Store! Go go go!
Hi. Long time no write. Again, I’ve just been working on ports and client work with nothing interesting I can talk about, but an issue came up recently that hopefully we can help others with. The alphabet soup in the title is referring to Cocos2d-x Blackberry In-App Purchases. (I wanted to keep it short)
Although Blackberry’s market share is small, cocos2d-x 2.2 supports it so why not port our Empire Casino games to it? The biggest issues were supporting a square screen (Q5/Q10), and in-app purchases. The Soomla project exists to help with cross-platform IAPs, but it doesn’t appear to support Blackberry. We weren’t using it anyway since we had existing IAP code on iOS, and implemented it on our own for Android.
Due to BB’s smaller market share, information isn’t as easy to come by, which is why I’m writing this. We started with the SDK examples.
You want the first one. We started with the second one, but that’s using Blackberry’s fancy UI stuff, and all of our UI is done in cocos2d-x. The code in main.c is relatively small, about 300 lines including comments and window init/shutdown code. We added the relevant purchasing code to our project, and noticed that not all purchases were getting a response.
Here’s the important bit which people new to BB may not realize at first: bps_get_event is used for more than just purchases. This function is called from cocos2d-x (in CCEGLView.cpp), so we were competing with it for events. Fortunately, cocos2d-x for Blackberry makes it easy to add your own event handler. Right after setFrameSize in main.cpp, we added the following, after writing our own purchasing event handler:
(Thanks to Flathead Games for some help on Blackberry IAPs)
A couple of posts ago, I recommended OptiPNG and pngcrush for reducing image sizes. Since then, I’ve found ImageAlpha and ImageOptim. (OSX – see note about Xcode) They’re great. They wrap multiple command-line tools into a GUI and brute-force or allow you to choose between them for the best results. ImageAlpha quantizes and dithers PNGs to convert 24-bit full-colour images into 8-bit palettized images with 256 (or fewer) colours. I found that almost all of my images looked quite good when compressed this way. Some background “noise” images looked okay with only 32 or even 8 colours. ImageOptim, on the other hand, tries compressing PNGs with different compression and other options available to find a smaller size. When I dragged in a bunch of PNGs that included some that had already been run through the tool, I found that their size could even go down a little more!
When porting the Slots game to Android, which has a lot of graphics in its seven slot machines, I wanted to get it under 50MB. If your app is over 50MB on Android, you have to have a separate “expansion file“, and I hoped to avoid the extra hassle. (Though saving people download time and device space are also worthy goals) The first APK I ended up with compiled from the repo was 120MB, but after running these two programs over the images, and compressing some of the sound, I got it down to about 42MB.
Finally, a tip that not everyone may know… When trying to find information about a problem you have with a framework or platform, Google might return pages with old and outdated information. It’s pretty easy to click on Search tools->Any time->Past year. You can also specify a custom range, but I’ve often used “past year” as a quick way to narrow down my search to more recent webpages. Obviously, if you’re searching for certain algorithm or language answers, you may not need current information. Use your best judgement for your own situation.
Links of the month: I Get Your Fail – Want to see more gamedev glitch pictures? This site doesn’t update very often, but there’s a decent archive to peruse if you’ve never been there before.
Steve’s Favourite Song – Well… today, anyway.
Git commit message of the month: “Fixed that there project file with its bad file paths and whatnot.”
Chat message of the month:
Steve: she actually prefers Ms. Stud
(Referring to “missStud” in a package name, short for Mississippi Stud)
Glitch of the month: Doing ports and web work doesn’t really lend itself as much to glitches, so here’s one from my previous job. I can’t remember exactly how I got this, but suffice it to say that the camera wasn’t in the right position!
It’s been a while since I’ve written here, but there hasn’t been much to say on the OA front. I’ve been working on a bit of client work, but also did some work on porting the Empire Casino suite from iOS to Android. Having been written in cocos2d-x, the engine makes it fairly easy, but there were a couple of iOS-only things I had to change to be cross-platform, and I ran into a few pitfalls that I’d like to mention here. As of this writing, cocos2d-x 3.0 is still in beta, so we’re using 2.2.2.
- To reiterate a little from a couple of posts ago, don’t use spaces in filenames. “make” doesn’t like them. Also, Android and Java are more strict about package naming than iOS, so the package name had to change on one of the projects.
- The Android toolchain doesn’t like incrementing enums. (And I’d prefer not to turn off warnings) To get around this you can do something like:
for (eMonth month = eMonth_March; month < eMonth_June; month = (eMonth)((int)month+1))
- By default, Android compiles chars as unsigned. To change this, add -fsigned-char to APP_CPPFLAGS in Application.mk. This wasn’t causing a problem for us to my knowledge, but I want to keep things as standard across platforms as possible.
- There’s a bug in cocos2d-x 2.2 Android’s Cocos2dxRenderer.cpp regarding starting an app when the screen is off. It shouldn’t affect many users, but it can be annoying for development. Just add
to the top of Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeOnPause.
- Remember you can
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
to ignore iOS specific code on Android. (Other platform defines: ANDROID, BLACKBERRY, WIN32, WINRT, WP8, MAC, LINUX, MARMALADE, NACL, EMSCRIPTEN, TIZEN)
- This one was a bizarre one, which I lost quite a bit of time figuring out. I kept getting the message “Failed to run aidl. Check workspace log for detail.” when building, after I added In-App Billing. (The workspace log just gave details about where in the code it crashed) Webpages about the issue that I could find gave vague “restart Eclipse” or “delete your project and recreate it” solutions. I finally narrowed it down to the seemingly unrelated fact that I had a <classpathentry> in .classpath that pointed to a directory that didn’t exist. (added when I erroneously followed an old tutorial)
- The In-App Billing flow seems to be a separate Activity, and when returning to the cocos2dx activity, text can appear as black boxes depending on when it’s rendered. Update your value/messagebox CCLabelTTFs using runAction(…callfunc…) as suggested in the link.
- Except for Subscriptions, all IAPs in Android IABv3 are “Managed”, and consumption is determined by code. To “undo” a nonconsumable purchase (ie: I bought an item, and now want to test not owning it again), I just changed the consumption code to operate on nonconsumables instead of consumables. I then exported to a separate (well-labeled) APK, and just run it to consume my purchase.
Finally, an APK is actually a zip file. You can unzip it to see exactly what’s included with the binary, and the file structure.
Link of the week: Jazzpunk – Being a fan of sci-fi (and absurdist humour), I enjoyed the trailer, which is rife with references to Johnny Mnemonic. Check out Giant Bomb’s Quick Look to get an idea of if you’d be interested in playing.
Git commit message of the week: “Updated all them there submodules y’all.”
Chat message of the week: “new tutorial: ‘f*** it. you’re on your own.’”
Glitch of the week: The buttons are positioned properly (although the background image is scaled too small) since they’re positioned as a percentage of the screen size, but the elements of the wheel were also being translated by percentages of the screen size. I changed them to percentages of the wheel background (circle image) size, and it looked better.
Note: Showdown went live today, as well as the Office Attacks update!
As the year comes to a close, the Empire will be shut down most of next week for Christmas, so there will be no blog post. I just wanted to do a quick rundown of what I did in 2013…
- Did a bunch of QA work on various apps
- Started this blog
- Made a 30-inch real-life tower from Office Attacks
- Made an Online Workshop Demo for Office Attacks
- Helped file a SRED claim for Office Attacks
- Went to the Calypso water park
- Got Showdown approved by Apple after several networking problems
- Released Office Attacks after redoing the tutorial a million times
- Went to a launch party for Office Attacks and edited a trailer video
- Helped my co-workers release a bunch of apps (testing, advice, etc)
- Started porting the Empire Casino suite to Android
Here are the apps we released this year, starting with Empire Casino:
- 3 Card Poker (iOS)
- Blackjack (iOS)
- Baccarat (iOS)
- Pai Gow Poker (iOS)
- Slots (iOS)
- Angry Janitors (iOS/Android) (Client work for ISSA)
- Showdown (iOS) (More client work)
- Critter Craze (iOS/Android)
- Office Attacks (iOS) (Canada-only at the moment)
So 2013 was a busy year, and 2014 will probably be busier. For now, though, I hope you all have a Merry Christmas and a Happy (and safe) New Year.Link of the week: Dungelot – I found this game in Mike Rose’s Best of 2013 article. It’s sort of a cross between Rogue and Minesweeper. An amusing time-waster. If you want something a little more involved, check out The Depths (Android) by a friend of mine.
Chat message of the week: “I’ll come in and bang some rocks together. ” (power was out at the office for part of the week)
Glitch of the week: When trying to get Office Attacks working on iPhone, we had an office that was zoomed out too far at one point.
Since the Office Attacks update was submitted to Apple this week (along with our new Empire Casino Slots), there’s not as much OA stuff to talk about, so I thought I’d share some random suggestions from my last year of work.
Learn keyboard shortcuts – Since programming involves a lot of typing, I try to avoid using the mouse whenever possible. So if I need to do any file operations, I’ll generally do them at a command line with cd, pushd & popd, mv, cp, rm, open, etc instead of mousing around. When I started using OSX, I thought it was cool that you could do Command-tab to switch windows like Alt-tab in Windows, but found it odd that windows of the same type were “grouped together”. I soon found that Command-` (backtick) could switch between windows of the same application. Command-shift-3 for taking a picture of the screen was weird, compared to “printscreen”, but oh well. (OSX keyboard shortcuts) Sometimes you can even create your own shortcuts. My old netbook doesn’t have PageUp/PageDown keys, so I used AutoHotkey to map them to Ctrl-Up and Ctrl-Down.
Don’t put spaces in filenames – This is arguably a matter of opinion (they really irritate me), but depending on your workflow they can be a major hindrance. At the command line, you have to either put the filename in quotes, or put a backslash before each space. (to distinguish a space in a filename from a space that separates arguments) Forbidding spaces can also save you headaches if you have to use certain tools like “make”. I’ve been given a cocos2d-x project that built fine for iOS, but the Android toolchain isn’t happy about the filename spaces. Similarly, be careful about naming. Project names like “example-project” and “com.company.2examples” were fine when compiling a cocos2d-x project on iOS, but not on Android.
Watch your data formats – You can end up with a lot of wasted space if you’re not careful. Going from >250MB to 10MB in animation data in the previous entry was an extreme example requiring a lot of work, but figure out what formats your platform supports and see how much you can compress your assets before they look or sound bad. Some things to look at include the following:
Graphics: Codec and settings, resolution, colourspace (e.g. RGBA8888 vs RGBA4444), frame rate
Audio: Codec and settings, KHz, number of channels (stereo vs mono)
Before I started on Office Attacks, the sound was being stored in DVD quality, or 48KHz stereo, since those were the default settings used in a sound tool. Sound effects really don’t need that high fidelity, so I converted them to 22KHz mono for large space savings. And although PNG is lossless compression, there are settings that affect the amount of compression, and it depends on the contents of the image, so no single one is “best”. There are tools like OptiPNG and pngcrush to help you. (PNG may not be appropriate for your project, but it’s just an example) To see what’s taking up space, I cd to the .app directory in the simulator folder and do “du -k | sort -n”.
When I needed to free up space on my iPad I found that a sudoku app was using 100MB. A hundred megabytes. I don’t know what caused it to take up that much space but now it’s been BALEETED. The bigger your app, the more likely it is to be on the chopping block when the user needs to delete something. (In comparison, Sudoku 7 by another local company uses <10MB) Also, try to remember to delete old or placeholder assets that are no longer used.
Keep notes – Although there are email histories and chat logs to remember things for us, I also have a text file to save random snippets that might help me later. Error messages, test results, helpful commands or settings I haven’t memorized, you name it. At my previous job we had an internal wiki, so I asked Steve to install Confluence, since we’re already running Jira for bug tracking.
Anyway, those are the tips that came to mind for now. Got any more?
Links of the week: GOG.com is offering Fallout 1/2/Tactics for free for a couple of days. Also, Ludum Dare is this weekend! Instead of playing games, make one! (where have I heard that before?) Admittedly, I’ve only made one Ludum Dare game… boing SMASH.
Git commit message of the week: “Doh. forgot to #build”
Chat message of the week: “heheh puddles are dead snowmen”
(We have a puddle graphic in the original Office Attacks release, and the Christmas update has a snow-nerd with a cardboard sword that was placed next to a couple of them)
Glitch of the week: Here’s a small issue where a particle system wasn’t being properly removed on the Critter Craze pause menu. Not overly notable… except my wife said it looked like blood.
Note: Yet another app! Our Critter Craze game is now on Google Play and is in the Apple submission queue!
No post last week, in part because it was American Thanksgiving, and I figured I didn’t want to disturb all of your food comas. I listened to the MST3K Turkey Day marathon in the background while working.
This week I thought I’d list some of the things that are going into the next update of Office Attacks!
- Auto-pick towers/powers when you have fewer items than slots
- Changed End Level screens to better match UI of other screens
- Added Christmas/Hanukkah decorations (icon sneak preview at right)
- Review stats during Supercharge
- Double-tap tower placement
- Double-tap tower selling (no more big dialog box over gameplay)
- Added sales, as mentioned in the previous entry
- Audio tweaks
- Added buying full towers
- Fixed piece of the day, added tower of the day
- Fixed graphics on older devices
- Fixed powers on iPhone (d’oh!)
- Fixed close upgrade dialog bug (sorry!)
And more! A big one is that for new players, we revamped the tutorial. (again) In our playtests, we found that people were having a lot of fun in the Workshop, so we put its tutorial first. We later realized that when people download the game, however, they’re expecting tower defence, so now we start the player in levels and introduce the Workshop later on. This update will be submitted to Apple soon.
This week, we presented a small postmortem of Office Attacks! at the local IGDA chapter. I went through our archives and calculated the space savings made via the SWF rendering system talked about in early entries.
- Old Steve spritesheets: 24MB
- Old Enemy spritesheets: 70MB
- Old Tower spritesheets: 150MB
The last value is actually a low estimate. Retina spritesheets were never generated for the towers, so I just multiplied the non-retina 75MB by two.
New “shapesheet” system:
- Steve data: 0.5MB
- Enemy data: 3.2MB
- Tower data: 6.2MB
So from over 250MB to 10MB. Not bad. But what causes the app to be around 100MB? Everything else. Code, sound, and stationary images for multiple chapters, UI, etc.
PS: I didn’t include an enemy scheduled for an update in the figures above. She has more animations, so the spritesheets for her alone were 30MB.
Link of the week: Starwhal – (online demo on their site) This is a fun, quirky, locally-made multiplayer game that’s been making the rounds in the indie scene. Their Kickstarter launched just over a week ago and is already over 40% funded! Check it out!
Also Windforge! I mentioned them here before but this is the last day of their Kickstarter! Go fund the fun!
Git commit message of the week: Nothing overly amusing in our repo lately, but a recent xkcd comic was rather relevant.
Glitch of the week: With an incorrect anchor point and/or position, it looks like Santa is sitting in Steve’s lap! “And I’d like a ‘Mr. Chills’ water cooler for Christmas! Ho ho ho!”
Note: Pictures from our launch party last weekend are now online.
Also, our Pai Gow Poker is now in the App Store!
Unfortunately, some bugs were introduced in the iPhone version update of Office Attacks (C’est la vie), but we’re working on them, and adding some holiday cheer to the game. A few new features will also make it in, but I wanted to talk about some design decisions first.Like many other tower defence games, we wanted to show the current health of the enemies. A standard “health bar” seemed very cliche, so I tried to think of something that wouldn’t be as obtrusive. After doing some research (read: Google image search) into health bars, I found an awesome concept by Adam Vian that you can see to the right. It looks cool, but isn’t appropriate for our game.
I stared at the enemies and thought about where we could display this information. After noticing that they all have a semitransparent oval shadow, I thought maybe we could display it there. So I made a green oval the same size as the shadow and placed it underneath, since it also had to go below the feet and legs. Then as the enemies take damage, the oval shrinks and changes colour in the standard green-yellow-red fashion. Some might find it a bit subtle, but I think it’s neat.
At first we had an “upgrade badge”, which can still be seen in the tower upgrade screen. We placed it to the upper left of the tower, so as not to obscure it, but it was difficult to see which badge went with which tower. When Steve originally created our OATowerViews, he placed a semitransparent square below the tower. I wasn’t sure what this was for, but it turned out to be useful, since I started changing its colour to show the tower’s upgrade level.
I was reading an article called Mastering Free2Play: The Titanic Effect by Ramin Shokrizade recently. Since technology now allows for payment in the middle of an experience, such as via in-app purchases, he proposes the scenario of asking the audience for money during the emotional moments near the end of Titanic. He talks about Distressed Monetization, where games put the player into distress, then offer to sell relief. Admittedly we have something like this, with the pay-to-skip tower building time, but I wanted to add something along the lines of his next point.
He also talked about Elated Monetization, where you basically make the player happy, then “pass the hat”. In the case of a movie, the viewer is happy at the end, but since it’s over, some may feel no incentive to pay. Given our design, however, we could put it in between levels, but which levels? After thinking about it, I decided to do it after the first time the player beats a level with a new enemy. (interns not included) In that case, the player’s happy about beating the level, but also about seeing something new. Also, rather than only driving people to the store, I made it so that an hour-long sale begins, for added incentive.
Another minor change I made this week was to the character bios screen. I figured that since I already have a shader that turns every pixel black, I could show silhouettes for characters you haven’t encountered instead of a question mark.
Finally, I’d like to give a shout out to the board game Castle Panic. The “Drive Him Back!” card is what inspired the “Paper Portal” power in Office Attacks. You can see Wil Wheaton and friends play Castle Panic in TableTop. (DHB card is at around 14:30)
Link of the week: The Trenches – This webcomic about game testers has been running for a couple of years now, but I think I prefer the “Tales From the Trenches” — reader-submitted stories about QA departments. (scroll down)
Git commit message of the week: “Fixed number of starting workshop slots. http://www.youtube.com/watch?v=8DdeLUA0Fms“
Chat message of the week: “An Office Attacks purchase has just gone through!” (Steve made it so this notification is posted to our chat. It’s neat)
Glitch of the week: Wow. After selling a tower, you could click on the empty space, then sell it again. D’oh!
Note: If you’re local to Ottawa, Canada, we’re having a Launch Party for Office Attacks tomorrow! Tickets still available! Meet Steve! He’s visiting Canada!
Edit: Office Attacks iPhone is now live! (in Canada)
We’re putting a few finishing touches on the iPhone version of Office Attacks, but I’m also helping out on a couple of other projects. I just wanted to do a small post on an issue that came up a while ago in OA concerning text quality.
For a while, text looked like this in the game. You can see dark “halos” around the letters, but the texture for the bitmap font looked fine. Note that we have a few general empty button graphics, and draw text on top of them. This is good if you change your mind about wording, or want to localize.
I’m sure most of you know about alpha, or the amount of transparency of a pixel. When using OpenGL, though, there’s another factor that comes into play when using alpha — the blend mode. They’re a bit like the blend modes for layers in Photoshop and GIMP, though those can be more complex.
After doing some tests, I found that even though we were using the default (“Normal”) setting for text in CocosBuilder (which we used for laying out our UIs), it was coming out with a different setting.
Then after some Googling, I found that there was a bug in CocosBuilder where the blend mode wasn’t being saved properly. We wanted the first mode below, but it was coming out as the second.
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
After updating CocosBuilder, the blend modes being saved were the right ones and it looked better. So all good, right? Wrong.
Although we don’t do much text fading in the game, we fade in and out the achievement dialog, and fade out the standard dialog. The standard dialog doesn’t fade in since it comes up more frequently than the achievements, but it fades out quickly to help distinguish one from another if another comes up.
At some point after fixing the text, I noticed the fade was looking odd. Since it’s a very quick fadeout and there were other issues to look at, I didn’t really get a chance to figure out what was happening until I had some time to slow down the fade and look at the issue. As you can see, after the dialog faded out, but just before it was removed, the text could still be seen.
It was a little bit of messing around, but the fix was to use the GL_SRC_ALPHA blend mode while fading, and the GL_ONE blend mode when displaying. So the text changes appearance a little, but a halo during the fade is better than not fading at all. To help illustrate the issue, below is a picture taken in the middle of the fade. The text on the left is using GL_SRC_ALPHA, while the text on the right is using GL_ONE.
Another issue at play here is premultiplied alpha. Premultiplied alpha seems to be standard when using PNGs in cocos2d to mimic the behaviour of UIImage, but is an option when using PVRs. For a font, though, we should be using a PNG (lossless) instead of a PVR. (lossy)
There are a lot of funky blend modes in OpenGL. Until next time, here are a couple of resources:
- Anders Riggelsen’s glBlendFunc Tool – An interactive testbed for blend functions.
- GL Blend Modes cheatsheet – This image is so old (from the days of Quake?), I’m not sure who made it, but it’s a handy guide.
Git commit message of the week: “Now this is a story all about how my screen got flipped turned upside down, and I’d like to take a minute just sit right there, I’ll tell you how I removed a function called preferredInterfaceOrientationForPresentation // be aware!”Chat message of the week: “Sounds like a new [Monday Morning Meeting] policy: ["Scotch makes meetings way better." -- tweet from Jenn during MIGS]“”
Glitch of the week: In the process of making Office Attacks work on iPhone, we ran into this problem on the iPhone 4. Unlike later devices, the maximum texture size on iPhone 4 is 2048×2048, not 4096×4096. Some changes were made to the max texture size sent to TexturePacker, and it was all fixed.
Note: Although Office Attacks! is out now on the Canadian App Store for iPad, an iPhone/iPod Touch version is coming soon!
As I promised last time, this week I’m going to talk about the special effect for the nerd… the lightning bolt! Who needs Thor to make lightning when you have code?
This effect is one of those things that didn’t make it in for the first release, but was something I wanted to work on. What happens is that when the nerd gets enough energy, he uses it to disable the player’s most expensive tower within range. Although he has a neat animation for the event, in the first release the tower simply turned semitransparent.
We needed something better. First I thought about creating an “X” out of lightning, which I would make with midpoint displacement. You basically keep subdividing a line, and displace the midpoint in the Y (in my case) axis by a random amount, reducing the random range each time. The first attempt is the first picture to the left, which didn’t look very good. I used a CCDrawNode like the tutorial circle. I wanted transparency, but it appears that you can’t change the alpha of a CCDrawNode, and if you draw multiple semitransparent line segments, you get ugly darker dots where the lines overlap.
While searching for how other people did lightning for inspiration, I came across a mention of CCMotionStreak, which I started looking into. CCMotionStreak is meant for the type of effect where you have something like a spaceship or a rocket, and you want a line tracing behind it as it moves. CCMS has code to handle the line fading out over time. Another nice thing about it is that you can specify a texture for the line, so you can easily have one with nice faded edges.
Although the cocos2d documentation is usually good enough to figure out what I want to do, I wasn’t sure how to use CCMS. After looking at some examples, and the code for CCMS itself (yay open source!), I found that the initial setPosition call places the CCMS, but then points are added with future setPosition calls. A little odd, but okay.
So I did this, and got… nothing. I took out my clear call, and got the straight line in the second image. After thinking about it and looking at the CCMS code some more, I realized I was trying to use CCMS in a way it wasn’t intended. To get a streak trailing a spaceship, you simply do setPosition, as I mentioned, but then the code is expecting update to be called, since you generally only want to add one streak segment per tick. I wanted to create the whole line every tick.
So I added an update call after each setPosition call, and got the third image. (I removed the clear call, remember?) I added back the clear to get the fourth image. For the fifth image, I changed the line texture, and put CCMS and CCDrawNode side by side to compare. In the fifth image you can also see a couple of sparks, which is basically a stripped down version of the welding effect.
At some point along the way, I decided that instead of having a “lightning X” on the tower, the lightning should shoot from the nerd to the tower, and the tower should start smoking and sparking. So I made a smoke particle system, and you can see it with the final lightning effect to the right. The lightning ended up being three copies of the same bolt, each one with points displaced slightly randomly from the others.
For the smoke, I originally made a particle effect with the simple circle graphic from the welding sparks. I soon remembered that we actually have a smoke effect in the game – the death animation for the crybaby and the interns – so I took the graphic from those animations and used it instead. Since it’s a larger image, it gave me more coverage and I could go from 200 particles to 50. I could also use the rotation parameters available in CCParticleSystemQuad – they’re usually useless with particle images that are symmetrical.
Links of the week: Kickstarter, Kickstarter, Kickstarter! A bunch of local indies have started Kickstarters recently, so I thought I’d list a few and see if any tickle your fancy.
- Windforge – “CONTRA meets MINECRAFT in a building-block RPG! Explore an ever-changing steampunk world featuring Airships & Sky Whales!”
- King Voxel – “King Voxel captures the magic of games like “The Legend of Zelda”, but adds a world generator and voxels!”
- RimWorld – “A sci-fi colony sim driven by an intelligent AI storyteller.” This one actually ended a week ago, and met its goal (and then some!), but you can still take a look.
Git commit message of the week:
“Strangest request ever – added analytics to animated rubble.”
(After having a small “emoticon war” on Hipchat. Hipchat has a lot of emoticons.)
Glitch of the week: It’s always the simple things that get you… When displacing midpoints I wanted a random range from, say, -10 to +10. So I did something like (arc4random()%21)-10, forgetting that arc4random() returns an unsigned int. (unsigned)0 – 10 = four billion and something = messed-up lightning.