谁有unity的unity qunity hierarchyy插件

努力加载中,稍等...
暂无新消息
努力加载中,稍等...
已无更多消息...
这些人最近关注了你
努力加载中,稍等...
已无更多消息
努力加载中,稍等...
已无更多消息
& Unity的50个使用技巧(2016 Edition)
50 Tips and Best Practices for Unity (2016 Edition)
征集热心朋友翻译文章,奖励规则:每100汉字奖励10QB,30天内互动奖励0 - 50QB.
翻译请求:申请翻译,号前完成
该文章来自用户转载
I published the original
about 4 years ago.Although a lot of it is still relevant, a lot has changed since then:Unity got better. For example, I now trust the FPS counter. The ability to use property drawers makes it less necessary to write custom editors. The way prefabs work makes the need for explicit nested prefabs or an alternative less. Scriptable objects are friendlier.Visual Studio integration got better, making it a lot easier to debug, and reducing the need for so much gorilla debugging.Third-party tools and libraries got better. There are now many assets in the Asset Store available that takes care of things such as visual debugging and better logging. Our own (free)
plug-in has much of the code described in the original article (and a lot of it described here too).Version control got better. (Or perhaps, now I know better how to use it effectively). No need to have multiple copies or backup copies of prefabs, for example.I got more experience. In the last 4 years I worked on many U including , production games such as , and our flagship Unity asset .This article is a revised version of the original taking all of the above into account.Before getting on with the tips, here is a disclaimer (essentially the same as the original).These tips don't apply to every Unity project:They are based on my experience with projects with small teams from 3 to 20 people.There's is a price for structure, re-usability, clarity, and so on — team size, project size, and project goal determine whether that price should be paid. , for example.Many tips are a matter of taste (there may be rivalling but equally good techniques for any tip listed here).Unity also has some best practices on their site (although these are mostly from a performance point of view):Best practices for physically based content creation 2D Best practices in Unity Internal Unity tips and tricks Unity Tips and Tricks Workflow1. Decide on the scale from the beginning and build everything to the same scale. If you don't, you may have need to rework assets later (for example, animation does not always scale correctly). For 3D games, using 1 Unity unit = 1m is usually the best. For 2D games that does not use lighting or physics, 1 Unity unit = 1 pixel (at "design" resolution) is usually good. For UI (and 2D games), pick a design resolution (we use HD or 2xHD) and design all assets to scale in that resolution.2. Make every scene runnable. Do this to avoid having to switch scenes to run the game so that you can test faster. This can be tricky if you have objects that persist between scene loads that is required in all your scenes. One way of doing this is to use make persistent objects singletons that will load themselves when they are not present in the scene. Singletons are described in more detail in another tip.3. Use source control and learn how to use it effectively.Serialize your assets as text. It does not in practice really make scenes and prefabs more mergeable, but it does make it easier to see what changed.Adopt a scene and prefab sharing strategy. In general, more than one person should not work on the same scene or prefab. For a small team it may be enough to ask around that no-one else is working on a scene or prefab before starting to work on it. It may be useful to swap physical tokens that denote scene ownership around (you are only allowed to work on a scene if you have the scene token on your desk).Use tags as bookmarks.Decide and stick to a branching strategy. Because scenes and prefabs cannot be smoothly merged, branching is slightly more complicated. However you decide to use branches, it should work with your scene and prefab sharing strategy.Use submodules with care.
can be a great way to maintain re-usable code. But there are a few caveats:Meta-files are not generally consistent over multiple projects. This is not generally a problem for non-Monobehaviour or non-Scriptable object code, but for MonoBehaviours and Scriptable objects using submodules can cause code to get lost.If you work on many projects (including one or more in submodules), you can sometimes get an update avalanche where you have to have to pull-merge-commit-push over various projects for a few iterations to stabilize the code over all projects (and if someone else is making changes while this is going on, it could turn into a sustained avalanche). One way to minimize this effect is to always make changes to submodules from projects dedicated to them. This way, projects that use submodules only they never need to push back.4. Keep test scenes and code separate. Commit temporary assets and scripts to the repository, and remove them from the project when you are done.5. If you upgrade tools (especially Unity), do so simultaneously. Unity is much better at preserving links when you open a project with a different version than it used to be, but links still sometimes get lost when people are working with different versions.6. Import third-party assets in a clean project and export a new package for your own use from there. Assets can sometimes cause problems when you import them directly in you project:There may be collisions (file or name), especially assets that have files in the root of the Plugins folder, or that use assets from Standard Assets in their examples.They may be unorganized on put their files all over your own project. This is especially a problem if you decide not to use it and want to remove it.Follow these steps to make importing assets safer:Make a new project, and import the asset.Run the examples and make sure they work.Organize the asset into a more suitable folder structure. (I usually do not enforce my own folder structure on an asset. But I make sure that all the files are in a single folder, and that there are not any files in important places that could overwrite existing files in my project.)Run the examples and make sure they still work. (On occasion, I had assets break when I move stuff, but generally this should not be a problem).Now remove all the things you won't need (such as examples).Make sure the asset still compiles and that prefabs still have all their links. If there is anything left to run, test it.Now select all the assets, and export a package.Import into your project. 7. Automate your build process. This is useful even for small projects, but it is particularly useful when:you need to build lots of different versions of the game,other team-members with varying degrees of technical knowledge need to make builds, oryou need to make small tweaks to the project before you can build.See
for a good guide on how to do this.8. Document your setup. Most documentation should be in the code, but certain things should be documented outside code. Making designers sift through code for setup is time-wasting. Documented setups improved efficiency (if the documents are current).Document the following:Tag uses.Layer uses (for collision, culling, and raycasting – essentially, what should be in what layer).GUI depths for layers (what should display over what).Scene setup.Prefab structure of complicated prefabs.Idiom preferences.Build setup.General Coding9. Put all your code in a namespace. This avoids code clashes among your own libraries and third-party code. But don't rely on namespaces to avoid clashes with important classes. Even if you use different namespaces, don't use "Object" or "Action" or "Event" as class names.10. Use assertions. Assertions are useful to test invariants in code and help flush out logic bugs. Assertions are available in the
class. They all test some condition, and write an error message in the console if the condition is not met. If you are not familiar with how assertions can be useful, see .11. Don't use strings for anything other than displayed text. In particular, do not use strings for identifying objects or prefabs. There are exceptions (there are still a few things that can only be accessed by name in Unity). In such cases, define those strings as constants in files such as AnimationNames or AudioModuleNames. If these classes become unmanageable, use nested classes so you can say something like AnimationNames.Player.Run.12. Don't use Invoke and SendMessage. These methods of MonoBehaviour call other methods by name. Methods called by name is hard to track in code (you cannot find "Usages", and SendMessage has a wide scope that is even harder to track).It is easy to roll out your own Invoke using Coroutines and C# actions:public static Coroutine Invoke(this MonoBehaviour monoBehaviour, Action action, float time){
return monoBehaviour.StartCoroutine(InvokeImpl(action, time));}private static IEnumerator InvokeImpl(Action action, float time){
yield return new WaitForSeconds(time);
action();} You can use this then like this in your monoBehaviour:this.Invoke(ShootEnemy); //where ShootEnemy is a parameterless void method. If you implement your own base MonoBehaviour, you can add your own Invoke to that.A safer SendMessage alternative is more difficult to implement. Instead, I usually use GetComponent variaties to get components on parents, the current game object, or children, and make the call directly.13. Don't let spawned objects clutter your hierarchy when the game runs. Set their parents to a scene object to make it easier to find stuff when the game is running. You could use an empty game object, or even a singleton (see later in this article) with no behaviour to make it easier to access from code. Call this object DynamicObjects.14. Be specific about using null as a legal value, and avoid it where you can.Nulls are helpful in detecting incorrect code. However, if you make if habit of silently passing over null, your incorrect code will happily run and you won't notice the bug until much later. Moreover, it can manifest itself deep in the code as each layer passes over null variables. I try to avoid using null as a legal value altogether.My preferred idiom is to not do any null checking, and let the code fail where it is a problem. Sometimes, in re-suable* methods, I will check a variable for null and throw an exception instead of passing it on to other methods where it may fail.In some cases, a value can be legitimately null, and needs to be handled in a different way. In cases like this, add a comment to explain when and why something can be null.A common scenario is often used for inspector-configured values. The user can specify a value, but if she doesn't, a default value is used. A better way to do this is with a class Optional that wraps values of T. (It's a bit like Nullable). You can use a special property renderer to render a tick box and only show the value box if it is ticked. (Unfortunately, you cannot use the generic class directly, you have to extend classes for specific values of T).[Serializable]public class Optional{
public bool useCustomV
public T}In your code, you can then use it like this:health = healthMax.useCustomValue ? healthMax.Value : DefaultHealthM 15. If you use Coroutines, learn to use them effectively. Coroutines can be a powerful way to solve many problems. But they are hard to debug, and you can easily code yourself into a mess that no-one, not even yourself, can understand.You should know:How to execute coroutines in parallel.How to execute coroutines in sequence.How to make new coroutines from existing ones.How to make
using CustomYieldInstruction.//This is itself a coroutineIEnumerator RunInParallel(){
yield return StartCoroutine(Coroutine1());
yield return StartCoroutine(Coroutine2());}public void RunInSequence(){
StartCoroutine(Coroutine1());
StartCoroutine(Coroutine1());}Coroutine WaitASecond(){
return new WaitForSeconds(1);}
16. Use extensions methods to work with components that share an interface. It is sometimes convenient to get components that implement a certain interface, or find objects with such components.The implementations below uses typeof instead of the generic versions of these functions. The generic versions don't work with interfaces, but typeof does. The methods below wraps this neatly in generic methods.public static TInterface GetInterfaceComponent(this Component thisComponent)
where TInterface : class{
return thisComponent.GetComponent(typeof(TInterface)) as TI} 17. Use extension methods to make syntax more convenient. For example:public static class TransformExtensions {
public static void SetX(this Transform transform, float x)
Vector3 newPosition =
new Vector3(x, transform.position.y, transform.position.z);
transform.position = newP
...} 18. Use a defensive GetComponent alternative. Sometimes forcing component dependencies through RequiredComponent can be a pain, and it may not always be possible or desirable, especially when you call GetComponent on somebody else's class. As an alternative, the following extension of GameObject can be used when a component is required to print out an error message when it is not found.public static T GetRequiredComponent(this GameObject obj) where T : MonoBehaviour{
T component = obj.GetComponent();
if(component == null)
Debug.LogError("Expected to find component of type "
+ typeof(T) + " but found none", obj);
}} 19. Avoid using different idioms to do the same thing. In many cases there are more than one idiomatic way to do things. In such cases, choose one to use throughout the project. Here is why:Some idioms don't work well together. Using one idiom forces design in one direction that is not suitable for another idiom.Using the same idiom throughout makes it easier for team members to understand what is going on. It makes structure and code easier to understand. It makes mistakes harder to make.Examples of idiom groups:Coroutines vs. state machines.Nested prefabs vs. linked prefabs vs. god prefabs.Data separation strategies.Ways of using sprites for states in 2D games.Prefab structure.Spawning strategies.Ways to locate objects: by type vs. name vs. tag vs. layer vs. reference ("links").Ways to group objects: by type vs. name vs. tag vs. layer vs. arrays of references ("links").Ways to call methods on other components.Finding groups of objects versus self-registration.Controlling execution order (Using Unity's execution order setup versus yield logic versus Awake / Start and Update / Late Update reliance versus manual methods versus any-order architecture).Selecting objects / positions / targets with the mouse in-game: selection manager versus local self-management.Keeping data between scene changes: through , or objects that are not Destroyed when a new scene is loaded.Ways of combining (blending, adding and layering) animation.Input handling (central vs. local)20. Maintain your own time class to make pausing easier. Wrap Time.DeltaTime and Time.TimeSinceLevelLoad to account for pausing and time scale. It requires discipline to use it, but will make things a lot easier, especially when running things of different clocks (such as interface animations and game animations).21. Custom classes that require updating should not access global static time. Instead, they should take delta time as a parameter of their Update method. This makes these classes useable when you implement a pausing system as explained above, or when you want to speed up or slow down the custom class's behavior.22. Use a common structure for making WWW calls. In games with a lot of server-communication, it is common to have dozens of WWW calls. Whether you use Unity's raw WWW class or a plugin, you can benefit from writing a thin layer on top that does the boiler plate for you.I usually define a Call method (one for each Get and Post), a CallImpl coroutine, and a MakeHandler. Essentially, the Call method builds a super hander from a parser, on-success and on-failure handlers using the MakeHandler method. It also calls the CallImpl courutine, which builds a URL, make the call, wait until it's done, and then call the super handler.Here is roughly how it looks:public void Call(string call, Func parser, Action onSuccess, Action onFailure){ var handler = MakeHandler(parser, onSuccess, onFailure); StartCoroutine(CallImpl(call, handler));} public IEnumerator CallImpl(string call, Func handler){ var www = new WWW(call);
handler(www);}public Func MakeHandler(Func parser, Action onSuccess, Action onFailure){ if(NoError(www))
var parsedResult = parser();
onSuccess(parsedResult); } else {
onFailure("error text"); }} This has several benefits.It allows you to avoid having to write a lot of boilerplate code.It allows you to handle certain things (such as displaying a loading UI component or handling certain generic errors) in a central place.23. If you have a lot of text, put it in a file. Don't put it in fields for editing in the inspector. Make it easy to change without having to open the Unity editor, and especially without having to save the scene.24. If you plan to localize, separate all your strings to one location. There are many ways to do this. One way is to define a Text class with a public string field for each string, with defaults set to English, for example. Other languages subclass this and re-initialize the fields with the language equivalents.More sophisticated techniques (appropriate when the body of text is large and / or the number of languages is high) will read in a spread sheet and provide logic for selecting the right string based on the chosen language.Class Design25. Decide how to implement inspectable fields, and make it a standard. There are two ways: make the fields public, or make them private and mark them as [Serializable]. The latter is "more correct" but less convenient (and certainly not the method popularized by Unity itself). Whichever way you choose, make it a standard so that developers in your team know how to interpret a public field.Inspectable fields are public. In this scenario, public means "the variable is safe to change by a designer during runtime. Avoid setting its value in code".Inspectable fields are private and marked Serializable. In this scenario, public means "it's safe to change this variable in code" (and hence you should not see too many, and there should not be any public fields in MonoBehaviours and ScriptableObjects).26. For components, never make variables public that should not be tweaked in the inspector. Otherwise they will be tweaked by a designer, especially if it is not clear what it does. In some rare cases it is unavoidable. In that case use a two or even four underscores to prefix the variable name to scare away tweakers:public float __aV27. Use property drawers to make fields more user-friendly.
can be used to customize controls in the inspector. This allows you to make controls that better fit the nature of the data, and put certain safe-guards in place (such as limiting the range of the variables).28. Prefer property drawers over custom editors. Property drawers are implement per field type, and is therefore much less work to implement. They are also more re-suable – once implemented for a type, they can be used for that type in any class. Custom editors are implemented per MonoBehaviour, and are therefor less re-usable and more work.29. Seal MonoBehaviours by default. Generally, Unity's MonoBehaviours are not very inheritance-friendly:The way Unity calls message-methods like Start and Update makes it tricky to work with these methods in subclasses. If you are not careful, the wrong thing gets called, or you forget to call a base method.When you use custom editors, you usually need to duplicate the inheritance hierarchy for the editors. Anyone who wants to extend one of your classes has to provide their own editor, or make do with whatever you provided.In cases where inheritance is called for, do not provide any Unity message-methods if you can avoid it. If you do, don't make them virtual. If necessary, you can define an empty virtual function that gets called from the message-method method that a child class can override to perform additional work.public class MyBaseClass{
public sealed void Update()
CustomUpdate();
... // This class's update
//Called before this class does its own update
//Override to hook in your own update code.
virtual public void CustomUpdate(){};}public class Child : MyBaseClass{
override public void CustomUpdate()
//Do custom stuff
}}This prevents a class from accidentally overriding your code, but still gives it the ability to hook into Unity's messages. One reason that I don't like this pattern is that the order of things becomes problematic. In the example above the child may want to do things directly after the class has done its own update.30. Separate interface from game logic. Interface components should in general not know anything about the game in which they are used. Give them the data they need to visualize, and subscribe to events to find out when the user interacts with them. Interface components should not do gamelogic. They can filter input to make sure it's valid, but the main rule processing should happen elsewhere. In many puzzle-games, the pieces are an extension of the interface, and should not contain rules. (For example, a chess piece should not calculate its own legal moves.Similarly, input should be separated from the logic that acts on that input. Use an input controller that informs your actor o the actor handles whether to actually move.Here is a stripped down example of a UI component that allows the user to select a weapon from a list of choices. The only thing these classes know about the game is the Weapon class (and only because Weapon is a useful source for the data this container needs to display). The game also knows nothing all it has to do is register for the OnWeaponSelect event.public WeaponSelector : MonoBehaviour{
public event Action OnWeaponSelect { }
//the GameManager can register for this event
public void OnInit(List
foreach(var weapon in weapons)
var button = ... //Instantiates a child button and add it to the hierarchy
buttonOnInit(weapon, () =& OnSelect(weapon));
// child button displays the option,
// and sends a click-back to this component
public void OnSelect(Weapon weapon)
if(OnWepaonSelect != null) OnWeponSelect(weapon);
}}public class WeaponButton : MonoBehaviour{
private Action&& onC
public void OnInit(Weapon weapon, Action onClick)
... //set the sprite and text from weapon
this.onClick = onC
public void OnClick() //Link this method in as the OnClick of the UI Button component
Assert.IsTrue(onClick != null);
//Should not happen
onClick();
} 31. Separate configuration, state and bookkeeping. Configuration variables are the variables tweaked in the inspector to define your object through its properties. For example, maxHealth.State variables is the variables that completely determines your object's current state, and are the variables you need to save if your game supports saving. For example, currentHealth.Bookkeeping variables are used for speed, convenience, or transitional states. They can always completely be determined from the state variables. For example, previousHealth.By separating these types of variables, you make it easier to know what you can change, what you need to save, what you need to send / retrieve over the network, and allows you to enforce this to some extent. Here is a simple example with this setup.public class Player{
[Serializable]
public class PlayerConfigurationData
public float maxH
[Serializable]
public class PlayerStateData
public PlayerConfigurationD
private PlayerState stateD
//book keeping
private float previousH
public float Health
public get { return stateData. }
private set { stateData.health = }
}} 32. Avoid using public index-coupled arrays. For instance, do not define an array of weapons, an array of bullets, and an array of particles, so that your code looks like this:public void SelectWeapon(int index){
currentWeaponIndex =
Player.SwitchWeapon(weapons[currentWeapon]);}public void Shoot(){
Fire(bullets[currentWeapon]);
FireParticles(particles[currentWeapon]);} The problem for this is not so much in the code, but rather setting it up in the inspector without making mistakes.Rather, define a class that encapsulates the three variables, and make an array of that:[Serializable]public class Weapon{
public GameO
public ParticleS
public B} The code looks neater, but most importantly, it is harder to make mistakes in setting up the data in the inspector.33. Avoid using arrays for structure other than sequences. For example, a player may have three types of attacks. Each uses the current weapon, but generates different bullets and different behaviour.You may be tempted to dump the three bullets in an array, and then use this kind of logic:public void FireAttack(){
/// behaviour
Fire(bullets[0]);}public void IceAttack(){
/// behaviour
Fire(bullets[1]);}public void WindAttack(){
/// behaviour
Fire(bullets[2]);}Enums can make things look better in code…public void WindAttack(){
/// behaviour
Fire(bullets[WeaponType.Wind]);}…but not in the inspector.It's better to use separate variables so that the names help show which content to put in. Use a class to make it neat.[Serializable]public class Bullets{
public Bullet fireB
public Bullet iceB
public Bullet windB}This assumes there is no other Fire, Ice and Wind data.34. Group data in serializable classes to make things neater in the inspector. Some entities may have dozens of tweakables. It can become a nightmare to find the right variable in the inspector. To make things easier, follow these steps:Define separate classes for groups of variables. Make them public and serializable.In the primary class, define public variables of each type defined as above.Do not initialize these variables in Awake or S since they are serializable, Unity will take care of that.You can specify defaults as before by assigning valuThis will group variables in collapsible units in the inspector, which is easier to manage.[Serializable]public class MovementProperties //Not a MonoBehaviour!{
public float movementS
public float turnSpeed = 1; //default provided}public class HealthProperties //Not a MonoBehaviour!{
public float maxH
public float regenerationR}public class Player : MonoBehaviour{
public MovementProperties movementP
public HealthPorperties healthP} 35. Make classes that are not MonoBehaviours Serializable even when they are not used for public fields. This allows you to view the class fields in the inspector when the Inspector is in Debug mode. This works for nested classes too (private or public).36. Avoid making changes to inspector-tweakables in code. A variable that is tweakable in the inspector is a configuration variable, and should be treated as a run-time constant and not double as a state-variable. Following this practice makes it easier to write methods to reset a component's state to the initial state, and makes it clearer what the variable does.public class Actor : MonoBehaviour{
public float initialHealth = 100;
private float currentH
public void Start()
ResetState();
private void Respawn()
ResetState();
private void ResetState()
currentHealth = initialH
}}Patterns
Patterns are ways to solve frequently occurring problems in a standard way. Bob Nystrom's
(readable free online) is a useful resource to see how patterns apply to problems that arise in game programming. Unity itself use a lot of these patterns: Instantiate is an example of t MonoBehaviours follow a version of the template pattern, UI and animation use the observer pattern, and the new animation engine uses state machines.These tips relate to using patterns with Unity specifically.37. Use singletons for convenience. The following class will make any class that inherits from it a singleton automatically:public class Singleton : MonoBehaviour where T : MonoBehaviour{
protected static T
//Returns the instance of this singleton.
public static T Instance
if(instance == null)
instance = (T) FindObjectOfType(typeof(T));
if (instance == null)
Debug.LogError("An instance of " + typeof(T) +
" is needed in the scene, but there is none.");
}} Singletons are useful for managers, such as ParticleManager or AudioManager or GUIManager.(Many programmers warn against classes vaguely named XManager because it points at a class either poorly named, or designed with too many unrelated tasks. In general, I agree with this. However, we have a small number of managers in every game, and they do the same in every game, so that these classes are in fact idioms.)Avoid using singletons for unique instances of prefabs that are not managers (such as the Player). Not adhering to this principle complicates inheritance hierarchies, and makes certain types of changes harder. Rather keep references to these in your GameManager (or other suitable God class
).Define static properties and methods for public variables and methods that are used often from outside the class. This allows you to write GameManager.Player instead of GameManager.Instance.player.As explained in other tips, singletons are also useful for creating default spawn points and objects that persist between scene loads that keep track of global data.38. Use state machines to get different behavior in different states or to execute code on state transitions. A light-weight state machine has a number of states, and for each state allows you to specify actions to run when entering or existing the state, and an update action. This can make code cleaner and less error prone. A good sign that you could benefit from a state machine is if your Update method's code has an if- or switch-statement that changes what it does, or variables such as hasShownGameOverMessage.public void Update(){
if(health &= 0)
if(!hasShownGameOverMessage)
ShowGameOverMessage();
hasShownGameOverMessage = //Respawning resets this to false
HandleInput();
} With more states, this type of code c a state machine can make it a lot cleaner.39. Use fields of type UnityEvent to set up the observer pattern in the inspector. The
class allows you to link in methods that take up to four parameters in the inspector using the same UI interface as the events on Buttons. This is especially useful for dealing with input.40. Use the observer pattern to detect when a field value changes. The problem of executing code only when a variable changes crops up frequently in games. We have baked a general solution of this in a generic class that allows you to register for events whenever the value changes. Here is an example with health. Here is how it is constructed:/*ObservedValue*/ health = new ObservedValue(100);health.OnValueChanged += () =& { if(health.Value &= 0) Die(); }; You can now change it everywhere, without doing checking in each place where you check it, for example, like this:if(hit) health.Value -= 10; Whenever the health hits a point below 0, the Die method is called. For further discussion and an implementation, see this .41. Use the Actor pattern on prefabs. (This is not a "standard" pattern. The basic idea is from Kieran Lord in .)An actor is the main com usually the component that provides the prefab's "identity", and the one higher-level code will most often interact with. The actor uses other components – helpers – on the same object (and sometimes on children) to do its work.If you make a button object from the menu in unity, it creates a game object with a Sprite and Button component (and a child with a Text component). In this case, Button is the actor component. Similarly, the main camera typically has several components (GUI Layer, Flare Layer, Audio Listener) in addition to the Camera component attached. Camera is the actor.An actor may require other components to work correctly. You can make your prefab more robust and useful by using the following attributes on your actor component:Use
to indicate all the components your actor needs on the same game object.
(Your actor can then always safely call GetComponent, without the need to check whether the value returned was null.)Use
to prevent multiple instances of the same component to be attached. Your actor can then always call GetComponent without having to worry what the behavior shoud be when there is more than one component attached).Use
if your actor object has children. This makes it easier to select in the scene view.[RequiredComponent(typeof(HelperComponent))][DisallowMultipleComponent][SelectionBase]public class Actor : MonoBehaviour{
...//} 42. Use generators for random and patterned data streams. (This is not a standard pattern, but one we found to be extremely useful.)A generator is similar to a random generator: it is an object with a Next method that can be called to get a new item of a specific type. Generators can be manipulated during their construction to produce a large variety of patterns or different types of randomness. They are useful, because they keep the logic of generating a new item separate from where you need the item, so that your code is much cleaner.Here are a few examples:var generator = Generator
.RamdomUniformInt(500)
.Select(x =& 2*x); //Generates random even numbers between 0 and 998var generator = Generator
.RandomUniformInt(1000)
.Where(n =& n % 2 == 0); //Same as abovevar generator = Generator
.Iterate(0, 0, (m, n) =& m + n); //Fibonacci numbersvar generator = Generator
.RandomUniformInt(2)
.Select(n =& 2*n - 1)
.Aggregate((m, n) =& m + n); //Random walk using steps of 1 or -1 one randomlyvar generator = Generator
.Iterate(0, Generator.RandomUniformInt(4), (m, n) =& m + n - 1)
.Where(n &= 0); //A random sequence that increases on average We have use generators for spawning obstacles, changing background colors, procedural music, generating letters sequences likely to make words in word games, and much more. Generators also work well to control co-routines that repeat at non-constant intervals, using the construct:while (true){
//Do stuff
yield return new WaitForSeconds(timeIntervalGenerator.Next());} To find out more about generators, see this .Prefabs and Scriptable Objects43. Use prefabs for everything. The only game objects in your scene that should not be prefabs (or part of prefabs) should be folders. Even unique objects that are used only once should be prefabs. This makes it easier to make changes that don't require the scene to change.44. Lin do not link instances to instances. Links to prefabs are maintained when dropping a links to instances are not. Linking to prefabs whenever possible reduces scene setup, and reduce the need to change scenes.As far as possible, establish links between instances automatically. If you need to link instances, establish the links programmatically. For example, the player prefab can register itself with the GameManager when it starts, or the GameManager can find the Player prefab instance when it starts.45. Don't put meshes at the roots of prefabs if you want to add other scripts. When you make the prefab from a mesh, first parent the mesh to an empty game object, and make that the root. Put scripts on the root, not on the mesh node. That way it is much easier to replace the mesh with another mesh without losing any values that you set up in the inspector.46. Use scriptable objects for shared configuration data instead of prefabs.If you do this:scenes are smalleryou cannot make changes to a single scene (on a prefab instance) by mistake.47. Use scriptable objects for level data. Level data is often stored in XML or JSON, but using scriptable objects instead has a few advantages:It can be edited in the Editor. This makes it easier to validate data and is friendlier to non-technical designers. Moreover, you can use custom editors to make it even easier to edit.You don't have to worry about reading / writing and parsing of the data.It is easier to split and nest, and manage the resulting assets, and so compose levels from building blocks rather than from a massive configuration.48. Use scriptable objects to configure behavior in the inspector. Scriptable objects are usually associated with configuring data, but they also allow you to use "methods" as data.Consider a scenario where you have an Enemy type, and each enemy has a bunch of SuperPowers. You could make these normal classes and have a list if them in the Enemy class… but without a custom editor you would not be able to set up a list of different superpowers (each with its own properties) in the inspector. But if you make these super powers assets (implement them as ScriptableObjects), you could!Here is how it looks:public class Enemy : MonoBehaviour{
public SuperPower superP
public UseRandomPower()
superPowers.RandomItem().UsePower(this);
}}public class BasePower : ScriptableObject{
virtual void UsePower(Enemy self)
}}[CreateAssetMenu("BlowFire", "Blow Fire")public class BlowFire : SuperPower{
override public void UsePower(Enemy self)
///program blowing fire here
}} There are a few things to be aware of when following this pattern:Scriptable objects cannot reliably be made abstract. Instead, use concrete bases classes, and throw NotImplementedExceptions in methods that should be abstract. You can also define an Abstract attribute and mark classes and methods that should be abstract with it.Scriptable objects that are generic cannot be serialized. However, you can use generic base classes and only serialize sub-classes that specify all the generics.49. Use scriptable objects to specialize prefabs. If two objects' configuration differ only in some properties, it is common to put two instances in the scene and adjust those properties on the instances. It is usually better to make a separate class of the properties that can differ between the two types into a separate scriptable object class.This gives you more flexibility:You can use inherit from your specialization class to give more specific properties to different types of object.Scene setup is much safer (you merely select the right scriptable object, instead of having to adjust all the properties to make the object into the desired type).It is easier to manipulate these objects at runtime through code.If you have multiple instances of the two types, you know their properties will always be consistent when you make changes.You can split sets of configuration variables into sets that can be mixed-and-matched.Here is a simple example of this setup.[CreateAssetMenu("HealthProperties.asset", "Health Properties")]public class HealthProperties : ScriptableObject{
public float maxH
public float resotrationR}public class Actor : MonoBehaviour{
public HealthProperties healthP}If the number of specializations is large, you may want to define the specialization as a normal class, and use a list of these in a scriptable object that's linked to a suitable place where you can get hold of it (such as your GameManager). There is a bit more glue necessary to make it safe, just the bare minimum is shown below.public enum ActorType{
Vampire, Wherewolf}[Serializable]public class HealthProperties{
public ActorT
public float maxH
public float resotrationR}[CreateAssetMenu("ActorSpecialization.asset", "Actor Specialization")]public class ActorSpecialization : ScriptableObject{
public List healthP
public this[ActorType]
get { return healthProperties.First(p =& p.type == type); } //Unsafe version!
}}public class GameManager : Singleton
public ActorSpecialization actorS
...}public class Actor : MonoBehaviour{
public ActorT
//Example usage
public Regenerate()
+= GameManager.Instance.actorSpecialization[type].resotrationR
}} 50. Use the
attribute to add ScriptableObject creation automatically to the Asset/Create menu. Debugging51. Learn how to use Unity's debugging facilities effectively. Add context objects to
statements to see from where they are generated.Use
to pause the game in the editor (useful, for example, when you want to an error condition occurs and you want to examine component properties in that frame).Use
functions for visual debugging (for example, DrawRay is very useful when debugging why ray casts are not hit).Use
for visual debugging. You can also supply gizmo renderers outside mono behaviours using the
attribute.Use the debug inspector view (to see the values of private fields at runtime in Unity using the inspector). 52. Learn how to use your IDE debugger effectively. See for example .53. Use a visual debugger that draws graphs of values over time. This is extremely helpful to debug physics, animation, and other dynamic processes, especially sporadic glitches. You will be able to see the glitch in the graph, and be able to see which other variables change at the same time. The visual inspection also makes certain types of strange behavior clear, such as values that change too often, or drift without apparent cause. We use , but there are several available.54. Use improved console logging. Use an editor extension that allows you to color-code output according to categories, and allows you to filter output according to those categories. We use , but there are several available.55. Use Unity's test tools, especially to test algorithms and mathematical code. See for example the
tutorial, or the post .56. Use Unity's test tools to run "scratchpad" tests. Unity's test tools are not only suitable for formal tests. They can also be exploited for convenient scratch-pad tests that can be run in the editor without having to run a scene.57. Implement shortcuts for taking screen shots. Many bugs are visual, and are much easier to report when you can take a picture. The ideal system should maintain a counter in PlayerPrefs so that successive screenshots are not overwritten. The screenshots should be saved outside the project folder to avoid people from accidentally committing them to the repository.58. Implement shortcuts for printing snapshots of important variables. This makes it easy to log some information when something unexpected happens during the game that you can inspect. Which variables depends on the game, of course. You will be guided by the typical bugs that occur in your game. Examples are positions of the player and enemies, or the "thinking state" an AI actor (such as the path it is trying to follow).59. Implement debug options for making testing easier. Some examples:Unlock all items.Disable enemies.Disable GUI.Make player invincible.Disable all gameplay.Be careful not to commit debug
changing debug options can mystify other developers on your team.60. Define constants for debug shortcut keys, and keep them in one place. Debug keys are not normally (or conveniently) processed in a single location like the rest of the game input. To avoid shortcut-key collisions, define constants in a central place. An alternative is to process all keys in one place regardless of whether it is a debug function or not. (The downside is that this class may need extra references to objects just for this).61. Draw or spawn small spheres at vertices when doing procedural mesh generation. This will help you make sure your vertices are where they are supposed to be and the mesh is the right size before you start messing with triangles and UVs to get your mesh to display.Performance62. Be wary of generic advice about design and construction for performance reasons.Such advice is often based on myths and is not backed by tests.Sometimes the advice is backed by tests but the tests are faulty.Sometimes the advice is backed by correct tests, but they are in an unrealistic or different context. (For example, it's easy to show how using arrays are faster than generic lists. However, in the context of a real game this difference is almost always negligible. Similarly, if the tests apply to different hardware than your target devices, their results may not be meaningful to you.)Sometimes the advice is sound, but out of date.Sometimes, the advice applies. However, there is a tradeoff. Slow games that ship are sometimes better than fast ones that don't. And heavily optimized games are more likely to contain tricky code that can delay shipping.Performance advice can be useful to keep in mind to help you track the source of actual problems faster using the process outlined below. 63. Test regularly on target devices from early-on. Devices have very different perform don't get surprised by them. The earlier you know about problems the more effectively you can address them.64. Learn how to use a profiler effectively to track the cause of performance problems. If you are new to profiling, see .Learn how to define your own frames (using
and ) for fain-grained analysis.Learn how to use platform-specific profiling, such as the .Learn to
in built players and
in the profiler.65. Use a custom profiler for more accurate profiling when necessary. Sometimes, Unity's profiler cannot give you a clear picture of what is going on: it may run out of profile frames, or deep-profiling can slow down the game so much that tests are not meaningful.
We use our own in-house profiler for this, but you should be able to find alternatives on the Asset Store.66. Measure impact of performance enhancements. When you make a change to increase performance, measure it to make sure the change is a real improvement. If the change is not measurable or negligent, undo it.67. Don't write less-readable code for better performance. Unless:You have a problem, identified the source with a profiler, you measured a gain after the change, and the gain is high enough compared with the loss in maintainability.ORYou know what you are doing.Naming Standard and Folder Structure68. Follow a documented naming convention and folder structure. Consistent naming and folder structure makes it easier to find things, and to figure out what things are.You will most probably want to create your own naming convention and folder structure. Here is one as an example.Naming General PrinciplesCall a thing what it is. A bird should be called Bird.Choose names that can be pronounced and remembered. If you make a Mayan game, do not name your level QuetzalcoatisReturn.Be consistent. When you choose a name, stick to it. Don't call something buttonHolder in one place and buttonContainer in another.Use Pascal case, like this: ComplicatedVerySpecificObject. Do not use spaces, underscores, or hyphens, with one exception (see Naming Different Aspects of the Same Thing).Do not use version numbers, or words to indicate their progress (WIP, final).Do not use abbreviations: DVamp@W should be DarkVampire@Walk.Use the terminology in the design document: if the document calls the die animation Die, then useDarkVampire@Die, not DarkVampire@Death.Keep the most specific descriptor on the left: DarkVampire, not VampireD PauseButton, not ButtonPaused. It is, for instance, easier to find the pause button in the inspector if not all buttons start with the word Button. [Many people prefer it the other way around, because that makes grouping more obvious visually. Names are not for grouping though, folders are. Names are to distinguish objects of the same type so that they can be located reliably and fast.]Some names form a sequence. Use numbers in these names, for example, PathNode0, PathNode1. Always start with 0, not 1.Do not use numbers for things that don't form a sequence. For example, Bird0, Bird1, Bird2 should be Flamingo, Eagle, Swallow.Prefix temporary objects with a double underscore __Player_Backup.Naming Different Aspects of the Same ThingUse underscores between the core name, and the thing that describes the "aspect". For instance:GUI buttons states EnterButton_Active, EnterButton_InactiveTextures DarkVampire_Diffuse, DarkVampire_NormalmapSkybox JungleSky_Top, JungleSky_NorthLOD Groups DarkVampire_LOD0, DarkVampire_LOD1Do not use this convention just to distinguish between different types of items, for instance Rock_Small, Rock_Large should be SmallRock, LargeRock.StructureThe organization of your scenes, project folder, and script folder should follow a similar pattern. Here are some abridged examples to get you started.Folder StructureMyGame
Scratchpad
DarkVampire
LightVampire
Structures
UI MyLibray
...PluginsSomeOtherAsset1SomeOtherAsset2...Scene StructureMainDebugManagers CamerasLightsUI
Structures
...Gameplay
...Dynamic ObjectsScripts Folder StructureDebugGameplay
...FrameworkGraphicsUI...
Unity的50个使用技巧(2016 Edition)
版权所有,禁止匿名转载;禁止商业使用;禁止个人使用。
翻译:高磊(稳定心态)
校审:罗倩(蘑菇)
大约四年前,我发布了关于Unity开发的50个技巧的初始版本。 虽然最新版本与初始版本仍有许多关联,但在初始版本之后,我修改了许多内容:
Unity更好用。例如,我现在信赖FPS计数器。使用property drawer的功能可以降低编写customeditors的必要性。同时Prefab的工作方式也降低了显式嵌套Prefab或替代件的需求。Scriptable objects更为友好。
VisualStudio集成度更佳,从而使调试操作更简便,同时减少了对于大量Gorilla调试操作的需求。
第三方工具和库更优化。AssetStore里现有许多可用的对于可视化调试和更佳日志记录等事物的辅助工具。我们自带(免费)的扩展插件有大量初始发布中所述的代码(以及本次版本发布所述的许多代码)。
版本控制更好。(或者可以说,我现在知道如何更有效地使用它)。比方说,无需对Prefab执行多个副本或者备份。
个人经验的积累。在过去四年里,我参与了许多Unity项目;包括大量的游戏原型制作,等的游戏制作,以及我们的旗舰工具Unity asset 。 本文是在考虑上述所有内容的基础上对初始版本进行的修订版本。
在继续讨论技巧前,本人现发布以下免责声明(与初始版本基本相同): 这些技巧并不适用于每个Unity项目。 这些技巧是基于本人与3到20人的小型团队协作参与项目所获得的经验。在结构性,可重用性,清晰度等方面存在费用——根据团队规模,项目规模和项目目标来确定是否此费用。比方说,你可能不会在游戏制作环节使用所有内容。
许多技巧涉及个人喜好问题(虽然这里列出的技巧之间可能有竞争,但都是很好的技巧)。
此外,Unity也在其官网上发布了一些最佳操作实例(虽然它们中大多数是从效能角度出发):
2)基于物理的内容创建的最佳操作实例:
3)Unity中的最佳2D操作实例:
4)Unity内部技巧和技巧:
5)Unity提示和技巧:
开发流程 <span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 确定开始的缩放比例,并以相同缩放比例构建所有原型。否则,你可能需要后续重做assets(例如,无法总是正确地缩放动画)。对于3D游戏,采用1 Unity单位= 1m通常是最佳的。对于不使用照明或物理的2D游戏,采用1 Unity单位 = 1 像素(在“设计”分辨率阶段)通常是较好的。对于UI(以及2D游戏),选择设计分辨率(我们使用HD或2xHD,并将所有assets设计为以此分辨率缩放。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 使每个场景都可以运行。这样可以避免为了运行游戏而必须转换场景,从而加快了测试速度。如果要在所有场景中必需的场景加载之间持续存在对象,这可能需要技巧。一种方法是当持续对象不存在于场景中时,使它们作为可自行加载的单例模式。另一个技巧中将详述单例模式。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 使用源代码控制,并学习如何有效地使用它。
将assets序列化为文本。实际上,它并不会提高场景和Prefab的可合并性,但它会使变化更容易观测。
采用场景和Prefab共享策略。一般来说,多个人不应在同一场景或Prefab工作。对于小型制作团队,只要在开始工作前确保没有人制作场景或Prefab即可。交换表示场景所有权的物理标记可能很有用(如果桌面上有场景标记,你仅可以在某一场景中工作)。
将标签作为书签。 确定并坚持采用分支策略。由于场景和Prefab不能平滑地合并,分支稍显复杂。然而当你决定使用分支时,它应该结合场景和Prefab共享策略使用。
使用子模块时要小心。子模型可能是维护可重用代码的最佳途径。但需注意几个警告事项: 元数据文件通常在多个项目中不一致。对于非Monobehaviour或非Scriptable object代码而言,这通常不是问题,但对于MonoBehaviours和Scriptable objects使用子模块可能会导致代码丢失。 如果你参与许多项目(包括一个或多个子模块项目),倘若你必须对几次迭代中的多个项目执行获取—合并—提交—推送操作以稳定所有项目的代码,有时会发生更新崩溃(并且如果其他人同时进行变更,它可能会转变为持续崩溃)。一种最大程度上降低此效应的方法是在项目初始阶段对子模块进行更改。如此一来,总是需要推送仅使用子模块的项目;它们从来无需推回。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 保持测试场景和代码分离。向存储库提交临时资源和脚本,并在完成后将它们移出项目。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 如果你要更新工具(尤其是Unity),必须同时进行。当你使用一个与先前不同的版本打开项目时,Unity能够更好地保留链接,但倘若人们使用不同的版本,有时仍然会丢失链接。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 在一个干净的项目中导入第三方assets,并从中导出一个可供自己使用的新的资源包。当你直接向项目导入这些资源,它们有时会导致问题: 可能存在冲突(文件或文件名),尤其对于在插件目录根中存在文件或者在实例中使用StandardAssets中assets的资源。 这些资源可能被无序地放入到自有项目的文件中。如果你决定不使用或者想要移除这些assets,这可能成为一个重要问题。
请按照下述步骤使assets导入更安全: <span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#)创建一个新项目,然后导入asset。 <span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#)运行实例并确保它们能够工作。 <span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#)将asset排列为一个更合适的目录结构。(我通常不对一个资源强制排列自有的目录结构。但是我确保所有文件均在一个目录中,同时在重要位置不存在任何可能会覆盖项目中现有文件的文件。 <span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#)运行实例并确保它们仍可以工作。(有时,当我移动事物时会导致assets损坏,但这通常不应该是一个问题)。 <span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#)现要移除所有无需的事物(如实例)。 <span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#)确保asset仍可编译,并且Prefab仍然拥有所有自身的链接。若留下任何需运行的事项,则对它进行测试。 <span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#)现选定所有assets,并导出一个资源包。 <span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#)导入到你的项目中。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 自动构建进程。甚至对于小型项目,这步很有用,但对于以下情况尤为适用: 你需要构建许多不同的游戏版本。 其他拥有不同程度技术知识的团队成员需要进行构建,或者 你需要对项目进行小幅调整后才能进行构建。 详见Unity构建编译:对于如何执行的较好指导的基本和高级可能性。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 为你的设置建立文档。大部分记录应在代码中,但是某些事项应记录在代码外。制作设计师通过耗时的设置来筛选代码。文档化的设置可以提高效率(若文档是最新的)。 对下述内容建立文档:
标签使用。
图层使用(对于碰撞,剔除和光线投射—从本质上来说,每个图层对应的使用)。
图层的GUI深度(每个图层对应的显示)
场景设置。
复杂Prefab的Prefab结构。
常用语偏好。
构建设置。
通用编码 <span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 将所有代码放入一个命名空间中。这避免了自有库和第三方代码之间可能发生的代码冲突。但不要依赖于命名空间以避免与重要类冲突。即使你会使用不同的命名空间,也不要将“对象”、“动作”或“事件”作为类名称。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 使用断言。断言对于代码中不变量的测试非常有用,它能够辅助清除逻辑错误。类提供了可用的断言。它们都可以测试一些条件,但如果不符合条件,则在控制台中写入错误信息。如果你不熟悉如何有效地使用断言,请参考使用断言编程的优点(a.k.a.断言语句)。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 切勿对显示文本以外的任何事项使用字符串。尤其应注意,不要使用字符串来标识对象或Prefab。但存在一些例外情形(仍然有一些内容只能通过Unity中的名称访问)。在这种情形下,将这些字符串定义为“AnimationNames”或 “AudioModuleNames”等文件中的常量。倘若这些类变为不可管理,使用嵌套类后便可类似命名AnimationNames.Player.Run。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 不要使用“Invoke”和“SendMessage”。这些MonoBehaviour方法通过名称调用其他方法。通过名称调用的方法难以在代码中追踪(无法找到“Usages”,而“发SendMessage”的范围更宽,因此更难以追踪)。 较简便的方法是使用Coroutines和C#操作推出“Invoke”:
1234567891011public static Coroutine Invoke(this MonoBehaviour monoBehaviour, Action action, float time) {
return monoBehaviour.StartCoroutine(InvokeImpl(action, time)); }
private static IEnumerator InvokeImpl(Action action, float time) {
yield return new WaitForSeconds(time);
action(); } 你可以参考monoBehaviour模式:
this.Invoke(ShootEnemy);
//其中ShootEnemy是一个无参数的void法。 如果你实现自己的基础MonoBehaviour,你可以向其中添加自己的“Invoke”。 另一种较安全的“SendMessage”方法更难以实施。与之相反,我通常使用“GetComponent”变量以获取父对象,当前游戏对象或子对象的组件,并直接执行调用。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 当游戏运行时,不要让派生对象混乱层次结构。将它们的父对象设为场景对象,以便在游戏运行时更容易找到内容。你可以使用一个空游戏对象,或者甚至使用一个无行为的单例模式(详见本文后面的部分),从而更容易地从代码进行访问。将此对象命名为“DynamicObjects”。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 明确是否要将空值(null)作为一个合法值,并尽量避免这么做 空值可辅助检测错误代码。但是,如果你使“if”默默地通过空值成为一种习惯,错误代码将很快运行,同时你只能在很久之后才会注意到错误。此外,随着每个图层通过空变量,它可以在代码深度暴露。我尝试避免将空值整体作为一个合法值。
我优先采用的常用语不是进行任何空检查,倘若它是一个问题,让代码失败。有时,在“可重用”方法中,我将检查出一个值为空的变量,并抛出一个异常,而不是将它传递至其它可能失败的方法。 在某些情形下,值可以合法为空,并且需要采取不同的方式处理。在此类情况下,添加注释来解释什么时候某些内容可能为空,并说明为什么可能为空。 常见场景通常用于inspector配置的值。用户可以指定一个值,但如果未指定任何值,则使用一个默认值。最好结合包含T值的可选类。(这有点像“可为空”)。你可以使用一个特殊的属性渲染器来渲染一个勾选框,若勾选,则仅显示数值框。 (但切勿直接使用泛型类,你必须扩展特定T值的类)。
1234567[Serializable] public class Optional&t& {
public bool useCustomV
public T } &/t& 在你的代码中,你可以采取这种使用途径: health= healthMax.useCustomValue ? healthMax.Value : DefaultHealthM
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 如果你使用“协程”,学习如何有效地使用它。 “协程”是解决许多问题的一种最有效的方法。但是难以对“协程”进行调式,同时你可以很容易地对它进行混乱的编码,从而使其他人,甚至包括你自己也无法理解其意义。 你应该知道:
1)如何并发执行协程。
2)如何按序执行协程。
3)如何从现有程序中创建新的协程。
4)如何使用“CustomYieldInstruction”创建自定义协程。
1234567891011121314151617 IEnumerator RunInSequence() {
yield return StartCoroutine(Coroutine1());
yield return StartCoroutine(Coroutine2()); }
public void RunInParallel() {
StartCoroutine(Coroutine1());
StartCoroutine(Coroutine1()); }
Coroutine WaitASecond() {
return new WaitForSeconds(1); }
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 利用扩展法来协同共享接口的组件。有时可以方便地获取实施某个接口的组件,或者找到这些组件相应的对象。 下述实例使用typeof而不是这些函数的通用版本。通用版本无法协同接口使用,但typeof却可以。下面的方法将其整洁地套入通用方法之中。
12345public static TInterface GetInterfaceComponent&tinterface&(this Component thisComponent)
where TInterface : class {
return thisComponent.GetComponent(typeof(TInterface)) as TI }&/tinterface&
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 利用扩展法使语法更简洁。例如:
1234567891011public static class TransformExtensions
public static void SetX(this Transform transform, float x)
Vector3 newPosition =
new Vector3(x, transform.position.y, transform.position.z);
transform.position = newP
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 使用另一种防御性GetComponent方法。有时通过RequiredComponent强制组件关系可能难以操作,但是这总是可能和可取的,特别是当你调用其它类上的GetComponent。作为一种替代方法,但需要某个组件打印找到的错误信息时,可以使用下述GameObject扩展。
123456789101112public static T GetRequiredComponent(this GameObject obj) where T : MonoBehaviour {
T component = obj.GetComponent();
if(component == null)
Debug.LogError("Expected to find component of type "
+ typeof(T) + " but found none", obj);
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 避免对相同的事项使用不同的常用语。在许多情况下,有多种常用法。此时,对整个项目选择一种常用法。其原因在于:
1)某些常用语不能一起工作。在某个方向中使用一种常用语强行设计可能不适合另一种常用语。
2)对于整个项目使用相同的常用语能够使团队成员更容易理解进展。它使结构和代码更容易理解。这样就更难犯错。
常用语组示例:
协程与状态机。
嵌套的Prefab,互相链接的Prefab和超级Prefab
数据分离策略。
对2D游戏中状态使用sprites的方法。
Prefab结构。
派生策略。
定位对象的方法:按类型,按名称,按标签,按图层和按引用关系(“链接”)。
分组对象的方法:按类型,按名称,按标签,按图层和按引用数组(“链接”)。
调用其他组件方法的途径。
查找对象组和自注册。
控制执行次序(使用Unity的执行次序设置,还是使用yield逻辑,利用Awake / Start和Update / Late Update依赖,还是使用纯手动的方法,或者采用次序无关的架构)。
在游戏中使用鼠标选择对象/位置/目标:SelectionManager或者对象自主管理。
在场景变换时保存数据:通过,或者是在新场景加载时未毁损的对象。
组合(混合、添加和分层)动画的方法。
输入处理(中央和本地)
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 维护一个自有的Time类,这可以更容易实现游戏暂停。包装一个“Time.DeltaTime”和“Time.TimeSinceLevelLoad”来实现暂停和游戏速度的缩放。它使用时有点麻烦,但是当对象运行在不同的时钟速率下就容易多了(例如界面动画和游戏动画)。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 需要更新的自定义类不应该访问全局静态时间。相反,它们应将增量时间作为它们Update方法的一个参数。当你如上所述实施一个暂停系统,或者当你想要加快或减慢自定义类的行为时,这样使这些类变为可用。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 使用常见结构进行WWW调用。在拥有很多服务器通信的游戏中,通常有几十个WWW调用。无论你是使用Unity的原始WWW类还是使用某个插件,你可以从生成样板文件的顶部写入一个薄层获益。 我通常定义一个Call方法(分别针对Get和Post),即CallImpl协程和MakeHandler。从本质上来说,Call方法通过采用MakeHandler法,从一个解析器,成功和失败的处理器构建出一个super hander。此外,它也调用CallImpl协程,创建一个URL,进行调用,等待直至完成,然后调用super handler。 其大概形式如下:
12345678910111213141516171819202122232425262728public void Call&t&(string call, Func&string, t=""& parser, Action&t& onSuccess, Action&string& onFailure) {
var handler = MakeHandler(parser, onSuccess, onFailure);
StartCoroutine(CallImpl(call, handler)); }
public IEnumerator CallImpl&t&(string call, Action&t& handler) {
var www = new WWW(call);
yield return
handler(www); }
public Action&www& MakeHandler&t&(Func&string, t=""& parser, Action&t& onSuccess, Action&string& onFailure) {
return (WWW www) =&
if(NoError(www))
var parsedResult = parser(www.text);
onSuccess(parsedResult);
onFailure("error text");
} }&/string&&/t&&/string,&&/t&&/www&&/t&&/t&&/string&&/t&&/string,&&/t& 它具有一些优点:
它允许你避免编写大量样板代码。
它允许你在中央位置处理某些事项(例如显示加载的UI组件或处理某些通用错误)。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 如果你有大量文本,将它们放在同一个文件中。不要将它们放入inspector将编辑的字段中。使其在无需打开Unity编辑器,尤其是无需保存场景的前提下易于更改。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 如果你想执行本地化,将所有字符串分离到同一个位置。有很多方法可以实现这一点。一种方法是针对每个字符串定义一个具有public字符串字段的Text类,例如默认设为英文。其他语言将其子类化,并使用同等语言重新初始化这些字段。 一些更复杂的技术(其适用情形是正文本较大和/或语言数量较多时)将读取到一个电子表格中,并基于所选语言提供选择正确字符串的逻辑。
类的设计 <span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 确定实现可检查字段的方法,并将其确立为标准。有两种方法:使字段public,或者使它们private并标记为[可序列化]。后者“更正确”但不太方便(当然不是Unity本身常用的方法)。无论你选择哪种方式,将它确立为标准,以便于团队中开发人员知道如何解释一个public字段。
可检查字段是public的。在这种情况下,public表示“设计师在
运行时更改此变量是安全的。避免在代码中设置该值”。
可检查字段是private,并被标记为“可序列化”。 在这种情
况下,public表示“在代码中更改此变量是安全的”(因此,你不应该看到太多,并且在MonoBehaviours 和ScriptableObjects中不应该有任何public字段)。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 对于组件,切勿使不应在inspector中调整的变量成为public。否则,它们将被设计师调整,特别是当不清楚它是什么时。在某些罕见的情况下,这是无法避免的。此时,使用两条,甚至四条下划线对变量名添加前缀以警告调整人员: public float __aV
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 使用Property Drawers使字段更加用户友好。可以使用Property Drawers自定义inspector中的控制。这样可以使你能够创建更适合数据性质的控制,并实施某些安全保护(如限定变量范围)。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 相较于Custom Editors,更偏好采用PropertyDrawers。Property Drawers是根据字段类型实现的,因此涉及的工作量要少得多。另外,它们的重用性更佳—一旦实现某一类型,它们可应用于包含此类型的任何类。而Custom Editors是根据MonoBehaviour实现的,因此重用性更少,涉及的工作量更多。
<span style='font-size:14.0font-family:&微软雅黑&,&sans-serif&;color:#. 默认密封MonoBehaviours。一般来说,UnityMonoBehaviours的继承友好不高:
类似于Start和Update,Unity调用信息的方式使得在子类中难以使用这些方法。你稍不注意就可能调用错误内容,或者忘记调用一个基本方法。当你使用custom editors时,通常需要对editors复制继承层次结构。任何人在扩展某一类时,必须提}

我要回帖

更多关于 unity hierarchy查找 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信