Bloodirony Blog


Blog

INSIGHTS, TUTORIALS AND DIRTY SECRETS

Viewing entries in
Tutorials

How to support multiple languages in Unity

4 Comments

How to support multiple languages in Unity

Together with our Publisher Noodlecake we have decided to translate Shooting Stars in order to boost the reach of the game.  For the initial release we have decided to go with the 7 most common (AppStore) languages English, German, Italian, French, Spanish, Portuguese and Russian.

To implement more than one language without a lot of fuzz, we wanted to find a solution that perfectly fits our workflow.  After a couple of experiments we have decided to create a custom Text component that allows our designer to easily build localized interfaces without the need to write a single line of code. To keep things simple, we are using simple .txt files to store our internationalized strings as key-value pairs. 

For example:

key=This is a test text

So there is a .txt file for every language we support, with same keys and translated values. To prevent lags during gameplay I load the right language file in a static Dictionary on app start. As a fallback I also load the english version in a separate Dictionary in case of a missing key.

Next step was the actual display of the translated text. Therefore I have overridden the built in Text component and added a key field to my new component.

This is how I load the strings from the file into a Dictionary:

private void LoadLanguage(string lang){
    fields.Clear();
    TextAsset textAsset=Resources.Load("I18N/"+lang);
    string allTexts="";
    if(textAsset==null){
        textAsset=Resources.Load("I18N/en");
    }
    allTexts=textAsset.text;
    string[] lines=allTexts.Split(new string[] { "\r\n", "\n" },
        StringSplitOptions.None);
    string key, value;
    for(int i=0;i < lines.Length;i++){
        if(lines[i].IndexOf("=") >= 0 && !lines[i].StartsWith("#")){
            key=lines[i].Substring(0, lines[i].IndexOf("="));
            value=lines[i].Substring(lines[i].IndexOf("=")+1,
                    lines[i].Length-lines[i].IndexOf("=")-1).Replace("\\n", Environment.NewLine);
            fields.Add(key, value);
        }
    }
}
    

The custom Text component:

public class TextI18N : Text {

    public string i18NKey;

    void Awake(){
        Refresh();
    }

    public void Refresh(){
        text=I18N.Instance.Get(i18NKey, false);
    }
}

UPDATE

After posting this tutorial on reddit, the user justaghostofanother wrote a super detailed do's and don'ts about localization which can be found in the /r/unity3d thread.

4 Comments

How to create a random tileset in Unity2D

2 Comments

How to create a random tileset in Unity2D

When it comes to the background, classic shmups usually use something like infinite scrolling. Because we are not making a normal shmup with spaceships and other flying stuff we wanted to handle this completely different.

Therefore we created a static background, which is not moving during the actual battle. To give the user some feedback regarding progress, we also move the background after defined waves or a boss fight.

To avoid creating hundreds of full sized background assets we thought of randomly create these backgrounds out of a tileset image.


This is actually quite easy, the only thing you need is a image with a defined grid of single tiles. Next thing you will need is something to draw the tiles on. I used the unity plane component with a mesh renderer for that.

First step, initialize the mesh:

int numTiles = size_x * size_y;
int numTris = numTiles * 2;

int vsize_x = size_x + 1;
int vsize_y = size_y + 1;
int numVerts = vsize_x * vsize_y;

Vector3[] vertices = new Vector3[ numVerts ];
Vector3[] normals = new Vector3[numVerts];
Vector2[] uv = new Vector2[numVerts];

int[] triangles = new int[ numTris * 3 ];

int x, z;
for(z=0; z < vsize_y; z++) {
    for(x=0; x < vsize_x; x++) {
        vertices[ z * vsize_x + x ] = new Vector3( x*tileSize, 0, -z*tileSize );
        normals[ z * vsize_x + x ] = Vector3.up;
        uv[ z * vsize_x + x ] = new Vector2( (float)x / size_x, 1f - (float)z / size_y );
    }
}

for(z=0; z < size_y; z++) {
    for(x=0; x < size_x; x++) {
        int squareIndex = z * size_x + x;
        int triOffset = squareIndex * 6;
        triangles[triOffset + 0] = z * vsize_x + x +           0;
        triangles[triOffset + 2] = z * vsize_x + x + vsize_x + 0;
        triangles[triOffset + 1] = z * vsize_x + x + vsize_x + 1;

        triangles[triOffset + 3] = z * vsize_x + x +           0;
        triangles[triOffset + 5] = z * vsize_x + x + vsize_x + 1;
        triangles[triOffset + 4] = z * vsize_x + x +           1;
    }
}

// Create a new Mesh and populate with the data
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.normals = normals;
mesh.uv = uv;

// Assign our mesh to our filter/renderer/collider
MeshFilter mesh_filter = plane.GetComponent();
MeshCollider mesh_collider = plane.GetComponent();

mesh_filter.mesh = mesh;

 

Next step, split your background image into single tiles:

private Color[][] SplitTiles() {
    int numTilesPerRow = texture.width / tileResolution;
    int numRows = texture.height / tileResolution;

    Color[][] tiles = new Color[numTilesPerRow*numRows][];

    for(int y=0; y < numRows; y++) {
        for(int x=0; x < numTilesPerRow; x++) {
            tiles[y*numTilesPerRow + x] = texture.GetPixels( x*tileResolution , y*tileResolution, tileResolution, tileResolution );
        }
    }

    return tiles;
}


Final step, draw your random tiles on the mesh:

int texWidth = size_x * tileResolution;
int texHeight = size_y * tileResolution;
Texture2D newTexture = new Texture2D(texWidth, texHeight,texture.format, false);

Color[][] tiles = SplitTiles();

for(int y=0; y < size_y; y++) {
    for(int x=0; x < size_x; x++) {
        Color[] p = tiles[Random.Range(0, 30)];
        newTexture.SetPixels(x*tileResolution, y*tileResolution, tileResolution, tileResolution, p);
    }
}
newTexture.anisoLevel=1;
newTexture.mipMapBias=0f;
newTexture.filterMode = FilterMode.Point;
newTexture.wrapMode = TextureWrapMode.Clamp;
newTexture.Apply();

MeshRenderer mesh_renderer = _plane.GetComponent();
mesh_renderer.sharedMaterials[0].mainTexture = newTexture;

With a shader on top the final result looks like this

Download Unity 5 Sample Project

As with every previous tutorial, we provide you with a sample project for Unity 5.

2 Comments