官术网_书友最值得收藏!

Assigning references to pre-existing objects

A simple approach to the problem of interobject communication is to use Unity's built-in Serialization System. Software design purists tend to get a little combative about this feature, since it breaks Encapsulation; it makes any field (the C# term for a member variable) marked private act in a way that treats them like a public field. However, it is a very effective tool for improving development workflow. This is particularly true when artists, designers, and programmers are all tinkering with the same product, where each has wildly varying levels of computer science and software programming knowledge, and with some of whom would prefer to stay away from modifying code files. Sometimes, it's worth bending a few rules in the name of productivity.

Whenever we create a public field in a MonoBehaviour, Unity automatically serializes and exposes the value in the Inspector window when the Component is selected. However, public fields are always dangerous from a software design perspective. These variables can be changed through code at anytime from anywhere, making it hard to keep track of the variable and is liable to introduce a lot of unexpected bugs.

A better solution, is to take any private or protected member variable of a class and expose it to the Inspector window with the [SerializeField] attribute. The value will now behave like a public field with respect to the Inspector window, allowing us to change it through the Editor interface for convenience, but keeps the data safely encapsulated from other parts of our codebase. 

For example, the following class exposes three private fields to the Inspector window:

using UnityEngine;

public class EnemyCreatorComponent : MonoBehaviour {
[SerializeField] private int _numEnemies;
[SerializeField] private GameObject _enemyPrefab;
[SerializeField] private EnemyManagerComponent _enemyManager;

void Start() {
for (int i = 0; i < _numEnemies; ++i) {
CreateEnemy();
}
}

public void CreateEnemy() {
_enemyManager.CreateEnemy(_enemyPrefab);
}
}

Note that the private access specifiers shown in the preceding code are redundant keywords in C#, since fields and methods default to private unless specified otherwise. However, it is often best practice to be explicit about the intended access level.

Looking at this Component in the Inspector window reveals three values, initially given default values of 0, or null, which can be modified through the Editor interface:

We can drag-and-drop a Prefab reference from the Project window into the Enemy Prefab field revealed in the Inspector window.

Note how Unity automatically takes a camel-cased field name and creates a convenient Inspector window name for it. _numEnemies becomes Num Enemies, _enemyPrefab becomes Enemy Prefab and so on.

Meanwhile, the _enemyManager field is interesting because it is a reference to a specific MonoBehaviour class type. If a GameObject is dragged-and-dropped into this field, then it will refer to the Component on the given object as opposed to the GameObject itself. Note that if the GameObject does not contain the expected MonoBehaviour, then nothing will be assigned to the field.

A common usage of this Component reference technique is to obtain references to other Components attached to the very same GameObject a Component is attached to. This is an alternative means of caching Components with zero cost, as discussed in the section entitled Cache Component references earlier in this chapter.

There is some danger to using this method. Much of our code would assume that a Prefab is assigned to a field that is used like a Prefab and a GameObject is assigned to a field that refers to an instance of a GameObject. However, since Prefabs are essentially GameObjects, any Prefab or GameObject can be assigned to a serialized GameObject reference field, which means we could assign the wrong type by accident.

If we do assign the wrong type then we could accidentally instantiate a new GameObject from an existing GameObject that was previously modified, or we could make changes to a Prefab, which would then change the state of all GameObjects instantiated from it. To make matters worse, any accidental changes to a Prefab become permanent since Prefabs occupy the same memory space whether Play Mode is active or not. This is the case even if the Prefab is only modified during Play Mode.

Therefore, this approach is a very team-friendly way of solving the problem of interobject communication, but it is not ideal due to all of the risks involved with team members accidentally leaving null references in place, assigning Prefabs to references that expect an instance of a GameObject from the Scene, or vice versa.

It is also important to note that not all objects can be serialized and revealed in the Inspector window. Unity can serialize all primitive data types (int, float, string, and bool), various built-in types (Vector3, Quaternion, and so on); enum, class, struct, and various data structures containing other serializable types such as List. However, it is unable to serialize static fields, readonly fields, properties, and dictionaries.

Some Unity developers like to implement pseudo-serialization of dictionaries via two separate lists for keys and values, along with a Custom Editor script, or via a single list of struct objects, which contain both keys and values. Both of these solutions are a little clumsy, and rarely as performant as a proper dictionary, but they can still be useful.

Another solution to the problem of interobject communication is to try and make use of globally accessible objects in order to minimize the number of custom assignments we need to make.

主站蜘蛛池模板: 思南县| 始兴县| 太仆寺旗| 吉林省| 武胜县| 靖边县| 柘城县| 宝兴县| 田阳县| 玛纳斯县| 大同市| 肃宁县| 濮阳市| 安塞县| 大庆市| 永安市| 繁峙县| 陆良县| 琼结县| 永春县| 武平县| 闻喜县| 元朗区| 枣强县| 朔州市| 大竹县| 杨浦区| 辛集市| 洛浦县| 濮阳县| 县级市| 壤塘县| 台安县| 仁布县| 包头市| 左贡县| 伊川县| 河南省| 定西市| 肇源县| 谷城县|