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

我遇到的问题是,使用RayCast选择对象时,它会跳到RayCast hit.point,因为该对象的collider可能会被击中边缘,然后将其移动到。

我已经尝试过使用偏移量,并且可以肯定这是一件微不足道的事情,但由于脑部屁屁而无法找到解决方案。

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;
                }
            }
        }
    }


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

最佳答案

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

而是根据玩家的光线撞击地面的位置设置偏移量:

    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;
    }

关于c# - 使用带有偏移量的Raycast命中点移动对象,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56548595/

10-09 18:32