Перемещение объекта с использованием точки попадания Raycast со смещением - PullRequest
3 голосов
/ 11 июня 2019

Я пытаюсь создать простой класс 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;
                }
            }
        }
    }

Если бы кто-нибудь мог сказать мне, что я не вижу и или что думаю об этом, был бы очень признателен.

1 Ответ

1 голос
/ 11 июня 2019

Одна проблема состоит в том, что ваша разница между столкновением луча с поверхностью здания и землей, и вы добавляете его к земле, чтобы получить источник преобразования.

Вместо этого установите смещение в зависимости от того, где луч игрока падает на землю:

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