How to load Multiple Sprites by name in Unity?

This is an open to suggestions tutorial. Meaning, this is just one way that I’m doing it, if you have any suggestions, please please leave a comment.

It’s funny, I’ve asked this question on Twitter, I Google-d and even asked this guy from a Unity group on Facebook, and either they didn’t understand my question or they didn’t really give me the answer I wanted. Is it because I am thinking in the wrong way and we actually don’t need to do this?

Anyway, someone suggested that I just grab by the index of the “child” sprite in the multiple sprite… but I wouldn’t know what that index is, because this is not for animation, this is just simply treating a Multiple Sprite as a spritesheet.

Oh by the way, I used the awesome TexturePacker and its super useful TexturePackerImporter script to slice the Multiple Sprite in Unity.

So my Multiple Sprite is automatically sliced:

Screen Shot 2014-05-26 at 12.13.07 PM

I mean, sure I can drag the sliced sprites individually into my Scene, but did you know that if you try to use Resources.Load to well, load them by name, it wouldn’t work? And if you are going to suggest why don’t I just create each child as prefabs first, or… I have my reasons for needing Resources.Load.

For example:

GameObject bg1 = GUIMgr.getSpriteObject(Resources.Load<Sprite>(Sprites/Rewards/rewards_bg-iphone4));

or

GameObject bg1 = GUIMgr.getSpriteObject(Resources.Load<Sprite>(Sprites/rewards_bg-iphone4));

Those are going to return a NullReference.

 

By the way GUIMgr.getSpriteObject is just a helper method I have to return a Sprite as a GameObject that is added in the Scene:

public static GameObject getSpriteObject(Spritesprite)
{    
GameObjectspriteObj = new GameObject();

    spriteObj.name = icon.name;
       
    spriteObj.AddComponent<SpriteRenderer>();
    SpriteRenderer spriteRenderer = spriteObj.GetComponent<SpriteRenderer>();
    spriteRenderer.sprite = sprite;

    return spriteObj;
}

 

Anyway, so I decided to do this instead, a Singleton named ResourceMgr:

using UnityEngine;
using System.Collections;

public class ResourceMgr : MonoBehaviour 
{
    private Hashtable m_sprites;

    #region Singleton
    private static ResourceMgr instance = null;
    public static ResourceMgr I
    {    get
        {    if (instance == null)
            {    GameObject resourceMgr = new GameObject();
                resourceMgr.name = ResourceMgr;
                resourceMgr.AddComponent<ResourceMgr>();

                resourceMgr.transform.position = Camera.main.transform.position;
            }
            return instance;
        }
    }
    public static bool HasInstance
    {    get
        {    return (instance != null);
        }
    }
    #endregion

    void Awake()
    {    if (instance == null)
        {    instance = this;

            // ResourceMgr is loaded per scene to avoid keeping too much in memory
        }
    }

    // spritsheet needs to be initialized first and foremost
    // initSpritesheet only accepts Multiple sprites
    public void initSpritesheet(Sprite[] spritesheet)
    {    
        if (m_sprites == null
        {    m_sprites = new Hashtable();        
        }

        foreach (Sprite sprite in spritesheet
        {    
            // sprites with same names are ignored
            if(!m_sprites.Contains(sprite.name))
            {    m_sprites.Add(sprite.namesprite);
            }
        }
    }

    public Sprite getSprite(string name)
    {    
        if (m_sprites.Contains (name)) 
        {    return (Spritem_sprites[name];        
        }

        return null;
    }
}

 

And this is how you use it:

using UnityEngine;
using System.Collections;

public class ResourcesTest : MonoBehaviour 
{
    public string[] m_filenames;

    void Start () 
    {    
        if(m_filenames != null)
        {    
            foreach(string filename in m_filenames)
            {    
                Sprite[] sprites = Resources.LoadAll<Sprite>(Sprites/ + filename);
                ResourceMgr.I.initSpritesheet(sprites);
            }
        }

        GameObject bg = GUIMgr.getSpriteObject(ResourceMgr.I.getSprite(rewards_bg-iphone4));
    }
}

 

Just load up m_filenames with the filenames of the spritesheets, such as Rewards.

And voila! (Maybe I should combine the getSpriteObject with getSprite. Yup, I’ll do that.)

Comments, suggestions, violent reactions, just tweet me @purplelilgirl.

Advertisements

Programmer girls in Taiwan, where’re you at? 台灣女程式設計師,妳們在嗎?

I attended a Google event in Taipei last week, and there were only a handful (quite literally) of girls in attendance, and even much less girl programmers. A guy I met mentioned that he doesn’t meet girl programmers that often, and I replied, “Me too”. And that makes me sad.

I attended another Apple event a couple of months ago in Shanghai. The lunch setup of the event was that there’s a buffet setup, and very few standing tables. So I obviously don’t want to eat holding my plate, so I went towards one of the tables, where a few guys were already at. I asked them if I could stand next to them, and they said okay. And it was sort of awkward, because no one was talking to each other (maybe it’s just because Chinese tech guys are shy). And when they were done eating, they just left. And then I saw another girl with a plate, and I offered the now empty table, and then we started talking (maybe Chinese girls are friendlier), and then another girl, seeing us, joined us, and then another. And one of the girls even remarked, so this is the girls table. And that was kind of fun, and made me feel less alone.

I know I should attend more developer meetups or join more physical game jams, but I don’t, it’s not because I don’t want to be more active, but because the male-to-female ratio throws me off. I would feel intimidated to be one of the very few girls in a predominantly male environment.

But that’s not the way to go, is it? The more us girls don’t attend the events, the less people would know about our existence. The less our voices are heard. And I believe we have voices too.

My point is, I want to know if there are any events or organizations in Taiwan similar to GirlsWhoCode, TechGirlsMovement, GirlGeekCoffees or GeekGirlMeetup? Can I join? Or if not, can I start one? And will people join me?

台灣女程式設計師,妳們在嗎?

How to Create Unity Android Plugins?

Disclaimer: This is based on FastEgg’s Tutorial http://fastegggames.com/blog/2013/3/30/tutorial-admob

Sometimes we need to create Plugins to easily use external frameworks such as Ad Networks in Unity.

Android

1. Create new Android Application Project

* Application Name, Project Name should refer to the specific plugin (admob for a plugin for AdMob)

2. Add Additional Libraries

  • right click on Project and select “Properties”
  • navigate to “Java Build Path”
  • select “Libraries” tab
  • click on “Add External JARs”
  • add Unity’s “classes.jar” (located at <UnityInstallPath>\Editor\Data\PlaybackEngines\AndroidPlayer\bin\classes.jar)
  • add other JARs that are required by the plugin (such as AdMob’s jar)

Unity’s classes.jar

  • it is found in the installation folder
  • C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\bin on Windows
  • /Applications/Unity/PlaybackEngines/AndroidPlayer/bin on Mac

  • select “Order and Export”
  • tick all external JARs to make sure that they will be exported together with the new JAR file

3. Code the Plugin

  • create new class with the same name as the project (such as AdMob.java)
  • make sure to give it the package name of your plugins package
  • create a Constructor
  • can refer to activity using UnityPlayer.currentActivity
package com.example.AdMob
public class AdMob
{ private Activity activity; //Store the android main activity

  //Constructor
  public AdMob()
  { activity =UnityPlayer.currentActivity;
  }
}

4. Export Plugin

  • right click on project and select “Export”
  • in the Export Window, select “Java/JAR file”
  • in Jar Export Window, make sure current project is ticked
  • set export location to our Unity Project’s “Assets\Plugins\Android folder”
  • name plugin (such as AdMob.jar)
  • the wizard will pack the jar file, making it available for Unity

* sometimes plugins require additional JARs to be included our Unity Project’s “Assets\Plugins\Android folder” (such as the AdMob SDK)

5. Call Plugin From Unity

  • create a new MonoBehaviour script in Unity, and name it closely to your plugin (such as AdMobController)
#pragma strict
public class AdMobController extends MonoBehaviour
{ function Awake()
  {
  }
}
  • to activate our new plugin, we need to create a new AndroidJavaObject, and by using our package name, we can have it cast to the type we created in our JAR file:
#pragma strict
public class AdMobController extends MonoBehaviour
{ private static var jo:AndroidJavaObject;
  function Awake()
  { 
   #if UNITY_ANDROID
   jo =newAndroidJavaObject("com.example.admob.AdMob");
   #endif
  }
}

6. Modifying the Manifest

  • Unity will usually create it’s own AndroidManifest.xml file when packaging your project, but also allows for you to override the one it creates with one you define inside of the Assets/Plugins/Android folder
  • you can either create one from scratch, or copy over the default one that Unity has created for your project
  • to get the default, make a build in Unity, and let it complete and save. Then look in your projects temp/stagingarea folder. There should be a AndroidManifest.xml file generated for you. Copy that to your projects Assets/Plugins/Android folder, and edit it using MonoDevelop
  • modify the Manifest file to include things required by your plugin (such as requires Internet): <uses-permission android:name=”android.permission.INTERNET” />
  • save changes

EDIT:

If you set your Android Application as Library, it’s Android Manifest will automatically be merged with any existing ones in your Unity project.

To set your project as Library, go to the project’s Properties > Android and tick Is Library, and click Apply.

It will add a project.properties file to your project, make sure to include that in your jar file.

Screen Shot 2015-01-09 at 4.57.34 PM

7. Testing

  • some Android related things (such as Ads) doesn’t show up in the Unity Editor, so you’d need to build and run on an Android device to test
  • you can use adb logcat in Terminal to debug while running on Android device
  • Ctrl+C stops logcat

That’s it!

logcat

Logcat is the used in terminal to track logs from Android devices.

To call logcat from terminal, just type: adb logcat

To stop adb logcat, just: Ctrl+C

To reset or clear terminal, just type: reset

 

Google Play Service and Unity

Michael McCarry over at FastEgg games is kind enough to write tutorials for how to get Google Play Service to work with Unity.

Texture Packer and Automator

Texture Packer has a command line tool, which means that you can run it as a shell script in Automator. This means that you can get sprites from the Automator and automatically build spritesheets using them.

Why would I want to do this? Why can’t i just use Texture Packer with all its lovely GUI?

Because well for me, I process the sprites first using Automator, before I pack them into spritesheets. Also this is a quick way to not set all the different Texture Packer settings each time (yes, I know you can use Save Defaults for settings).

But double-clicking on my Automator app, and then select my sprites, and wait for it (it’s actually pretty quick) to automagically spit out spritesheets is still faster (and it only requires like 2 to 3 clicks), and it even supports multipack (in case the sprites doesn’t fit in just one spritesheet)

This is my (partial) Automator workflow:

Screen Shot 2014-05-08 at 11.31.15 AM
Screen Shot 2014-05-08 at 11.31.45 AM

A big shout out and thank you to Andreas Lowe for replying to all questions on Twitter, and helping me whip out that Shell Script.

This Automator app requires the Pro version of Texture Packer as well.

That’s it!

 

Sorry for not posting anything since creating this new blog in January, that’s because I temporarily moved back home (hello, Manila).