Endless Elevator – Show Reel

DevLog March 2019: This month we did a cheesy show reel of the latest changes in development for Endless Elevator.

This is a very early “In Editor” development sketch up of some of the features currently being included… Enjoy the show.

The game will be available on the mobile app stores in 2019.
For more information subscribe to our Dev Blog at: http://www.zuluonezero.net/zuluonezero-devblog/

Endless Unity Camera Tricks

In our game currently under development called Endless Elevator I decided to add a new feature of more depth.  The game is 2.5D and mostly sits in a very shallow Z axis, a limited X axis, and an endless Y axis. As the name suggests your character is inside a never ending building trying to kill or avoid the enemies by escaping up elevators and escalators. The mock-up of a level below in the Scene view of Unity gives you the picture.  

Endless Elevator Opening Level Spawn

The player view is a much smaller slice of this level…about this much:

Roughly how much the camera views

I had the idea that I wanted to extend this playing field into a deeper third dimension where the character could walk down a hallway,  away from the camera, and seemingly deeper into the building.  Instead of the camera following the character down the hall on the Z axis (as it follows him on the X and Y) I wanted to pan around the edge of the building and take up the character again on the new parallel so that it looks like the camera is turning Ninety degrees and that we are looking at a new side of the building.  

Have a look at the .gif below and I’ll try and explain that better. The top half of the image is the Scene view and the bottom half is what the camera (and the player) sees in the game.  In the top half  Scene view you can see my character in green and highlighted by the handler arrows. He scoots around a bit and then disappears down the hall. When he gets to a set depth it triggers the camera to move in the game window below and you will see the levels reconfigure into a new building face (note the elevators and escalators and doors will be in different positions).

On the bottom half of the .gif that shows what the player sees it looks like once the character disappears down the hall the camera pans right, looks at the edge wall of the building as it goes around the corner in a Ninety degree turn, and then follows the character on the new level again.

(Watch the top half for a bit then the bottom half)

You can see in the Scene view we are not really moving anything with the building. It just recreates the levels. The camera is doing all the work.  It’s not perfect yet and without any background around the building to relate the movement to it’s a bit hard to tell if we are turning Ninety or One Hundred and Eighty degrees on that camera flip but it’s getting there.

It took a while to work out how to do this and I tried several different methods but this is the basic logic of the camera move script that is attached to the character.

  1. The movement of the camera is triggered when the character moves past a certain point on the Z axis.
  2. Stop the regular character and camera movement functions by disabling that scripted behaviour on each object.
  3. I set up an empty game object called the CameraLookAtPoint that hovers at the end of the building on the far Right of the X axis.
  4. Pan the camera Right toward the CameraLookAtPoint.
  5. When the camera gets to within a certain distance to the LookAtPoint it starts to rotate towards it.
  6. The camera moves around the LookAtPoint so that it faces that end wall of the building as it turns.
  7. At this point while the only thing the camera can see is that blank edge wall of the building I destroy the old level we just walked out of down the hall and create a new randomly generated level.
  8. This is the great illusion! The camera is then moved instantly to the far Left of the building (the opposite end) and it appears as if we have just turned a corner.
  9. Lastly the camera picks up the character again and we hand control back to the normal camera and character movement scripts.
This is where the CameraLookAtPoint sits that the camera rotates around  Ninety degrees as it gets to the edge of the building.

I’ll post the whole script below with comments but I’ll walk through the interesting bits here. 

To start off with I needed to grab references to several external elements like the camera, the level instantiating script, the character controller script, and the characters Rigidbody. (I needed the rb because when the levels were destroyed and recreated gravity would take hold between them and the character would fall into the endless abyss!)

I had to generate a series of “if” conditionals and Boolean flags to control the movements of the camera. This was surprisingly hard to get right. It’s often not intuitive when you are in the Update function what the looping iterations will do with your code but this allowed me to slow things down and get control back. 

The calls to the instantiateScene1 script were needed to copy variables used there in the main flow of the game to track what level we were on and how high up the building we had climbed. That way I could decouple that mechanism from this one and happily destroy levels and recreate them without interrupting the flow of the rest of the game.

 


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpinLevelDownHall : MonoBehaviour {
public GameObject cameraLookAtPoint;
public bool triggered;
public bool cleanAndRebuild;
public bool moveRightEnd;
public bool moveRightEndBack;
public bool moveLeftEnd;
public bool movebacktoCharacter;
public int levelHolder;
public float cleanUpHeightHolder;
public bool firstRunHolder;
public Vector3 target_camera_Position;
private GameObject the_camera;
private RigidbodyCharacter move_script;
private CameraFollow cameraFollow_script;
private InstantiateScene1 instantiate_script;
private Rigidbody rb;
// Use this for initialization
void Start () {
the_camera = GameObject.FindGameObjectWithTag("MainCamera");
move_script = GetComponent();
cameraFollow_script = the_camera.GetComponent();
instantiate_script = GetComponent();
rb = GetComponent();
}
// Update is called once per frame
void Update () {
if (transform.position.z > 15)
{
triggered = true;
moveRightEnd = true;
cleanAndRebuild = false;
if (triggered)
{
// Stop the character and the camera moving
move_script.enabled = false;
cameraFollow_script.enabled = false;
if (moveRightEnd)
{
// Set the target camera position on the x axis at the far right of the building
Vector3 target_camera_Position = new Vector3(78f, the_camera.transform.position.y, the_camera.transform.position.z);
// Set the camera look at point on the y axis (so it's on the same level as the player)
cameraLookAtPoint.transform.position = new Vector3(cameraLookAtPoint.transform.position.x, transform.position.y + 4f, cameraLookAtPoint.transform.position.z);
// start moving the camera
the_camera.transform.position = Vector3.MoveTowards(the_camera.transform.position, target_camera_Position, 50 * Time.deltaTime);
// When you get close to the end start swinging the camera around to look at the wall
if (the_camera.transform.position.x > cameraLookAtPoint.transform.position.x - 10)
{                        the_camera.transform.LookAt(cameraLookAtPoint.transform);
}
// When you get really close to the first position move the camera beyond the wall to the side
if (the_camera.transform.position.x > target_camera_Position.x - 0.5f)
{
target_camera_Position = new Vector3(78f, the_camera.transform.position.y, 4f);  // 4f is perfect when the camera is at -90 deg
moveRightEnd = false;
moveRightEndBack = true;
cleanAndRebuild = true;
if (moveRightEndBack)
{
the_camera.transform.position = Vector3.MoveTowards(the_camera.transform.position, target_camera_Position, 50 * Time.deltaTime);
// When you get really REALLY close to the second position move the camera to the negative X Axis side
if (the_camera.transform.position.z > target_camera_Position.z - 0.2f)
{
moveLeftEnd = true;
if (moveLeftEnd)
{
target_camera_Position = new Vector3(-78f, the_camera.transform.position.y, 4f);  // The other side
the_camera.transform.position = target_camera_Position;  // snap move the camera
the_camera.transform.LookAt(cameraLookAtPoint.transform);
moveRightEndBack = false;
moveLeftEnd = false;
movebacktoCharacter = true;
}
}
}
}
}
if (cleanAndRebuild)
{
// Call cleanup on everything
rb.useGravity = false;  // so the character doesn't fall through the floor
//cleanUp;
cleanUpHeightHolder = instantiate_script.floorCntr;
cleanUpHeightHolder = cleanUpHeightHolder * 8;  // so it cleans up all the floors
firstRunHolder = instantiate_script.firstRun;
instantiate_script.cleanUp(cleanUpHeightHolder, firstRunHolder);  // cleanup height is usually two levels below the character - we are raising it to two levels above him
//then make three levels
levelHolder = Mathf.RoundToInt(instantiate_script.player_level);
instantiate_script.makeLevel(levelHolder);
instantiate_script.makeLevel(levelHolder + 8);
instantiate_script.makeLevel(levelHolder + 16);
cleanAndRebuild = false;
}                
triggered = false;  // makes it only run once
}
}
if (movebacktoCharacter)
{
// new position is back to the character
// Start breaking and making your new levels in here
// first move the character
transform.position = new Vector3(transform.position.x, transform.position.y, -4);
target_camera_Position = new Vector3(transform.position.x, transform.position.y + 4.9f, -20.51f);  // the starting camera position
the_camera.transform.position = Vector3.MoveTowards(the_camera.transform.position, target_camera_Position, 50 * Time.deltaTime);
the_camera.transform.LookAt(transform);
// Once again when you get close to your original camera position disable and enable normal camera tracking again
if (the_camera.transform.position.x > transform.position.x - 0.02f)
{
movebacktoCharacter = false;
rb.useGravity = true;
move_script.enabled = true;
cameraFollow_script.enabled = true;
}
}
}
}

It’s not the prettiest code, and I admit to hacking my way through it, but it works.  Maybe you have a better method for doing something similar – if you do please feel free to add a comment – I’d like to hear it.

Zulu Out.

Getting a Foot in the Door of Game Design

First of all – sorry about the misleading title – this post is about getting the doors working in the Endless Elevator game that we are currently developing. I thought it was a good pun and that as this post is all about our development process that it wasn’t too bad. The only career advice I got is just to start making games…any games.

You’d think making a door that opens and closes would be a pretty simple thing to do but surprisingly it wasn’t.

I designed and built some 3D models of building components in MagicaVoxel and exported them as .obj files. I use MagicaVoxel because it’s free and really quick and simple to use. When I model components I can be sure that they are all exactly the same scale and that all the different bits (no matter when I made them) are going to fit together without any hassle. But the models are not optimised for game engines as they have a reasonably high poly count due to the modelling process within the program. Most of the models come out with heaps of unnecessary triangles that need to be managed first. In most cases I will import the object into Blender and use the “decimate” modifier on the planes (with an angle of about 20 if you are interested). In the case of the door object it was pretty simple, it is just a door after all, and I didn’t need to optimise it.

Here is what the door looks like in MagicaVoxel:

Notice that the door object is sitting just forward of the enclosing frame and that when exporting the object the center is at the middle bottom plane of that frame. The door is off center because it’s modelled to fit exactly in a doorway that sits exactly in that position within the frame. This saves me a huge amount of time lining everything up when it gets to Unity as the position of the door (or any other object for that matter) is already in the right spot. The problem is that the point of origin for the door is in the wrong spot. It exports a few units behind the door and on the floor! This becomes important when you try and rotate that object (like you would when opening and closing a door) and the pivot point is not where the hinges should be.

To fix this I had to import the .obj file into Blender and reposition the point of origin for the model.

This is what it looks like in Blender when I did this:

To do it I selected the edge that I wanted the door to pivot on when opened.

Then in Edit Mode:
Mesh -> Snap -> Curser to Selected

In Object Mode:
Object -> Transform -> Origin to 3D Curser

So that puts the curser onto the correct position in the middle of that edge where the hinges would be and resets the point of origin (which is where it will pivot when rotated in Unity) to the right spot.

Once we were all imported into Unity and looking good I set up a prefab for the “Doorway” object with the door as a child object. The doorway object has a bunch of colliders to stop the player walking through the walls and a big sphere collider where the door would be to trigger the open / close function when the player walks into it.

This is what the doorway looks like in Unity:

Next I scripted up a few options for opening the door. I’ll post the script at the end of this article but basically there were three options of opening the door that I wanted to test. (Actually I tried it about six ways but whittled it down to the most simple methods – and just as an aside there is an actual “hinge” object type in Unity if you ever need it).

This is how the script looks in the editor:

Notice the slider at the bottom to control how far I want the door to open. It’s really handy to have this when playing in the editor and getting your settings right. If you want to know more about using it see this post

The three tick boxes are for testing the three different ways of opening the door.

Snappy was a quick simple transform of the position from open to closed with no “in betweening”. It looks a bit unnatural as the door magically goes from closed to open but it’s not too bad and most people are pretty used to similar behaviour in video games.

The active line in the code is:
the_door.transform.Rotate(-Vector3.up * 90 * Time.deltaTime);

The next method works more like a door should. In that it swings open and closed the whole way in a steady fashion. But the big problem with this method was that it was OK when the character was going into the doorway and with the swing of the door but when it was time to come back out of the doorway the door was in the way. There was not enough room to trigger the door opening from the other side without being hit by the door as it opened. Plus if the player enters the collider from the wrong trajectory the character gets pushed through a wall by the swinging door which is sub-optimal. I called this method InTheWay!

The active line here is:
the_door.transform.rotation = Quaternion.Euler(targetPositionOpen);

In an effort to combat this I chose to do a hybrid method that opened the door to a point that wouldn’t hit the player and then do the magic transform to get all the way open. I call this one aBitBoth. It looks a little weird too. Like there is an angry fairy pulling the door closed with a snap after the character enters.

Here are all three to compare.

Snappy

In The Way

A Bit of Both

I’m not too sure which one I’m going to use at this stage as the Snappy method works best for now but I like the In The Way method better. I looks more normal and I like that you have to wait just a few milliseconds for the door to swing (adds tension when you are in a hurry to escape a bullet in the back). I could do something like halt the player movement from the rear of the door when it triggers to open from the closed side or maybe play around with the radius of the sphere. Neither solutions seem like great ideas to me right now but something like that will need to be done if I’m going to use that method. Maybe I could just have the door swing both ways and open to the outside when he is behind it but that’s probably a bit weird for a hotel door.

Here is that script that I was testing with:

using UnityEngine;
public class OpenDoor : MonoBehaviour {
public bool openMe;
public GameObject the_door;
public bool snappy;
public bool inTheWay;
public bool aBitBoth;
public Vector3 targetPositionOpen;
public Vector3 targetPositionClosed;
[Range(0F, 180F)]
public float turningOpen;
void Start ()
{
targetPositionClosed = new Vector3(0f, 180f, 0f);
targetPositionOpen = new Vector3(0f, turningOpen, 0f);
}
void Update()
{
if (openMe)
{
OpenMe();
}
else
{
CloseMe();
}
}
private void OpenMe()
{
if (inTheWay)
{
if (the_door.transform.rotation.eulerAngles.y > turningOpen)
{
the_door.transform.Rotate(-Vector3.up * 90 * Time.deltaTime);
}
}
if (snappy)
{
the_door.transform.rotation = Quaternion.Euler(targetPositionOpen);
}
if (aBitBoth)
{
if (the_door.transform.rotation.eulerAngles.y > turningOpen)  // 144f
{
the_door.transform.Rotate(-Vector3.up * 90 * Time.deltaTime);
}
else
{
the_door.transform.rotation = Quaternion.Euler(targetPositionOpen);
}
}
}
private void CloseMe()
{
if (inTheWay)
{
if (the_door.transform.rotation.eulerAngles.y <= 180)
{
the_door.transform.Rotate(Vector3.up * 90 * Time.deltaTime);
}
}
if (snappy)
{
the_door.transform.rotation = Quaternion.Euler(targetPositionClosed);
}
if (aBitBoth)
{
if (the_door.transform.rotation.eulerAngles.y <= turningOpen)  // 144f
{
the_door.transform.Rotate(Vector3.up * 90 * Time.deltaTime);
}
else
{
the_door.transform.rotation = Quaternion.Euler(targetPositionClosed);
}
}
}
void OnTriggerEnter(Collider col)
{
string colName = col.gameObject.name;
Debug.Log("Triggered OpenDoor!!! : " + colName);
if (col.gameObject.name == "chr_spy2Paintedv2" || col.gameObject.name == "BadSpy_Package(Clone)") 
{
openMe = true;
}
}
void OnTriggerExit(Collider col)
{
if (col.gameObject.name == "chr_spy2Paintedv2" || col.gameObject.name == "BadSpy_Package(Clone)") 
{
openMe = false;
}
}
}

Why Normalize()

I’ve been doing some work on the AI for enemy behaviours for an unreleased game Endless Elevator and have been delving into the book “Unity 2018 Artificial Intelligence Cookbook – Second Edition” by Jorge Palacios.

It uses the Normalize() function regularly to record the direction of an object in relation to another object and it got me thinking about the usefulness of this function.  You can see why something like knowing the direction of the Player could be good for an enemy AI behaviour but I wanted to investigate more deeply about how this worked and how I could use it.

One of the things I hadn’t consciously been aware of, but is obvious once you point it out, is that a Vector3 can define a location (ie. a point in space 0, 0, 0) but it can also define a direction if you have a starting position and a target position.

A good example of a Vector being used to denote a location and a direction in Unity is the Ray.
The Ray consists of two Vector3 data points. The first Vector3 is the source position the ray is taken from and the second Vector3 is the direction of the target.

When a direction Vector3 is Normalized it keeps it’s direction but it’s length (how far away one object is from another) is set to a point between 0 and 1.  In Unity they round down to 0 if the objects are very close to each other (anything under about 0.04f of a unit) anything above that gets Normalized up to 1.

As an example we can simplify a bit by limiting ourselves to a Vector2.

A Vector2 can also be either a point or a direction depending on what you want to use it for.
If it’s (3, 0) then it could be either a point at x=3, y=0, or a direction along the x axis (one dimension) with a length of 3 and a slope of 0.
If you Normalize() that example the Vector2 becomes (1, 0). It will have a length of 1 but still be pointing in the x direction.
If you add the y value (dimension) into the picture where x=3, y=3 the Normalized value becomes (0.7, 0.7). So without going into the maths of why … you can see that the axis/dimensions impact on each other to define the Normalized direction.

We can extrapolate this into three dimensions without too much difficulty but I’m not going to describe that here as it’s not that necessary to understand. We only need to grasp what this means and what sort of use you can make of it in a Unity project.

For Example…

This is a simple use case of why you might want to use Normalize().

(Note that there is also a Normalized() function where the current vector is left unchanged and a new normalized vector is returned).

I’ve created an example project where the relationship between two objects (a Cube and a Sphere) will give us a direction (using Normalize()).  The direction is applied to a transform.Translate function on a third object (a Cylinder) which now moves in the given direction.

In this script below attached to our Green Cube (the unchanging point of origin – 0, 0, 0) we define the Pink Sphere as the target.  In the script we get the difference between the position of the sphere and the Cube and Normalize() it to make a direction. It doesn’t matter how far the Sphere is from the Cube the direction stays the same.

Also to extend the example I’ve added a Ray with the same origin as the Cube (0, 0, 0) that will always point to the Sphere.  This shows that the Normalized Vector3 is the same as the direction of the Ray pointing to the Sphere.

Normalizing Direction

The public Vector3 lineDirection is the difference between the position of the Cube and the Sphere. This is the variable we will pass to the Cylinder to move it in the required direction.

using UnityEngine;
using UnityEngine.UI;
public class HowToNormalizeDirection : MonoBehaviour {
public Vector3 lineDirection; 
public GameObject target;   
public Text vector_text;
public Text norm_text;
public Text ray_direction;
public Ray whatsTheRay;    
// Use this for initialization
void Start () {
lineDirection = new Vector3();
whatsTheRay = new Ray();
}
// Update is called once per frame
void Update () {
lineDirection = target.transform.position - transform.position;  
vector_text.text = "Vector3 target.transform.position :" + lineDirection;
lineDirection.Normalize();  
norm_text.text = "Vector3 Normalized() :" + lineDirection;
whatsTheRay = new Ray(transform.position, target.transform.position);
ray_direction.text = "Ray : " + whatsTheRay.ToString();   
}
}

Moving the Capsule in the Same Direction

In this script below attached to the Cylinder we get the direction from the lineDirection variable above but we could also have used the inbuilt Ray.direction function to return the same result.

using UnityEngine;
public class MoveCapsule : MonoBehaviour {
public Vector3 direction;
public float speed;
// Use this for initialization
void Start () {
direction = Vector3.zero;
speed = 0.50f;
}
// Update is called once per frame
void Update () {
var script = GameObject.FindWithTag("Cube").GetComponent();   
direction = script.lineDirection;
//direction = script.whatsTheRay.direction;  
transform.Translate(direction * speed * Time.deltaTime); 
}
} 

In the Video below you can see this demonstrated. The position of the Pink Sphere is being manually manipulated using the Transform on the right. The text areas expose the vector3 values being accessed by the objects and scripts.

So what’s going on in this video amateur hour?

The three lines of text at the top of the game scene represent:

1. The Vector3 Position of the Pink Sphere.

2. The Vector3 Normalized  direction which is the relationship between the position of the Green Cube (0, 0, 0) and the Pink Sphere (x, y, z).

3. The uses of Vector3 in the Ray.  First the position of the origin and then the Direction of the Ray which is exactly analogous to the Normalized Vector3 in point 2 above.

The uses for something like this could extend to a weapon aiming system, a custom controller or be passed into a path finding routine. This is not the only option of course there are other built in methods like transform.LookAt(target) available which may accomplish your programming goal.

During the research for this post I found the following links helpful.

https://docs.unity3d.com/ScriptReference/Vector3.Normalize.html
https://docs.unity3d.com/ScriptReference/Vector3-normalized.html (which is a different but related function)
https://forum.unity.com/threads/what-is-vector3-normalize.164135/
https://answers.unity.com/questions/52881/vector-normalization-question.html
https://www.dummies.com/education/math/calculus/finding-the-unit-vector-of-a-vector/ (for why you really don’t want to know about the math or do it by hand)
https://www.mathsisfun.com/algebra/vector-unit.html (best for simple vector explanation)
https://docs.unity3d.com/ScriptReference/Ray.html
https://docs.unity3d.com/ScriptReference/Ray-ctor.html

Indie Game Release Click Through and Conversion Rates

Hi Harmony here…

Last Sunday we released our game The Dog Run into the Google Play Store.

This post is a breakdown of the first week release click through rates from this web site and the conversion rates that resulted in an actual download of a game.

Let me say from the outset that without some form of “advertising” or publication campaign a new game on the Google Play Store is never going to be successful.  We found this out the first time around with our game NumBlocks:

NumBlocks is a fun little numbers game that’s a bit like Tetris with numbers that add together. It was our first game with Unity and only a bit more than an experiment in finishing a game that looked good enough to publish.  We weren’t that proud of it and were really just proving our development and release cycle for future games.  It was never publicised and never got downloaded…by anyone.  So if you are not promoting your game… no-one is looking … and no-one will play it.

This time around with The Dog Run we really do want people to play the game and enjoy it. The game is a simple endless runner with a quirky hand drawn style that held many interesting development challenges. More information about the game is available here:

The Dog Run

But what I want to investigate and be transparent about is the way we used this web site and the landing page as a way to interest people into playing the game.

We released the game into the Production Google Play Store on Sunday October 21st.

On the next day at about 8 am New York time we posted on social media channels (we found from research, and validated with our own posts over several months on this site, that this is one of the times of the week that got the most traffic).

We only used Reddit, Facebook and Google+ to promote the release of our game.  We Tweeted and posted to LinkedIn and Tumblr but no-one really follows our feeds on those platforms so I won’t count them.

This is the list of Groups that we hit on social:

Facebook

IGC : Indie Game Creators
Unity 3D Game Developers
Unity 3D Game Developers (different group)
UNITY3D Game Developers
GameDev Show and Test
Game Developers
Indie Game Players & Developers!
INDIE GAMES
Indie Game Promo
Indie Game Development Feedback (IGDF)
Indie Game Chat
Indie Game Promotions

Google+

Unity3d Indie game developers
Unity3D Mobile Developers
Unity 3D Enthusiasts
Unity 3D Developers
Android Apps and Games-Android Mobile Zone
Android Game Developer
Free Mobile Games
Game Developers
Game Developers (different group)
Mobile Game & App Developers
Unity
Unity3d Indie game developers
Unity 3D Developers
Unity3D Mobile Developers

Reddit

/r/Games/
/r/gamedev/
/r/androidapps/
/r/AndroidGaming/
/r/gamernews/
/r/IndieGaming/
/r/androiddev/
/r/gamedesign/
/r/devblogs/
/r/SideProject/
/r/playmygame/
/r/Unity2D/
/r/IndieDev/
/r/indiegames/

So what can we say about this set of groups?  Well they are all Gamer’s Groups. These are “our” people. This is where we go when we need feedback or inspiration or help.  We may not be active in all of them and by no means is this an exhaustive list but after working our way through this lot on a Monday morning there is no energy or time left to look for more groups to join and contribute to.  So this is not a “general public” group nor are they representative of what I think our target audience might be.  But these are gaming enthusiasts and I think more likely to try new games and provide that sort of validating critical and informed feedback that is important for making the game better.  (I love you all!)

So now for the stats….

After seven days since posting these are the figures that hit our website (which normally gets about 20 – 30 visitors a day).  The graph below shows the two weeks previous to release and is indicative of the sort of traffic we get. The blue bar just under the 500 mark is what happens when we do a blog post.  The big orange one is the day after our “social media campaign” (if you could call it that).

Total number of visitors over that week was: 1199

But as you can see a day or two after we posted the traffic dropped straight back to normal. Although the traffic we did get that week was all mostly to look at the landing page of the game.

It is interesting to see where that traffic came from  –  and it’s overwhelmingly Reddit that drives the traffic to our site.  This image below is the stats from the big orange day.

So as you can see even though we posted to heaps of groups in Google+ and Facebook not many users of those platforms were reached.

Out of this massive spike in traffic the number of people who actually clicked on the link to go to the Google Play Store was: 76

This is about 6.5% (rounded up) of our total traffic during that period.  Every day during that week the click through rate was about the same average rate.

Now for the fun part.  Out of those 76 players the number who actually downloaded the game and played it was:  21!

Well that’s a huge improvement over none.

I was very excited by this number.  I’ll type it again in long form…..  Twenty One !

So what I want to drive home with this post is the amount of traffic that you got to drive into your web site to get the sort of volumes that will get your game downloaded.

Let’s break it down into simple numbers……  Out of 1200 people only 75 looked at the game and out of that number only 21 downloaded it.  That’s 1.75% of the traffic to my landing page downloaded the game. That’s the truth of it and remember that these are “our peeps” not the general public so I couldn’t even say that this is indicative of the way the real world works.  But it sure is interesting and in a few months after I’ve spent some time marketing this game to real people (not just the gaming community) I’ll do another post and see if the stats still hold.

Harmony out!

P.S. If you want a friendly copy of all those social media links email me at zuluonezero.z10@gmail.com and I’ll forward them back. (Maybe on a later post – if there is enough interest – I’ll  put them online). Zulu.

The Dog Run is in Production on the Google Play Store

This week we moved our latest game The Dog Run into Production on the Google Play Store.

The Dog Run is an Endless Runner for Android that supports animal welfare!

It’s a free game. There is the option to watch ads but instead of in game rewards all profits from the advertising goes to support animal welfare and animal hospitals.

The game is about taking your fun lovin’ pooch for a run. But watch out! There is a bunch of obstacles in your path. It’s a good thing your dog is a natural jumper and can run all day in all sorts of weather.

Read the review from Daikon Media here.

They say, “… it’s not only the style, that I like, it’s also the unique sense of humor…the game is even fun and original.”

Feedback can be posted on the Google Play Store,  on this website in the comments,  or directly by email.

ZuluOneZero Game Design
http://www.zuluonezero.net/
zuluonezero.z10@gmail.com

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/

Using ADB Logcat to Debug Unity Application Start Up Times

Our game The Dog Run has been in open Beta testing for some time and we are wrapping up new builds after the feedback we received during the process.

There is still time if you want to check out the Beta edition of the game on the Play Store: https://play.google.com/apps/testing/com.ZuluOneZero.TheDogRun

We used the built in Unity Profiler to work on performance improvements on the running game.
https://docs.unity3d.com/Manual/Profiler.html

But when it came to the application start up we found that we had a problem. It was taking over 20 seconds for the game to open up after a user pressed the icon.
Obviously this was way too long as much of that time was spent sitting on a black screen.

Trouble was with the Debug builds of our APK’s running in the profiler it would only pick up on game play issues and we couldn’t identify what was causing the slowness on start up. So we switched to using ADB the Android Studio Debugging Bridge to see what was actually going on under the hood of Android system while our game was loading.

If you want more info on the ADB tool have a look at my previous post here.

Unity Debugging with ADB for Android

We had a handfull of APK’s that we wanted to test against on out local workstation and used APK to copy them over to our attached development device using ADB commands.
Like this:

C:\Users\<User>>adb install C:\Users\<User>\Downloads\TheDogRun_v13_debug.apk
Success

Then we ran the adb command with the logcat argument and passed that output into a file to read afterwards.
Now pumping logcat into a file generates lots of data including the buffer that was recorded before the command was run so you will get lots of info from earlier that you either need to filter out or grep through.

This is one way to filter out in adb command line but it’s not as useful as grepping through the file:
>adb.exe -d logcat -e isApplicationExternalStorageWhitelisted

It’s a pretty big log generated in just a few seconds of logging:

I preferred to have the full log in the file and use notepads and command line greps and epreps to get the info I wanted to hone in on.
On Windows I have a great Unix like tool called MobaXTerm which is really useful for this sort of work.
https://mobaxterm.mobatek.net/
It’s a Windows ssh client but includes a Cygwin type access to your local Windows file systems.

So when we were searching through the logs (and it takes a while to get used what you are looking at. I’d recommend getting on google for anything that looks interesting).

Here is an example of the log:

 

It changed a little bit with each run but this is the main info that I was using for to make my judgements about start up speed on.

 

### This is the initial call for the application
09-28 10:29:08.684 4938 4938 D StorageManagerService: getExternalStorageMountMode : final mountMode=1, uid : 10495, packageName : com.ZuluOneZero.TheDogRun

### This is the process being allocated
09-28 10:29:08.752 4938 4938 I ActivityManager: Start proc 8679:com.ZuluOneZero.TheDogRun/u0a495 for activity com.ZuluOneZero.TheDogRun/com.unity3d.player.UnityPlayerActivity

### The Window for the game is open had has focus
09-28 10:29:09.336 8679 8679 I Unity : windowFocusChanged: true
09-28 10:29:09.337 8679 8679 V InputMethodManager: Starting input: tba=android.view.inputmethod.EditorInfo@23ee73 nm : com.ZuluOneZero.TheDogRun ic=null
09-28 10:29:09.346 4938 6402 V InputMethodManagerService: windowGainedFocus : reason=WINDOW_FOCUS_GAIN client=android.os.BinderProxy@9752c36 inputContext=null missingMethods= attribute=android.view.inputmethod.EditorInfo@ea1162f nm = com.ZuluOneZero.TheDogRun controlFlags=#105 softInputMode=#20 windowFlags=#80e90500

### The Game has the Window on
09-28 10:29:09.420 4938 5124 D SamsungPhoneWindowManager: Turning screen on : com.ZuluOneZero.TheDogRun uid = 10495

### Splash Screen is coming up
09-28 10:29:09.424 4938 5124 D InputEventReceiver: channel ‘d7f15c8 Splash Screen com.ZuluOneZero.TheDogRun (client)’ ~ Disposing input event receiver.

### Splash Screen removed
09-28 10:29:09.431 3192 3192 I Layer : id=6192 onRemoved Splash Screen com.ZuluOneZero.TheDogRun#0

### This is a Debug Package but the Unity Debugger or Profiler was not attached so there is a 4-5 seconds delay while it times out waiting for the connection
48832 [Id] AndroidPlayer(samsung_SM-G930F@192.168.1.139) [Debug] 0 [PackageName] AndroidPlayer” to [225.0.0.222:54997]…
09-28 10:29:09.544 8679 8696 D Unity : Waiting for connection from host on [0.0.0.0:55070]…

09-28 10:29:14.571 8679 8696 D Unity : Timed out. Continuing without host connection.

### Scripting Engine starts up
09-28 10:29:14.682 8679 8696 D Unity : InitializeScriptEngine OK (0xe7543ee0)
09-28 10:29:14.682 8679 8696 D Unity : PlayerConnection already initialized – listening to [0.0.0.0:55070]

## Creating Open GL
09-28 10:29:14.761 8679 8696 D Unity : OPENGL LOG: Creating OpenGL ES 3.2 graphics device ; Context level <OpenGL ES 3.1 AEP> ; Context handle -1013109888

### Unity Reports it’s Unload time
09-28 10:29:22.067 8679 8696 D Unity : UnloadTime: 2.403000 ms

### Ads Start up
09-28 10:29:22.309 8679 8696 D UnityAds: com.unity3d.ads.cache.CacheDirectory.getCacheDirectory() (line:42) :: Unity Ads is using external cache directory: /storage/emulated/0/Android/data/com.ZuluOneZero.TheDogRun/cache/UnityAdsCache

### About this time the Debugging on the Application side kicks in and you start to get these sorts of messages in the log:
09-28 10:29:23.533 4938 9976 I ActivityManager: DSS on for com.ZuluOneZero.TheDogRun and scale is 1.0
09-28 10:29:23.731 8679 8696 I Unity : Building GPG services, implicitly attempts silent auth
09-28 10:29:23.731 8679 8696 I Unity : #0 0xc6578460 (libunity.so) GetStacktrace(int) 0x44
09-28 10:29:23.731 8679 8696 I Unity : #1 0xc5f5e978 (libunity.so) DebugStringToFile(DebugStringToFileData const&) 0x230
09-28 10:29:23.731 8679 8696 I Unity : #2 0xc546bf9c (libunity.so) DebugLogHandler::Internal_Log(LogType, core::basic_string<char, core::StringStorageDefault<char> >, Object*) 0xa8
09-28 10:29:23.731 8679 8696 I Unity : #3 0xc546be8c (libunity.so) DebugLogHandler_CUSTOM_Internal_Log(LogType, MonoString*, MonoObject*) 0xb4
09-28 10:29:23.731 8679 8696 I Unity :
09-28 10:29:23.731 8679 8696 I Unity : (Filename: ./Runtime/Export/Debug.bindings.h Line: 43)

09-28 10:29:24.925 8679 8850 I UnityAds: com.unity3d.ads.api.Sdk.logInfo() (line:70) :: Requesting configuration from https://publisher-config.unityads.unity3d.com/

Total start up time around 16 Seconds – we got lucky with this one.

But you know it’s system dependent so we did it another ten times for each package to make sure that the readings were comparable to our out loud counting of seconds while watching the device.

To make a long story short we started getting an idea of what happens when the app starts up.
In the end the thing that most improved our startup time was the build that had changed settings to our audio files in the Unity import settings.


Basically all audio files were converted from the Unity default to the Overide for Android setting. The background music was streamed and shorter sound effects were compressed in memory.

So hopefully if you take this advice do that up front and you won’t have to spend a fun rainy Sunday debugging the start up time problem.

Interesting as it was it would have been more fun doing promo art for the upcoming release!

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

Unity Debugging with ADB for Android

Hi Zulu here… (First of all … sorry for the cat)

Let me say straight off that your first port of call for any Unity debugging should be the Unity Console.

Though sometimes you need more low level operating system logging for Android. This is where ADB (in lower case) comes in.

On Windows this is a command line tool to view the logs from a connected Android device.

The command line is not the only way to use the tool sometimes it’s better to use the Android Studio interface (a bit more graphical).

You will need to have your Android device connected to your workstation and USB debugging turned on  (Google that if you need to). You could also use an Android emulator on your desktop.

I use Leapdroid or KoPlayer.  (Leapdroid have now joined Google and no longer support the emulator but it’s still available to download on the internet).  I guess you could also use the emulator that comes with Android Studio.

When your game is installed and running on your device go to the directory in your workstation (PC) where the Android SDK Tools are.

On mine they are here:

C:\Users\<user_name>\AppData\Local\Android\sdk\platform-tools

In the tools directory open the monitor.exe (this tool was depreciated in Android Studio 3.0 and replaced with profiler.exe mine is still on the lower revision).

This documentation on the Android site is a good start investigating the profiler:

https://developer.android.com/studio/profile/android-profiler

You can also get into LogCat directly from Android Studio (if you have it open):

Go to  View | Tool Windows | Android Monitor

At the bottom of Android Monitor in it’s own tab is the LogCat console window. This contains all of the information about what’s happening in the Android operating system.

As you can see the LogCat console contains a lot. It logs everything.

To filter it type “tag:Unity” in the textbox at the top to see messages that relate to Unity.

Using adb logcat from the command line

Open a command prompt on your development workstation and find the location of your Android SDK platform-tools folder.

Mine was here:

/Users/YourUserName/Library/Android/sdk/platform-tools

If you get this error when you run adb.exe using the command prompt:
‘adb’ is not recognized as an internal or external command operable program or batch file

You can add ‘adb’ to the $PATH environment variable (and restart the command prompt).

setx PATH "%PATH%;C:\Users\AppData\Local\Android\sdk\platform-tools"

 

To run logcat through the adb shell, the general usage is:

[adb] logcat [<option>] … [<filter-spec>] ..

This is the official Android Developer Logcat Command-Line Tool documentation:

https://developer.android.com/studio/command-line/logcat

but you can get –help on the command line.

It can be handy to know the device id of your Android phone/tablet whatever. This command will help:


C:\Users\<user_name>>C:\Users\<user_name>\AppData\Local\Android\sdk\platform-tools\adb.exe devices
List of devices attached
ce10171a5c19853003 device

 

You can specify that the log spew into a file instead of into your console (the console is pretty much useless as there is too much to scroll through).


C:\Users\<user_name>\AppData\Local\Android\sdk\platform-tools\adb.exe -d logcat > adb_logcat_out.txt
-d use USB device (error if multiple devices connected)
logcat show device log (logcat --help for more)
-s SERIAL use device with given serial (overrides $ANDROID_SERIAL)

 

The default Log Location on my machine was:
C:\Users\<user_name>\AppData\Local\Temp\adb.log

A few seconds of output got me a 6.5 MB file so a bit of filtering is advisable

If you run into trouble with the adb server just kill it and restart.


C:\Users\<user_name>\AppData\Local\Android\sdk\platform-tools\adb.exe kill-server
C:\Users\<user_name>\AppData\Local\Android\sdk\platform-tools\adb.exe logcat -s ce10171a5c19853003 DEBUG
C:\Users\<user_name>>C:\Users\<user_name>\AppData\Local\Android\sdk\platform-tools\adb.exe logcat -s ce10171a5c19853003 DEBUG
* daemon not running; starting now at tcp:5037
* daemon started successfully
--------- beginning of main
--------- beginning of system

 

If you want further help check out these pages from the Unity Manual and Tutorials:

https://docs.unity3d.com/Manual/TroubleShootingAndroid.html

https://docs.unity3d.com/Manual/LogFiles.html

https://unity3d.com/learn/tutorials/topics/mobile-touch/building-your-unity-game-android-device-testing

As a final word I’ll also direct you to this package called Device Console on the Unity Asset Store. I’ve not used it but it looks really good and for fifteen dollars might save you a lot of hassle.

https://assetstore.unity.com/packages/tools/utilities/device-console-44935