Archives

TapPhrase – the lowdown

TapPhrase is a free game developed solely by myself; art, original concept, heavy testing and design input provided by Kevin Craven. It’s available on iPhone, iPad and iPhone touch devices running iOS8+. This article describes the game, how it evolved, the approach, and thoughts for the future. If you want a quick taster – see this gameplay video.

What is the game?

Well, if the above YouTube video has failed, TapPhrase is a two dimensional ‘guess the phrase’ game, where you guess the phrase as quickly as you can to earn stars to unlock more phrases. The generic game concept is inspired by shows like CatchPhrase and other games like What’s the saying?

The stars can also be used to buy in-game helpers such as keys and trashcans. Keys place a correct letter in the slot, and trashcans delete a letter that isn’t required.

Every day you play TapPhrase you receive a free daily spin token, which allows you to play a huge wheel of fortune. This can reward you with free stars, trashcans or keys. You can also discover by playing (or replaying) levels one of four pieces of a wheel which crash-land into place, as seen in this video.

The overall feel of this game is supposed to be a 1920s cinema, embodied in the quirky tutorial with crackly Queen’s English voiceover.

Game motivation

I’ve been stuck in the narrow Microsoft world for a long time, developing desktop business applications. I’ve also dabbled with hybrid mobile business apps using HTML/Javascript and it all felt a bit of a ridiculous process. The promise of cross platform delivery typically sacrificed any benefit due to the complete inconsistencies with the host operating system UI principles. I’ve always wondered what the Mac development ecosystem felt like without being numbed by cross-platform libraries or layers. I’ve also a huge history of games and gaming. I developed many small games from the age of 8, on my Acorn Electron and through to University in 1994 I joined an international group called ANGLE, developing “Portals” – what we would call now a world travelling Diablo style MMO RPG. #wayaheadofitstime but it didn’t publish.

At first I wanted to do an iOS business app, but started to fall asleep. I found more enthusiasm for building a game, yet I needed a simple starting point, however, I realised I really suck at art, really. I wanted to make a game that I would enjoy playing and doesn’t take advantage of the user with harsh in-app purchases and adverts. And if we can, even inject moments of delight (e.g. in the video above you can see the tiles move freely at the end of the phrase like a spirit level, for no actual game reason!).

Luckily a friend (Kev Craven) had spent his evenings for the last two years drawing a common phrase and posting it to twitter/facebook. He’s had moderate success and I wondered if we could turn that into something bigger, so we got together and worked on delivering this to a mobile app to make it more interactive.

If you’re interested, the kinds of games I love and have played the list is endless but big highlights are Chuckie Egg, Elite, Cannon Fodder, Resident Evil, Max Payne, Half Life, EVE Online, Lord of the rings online, Broken Sword, Diablo, COD, SOCOM, X-the threat, Civilisation Flight Sims, Bejewelled, Starwars Galaxies,  Guild Wars, Forza, Project Gotham Racing, Syphon Filter, Tom Clancy’s Rainbow 6, Ghost Recon, Kings Road, and even FarmVille.

Fears & Rewards

What’s been the most frustrating point looking back is that both of us were probably too defensive and protective of our rights, IP and revenue protection. Even though our initial remit was to bring the art and fun of his concept to more people, and money wasn’t actually the initial driver, it’s only natural that when you formalise things, develop in-app purchases, look at competing products, and integrate advertising that you start to wonder ‘if this thing goes big, we want to maximise our rights and revenues and protect ourselves from liability’. 6 months after release and less than 50 downloads we’ve made less than $10 revenue, so we shouldn’t have worried so much. I went to great lengths, to limit liability by setting up a Limited Company, creating formal agreements, company bank account, learning basic accounting – all these activities are not making sense currently.

The biggest reward has been the positive reviews and feedback. It’s great hearing from people who genuinely love the game.  Its pretty awesome to say “Hey, have you seen my game” and their jaw drops at the fact you have released a game to the AppStore.  It’s also incredible when you get unsolicited feedback from people you know, I remember one colleague saying “Hey we all huddled round a table and played your game in the pub last night, it was good fun!”. – That’s a great reward.

There’s been one negative review, and my philosophy (thanks Kev) is that bad reviews is still publicity. I do however wish the review had something constructive and useful that could help me better the game, given that they spent part of their life they’ll never get back, but alas it’s a typical butt-hurt review.

Adverts & In-App Purchases

Both Kev and I hate ads and in-app purchases, but couldn’t see a way of recouping some kind of financial reward for our efforts other than to include them.

So we tried to make the game the priority, not our pockets.

Thus, full screen adverts happen every 5 minutes, and never appear during a level. Banner ads also only appear when the game level is finished. We’ve also incentivised advertising, so if you click on the adverts, you get some coins. We also decided that the user was quite entitled to play the game without a data/wifi connection, and didn’t nerf the game if we couldn’t get adverts (which some apps do).

As for in-app purchases, we thought it’d be rude not to have them as an alternate way people could donate some money to show their appreciation. We feel that if you like this type of game, you’ll probably never need to buy an in-app purchase. Hell, you can even find all the answers on Facebook if you look hard enough! But that would spoil your fun.

You can wait for your daily spin and share a failed level on Twitter/Facebook for more coins, so really, there’s little need to buy the in-app purchase unless you are really really keen – which means (we hope) you’re probably getting your money’s worth.

Menus are so 90s

One of the things I wish I’d have realised earlier is that structured menus are completely unnecessary in games.

I started off with the typical systematic breakdown of game functionality and created unnecessary barriers to start the game, to buy in-app purchases and reach other aspects of the game, such that some people didn’t actually know how to start playing!

Simulator Screen Shot 19 Sep 2015 12.56.38 Simulator Screen Shot 19 Sep 2015 12.56.44 Simulator Screen Shot 19 Sep 2015 12.56.58

We haven’t got rid of the menus, but we’ve made a few tweaks to hold a glass up to our intent; the first time you start the game, when you press START you are flung straight into a tutorial.

When you finish a game you can click on the traditional ‘+’ icons, to visit the store or spin the wheel, instead of being forced to navigate a maze of menus.

Simulator Screen Shot 19 Sep 2015 12.58.20

Ideally we’d like to get rid of all menus, and you should be able to perform all required actions (even visit the ‘about’ screen) from where you are.

Testing

Who likes testing? I don’t think anyone does. As you may have already read, my previous post about choosing iOS, focusing just on iOS allowed me to complete the game efficiently by limiting the matrix of testing of just one platform. However, I don’t have lots of devices, and the most important thing I learned is….

Not everyone does what you would do.

This isn’t just about the way you play the game, but the way you use the device. E.g. do you like to play your own music or have your device on silent? Do certain apps send push notifications which can disrupt yours? Do you have low memory conditions? Are you always switching between text messages or other Apps, what does that do to the experience. This led me to make sure that TapPhrase doesn’t stop your music, that you can play your own, that your audio settings are respected, and that the App behaved as users would expect it to.

The top tip here is, get it tested by humans that aren’t you.

I also found valuable feedback on reddit, where users helped provide crash reports and gave me really honest opinions.  TapPhrase is currently crash free because of this.

Art work and music

Most of the artwork is hand drawn by Kevin, but some of the early art wasn’t. I hunted high and low for music and art to get the concept going. I learned that free stuff isn’t really free, first of all there are licence conditions attached which you have to be really careful about, and can cause a burden to your processes; secondly you spend a lot of time wading through rubbish to find something of good quality. I’ve written before about Free and Open Source Software, and will be writing more about this soon.

I spent a long time finding the right music and worrying about licence agreements of code and assets. – Necessary precautions in todays litigious world.

Marketing
Obviously there’s a Facebook and Twitter account, but apart from that, the following marketing antics have been employed (to very minimal success).

  • Having a orchestrated marketing plan of what to do every hour after release for the next few days.
  • Tweeting those who helped/inspired the game creation. This exposed me to about 100k eyes.
  • Tweet and mention the authors of the FOSS content you’ve used – that’s nice for them and potentially good for you too.
  • Enter discussions around the topic of your game or similar games and subtly include your game.
  • Engage with the online discussion forums to get early feedback on your game.
  • Submitted to game review sites (no luck).

Indicating that either TapPhrase actually sucks, and everyone is being nice to us; or, this is a heavily saturated and competitive market with fewer customers than we’re told. Alternatively I’m just bad at PR!

What I’ll need to do soon is hook up the App-Preview video and release some fixes for iOS9, plus Kev’s working on a high quality video we can promote through Facebook or Twitter.

We do get requests to port the game to Android and Windows Phone, but speaking to other developers, they consider about 200k downloads to be the point where they’d consider it. #notthereyet

Historical graphics

TapPhrase didn’t always look this pretty – check out these early screenshots!

Screen Shot 2015-09-19 at 13.29.03 Screen Shot 2015-09-19 at 13.29.18 Screen Shot 2015-09-19 at 13.29.33 Screen Shot 2015-09-19 at 13.29.50iOS Simulator Screen Shot 2 Jun 2015 18.30.55

And check out some of the designs and discussion boards we used to communicate needs/ideas.

dimensions 001 ShowTimeFeedback1.001 TapPhrase.Screen discussions.001

And here’s two HUGELY significant graphics….

The first time the new logo is used to represent the game.. Gave us a massive boost.

IMG_4927

The first approved submission to the App Store!

Screen Shot 2015-06-06 at 10.51.46

The unusables

There are a number of TapPhrases we couldn’t use, for instance a picture of the band KISS is used twice, but we didn’t want to even get into the legal wrangling of that one. There are about 25 TapPhrases whose answer was too long; more than 24 letters (3 rows of 8). For instance, here’s one of my all-time favourites..

CROUCHINGTIGERHIDDENDRAGON

(Crouching Tiger, Hidden Dragon)

As you progress in TapPhrase, you’ll are able to unlock a pack of full colour TapPhrases – a rare stint where Kev wanted to spice the images up a little.

We might put a few of these unusables in the game at some point, if it becomes popular.
How long did it take to make?
Well, given Kev created around 750 TapPhrases, (609 in the game), we can estimate that the base effort is around 750 hours.

Then we include all the icons, movies, rework, curtains, the EPIC wheel of fortune, discussions. His contribution alone must be over 1000 hours.

Mine has been pretty much nearly every free minute of my lunch break, most evenings and weekends (the fiancé works some weekends, enabling this), I even took a week off work to move the game forward. So probably about the same as it’s taken a year to date, so over a thousand hours and about £1000 to get the MacBook + subscription to Apple’s developer programme.

In the process I’ve learned how to use a Mac, Xcode, crash log analysis, performance profiling, the App Store/ITunes Connect, objective C, and SpriteKit + supporting frameworks. The hardest bit has been to integrate with the Apple in-app purchase mechanism, it’s much harder than it should be and the documentation is quite misleading or poor. Audio was much easier to implement than anticipated, and so was social sharing and adverts.

I’ve also been giving back to the community on stack overflow and blogging topics.

Thanks

TapPhrase wouldn’t be possible without friends, family, understanding partners, work colleagues, the friends at Kev’s Church and the randoms on reddit. But I’d especially like to thank the Beta Testers as captured below in an early tweet. But a special mention should go to our biggest fan , who eagerly awaits all level packs and retweets, shares, comments, added a review and even provided crash reports.

Screen Shot 2015-09-19 at 14.04.22

The next time you see someone trying to share or tweet or post about a game, help them. It’s hard to get noticed!

Advertisements

iOS SpriteKit – Font Loading Times of SKLabelNodes

The other day I encountered massive performance bottlenecks in my SpriteKit project, when setting the .text property of an SKLabelNode.

I discovered that I wasn’t alone.

After investigating many different techniques to pre-cache, thread and speed up font loading, I discovered the real solution was simply to load a very specific font. The documentation from Apple is somewhat poor, and leads newbies into the same trap.

If you’re experiencing problems with font loading speed it maybe because you’re loading the ENTIRE font family and all its variants.

If you want a font to load quickly, be explicit.
If I load “Chalkboard SE” it will take 4-6 seconds, and appear to work.

But if I load “ChalkboardSE-Regular” , it’s virtually instantaneous ~100ms or less.

To determine the exact font name you need, simply use a more specific font name from the list below, and pass it to your SKLabelNode constructor.

Here’s the code used to extract the list…

for( NSString* familyName in [[UIFont familyNames] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] )

{

NSLog(@"%@", @"\n");

for( NSString* explicitFontName in [UIFont fontNamesForFamilyName:familyName] )

{

NSLog(@"  %@", explicitFontName );

}

}

AcademyEngravedLetPlain

AlNile-Bold

AlNile

AmericanTypewriter-Light

AmericanTypewriter-CondensedLight

AmericanTypewriter-CondensedBold

AmericanTypewriter

AmericanTypewriter-Condensed

AmericanTypewriter-Bold

AppleColorEmoji

AppleSDGothicNeo-Thin

AppleSDGothicNeo-UltraLight

AppleSDGothicNeo-SemiBold

AppleSDGothicNeo-Medium

AppleSDGothicNeo-Regular

AppleSDGothicNeo-Bold

AppleSDGothicNeo-Light

ArialMT

Arial-BoldItalicMT

Arial-ItalicMT

Arial-BoldMT

ArialHebrew-Bold

ArialHebrew-Light

ArialHebrew

ArialRoundedMTBold

Avenir-Heavy

Avenir-Oblique

Avenir-Black

Avenir-Book

Avenir-BlackOblique

Avenir-HeavyOblique

Avenir-Light

Avenir-MediumOblique

Avenir-Medium

Avenir-LightOblique

Avenir-Roman

Avenir-BookOblique

AvenirNext-MediumItalic

AvenirNext-Bold

AvenirNext-UltraLight

AvenirNext-DemiBold

AvenirNext-HeavyItalic

AvenirNext-Heavy

AvenirNext-Medium

AvenirNext-Italic

AvenirNext-UltraLightItalic

AvenirNext-BoldItalic

AvenirNext-Regular

AvenirNext-DemiBoldItalic

AvenirNextCondensed-Regular

AvenirNextCondensed-MediumItalic

AvenirNextCondensed-UltraLightItalic

AvenirNextCondensed-UltraLight

AvenirNextCondensed-BoldItalic

AvenirNextCondensed-Italic

AvenirNextCondensed-Medium

AvenirNextCondensed-HeavyItalic

AvenirNextCondensed-Heavy

AvenirNextCondensed-DemiBoldItalic

AvenirNextCondensed-DemiBold

AvenirNextCondensed-Bold

BanglaSangamMN

BanglaSangamMN-Bold

Baskerville-Bold

Baskerville-SemiBoldItalic

Baskerville-BoldItalic

Baskerville

Baskerville-SemiBold

Baskerville-Italic

BodoniSvtyTwoITCTT-Book

BodoniSvtyTwoITCTT-Bold

BodoniSvtyTwoITCTT-BookIta

BodoniSvtyTwoOSITCTT-BookIt

BodoniSvtyTwoOSITCTT-Bold

BodoniSvtyTwoOSITCTT-Book

BodoniSvtyTwoSCITCTT-Book

BodoniOrnamentsITCTT

BradleyHandITCTT-Bold

ChalkboardSE-Light

ChalkboardSE-Regular

ChalkboardSE-Bold

Chalkduster

Cochin-Bold

Cochin-BoldItalic

Cochin-Italic

Cochin

Copperplate

Copperplate-Light

Copperplate-Bold

Courier

Courier-Oblique

Courier-BoldOblique

Courier-Bold

CourierNewPSMT

CourierNewPS-BoldMT

CourierNewPS-ItalicMT

CourierNewPS-BoldItalicMT

DamascusBold

Damascus

DamascusLight

DamascusMedium

DamascusSemiBold

DevanagariSangamMN

DevanagariSangamMN-Bold

Didot-Bold

Didot-Italic

Didot

DINAlternate-Bold

DINCondensed-Bold

EuphemiaUCAS

EuphemiaUCAS-Bold

EuphemiaUCAS-Italic

Farah

Futura-Medium

Futura-CondensedMedium

Futura-MediumItalic

Futura-CondensedExtraBold

GeezaPro-Bold

GeezaPro

Georgia-BoldItalic

Georgia-Bold

Georgia-Italic

Georgia

GillSans

GillSans-Italic

GillSans-BoldItalic

GillSans-Light

GillSans-LightItalic

GillSans-Bold

GujaratiSangamMN-Bold

GujaratiSangamMN

GurmukhiMN-Bold

GurmukhiMN

STHeitiSC-Medium

STHeitiSC-Light

STHeitiTC-Medium

STHeitiTC-Light

Helvetica-Oblique

Helvetica-Light

Helvetica-Bold

Helvetica

Helvetica-BoldOblique

Helvetica-LightOblique

HelveticaNeue-BoldItalic

HelveticaNeue-Light

HelveticaNeue-Italic

HelveticaNeue-UltraLightItalic

HelveticaNeue-CondensedBold

HelveticaNeue-MediumItalic

HelveticaNeue-Thin

HelveticaNeue-Medium

HelveticaNeue-ThinItalic

HelveticaNeue-LightItalic

HelveticaNeue-UltraLight

HelveticaNeue-Bold

HelveticaNeue

HelveticaNeue-CondensedBlack

HiraKakuProN-W6

HiraKakuProN-W3

HiraMinProN-W6

HiraMinProN-W3

HoeflerText-Regular

HoeflerText-BlackItalic

HoeflerText-Italic

HoeflerText-Black

IowanOldStyle-Bold

IowanOldStyle-BoldItalic

IowanOldStyle-Italic

IowanOldStyle-Roman

Kailasa

Kailasa-Bold

KannadaSangamMN

KannadaSangamMN-Bold

KhmerSangamMN

KohinoorDevanagari-Light

KohinoorDevanagari-Book

KohinoorDevanagari-Medium

LaoSangamMN

MalayalamSangamMN

MalayalamSangamMN-Bold

Marion-Regular

Marion-Italic

Marion-Bold

MarkerFelt-Thin

MarkerFelt-Wide

Menlo-BoldItalic

Menlo-Regular

Menlo-Bold

Menlo-Italic

DiwanMishafi

Noteworthy-Bold

Noteworthy-Light

Optima-Regular

Optima-Italic

Optima-Bold

Optima-BoldItalic

Optima-ExtraBlack

OriyaSangamMN

OriyaSangamMN-Bold

Palatino-Roman

Palatino-Italic

Palatino-Bold

Palatino-BoldItalic

Papyrus-Condensed

Papyrus

PartyLetPlain

SavoyeLetPlain

SinhalaSangamMN

SinhalaSangamMN-Bold

SnellRoundhand-Black

SnellRoundhand-Bold

SnellRoundhand

Superclarendon-Regular

Superclarendon-BoldItalic

Superclarendon-Light

Superclarendon-BlackItalic

Superclarendon-Italic

Superclarendon-LightItalic

Superclarendon-Bold

Superclarendon-Black

Symbol

TamilSangamMN

TamilSangamMN-Bold

TeluguSangamMN

TeluguSangamMN-Bold

Thonburi-Bold

Thonburi

Thonburi-Light

TimesNewRomanPS-BoldItalicMT

TimesNewRomanPSMT

TimesNewRomanPS-BoldMT

TimesNewRomanPS-ItalicMT

Trebuchet-BoldItalic

TrebuchetMS

TrebuchetMS-Bold

TrebuchetMS-Italic

Verdana-BoldItalic

Verdana-Italic

Verdana

Verdana-Bold

ZapfDingbatsITC

Zapfino

 

As for how I got here – Read my journey below!

 

See

http://stackoverflow.com/questions/20380954/delay-when-calling-sklabelnode

http://stackoverflow.com/questions/24478476/settext-is-slow-with-sprite-kit

http://stackoverflow.com/questions/23255143/sklabelnode-delays-app-start

Many workarounds are suggested in the attempt to prevent ‘stutter’ or pauses in the game, particularly during transitions between scenes where the font is used for the first time. The pause happens when the .text property is assigned (the font is obviously loaded on demand).

During testing, I noticed that keeping strong references to SKLabelNodes in a singleton cache doesn’t help – when transitioning to a new scene the fonts still re-loaded.

 

I decided to look at the differences in the time the thread is blocked loading the different font families.

There’s also a difference depending on if you’ve loaded another font beforehand.

The table – explained

The first timing is an average of 5 runs, where I simply iterated through all the font families installed on the device, and produced the time it took to load each without restarting the device.

The second set of timings show the cost of loading the font from cold (one run).

The third set of timings show the cost of loading the same font, having loaded Arial first (one run).

The final column shows the seconds potentially gained by loading Arial first – not including the time it took to load Arial.

 

Average time (s) over 5 runs loading all font family names  If Loaded First   If Loaded after Arial   Gain in doing so 
Academy Engraved LET

2.871

3.134

2.180

0.954

Al Nile

0.008

0.269

0.196

0.074

American Typewriter

0.015

0.039

0.007

0.032

Apple Color Emoji

0.015

0.257

0.213

0.044

Apple SD Gothic Neo

1.195

2.210

2.151

0.059

Arial

0.018

0.009

0.002

0.007

Arial Hebrew

0.006

0.226

0.180

0.046

Arial Rounded MT Bold

0.006

0.009

0.004

0.005

Avenir

1.421

2.165

2.154

0.011

Avenir Next

1.148

2.168

2.096

0.072

Avenir Next Condensed

1.195

2.409

2.234

0.175

Bangla Sangam MN

0.010

0.039

0.004

0.035

Baskerville

0.010

0.026

0.006

0.021

Bodoni 72

1.221

2.169

2.168

0.000

Bodoni 72 Oldstyle

1.261

3.046

2.508

0.538

Bodoni 72 Smallcaps

1.132

2.149

2.133

0.016

Bodoni Ornaments

0.004

0.006

0.004

0.003

Bradley Hand

1.252

2.192

2.066

0.126

Chalkboard SE

1.182

2.584

2.102

0.482

Chalkduster

0.008

0.021

0.004

0.017

Cochin

0.011

0.031

0.005

0.026

Copperplate

0.010

0.020

0.004

0.016

Courier

0.018

0.029

0.007

0.022

Courier New

0.002

0.023

0.006

0.018

Damascus

0.006

0.210

0.186

0.024

Devanagari Sangam MN

0.011

0.042

0.007

0.035

Didot

0.007

0.025

0.023

0.002

DIN Alternate

1.161

3.066

2.341

0.725

DIN Condensed

1.248

3.492

2.279

1.213

Euphemia UCAS

0.003

0.016

0.016

-0.001

Farah

0.003

0.237

0.215

0.022

Futura

1.174

2.185

2.317

-0.132

Geeza Pro

1.582

2.275

2.517

-0.242

Georgia

0.009

0.020

0.004

0.016

Gill Sans

0.004

0.036

0.035

0.002

Gujarati Sangam MN

0.008

0.016

0.007

0.009

Gurmukhi MN

0.005

0.013

0.005

0.008

Heiti SC

1.189

3.278

2.462

0.816

Heiti TC

1.191

2.474

2.179

0.295

Helvetica

0.001

0.023

0.007

0.016

Helvetica Neue

0.005

0.024

0.015

0.008

Hiragino Kaku Gothic ProN

1.275

2.939

2.229

0.710

Hiragino Mincho ProN

1.233

3.319

2.119

1.200

Hoefler Text

0.015

0.030

0.007

0.023

Iowan Old Style

1.246

2.151

2.281

-0.130

Kailasa

0.002

0.211

0.213

-0.002

Kannada Sangam MN

0.009

0.025

0.007

0.018

Khmer Sangam MN

0.013

0.011

0.006

0.005

Kohinoor Devanagari

1.166

3.374

2.112

1.262

Lao Sangam MN

0.003

0.009

0.005

0.004

Malayalam Sangam MN

0.007

0.015

0.005

0.010

Marion

1.432

3.151

2.494

0.657

Marker Felt

1.443

2.193

2.415

-0.222

Menlo

1.278

2.157

2.056

0.101

Mishafi

1.263

2.394

2.697

-0.304

Noteworthy

1.170

2.162

2.134

0.028

Optima

1.167

2.358

2.118

0.240

Oriya Sangam MN

0.006

0.017

0.006

0.011

Palatino

0.019

0.050

0.011

0.039

Papyrus

0.013

0.014

0.004

0.010

Party LET

1.177

3.299

2.140

1.159

Savoye LET

1.202

2.453

2.164

0.289

Sinhala Sangam MN

0.011

0.021

0.014

0.007

Snell Roundhand

0.010

0.064

0.006

0.058

Superclarendon

1.200

2.391

2.129

0.262

Symbol

0.002

0.181

0.226

-0.045

Tamil Sangam MN

0.008

0.016

0.004

0.011

Telugu Sangam MN

0.012

0.021

0.006

0.015

Thonburi

0.017

0.011

0.009

0.002

Times New Roman

0.018

0.034

0.007

0.028

Trebuchet MS

0.012

0.018

0.006

0.012

Verdana

0.010

0.026

0.006

0.020

Zapf Dingbats

0.002

0.263

0.255

0.008

Zapfino

0.008

0.063

0.011

0.053

Using threads

I also discovered a way of making the majority of the pause happen without blocking the main SpriteKit animation, and that was to invoke an NSThread in the background to load the fonts. This maybe ideal if you have other assets you are loading and you can then present a progress bar, or spinning loading symbol to help the user understand what’s going on. I’ve not looked into making atlases of my fonts or other tools like http://www.bmglyph.com.

In my first scene, I simply spawn a thread which loads the fonts.

[NSThreaddetachNewThreadSelector:@selector(cacheFont) toTarget:selfwithObject:nil];

And add a function which loads, and stores a strong reference to the label node to keep the font alive in memory. – I’ve not tested if that’s required, but S.O. mentioned it.

-(void)cacheFont
{
SKLabelNode* cacheThisFont = [SKLabelNode labelNodeWithFontNamed:@"Noteworthy"];
cacheThisFont.text = @"a"; // setting .text loads the font.

   // todo - possibly a strong reference to the node in a singleton or member on your app.
}

However this just feels wrong…

 

Scene transitions

I witnessed that having a bare NSObject singleton which kept strong references to the SKLabelNodes populated with text was not enough – subsequent scenes can still suffer a delay. I even tried using the [copy] message on the existing label nodes in the cache to speed things up, but that also demonstrated delays if the SKLabelNodes weren’t in any scene.

However, I did discover that keeping the SKLabelNodes as children in a scene, prevents the lengthy loading delay.

That means, that if you load all your fonts in scene 1 with a progress bar, you have to keep making sure that every scene you transition to has SKLabelNodes added to it.

In my architecture I lazy delete the previous scene on a timed action, which may be help keep the nodes alive between transitions.

However.. It seems odd to have to go to such trouble…

 

And then I discovered loading more SPECIFIC font names took no time at all….. Thanks Apple – for giving a bad code example!!!