Google Play Data Safety

Some developers are maintaining a list of the data types required by various SDKs, available here, which is useful to figure out how to fill out the Google Play Data Safety checklist for the SDKs your app uses. I’ve put together a website to collate the data into a more usable format. Select the relevant SDKs and it will aggregate all Data Types that are required by your app, and their purpose, so you can enter it directly into Google Play.

Webapp is available here

It’s not very pretty, feel free to rip off the code and make a better version. It can also get confused by some fields that don’t match correctly in the source data, and might split Data Types which should be combined.


All Unity EditorStyles

Example of all EditorStyles
Examples of all options within EditorStyles

Reference image for the properties in EditorStyles when making custom inspectors.

Used the following code to generate it:

var width = EditorGUILayout.GetControlRect(GUILayout.Height(0f)).width;
foreach (var style in typeof(EditorStyles).GetProperties()) {
    if (style.GetValue(null) is GUIStyle guistyle) {
        var content = new GUIContent(style.Name);
        var rect = EditorGUILayout.GetControlRect(false, guistyle.CalcHeight(content, width), guistyle);
        GUI.Box(rect, content, guistyle);

Generating SDF (Signed Distance Fields) in Unity

I’ve built a package for generating distance fields from texture masks in Unity. It works in several modes:

A) Append “+sdff” to the end of a filename to generate an SDF from its alpha mask. Use “+sdff4” if you pack mask data into all 4 channels (RGBA). Drop the second ‘f’ to stop the importer from overwriting format settings. The SDF is generated from the source texture file, the intention is to have large mask textures and use “Max Size” to reduce the texture size after importing as an SDF.

B) Or use an SDF Generator asset, which will give you some more options. Right-click in your project window, select Create->2D->SDF Generator, then drag a texture into the “Targets” list, then hit “Generate”. The SDF variants will be created in the same folder as the source assets (with “.sdf” appended to their names).

Package can be downloaded from here (free)

Its using JFA (Jump Flood Algorithm) which is not completely accurate, but covers almost all cases and can generate almost instantly. There’s a decent visualisation for how the algorithm works here.

Q) “My SDF textures look noisy”
A) Make sure you are not using texture compression.

Q) “These textures are too big”
A) Use “Max Size” to reduce the size.

Q) “The textures look too blurry”
A) You’ll need to use an SDF-aware shader (or just enable alpha clipping). A shader is included with some Text Mesh Pro -like functionality under “Shaders/Sprites/SDFDisplay” and a material you can use for sprites is included named “Default SDF Material”.

Q) “The texture edges are aliased”
A) Switch to a shader that supports SDFs. If you already are, try adjusting the “Gradient Size” when generating your SDF, or the “Gradient Scale” in the SDFDisplay material to tweak the sharpness.

Q) “Drop shadows are being cut off”
A) You’ll need to implement your own SpriteRenderer / Image component which inflates the quad used to render your sprite, so there is space for the full shadow.

Q) “My texture has become grayscale”
A) Remove the second ‘f’ from the filename, so that it is just “+sdf”, and then change the Texture Format to “RGBA 32 bit” in the asset import settings (or Automatic if you can put up with some compression artifacts)

RTS4 Update 4

I had to move house so had less time to code, but this month I focused on bug fixing, cleaning up the code, and improving the visuals. I’ve also switched to my own pathfinding solution which can (most of the time) avoid the grid zig-zag effect with very little extra cost, but it’s incomplete and can break in situations (when more than 1 unit tries to use the same navigation result).

RTS4 Update 4; new building models, procedural foliage

The terrain supports cliffs and will cut the mesh (using a geometry shader) and insert cliff geometry to fill the gaps. A compute shader procedurally places foliage instances based on the terrain type. I bought the Toony Tiny RTS Set and switched the buildings out, they overlap with the AOM buildings surprisingly well except that the Town Centre is actually a blacksmith and many of them are too tall. (Note: These will be replaced with something less cartoony eventually)

I’ve more closely replicated the AOM camera angle, but after seeing the AOE4 gameplay trailer, I’d like to try a wider FOV.

Standard RTS Camera FOV (15° FOV 125m distance)

I was hoping to get a public WebGL build up, but many of the recent changes have made the game incompatible with WebGL, so I’ll need to disable them or build fallbacks first.

RTS4 Update 3

We’re 1 month in on the project reboot! I’ve fixed up the lobby UI and switched multiplayer to a relay server (having trouble with UDP Punchthrough on Azure).

In the game above, the two players desync after the Pegasus is trained. This has been fixed now, but it was caused by the “cheat” command (to instantly give a player resources) which I used for favor, did not serialize its data, so didn’t execute correctly on the other client.

The game has also been more heavily optimised so that it will run in WebGL, and I’ve been working on simplifying some of the internal simulation logic (ie. so that all things that deliver resources to players use a unified system).

RTS4 Update 2

Over the last week I’ve added LOS, D*Lite pathfinding, hitpoint bars, upgrades work (but only ones which affect prerequisites), and the UI has been updated with better placeholder graphics (the same used in the previous RTS4 attempt).

The random maps still generate incorrectly. Forests seem to fail randomly, but breakpointing in the code suddenly makes them work again.

NAT punchthrough looks a lot simpler than expected, so I’d like to try that next, along with some of the server-side player profile management and game listing.

RTS4 Attempt 3

I’ve restarted the RTS4 project and made a lot of progress very quickly thanks to pulling in existing code from the previous attempts. It’s not quite up to where I was in the previous attempt but getting close.

Currently the following is implemented:

  • Parsing the AOM format of proto.xml, terraintypes.xml, clifftypes.xml, forest.xml, waterbodies.xml (partial), techtree.xml
  • Parsing and executing random map .xs files
  • Primary unit actions (Move, Gather, GatherDrop, Attack, Build)
  • Building placement
  • Networking (only direct IP, no punch-through)

To speed up development, I’ve pulled in code for the following

  • Parser logic comes from ReGE (PhD project)
  • XScript runtime is (a modified version of) what was used in XNA RTS4
  • Math and Networking is based on that used in Unity RTS4
  • Simulation has been completely recoded and is implemented in Unity ECS

It’s not much to look at, but here is how it looks currently

There is currently no pathfinding and technologies cannot be activated, and there are several bugs. Food amount starts as -1. Ranged attacks work but are instant and do not use projectiles, and all attacks work off of the global clock instead of per-unit timers. Something is wrong with random map constraints and water decorations (purple boxes) are being spawned in base, and the starting forest rarely spawns correctly.

(Started 20 Feb 2020)

Minimum code for Android Alert Dialog Box in Unity

This was difficult to find, so here it is; the smallest snippet of code to present an AlertDialog (with OK button callback) to the user:

private class OnClickListener : AndroidJavaProxy {
    public readonly Action Callback;
    public OnClickListener(Action callback) : base("android.content.DialogInterface$OnClickListener") {
        Callback = callback;
    public void onClick(AndroidJavaObject dialog, int id) {

public override void ShowAlert(string title, string content) {
    AndroidJavaObject activity = null;
    using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
        activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
    activity.Call("runOnUiThread", new AndroidJavaRunnable(() => {
        AndroidJavaObject dialog = null;
        using (AndroidJavaObject builder = new AndroidJavaObject("$Builder", activity)) {
            builder.Call<AndroidJavaObject>("setTitle", title).Dispose();
            builder.Call<AndroidJavaObject>("setMessage", content).Dispose();
            builder.Call<AndroidJavaObject>("setPositiveButton", "OK", new OnClickListener(() => {
                Debug.Log("Button pressed");
            dialog = builder.Call<AndroidJavaObject>("create");

Note: I’m assuming all AndroidJavaObject objects need to be disposed (even when the same instance is returned in the case of the Builder), but that may not be the case.

You can remove the OnClickListener class and pass null as the final parameter for setPositiveButton if you do not need a callback.

BAR Extractor improvements

Texture support was (possibly) fixed for BTX (and DDT support added) to the BAR extractor. Just drag a BAR file into the page and it’ll load up thumbnails for all images within. I’m using it to look through the Age of Mythology files; I dont have Age of Empires 3 installed, but it should work with that too.


Visit here to use the tool.