This is more for my future reference than anything else.
You can either watch an hour-long tutorial. Or you can look at this list of steps. Because it’s way too complicated for any sane human being to memorize.
Here’s the hour-long tutorial:
The following tutorial will make a scrollable panel with the following features:
- Vertical scroll
- Buttons (or any other content) have their preferred sizes
- The scrollable panel automatically scales to fit the content
- The content is clipped/masked using an image mask.
Here is your GameObject tree structure with names. I will be referring back to this. Create blank objects with this tree structure.
Panel Background ScrollView ScrollContent Button0 Button1 (other content)
Here’s what each object does, the components it needs and the settings required:
Panel is just the parent. Stuff it wherever and change its size however you like.
- Rect Transform Component. No specific settings required.
A background graphic for your scrollable area. It’s not required.
- Rect Transform Component (added automatically). No specific settings required.
- Canvas Renderer Component (added automatically). No specific settings required.
- Image Component. No specific settings required.
Sets up how the content is viewed.
- ScrollRect Component. Set the Content to ScrollContent.
- Mask Component. No specific settings.
- Canvas Renderer Component (added automatically).
- Image Component. Set an image to set up a clipping mask for the content in the scroll view.
Deals with layout.
- Vertical Layout Group Component. Disable force child expand Height.
- Content Size Fitter Component. Set vertical fit to “Preferred Size”.
Button0, Button1 etc.
- Layout Element Component. Set up the Min Height to something sane like 25
- Any components you want, labels, buttons whatever
And that’s all you need to do to set up a vertical scrollable list!
If you want scrollbars, good luck.
I will be leaving Japan in August, after 6 years of living here. I’ve made some amazing friends, worked in three different fields and had some wonderful experiences. But now it’s time to start a new chapter.
@petitegeek and I are moving to Paris!
By August I will have finished my current project, a Unity-based rhythm game for PlayStation Vita called IA/VT. I was responsible for UI, shaders and the music video director system (special effects and artist tools).
If you need a generalist programmer with experience shipping a game in Unity on PlayStation Vita, I will be available from August.
For more information my CV/resume is on my website and also I have a LinkedIn thing that’s cool with the kids these days.
Originally I thought coroutines in Unity were exclusively for performing asynchronous operations, like
AssetBundles.LoadAsync(). However when I was planning the UI for my new game, the most wise Eddie and Kalin told me about using coroutines to simulate a state machine. It took me a while to understand how this worked conceptually, and then how to implement it. I couldn’t find any particularly good tutorials either.
This is more of a series of annotated snippets than a full tutorial, but hopefully it will be useful for someone.
The important thing to remember with this is that we’re emulating a finite state machine. If you’re unfamiliar with FSMs, read up on them before starting this and sketch out a few FSMs for games you understand, thinking about what possible transitions there are between states, and what setup/teardown each state does.
With that out of the way, let’s look at some snippets!
The first snippet is a template of what we’ll be doing with our UI-related coroutines. There’s not much to say about it beyond what’s in the comments.
Basic Pause Menu
Our second snippet has some code that actually does something useful.
The most important line in this snippet is
yield return StartCoroutine(DoShowPause()). Here we start the pause coroutine, and importantly pause the Start method at that line until the DoShowPause coroutine finishes. As we are simulating a finite state machine, it will only finish when it exits the state.
One convention I like to use with coroutines is to prefix all coroutine method names with Do. The reason for this is if you call a coroutine without wrapping it in
StartCoroutine() the coroutine stops at the first yield, but no warnings or errors are logged. By using a consistent naming convention, it’s easy to notice naked
As in the comments make sure you include
yield return null somewhere in your while loops or Unity will appear to freeze as it goes into an uninterrupted loop.
Player/Enemy Turn System with Pause
The third and final snippet for this tutorial is a very simple simulation of a turn-based game. It has alternating Player/Enemy turn states, and a pause state that can be called from either.
This snippet illustrates the possibility of having “global” states that can be called from any state. However you explicitly have to check for the trigger to enter the global state at a safe point in each state.
As you can see even with a simple situation, the code can get fairly long. In a more complicated game it would quickly become worthwhile to split up the code for each state into its own class.
So that’s a simple example of how you’d use coroutines to set up a FSM for menus. You can jump in and out of states easily and be sure which state you’re in.
After a year of using Unity at work, I’ve learned a lot of new techniques, and my coding habits have changed over time.
When I started with Unity I read a bunch of tutorials and recommended practices, but with practical use I learned other techniques.
Some of these techniques may be blindingly obvious to you, others might have slipped you by.
If you have any tips of your own please add them in the comments section!
Make Most of your MonoBehaviour Variables Public
Programming 101 always teaches you to only make member variables public if you really need them to be modified by other classes. Consequently it feels dirty and dangerous to make everything public.
However after a year I think the benefit of being able to view variable values in the inspector outweighs the danger of exposing private variables.
Personally I make the interface of the class with accessors, and make member variables public for the inspector. By prefixing member variables with m_, even if they’re public it’s clear that you’re accessing a member variable.
Select GameObjects from Debug.Log and Use Color
I didn’t notice there was an optional second parameter for
LogError). It lets you specify a gameObject that is automatically selected when you click on the log message. Also you can add colors to your log. If you have messages that are specifically for different parts of your team, like artists and designers, it can help to highlight them in specific colours.
Make Everything a Subclass of MonoBehaviour for Visual Debugging
Everything might be too strong a word, but most things that can be made a subclass of MonoBehaviour, should be. Even for classes that you think will have no renderers, making them subclass MonoBehaviour and adding them to a GameObject somewhere means you can visually debug them from the inspector and scene views.
This applies for singletons too. Use a singleton instead of a static class, and make the singleton a subclass of MonoBehaviour means you can have the instance of your singleton be a component on a GameObject in your scene. Beyond the usual ability to use the inspector to view values, you can add OnDrawGizmos to it and visualise its current state better.
Deal with Unity’s 90-degree FBX Rotation
Unity imports most models with a rotation of -90 in X. Most modelling programs use a Z-up coordinate system, whereas Unity uses the Y-up convention. For whatever reason, their attempt to fix this is to give a -90 X rotation to any GameObjects created from model files. With this rotation the models look correct in Unity. However if you child any objects to them, or set rotations based on the assumption that 0,0,0 is “unrotated”, you can get unexpected rotations.
The only way to hack around this is to work with your modelling software and build your model with a baked-in rotation of +90 in X on the root node. That way the model is imported with a rotation of 0,0,0, and looks correct. There are tutorials online that go into more detail about this, but it’s something you need the artists to understand and work with from day 1.
Know About Unity’s Model Animation Quirks
This will only apply to you if you’re using imported models with animation. Unity’s treatment of model animations is most charitably described as “unique”. Animation curves are forced to be regular bezier curves. Meaning that 0-frame instant jump your animator put on the Camera node for animation, does not actually take zero frames In fact there will be at least one frame where the camera is partway between the start and end points of the curve. While Unity’s in-engine animation system supports a variety of animation curve types,
Resources.Load is Slower Than you Think, AssetBundle.AsyncLoad is the Solution
For loading a very small number of very simple objects,
Resources.Load() is perfectly fine. However it can get out of hand surprisingly fast.
We had to load around sixteen 256px images and create some complex GameObjects, and the slowest parts of the whole process were but the 16 calls to
Resources.Load(). Each one was synchronous and in total they caused the game to freeze for half a second. Changing the process to use a placeholder image, load each image with
AsyncLoad(), and replace the image when AsyncLoad completed, stopped any freezing.
Instantiate is not Free, You Need to Work to make it Asnyc
GameObject.Instantiate() is another cuprit for freezing. There’s no async version of it, but the slow part in calling it is not usually the instantiation itself but the code that gets called on instantiation -
Start() If your
OnEnable() or any other functions called on startup are heavy and synchronous, every time you create an instance of that GameObject your game will freeze. Split up the work needed into smaller asynchronous chunks by using coroutines.
Don’t Put Everything in the Resources Directory
As a programmer it’s frustrating to have assets outside of the Resources directory as it means you can’t load them at runtime. You’re stuck with using the inspector to set up links to GameObjects in other parts of the file directory.
Our first reaction to this was to put nearly everything in the Resources directory. The problem with this is if you stop using assets, if they’re in the resources directory they’re still put into every build you make. If they’re in the regular Assets directory, they will only be added to a build if they’re used by something included in the build (another
Pick the Right File Format for your Designers and Artists
This isn’t a Unity-specific tip but I think it’s important nonetheless. Our game required both artists and designers to input a lot of data for setting up the game’s visual content. In the early prototype stage we chose JSON as we had an existing importer and it was what we as programmers were used to. This was not a good idea. The artists and designers put up with it very well but a mixture of picky syntax and verbosity the files quickly became unmanageable. We should have built the tool on their terms. They were most comfortable with Excel, so we should have written an exporter and converter for Excel. It would have taken longer on our part at first, but we would have saved hundreds of hours of debugging in the long-run.
GameObject.Find and Transform.Find should be used Sparingly
When I first started using Unity, the lead programmer and I tried to do as much work as we could through code. We had been burned by SVN conflicts for prefabs and scenes where we had to choose whose work would be preserved, and whose work would be lost. The logic was that if we did everything in code, we could easily merge the results and not lose work. To set up objects, I used
Start to find objects higher and lower in the object hierarchy. However some objects were not yet set up in
Awake(), so I moved the
Find() calls to
Start(), but sometimes even then the target object hadn’t been created yet.
Even after untangling that spaghetti, I started noticing how much the calls to
Find() were slowing down instantiation.
The solution to this was to get used to Prefabs and Scenes and set up the objects to avoid calls to
Find(). There was no golden bullet with SVN conflicts but clearly separating responsibilities and confirming with other team members before making major changes to scenes seemed to work out.
Set up your Directory Structure by Use, Not Filetype
This might be more controversial, and is more of a personal preference than something I can empircally prove is better. One file structure I see recommended in Unity tutorials is based around file format. Separating materials, textures etc. into different subdirectories like below:
/Materials/ /Materials/PetShop/ /Materials/Menus/ /Textures/ /Models/ /Shaders/
The benefit of this is it’s immediately clear where a resource should go. The downside is you often have to jump around the directory tree to find related resources. You find Puppy.png and Kitty.png, but you have to jump to the Materials directory to find the material that might use them.
The alternative looks something like this:
/ /PetShop/ /WorldMap/ /Menus/ /Menus/Options/
Each directory contains a mix of filetypes; materials, textures, models.
We kept shaders and scripts outside of this structure in their own dedicated directories, but maybe mixing them with the assets they use might work.
There’s a lot to learn when starting Unity. Some of the lessons are just things you have to experience yourself, but I hope this post saves you a few hours of pain.
3DSMax uses a z-up coordinate system. Unity uses a y-up coordinate system. 3DSMax can export models in a y-up coordinate system. You’d think this fixes things, but Unity ignores this flag and imports all FBX files with a rotation of -90 around the X axis. The models themselves look correct, but the root has a baked in rotation of -90. So if you attach a child to the model, and set its rotation to (0, 0, 0), suddenly it’s rotated 90 degrees. The official “solution” is to set the root to +90X rotation, then build your model’s mesh in a -90X way. But if you’ve made your model without knowing this and made animations, 3DSMax makes this a huge headache to fix (apparently). Etc. All because Unity tries to “fix” the y-up bug.
This has been an open issue with Unity since 2009.
But at least they support Blackberry now.
subscribe via RSS