Unity 模仿Unreal快速放置物体快捷键工具

布置场景的时候,经常会遇到要把物体A放置在物体B表面的情况。
在Unity里面调整起来很麻烦,各种切换视角,调整参数。
而Unreal里面这种情况只需要按下End键,就可以将A完美的放置在B上,所以想在Unity里面也弄一个同样的功能。

想把这个宝剑放到地上
快捷放置之后
快捷放置的实现方法如下:

	[MenuItem("Wonderland6627/PlaceTools/PlaceBox")]
    public static void PlaceBoxOnFloor()
    {
        Transform transform = Selection.activeTransform;
        if (transform == null)
        {
            Debug.LogError("NoSelectionTransform");
            
            return;
        }

        bool isCollider = true;//是否自带碰撞器,有的话不需要移除
        BoxCollider collider = transform.GetComponent<BoxCollider>();
        if (collider == null)
        {
            isCollider = false;
            collider = transform.gameObject.AddComponent<BoxCollider>();
        }

        Ray ray = new Ray(transform.position, Vector3.down);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit))
        {
            if (hit.collider)//向下的射线所遇到的物体有碰撞器
            {
                Debug.Log("On the " + hit.collider.name);
                if (transform.localScale.y == 1)//物体y轴上没有缩放,碰撞器的高度数据准确
                {
                    float offsetY = collider.size.y / 2;
                    float targetPosY = hit.point.y + offsetY;
                    Vector3 offsetVec = new Vector3(transform.localPosition.x, targetPosY, transform.localPosition.z);
                    transform.localPosition = offsetVec;
                }
                else
                {
                    Debug.LogError("Y!=1");
                }
            }
            else
            {
                Debug.LogError("No Plane");
            }
        }

        if (!isCollider)
        {
            Object.DestroyImmediate(collider);
        }
    }

原理很笨,利用了射线检测,对A下方发射射线,计算自己应该落到的坐标。
大多情况下,Unity都能给物体计算出完美的BoxCollider,但这种方法也只能应付简单的环境。
以后毕业有空了再对复杂的环境多做一些处理。O(∩_∩)O

	[MenuItem("Wonderland6627/PlaceTools/PlaceObject")]
    public static void PlaceObjectOnFloor()
    {
        Transform transform = Selection.activeTransform;
        if (transform == null)
        {
            Debug.LogError("NoSelectionTransform");
            
            return;
        }

        MeshFilter[] meshFilters = transform.GetComponentsInChildren<MeshFilter>();
        SkinnedMeshRenderer[] skinnedMeshRenderers = transform.GetComponentsInChildren<SkinnedMeshRenderer>();
        List<Vector3> modelVerticesList = new List<Vector3>();

        if (meshFilters.Length + skinnedMeshRenderers.Length == 0)
        {
            Debug.LogError("No MeshFilter or SkinnedMeshRenderer");
            
            return;
        }

        //找到y最小的部件
        List<Transform> partsList = new List<Transform>();
        for (int i = 0; i < meshFilters.Length; i++)
        {
            partsList.Add(meshFilters[i].transform);
        }
        for (int i = 0; i < skinnedMeshRenderers.Length; i++)
        {
            partsList.Add(skinnedMeshRenderers[i].transform);
        }

        //只找位置最低的这个
        Transform lowestGO = null;
        PlaceToolsHelper.FindLowest(partsList, ref lowestGO);
        if (lowestGO == null)
        {
            Debug.LogError("No lowest");
            
            return;
        }

        Object part = lowestGO;
        Mesh partMesh = null;

        if (lowestGO.GetComponent<MeshFilter>())
        {
            part = lowestGO.GetComponent<MeshFilter>();
            partMesh = (part as MeshFilter).sharedMesh;
        }
        else if (lowestGO.GetComponent<SkinnedMeshRenderer>())
        {
            part = lowestGO.GetComponent<SkinnedMeshRenderer>();
            partMesh = (part as SkinnedMeshRenderer).sharedMesh;
        }
        if (partMesh == null)
        {
            Debug.LogError("No partMesh");
            
            return;
        }

        var modelVertices = partMesh.vertices;
        for (int i = 0; i < modelVertices.Length; i++)
        {
            if (!modelVerticesList.Exists(vec => vec == modelVertices[i]))
            {
                modelVerticesList.Add(modelVertices[i] + lowestGO.position);
            }
        }

        /*//全找
        List<Mesh> modelMeshesList = new List<Mesh>();//mesh的vertices可能是相对自身center的
        for (int i = 0; i < meshFilters.Length; i++)
        {
            modelMeshesList.Add(meshFilters[i].sharedMesh);
        }

        for (int i = 0; i < skinnedMeshRenderers.Length; i++)
        {
            modelMeshesList.Add(skinnedMeshRenderers[i].sharedMesh);
        }

        for (int i = 0; i < modelMeshesList.Count; i++)
        {
            var vs = modelMeshesList[i].vertices;
            for (int j = 0; j < vs.Length; j++)
            {
                if (!modelVerticesList.Exists(vec => vec == vs[j]))
                {
                    modelVerticesList.Add(vs[j]);
                } 
            }
        }*/

        Vector3 lowestVec = default(Vector3);
        PlaceToolsHelper.FindLowest(modelVerticesList, ref lowestVec);

        Ray ray = new Ray(lowestVec, Vector3.down);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit))
        {
            if (hit.collider)//向下的射线所遇到的物体有碰撞器
            {
                Debug.Log("On the " + hit.collider.name);
                Vector3 centerOffsetVec = lowestGO.root.position - lowestVec;
                float offsetY = Mathf.Abs(lowestVec.y - hit.point.y);
                float targetPosY = lowestGO.position.y - offsetY;
                Vector3 offsetVec = new Vector3(transform.localPosition.x, targetPosY, transform.localPosition.z) + centerOffsetVec;
                transform.localPosition = offsetVec;
            }
            else
            {
                Debug.LogError("No Plane");
            }
        }
    }

PlaceToolsHelper用来辅助找到网格里面最下面的点

public class PlaceToolsHelper
{
    public static void FindLowest(List<Transform> transformList, ref Transform lowest)
    {
        if (transformList.Count == 0)
        {
            Debug.LogError("Empty List");
            return;
        }

        lowest = transformList[0];
        for (int i = 0; i < transformList.Count; i++)
        {
            if (transformList[i].position.y < lowest.position.y)
            {
                lowest = transformList[i];
            }
        }
    }

    public static void FindLowest(List<Vector3> vectorList, ref Vector3 lowest)
    {
        if (vectorList.Count == 0)
        {
            Debug.LogError("Empty List");
            return;
        }

        lowest = vectorList[0];
        for (int i = 0; i < vectorList.Count; i++)
        {
            if (vectorList[i].y < lowest.y)
            {
                lowest = vectorList[i];
            }
        }
    }
}

最后在EditorWindow的OnGUI方法中监听End按键,就可以实现和Unreal放置物体一样的效果了。

		Event e = Event.current;
        if (e.keyCode == KeyCode.End)
        {
            if (e.type == EventType.KeyDown)
            {
                if (!isEndDown)
                {
                    isEndDown = true;
                    PlaceTools.PlaceObjectOnFloor();
                }
            }
            else if (e.type == EventType.KeyUp)
            {
                isEndDown = false;
            }
        }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值