Unity: Networking Does Not Work in the Editor

This is an embarrassing post. Some days are there to simply remind you that you don’t really know what you are doing. I spent two days trying to track down why my networking scripts were not working when I ran them in the editor. Turns out that the editor makes it’s own internal network stack when running a project which does not connect to the normal ethernet ports on your machine.

Top Tip ! Build you project and run it natively if you are using TCP/IP connections ! Don’t try and run it in the editor (not even for quick checks or small simple projects) it simply won’t work.

The really nice thing is after I got it all working I built a touch screen controller for Android phones that can be used as input in a Unity game.

The code is in the repo: https://github.com/zuluonezero/AndroidTouchController

Unity: Show Grid Coordinates Scene View

I’ve been working with the FFTWindow analysing audio input and needed a quick way to view the coordinate space of the grid. I’m surprised there is not a feature in Unity to do this (at least not that I could find). This is a quick script to help with debugging coordinates in the Scene View.

Grid Line Coordinates on the XY plane

The script basically just draws a set of Axis in the scene and labels the x,y,z coordinates. It’s best when you are viewing along the alignment of an axis (I use the XYZ Gizmo in the corner to switch between them).

In 3D view with Cyan and Magenta sample lines.

You can toggle the Z plane on and off to make it easier to see just the XY plane and there is a few sample lines to show how it might be useful.

I was using to analyse audio spectrum data on the fly like this:

The blue Logarithmic representation of the PCM signal in red

Code

Here is the code for the grid lines/coordinates below:

It’s also up on GitHub: https://github.com/zuluonezero/UnityGrid

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class GridLines : MonoBehaviour
{
	public int X_Axis_Length = 10;
	public int Y_Axis_Length = 10;
	public bool Z_Plane = true;
	public int font_Size;
	public bool show_Test = true;
	
	private GUIStyle guiStyle = new GUIStyle(); //create a new variable
	public void OnDrawGizmos()
	{
		// Axis
		Gizmos.color = Color.red; // X
		Gizmos.DrawLine(new Vector3((X_Axis_Length / -1), 0, 0), new Vector3((X_Axis_Length), 0, 0));
		Gizmos.color = Color.green; // Y
		Gizmos.DrawLine(new Vector3(0, (Y_Axis_Length / -1), 0), new Vector3(0, (Y_Axis_Length), 0));
		Gizmos.color = Color.blue; // Y
		Gizmos.DrawLine(new Vector3(0, 0, (X_Axis_Length / -1)), new Vector3(0, 0, (X_Axis_Length)));  // assumes Z Axis is the same length as the X


		//Gizmos.DrawWireSphere(centre, Radius);
		guiStyle.fontSize = font_Size;
		int x = -X_Axis_Length;
		int y = -Y_Axis_Length;	
	
		while (x < X_Axis_Length)
		{

			Handles.Label(new Vector3(x, y, 0), (x + "," + y));
			
			while ( y < Y_Axis_Length)
			{
				Handles.Label(new Vector3(x, y, 0), (x + "," + y));
				y++;
			}
			 x++;
			 y = -Y_Axis_Length;
		}
		
		if (Z_Plane)
		{
			int z = -X_Axis_Length;	// Z Axis
			y = -Y_Axis_Length;	
	
			while (z < X_Axis_Length)  // Z Axis
			{

				Handles.Label(new Vector3(0, y, z), (z + "," + y));
				
				while ( y < Y_Axis_Length)
				{
					Handles.Label(new Vector3(0, y, z), (z + "," + y));
					y++;
				}
				z++;
				y = -Y_Axis_Length;
			}
		}
		
		if (show_Test)
		{
		// Test lines
		Gizmos.color = Color.cyan; 
		Gizmos.DrawLine(Vector3.zero, new Vector3(4, 5, 0));
		Gizmos.DrawWireSphere(Vector3.zero, 0.2f);
		Gizmos.DrawCube(new Vector3(4, 5, 0), new Vector3(0.2f, 0.2f, 0.2f));
		
		Gizmos.color = Color.magenta; 
		Gizmos.DrawLine(new Vector3(-3, 3, -3), new Vector3(2, 2, 2)); 
		Gizmos.DrawWireSphere(new Vector3(-3, 3, -3), 0.2f);
		Gizmos.DrawCube(new Vector3(2, 2, 2), new Vector3(0.2f, 0.2f, 0.2f));			
		}
	}
}

Unity: High CPU on Small Projects

Quick Tip: I have been working on a TCP/IP Networking project using a client/server architecture. The client (and the server for that matter) are both relatively small code bases and the UI and object count are really low in the scene. I had been struggling with CPU load in the project and feverishly trying to work out why my code was baking the CPU (and GPU!). I’d assumed it was something stupid I had done in a loop with the networking structures I was not that familiar with. It’s really not easy to concentrate on new code when your laptop fan is literally screaming at you! I’d hit Play and the CPU would spike almost immediately. So I would switch to my local terminal and scrape through the open ports and network connections looking for a smoking gun. Turns out it was the default frame rate in the Editor trying to deliver the fastest graphics performance it could on my PC – and with such a low object count and and very simple graphics being asked for it was running like a Formula One race car when all I wanted was an old jalopy.

This is my CPU on Speed

Solution: Set Target Frame Rate!

A Unity project will attempt to run your project as fast as possible. Frames will be rendered as quickly as they can (limited by your display device’s refresh rate).

There are two ways to control frame rate:

Application.targetFrameRate – controls the frame rate by specifying the number of frames your game tries to render per second. (I wrote a script to use this – see below).

QualitySettings.vSyncCount – specifies the number of screen refreshes to allow between frames. (look for it in the Editor Settings). For a 60Hz display, setting vSyncCount=2 will cause Unity to render at 30fps in sync with the display.

Note that mobile platforms ignore QualitySettings.vSyncCount and use Application.targetFrameRate to control the frame rate.

The default value of Application.targetFrameRate is -1. (the platform’s default target frame rate)

I set mine using the script to 20 and when I hit Play got this result:

This is my CPU chilling out
using UnityEngine;

public class SetFrameRate : MonoBehaviour
{		
		[SerializeField]	// Just so you can check it in the inspector
		private int FrameRate = 20; // 20 is really low but got my CPU down to < 10% - 30 is the target for mobile and was < 20% CPU usage
		//private int FrameRate = -1; // reset to default
	
		private void Awake()
		{
			Application.targetFrameRate = FrameRate;
		}
}

I attached it to my Camera object.

Set Frame Rate

One interesting behavior of setting this using a script in Unity 2020.3.26f1 was that once it was attached to the Camera object and Play was initiated for the first time it must have set the frame rate somewhere internally in the Engine. When I removed the script (for testing) the frame rate did not automatically reset to -1. I had to re-attach the script and update it to set the frame rate back to the default. I had a search of the settings in the Inspector and Preferences and couldn’t find a visible reference to it anywhere so you have to be careful if you are going to put this on a Production build that you reset it before releasing otherwise you might end up with a lower frame rate than what the platform could achieve by default.

Enough procrastination – back to sockets, ports and buffers.

Blender 2D Animation with Meshes

This is a follow on from the workflow discussed in the previous post: Preparing 2D Art for Animation.

This is the end result of the process described:

Sprightly Spring Deer

I’m looking to see if there are any advantages to using Blender as a 2D Animation tool using meshes over Unity’s Spline Sprite based animation system. The differences between them at the effort and usability/flexibility layer are many and subtle. Hence the investigation. The two biggest differences for me is that 1. With the Blender animation option you are animating in Blender (which I like much more than animating in Unity). But the down side is that you have to import the animations into Unity and it’s pretty hard to modify once they are there. Which also means that it’s harder to adjust them to react to other actors, objects, and scene elements once you get it into the game. 2. With the Blender approach it’s a mesh in Unity not a Sprite so you can do all the transforms that mesh’s support. You can also light it as a mesh (the default Sprite Renderer cannot be lit). Being able to use light effects on a 2D image within the game is pretty huge for making it look pretty and making effects or plot devices (think lightning on a dark and stormy night). You can get light effects on Sprites in Unity if you swap out the default shader with another shared and with the Light Weight Render Pipeline in Unity (LWRP) but not every project will suit that. There are also Unity solutions that use custom shaders or use a similar mesh and material based solution (see further below for more on that).

Comparing Unity Sprites to Blender Meshes in Unity

The images directly below are taken from the Game Screen in Unity. The one on the left is a Sprite based Spline rendering while the one on the right is the Mesh based fbx from Blender. You can see the difference in quality between the Sprite on the left and the lossy baked images of the Mesh on the right – it’s not huge and can be improved with some tweaking (Bilinear Filter mode and upping the Ansio Level to 2 helped with the anti-aliasing and working with the material Metallic and Smoothness parameters also helped).

Sprite (left) and Mesh (right)
Night Time lighting affects the Blender mesh image but not the Sprite based image.
Lighting effects can be much more complex and creatively arranged to hit separate parts of the mesh.

As stated above you can drop an image onto an object in Unity as a material but it doesn’t light as well and is prone to shadowing. Use the Cutout and not the Transparent Rendering Mode in Unity or you get this shadow on the transparency. The below image shows a material with a standard shader with an image on a Unity 2D plane mesh but there is a shaded square around the outside that marks the image boundary.

Transparency Shader

The image below is the same sprite using a material with a standard shader and a cutout rendering mode (the diffuse sprite shader worked similarly). The top one is a normal sprite renderer with the custom material replacing the default-sprite material. The bottom one is a Unity 2D Plane with the custom material applied. Both tests look better than the quality of the Blender imported model and could be layered and they react with lighting in game.

So these are the alternatives to the process I’m describing below with Blender and they are good and valid options. I guess the only reason why I would choose to use the Blender animation workflow is because I hate doing this process in Unity’s Animator window. Add Property | drill down through the object | the child | the other child | the bone | the transform | and finally the tiny little plus sign that let’s me add one manipulation point! For a Deer Kick I had 88 different animation points – that’s a LOT of stupid clicking down through an object hierarchy to add Properties (I know you can hold down shift and add more than one property at a time but you still have to manually expand them all). The other alternative is to right click and add all properties for an object and then if you are patient enough you can remove the one’s you don’t use.

I do like the record feature that adds properties dynamically but these problems and that I find the interface finicky and too small made me look at Blender.

Importing the Images to Blender and Setting up the Workspace

Moving on to working in Blender with images and Meshes the basic process is this:

  1. For every layer in the artwork of our animated character we exported a separate image file on a transparency. Each png file is imported into Blender as an empty image object (Add | Empty | Image) you could use a reference or background image but since all the parts might move I wanted to group them all under empties.
  2. A Mesh is created for each image and either shaped to the outline of the image or left as a plane and weighted correctly (more on that later).
  3. The image is baked into the UV of the mesh.
  4. The components are then parented to an Armature with automatic weights.
  5. The meshes are weight painted to correct the deforms.
  6. Now it’s ready for animation.

The image objects are all placed at the same origin (0, 0, 0) and rotated 90 degrees on the ‘x’ Axis so they are visible in the viewport from the “front” view.

All the Deer components Frankenstein’d together into a whole
The visibility of parts are toggled on and off so individual pieces can be worked on.

Making the Mesh’s

For each piece a mesh is made. I took two approaches here: 1. Model a plane mesh as closely as I could to the shape of the sprite. 1. Use a plain rectangular mesh and use weight painting to deform correctly.

To start with the modelling approach I started with an image and dragged a plane in edit mode over it as a wireframe. The origin of the plane was kept at 0, 0, 0 so all the pieces that were made had a common reference (same as all the images). Using basic mesh deform and subdivision I created a mesh that matched the image.

The foreleg Mesh

The method was a lot of work manually placing each vertex on the border of the image boundary. If the vertex is placed a little bit outside the image you get a white space on the final product and if you don’t come all the way to the edge you lose some of the black line and smooth finish (UV mapping is slightly out). Plus I found that if you have to warp the mesh too much for a sharp angle or awkward placing of the square tiling you get some minor defects along the line during animation.

Vertices placement

After about the fourth component I got a bit sick of manually moving around vertexes. So I took another approach of just using a rectangular mesh and relying on the transparency of the image to do all the work. This is much easier and faster but there were gotcha’s during adding the armature and weight painting. This rear leg below is just one big mesh subdivided into enough squares to give a decent deform without stretching or warping the black line during animation.

Venison

In Solid shading here is a comparison of the rear leg mesh and the front leg mesh.

Solid Mesh Planes

The image below is both meshes in Render mode (including the armature) and you really can’t tell the difference between them.

Rendered Meshes

The whole mesh ended up looking like this:

Armature and Weight Painting

As you can see above the armature was added and the Mesh objects were parented to it with automatic wights. Because everything is a flat plane of which some are meant to overlap the others (like the closest front leg is in front of the torso and the back leg is behind it) parenting the armature with automatic weights meant that both front, middle, and rear mesh’s would get an equal measure of weight in parts. This all had to be manually painted.

Here the Torso was weighted across three bones and only the rear was affecting the rump (any leg meshes had to be removed from these vertex groups).

Weights had to be carefully graded otherwise warping of the line would result:

The weight is too strong a transition here.
It causes artifacts like this.
This is the resulting gradient changes in weight to get a correctly deforming line.

The other problem was that random single or lone groups of vertices would be weighted to a bone and not visible until you moved it in pose mode:
A few vertices on the chest were registered to the root bone. These all have to be manually removed.

The other interesting anomaly with the large rectangular plane meshes was that the weight would sometimes cause improper warping of the mesh which bent it round itself in places and showed up as black squares.

The foot vertex group covers all these vertices.
Which you cannot tell in edit mode when you select it with “show weights”.
During transform in animation these black marks show where the mesh does not warp properly.
The mesh is a mess.
It’s because the shin bone weight doesn’t go all the way to the edge.
It looks right in edit mode.
But if you use the vertex group to select all the vertices it should look like this (all the way to the edge).

These are pretty quick things to fix really but it took a while to work out what exactly was happening. It’s was still faster than individually making all the mesh components by hand to fit the image.

Probably a better workflow would be to make reduced simpler meshes that fit closer to the image but don’t have to slavishly man handle the vertices around the borders.

The Shading

UV Mapping is totally easy here but getting the material right was a bit tricky with the transparencies and images. This is the setup I used:

The Transparent Shader in Blender

That’s about it for getting everything set up in Blender. For more info on the animation steps and getting it into Unity see my other post about this. https://www.zuluonezero.net/2021/11/16/exporting-multiple-animations-from-blender-to-unity/

Exporting Multiple Animations from Blender to Unity

This is one of those workflows that is always a bit fiddly to get right so I’ve documented how to do it here in case I forget! One of the downsides to being a solo developer is that your skillset is always being stretched by the available time so you can end up getting proficient in once aspect of game building and then by the time you get back to that phase you forget everything you’ve learned and all the tricks of efficiency and process. Also, in case someone else needs it.

This is what we are aiming for in Unity. An imported mesh with multiple animations being called independently.

Blender Workflow for Saving the Animations

Start with a new project. Select everything (the default cube and lamp) x –> delete.

In this case I’ve imported an existing fbx of a hand with supporting armature ready for animation. I won’t go over the modelling or rigging procedure there is plenty of help with that out there – but if you need it I would recommend the Riven Phoenix courses because they are so dense (these tutorials are no quick start or tricks videos but deep deep dives into the process and reasons behind it and how stuff works in Blender at a very technical level).

This is how I layout Blender for Animation with a dual screen front and right view with the Timeline below

Get your animation window set up and make sure the timeline is available at the bottom.

Making a Pose Library

In the Outliner select the Armature and make a Pose Library.
We can use this to set a few basic poses to make the animation process run a little easier.
The poses will be major key frames that we can interpolate between.

It’s not the best workflow but in the tech preview for upcoming Blender versions is an enhanced workflow for the animation process which looks really exciting – google it.

Make a Pose Library

Add the default pose as the first item.
Go to Pose Mode. Get the model into your default position and save this pose. (Important – this will be the pose that the model is exported as by default so try and make it your idle or standing pose).

Save several other poses (make sure you save all the bones you want the pose to effect – usually this is all the bones).
You can overwrite poses if you get it wrong.

Also, when a pose is added and a pose marker is created the whole keying set is used to determine which bones to key. But if any bones are selected, only keyframes for those bones are added, otherwise all bones in the keying set are keyed (this is why I usually have all the bones selected).

I’ve made several poses and saved them

It’s a good idea to set and select the poses a few times for each one to make sure you got it right. I’ve found that sometimes it’s a bit glitchy or I do something a little bit wrong and it doesn’t save properly (actually it’s probably not glitchy it’s probably just me).

That Book icon with the Question Mark is useful when you have all your poses completed. Pose libraries are saved to Actions. They are not generally used as actions, but can be converted to and from them. If you use this icon to “sanitize” the pose library it drops all the poses down to an Action with one pose per frame. So you can go into the NLA Editor window and select this track and sweep/scrub through them. Maybe this is useful as a clip in Unity if you want to split it up using the timing editor and make custom animations in Unity (never tried it).

Making the Animations

Go to Dope Sheet – and switch to the Action Editor View.

Action Editor


Make the animation (ie. start on the first frame – Assign the pose from the library – Shift + I save rotation and location. Go to last frame – assign the next pose – Shift + I and save again).

In the Timeline make sure you are on the beginning frame. Set the pose you want to move from (first keyframe) and save the required parameters.

Shift – I
Insert Location and Rotation
(make sure the Armature is Selected)

Start with the first pose
The Dope Sheet

Move to the next frame at a suitable scale and change the pose to your ending pose in the editor. Save the Location and Rotation parameters (if that’s all that’s changed).

Add the second pose
Saved Pose in the Dope Sheet

Pushing the Animation down the Action Stack

Once you are done hit the “Push Down” button. This is the magic button.

Magic Push Down Button

Next move over the the Nonlinear Animation Window.

The NLA Window

Your animations get made as Actions in the Non-Linear Action Editor Window: NlaTrack, NlaTrack.001, etc.

In the NLA Editor you can click the Star next to the NLA Track (rename them to make it friendlier) to scrub through the track. Make sure you got the right animation under the right name etc.

After hit Push Down after each animation is finished it appears as an NLA Track in the NLA Editor

I make a few more animations and hey presto. Each one of those NlaTracks is an animation that we can use in Unity. Also the PoseLib track is marked there with orange lines – one for each pose on a frame which is a good reference track if you need it.

The Animations Stacked up in the NLA ready for Export with the *.fbx

Export from Blender

These are the settings I use to export. It’s safer to manually select only the Armature and the Mesh here.

It’s useful to have Forward as -Z Forward for Unity.

Blender Export Settings

Import Into Unity

This is what it looks like when I import the .fbx into Unity.

The Animation Tab of the Asset (on import)

The animations come out as duplicates but you only need one set. Work out which one’s you want and delete the others using the minus button when you import. This bit can be a bit fiddly and sometimes I’ve had to do the process of exporting and importing a couple of times to get it to work. Sometimes what works is to drag and drop all your animations NLA Tracks into one track in the NLA Editor and select it with the star before exporting. Sometimes it works – sometimes not. Not sure why.

After that I drag the model into the scene and add an animation controller. Then you can just drag the animations from the imported model into the Animator window like below and set up transitions as you see fit. Below I’ve made them all come from an Any State and added some Triggers so I can play with them in the Window for Testing.

You can see the result of that testing in the .gif at the top of the article. (Apologies for the quality of that .gif it seems to have picked up some ghosting artifacts around the fingers – promise it looks awesome on the screen).

The Animator Controller

So there are a few limitations to this workflow that need to be mentioned. Some people like to save their whole .blend file into their Unity Assets so they can make updates on the fly while modelling etc. This won’t work with that set up. The animations need to be saved down to a *.fbx file so that Unity can find them when it’s imported as an asset. So if you like to have access to your .blend and use animations like this you need to export the *.fbx and import it again and have both .blend and .fbx in your asset folders which can be a bit confusing and messy and makes for a bigger project.

BYE

Vector Maths with Unity Gizmos

It’s really nice to get a game finally published. Endless Elevator is now on the Google Play Store so I’ve been planning and experimenting and just plain playing around with Unity and Game Ideas before I start a new project. It’s kind of like a holiday.

I like maths and I also like visualising it with Unity. This little project is one of the simplest types of maths that is generally associated with game programming and that’s adding and subtracting vectors and getting the distance between two vectors.

These sorts of functions are endlessly useful. They answer questions like: How far away is my target? Is my Player going to fall off the platform? Can I jump between two platforms? And so on and so on.

What I like about this one is that it highlights two things. 1. The difference between a player and a target’s perspective (in this project I nominate the circle as the player and the cylinder as the target), and 2. calculating the magnitude or distance between them.

To calculate the distance (magnitude) between two points you take the square root of the square of the x and y values. Which sounds a little complicated but is really just making a square the size and length of your x, and adding that to a square the size of your y, and then getting the square root of the total size of that square.

This is how it looks in the Scene View:

How it works

The project is really easy to set up. All you need is an Empty Game Object to hold the script and two other objects. I used a Capsule and a Sphere. Drag the two objects into the public Transform components on the script.

The Project Hierarchy and Properties of the Script

Here is the reproduced script below:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor.UI;
    
public class GridGizmo : MonoBehaviour
{

    public Transform circle;
    public Transform capsule;
    public Vector3 differenceToCapsule;
    public Vector3 differenceToCircle;
    public float magnitude;

    void Update()
    {
        differenceToCapsule = capsule.position - circle.position;
        differenceToCircle = circle.position - capsule.position;
        /*
           Magnitude of a Vector (Length of the Vector)
           We use Pythagoras' theorem to calculate it:
           |a| = √( x2 + y2 )
        */
        magnitude = Mathf.Sqrt(Mathf.Pow(differenceToCapsule.x, 2) + Mathf.Pow(differenceToCapsule.y, 2));
    }


    public void OnDrawGizmos()
    {

        // Grid Number Labels
        for (int i = -10; i < 11; i++)
        {
        UnityEditor.Handles.Label(new Vector3(0f, i, 0f), "" + i);
        }

        for (int i = -20; i < 21; i++)
        {

            UnityEditor.Handles.Label(new Vector3(i, 0f, 0f), "" + i);
        }

        // Labels of Objects
        UnityEditor.Handles.Label(new Vector3(circle.position.x + 1, circle.position.y, circle.position.z), "POSI: " + circle.transform.position);
        UnityEditor.Handles.Label(new Vector3(circle.position.x + 1, circle.position.y - 0.5f, circle.position.z), "DIFF: " + differenceToCapsule);
        UnityEditor.Handles.Label(new Vector3(circle.position.x + 1, circle.position.y - 1f, circle.position.z), "Magnitude: " + magnitude);

        UnityEditor.Handles.Label(new Vector3(capsule.position.x + 1, capsule.position.y, capsule.transform.position.z), "POSI: " + capsule.transform.position);
        UnityEditor.Handles.Label(new Vector3(capsule.position.x + 1, capsule.position.y - 0.5f, capsule.transform.position.z), "DIFF: " + differenceToCircle);

        // The line
        Gizmos.color = Color.blue;
        Gizmos.DrawLine(circle.position, capsule.position);
    }
}

As I mentioned above I do like mathy things in Unity. Here are some of the other one’s I’ve done which are a bit more complicated. I find them really really useful to cement those ideas that I should know back to front but always forget exactly how they work.

Unity: Android Native Crash – [Solved] MP4 Audio Encoding Problem

I’ve been in Beta Testing for a new game I’m about to release on the Google Play Store (the game is called Endless Elevator). I kept having Native Crashes on specific Android platforms in all my builds in the Pre-Launch Reports. Native Crashes can be terrible to work through if you get unlucky so I was a bit worried and figured I’d just have to leave it like it was and release with errors! But being a bit stubborn I threw a few days into sorting through it and am very glad I did. Working through the problem highlighted some things I didn’t know about Android Video support and was an interesting exercise in troubleshooting. So here is the method I followed and the resolution to the problem.

In each case it was always the armeabi-v7a package that was causing the issues. (I split my build into two APK’s for arm64 and armeabi to make it a smaller installation size – I haven’t gone the android bundle path yet).

These are some of my base Beta builds and in most cases there were 4 errors relating to specific platforms.

The Pre-Launch tests are run on a variety of Android platforms but usually they will include these four below in some form or other and my build kept crashing with a Native Error on each of them.

The usual suspects

When I looked at each of them in turn and played the video of the interactive session the fail point always seemed to be about the time when I had a full screen projected video playing or about to play. The video is used as an introduction and tutorial to the game so it was pretty important for me to get it working.

The drill down screen of the crash report where you can see the video of the session and get access to the logs.

I downloaded all the Logcat’s from the console above and looked for any errors or crash reports.

In each case I found this line (which was a bit of a dead giveaway):

——— beginning of crash

A half dozen lines above the likely culprit was writ large:

07-23 04:00:47.862: W/MediaAnalyticsItem(9345): Unable to record: (codec:0:-1:-11:0:3:android.media.mediacodec.mime=audio/ac3:android.media.mediacodec.mode=audio:android.media.mediacodec.encoder=0:) [forcenew=0]
07-23 04:00:47.890: W/Unity(9345): AndroidVideoMedia: Could not create decoder for mime type audio/ac3.
07-23 04:00:47.890: W/Unity(9345): (Filename: Line: 2177)
07-23 04:00:47.906: I/Robo(9288): No foreign elements detected, falling back to original ScreenState.
07-23 04:00:47.910: I/Robo-HyperMultiGraph(9288): New Screen: Optional.of(ScreenNode {Id=5, PackageName=com.ZuluOneZero.EndlessElevator, ActivityName=Optional.of(com.unity3d.player.UnityPlayerActivity)})
07-23 04:00:47.913: E/Unity(9345): Could not allocate memory: System out of memory!
07-23 04:00:47.913: E/Unity(9345): Trying to allocate: 4294705156B with 16 alignment. MemoryLabel: Audio
07-23 04:00:47.913: E/Unity(9345): Allocation happened at: Line:70 in
07-23 04:00:47.913: E/Unity(9345): Memory overview

A bit of googling about led me to believe that as per the error message above the audio codec used in the video was a problem. The AC3 codec is an Audio format that’s used in my MP4 Video. I’d never given it much thought but this format is not supported across all the Android platforms (one of the problems of Android development is that there is so many different platforms out there).

The Video Editing Software that I normally use is called OpenShotVideo. It’s fantastically good for the price (free) and is easy to use and powerful enough for my meagre needs. Turns out the default audio codec used is AC3 (there is probably a way to modify this with OpenShotVideo but I wasn’t in the mood to troubleshoot someone else’s software). I really hadn’t given the audio codec part of the MP4 a second thought.

This is the Export Panel from OpenShotVideo where I confirmed that the Codec was indeed ac3.

While I was doing all this work and after I worked out that the audio codec in the Video was the problem I had a look at the video settings in Unity. I found that there was already a built in transcoder that I’d never noticed right there in the Unity Video Asset Import screen.

Transcode !

That’s pretty cool! Unity has already solved all my problems before I even knew I had them. So I hit the Transcode tick box and waited for twenty minutes while it went to work transcoding. That wait time should have been a bit of a warning. I did the build and uploaded the new apks to the Google Developer Console but while doing that I found that my build size had jumped almost 17 MB!

This was my size before the transcoding:

And afterwards:

A quick look at the Editor.Log confirmed that the transcoding process had made my lovely low quality 7 MB Movie over 20 MB:

Used Assets and files from the Resources folder, sorted by uncompressed size:
22.1 mb 6.8% Assets/Art/IntroMovePlusFoyerClippedSlowLow.mp4

To fix this I downloaded the HandBrake Open Source Video Transcoder and transcoded my original video asset using the AAC Codec for Audio.

The HandBrake Tool

After importing this into my project and rebuilding again I was left with a similar package size and no Native Crashes. Hooray. I’m going to release this Beta Build to Production soon so getting over this little hurdle feels like a huge V for Victory. Huzzah.

Unity: Circular Movement with Triangles

My son asked to do some game programming with me last week. I was super excited. We did an eyeball rolling around in a circular motion (see image below). He did everything from working in Unity to making the assets and materials and I was very proud. I helped with the function to make it circle. It got me thinking how to explain circular movement and the use of Pythagorean triangles and cos and sin functions. They are simple maths but kind of hard to explain without a picture so hence this project about making circles with triangles.

Rolling Eyeballs!

The Basic Idea

To simplify I’ll work in 2D so we only got two Axis. X and Y. The Center of our circle will be on the 0 (zero, zero). So we could think of this as a co-ordinate system with two planes. If we were mathematicians we would call this a Cartesian Plane after Rene Descartes but being a Game Developer with Unity I’m going to call it the Scene view in 2D.

Our circle is defined by it’s Radius (ie. the distance from the Center).

On each Update() event our GameObject gets a new X,Y position on the Radius of the circle.

The Angle of Change

To start with we work out what the angle of difference is between this Update and the last one. The time passed during that period is known as the DeltaTime. Our “speed” is how many Radians we are travelling around the circle in that time. A radian is the length of the radius laid around the circumference of the circle. We multiply the Speed (how fast we are ticking through the radians) by the DeltaTime (time passed since last Update) to tell us that angle size.

# angleInRadians += RotateSpeed * Time.deltaTime;

Convert Polar to Cartesian

After working out the angle we have what is called a Polar Coordinate. That is we define our location by how far away it is (the distance – in this case as it’s a circle it’s always the same i.e. the radius), and what the angle (θ) is. Now we need to convert between that definition of a location in the Scene View to another one we can use in the Unity move function.

This is where Pythagoras and right angled triangles comes in – to convert from Polar Coordinates (r,θ) to Cartesian Coordinates (x,y) we use cos and sin :
x = r × cos( θ )
y = r × sin( θ )

In Unity there is already a function that does this:

newPosition = new Vector2(Mathf.Cos(angleInRadians), Mathf.Sin(angleInRadians)) * Radius;

But what does this really mean? Cos and Sin are just numbers. They represent the relationship between two sides of the triangle.

For example cos is the value of the relationship between the side of the triangle that is adjacent to the angle (the side next to the angle) and the hypotenuse (the long side on the other side of the angle). We are going to use cos to find the x value (how far along the horizon it is) in our Vector2 position. The same way we use Sin to find the y position (how far up or down).

In the image below see how there are squares built off each side of the triangle. Pythagorean theory for right angled triangles states that the area/volume of the two squares built of the two smaller sides will be equal to the area/volume of the big square built off the longest (hypotenuse) side of the triangle.

The big blue square has a volume of 1 because the radius of the circle is 1 unit on the Cartesian plane and One Squared is One. The volume of the other two squares (Magenta and Yellow) will always add up to one. Their volumes added together will always equal the volume of the blue square. This is the Pythagorean theory of right angled triangles in action. The length of a side of the Yellow box is the x value and the length of a side on the Magenta box is the y value. That’s our current position in (x, y) format which we can pass to the transform.position function.

The two images below show two Updates() that are reasonably close together so you can see in detail and in “freeze frame” what’s going on between the Updates() with all the variables. You can see the angle go from about 8 degrees to 20 degrees and the changing values for sin and cos which result in changing (x,y) values and volumes of the squares.

One Position on Update()
The next position on Update()

That’s basically it apart from some modifications to the values for being on the negative sides of the circle.

The script attached to the moving object is below but I’ve also put it on github here: https://github.com/zuluonezero/MoveInACircleWithTriangles

I’m finding it deeply satisfying to watch the triangles and squares get used to define a circle going round over and over again.

If this sort of thing floats your boat I’ve done some other posts on making curves using intersecting lines:

http://www.zuluonezero.net/2019/06/04/unity-2d-curves-using-triangles/

Also on using sin to use curves in movement: http://www.zuluonezero.net/2018/06/20/fly-birdy-fly-2d-curved-movement-in-unity/.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;

public class CircleMe : MonoBehaviour
{

    public float RotateSpeed = 5f;      // How fast we move through the radians
    public float Radius = 0.1f;         // How "deep" the circle is

    public Vector2 centreOfCircle;      // The Centre of our circle
    public float angleInRadians;        // The angle (in radians) between our position between one update and the next
                                        // A radian is the angle created if the length of the radius 
                                        // is laid along the circumference of the circle (about 57.2958 degrees)
    public Vector2 newPosition;         // The new position for every new Update event

    public Text displayText;
    public float angleInDegrees;
    private LineRenderer triLine;
    private Vector3 centre;
    private Vector3 yLoc;
    public float angle4Display;
    private Vector3 sq1;
    private Vector3 sq2;
    private Vector3 sq3;
    private Vector3 sq4cyan1;
    private Vector3 sq4cyan2;
    public Vector2 slopecyan;
    public Vector2 p1cyan;
    public Vector2 p2cyan;

    private void Start()
    {
        centreOfCircle = transform.position;
        // (0, 0) but could be anywhere
        centre = transform.position;
    }

    private void Update()
    {

        angleInRadians += RotateSpeed * Time.deltaTime;
        if (angleInRadians > 6.28319f)
        {
            angleInRadians = (angleInRadians % 6.28319f);
        }
        // eg.       0 += 5 * 0.25  (answer is 1.25)    // if deltaTime was 0.25 of a second
        // and our initial angle was 0 radians. 
        // Remember += is short for x = the current value of x plus itself (x = x + x)

        // we need to convert the angle and radius into an x, y position
        newPosition = new Vector2(Mathf.Cos(angleInRadians), Mathf.Sin(angleInRadians)) * Radius;
        //          = new Vector2(opposite/hypotenuse(1.25), adjacent/hypotenuse(1.25)) * Radius;
        // (x, y)   =            (0.9997, 0.0218) * 0.1
        // (x, y)   =            (0.09997, 0.00218)
        transform.position = centreOfCircle + newPosition;   // Adding Vectors
                                                             // (0.09997, 0.00218) = (0, 0) + (0.09997, 0.00218)
                                                             // this is our starting (x, y) position

        // Now do it again for the next Update - the code below has been duplicated for this example
        /*
        angleInRadians += RotateSpeed * Time.deltaTime;
        // eg.       1.25 += 5 * 0.25  (answer is now 1.25 + 1.25 = 2.5)    // if deltaTime was 0.25 of a second 
        newPosition = new Vector2(Mathf.Cos(angleInRadians), Mathf.Sin(angleInRadians)) * Radius;
        //          = new Vector2(opposite/hypotenuse(2.5), adjacent/hypotenuse(2.5)) * Radius;
        // (x, y)   =            (0.99904, 0.0436) * 0.1
        // (x, y)   =            (0.09990, 0.00436)
        transform.position = centreOfCircle + newPosition;   // Adding Vectors
        // (0.09990, 0.00436) = (0, 0) + (0.09990, 0.00436)
        */
        if (transform.position.x > 0)
        {
            yLoc = new Vector3(centre.x + Radius, centre.y, centre.z);
        }
        else
        {
            yLoc = new Vector3(centre.x - Radius, centre.y, centre.z);
        }

        angleInDegrees = angleInRadians * 57.2958f;
}

    public void OnDrawGizmos()
    {
        // Radius
        Gizmos.color = Color.blue;
        Gizmos.DrawWireSphere(centre, Radius);

        // Yellow square
        Gizmos.color = Color.yellow;
        sq1 = new Vector3((transform.position.x / 2), (transform.position.x / 2f), 0f);


        if (transform.position.x > 0)
        {
            if (transform.position.y > 0)
            {
                Gizmos.DrawWireCube(new Vector3((transform.position.x / 2), -(transform.position.x / 2f), 0f), new Vector3((transform.position.x), (transform.position.x), (transform.position.x)));
                Handles.Label(new Vector3((transform.position.x / 2), -(transform.position.x / 2f), 0f), "Vol: " + (transform.position.x * transform.position.x));

            }
            else
            {
                Gizmos.DrawWireCube(sq1, new Vector3((transform.position.x), (transform.position.x), (transform.position.x)));
                Handles.Label(sq1, "Vol: " + (transform.position.x * transform.position.x));
            }

        }
        else
        {
            if (transform.position.y > 0)
            {
                Gizmos.DrawWireCube(sq1, new Vector3((transform.position.x), (transform.position.x), (transform.position.x)));
                Handles.Label(sq1, "Vol: " + (transform.position.x * transform.position.x));
            }
            else
            {
                Gizmos.DrawWireCube(new Vector3((transform.position.x / 2), -(transform.position.x / 2f), 0f), new Vector3((transform.position.x), (transform.position.x), (transform.position.x)));
                Handles.Label(new Vector3((transform.position.x / 2), -(transform.position.x / 2f), 0f), "Vol: " + (transform.position.x * transform.position.x));
            }

        }

        // Magenta square
        Gizmos.color = Color.magenta;
        sq2 = new Vector3((transform.position.y / 2), (transform.position.y / 2f), 0f);
        if (transform.position.x > 0)
        {
            if (transform.position.y > 0)
            {
                Gizmos.DrawWireCube(new Vector3((transform.position.x + transform.position.y / 2), (transform.position.y / 2f), 0f), new Vector3((transform.position.y), (transform.position.y), (transform.position.y)));
                Handles.Label(new Vector3((transform.position.x + transform.position.y / 2), (transform.position.y / 2f), 0f), "Vol: " + (transform.position.y * transform.position.y));
            }
            else
            {
                Gizmos.DrawWireCube(new Vector3((transform.position.x + Mathf.Abs(transform.position.y) / 2), (transform.position.y / 2f), 0f), new Vector3((transform.position.y), (transform.position.y), (transform.position.y)));
                Handles.Label(new Vector3((transform.position.x + Mathf.Abs(transform.position.y) / 2), (transform.position.y / 2f), 0f), "Vol: " + (transform.position.y * transform.position.y));
            }

        }
        else
        {
            if (transform.position.y > 0)
            {
                Gizmos.DrawWireCube(new Vector3((transform.position.x - transform.position.y / 2), (transform.position.y / 2f), 0f), new Vector3((transform.position.y), (transform.position.y), (transform.position.y)));
                Handles.Label(new Vector3((transform.position.x - transform.position.y / 2), (transform.position.y / 2f), 0f), "Vol: " + (transform.position.y * transform.position.y));
            }
            else
            {
                Gizmos.DrawWireCube(new Vector3((transform.position.x + transform.position.y / 2), (transform.position.y / 2f), 0f), new Vector3((transform.position.y), (transform.position.y), (transform.position.y)));
                Handles.Label(new Vector3((transform.position.x + transform.position.y / 2), (transform.position.y / 2f), 0f), "Vol: " + (transform.position.y * transform.position.y));
            }
        }   

        // Red Triangle
        Gizmos.color = Color.red;
        Gizmos.DrawLine(centre, new Vector3(transform.position.x, centre.y, centre.z));
        Gizmos.DrawLine(new Vector3(transform.position.x, centre.y, centre.z), transform.position);
        Gizmos.DrawLine(transform.position, centre);

        if (transform.position.x > 0)
        {
            if (transform.position.y > 0)
            {
                Gizmos.DrawLine(new Vector3(transform.position.x - 0.1f, 0f, 0f), new Vector3(transform.position.x - 0.1f, 0.1f, 0f));
                Gizmos.DrawLine(new Vector3(transform.position.x - 0.1f, 0.1f, 0f), new Vector3(transform.position.x, 0.1f, 0f));
            }
            else
            {
                Gizmos.DrawLine(new Vector3(transform.position.x - 0.1f, 0f, 0f), new Vector3(transform.position.x - 0.1f, -0.1f, 0f));
                Gizmos.DrawLine(new Vector3(transform.position.x - 0.1f, -0.1f, 0f), new Vector3(transform.position.x, -0.1f, 0f));
            }
            
        }
        else
        {
            if (transform.position.y > 0)
            {
                Gizmos.DrawLine(new Vector3(transform.position.x + 0.1f, 0f, 0f), new Vector3(transform.position.x + 0.1f, 0.1f, 0f));
                Gizmos.DrawLine(new Vector3(transform.position.x + 0.1f, 0.1f, 0f), new Vector3(transform.position.x, 0.1f, 0f));
            }
            else
            {
                Gizmos.DrawLine(new Vector3(transform.position.x + 0.1f, 0f, 0f), new Vector3(transform.position.x + 0.1f, -0.1f, 0f));
                Gizmos.DrawLine(new Vector3(transform.position.x + 0.1f, -0.1f, 0f), new Vector3(transform.position.x, -0.1f, 0f));
            }

        }

        // Cyan Square
        Gizmos.color = Color.cyan;
        if ((transform.position.x > 0 && transform.position.y > 0) || (transform.position.x < 0 && transform.position.y < 0))
        {
            slopecyan = new Vector2(transform.position.x, transform.position.y);
            p1cyan = new Vector2((transform.position.x - slopecyan.y), (transform.position.y + slopecyan.x));
            p2cyan = new Vector2((centre.x - slopecyan.y), (centre.y + slopecyan.x));
            Gizmos.DrawLine(transform.position, p1cyan);
            Gizmos.DrawLine(centre, p2cyan);
            Gizmos.DrawLine(p1cyan, p2cyan);
        }
        else
        {
            slopecyan = new Vector2(transform.position.x, transform.position.y);
            p1cyan = new Vector2((transform.position.x + slopecyan.y), (transform.position.y - slopecyan.x));
            p2cyan = new Vector2((centre.x + slopecyan.y), (centre.y - slopecyan.x));
            Gizmos.DrawLine(transform.position, p1cyan);
            Gizmos.DrawLine(centre, p2cyan);
            Gizmos.DrawLine(p1cyan, p2cyan);
        }

        Vector3 lbl = new Vector3((p1cyan.x / 2), (p1cyan.y / 2), 0f);
        Handles.Label((lbl), "Vol: " + (Radius * Radius));

        // Angle Marker
        if (transform.position.y > 0)
        {
            if (transform.position.x > 0)
            {
                angle4Display = angleInDegrees;
                Handles.DrawSolidArc(centre, Vector3.forward, yLoc, angle4Display, 0.25f);
            }
            else
            {
                angle4Display = -(angleInDegrees - 180f);
                Handles.DrawSolidArc(centre, Vector3.forward, transform.position, angle4Display, 0.25f);
            }
        }
        else
        {
            if (transform.position.x < 0)
            {
                angle4Display = (angleInDegrees - 180f);
                Handles.DrawSolidArc(centre, Vector3.forward, yLoc, angle4Display, 0.25f);
            }
            else
            {
                angle4Display = -(angleInDegrees - 360f);
                Handles.DrawSolidArc(centre, Vector3.forward, transform.position, angle4Display, 0.25f);
            }
        }



        // Labels
        Handles.color = Color.blue;
        Handles.Label(centreOfCircle, angle4Display.ToString());
        Handles.color = Color.white;
        Handles.Label(transform.position, "X: " + System.Math.Round(transform.position.x, 2) + " Y: " + System.Math.Round(transform.position.y, 2));
        // sin opposite/hypotenuse
        Handles.Label(new Vector3(1.2f, 0.8f, 0f), "sin opposite/hypotenuse");
        Handles.Label(new Vector3(1.3f, 0.7f, 0f), "sin: " + Vector3.Distance(centre, (new Vector3(transform.position.y, 0f, 0f))) / Vector3.Distance(centre, (transform.position)) );
        // cos adjacient/hypotenuse
        Handles.Label(new Vector3(1.2f, 0.6f, 0f), "cos adjacient/hypotenuse");
        Handles.Label(new Vector3(1.3f, 0.5f, 0f), "cos: " + Vector3.Distance(centre, (new Vector3(transform.position.x, 0f, 0f))) / Vector3.Distance(centre, (transform.position)));
        // tan opposite/adjacient
        Handles.Label(new Vector3(1.2f, 0.4f, 0f), "tan opposite/adjacient");
        Handles.Label(new Vector3(1.3f, 0.3f, 0f), "tan: " + Vector3.Distance(centre, (new Vector3(transform.position.y, 0f, 0f))) / Vector3.Distance(centre, (new Vector3(transform.position.x, 0f, 0f))));

        Handles.Label(new Vector3(1f, -0.3f, 0f), "Next Position on Update()");
        Handles.Label(new Vector3(1f, -0.4f, 0f), "newPosition = new Vector2(Mathf.Cos(angleInRadians), Mathf.Sin(angleInRadians)) * Radius");
        Handles.Label(new Vector3(1f, -0.5f, 0f), "= new Vector2(opposite/hypotenuse(angleInRadians), adjacent/hypotenuse(angleInRadians)) * Radius");
        Handles.Label(new Vector3(1f, -0.6f, 0f), "" + Mathf.Cos(angleInRadians) + ", " + Mathf.Sin(angleInRadians) + " * "  + Radius + " = " + newPosition);

    }

}

/*
        Using Cartesian Coordinates we mark a point by how far along (x) and how far up (y) it is:
        Using Polar Coordinates we mark a point by how far away (magnitude or in this case as it's a circle always the radius is the same), and what angle (θ) it is:
        To convert from Polar Coordinates (r,θ) to Cartesian Coordinates (x,y) :
        x = r × cos( θ )
        y = r × sin( θ )

        Example: add the vectors a = (8,13) and b = (26,7)
        c=a+b
        c= (8,13) + (26,7) = (8+26,13+7) = (34,20)
*/

Image File Size in Unity and their Impact on Start Up Time on Android

Xander here…

We have been Beta Testing our soon to be released game The Dog Run and it’s been mostly OK but we had a number of issues with memory on smaller or older devices.  We made some gains with modifying our audio files (See this post) but were still running into niggling crashes on start up and longer than normal load times.

We were getting feedback like:

“Hey, I installed the game and couldn’t run it. When I started it there was a black screen for about 15s and then it went back to the launcher. Then each time I went back to the game there was unity and game logo fading out and again the app crashed/hanged and I was sent back to the launcher.”

(Thanks slomoian and the_blanker for all your help testing)

Obviously feedback like this is a little disheartening and far from ideal.  The game was running fine on every device and emulator I had access to but it’s only when you send something into the wild that you realise the full breadth of the spectrum that is the Android platform.  I guess this is another lesson in the importance of proper Beta testing.  One we hadn’t learned last time we released an app (see this old post on the perils and difficulty of finding Beta Testers).

We were using adb logcat to monitor our start up problems but not finding a “smoking gun” that solved every case. It seemed to be a memory problem and often with the graphics cache so again we went back to the Unity Editor build log to investigate our image files.  The game uses multiple large files to ensure that our animated sprites were always in the right spot. The game is dependent on the titular Dog hitting the ground line accurately on every frame to achieve the look we wanted when he runs and the paw breaks the ground line and appears as a gap.  We used a “flip-book” old fashioned style of animation where each frame sits exactly on top of the old frame and everything lines up on a transparency like in classic animated movies.

By using this schema we had to keep to a certain scale that fit within the constraints of a typical Android device format. This meant that when the images were imported the POT was not going to be something we could play with easily to get performance gains.  (Image files that have a width and breadth that is a power of 2 are faster and easier for the compression functions to work with – so 2, 4, 8, 16, 32, 64, 128, etc).  If I had the chance to do this again this is something I would probably start doing right from the beginning of development. When going through the Editor Logs we did find something interesting (get to the Editor Logs by right clicking on the arrow or tab near the Console and selecting it).

We found that some of our image files were 10 MB and a few were 2 MB.  Which was a little weird as they were all exported as layers from the same Gimp file so I must have done something in the import settings or the editor to change them.

This is a comparison of two files of the same dimensions and basically the same content but with two very different file sizes:

10.6 mb 0.8% Assets/artwork/RunOnSpot6.png

2.0 mb 0.1% Assets/artwork/DogSitHeadWag.png

The difference that I found was MIP Maps.  I’d selected to use MIP Maps fairly early on as it made the art work look smoother in the Editor.  MIP Maps are generated in the engine to make smaller more compressed versions of your artwork that can be used at longer distances from the camera where the detail is less visible. My game is 2D and has everything running at pretty much the same distance from the screen so really MIP Maps should not be required.  My art did look a bit better in the editor with them turned on but on a smaller device like a phone I couldn’t really tell the difference.  See below the difference in a file with MIP Maps selected and a file without.

With MIP Maps turned on (see the file size at the bottom and that the type is RGBA 32 bit):

The same file with MIP Maps removed (down to 2 MB and using ECT2 compression):

This is the difference that generating those MIP Maps makes. Your file is converted from the Android default compression to a larger (harder to process) 32 bit compression format.

So by turning off MIP Maps across the three hundred plus image files in my game reduced my application start up time to under a few seconds and reduced the APK file size by over one thousand MB.

This is the Build Report from the Editor Logs that shows the larger texture sizes and final build size:

Uncompressed usage by category:
Textures 1292.3 mb 94.6%

Complete size 1365.9 mb 100.0%

Compare this later Build Report with MIP Maps turn off to the original one above:

Textures 284.5 mb 82.6%

Complete size 344.6 mb 100.0%

It’s a considerable difference with little or no quality loss on most devices.  When I say most devices there were a few cases where the running dog did look a little tatty.  On very small emulated devices (3.5″ screens and low memory) the images were being scaled down quite a lot and the results were a lot less enjoyable but still an acceptable compromise considering previously the game would not run on these devices at all.

The next thing I started playing with was the different texture compression variables available for Android. I tried all of the settings (see screenshot below) in a different build and tested them against at least ten different devices with various architectures and screen dimensions and Android versions.

In each of the cases but one there was at least one test device that failed to start the game.  Once again exposing the issues of working with so many platform variables on Android.  Even when I built the APK with the (default) ETC selected one device failed the start up test.  So in the end the final build used the “Don’t override” setting which seemed to work on all devices.

Hopefully this is helpful to someone else out there and if it is try hitting the “Like” button below or sharing the link (the feedback keeps me going).

I found these references useful when troubleshooting my start up issues and learning more about compression on Android:

https://docs.unity3d.com/Manual/android-GettingStarted.html

https://docs.unity3d.com/Manual/class-TextureImporterAndroid.html

https://docs.unity3d.com/Manual/class-PlayerSettingsAndroid.html

https://answers.unity.com/questions/1406451/sprites-increase-android-apk-size.html

https://www.unity3dtips.com/unity-texture-compression-android-ios/

Unity Editor Tool Tips, Headings and Clamping Ranges

Hi Xander here…

Firstly a reminder that we are Beta Testing The Dog Run. If anyone would like to join please opt in here: https://play.google.com/apps/testing/com.ZuluOneZero.TheDogRun

(Apologies in advance for the pop up on this site advertising for help! It should only come up once)

The Dog Run has just been reviewed by Daikon Media if you are interested in reading what they thought please follow this link: https://daikonmedia.com/the-dog-run-review/

This post is about how to help yourself organise scripts in the Unity Editor using Tool Tips and Headings.  When you are developing as an Indie there can be long gaps between coding runs.  There is never enough time and so much to do so using anything that can help you quickly understand what those behaviours are supposed to be doing is a godsend (and adds to your godspeed).

Take this script for example from our game currently in development Endless Elevator.

This is the raw Editor display before beautifying:

I come back to this after a few days and have no idea what all those things should be and as they are so terribly unorganised it makes it all the harder to work it out.  Sure the code is commented and hell I wrote the stuff but it still takes time.

This is the same script with the addition of Headers and Tooltips (unfortunately the Tooltips are a bit hard to capture in a screen shot but you all know what they are).

Much Easier!

How to Make and Do!

I’ll use a quick example project to demonstrate how to use Tool Tips and Headings. They are super quick and easy.  I’ll also go over another extremely useful Editor function which is the Range. The Range can be used to clamp down a variable between a maximum and a minimum. This allows greater control over it’s setting while working on your game in Run Time so that you can tweak the variables easier.

Have a look at this example script:

HandyEditorTips Class


using UnityEngine;

public class HandyEditorTips : MonoBehaviour {

[Header("Speed Settings")]
[Tooltip("Speed value between 100 and 500.")]
// Show this float in the Inspector as a slider between 100 and 500
[Range(100.0F, 500.0F)]
public float speed = 100;

[Header("Point to Rotate Around X Axis")]
[Tooltip("Location Across the screen on the X axis")]
public float vector_x = 0;

// Update is called once per frame
void Update () {
// Rotate the object around the given point
// (Point to rotate round, direction of rotation, speed * frame rate delta)
transform.RotateAround(new Vector3(vector_x, 0, 0), Vector3.forward, speed * Time.deltaTime);
}

}

 

This script is added to the Sphere in the project and the Slider is a huge help in setting the required speed of turning.

Juxtapose this with the other setting for the X Axis vector variable and see when I try and use that when it’s not clamped into some sensible settings. It sends my Sphere cycling all other the shop.

It’s not groundbreaking stuff I know. But it’s easy to implement and does make your coding job easier in the initial phases.  Once you’ve got all your scene elements into some kind of decent shape you can start moving those variables that shouldn’t be exposed into a private scope and setting them to static.

Here is the Unity API Documentation Links if you want to do more reading:

https://docs.unity3d.com/ScriptReference/TooltipAttribute.html

https://docs.unity3d.com/ScriptReference/HeaderAttribute.html

https://docs.unity3d.com/ScriptReference/RangeAttribute.html