使用带有偏移量的Raycast命中点移动对象

时间:2019-06-11 17:03:08

标签: c# unity3d raycasting

我正在尝试创建一个简单的ObjectMover类来在基础内移动对象(想想《部落冲突》的基础编辑)。

我遇到的问题是,使用RayCast选择一个对象时,它会跳到RayCast hit.point,因为该对象的collider可能会碰到边缘,并且会然后移动到hit.point的中心。

我已经尝试过使用偏移量,并且可以肯定这是微不足道的,但是脑子屁,却找不到解决方法。

ObjectMover.cs

    using UnityEngine;
    using System.Collections;
    public class ObjectMover : MonoBehaviour
    {
#pragma warning disable 0649
        [SerializeField] private GameObject _tmpObjectToMove;
        [SerializeField] private LayerMask _groundLayerMask;
        [SerializeField] private LayerMask _objectsLayerMask;
#pragma warning restore 0649

        private Camera _cam;
        private GameObject _movableObject;
        private bool _objectIsSelected;
        private Vector3 _objectSelectionOffset;

        private void Awake()
        {
            _cam = Camera.main;
        }

        private void Start()
        {
            //TMP call and object instantiation for testing purposes
            GameObject obj = Instantiate(_tmpObjectToMove, Vector3.zero, Quaternion.identity);
            MakeObjectMoveable(obj);
        }

        private IEnumerator UpdatePosition()
        {
            while (_movableObject != null)
            {
                if (Input.GetButtonDown("Fire1"))
                {
                    TestObjectSelection();
                }
                else if (Input.GetButtonUp("Fire1"))
                {
                    if (_objectIsSelected)
                    {
                        _objectIsSelected = false;
                    }
                }

                if (_objectIsSelected)
                {
                    _movableObject.transform.position = GetNewPosition();
                }

                yield return null;
            }
        }

        public void MakeObjectMoveable(GameObject objectToMakeMovable)
        {
            _movableObject = objectToMakeMovable;
            StartCoroutine(UpdatePosition());
        }

        private Vector3 GetNewPosition()
        {
            if (_movableObject != null)
            {
                Ray ray = _cam.ScreenPointToRay(Input.mousePosition);
                if (Physics.Raycast(ray.origin, ray.direction, out RaycastHit hitInfo, 200f, _groundLayerMask, QueryTriggerInteraction.Ignore))
                {
                    Vector3 pos = hitInfo.point - _objectSelectionOffset;
                    return new Vector3(pos.x, 0f, pos.z);
                }
            }
            return _movableObject.transform.position;
        }

        private void TestObjectSelection()
        {
            Ray ray = _cam.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray.origin, ray.direction, out RaycastHit hitInfo, 200f, _objectsLayerMask, QueryTriggerInteraction.Ignore))
            {
                if (hitInfo.transform.gameObject == _movableObject)
                {
                    Vector3 difference = hitInfo.point - _movableObject.transform.position;
                    _objectSelectionOffset = new Vector3(difference.x, 0f, difference.z);
                    _objectIsSelected = true;
                }
            }
        }
    }

如果有人能告诉我我没有看到和/或想到的内容,将不胜感激。

1 个答案:

答案 0 :(得分:1)

一个问题是,您的区别在于射线与建筑物表面和地面的碰撞之间的差异,并且将其添加到地面以获取变换的原点。

相反,请根据玩家的光线撞击地面的位置设置偏移量:

    private void TestObjectSelection()
    {
        Ray ray = _cam.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray.origin, ray.direction, out RaycastHit hitInfo, 200f, _objectsLayerMask, QueryTriggerInteraction.Ignore))
        {
            if (hitInfo.transform.gameObject == _movableObject)
            {
                Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
                float groundDistance;
                groundPlane.Raycast(ray, out groundDistance);

                Vector3 groundPoint = ray.GetPoint(groundDistance);

                // groundPoint is guaranteed y=0, so _objectSelectionOffset y=0;
                _objectSelectionOffset = _movableObject.transform.position - groundPoint;

                _objectIsSelected = true;
            }
        }
    }

如果您的地面不是飞机,则可以使用另一个Physics.Raycast来获取groundPoint

    private void TestObjectSelection()
    {
        Ray ray = _cam.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray.origin, ray.direction, out RaycastHit hitInfo, 200f, _objectsLayerMask, QueryTriggerInteraction.Ignore))
        {
            if (hitInfo.transform.gameObject == _movableObject)
            {
                if (Physics.Raycast(ray.origin, ray.direction, out RaycastHit hitInfo, 200f, _groundLayerMask, QueryTriggerInteraction.Ignore)) 
                {
                    _objectSelectionOffset = _movableObject.transform.position - hitInfo.point;

                    _objectIsSelected = true;
                }
            }
        }
    }

无论哪种方式,您都可以基于偏移量设置位置:

    private Vector3 GetNewPosition()
    {
        if (_movableObject != null)
        {
            Ray ray = _cam.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray.origin, ray.direction, out RaycastHit hitInfo, 200f, _groundLayerMask, QueryTriggerInteraction.Ignore))
            {
                return hitInfo.point + _objectSelectionOffset;
            }
        }
        return _movableObject.transform.position;
    }