使用Unity的50个建议:Part1(译文)

  • 关于这些建议

    这些建议并不适用于所有的项目

    • 这些建议是基于我与3-20人的小团队项目经验总结出来的

    • 结构、可重复使用性、明晰度都是有价的——团队规模和项目规模决定了是否值得付这个价。

    • 一些建议也许公然违抗了传统的Unity开发。例如:使用专业化的组合而不是使用实例就很不像Unity的作风,价格也很高。即使看上去挺疯狂的,但我还是看到了这些建议给开发者带来了利益。


    过程方面

    1.避免分支资产 对于任何资产我们应该只有一个版本。如果你非要把一个预设,场景,或网格分支开来,那么情遵循一个过程,这个过程必须清楚的表明哪一个才是正确的版本。错误的分支应该有一个显著的名字,例如,使用双下划线前缀__MainScene_Backup。预设的分支需要一个特定的过程来使其安全。(详见预设部分)


    2.持有项目副本 任何一个使用版本控制的团队成员都应该持有项目的副本,用于测试检查。在变动之后,副本,这一干净的版本,应该更新并测试。任何人都不能对干净的副本做任何改变。当找回丢失的东西的时候,这一点就会发挥作用。


    3.考虑使用外部关卡工具来编辑关卡 Unity并不是完美的关卡编辑器。例如,我们曾经使用TuDee来为3D游戏建立关卡,这是个能让我们轻松建立砖块的工具(捕捉网格、多倍数的90度旋转、2D视图、快速选择砖块)。从XML文件转为预设的实例是简单的。你可以从GuerrillaTool Development获得更多的启发。


    4.在XML中保存关卡而不是在场景中 这是一个极好的技术

    • 它使我们不必重新设置每一个场景

    • 它使装载更加迅速(在场景之间共享大部分对象的情况下)

    • 它让合并场景更加容易(即使用Unity基于文本的场景,但太多的数据仍就导致合并在任何情况下都是不切实际的)

    • 它使跟踪关卡的数据轨迹更为简单

    当然你也可以使用Unity作为关卡编辑器(虽然你不需要),你需要写一些代码来序列化和反序列化你的数据,在编辑器内和运行的时候装载关卡,然后在编辑器里保存关卡。你可能还需要模仿Unity的ID系统来维护对象之间的参照。


    5.编写自定义的检查代码 写自定义检查相当简单,但是Unity的系统有很多弊端

    • 它不支持利用继承

    • 它不支持定义字段类型关卡的检查组件,只支持类的关卡。例如,如果每个游戏对象都有字段类型SomeCoolType,你想在检查过程中以不同的描述显现出来,那你就需要为所有的类写检查的代码。

    你可以彻底的重新执行检查系统来解决这些问题。用一些映像技巧,这并不想看起来那么难,具体方法将在文章末尾提供。


    6.使用命名为空的游戏对象作为场景文件夹 仔细安排你的场景使它容易找到对象。


    7.在000条件下维护预设和文件夹(空的游戏对象) 如果一个转变专门用于定位一个对象,那么它应该在原点。这样,在本地和世界空间运行出错的风险就更小,代码也会更简单。


    8.减少GUI组件的偏移 偏移量应该始终用在父组件的布局组件里;它们不应该依靠更上一级的组件定位。偏移量不应通过互相抵消来正确显示,这基本上是为了防止以下事件:

    父容器定位在(100, -50),子容器,应该定位在(10, 10),然后定位在(90, 60)[相对于父容器]。

    这种错误在容器隐藏,或者是根本没有可视化表现的情况下是常见。


    9.把你的世界基准定义在y = 0。这样更容易把对象放在地面上,在游戏逻辑、AI以及物理方面把世界看成一个2D空间(适当时)


    10.让游戏的每个场景运行流畅。这大大减少了测试时间,要让所有的场景运行流畅,你需要做两件事:

    首先,你要找到一种方法仿制所有的数据,这些数据在之前下载的场景里是需要但却是不可用的。

    第二,产生的对象必须坚持场景负载之间的下列语句

    1
    2
    3
    4
    5
    myObject= FindMyObjectInScene();
    if (myObjet == null )
    {
        myObject = SpawnMyObject();
    }



    美术方面

    11.把人物和站立物体的支点放在底部,而不是中心。这样易于将人物和物体精准的放在地面上。同时在游戏逻辑、AI以及物理方面,这也能让3D工程像2D一样简单,当然是在某些合适的情况下。


    12. 让所有的网格面向同一方向(正或负Z轴)。这适用于那些有朝向概念的人物或者事物的网格。如果所有的东西都面朝一个方向,那么许多算法都可以得到简化。


    13.从一开始就确定尺寸。


    14.制作二聚平面用以GUI组件和手动创建粒子。


    15.制作和使用的测试技术

    • 网格

    • 各种纯的颜色:白色,黑色着色试验,50%的灰,红,绿,蓝,黄,洋红,青蓝。

    • 阴影检测梯度:黑色到白色,红色,绿色,红色,蓝色,绿色,蓝色。

    • 黑白棋盘

    • 平滑和崎岖的法线贴图

    • 照明设备(如预制)快速建立测试场景

    16.对于一切都可以使用预制。游戏场景中的唯一对象不应该是预制,而应该是文件夹。即使是只使用一次的特殊对象也应该是预制。这使得不改变场景也可以轻松实现转变。(这也让使用EZGUI构建sprite地图时更加可靠)


    17.使用不同的预制来专业化,不使用专门的实例。如果你有两个类型的敌人,他们的唯一区别是他们的财产不同,那么对财产分别作预制,然后再将其链接,这让下面两点成为可能:

    • 在一个地方对任何类型做改变

    • 在不改变场景的情况下做出变化

    如果你有太多的敌人类型,那么就不用在编辑器重做出专业化实例了。一种代替方法是做程序,或者使用对所有的敌人使用一个核心文件/预制。一个下降动作可以用于不同的敌人,一个运算可以基于敌人位置或玩家进程。


    18.将预制之间链接,实例之间不链接。当把预制拖放到场景时,预制的链接可以得到保证,而实例则不可以。链接到预制可以在任何时候减少场景的建立,也可以减少场景变化的需求。


    19.尽可能在实例之间自动建立连接。如果你需要链接实例,建立编程链接。例如,玩家预制可以在GameManager启动时自己注册,或者GameManager在启动时可以找到玩家预制实例。

    • 如果你想添加其他脚本的话,就不要把网格放在预制的根源。

    • 用链接预制代替嵌套预制。


    20.使用安全的流程来分支预制。我们以玩家预制为例来解释:

    如下是一个有风险的改变玩家预制的方法:

    1.  复制玩家预制

    2.  重命名该副本 __Player_Backup

    3.  改变玩家预制

    4.  如果一切顺利,删除 __Player_Backup

    不要把把副本命名为Player_New,并且改变它!

    有些情况更加复杂。例如,某个改变可能涉及两个人,按照上述步骤直到Person 2完成的时候,可能会破坏掉所有人的工作场景。如果足够快的话,仍会是这样。因为变化需要的时间更长,你可以仿照下面的方法:

    Person 1

    1.  复制玩家预制

    2.  重命名为__Player_WithNewFeature或者__Player_ForPerson2.

    3.  在副本上修改,并且提交到Person 2

    Person 2

    1.  在新预制上做修改

    2.  复制玩家预制,命名为 __Player_Backup.

    3.  拖动__Player_WithNewFeature的实例到场景

    4.  拖动这个实例到原来的玩家预制

    5.  如果一切顺利,删除__Player_Backup 和 __Player_WithNewFeature.

     

    拓展组件和 MonoBehaviourBase

    21.拓展你自己的基本单一行为,并推导出所有它的组件。这让你实现一些通用功能,如类的安全调用和其他更复杂的调用。


    22.定义调用的安全方法StartCoroutine和实例化。定义一个委托任务,并用它来定义不依赖于字符串名称的方法,例如:

    1
    2
    3
    4
    public voidInvoke(Task task, float time)
    {
        Invoke(task.Method.Name, time);
    }


    23.使用共享界面的拓展组件。有时候可以很方便的得到执行某个界面的组件,或者使用组件找到对象。下面的执行使用typeof来代替这些功能的通用版本。通用版本不能使用这些接口,但是typeof可以。请参考:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //Definedin the common base class for all mono behaviours
    publicI GetInterfaceComponent<I>() where I : class
    {
        return GetComponent( typeof (I)) as I;
    }
    publicstatic List<I> FindObjectsOfInterface<I>() where I : class
    {
        MonoBehaviour[] monoBehaviours =FindObjectsOfType<MonoBehaviour>();
        List<I> list = new List<I>();
         foreach(MonoBehaviour behaviour inmonoBehaviours)
        {
           I component =behaviour.GetComponent( typeof (I)) as I;
            if (component != null )
           {
              list.Add(component);
           }
        }
         return list;
    }


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //Definedin the common base class for all mono behaviours
    publicI GetInterfaceComponent<I>() where I : class
    {
        return GetComponent( typeof (I)) as I;
    }
    publicstatic List<I> FindObjectsOfInterface<I>() where I : class
    {
        MonoBehaviour[] monoBehaviours =FindObjectsOfType<MonoBehaviour>();
        List<I> list = new List<I>();
         foreach(MonoBehaviour behaviour inmonoBehaviours)
        {
           I component =behaviour.GetComponent( typeof (I)) as I;
            if (component != null )
           {
              list.Add(component);
           }
        }
         return list;
    }


    24.使用拓展组件使语法更加方便,例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public staticclass CSTransform
    {
        public static voidSetX( this Transform transform, float x)
        {
           Vector3 newPosition =
              new Vector3(x, transform.position.y,transform.position.z);
        
           transform.position = newPosition;
        }
        ...
    }


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public staticclass CSTransform
    {
        public static voidSetX( this Transform transform, float x)
        {
           Vector3 newPosition =
              new Vector3(x, transform.position.y,transform.position.z);
        
           transform.position = newPosition;
        }
        ...
    }


    25.使用备用的GetComponent以供选择。有时候强制组件依赖关系令人头疼(通过RequiredComponent)。例如:这使得在检查器中难以改变组件(即使它们是相同的基本类型)。作为替代品,当一个组件需要输出一条没有被发现的错误信息时,下面的GameObject拓展就可以使用了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    publicstatic T GetSafeComponent<T>( this GameObject obj) where T : MonoBehaviour
    {
        T component = obj.GetComponent<T>();
        
        if (component == null )
        {
           Debug.LogError( "Expected to findcomponent of type "
              + typeof (T) + " but foundnone" , obj);
        }
        
        return component;
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    publicstatic T GetSafeComponent<T>( this GameObject obj) where T : MonoBehaviour
    {
        T component = obj.GetComponent<T>();
        
        if (component == null )
        {
           Debug.LogError( "Expected to findcomponent of type "
              + typeof (T) + " but foundnone" , obj);
        }
        
        return component;
    }


    原文链接:http://devmag.org.za/2012/07/12/50-tips-for-working-with-unity-best-practices/

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值