Reputation: 2250
I am trying to create a simple ObjectMover
class to move objects within a base (think Clash of Clans base editing).
The issue I am having is that when an object is selected using a RayCast
it jumps to the RayCast hit.point
as the collider
for the object can be hit on the edge and will then move to be centered on the hit.point
.
I have tried using an offset and am sure it is something trivial but having a brain fart and not able to find the solution.
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;
}
}
}
}
What am I not seeing?
Upvotes: 3
Views: 1592
Reputation: 20249
One problem is that your difference is between the ray's collision with the surface of the building and the ground, and you're adding it to the ground to get the origin of the transform.
Instead, set the offset based on where the player's ray hits the ground:
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;
}
}
}
If your ground isn't a plane, you can use another Physics.Raycast
to get the 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;
}
}
}
}
Either way, then you can set the position based on the offset:
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;
}
Upvotes: 1