There are a number of "gotchas" that hit pretty much everyone new to Unity programming. Sometimes these are due to you being new to programming in general, and sometimes it's due to the fact that Unity uses a slightly unusual method of attaching multiple scripts to a game object.
This overview tries to take you through the ones we see most often on Unity Answers.
The Gotchas
How to access a variable or method on another script or object
How to access a variable or method on another script or object
So you've written two scripts and you want to access variables or methods on one script from the other one, but you can't quite work out how to get a reference to the script or how to get the variable. Lets call the script that wants to access the variable the caller and the script that contains the variable or method the target.
So to get the target script you need to know the object that it is attached to. There are five ways you can know that:
1. The target script you want is attached to the same game object as the caller script.
In this case you just need to insert a call to GetComponent, which returns the script attached to the game object on which you call it. If you don't put something infront of GetComponent it will return a script attached to the same game object.
In Javascript you should use GetComponent like this:
GetComponent(TargetScriptName).someVariable = someValue;
GetComponent(TargetScriptName).SomeMethod();
In C#:
GetComponent<TargetScriptName>().someVariable = someValue;
GetComponent<TargetScriptName>().SomeMethod();
2. You want to access the target script on another object because you've collided with it, or invoked a trigger in the caller script
In this case your method already has a reference to the other object being passed in, either as a Collider in the case of OnTriggerXXXX functions or as a Collision in the case of OnCollisionXXXX
In this case you just need to use GetComponent on the thing you've collided with.
In Javascript this would like:
function OnTriggerEnter(other : Collider) { other.GetComponent(TargetScriptName).someVariable = someValue; }
In C#
void OnTriggerEnter(Collider other) { other.GetComponent<TargetScriptName>().someVariable = someValue; }
A collision event takes a collision, so you just need to get something that represents the other object and call get component on that:
function OnCollisionEnter(collision : Collision) { collision.collider.GetComponent(TargetScriptName).someVariable = someValue; }
Or in C#
void OnCollisionEnter(Collision collision) { collision.GetComponent<TargetScriptName>().someVariable = someValue; }
If for some reason you want to set a variable on some child of the object you triggered or collided with then you can use GetComponentInChildren.
3. You have a relationship between two objects that you can establish using the editor or set using script when the relationship is created
So in this case you have a relationship between two objects, not because they are currently interacting, but rather because they have some other long term relationship. For example an enemy might be chasing the player, a player may have "locked" a weapon onto an enemy, or perhaps the player is currently riding in or on some vehicle.
In this case you want to have a variable in your caller script that references your target script. The easiest way is to define that variable as being of the type TargetScriptName but it might be that you have some other kind of object referencing variable like GameObject or Transform.
You will assign the variable using the inspector if the relationship can be created at editing time - or if you assign it at runtime you will use either method 2 (above) or method 4 (below) to get the component and set up the variable.
Here's an example of creating a long term relationship due to a trigger:
var targetScript : TargetScriptName; function Update() { //If we have a target, and that target is ready to be targeted if(targetScript && targetScript.someVariable > 100) { //Do one thing } else { //Do something else } } function OnTriggerEnter(other : Collider) { //Check whether the thing we hit can be a target var target = other.GetComponent(TargetScriptName); if(target) { //If it can then target it targetScript = target; } }
4. You can find the object that has the target script, by searching by name or tag
If you can find a game object then you can just call GetComponent on it in the same way as you did using method 2.
There are several ways to find the object - you can use GameObject.FindWithTag or GameObject.Find to search everywhere in the scene. Or you can use transform.Find to search the children of a game object. Whatever you use to find it, you will them employ method 2 to access the script you are looking for and potentially cache the result so that in future you have a long term relationship and can use method 3 to work with the object in subsequent functions.
TargetScriptName targetScript; void Start() { targetScript = GameObject.Find("someObjectName").GetComponent<TargetScriptName>(); } void Update() { if(Vector3.Distance(targetScript.transform.position, transform.position) < 10) { targetScript.someVariable -= someValue; } }
5. You have another component on the object, but its not the one with the script you want
This often happens when you are traversing the hierarchy, or you just have another script on the same object. In essence this is pretty much the same as method 2.
For instance if you wanted to get a script on all of the children of the object your caller script is attached to:
for(var t : Transform in transform) { target = t.GetComponent(TargetScriptName); target.DoSomething(); target.someVariable = someValue; }
Rigidbody,how dows that work?
Rigidbody,how dows that work?
So you just spent the last 3 days with no sleep, trying to make your object act realistic. You went over most of th ephysics website to fully understand the principles of gravity, friction, collision and others. But then you hear about Rigidbody and you feel like a growth of anger inside. And you go "Naaa come ooon"
Now how to use it? I would recommend to download this projet and see the video below:
I want explosion but nothing happens!!!
From the project above you can try to add this code:
if(Input.GetKeyDown(KeyCode.E)){ float radius = 10.0F; float power = 1000.0F; Collider[] colliders = Physics.OverlapSphere (_transform.position, radius); foreach(Collider col in colliders){ if(col.rigidbody){ if(col.rigidbody != gameObject.rigidbody) col.rigidbody.AddExplosionForce (power, _transform.position, radius); } } }
This code makes sure the actual object is not affected by the explosion. You can now add this to your sphere so that it makes the scaffolding explode.
Simply, you define the radius and the force of the explosion. You collect all colliders around in a certain radius. Then you check which ones have a rigidbody. Finally you check if this rigidbody is not the object.
You just created a bomb.
Colliders: how to make sure you get OnCollisionXXXX and OnTriggerXXXX functions called in your scripts
How to make sure you get OnCollisionXXXX and OnTriggerXXXX functions called in your scripts
In order to collide both objects must have colliders attached. If you want OnCollisionXXXX then the objects should not have isTrigger set to true, if you want to have OnTriggerXXXX then the objects should have isTrigger set to true. You must also follow the rules below.
This is one of the most common problems.
There are some two important rules:
You should have a Rigidbody component attached to the game object that has the script containing the OnCollisionXXXX, OnTriggerXXXX methods on it.
This rigidbody can be set to isKinematic = true to ensure that you don't have physics affecting your object if you don't want it.
At least one of the parties must have a rigidbody that isn't asleep
To improve performance Unity will put rigidbodies to sleep when they stop moving. They will be awoken by another rigidbody colliding with them, presuming that body isn't asleep. However if you just move or make a collider on an object without a rigidbody, or whose rigidbody is not awake then your collisions will be ignored.
Other important considerations
There are a number of other rules that concern what things will make a collision occur.
You can construct colliders for an object out of multiple primitives - this gives you the ability to simulate quite complex objects. What you need to do is attach multiple child game objects to the object that you want to collide with. Give that object a rigidbody. Attach primitive colliders to the child game objects, size and scale them accordingly - the compound shape of them will comprise your new collider.
Getting your Input to work properly
Getting your Input to work properly
Beginners often end up wondering why their input does not report properly. They set it all up according to the docs (at least they think so) but sometimes they feel nothing is happening. Most of the time, the issue comes up when trying to control some physics via the Input. Since, physics is placed in the FixedUpdate, it would make sense to add the Input there too. Wrong.
Update is machine dependent, that means each computer can have a different frame rate depending on the processor and the processes actually running on the machine.
FixedUpdate is user-defined. You can modify its value from the Physics Manager by selecting Edit->Project Settings->Physics from the menu bar. The default value for Fixed Timesteps is 0,02 which represents 50 fps. A smaller value will ask the system to run FixedUpdate faster. Note that the value is a request but the computer on which the game is running might not be able to run at that speed.
Now, Update and FixedUpdate both run independently, it means for one Update, you may have one, many or no FixedUpdate. If your computer runs 100 fps but the FixedUpdate is 50fps that simply means for two Updates we have only one FixedUpdate.
So this is what you should not be doing:
using UnityEngine; public class Test:MonoBehaviour{ void FixedUpdate(){ if(Input.GetKeyDown(KeyCode.Space)){ //Action } } }
This is what you could be doing instead:
using UnityEngine; public class Test:MonoBehaviour{ bool action = false; void Update(){ if(Input.GetKeyDown(KeyCode.Space)){ action = true; } } void FixedUpdate(){ if(action){ //Action action = false; } } }
The Input in the Update is checked every frame guaranteeing no Input gets discarded. A variable is modified in the Update and used in the next call of the FixedUpdate. The boolean is set back to false to ensure that the action does not get repeated endlessly.
Make something happen in the future | Coroutines and Invoke
Make something happen in the future
Coroutines are complicated overkill if you just want something to happen in the future. You may tie yourself up in knots by forgetting to start them properly in C# or just get confused over what they are for. If you are starting Unity and you want something to happen in the future forget the coroutines for now and follow this advice.
Just for the sake of it, here is a way to create a timer:
float timer; int waitingTime; void Update(){ timer += Time.deltaTime; if(timer > waitingTime){ //Action timer = 0; } }
The variable timer is increased each frame with deltaTime (the time elapsed between this frame and the previous one). The variable is compared each frame with waitingTime which stands for the amount of time we want to wait. When timer becomes greater than waitingTime, the statement is true, the action inside the statement are performed and finally timer is set back to 0 for the process to start again. Wrapping the timer into a boolean will allow triggering the timer for a particular situation:
using UnityEngine; using System.Collections; public class Test:MonoBehaviour{ float timer; int waitingTime; bool inside; void Start(){ timer = 0.0; waitingTime = 2; inside = false; } void Update(){ if(inside){ timer += Time.deltaTime; if(timer > waitingTime){ //Action timer = 0; } } } void OnTriggerEnter(Collider other){ if (other.gameObject.tag=="Player")inside = true; } void OnTriggerExit(Collider other){ if (other.gameObject.tag =="Player"){ inside=false; timer = 0; } } }
The example above shows how to create a timer suitable for triggering an action when the player remains inside a zone. For instance if the player enters a fire zone, simply harming the player in the Update would kill him within a second. With the timer we can harm him every x seconds.
Still, Unity offers better solutions as shown in the next part.
Make an object live for a few seconds
If you want your object to only appear on screen for a few seconds, just destroy it in Start! Destroy can take a parameter which is the delay before it actually gets killed.
function Start() { //Destroy the game object in 5 seconds Destroy(gameObject, 5); }
Delaying an action
You want something to happen and another action after a defined amount of time. UnityScript and C# use different action for the same result. In those example, the check variable is necessary to enter the statement. When entering it is set to false, after 2 seconds it is set back to true.
UnityScript
#pragma strict var check :boolean =true; var i:int =0; function Update () { if(Input.GetKeyDown(KeyCode.A)&&check){ check = false; print("Inside" + i++); WaitForIt(); } } function WaitForIt(){ yield WaitForSeconds(2.0f); check=true; }
C#:
using UnityEngine; using System.Collections; public class Wait : MonoBehaviour { public bool check =true; int i =0; void Update () { if(Input.GetKeyDown(KeyCode.A)&&check){ check = false; print("Inside" + i++); StartCoroutine(WaitForIt()); } } IEnumerator WaitForIt(){ yield return new WaitForSeconds(2.0f); check=true; } }
Running those scripts, you see that you can press A and it prints out"Inside" and the value of i. But you will have to wait for 2 seconds before being able to print again.
Do something in a few seconds time
If you want something to happen a couple of seconds after something else then you can just use Invoke. Write a function containing the script you want to run, then call Invoke("NameOfYourFunction", delayInSeconds). In the example below, the TurnMeBlue function is called after 2 seconds.
function Start() { //Turn the object blue in 2 seconds Invoke("TurnMeBlue", 2); } function TurnMeBlue() { renderer.material.color = Color.blue; }
You might even want to use multiple invokes to make things happen in a sequence:
var startPosition : Vector3; var health = 100.0; var respawning = false; function Start() { //Cache were this object started startPosition = transform.position; } //Reset the object function Respawn() { transform.position = startPosition; health = 100; renderer.enabled = true; respawning = false; } //Hide the character function Hide() { renderer.enabled = false; transform.position = new Vector3(1000,1000,1000); } function Update() { if(health < 0 && !respawning) { respawning = true; animation.Play("die"); Invoke("Hide", 3); Invoke("Respawn", 10); } }
This script uses invoke to play a death animation and then respawn the character. When the health drops below 0 a "die" animation is played. The model is hidden (and moved out of the way) in 3 seconds and then respawned in 10 seconds.
Do something every few seconds
Rather than writing a coroutine with a loop you can simply write a function that contains the script you want and use InvokeRepeating("YourFunctionName", delayForFirstCall, timeBetweenCalls).
You can always cancel this using CancelInvoke("YourFunctionName") or without any parameters to cancel all of them.
float health = 100f; void Start() { InvokeRepeating("Heal", 2, 2); } void Heal() { health = Mathf.Clamp(health + 1, 0, 100); }
When to use a coroutine
A coroutine can be useful when you need something that would otherwise be in an Update call and you don't want to make Update too complicated.
For example you might want to rotate an object to a new angle over time. This could be a use for a coroutine. In the following example calling RotateBy rotates the object to a new angle over a period of time.
function OnMouseUp() { RotateBy(Vector3(0,90,0),2); } function RotateTo(angle : Vector3, time : float) { var currentRotation = transform.rotation; var targetAngle = currentRotation.eulerAngles + angle; var targetRotation = Quaternion.Euler(targetAngle); var t = 0; while(t < 1) { transform.rotation = Quaternion.Slerp(currentRotation, targetRotation, t); t += Time.deltaTime / time; yield null; } transform.rotation = targetRotation; }
How to move with Vector3.Lerp
How to move with Vector3.Lerp
You have your line of code but for some reasons nothing is happening or it all happens at once. Lerp is a interpolation between two points start and to by a ratio t. So t <=0 the result is the start position and t >=1 then the result is the to position.
transform.position = Vector3.Lerp(start, to, t);
If you want to move between two points exactly in a set amount of time, then you need to record the starting position and increment t (usually by a factor of Time.deltaTime/NumberOfSecondsToComplete) and the object will reach the destination when t reaches 1.
Like this:
Vector3 _start; Vector3 _target; float _t; void Update() { transform.position = Vector3.Lerp(_start, _target, _t); _t += Time.deltaTime/2; //Take 2 seconds } public void SetTargetPosition(Vector3 newTargetPosition) { _start = transform.position; _target = newTargetPosition; _t = 0; }
Otherwise you might want a smoothed effect where you just move from where you currently are towards the target - in that case you use it differently and keep passing the current position as the first Lerp parameter.
In human words, it means you take the start(perhaps transform.position) and you look at to (the target.position). Then you move the object by the amount of t. If you give t= 1 it moves suddenly from start to to. If you give 0.5 it will move 1/2 of the current distance between the two points each frame.
function Update(){ transform.position = Vector3.Lerp(transform.position, target.position, Time.deltaTime); }
If there would be 10 m between transform(0) and target(10) and deltaTime is 0.2 (20%) then you get:
- 20% of target-transform (10-0) = 2m so transform = 2
- 20% of target-transform (10-2) = 1.6m so transform = 3.6
- 20% of target-transform (10-3.6) = 1.28m so transform = 4.88
- And so on
Note that the object will never really get exactly to its destination but more likely get really close, similarly to the limit principle in algebra.
How to store lists of objects that can grow
How to store lists of objects that can grow
In C# you need to add:
using System.Collections.Generic;
To the top of your source file to use the generic collections. There is no such requirement for Javascript.
Instead of using Array or ArrayList use generic Lists. Lists can have items added or removed at runtime, they contain a specific type (or a subclass of that type), they can also be sorted easily and turned into arrays when necessary.
You work with Lists like this:
//Define a list using Javascript var myList = new List.<int>(); var anotherList = new List.<SomeClass>(); //Define a list using C# List<int> myList = new List<int>(); List<SomeClass> anotherList = new List<SomeClass>(); //Add element to a list myList.Add(someValue); //Add multiple elements to a list myList.AddRange(someListOrArrayOfValues); //Clear all elements myList.Clear(); //Insert into a list myList.Insert(1, someValue); //Insert multiple elements myList.InsertRange(1, someListOrArrayOfValues); //Remove a specific value myList.Remove(someValue); //Remove at a specific index myList.RemoveAt(1); //Find an index of an element var index = myList.IndexOf(someValue); //Find an index of something using a function in Javascript var index = anotherList.FindIndex(function(entry) entry.someValue == something); //Turn a list into an array var myArray = myList.ToArray(); //Find an index of something using a function in C# var index = anotherList.FindIndex((entry) => entry.someValue == something) //Get the number of items in the list var itemCount = myList.Count
Dictionaries are the .NET generic associative arrays you work with Dictionaries like this:
//Define of a string to an int in Javascript var myDic = new Dictionary.<String, int>(); //Define a dictionary of GameObject to a class in Javascript var anotherDic = new Dictionary.<GameObject, SomeClass>(); //Define a string to int dictionary in C# Dictionary<string, int> myDic = new Dictionary<string, int>(); //Define a dictionary of GameObject to a class in C# Dictionary<GameObject, SomeClass> anotherDic = new Dictionary<GameObject, SomeClass>(); //Add an element to a dictionary myDic["Something"] = someIntValue; //Get a value from a dictionary var someValue = myDic["Something"]; //Get a complex value and change one of its properties anotherDic[gameObject].someProperty = someValue; //Check if a value exists if(myDic.ContainsKey("Something")) { } //Remove an element from a dictionary myDic.Remove("Something'); //Run through all of the keys in the dictionary JS for(var key : String in myDic.Keys) { } //Run through all of the values in the dictionary in C# foreach(int value in myDic.Values) { } //Clear all elements myDic.Clear(); //Get the number of items in the dictionary var count = myDic.Count;
How to configure and move rigidbodies
How to configure and move rigidbodies
Physics is your friend, it will simulate lots of things for you, but you have to treat it right!
You cannot use Input functions in FixedUpdate. If you must get user input, you will need to get it in Update and apply it in FixedUpdate.
If your "ball" appears to drop too quickly have you made it the size of a marble? Try dropping a marble onto your desk - see how fast it moves? If you were looking for a more "floating" effect you need a much bigger ball, much further away.
What the CharacterController is for
What the CharacterController is for
Character Controller is designed for both the player and NPC characters in a running around and shooting game. Character Controllers are great for this, they cover a lot of the complicated logic concerning moving platforms, characters blocking other characters etc.
Many games just don't lend themselves to using a Character Controller - so just be sure that is what you want before you commit to it - if you do use it, only use it on the things you need it on.
How to modify the rotation of an object using Quaternions | Don't change x,y,z!
How to modify the rotation of an object using Quaternions
You should not modify x,y,z,w of a Quaternion unless you really know what you are doing. If you want to change the rotation of something using degrees then modify the .eulerAngles.
If you want to understand Quaternions in detail then it's a big subject. See this tutorial.
To set the angles of an object as seen in the inspector do this:
transform.rotation.eulerAngles = new Vector3(100,0,100);
How to access C# script from Javascript or vice versa
You need to access a C# script from Javascript or vice versa
Javascript and C# are not compiled into the same assembly so you can't just easily refer to one from the other. Any code in Plugins, Standard Assets or Pro Standard Assets will be compiled first and so code that isn't in those directories will have access to all of the scripts and classes that were defined in them.
In this way you can make a C# script access Javascript classes, by putting the Javascript in Standard Assets, Pro Standard Assets or Plugins (or any folder beneath them). You can make a Javascript script access C# classes by putting the C# classes in a folder beneath Standard Assets, Pro Standard Assets or Plugins. Remember that Javascript in one of the special folders cannot access C# in any folder and C# in a special folder cannot access Javascript classes no matter where they are. This is why you shouldn't try to mix languages in your own project, if you suddenly need access to something you may find that there is no way to get it.
You think you are programming Unity in Java or Javascript
You think you are programming in Java or Javascript with Unity
Your coroutine doesn't finish after a wait or yield
Your coroutine doesn't seem to finish after a wait or yield
This often happens because you have disabled the script containing the coroutine or destroyed the object that the script lives on. Often when you kill something you want to start a coroutine or use an Invoke to have something happen after a dying animation, a score update or some other delayed process.
The wrong way to write a coroutine:
void Update() { if(health < 0) { StartCoroutine(Die()); Destroy(gameObject); //or enabled = false; } } IEnumerator Die() { animation.Play("wobble"); yield return new WaitForSeconds(3); //This will never be called animation.Play("die"); }
The correct way to write that script would be:
bool dying; void Update() { if(dying) return; if(health < 0) { StartCoroutine(Die()); } } IEnumerator Die() { dying = true; animation.Play("wobble"); yield return new WaitForSeconds(3); animation.Play("die"); yield return new WaitForSeconds(3); Destroy(gameObject); }
October 21, 2012 - 4:54 pm
You answered some nagging questions in such a clear way. I am grateful. You need to write a Gems book! I would pre-order that! Hope you have time to post some more. (It’s funny, I’ve only ever used C# from the beginning, seems like the javascript syntax creates more problems for people despite the “ease” it should offer.)
Keep up the good work and thanks again.
-JR
November 10, 2012 - 6:40 pm
Wow. Simply wow. I wish I found this like 3 months ago.
December 1, 2012 - 11:07 pm
I think this is the best c# Unity tutorial site found on the internet!
Thank you for taking the time to help people learn and understand quickly!!
December 4, 2012 - 9:54 am
I think if I can translate all you articles to Chinese. All these things are great!
December 10, 2012 - 2:02 am
This information helped me a great deal. Thanks for taking the time to write and post it!