最佳实践|对象放置和物理效果

发表于2018-01-31
评论0 1.2k浏览
在Spotlight团队中,我们和优秀的Unity开发人员一起合作,深度挖掘Unity在游戏开发中的潜力。针对复杂图形、性能和设计方面的问题,我们看到了各种具有创新性的优秀解决方案。我们也看到了同样的问题与解决方案不断的反复出现。

最佳实践系列文章将探讨我们在与客户合作时遇到的一些常见的问题。这些都是我们的合作团队辛苦得出的经验和教训,我们很自豪能够和大家分享他们的智慧。



这其中的很多问题只有在真正制作主机游戏、手机游戏、或者处理巨量游戏内容时才会出现。如果能在开发早期就将这些问题考虑进去,那么开发过程就会更轻松,而游戏也会更炫酷。

 

将对象放置到理想位置

尽管摆放对象是在Unity中最为常见的任务之一,我们还是看到不少团队为了如何能更好地完成这项工作而苦恼。要把对象排列完美,不让角色跑出世界,不让寻路出现障碍,并让各个物理对象能够放置在理想的位置,这的确不是件轻松的事。

 

 

Unity提供了一些工具来方便处理这个问题,这些工具各自扮演着不同的角色。尤其是在我们想要制作大量重复使用的内容,或是制作一张放置了大量动态物理对象的场景,了解这些工具的使用方法则尤为重要。

 

使用正确的辅助工具

Unity中提供了变换、旋转和缩放工具,在Unity 2017.3中还加入了能同时拥有这三个功能的通用工具。这些小工具能在3D环境下修改游戏对象的变换属性。除此之外,还有2D环境下处理对象的矩形变换工具(Rect Transform)。

 

所有这些辅助工具的运作方式都与其所处的工作模式直接相关。如果将模式设为中心(Center),辅助工具将使用根据对象边界计算得出的对象中心点。这个计算结果是个近似值,所以在处理复杂对象时或许会感觉该中心点偏移了真正的中心点。

 

如果将辅助工具模式设为轴心(Pivot),辅助工具将使用对象自身引用框的原点(0,0,0)。这个轴心点是由创建该资源的作者在制作时设定的。通常需要将对象按照彼此相对的位置进行摆放时,我们推荐使用轴心模式,而且在制作资源时要留意轴心点的位置。

 

第二个切换按钮,全局/局部(Global / Local)切换按钮,确定了操作对象时所处的空间。设为全局(Global)意味着辅助工具将会对齐世界(x,y,z)坐标轴,而设为局部(Local)将会对齐对象自身的(x,y,z)坐标轴。通常我们需要在局部空间下处理对象。但如果我们想要让多个对象互相对齐,则会切换到全局坐标系,并使用网格对齐工具来排列对象。


吸附对象

在Unity中,最为直接的定位工具是按下CTRL键(Windows系统)或是Command键(OSX系统)来吸附网格。在我们通过坐标轴手柄(也就是分别为蓝、红、绿的坐标轴辅助工具)来移动对象时,按下吸附键会让对象根据当前网格大小计算的增量而移动,这个增量可以自行修改。

 

 

自动吸附设置(Snap Settings)菜单能为网格大小设置每个轴的数值,让我们控制旋转角度和缩放大小的吸附,还能将所选对象的当前变换属性近似取其最近的网格线。

 

吸附所有坐标轴(Snap All Axes)按钮在排列任意对象时十分有用。我们可以选择一组对象,将它们在坐标轴上吸附到网格,然后重新定位,这样就能把他们排列好了。效果如下图所示:

 

 

这个方法在处理规则几何体、大型关卡布局和设置碰撞块时十分管用。如果我们需要处理更为复杂的放置情况,则需要下面这些工具。

 

顶点吸附

在使用变换工具时按住V,变换辅助工具会切换为顶点吸附模式,显示为一个小方框。然后点击所选对象网格上的一个顶点,将其拖到光标下的任意顶点上。在处理任意网格的对齐时,尤其是要处理那些复杂的或是不存在碰撞的网格对齐时,这个方法很实用。

 

 

在上图中,不管是电线还是台灯都没有紧凑的碰撞网格,而且它们也都不是规则的立方体,所以它们不能用网格吸附或碰撞吸附处理。

 

如果需要这样放置多个游戏对象,我们也可以将顶点吸附模式保持在打开状态。按下Ctrl+Shift+V打开,按下Ctrl+V关闭。这样便不需要一直按住V就能顶点吸附多个对象了。


碰撞吸附

 按下CTRL + SHIFT可以启用碰撞吸附。启用后,必须拖拽变换辅助工具中心的黄色小方框,而不是使用坐标轴控制工具。这样会让所选对象与光标下对象的碰撞体相对齐。

 


用顶点吸附或网格吸附来对齐网格在处理静态几何体时十分出色,但如果要处理动态对象,比如说互相穿透并在触碰后发射到空中的对象时,应该吸附到碰撞体而不是可见网格。

 

碰撞安置

如果有大量动态物理对象,或是十分复杂的关节系统,利用碰撞吸附来放置对象也许就不太合适。幸运的是,现在Unity编辑器中可以运行PhysX。它能模拟几秒的游戏时间,完成物理效果,然后保存完成时对象的位置信息。

 

 

下方是示例代码,代码中不仅有大量注释,还包含了调试信息绘制和一个菜单项。只要将其放入项目中的任何一个Editor文件夹里,便能看到一个新的GameMenu->Settle Physics菜单项。


示例代码:PhysicsSettler.cs

using UnityEngine;

using UnityEditor;


[InitializeOnLoad]

class PhysicsSettler

    static bool registered = false;

    static bool active = false;

    static Rigidbody[] workList;

    static bool cachedAutoSimulation;

    const float timeToSettle = 10f;

    static float activeTime = 0f;


    static PhysicsSettler()

    {

        if (!registered)

        {

            EditorApplication.update += Update;

            SceneView.onSceneGUIDelegate += OnSceneGUI;

            registered = true;

        }

    }


    [MenuItem("GameMenu/Settle Physics")]

    static void Activate()

    {

        if( !active )

        {

            active = true;

            workList = Object.FindObjectsOfType<Rigidbody>();

            cachedAutoSimulation = Physics.autoSimulation;

            activeTime = 0f;


            foreach( Rigidbody body in workList )

            {

                body.WakeUp();

            }

        }

    }


    [MenuItem("GameMenu/Settle Physics", true)]

    static bool checkMenu()

    {

        return !active;

    }


    static void Update()

    {

        if( active )

        {

            activeTime += Time.deltaTime; 

            Physics.autoSimulation = false;

            bool allSleeping = true;

            foreach( Rigidbody body in workList )

            {

                if( body != null )

                {

                    allSleeping &= body.IsSleeping();

                }

            }


            if( allSleeping || activeTime >= timeToSettle)

            {

                Physics.autoSimulation = cachedAutoSimulation;

                active = false;

            }

            else

            {

                Physics.Simulate(Time.deltaTime);

            }

        }

    }


    static void OnSceneGUI(SceneView sceneView) 

    {

        if( active )

        {

            Handles.BeginGUI();

            Color cacheColor = GUI.color;

            GUI.color = Color.red;

            GUILayout.Label("Simulating Physics.", GUI.skin.box, GUILayout.Width(200));

            GUILayout.Label(string.Format("Time Remaining: {0:F2}",(timeToSettle - activeTime)), GUI.skin.box, GUILayout.Width(200));

            Handles.EndGUI();


            foreach( Rigidbody body in workList )

            {

                if( body != null )

                {

                    bool isSleeping = body.IsSleeping();

                    if( !isSleeping )

                    {

                        GUI.color = Color.green;

                        Handles.Label(body.transform.position, "SIMULATING");

                    }

                }

            }

            GUI.color = cacheColor;

        }

    }


 

当了解了何时和为何使用网格、顶点吸附、碰撞吸附时,我们将在关卡设计工作流程获得更好的开发体验。推荐开发者们多多尝试这些工具,例如:对比轴心(Pivot)和中心(Center)模式的区别,或是比较全局(Global)和局部(Local)模式的不同,这样就可以知道什么模式更为适合。花费在布置虚拟对象的时间越少,那么在开发时得到的乐趣就越多。

 

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论