Old Unity notes
Created: 2014-12-13 16:37:45 -0800 Modified: 2021-03-27 09:16:26 -0700
DISCLAIMERS (12/28/2015): I wrote all of these notes starting almost two years ago, and I never intended to make them public. I also only learned Unity for about a month or two, so I am by no means an expert, and these notes were for an old version of Unity (4.x probably). Some notes may not only be out-of-date, but they could be wholly wrong.
Also, a large section of these notes is devoted to NGUI, which if I understand correctly is now sort of baked into Unity 5.
-
1/31/14: Starting with the Space Shooter tutorialhere. Even though I know a lot about programming, rendering, etc., I learned a LOT about Unity by following along with this tutorial.
-
It’s a very good idea to go to “Edit —> Preferences… —> Colors —> Playmode tint” and change it to something that isn’t gray. That way, when you’re playing your game, Unity will change colors so that you know any changes you make via the Hierarchy/Inspector/Scene-view will only be temporary.
-
You can change layouts just like Visual Studio, but you can save them easily via the layout menu. If you pick the same name as an existing layout, you’ll overwrite it.
-
Press ‘F’ with the cursor over the Scene View to “frame” the selected object, which basically zooms/centers on it. Double-clicking the item in the Hierarchy view will do the same thing.
- REMEMBER: the cursor needs to be over the Scene View for ‘F’ to work.
-
Adding physics is pretty easy. In the Inspector, add a component with ctrl+shift+A. Then choose Physics —> Rigid Body. Your object still doesn’t have a volume yet until you add another component, Physics —> Collider (e.g. Capsule Collider). A capsule collider is actually the space between two spheres, sort of like a pill shape, duh. If you only want your collider to trigger an event (e.g. hitting a ship) as opposed to full physics simulations, then check the “is trigger” box.
-
Easy camera controls:
- Alt+left-click+drag will revolve the camera around what you’re looking at
- Right-click+drag will change the angle the camera (e.g. like moving your neck)
- Clicking the axes in the Scene view will snap the camera to that axis.
-
If you’re tabbed to a field and it’s not editable (but should be), just press enter.
-
When modifying the camera (which, remember, is just a component), you can use the game view to see what looks right.
-
Window shortcuts are great:
- Ctrl+1 for scene view
- Ctrl+2 for game view
-
Whenever you’re dragging the mouse (e.g. in the Inspector or alt+right-click+drag to zoom) the mouse cursor will wrap to the other side of the screen so that you don’t have to stop moving it. What a great feature!
- When dragging without holding alt, pressing escape will revert the value (alt+escape is an OS shortcut on Windows, that’s why you can’t be holding alt).
-
Ctrl+D will duplicate something.
-
You can use an empty game object (ctrl+shift+N) as a “folder”. Note that anything beneath it in the hierarchy will still inherit from it, so you’ll want to reset its entire transform.
-
Directional lights COMPLETELY IGNORE their position it seems. Only the rotation matters.
-
You can drag a texture directly onto the scene view to apply it to a component as a material (Unity will create the material if there isn’t one already associated with the texture).
- You can also first make a material by selecting Assets->Materials in the Project view, then clicking “Create” (also in the Project view) and choosing Material.
- Then, in the Inspector for the Material, expand the material by clicking its “title bar” as you would expand any other component, then select its texture.
-
Lights only interact with things on their own layers (as far as I understand).
-
They add a C# script by adding a component (ctrl+shift+A) and then choosing “New Script”. When you do this, it will get added to the root Assets folder, so you should drag it to your Scripts folder so that everything is organized.
-
After working on your script externally and then coming back to Unity, check the status bar for an error message or read your console (ctrl+shift+C).
- You can double-click the status bar OR the messages in the console to go directly to the faulting line of code.
-
When you add public variables to a script that is attached to an object, you’ll be able to modify those variables directly in Unity. E.g. by adding “public float speed” in the player’s PlayerController script, “speed” now shows up as an editable field in Unity.
- Unity doesn’t know about classes that aren’t serializable, so annotate any new classes with[System.Serializable]. E.g. if you make a Boundary class on your own to hold four variables, then make a “public Boundary boundary” in some other class, Unity won’t enumerate the four variables until you make Boundary serializable.
- Be very careful with this! The value you set in Unity will override a value you set in code in the declaration, e.g. “public float velocity = 2.0f;“. If you set this to 10.0f in Unity, then that’s what it’ll be when the object is created.
-
When you can, use a mobile shader instead of a regular shader. You may sacrifice some appearance/functionality, but it will perform better.
-
Note: when you make a quad, it comes with a mesh collider attached to it.
-
You can drag prefabs right to the Hierarchy view from the Project view WHILE you’re playing the game.
-
When making a collider, you can use the Inspector to change the properties, or you could hold shift and drag right in the Scene view.
-
Even while you’re playing the game, you can still switch to Scene view and interact with objects there. Remember, this is basically just a more visual way of using the Inspector.
-
Use “Debug.Log(string)” to log things to the console (ctrl+shift+C to view).
- Remember that you always see the most recent error/message in the status bar of Unity, so Debug.Log will also print to the status bar.
-
You can tag game objects in the Inspector (in the header under “Tag”) and then use the tag in code (other.tag == “Boundary”).
-
GUI text is not shown in the Scene view. Also, it’s drawn above the entire game. It draws to Viewport Space, which goes from (0,0) at the bottom left to (1,1) at the upper right.
- Don’t forget that when setting it, there’s a “pixel offset” vector2 in its GUIText component that makes it easier to set things like padding.
-
Remember to group your game objects in the Hierarchy by making empty game objects. For example, make something called “Lighting” and put the various lights in there. Then, any directional lights you have can be moved up 100 units in the Y axis since their position doesn’t matter.
-
Each component (e.g. a sphere collider) has a book with a ”?” on it that links to the reference page for that component. This reference page has a “Switch to Scripting” button that will tell you about code for that component.
-
From here: any collider with a rigidbody component is considered dynamic. Any object with a collider and NO rigidbody is expected to be static. Static colliders are cached by Unity, so if you end up transforming them as you would a dynamic collider, then the cache will need to be reconstructed, and that takes resources. So add a rigidbody to all colliders whose position/rotation/scale you change.
-
Kinematic rigidbodies cannot be affected by physics forces (e.g. gravity/drag), but its transform can still be modified to move/animate it. This is what we used for the PickUp items in the RollABall game. PickUp objects are all being rotated, so they should be dynamic colliders, which means they need a rigidbody. However, having a rigidbody attached would cause it to be influenced by physics forces. Simply unchecking the “use gravity” checkbox is not enough as there are other physics forces that would impact this object. Kinematic rigid bodies are only moved using transforms, NOT physics.
-
Marking an object as static will allow for static batching, which consumes more memory but reduces draw calls for geometry of any size (provided it does not move and “shares the same material” (as what, I don’t know, maybe just that the material never changes)).
-
You can toggle built-in lighting by clicking the sun icon at the top of the Scene view. It may affect performance, so you can always turn it off.
-
Baked lighting makes for more performant lighting, but it’s not dynamic because it’s baked right into the texture.
- Using the Lightmapping window, you can bake the lighting in (which takes a few minutes since it needs to modify textures). This requires the Pro version.
-
So that you can freely change your tags, it’s a really good idea to have a Tags script that simply enumerates your different tags as public const string TAGNAME = “TAG VALUE”;
-
GUI Textures can be used, for example, to fade your entire screen in or out.
-
To find a child object of a game component, you can use transform.Find().
-
If a GameObject has an AudioSource on it, then you must get the GameObject (using FindGameObjectsWithTag) and THEN get the AudioSource from them by just accessing “.audio” on it.
-
To see if there’s line-of-sight between two objects, use Physics.Raycast. This results in a RaycastHit object, where you can access “.collider.gameObject” and compare it to whatever it is you’re looking for (say, with GameObject.FindWithTag()).
-
When modifying a Hierarchy object that happens to also be a prefab, changes in the Inspector will only save to that specific instance of the Hierarchy object. However, at the top of the Inspector is a “Prefab” section that you can use to save to the entire class of objects (i.e. the prefab itself).
-
Animations can get pretty advanced. At its most basic, you can do something simple like change the transform using the Animation window and some key frames (and then it will interpolate between the positions at the key frames). However, based on this, there’s a state machine that you can hookup to animations, and you can add curves to control things like collision values during your animation, e.g. a character that rolls when it hits the ground could have its collider height set lower so that it could roll under a fence. Note: in that example, the curve is controlling an intermediate float variable (e.g. “AmountToChangeColliderHeightBy”), then you later use a script to do the actual change, i.e. you can’t use a curve to directly change anything other than intermediate variables (as far as I know), and these intermediate variables are called Parameters.
- When modifying animations or using these curves, it’s really helpful to pause the gameplay, go to the Scene view, and then go frame-by-frame to see what effects you’re having on the object, e.g. its collider.
- You can use layers to separate unrelated animations. For example, running in the 2D Bridge Tower Defense is separate from shooting the bazooka since running controls the legs/hands/body, and shooting only controls the bazooka, so they’re on two different layers.
- Layers are on an Animation Controller, which show in the Animator window. The only way I can figure out for opening this window is to find an Animation Controller in your Assets folder and opening it from there (or finding a controller on another game object, e.g. a player object).
- Transitions between layers are based on parameters or Exit Time. Parameters have a type and can be set through code like this:anim.SetTrigger(“Jump”); // “Jump” is a boolean parameter (hence SetTrigger), and anim is an Animator, typically private, that you set withanim = GetComponent<Animator>();
- Trigger parameters automatically set themselves to false on the next frame.
-
Color.Lerp(ColorA, ColorB, float) will be 100% ColorA when the float is 0. When the float is 1, it will be 100% ColorB. That means that if you do this: Color.Lerp(Color.red, Color.blue, .5f), it will never be red or blue, always purple (or whatever halfway in between is). I didn’t find renderer.material.GetColor to be a reliable way of getting the current color, so this is the code I used to interpolate between two colors without needing to do tricks with the time part:
- currentColor = Color.Lerp(currentColor, desiredColor, Time.deltaTime);
- renderer.material.SetColor(“_Color”, currentColor);
- I think that the reason I needed to use the above is because Unity makes a separate copy of the material when you modify it, so the below WOULDN’T work:
- renderer.material.SetColor(“_Color”, Color.Lerp(renderer.material.GetColor(“_Color”), desiredColor, Time.deltaTime));
- Note: doing Color.Lerp between two constant colors is great for something like a healthbar
-
To visually modify the vertices of a polygon collider, hold left-shift in the Scene view and you’ll be able to change the shape.
-
If you lock a layer (by going to the Layers menu and clicking the lock icon), then you won’t be able to select anything in that layer from the Scene view. You can still move objects in locked layers, but you have to select it first in the Hierarchy view, THEN the Scene view will let you move it. This is useful when you’ve settled everything in your background layer and you don’t want to accidentally click/move it when modifying things in the foreground.
- I’m PRETTY sure Unity does this to every single project you open if you lock the default layer, so if you can’t click anything in the Scene view, that’s probably why.
-
In the Bridge Tower Defense 2D game, the way that they check if the character is on the ground is with this:
- File all appropriate scene objects under the “Ground” layer.
- Put an empty game object called groundCheck under the player in the Hierarchy. Locate it at the player’s feet.
- In the player’s script, include GroundCheck as a Transform (we don’t care about any other aspects of it as a GameObject).
- In the player’s script, do a Physics2D.LineCast from the transform.position to the groundCheck.position, but for the layer mask, specify “1 << LayerMask.NameToLayer(“Ground”)“. If this returns true, then it means you’re touching the ground.
-
When a texture’s type is “Sprite” and the “Sprite Mode” is “Multiple”, you can open the Sprite Editor from the Inspector and slice the sprites manually or automatic. This saves them as individual sprites underneath the texture in the Hierarchy.
-
When a player loses in the game, you can disable a script usingGetComponent<NameOfScriptClass>().enabled = false; - this is helpful for preventing the player from moving/firing.
-
IMPORTANT: Unity’s scripting only exposes Unity components. To get custom components, do PlayerController playerController = GameObject.FindWithTag(“Player”).GetComponent<PlayerController>();
-
If you want something to happen in a certain amount of time, do this:
- StartCoroutine(“ReloadGame”);
- IEnumerator ReloadGame() // this is its own function
- {
- yield return new WaitForSeconds(2);
- Application.LoadLevel(Application.loadedLevel)
- }
-
Animators can call game functions in their animation timelines. Just right-click the timeline and click “Add Animation Event”, then choose a function (which I’m pretty sure doesn’t even have to be public) from associated scripts. This is useful when you want something like an explosion graphic to play and then remove itself. Note that the function you expose for that specific purpose just needs to wrap “Destroy(gameObject);“.
-
Animations can rapidly change the position of something too, so if you want clouds to constantly move from right to left in your game, then put them on a simple transform.x animation and reset the x position at the end to something out-of-view.
-
Making a new project:
-
Make a new folder called _Scenes Make a new folder called Scripts
-
Make a new scene (ctrl+N) and save it in that folder (ctrl+S).
-
GameController:
- Make a new GameObject (ctrl+shift+N).
- Rename it (F2) to “Game Controller”.
- Reset the transform if you want.
- Tag it as GameController.
- Make a new script called GameController and associate it with the Game Controller.
- Include “using System.Collections.Generic;” if you want to use something like List.
-
SAVE YOUR SCENE OFTEN. Unity hangs so frequently that you’ll hate yourself if you don’t.
- When you DO reload Unity, remember that your scene probably won’t load by default, so you probably won’t have lost EVERYTHING.
-
If you’re going to make a networked game, go into Edit -> Project Settings -> Player, then check the “Run In Background” box so that the game runs even when it doesn’t have the focus.
-
-
To test if the mouse hit a component, just use OnMouseDown. Unity handles the raycasting for you, so this will trigger on the object closest to the camera.
-
To figure out the extent of what the camera can see (helpful in an orthographic game):
- Vector3 maxWorldCoordinates = Camera.main.ViewportToWorldPoint (new Vector3 (1, 1, Camera.main.nearClipPlane));
- Note: if you make a resizable window and only stretch one dimension, then your camera will draw the background (or whatever is in your scene) to the “blank” space you add by resizing, so more of your world IS visible, but only if you stretch the window in a single dimension. If you stretch it in both, then everything will just draw bigger.
-
It’s better to make a “3D Text” component through the GameObject —> Create Other menu than to add a Text Mesh to a new GameObject because you want the FontMaterial to be set.
-
If your class derives from MonoBehaviour, use Awake() instead of a constructor. If it derives from ScriptableObject, use CreateInstance. Otherwise, use the constructor. Note: there’s a nice chart here.
- Awake is called after all the objects are initialized so that you can speak to them safely. I believe this is right after Instantiate is called.
- GameObject newPlanet = Instantiate(somePrefabYouSetThroughTheInspector, position, Quaternion.identity) as GameObject;
- newPlanet.GetComponent<PlanetController>().SetSize(size);
-
To debug scripts with MonoDevelop, do the following:
- Debugging from the beginning:
- [one time] Go to Edit —> Preferences —> External Tools —> Check the “Editor Attaching” box
- Go to Assets —> Sync MonoDevelop Project. This will launch MonoDevelop. UPDATE (4/19/14): if you’re using Visual Studio, then all of these steps will still work except this one. You need to manually launch MonoDevelop, then open your PROJECT-csharp.sln file (MonoDevelop understands this).
- Close Unity
- Press F5 in MonoDevelop to debug
- Unity will open again (choose your project)
- Press “Play” in Unity
- Your breakpoint should be hit now in MonoDevelop.
- After continuing execution in MonoDevelop, you can simply stop execution in Unity and hit the Play button (in Unity) again without having to close/reopen it; your breakpoint will still be hit. Code modifications will also work at this point without restarting Unity.
- I’m not POSITIVE that what I’ve written above is correct, so you should check it for yourself, and when in doubt, just restart Unity.
- Also note: unless you’ve configured Unity to run in the background, it may appear that Unity is broken-in when really it just needs the focus.
- Attaching to a process
- From MonoDevelop, go to Run —> Attach to Process
- You can click the “Pause” button (in MonoDevelop) and view threads with View —> Debug Windows —> Threads, or instead of pausing you could’ve just set a breakpoint.
- Debugging from the beginning:
-
From the Inspector, you can click the little cube icon at the upper left to change an object’s icon. This is how you can get name labels to show up.
-
I wanted to make an invisible quad. You can do this with one of the following methods:
- [PREFERRED] Simply remove its renderer. Note that if you still have the quad selected in the Hierarchy view that its collider(s) will show in the Game view.
- Put the quad in a new layer (Inspector —> Layer —> Add Layer…), then go to your camera and change the Culling Mask so that it won’t display that layer.
-
The “Stats” button in the Game view will show FPS, memory usage, triangles/vertices used, etc.
-
Networking wiki is here.
- They advise looking over the entire Network class.
-
“Network view” is a game component you can add to an object (Add Component —> Miscellaneous —> Network view). Each Network View has a State Synchronization mode (“off”, “unreliable”, or “reliable delta compressed” - see below or here for an explanation) and a game object that it observes changes in. You can synchronize different COMPONENTS (not OBJECTS): Transform, Animation, RigidBody, and MonoBehaviour (which are scripts that will just call OnSerializeNetworkView internally). When you drag a game OBJECT to this, it will default to its Transform. I don’t know how to change that, but I do know that you can drag the current object’s Animation, RigidBody, or MonoBehaviour there.
- Off: if you’re only sending RPC calls, then you can turn off State Synchronization.
- Reliable Delta Compressed: this means that only the modified data is sent (e.g. modifying a Transform’s position will not send the rotation), and it’s resent until it’s received. All “future” packets are buffered while waiting for any dropped packets.
- Unreliable: since you can’t guarantee receipt, this sends the entire state (e.g. only modifying a Transform’s position will still send the rotation) X times per second, where X is Network.sendRate (default is 15, which is apparently “about right” for an action game).
- General guidance says to use Unreliable for quick, frequent updates since later updates will fix any dropped packets. When data doesn’t change so frequently, use Reliable to save bandwidth.
-
OnSerializeNetworkViewis calledfor both writing/reading. The owner is the person for whomnetworkView.isMine == true; stream.isWriting will be true for that person, so they should serialize the state. A good way to use this is to do “networkView.observed = this;” in the Start() function of the script that implements OnSerializeNetworkView so that the script is actually being observed.
- Your synchronization can’t be set to “Off”. Both of the others will work.
- BE VERY VERY CAREFUL WITH THIS! Setting it to “delta compressed” means that you may do something like this:
- Client connects to the server.
- Client receives objects spawned with Network.Instantiate. These are spawned at the Vector3 representing position. Nothing unusual so far.
- The unusual part is actually expected, but it goes like this: you set the synchronization to “delta compressed”, so the client will only receive CHANGES.
- When the client got the spawned object, let’s say it was at (0,0,0). The server had since moved to (5,5,5), but the client wasn’t connected for that, so the client gets the object at (0,0,0).
- The server modifies his version of the object by pressing the right arrow. This changes the position to (6,5,5) on the server. However, serializing transform.position (a Vector3, which is valid) will still ONLY send the X component, since that’s all that was modified.
- The client’s view of the object is now (6,0,0).
- To fix this, either set the synchronization method to “unreliable” so that it sends the entire state regardless of changes, or send the current state of the world when the client connects so that all it will need is the changes.
- BE VERY VERY CAREFUL WITH THIS! Setting it to “delta compressed” means that you may do something like this:
- There needs to be more than one player connected for this function to be called.
- You can have “Observed” set to None in the Inspector for your game object since the script can set it with “networkView.observed = this;“.
- Both the client and the server will call this function.
- Your synchronization can’t be set to “Off”. Both of the others will work.
-
To instantiate something on some/all clients at once, use Network.Instantiate. This takes in a group number (which I believe is also called a “relevant set”). I couldn’t find much documentation, but look at NetworkView.SetScope.
- Note: according to this, Network.Instantiate apparently suffers from bugs, but I think this was from quite a long time ago. He instead suggests RPCs combined with Network.AllocateViewID(). I guess one thing that I’ve noticed is that Network.Instantiate will create the object even for future players, whereas you’re probably managing the state of the world yourself and don’t want to send created-and-then-destroyed objects to new players (rather you only want to send extant objects).
-
There is a MasterServer that can be used to put clients in touch with one another, and apparently this is open source. Read about more here.
-
There are built-in social APIs that Unity can use to connect to something like XBox Live (see here).
-
The NetworkManager can be used to modify logging for networked games (Edit —> Project Settings —> Network). You can also debug networked games by launching two instances of Unity (which is really easy on Windows, but on Mac you should read the guidance) and then using the Inspector/Hierarchy to track object creation and inspect view IDs (etc.).
-
If your TextMesh is rendering on top of everything else, then give it a 3DText Shader (reference).
- Note: Unity can import TTFs directly, just drag them into your Assets folder in Unity
- Note #2: if your preview picture doesn’t look just like the wiki, it’s because you have some text selected already (for me, it was the number “24” which was being displayed in the small preview window, so it LOOKED like it was blank).
-
Disabling a MonoBehaviour (with “enabled = false;”) will only stop certain functions from working: Update, LateUpdate, and FixedUpdate. Also, OnEnable and OnDisable are affected too of course (reference).
-
If Unity is starting fullscreen despite that “Default is Full Screen” is unchecked in the Player settings, then that’s because you ran it fullscreen before. To fix this, set “Display Resolution Dialog” to “Enabled”, then check the “Windowed” box, then you can disable the dialog and it won’t be fullscreen anymore.
-
To use NGUI 2.7.0 (which is free), download it from here, then do the following:
- Make a new project
- Open the unitypackage file you downloaded.
- Delete the entire “examples” folder under “Assets”. The examples are about 16-17 MB of the total 17-18 MB, so you’re left with about 1 MB of files required for NGUI. For more information on how the examples work, see the other note in this file about NGUI.
- All the UI things that you make have their own camera. You should make sure that your layers are set up correctly so that only the UI’s camera is viewing the UI (modify its Culling Mask)! This also means that you can locate the UI in the middle of nowhere in the Scene View (by changing the transform of the UI Root) since it has its own camera under it. It may make it easier for you to see how the UI is laid out.
- If, for whatever reason, you add a widget to the UI and it doesn’t work (e.g. hovering over a freshly made button doesn’t trigger the default scripts), then it’s VERY LIKELY that you somehow added it to the wrong layer (usually “default”). I think I found out how I did this too. I did ctrl+shift+N to make a new game object just to organize my stuff, and Unity of course gives that a default layer. Another thing to keep in mind with ctrl+shift+N, as usual, is to reset the position/transform so you don’t end up with huge components.
- Other reasons why your widget doesn’t react to the mouse:
- It doesn’t have a collider
- The collider is has is of size Vector3.zero
- Another collider is covering it - this could be a depth issue too. Things that DRAW (e.g. labels and sprites) have a depth, and you should make sure to specify a unique depth for each UIWidget;CalculateNextDepth will only tell you the next depth amongst the immediate children, i.e. it’s not a recursive function.
- For any of the above (in version 2.7.0 at least…), it’s possible that your object is being created in code while its GameObject is not active. That would give bounds of 0. The solution is to enable your GameObject, add a collider to it with NGUITools.AddWidgetCollider, then call gameObject.SetActive(false) on it.
- To make a simple label…
- Open the fonts folder on Windows (win+R —> “control.exe” —> search for “fonts” —> drag a font onto your Assets and then delete the 3 “variations” of the font so that you’re left with the “original”)
- Click the “NGUI” menu in the toolbar at the top. Choose “Create a Label”.
- Under the Inspector, you’ll see “Font”. Click that, then open the Font Maker.
- Choose “Type” —> “Dynamic” and drag your TTF font onto the window. The size that you choose should likely be the size you want to use (as opposed to making a font of size X and then scaling its transform). Click the “create” button or whatever it’s called when you’re done.
- The font will now exist in the root of the Assets folder. Back in the Inspector for the label, choose the font you created. You can now set its text.
- Note: you can very easily set a font outline/shadow in the GUI directly. There’s no easy way that I found to do this in vanilla Unity.
- To make a button, use the Widget Wizard (NGUI —> “Open the Widget Wizard”). Everything is already set up for you. You only need to add any script with “void OnClick()” in it. You can put any code you like in the OnClick().
- If you’re not getting events after adding the wizard-created button, then you’re probably masking events on the wrong layer. Select UI Root —> Camera in the Hierarchy, then for “Event Receiver Mask”, choose the layer containing your UI objects. This is “Default” by default.
- Atlases are a group of textures placed in a single texture (i.e. it’s a spritesheet). It’s not an NGUI concept, and it’s helpful because the graphics card only needs to bind a single texture in order to draw multiple parts of something. To make one…
- Drag images into your project.
- WARNING: Atlases seem to form spritesheets FOR you, i.e. you drag “sword.png”, “shield.png”, and “armor.png” onto your project, NOT “all_items.png”. NGUI 2.7.0 was made in 2012, and back then, Unity didn’t have a sprite creator built in. It doesn’t seem like there’s an easy way to incorporate Unity’s auto-sprite-slicing feature with NGUI.
- If you’re in 2D mode, then they might get imported as sprites. To change that, go to the Inspector and switch “Texture Type” to “Texture”.
- NGUI —> Open the Atlas Maker
- Select your texture(s) in the Project View. You’ll see them marked for “Add” in the Atlas Maker.
- Click “Create”
- You’ll see a “New Atlas” material in the root of your project.
- Drag images into your project.
- UISlicedSprites are sprites that are intended for buttons/borders, etc. They have corners, edges, and a middle part, and they know how to expand themselves. To make one, just choose a sprite in an atlas and set its borders. If all borders are 0, then it’s not a sliced sprite.
- TABLES WERE THE BANE OF MY EXISTENCE FOR LIKE 3 HOURS ON 3/13/14. I was trying to follow the QuestDialog example, and I had everything EXACTLY the same as that project other than the transforms. Well, the transforms were the problem. Basically, when you’re having trouble with tables and the reposition button, it’s because you need to reset the transforms (specifically the positions) of all of the game objects in the hierarchy under the table (I highly suggest the Hierarhcy’s multi-select to help with this). After that, reposition the table, then you can make adjustments so that everything looks good. If you don’t do this, you’ll end up with very strange paddings between game objects.
- NGUI grid vs. table discussion here. Summary: a grid is like a table whose cells don’t change to fit their contents. A table DOES change the cell size to fit its contents, which is useful when your content size is variable (whether that variability is introduced at runtime or not). For example, in the quest log example, there are three quests whose contents are collapsed. When you expand them, the cell grows to fit the content, which will push the next cell down.
-
NGUI 2.7.0 making the GUI through a script - I only messed around with the basics since some things were not totally obvious.
- public class InitializeGUI : MonoBehaviour {
- public UIFont labelFont; // this is the easiest way to use a font for your label - to provide it through the Inspector. I’m sure there’s a way to look it up with code too.
- public GameObject parameterTween; // this was simply the GameObject I wanted to add the label to. Again, I provide this through the Inspector.
- void Start () {
- UILabel lbl = NGUITools.AddWidget<UILabel>(parameterTween); // Make the label under the specified GameObject
- lbl.font = labelFont;
- lbl.name = “Test Label”;
- lbl.pivot = UIWidget.Pivot.TopLeft;
- lbl.text = “New Label”;
- lbl.color = Color.red;
- lbl.active = false; // do this if you’re adding the object to something that’s intended to be disabled, e.g. a Tween that hasn’t triggered yet
- lbl.MakePixelPerfect();
- }
- }
-
Explanation of NGUI 2.7.0 example scene 13, Tabs.unity
- The tab buttons are arranged like this in the hierarchy:
- Tab 1
- Animation
- Highlight
- Label
- SlicedSprite
- Animation
- Tab 1
- Tab 1 is responsible for a lot of scripts:
- Highlighting SlicedSprite when you hover over it.
- Moving the buttons when you press them. UIButtonOffset does this (you can see there is a transform for “Pressed” that shifts them a bit).
- Plays a sound when the button is clicked.
- Animates the buttons on hover (see below).
- Turning itself into a checkbox. It’s part of the “Window (Transform)” as its radio button group, so only one checkbox under that component can be checked. Changing this to something else (e.g. “Tab 1 (Transform)”) will allow both tabs to be shown at once.
- When you click the button, Content 1 becomes activated, meaning it will show. When you click a different button, Content 1 becomes deactivated, meaning another tab can show ITS content.
- Animation is simply a Unity Animation. You can view it in the Animation Window. It just scales the button a bit to give it some visual appeal. NGUI will automatically add the ActiveAnimation script when it is indeed active, but stopping the game will remove that component from the game object.
- Highlight is just a colored box that fades in/out depending on whether the tab has the focus. It fades smoothly using the TweenAlpha script, which comes from UICheckbox.cs (because Tab 1 has “Instant Tween” unchecked in the Inspector).
- Label simply reads “Tab 1” and has no scripts, nor does it ever seem to be given them during execution.
- SlicedSprite is simply the background of the button. Its depth is 3 (lower farther back), so it shows behind the highlight (depth 4) and the label (depth == 5).
- The tab buttons are arranged like this in the hierarchy:
-
NGUI 3.5.7 - wow, still super frustrating to figure anything out on your own. Always check the forums or the help for a component.
- Tables
- Right-clicking the UITable in the Inspector will let you “Execute” the table, which is the “Reposition now” from 2.7.0. I don’t think it ever hurts to do this since it will happen anyway when you start your game. As always, reset the position of your entire child-tree before clicking “Execute” for the best results.
- Scrollviews
- Right-click the UIScrollView in the Inspector and click “Reset Clipping Position” if your widgets are getting moved randomly when you click Play. The comments for that function (ResetPosition()) suggest that you do it before AND after you repopulate a scroll view’s contents.
- UIWidget vs UIPanel vs GameObject
- UIPanel is heavy-duty and should only be used for contents that require clipping (i.e. scroll views)
- UIWidget has a rectangle for easy layouts. Let’s say you’re making a standard button - there’s a label and a sprite underneath a UIWidget. You can anchor the label to the background and the background to the widget so that resizing the button will automatically lay out the children.
- The other nice thing about UIWidget is that alt+shift+W will create it, and more importantly, it will automatically have the same layer as the parent, which is important when you’re not using “Default” (which all GameObjects reset themselves to).
- Tooltips
- I had two big problems with tooltips: the background wouldn’t scale correctly, and parts of the tooltip may show offscreen. To fix them both:
- Make sure that the tooltip is a sibling of the camera in the hierarchy.
- Make sure the tooltip itself has a reset transform, and especially make sure that the label in the tooltip has a position of X=16,Y=-10,Z=0. The specific numbers can change a bit, but it’s important that they’re not random numbers.
- Anchor the background sprite to the label.
- The tooltip itself doesn’t have to be a widget, and it doesn’t need to be a child of an Anchor widget.
- I had two big problems with tooltips: the background wouldn’t scale correctly, and parts of the tooltip may show offscreen. To fix them both:
- Tables
-
To use Visual Studio with Unity, go to Edit —> Preferences —> External Tools. Set the “External Script Editor” to something like”C:Program Files (x86)Microsoft Visual Studio 12.0Common7IDEWDExpress.exe”, and for “External Script Editor Args”, remove the ”$(File)” so that the line is blank. At that point, you should be able to click Assets —> Sync MonoDevelop Project and Visual Studio will load. If not, go to your root Unity folder (i.e. the parent of the “Assets” folder) and double-click PROJECTNAME-csharp.sln.
- Note that apparently debugging is only really supported with MonoDevelop unless you want to buy a Visual Studio plug-in.
- Just like with any other Visual Studio project, if you don’t open the sln file, you won’t get proper Intellisense.
- Unity will complain about line endings. There are two possible solutions:
- Fix the template script file from an elevated text editor:
- UnityEditorDataResourcesScriptTemplates
- 81-C# Script-NewBehaviourScript.cs.txt
- UnityEditorDataResourcesScriptTemplates
- Change line endings on save (this MAY be a per-file setting, not sure, so I would fix the template file above):
- File —> Advanced Save Options… —> Line endings: Windows (CR LF)
- If you don’t see Advanced Save Options, then go to Tools —> Customize and modify the File menu so that it shows.
- Fix the template script file from an elevated text editor:
-
To use a spritesheet for a tilemap, I found the best way to do this was to write some custom code, since there’s no “easy” way to switch which sprite you’re using in a “Multiple” sprite (i.e. a Unity spritesheet). You CAN switch the sprite with the following, but it’s not the best:SpriteRenderer renderer = GetComponent<SpriteRenderer>();Rect newRect = renderer.sprite.rect; newRect.x += tilesize; renderer.sprite = Sprite.Create(renderer.sprite.texture, newRect, new Vector2(0.0f,1.0f)/top-left pivot/,tilesize);
- Here’s a better way, although it requires that your sprites are placed in a grid
- using UnityEngine;
- using System.Collections.Generic;
- public class SpriteCollection
- {
- private Dictionary<int, Sprite> sprites; // this is a dict so that sprites aren’t created until they’re needed. You could just as easily make it an array.
- private Texture2D spriteTexture; // the “spritesheet” texture - this is treated as a grid of sprites
- private int numCols; // number of columns in the texture
- private int tileSize; // size of a single tile
- public SpriteCollection(string path, int numCols, int tileSize)
- {
- }
- public Sprite GetSprite(int number)
- {
- }
- }
- To use the above code from your GameController, do this:
- SpriteCollection spriteCollection = new SpriteCollection(“env24”, 57/_numCols/, 24/tilesize/);
- GameObject go = new GameObject();
- SpriteRenderer rend = go.AddComponent<SpriteRenderer>();
- rend.sprite = spriteCollection.GetSprite(index);
- go.transform.parent = this.transform;
- To modify the sprite after it is already loaded, just get the SpriteRenderer’s sprite and set it to the result of GetSprite (something like this: GetComponent<SpriteRenderer>().sprite = spriteCollection.GetSprite(newIndex)).
- Here’s a better way, although it requires that your sprites are placed in a grid
-
Lighting sprites is really easy (reference)
- Create a new material in the Project view (Create —> Material)
- Set its shader to Sprites/Diffuse
- Apply the material to your sprite
-
To use the A* Pathfinding Project from here:
-
I wanted to make a GridGraph for my 2D game:
-
Add a new GameObject and add the Pathfinder component. Through the Inspector, configure a GridGraph. Make sure the rotation is X=90, turn off cut corners most likely, and check “Use 2D Physics”. Under “Collision testing”, use Ray. You NEED to set a Mask, even if it’s Everything, otherwise nothing will collide with this. I made a new layer called Walkability. Anything with a collider on theWalkabilitylayer can now modify the graph, but you need some code too:
-
GameObject pathGo = GameObject.Find(“/Pathfinder”); AstarPath activePath = AstarPath.active; GridGraph gridGraph = activePath.graphs[0] as GridGraph; //this assumes you only have a single graph in your project. If not, just initialize all of your graphs, probably using graph.matrix. gridGraph.collision.Initialize(gridGraph.boundsMatrix, 1); // according to “Scan”, you need to call collision.Initialize first. activePath.Scan(); // without this, your graph remains unchanged, but you can always Scan from the Inspector.
-
To give an object a collider that will affect this:
-
-
-
If you’re going to have per-tile collision that can change, then perhaps one of the following is best:
- Make GameObjects with colliders but no renderers for each tile and set the layer to Walkability. Disable the collider until you want to make the tile unwalkable.
- OR
- Make game objects only for tiles that are unwalkable (also with a collider but no renderer as above).
- Make GameObjects with colliders but no renderers for each tile and set the layer to Walkability. Disable the collider until you want to make the tile unwalkable.
-
Note: calling Scan() can take many milliseconds to update a graph. GridGraphs are optimized for UpdateGraphs and will only update regions modified, but you need to tell it which areas were modified by passing in a Bounds object
-
The above works after you’ve created a game object that collides with the graph, but if you REMOVED a game object, then you’ll need to do the following:
- Destroy(someGameObject.GetComponent<BoxCollider2D>()); // this is needed so that the game object in question no longer intersects with the graph. UpdateGraphs will only check the physics at the area you pass in; it won’t just disable/enable walkability, otherwise a future .Scan() wouldn’t know about your changes since it too is based on physics/colliders.
- Bounds b = new Bounds();
- b.center = someGameObject.transform.position;
- b.size = new Vector3(1, 1, 0);
- GraphUpdateObject guo = new GraphUpdateObject(b);
- guo.setWalkability = true;
- guo.modifyWalkability = true; // Tell UpdateGraphs to change the walkability of tiles to whatever the value of ‘setWalkability’ is
- AstarPath.active.UpdateGraphs(guo);
-
To make a GridGraph entirely from code, see the documentation
- // This holds all graph data
- AstarData data = AstarPath.active.astarData;
- // This creates a Grid Graph
- GridGraph gg = data.AddGraph(typeof(GridGraph)) as GridGraph;
- // Setup a grid graph with some values
- gg.width = 50;
- gg.depth = 50;
- gg.nodeSize = 1;
- gg.center = new Vector3 (10,0,0);
- // Updates internal size from the above values
- gg.UpdateSizeFromWidthDepth();
- // Scans all graphs, do not call gg.Scan(), that is an internal method
- AstarPath.active.Scan();
-
WARNING: if you know for sure that a path doesn’t exist between ‘start’ and ‘end’ but you’re still getting one returned with the path’s CompleteState set to Complete, then that’s because your “Max Nearest” under “Settings” in the Inspector is set too high (the corresponding variable is maxNearestNodeDistance in AstarPath.active). This value is used when the endpoint is unreachable; ABPath.cs will call “NNInfo endNNInfo = AstarPath.active.GetNearest (endPoint,nnConstraint, endHint);” and silently change your endpoint. This is probably desirable behavior in most cases, e.g. you attempted to path to a nonwalkable tile (e.g. a building) that you couldn’t see. Rather than not move at all, you’d at least want to get close.
-
Still, to prevent this behavior, set the Max Nearest to 1 (not zero). This will cause an error when a path can’t be found.
-
-
To create a new C# script via the keyboard:
- Ctrl+5 to go to the Project view
- Alt+A, C, C, enter to go to Assets —> Create —> C# Script
-
Pretty good tutorial on using fake shadows in Unity basic (reference):
- This relies on the Projector component, which as far as I can tell will apply a material with an opacity that’s based on how close the projection is. In perhaps simpler words: you make a projector above your character’s head, aim it at the ground, and set the far clip plane to slightly beneath the ground. When your character is on the ground, the projection will show your shadow at the ground. As you raise your character off the ground (e.g. when jumping), the projection surface (the ground) gets farther away, so the projection material (the shadow) will be more transparent. When you’ve left the ground by a large amount, it will be completely transparent, meaning you won’t cast a shadow on the ground any longer.
- You can’t make realtime shadows [easily] without Unity Pro.
Running builds on Linux:
- I think you just need to right-click them and mark them as executable in the properties if they’re downloaded from the Internet. MAYBE Wine is needed, in which case this guide is what you can use: http://www.binarytides.com/install-wine-debian-wheezy/
- Then just right-click the x86_64 version and run it.