c# - 使用带偏移的 Raycast 命中点移动对象

标签 c# unity3d raycasting

我正在尝试创建一个简单的 ObjectMover 类来在基地内移动对象(想想 Clash of Clans 基地编辑)。

我遇到的问题是,当使用 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;
                }
            }
        }
    }

我没看到什么?

最佳答案

一个问题是您的区别在于光线与建筑物表面和地面的碰撞,而您将其添加到地面以获得变换的原点。

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

    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/

相关文章:

c# - 如何从用户定义的类创建列表

c# - 与 Crystal Reports 中的公式相关的问题

c# - 如何保存 bool 变量的播放器偏好设置?

c# - 通过一个排序变量对各种对象的数据结构进行排序的最佳方法?

c# - 使用 asp.net 编写将 excel 文件导入数据库的代码的问题

unity3d - Unity Vertical Layout Group 高度不计算子项

c# - unity NavMeshPath 改变和重新分配

three.js - 如何在 Three.js 中获取某个位置半径内的其他 3D 对象

geometry - 射线-正方形相交 3D

THREE.js 识别对一个几何体的点击(合并几何体)