Wednesday, August 10, 2016

Unity or Unity3D Best Practices and Tips From Game Experts

This resource contains a collection of Unity or Unity3D best practices and tips provided by our Toptal network members, and will be updated regularly with additional information and emerging Unity techniques. This is a community-driven project, so we encourage you to contribute as well, and we are counting on your feedback.
Unity is a cross-platform game engine, and here we will embrace Unity and Unity3D best practices that will make you a better game developer.
Check out the Toptal resource pages for additional information on Unity or Unity3D interview questions.

How can I access all Elements of a hierarchy using The “Depth-first search” algorithm?

Sometimes, developers need to find or test the elements that are inside of a complex structure made of intricate transforms relationships. To find or test the desired element, it is necessary to visit all the nodes of the mentioned structure.
Usually, transforms are organized as a complex tree data structure, and one of the most common algorithms for visiting all tree nodes is the Depth-first search. This algorithm recursively visits all nodes prioritizing the innermost ones, from left to right.
using System;

//Visits all nodes using the DepthFirstSearch algorithm calling ‘p_callback’ on each visit.
public bool TraverseDFS(Transform p_root,Predicate<Transform> p_callback)
{

  //’Predicate’ is a C# delegate that accepts one parameter and returns a ‘bool’
  //We can use this ‘bool’ it to check if the user wants to keep searching the tree.
  if(!p_callback(p_root))
  {
    //The desired query was found and we can stop searching.
    return false;
  }

  for(int i=0;i<p_root.childCount;i++) 
  { 
    if(!TraverseDFS(p_root.GetChild(i),p_callback))
    {
      //Stop searching
      return false; 
    }
  }

  //Keep searching
  return true;
}

Contributors

How to move objects towards the desired position with constant and/or variable rates in a defined time frame?

Things in games must move. It is just a matter of speed, acceleration, and time.
The most common methods for moving things outside the physics loop of Unity is using MoveTowards and Lerp.
If you want to move things with constant speed, MoveTowards increments your position with a constant rate for each frame.
MoveTowards
//Constant Speed
Vector3 position;
Vector3 target;
float speed;
void Update()
{
  position = Vector3.MoveTowards(position,target,Time.deltaTime * speed);
}
To move things with the feel of acceleration one must use Lerp. The effect we get is caused because the next position is a percentage of the remaining distance. So, the first steps are bigger than the last ones because the remaining distance keeps getting shorter.
Lerp
//Variable Speed
Vector3 position;
Vector3 target;
float speed;
void Update()
{
  position = Vector3.Lerp(position,target,Time.deltaTime * speed);
}
The interesting part here is that these equations operate in numbers, and considering that quaternions (rotations), colors, rectangles and other Math structures have the same composition, it can be seen that everything can be interpolated using this technique. For instance, fading or sliding screens and rotating objects are other use cases of it.

Contributors

How to correctly destroy an item from the scene?

Somewhere in the gameplay, your player eliminated a monster or picked an item. Now your code must remove those instances from the scene.
New developers usually mistake the gameObject’s components, such as the Transform and the attached MonoBehaviours as the main instance in the scene.
//Reference to the scripts
MonsterScript monster;
ItemScript item;

void OnPlayerWin()
{
    //Process score.
    Destroy(monster); //Will destroy the monster’s script only and the monster will be on scene. 
}

void OnPlayerGetItem()
{
    //Process item.
    Destroy(item); //Will destroy the item’s script only and the item will be on scene. 
}
Every component in the Unity API has a reference to its gameObject, which is the element containing allscripts and components related to a game element.
//Reference to the scripts
MonsterScript monster;
ItemScript item;

void OnPlayerWin()
{
    //Process score.
    Destroy(monster.gameObject); //Will destroy the monster’s entire instance.
}

void OnPlayerGetItem()
{
    //Process item.
    Destroy(item.gameObject); //Will destroy the item’s entire instance.
}
The knowledge to differentiate between a gameObject and its components is crucial to avoid unwanted behaviour in the key parts of your gameplay.
Yet, sometimes, the objective is to actually kill a given script to open a slot for another one. One example is changing between AI behaviours.
GameObject monster;

void OnPlayerClose()
{
    AIScriptIdle ai = monster.GetComponent<AIScriptIdle>(); //Gets the current AI instance
    if(ai) Destroy(ai); //If it exists, destroy.
    monster.AddComponent<AIScriptAttack>(); //Adds the Attack AI Script.
}

void OnPlayerFar()
{
    AIScriptAttack ai = monster.GetComponent<AIScriptAttack >(); //Gets the current AI instance
    if(ai) Destroy(ai);//If it exists, destroy.
    monster.AddComponent<AIScriptIdle>(); //Adds the Idle AI script.
}

Contributors

How to customize a material on runtime exclusively for its GameObject?

Sometimes you have one material that is configured to render your character with the correct shader and parameters; but your game could have a great number of characters with different textures and parameters for each.
Usually, one would create one material for each. However, if at some point the base material needs its shader, textures or parameters changed, you would need to update all previously created ones.
One way to avoid that is to have one material for all characters and store the parameters and textures in the character script.
//Character.cs

Texture2D skin; //Reference to the character skin texture.
Color tint;     //Some tint parameter for the shader.

void Start()
{
    Material m = GetComponent<Renderer>().sharedMaterial; //Get the renderer material reference.
    m.color = tint;       //Change the shader color parameter to the character’s.
    m.mainTexture = skin; //Change the skin texture to the character’s.
}
Pretty easy? However, there is a catch. To simplify the workflow, we had only one material for all characters. So, if someone changes the material attributes, all characters would be affected.
To avoid this, you must duplicate the material instance as soon as the game starts and make it exclusive to that character.
//Character.cs

Texture2D skin; //Reference to the character skin texture.
Color tint;     //Some tint parameter for the shader.

void Start()
{
    Material m = GetComponent<Renderer>().sharedMaterial; //Get the renderer material reference.
    m = Instantiate<Material>(m);                         //Duplicate the original
    m.color = tint;       //Change the shader color parameter to the character’s.
    m.mainTexture = skin; //Change the skin texture to the character’s.
    GetComponent<Renderer>().sharedMaterial = m; //Assign the new material only for this character.
This article is from Toptal.

No comments:

Post a Comment