Skip to content

Archive

Category: Uncategorized

Today I purchased a Sony Ericsson Experia X8. It appears to be running Android 2.1.1 and is therefore officially out of date. I’ve been curious about Android for a long time. As a user I care little (got my iPad2). As a developer I feel that it may be a bit early to jump onboard. Overall I find the user experience off-putting, sugar substitute and less of the real thing.

Despite endless choices and attractive price tags, app gourmets will be disappointed. Incidentally none of what follows refers to hardware specs, so getting the latest model isn’t a fix.

I’d love to see more innovation from the iPhonoid team. Is iOS doing a lot more than scrapping overlapping windows (possibly one of the worst design ideas borrowed from Xerox), keeping icons lined up and beautifully taking advantage of haptics and UI design metaphors? Surely there is more to mobile UIs than merely copying the core user experience and the form factor? Sadly enough lawyers may be cheaper and safer than innovation, at least in our galaxy.

My new phone came installed with a load of krapps but…

…let’s not get ahead of ourselves. I talked with my girlfriend (who started with an iPod touch, lost a 3GS and sold an iPad2) and suggested that she might use my iPhone4 until 5 comes out, and I would get myself something ‘middle range’.  I could buy an Experia Play with a 30% discount over list price, or a low range HTC for 40% less – but I’d rather save my money to buy the next PSP while anything under 200 bucks felt somewhat flimsy.

So I got an X8 and it runs average 3D content around 20-30 FPS – pretty much what a 2nd gen. iOS device did a couple of years ago.

First contact

After installing an SD card (I somehow put into my head that this might be needed to play games, and apparently it is), I fired the thing up and hurriedly glossed over the setup wizard (nice touch tho’). I then connected to our WIFI and felt growing frustration as I could browse the web but couldn’t log into my google account (needed for gmail and the android store). Then I realized I forgot to set the date. Well yea, but it would be nice to pop up ‘certificate not valid yet’ instead of the ambiguously quizzical ‘no reliable connection to the server’.

The krapps I need, the hand that feeds

Learning how to remove things is always good. Understanding how to do it on iPhone had been somewhat of an accidental discovery. After removing a dozen of these (one reported ‘failed to uninstall correctly’) and removing the icons (somehow resurrected as ‘shortcuts’ after deleting the apps), I noticed with some frustration that way too many apps remained, and these apps cannot be removed (without jailbreak or so called ‘rooting’). I don’t need Crazy Penguins (by no means a shiny demonstration of state of the art technology). I don’t need Facebook (who does?). No more than I needed the sticky Nike app on my iPhone (or YouTube, which I can’t access anyway).

If I can’t even clean my desktop, ‘Open System’ is off to a bad start. And by the way throwing in a couple of demos, like videos and a bit of realtime 3D, might have encouraged me to get something higher-spec-ed. Oh well.

A hopeful reset to factory settings resurrected krapps that weren’t there before along with utterly definitely, irretrievably sticky apps. Sony Ericsson’s very own game store, ‘PlayNow’, can be deleted. I actually was somewhat curious about it (Sigh).

Essentials (less is more)

Even without a whole page of annoying stuff that can’t even be dumped in a group, my phonetop would look cluttered. Amusingly there are 3 separate apps for timer, alarm and clock.

There are also two mail apps including mobile gmail and foobar mail. I’d like to believe they use a completely different protocol unfortunately that’s not the case. foobar mail can receive gmail, and although it looks rudimentary at first sight, it works.

I can’t get mobile gmail to work on my Android phone. My guess – a little glitch that only affects ‘unlucky users’.

Shopping

After pushing all the krapps on page 3 (at least until I get my hands on the Sun’s famed Page 3 Widget For Android) I headed for the marketplace and the PlayNow store.

The marketplace gracefully skips all paid apps. This has given rise to a Chinese Legend according to which ‘everything Android is free’. The bleak truth is that the best stuff isn’t free stuff, and the paid stuff isn’t available worldwide until you root (a.k.a jailbreak) your Android phone (may also require a foreign credit card). Paid apps aren’t available in all countries.

PlayNow vaguely hints at a server or connection issue. It would be so much easier to just say it: PlayNow is only available in 13 countries (apparently doesn’t include the US or Australia. WTF ). A-ha! Here is why SE gracefully allows the frustrated, bewildered user to uninstall PlayNow. How ingenious, how considerate! Millions of frustrated game-jocks can now get rid of the neutered game widget after only minutes of intense frustration.

Back to the Android marketplace, it’s a boring list of… …apps. No editorial. Nothing like decent, well organized features, enticing layouts or attractive banners. Nothing like a shop. Even the (defunct?) Ovi store looks better in some ways (except for making downloading apps a major pain). While it might take the average iPhone gamer at least a year to touch the bottom of the top 200s (with all the minimally well organized sections and subsections), it took me just under 2 minutes to give up and head off to Android Games on Appo.

The problem is, assuming our average user haven’t drowned under the 5 pages of krapps bundled with their phone, the Android marketplace does precious nothing to advertise apps as shiny, fashionable little gems.

So I’m not really surprised given the lack of polish that user comments are badly spelled and the sparse categories are filled with irrelevant apps that belong nowhere. The window is already broken. iPhone users like the app store enough that they (seldom) deface it with ugly, idiotic comments (I love receiving abuse in my support email instead of reading it in the comments section and ‘being the last one to know’).

Before submitting apps, iPhone developers need to consider the possibility that their apps might actually get rejected. As a result iPhone categories are something short of a joyful mess since developers (reluctantly) self-discipline themselves.

The best way to advertise your shiny Android app is to make an iPhone version and submit it to Apple. If you make a good app, it will appear in one of the many top 200s. But that’s not the point. The point is App Store bling bling casts an aura on every little app. After looting the main chambers dedicated app geeks go on questing, looking for the famed hidden gems, passing on the knowledge and keeping your sales alive with neatly written, educated reviews.

I know there are local stores – just uninstalled one because I don’t read Chinese – whereas there won’t ever be anything but a localized app store on the iPhone. Call it many more opportunities, what I see is a fragmented market and sub-critical business models (freemium without alternative, advertisement a must and quality content an accessory).

Conclusion

  1. My Android phone needs a jailbreak. Ironically I never had much of a need to jailbreak an iOS device (so I didn’t). With an open OS, every manufacturer and operator along the chain can push whatever content they please until this gets delivered to the end user, all neat and frozen. Clearly Google took precious little steps into making sure that these guys wouldn’t spoil the fun (but hey, it’s not GNU. Ain’t you tired of all the angel company buzz anyways?). I need a jailbreak just so I can buy apps(!). I need a jailbreak to get rid of the krapps. Heck I might even install an iOS emulator.
  2. Google aren’t interested in selling apps. The (Apple) App Store, the submission process, the presumption of censorship and partiality, that’s all work put into creating a store that’s inviting and engaging – for the worse, and definitely for the better.
    The Android marketplace owes it’s momentum to the App Store. Without the latter, it might take substantial advertisement budgets and a complex network of publishers and distributors to sell apps on Android. In other words apps will get more expensive, 90% of app users will use a cracked version and indies will go back to the day job (Bye bye to the buzzing and vibrant community of happy-go-lucky developers that brought you Angry birds, Doodle Jump and Tiny Wings).
  3. If you want an affordable, modern handset and don’t wanna be appy, an Android phone is no worse than an iPhone, and a lot cheaper if you shrug off the latest offerings.

Epilogue

For high quality adult content, get a subscription with an established provider and watch it on your iPad. For anything more interactive the real world may occasionally offer a more tangible (tactile, even) experience. Anyway who needs to jerk off on the go? In parks? On the underground? In the middle of a sodding meeting?

Even in the 22nd century, you’ll be shamed when your android gets all over you in the street, purring in a moist, digital voice.

I haven’t worried about device orientation in a while. Supporting a variety of orientations is *nice*.

On iPad, supporting all orientations is strongly recommended; not supporting ‘upside down’ variants of your orientation is rarely tolerated(*). Unless you have a good reason not to (e.g. accelerometer freak app).

So I thought I’d rehearse a little. I learned new things and discovered an iOS 4.0 cherry that helps avoiding orientation caveats: UIWindow’s rootViewController property.

(*) Keep safe from harm.

How to support device orientation changes?

  • Edit the info.plist file for your target
  • Your views should be managed by view controllers. You can do without view controllers but then you’re pretty much on your own although at least there’s a system callback to tell you the orientation changed (seriously, you can find it yourself).
  • Your view controllers should override shouldAutorotateToInterfaceOrientation.
  • Your nib files should be nicely configured so your widgets won’t fly around in a happy mess when rotated.
  • Know more about how views are managed, and how view controllers relate to the key window (see caveats)

info.plist

Specify supported orientations in the info.plist for your target:

  1. Select your project in the navigator
  2. Select your target
  3. Press/Depress orientation toggles in the summary tab (big icons showing rotated devices).

view controller

Override this and return YES to your supported orientations:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

interfaceOrientation may take one of the following values:

UIDeviceOrientationLandscapeLeft, UIDeviceOrientationLandscapeRight, UIDeviceOrientationPortrait,

UIDeviceOrientationPortraitUpsideDown

Sadly this appears to have a life of its own. Setting the info.plist property doesn’t further restrict a view controller’s response to orientation changes. In other words your plist may say that you only support landscape left/right. If a VC returns yes to all orientations, this won’t prevent its views from rotating.

Configure your nib files

A savvy approach to managing rotation is to create documents that look good both in portrait and landscape mode. It is possible to use different nib files for different orientations (not covering this here).

  • In the attributes inspector for your document’s main view, set ‘Autoresize subviews’ to true.
  • In the size inspector, use the autosizing toggles to determine what should happen to each element when the view rotates. You (or your UI designer) should do this for each element in the UI, deciding whether that element should size to fit / use a fixed size, and how to anchor the element (left, right, top-left, bottom, …).
  • You can toggle the current ‘simulated’ orientation of a view using the orientation setting in the attributes inspector.

You can remove/resize elements programmatically (check Apple docs as referred below).

Caveats

In a worse case scenario, changing device orientation will do little other than moving the status bar (if there is one). Two possibilities:

  1. Your view controller isn’t notified of the change, so it can’t rotate its view.
  2. Your view isn’t managed by a view controller.

Read about custom view controllers (link to SDK docs). Under “Presenting a view controller’s view”:

” [...] use only the suggested techniques for displaying the views of your view controllers. In order to present and manage views properly, the system makes a note of each view (and its associated view controller) that you display directly or indirectly. [...] When the device orientation changes, a window uses this information to [notify] the frontmost view controller. If you incorporate a view [...] by other means (by adding it as a subview to some other view perhaps), the system assumes you want to manage the view yourself [...]“

And this, under “Understanding the rotation process”:

“[...] the window object does much of the work associated with changing the current orientation. [...] Specifically, it works with the view controller whose root view was most recently added to, or presented in, the window.”

Only one view controller will receive the event, and propagate it to it’s (unique) view (and the underlying hierarchy).

Note: if you search for UIVviewController in XCode docs, also read the lovely article: “Why won’t my UIViewController rotate with the device”.

Common Solutions

In my experience, using several view controllers and adding their views to the main window is error prone. Pity, because it feels like a natural, no-nonsense approach to displaying views (but see the nice solution, below).

  • Use a unique view controller. Doesn’t sound great but if you have a simple app, at least it’s safe.
  • Display the view controller’s view modally. This often makes sense since a view controller is meant to manage one screen’s worth of content. IMHO the downside is that system semantics are associated with modal transitions, and misinterpreting how these transitions should be used may hurt you. For example, you may be struck by lightning.
  • Use a navigation controller, a tab bar interface or a popover.
  • Hacking a controller’s view down the hierarchy of the controller that receives orientation events. Ugly. However, note that hiding and showing views is a lot faster than adding and removing views. On older devices this may affect the user experience.

A nice solution

I wasn’t desperately happy with *any* of the above. Following the ‘one controller manages one screen’s worth of content’ idea, it would make sense if…

  • APIs prevented us from adding several views to the main window.
  • Any view added to the main window must be managed by a view controller.
  • We could actually tell the window which controller it should be working with.

In IOS4.0 and later, there is a public, documented property that works with UIWindow. You can assign a window’s rootViewController property. This does a lot of work underhand:

  • Remove the existing view hierarchy displayed by the window
  • Add the new root controller’s view hierarchy to the window
  • Make the VC ‘frontmost’. That VC will now receive notifications when the orientation changes.

I tried this and it works like a charm. I seemed less lucky when combining [window addSubview:] with the root view controller property, but maybe it can still work.

Resizing OpenGL surfaces

EAGLView is a utility class for displaying OpenGL content. You can find it in iOS SDK samples and it is often used as a starting point for OpenGL applications.

When an instance of EAGLView is rotated, it calls its [layoutSubviews] method. If you created your EAGLView programmatically, remember to set the autoresizing mask, as follows:

view.autoresizingMask=UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;

Otherwise your EAGLView won’t resize, and [layoutSubviews] is never called.

layoutSubviews calls this onto another utility class, aka ‘renderer’ (typically, ES1/ES2Renderer).

[renderer resizeFromLayer: (CAEAGLLayer*) self.layer];

The sample code provided in ES1/ES2Renderer resizes the underlying buffers correctly, so you could use it as a starting point. I get wimpy when it comes to buffers, but I still managed to get it right using the following ideas:

  • The color buffer is resized from the CAEAGLLayer.
  • We then retrieve the backing width/height from that same buffer.
  • Other buffers (depth, MSAA etc…) can be reallocated using whatever method we already know (remember to use the updated width/height).

This post is password protected. To view it please enter your password below:


What if we just wanted to share a little bit of code?
What if we where extremely lazy and didn’t want to learn about XCode 4 workspaces?
What if we didn’t want to create and maintain static libraries?

What if we wanted a solution that’s not even scalable, but does the trick while we bide our time?

And finally, what if we had a bunch of similar products based on a budding ‘framework’. Then we might decide to be evil, and dump all of our products in the same XCode project. Yay.

I decided to have a try. Until I hire another programmer, every day of my life is a Sunday. I found that cloning targets is easy, and just a little fiddling will take us from cloning targets to cloning apps and mutating them into something new.

Creating new targets isn’t hard, but then we have all these files we want to add to the new target. That should be easy as pie, but it’s not.

You can try this on a Saturday too, unless you’re working overtime.

1. Duplicating targets

(right click on an existing target, select ‘duplicate’ and follow the signposts).

If two products (an existing one and a new one we’re about to make) are rather similar, duplicating targets may be the fastest way to setup the new product. It doesn’t duplicate any code or resource. Initially both targets use exactly the same stuff. To ‘fork’ a new product, we then duplicate the resources that ‘aren’t the same’.

The big advantage of this approach is that all existing resources are added to the target right away. So this approach is easy if somebody bothers defining a ‘product template’ that contain little / no product specific stuff.

But first and foremost…

We don’t want the same info.plist, right?

Correct. If we use the same info.plist, we can still have two different products on the desktop, but we won’t have different products in-store. A lot of the meta-data distinguishing a ‘product’ (something users can eventually download) from another is the metadata in the plist.

The fastest way I found to actually clone the plist file is to do it from the desktop (sigh) and adjust the Info.plist File setting in Build settings.

Why my ‘target names’ don’t update in the build selection drop-down? Why do I still see an entry there after deleting a target?

At this point I had created, deleted and renamed several targets. My ‘build selection drop-down’ ignored whatever name change or deletion I carried out.

After a few minutes of deep anxiety, I realized that the build thingy doesn’t directly relate to targets. At the bottom of the drop-down thing XCode 4 shows 3 options: Edit Scheme, Add Scheme, Manage Schemes.

  • After deleting targets, delete the associated schemes (go to ‘manage schemes’).
  • After renaming targets, delete all schemes (go to ‘manage schemes’) then press the top right button (‘auto create schemes now’).

2. Setting up a new target

Setting up a new target and adding resources to it is the slower, meaner way to (loosely speaking) ‘clone an existing product’:

  1. Go to File > New > New Target.
  2. Choose Window-based application (or whatever you please)
  3. Select a product name and fill in the company id.
  4. Leave ‘include Unit Tests’ checked (no need to be THAT evil)
  5. Press finish.

The sad point here is that no existing files are included in the new target (other than the boiler plate generated by the wizard) and XCode 4 doesn’t provide much of a nice, quick and easy way to add all these files we want to share across targets. We can select several files at once and edit their target setting in the file inspector, but we can only do this for files that belong to the same group.

3. to share or not to share…

There are several types of resources we might want to share across products:

  • Code
  • Frameworks
  • XIB files
  • Data files
  • Image files

If you use the ‘target clone’ method, at some point you definitely want to introduce these classes that make your app what it is (a new app! with some resources and code borrowed from the old one). Then comes the ‘forking’ question:

  • Do the targets use the same MainWindow.xib?
  • Do they use the same app delegate

If the two targets use a different xib as entry point, it’s easy to fork (because the xib is listed in the info.plist, and surely the info.plist is forked).

Otherwise, the quick and dirty way is to use homonyms. So we may have two ‘MyAppDelegate’ classes (same name, different folder); and we associate one with each target. IMHO this is really evil, but it’s like chocolate. A tiny bit won’t hurt.

You could also check PrEV’s Demystifying app startup article. Knowing more about app startup can help us resolve certain questions pedantically.

I also found a post about renaming projects. I kinda managed it before, but maybe the post is useful.

Conclusion

Maintaining several apps in the same XCode project is possible. It doesn’t mean it’s a good idea. Learning how to do it is a moderately fun way to learn about targets in XCode.

Twilight Cherry

Back in 2010, our game got popular in iTunes Japan and I got curious about local titles. Shockingly, popular anime/manga serializations lie in the $15-$30 range; at this price, local publishers aren’t short of customers.

Need an example? Chaos Head Noah ($26) ranks #30 in top grossing adventure games and there’s hardly a reason to think it’s anything more than a semi interactive bunch of pics with voiceovers and subtitles. Now you think these ‘derived products’ couldn’t be successful without ip, brand, loads of content and a generous lite version, right?

Doujin developer m-kz put it to the test. They did away with the scary price tag and started rolling moe novels at ‘app store prices’. For just a buck, Twilight Cherry lightly carries the glamor, emotion and humor characteristic of bishoujo mangas, and does it pretty well. 12 months after its initial release, Twilight Cherry carries on merrily in the JP store, proving that ‘moe for less’ is a reasonable option for an indie studio.

.

Lots of text

There was something indefinitely enticing about Twilight Cherry that motivated me to offer helping out with a polished english translation. I didn’t know what I was getting into when I started playing the game, let alone when I suggested fixing up the translation – not just it takes time, but it requires a reasonable dose of inspiration too! After a dozen sessions at my favorite coffee shop, lolled by sirupy A-pop and sore from typing, I eventually saw the end of it. A month later, m-kz rolled the update and voila! (app store link).

Technology

Long before Apple officially lifted the ban on scripted apps, japanese studios were already using markup solutions to get the job done. Twilight Cherry uses the Artemis engine, a cross platform solution running windows and iOS. I didn’t get awfully deep into it, but among interesting things it would seem that even smileys can be used to control the app. WYSIWYG? On the downside, files that combine the original text with markup and translations are fairly hard to read and I would feel sorry to lend them to translators without a technical background.

After a short break in Paris, I’m finding my game physics module where I left it: unfinished. I’m afraid this post isn’t all too informative, but I’ve renewed my commitment to keeping this blog a developer’s diary…

PhysicalModel, for all it’s worth…

I tried to have a look at how PhysicalModel could be implemented. The idea behind PhysicalModel was to have a protocol giving access to physical bodies and/or colliders. This uses a well known pattern: instead of adding the items we want to process to a list or set, we have a protocol specifying how the items are being accessed (a kind of iterator). But frankly I doubt whether the pattern is relevant here. For the sake of efficiency we’d probably allocate an old style array of pointers and iterate that instead, because it’s much faster. For the sake of simplicity, we’d just use an NSArray.

What partakes the physical model?

How do I initialize and maintain the content of PhysicalModel? Nothing explicitly specifies the content of PhysicalModel. We have candidates, but none of these actually describes the model the way I want it to be:

  • I have the cast (the list of actors). To keep things simple i’d like every actor to be modeled as a cylinder.
  • Then we have the terrain. I’d like every platform to be modeled as a special physical body with detailed processing (on a per face basis)
  • Finally, there are a number of elements present in the scenegraph that I would model as obstacles. For these, cylinder/spherical bounds may be enough.

Provisionally, I decided to rebuild the model at the beginning of each chapter. The physical model might need additional maintenance – for example, if actors are added/removed while playing a chapter – but it seems like a decent start. I have a utility class in charge of (re)building the model, looking like this:

@class AntistarCast,WalkableScene,PhysicalModel;

@interface AntistarPhysicalSystemBuilder : NSObject {

}

+(void)buildUsingCast:(AntistarCast*)cast

terrain:(WalkableScene*)terrain

target:(PhysicalModel*)model;

@end

All we have to do to build the model is iterate the terrain/cast and generate physical bodies for matching elements.

Simplifying the model

I had Collider (something other objects can collide with) and PhysicalTarget (can be affected by collisions) protocols. I got rid of that and only kept PhysicalBody. As a result, processing is simplified – each PhysicalBody instance is tested against all other instances for overlaps.

Whether we use separate interfaces or not, we can still reduce overheads - only moving objects need to be tested for collisions. So maybe the distinction between colliders and targets isn’t very useful at this point.

In this post I am exploring a lazy, (somehow) entertaining method to do path-finding.

The idea of this can be captured simply:

Walking randomly will take us anywhere

If an actor is walking randomly on a terrain, no matter where they actually mean to go, they will eventually get there – whether the target is moving or not. A more interesting variant of the above involves stigmergy. An example of stigmergy is provided by ants dropping a ‘scent’ on the ground for other ants to follow. In this case the environment is used by agents to share information, which is pretty much what stigmergy is about(*).

Pathfinding using particles

I am not suggesting actors should be roaming randomly, however. Instead, here’s the idea:

  • Actors and items (targets) function as particle emitters
  • Particles travel randomly onto a navmesh (this could also work with waypoint interpolation)
  • Whenever a particle reaches a new site (another triangle on the nav-mesh), its age is increased.
  • To reach a target, agents follow particles ‘back to the source’.

I have been thinking about this for a while and tried to simplify it in order to avoid overheads. For example, we could actually keep track of the distance from a particle to the source, or integrate signals (that is, two particles referring the same target cannot co-exist on the same site); or a particle could maintain a ‘history’ of visited locations. All this stuff would require more memory, more reallocations and more development time, so until we have a good reason to add this or that (e.g.: we actually believe this will somehow benefit in some way), there’s no reason to worry about this or that.

Interfaces

Before going further, it’s probably useful to figure a few interfaces that clients will use to interact with the path-finding system.

  • SPListener is used by a client to observe particles from an agent’s neighborhood. The interface identifies the type (the kind of object detected, e.g. bread, a flower, a rat etc…) of the particle and it’s source – agents only receive signals from their immediate neighborhood on the navmesh.
  • SPEmitter is implemented by clients that act as sources. A registered emitter can return it’s location on the navmesh and it’s type, so we can generate particles from that location.
  • SPNetModel is implemented by the navmesh (or a wrapper) and is used as ‘propagation model’. In other words, it is used to retrieve the neighbors of a given site on the mesh. It is also used to retrieve listeners local to a site.

Finally SPSystem allows clients to perform the following actions:

  • Register an emitter
  • Update particles
  • Configure the number of particles maintained by the system

System behavior

At each iteration, the system will perform the following operations:

  1. Generate new particles. Typically new particles will override older particles, and this happens at a set rate.
  2. Update all particles. Each particle will be moved to a new (randomly selected), nearby site and it’s age is increased by 1.
  3. Notify listeners. Every time a particle has reached a new site, listeners interested at the same location will be notified.

What’s the point of doing all this when we already have A*?

Compared to regular path-finding, this method probably has disadvantages. For one, it hardly warrants an agent will use the shortest route to reach a target. However, it does have a interesting features:

  • We directly control the balance between resource allocation and accuracy. By controlling the number of particles and the frequency of updates, we control memory allocation and CPU overheads.
  • We don’t need to worry about the number of agents or targets, or how often agents need to use path-finding. The overhead of notifying agents is marginal. Adding emitters doesn’t directly increase processing overheads.
  • Performance degrades gracefully. We may lose in accuracy and liveliness if we under-allocate resources, but at least there’ll be something going on with agents roaming around and homing onto their targets ‘more or less randomly’. So we can scale down overheads on lower performance devices, or temporarily reduce update rates if we want to allocate more resources to, say, rendering, or something else.

(*)This method has been used in games for quite a long time. Recording the PC’s ‘track’ on the terrain may be one of the oldest, cheapest and most reliable ways to do path-finding.

I’ve collected a little information from various app stores to find out how much our players love Antistar.

Unfortunately raw data isn’t terribly useful in this case because the average rating across all apps easily varies +/- one star per country. Ratings in Japan are typically lower than in other countries, and often disconnected from app rankings. In the US, ratings are typically higher and there is usually a correlation between rankings and ratings in the top 200.

We can discount localization issues as the app has been translated in most languages represented on this list.

  • 3.5 : Germany (17 ratings)
  • 3.0: US (185 ratings)
  • 3.0: Canada (13 ratings)
  • 3.0 : China (13 ratings)
  • 2.5: Japan (48 ratings)
  • 2.0: UK (17 ratings)

The percentage of players that will rate an app after downloading it varies by country as well – not just a little bit.

  • 1.9% Germany, UK
  • 1.4% US
  • 1.2% Canada
  • 0.9% China
  • 0.6% Japan

Somehow I feel this blog is about to lose it’s raison d’etre and I wish to set things right.

The OOgtech blog started as an online scratch-book. Explaining this stuff to a hypothetical audience while doing it felt good. Maybe I’m not so sure if the audience is hypothetical anymore, and I get worried about writing silly/contradictory stuff, or whatever…

But that’s not the way it works. The way it works is, some articles might be useful, and the rest of it should remain as it used to be. Raw.

If you happen to read this post, In short I’d like to remind you that this blog has no commitment to being especially authoritative. I’m discovering a lot of things as I go along and the original idea was to capture this process – as our tutor used to say:

You are confused? Good. If you are confused, it means you’re learning.

Either way. I’m not especially confused at the moment. Bored, rather. The technical requirements towards the next Antistar release are heavy, and I somehow fail to appreciate how much work has been done already. Without the blog, it all gets lost in a blur.

Off I go.

After a little down time – 1 week maybe ? – I looked up the Antistar task-list and… decided to put it aside. The game starts and the basic controls are working. I improved the design of the code, not as much as I’d like, yet improved still. But there’s no point in dragging bugs that are two months old when you expect unrelated regressions and, overall, don’t really know where you stand anymore.

Test coverage is still low I suppose, but it’s improving too. So I’ll just go back to stage 1, play test, try to clean everything up, and move on.

It doesn’t matter one bit which end I want to take it from. The only thing that matters is everything needs to work as intended, and I can’t afford writing ugly code any more.

Sigh…