【扩展和MonoBehaviourBase】

21、扩展一个自己的Mono Behaviour基类。然后自己的全部组件都从它派生

这能够使你方便的实现一些通用函数。比如类型安全的Invoke。或者是一些更复杂的调用(比如random等等)。

22、为Invoke, StartCoroutine and Instantiate 定义安全调用方法

定义一个托付任务(delegate Task),用它来定义须要调用的方法,而不要使用字符串属性方法名称。比如:

public void Invoke(Task task, float time){   Invoke(task.Method.Name, time);}

23、为共享接口的组件扩展

有些时候把获得组件、查找对象实如今一个组件的接口中会非常方便。

以下这样的实现方案使用了typeof。而不是泛型版本号的函数。

泛型函数无法在接口上工作,而typeof能够。以下这样的方法把泛型方法整洁的包装起来。

[C#] 
1
//Defined in the common base class for all mono behaviourspublic I GetInterfaceComponent() where I : class{  return GetComponent(typeof(I)) as I;} public static List FindObjectsOfInterface() where I : class{  MonoBehaviour[]
monoBehaviours = FindObjectsOfType();  List list = new List();   foreach(MonoBehaviour behaviour in monoBehaviours)  {     I component = behaviour.GetComponent(typeof(I)) as I;      if(component != null)     {        list.Add(component);     }  }   return
list;}

24、使用扩展来让代码书写更便捷

比如:

[AppleScript] 
1
public staticclassCSTransform{  public static void SetX(this Transform transform,float x)  {     Vector3newPosition=         newVector3(x,transform.position.y,transform.position.z);      transform.position=newPosition;  }  ...}

25、使用防御性的GetComponent()

有些时候强制性组件依赖(通过RequiredComponent)会让人蛋疼。

比如。非常难在Inspector中改动组件(即使他们有相同的基类)。以下是一种替代方案。当一个必要的组件没有找到时。输出一条错误信息。

[AppleScript] 
1
public static T GetSafeComponent<</code>T>(this GameObject obj)whereT:MonoBehaviour{  T component=obj.GetComponent<</code>T>();   if(component==null)  {     Debug.LogError("Expected to find component of type "         +typeof(T)+" but found none",obj);  }   returncomponent;}

【风格】26、避免对同一件事使用不同的处理风格

在非常多情况下,某件事并不仅仅有一个惯用手法。在这样的情况下,在项目中明白选择当中的一个来使用。以下是原因:

  • 一些做法并不能非常好的一起协作。

    使用一个。能强制统一设计方向。并明白指出不是其它做法所指的方向。

  • 团队成员使用统一的风格。可能方便大家互相的理解。

    他使得总体结构和代码都更easy理解。这也能够降低错误;

几组风格的样例:

  • 协程与状态机(Coroutines vs. state machines);
  • 嵌套的Prefab、互相链接的Prefab、超级Prefab(Nested prefabs vs. linked prefabs vs. God prefabs);
  • 数据分离的策略。
  • 在2D游戏的使用Sprite的方法;
  • Prefab的结构;
  • 对象生成策略;
  • 定位对象的方法:使用类型、名称、层、引用关系;
  • 对象分组的方法:使用类型、名称、层、引用数组。
  • 找到一组对象,还是让它们自己来注冊。
  • 控制运行次序(使用Unity的运行次序设置。还是使用Awake/Start/Update/LateUpdate,还是使用纯手动的方法,或者是次序无关的架构)。
  • 在游戏中使用鼠标选择对象/位置/目标:SelectionManager或者是对象自主管理;
  • 在场景变换时保存数据:通过PlayerPrefs,或者是在新场景载入时不要销毁的对象;
  • 组合动画的方法:混合、叠加、分层;

【时间】

27、维护一个自己的Time类,能够使游戏暂停更easy实现

做一个“Time.DeltaTime”和""Time.TimeSinceLevelLoad"的包装,用来实现暂停和游戏速度缩放。这使用起来略显麻烦,可是当对象执行在不同的时钟速率下的时候就方便多了(比如界面动画和游戏内动画)。



                                                                                                                                                     

【生成对象】

28、不要让游戏执行时生成的对象搞乱场景层次结构

在游戏执行时,为动态生成的对象设置好它们的父对象。能够让你更方便的查找。

你能够使用一个空的对象,或者一个没有行为的单件来简化代码中的訪问。能够给这个对象命名为“DynamicObjects”。





                                                                                                                                                    

【类设计】

29、使用单件(Singleton)模式

从以下这个类派生的全部类,将自己主动获得单件功能:

[AppleScript] 
1
publicclassSingleton<</code>T>:MonoBehaviourwhereT:MonoBehaviour{  protected static T instance;   /**     Returnstheinstanceofthis singleton.  */  public static T Instance  {     get     {        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.");           }        }         returninstance;     }  }}

单件能够作为一些管理器,比如ParticleManager或者AudioManager亦或者GUIManager。

  • 对于那些非唯一的prefab实例使用单件管理器(比如Player)。不要为了坚持这条原则把类的层次关系复杂化。宁愿在你的GameManager(或其它合适的管理器中)中持有一个它们的引用。
  • 对于外部常常使用的共同拥有变量和方法定义为static。这样你能够这样简便的书写“GameManager.Player”。而不用写成“GameManager.Instance.player”。

30、在组件中不要使用public成员变量,除非它须要在inspector中调节

除非须要设计师(策划or美术)去调节的变量。特别是它不能明白表明自己是做什么的变量。不要声明为public。假设在这些特殊情况下,无法避免,则可使用两个甚至四个下划线来表明不要从外部调节它,比如:

public float __aVariable; 

31、把界面和游戏逻辑分开

这一条本质上就是指的MVC模式。

全部的输入控制器,仅仅负责向对应的组件发送命令。让它们知道控制器被调用了。举一个控制器逻辑的样例,一个控制器依据玩家的状态来决定发送哪个命令。

可是这样并不好(比如,假设你加入了多个控制器。那将会导致逻辑反复)。相反的,玩家对象应该依据当前状态(比如减速、惊恐)来设置当前的速度。并依据当前的面朝向来计算怎样向前移动。控制器仅仅负责做他们自己状态相关的事情,控制器不改变玩家的状态。因此控制前甚至能够根本不知道玩家的状态。另外一个样例,切换武器。

正确的方法是,玩家有一个函数:“SwitchWeapon(Weapon
newWeapon)”供GUI调用。GUI不应该维护全部对象的Transform和他们之间的父子关系。





全部界面相关的组件,仅仅负责维护和处理他们自己状态相关的数据。比如,显示一个地图,GUI能够依据玩家的位移计算地图的显示。可是。这是游戏状态数据。它不属于GUI。GUI仅仅是显示游戏状态数据。这些数据应该在其它地方维护。地图数据也应该在其它地方维护(比如GameManager)。





游戏玩法对象不应该关心GUI。有一个例外是处理游戏暂停(可能是通过控制Time.timeScale,事实上这并非个好主意)。游戏玩法对象应该知道游戏是否暂停。可是,这就是所有了。

另外。不要把GUI组件挂到游戏玩法对象上。





这么说吧。假设你把全部的GUI类都删了,游戏应该能够正确编译。





你还应该达到:在不须要重写游戏逻辑的前提下,重写GUI和输入控制。







32、分离状态控制和簿记变量

簿记变量仅仅是为了使用起来方便或者提高查找速度。而且能够依据状态控制来覆盖。将两者分离能够简化:

  • 保存游戏状态
  • 调试游戏状态

实现方法之中的一个是为每一个游戏逻辑定义一个”SaveData“类,比如:

[AppleScript] 
1
[Serializable]PlayerSaveData{  public float health;//publicforserialisation,notexposedininspector} Player{  //... bookkeeping variables   //Don’t exposestateininspector. Stateis nottweakable.  private PlayerSaveData playerSaveData;}

33、分离特殊的配置

如果我们有两个敌人,它们使用同一个Mesh。可是有不同的属性设置(比如不同的力量、不同的速度等等)。有非常多方法来分离数据。以下是我比較喜欢的一种。特别是对于对象生成或者游戏存档时,会非常好用。

(属性设置不是状态数据。而是配置数据。所以我们不须要存档他们。

当对象载入或者生成是,属性设置会自己主动载入。)

  • 为每个游戏逻辑类定义一个模板类。

    比如。对于敌人。我们来一个“EnemyTemplate”。全部的属性设置变量都保存在这个类中。

  • 在游戏逻辑的类中,定义一个上述模板类型的变量。
  • 制作一个敌人的Prefab。以及两个模板的Prefab:“WeakEnemyTemplate”和"StrongEnemyTemplate"。

  • 在载入或者生成对象是,把模板变量正确的复制。

这样的方法可能有点复杂(在一些情况下,可能不须要这样)。



举个样例,最好使用泛型。我们能够这样定义我们的类:

[AppleScript] 
1
publicclassBaseTemplate{  ...}publicclassActorTemplate:BaseTemplate{  ...}publicclassEntity<</code>EntityTemplateType>whereEntityTemplateType:BaseTemplate{  EntityTemplateType template;  ...}publicclassActor:Entity<</code>ActorTemplate>{  ...}

34、除了显示用的文本。不要使用字符串

特别是不要用字符串作为对象或者prefab等等的ID标识。一个非常遗憾的例外是动画系统,须要使用字符串来訪问对应的动画。

35、避免使用public的数组

举例说明,不要定义一个武器的数组,一个子弹的数组,一个粒子的数组,这样你的代码看起来像这样:

public void SelectWeapon(int index){    currentWeaponIndex = index;   Player.SwitchWeapon(weapons[currentWeapon]);} public void Shoot(){   Fire(bullets[currentWeapon]);  FireParticles(particles[currentWeapon]);   }

这在代码中还不是什么大问题。可是在Inspector中设置他们的值的时候,就非常难不犯错了。



我们能够定义一个类,来封装这三个变量,然后使用一个它的实例数组:

[Serializable]public class Weapon{   public GameObject prefab;   public ParticleSystem particles;   public Bullet bullet;} 这样代码看起来非常整洁。可是更重要的是,在Inspector中设置时就不easy犯错了。



36、在结构中避免使用数组

举个样例,一个玩家能够有三种攻击形式,每种使用当前的武器,并发射不同的子弹、产生不同的行为。

你能够把三个子弹作为一个数组。并像以下这样组织逻辑:

public void FireAttack(){   /// behaviour   Fire(bullets[0]);} public void IceAttack(){   /// behaviour   Fire(bullets[1]);} public void WindAttack(){   ///
behaviour   Fire(bullets[2]);} 使用枚举值能够让代码看起来更好一点:



public void WindAttack(){   /// behaviour   Fire(bullets[WeaponType.Wind]);} 

可是这对Inspector一点也不好。



最好使用单独的变量,而且起一个好的变量名,可以代表他们的内容的含义。使用以下这个类会更整洁。

[Serializable]public class Bullets{   public Bullet FireBullet;   public Bullet IceBullet;   public Bullet WindBullet;}这里如果没有其它的Fire、Ice、Wind的数据。



37、把数据组织到可序列化的类中,能够让inspector更整洁

有些对象有一大堆可调节的变量,这样的情况下在Inspector中找到某个变量简直就成了噩梦。

为了简化这样的情况,能够使用一下的步骤:

  • 把这些变量分组定义到不同的类中。并让它们声明为public和serializable。
  • 在一个基本的类中,把上述类的实例定义为public成员变量;
  • 不用在Awake或者Start中初始化这些变量,由于Unity会处理好它们。
  • 你能够定义它们的默认值;

这能够把变量分组到Inspector的分组页签中,方便管理。

[Serializable]public class MovementProperties //Not a MonoBehaviour!{   public float movementSpeed;   public float turnSpeed = 1; //default provided} public class HealthProperties
//Not a MonoBehaviour!{   public float maxHealth;   public float regenerationRate;} public class Player : MonoBehaviour{   public MovementProperties movementProeprties;   public HealthPorperties healthProeprties;}

版权声明:本文博客原创文章,博客,未经同意,不得转载。

Unity3D游戏开发最佳实践20技能(两)的更多相关文章

  1. Unity3D游戏开发最佳实践20技巧(一)

    关于这些技巧这些技巧不可能适用于每一个项目. 这些是基于我的一些项目经验.项目团队的规模从3人到20人不等. 框架结构的可重用性.清晰程度是有代价的--团队的规模和项目的规模决定你要在这个上面付出多少 ...

  2. Unity3D游戏开发最佳实践20技巧(三)

    [文本] 38.假设你有非常多的剧情文本.那么把他们放到一个文件中面. 不要把他们放到Inspector的字段中去编辑. 这些须要做到不打开Unity,也不用保存Scene就能够方便的改动. 39.假 ...

  3. Unity3D游戏开发从零单排(四) - 制作一个iOS游戏

    提要 此篇是一个国外教程的翻译,尽管有点老,可是适合新手入门. 自己去写代码.debug,布置场景,能够收获到非常多.游戏邦上已经有前面两部分的译文,这里翻译的是游戏的最后一个部分. 欢迎回来 在第一 ...

  4. Hadoop MapReduce开发最佳实践(上篇)

    body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...

  5. Unity3D游戏开发初探—2.初步了解3D模型基础

    一.什么是3D模型? 1.1 3D模型概述 简而言之,3D模型就是三维的.立体的模型,D是英文Dimensions的缩写. 3D模型也可以说是用3Ds MAX建造的立体模型,包括各种建筑.人物.植被. ...

  6. [转]Android开发最佳实践

    ——欢迎转载,请注明出处 http://blog.csdn.net/asce1885 ,未经本人同意请勿用于商业用途,谢谢—— 原文链接:https://github.com/futurice/and ...

  7. [Unity3D]Unity3D游戏开发《反对》说到游戏(上)——目标跟踪

    朋友,大家好.我是秦培,欢迎关注我的博客.我的博客地址blog.csdn.net/qinyuanpei. 首先博主要自我反省,过了这么久才来更新博客,这段时间主要是在忙着写期末的作业,所以博主基本上没 ...

  8. [Unity3D]Unity3D游戏开发之跑酷游戏项目解说

    大家好,我是秦元培.我參加了CSDN2014博客之星的评选,欢迎大家为我投票,同一时候希望在新的一年里大家能继续支持我的博客. 大家晚上好.我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.c ...

  9. Android和PHP开发最佳实践

    Android和PHP开发最佳实践 <Android和PHP开发最佳实践>基本信息作者: 黄隽实丛书名: 移动应用开发技术丛书出版社:机械工业出版社ISBN:9787111410508上架 ...

随机推荐

  1. 【原创】leetCodeOj --- Min Stack 解题报告

    题目地址: https://oj.leetcode.com/problems/min-stack/ 题目内容: Design a stack that supports push, pop, top, ...

  2. ORA-00600 [kollasg:client-side tmp lob]

    今天在查看一个库的日志时,发现被ORA-00600 [kollasg:client-side tmp lob] 错误刷屏了. 发生该错误的原因是由于应用那边lob的问题.lob没有被初始化,建议使用E ...

  3. Flex强制类型转换错误

    1.错误描写叙述 TypeError: Error #1034: 强制转换类型失败:无法将 HoleDetailInnerClass0@c2cccf1 转换为 mx.controls.listClas ...

  4. ubuntu下安装wine

    直接在终端里面输入“sudo apt-get install wine”不要引号,即可安装wine.至于使用它,终端命令就是“wine ....”举个例子,你现在要运行魔兽, 然后你的魔兽的文件夹的位 ...

  5. Codeforces Round #257 (Div. 2) D题:Jzzhu and Cities 删特殊边的最短路

    D. Jzzhu and Cities time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  6. 得到Android系统语言设置

    private int g_lag = 1; // String filename = Locale.getDefault().getLanguage(); if (filename != null) ...

  7. WPF和Expression Blend开发实例:一个样式实现的数字输入框

    原文:WPF和Expression Blend开发实例:一个样式实现的数字输入框 今天来一个比较奇淫技巧的手法,很少人用,同时也不推荐太过频繁的使用. 先上样式: <Style x:Key=&q ...

  8. 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)

    原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...

  9. hdu3790最短路径问题

    题意是这种,给你一个无向图, 每条边有距离和花费, 假设从第一个点到末点的最短路不唯一, 则输出最短路长度以及最少的花费. 否则输出长度和花费即可. 用传说中的链式向前星优化了一下边的存储, 写了个s ...

  10. Objective-C语言的一些基础特性

    OC与C++.Java等面向对象语言有很多的类似之处,不过在很多方面也是有所差别的.若是用过某一种面向对象语言,那么就很容易理解OC语言所用的范式和模板了.但是在语法使用上,也许会显得陌生.因为OC语 ...