Monday, September 30, 2019

Early Results from Google Play Experiment.

I have some early results to share about my Google Play Experiment. To recap: I released a game that requires Android 9+, hence is available for a mere 10% of the devices that use Google Play. So, is store-traffic reduced with -90% as well?

I was baffled to see that store traffic was flat out zero. Worse than that, the title will not show up at all, no matter how you search for it! I have created an un-searchable title!

First order of business, let's verify whether the title actually exists on the store, using the link from the Google Play Developer Console, here:

Ah, phew... it is actually there!

Now, let's search for it in Google Play using it's exact (longish) title:

I guess I should be happy that the first hit is actually on one of my own apps, and not a competitor. But still... the game is not in the search results. It returns 249 results in total, by the way.

Ok, maybe the fans of my older titles will find it by checking out the store page on my company? Let's go to my other game, and click on the "Game Studio Abraham Stolk Inc." link.

Even here, it is not present. Ok, a title that has zero links pointing at it from the Google Play store it self. This is going to be tricky. I need to leverage the existing customer base. So I put an advertisement for Little Crane 2 in my free game 'Six Wheels and a Gun.'

And yes! A big thank you to my first customer from Switzerland. I appreciate your support. So the store is working: you can buy Little Crane 2: Mud Play.

I celebrate every sale. That customer rocks. But hey... wait a minute.... what is that?

Der Nachfolger von "The Little Crane That Could", mit dem Sie im Schlamm graben k├Ânnen.

A German translation of the store page? I never added any translations. The developer console also lets you purchase translations, but I never did that. Who translated this? To me, it seems good enough to be a human translation, not a machine one, by the way.

So there we have it, ladies and gentlemen... A title on Google Play that you cannot search for and cannot find without a direct link taken from the developer console. But somehow, it has been translated.

UPDATE:
I complained to Google. It took them a few weeks, but they have half-way fixed it now. It is now possible to get "Little Crane 2: Mud Play" back as a search result. Sadly, only as the 5th title, even when searching the exact name. But hey, at least it is returned, and listed on my portfolio page.

Wednesday, September 25, 2019

Google Play Experiment.

In 2015, I released a successor to The Little Crane That Could on Steam for Windows, Linux, MacOS.

In the sequel, I wanted to do something special, and decided to make the terrain deformable. This means, you can dig into the terrain, anywhere, and deposit soil anywhere. And not just constrained to height-maps, but a real full 3D terrain that includes caves, overhangs, and such.

An ambitious undertaking, but I think it got there. Being on desktop, instead of mobile, meant that there was more CPU power available. The collision of all those dirt particles against a dynamic terrain is far from cheap.

After publishing the Windows/Linux/Mac versions, I decided to push the iOS hardware and see if the latest iOS devices were up to the task. To my surprise, iOS CPUs were surprisingly powerful, and able to pull it off (although often below 60fps, or even 30fps, but the 2015 devices were able to run it.)

Back then I also tried a quick Android port, but the average Android device was trailing so far behind Apple's performance (floating point calculations), that I could not publish it there. An Android tablet would just be bogged down, unable to compute the simulation at interactive rates.

Now, skip to 2019. Four years later, the Android devices are roughly where iOS devices were in 2015, when it comes to running my (OpenDE based) soil simulation. It is time to revisit the Android port!

For the Android port, I decided to do an experimental release. Since I can only afford to run on fast devices, I thought I would cut down massively on supported devices. For starters, 64 bit ARM only. And I will publish for Android version 9 and newer (which is just a 10% marketshare.) To top it off, only for devices with ES3.1 and higher.

One of the questions I want to answer with this experiment is how Google Play would treat such a title. Let's say Google Play decides to promote your title in the "Similar Titles" section on the store. And let's say, the Google Play store algorithm decides to grant you 1000 slots, or views. Will it simply drop 900 of those slots for customers on incompatible devices? Or will it keep trying to find customers with compatible devices to show your app to?

Or in short.... by reducing your compatibility to 10% of the devices, will you reduce the in-store visibility to 10% of its potential, as well? I honestly do not know. And I don't even know if I can reliably measure it? I guess compare store-stats with my other apps? Or after a while, add Android-8 support and see if store hits increase?

UPDATE: It has been released. But somehow, it is impossible to search for it? It will not even show up under the publisher page of Game Studio Abraham Stolk Inc. This is weird.

Tuesday, September 17, 2019

Sanitizing addresses of your Android NDK application.

In my attempt to squash all bugs, I ran my Android app with clang's address sanitizer. Because the steps are not trivial, I thought I would documents the process here.

This recipe makes some assumptions, though:

  • I'm on Linux.
  • I use AndroidStudio with CMake.
  • I run on the Android emulator. (A real device shouldn't be much different.)

First, we need to pass the -fsanitize=address flag to both the compiler and the linker. To compile your C/C++ code with this flag, use this in your CMakefile

target_compile_options(foo PRIVATE
        "$<$<CONFIG:DEBUG>:-fsanitize=address>"
)

And to link, add "-fsanitize=address" to your target_link_libraries.

Ok, now your shared-object has been built with the address sanitizer. But if you try to run it, you will see: dlopen failed: library "libclang_rt.asan-i686-android.so" not found.

Luckily, the Android NDK comes with the correct libraries, you just need to package them with your build. First locate where this library is on your system, doing:

$ locate libclang_rt.asan-i686-android.so
/home/bram/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/8.0.7/lib/linux/libclang_rt.asan-i686-android.so

You simply need to copy this alongside your own shared object file, as generated by cmake. In my case, it goes in: ./app/build/intermediates/cmake/debug/obj/x86/libclang_rt.asan-i686-android.so

If you now build and run your app on the emulator, your addresses will get sanitized. In my first run, it found a free() of a pointer that was never malloc()-ed in the Google Play Games SDK.

Note that if you want to do this on a ARM device, you would need libclang_rt.asan-arm-android.so and on a 64-bit ARM device, look for libclang_rt.asan-aarch64-android.so instead.

Thursday, September 12, 2019

Keeping user directory names out of your binary.

So, if you use ASSERT macros a lot, you typically want to point to the offending line of code, and use __LINE__ and __BASE_FILE__ macros for that.

Now, where it gets tricky: VisualC does not have __BASE_FILE__ so what I did was use __FILE__ instead. But that has the nasty side effect of putting your home directory in your build, like so: C:\Users\bram\src\.. which is bad practice.

Stackoverflow.com to the rescue. I found this neat solution for it.



UPDATE

Gregory warned about this affecting parallel builds. Personally, I do see all cores used for my single project solution, but your mileage may vary. An alternate approach would be to ditch VisualC compiler, and use clang instead.

Monday, September 2, 2019

High volume, low friction art work for video games.

I am a one-man indie game studio (I like to use the term INDIEvidual.) The lack of employees means that I need to shoulder all the tasks, including art work. With so many tasks, I can not afford to spend a lot of time on it. Here are some tricks to make it manageable.

So many pictograms!

One tedious type of Art related tasks, is creating pictograms for Achievements, or Leader-boards. Here you see some (very minimal) pictograms for four such achievements. To illustrate the volume of work involved, imagine that the game has 100 achievements. How is a part-time artists supposed to generate the 200 images required for this?

It starts with the right Art style. Steam wants these in 64x64 pixels, and believe me, you do not want to do this art as pixel-art in Photoshop. Instead, it will have to be vector art, which is rendered at the required 64x64 resolution. Additionally, this will have to be low-poly vector art. I also chose a two-colour palette to reduce the work even more.

Ok, fine, now it comes down to generate vector images in low-poly style, and just two colours. Massively easier, but still a crazy amount of work.

This requires a ruthlessly reduced work-flow.

Open Source to the rescue, and more specifically, Inkscape to the rescue.

With Inkscape, we can put all our pictograms in just one SVG file, each pictogram in a separate layer. Never opening/closing/copying files, just work in that one achievements.svg file, and create layers.

With everything in one file, Inkscape will let you invoke an export to PNG from the command-line, and pick each layer in isolation for each export command. This is achieved with the --export-id= flag and the --export-id-only flag.

To identify each layer, we need to name the layer object, and set its ID. Unfortunately, renaming the layer is not enough, you need to use the XML-edit widget in Inkscape to actually set the ID of the layer object, as shown below.

Command Line Conversions

For the actual generation of the PNG files, we can leverage a UNIX-style Makefile like so:
IMAGES = \
 a_pentdrive.png \
 a_coaster.png \
 a_millennialbuilder.png \
 a_eagerlearner.png \

all: $(IMAGES)


%.png: achievements.svg
 inkscape --export-id=$(basename $@) --export-id-only --export-png=$@ --export-area-page --export-width=64 --export-height=64 $<
 convert -colorspace Gray $@ lo-$@

In this example, a single SVG file with 4 layers is used as input, and the Makefile will generate 4 pictograms as PNG, and 4 additional grey-scale pictograms to serve as the images for uncompleted achievements. Note that to generate the grey-scale, the Makefile uses the Imagemagick convert command.

Before adopting this workflow, I would export the PNGs using the Inkscape GUI. Sure, doable if you have 4 achievements, but now, with this streamlined workflow, I can pump out a 100 achievements, as 200 images, in a single day, if I have to. Just draw them all in a crude Art style, list them in the Makefile, and run a single make command. And now, the bottleneck (apart from the drawing of the pictograms) is the actual uploading to the steam portal, as I still have to do that as one image at a time.

Future Work

It would make sense to auto-generate the IMAGES list in the Makefile from the SVG itself. Just search the SVG for layer objects. I should also look into the Steam portal, if it allows me to batch-upload the pictograms somehow, without going through the WEB interface.