简体   繁体   中英

How to accurately get the objects that the camera is directly pointed to?

I'm trying to pick up objects on my game. so the object that the user is directly looking at can be picked up on a button click. I have LookObject which is a gameObejct that stores the objects that I'm currently looking at. The issue I'm facing here is that the LookObject is NOT accurately showing the objects that I'm looking at. please see this image.

as seen in this image the reticle is not exactly on the cube, yet the look object is showing the cube. How can I make it accurate?

 [Header("InteractableInfo")]
public float sphereCastRadius = 0.5f;
public int interactableLayerIndex;
private Vector3 raycastPos;
public GameObject lookObject; 
private PhysicsObjects physicsObject;
private Camera mainCamera;
public GameObject winUI;
private InteractiveObjects interactiveObjects;
  void Update()
    {
        //Here we check if we're currently looking at an interactable object
        raycastPos = mainCamera.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2, 0));
        RaycastHit hit;
        if (Physics.SphereCast(raycastPos, sphereCastRadius, mainCamera.transform.forward, out hit, maxDistance, 1 << interactableLayerIndex))
        {

            lookObject = hit.collider.transform.gameObject;
           
        }
        Physics.Raycast(mainCamera.transform.position, mainCamera.transform.forward, out hit, maxDistance);
        if (hit.transform)
        {
            interactiveObjects = hit.transform.GetComponent<InteractiveObjects>();
        }
        else
        {
            lookObject = null;
            interactiveObjects = null;

        }
        //if we press the button of choice
        if (Input.GetKeyDown(KeyCode.Space))
        {
            //and we're not holding anything
            if (currentlyPickedUpObject == null)
            {
                //and we are looking an interactable object
                if (lookObject != null )
                {
                    PickUpObject();     }
}
}
}

Here is the cube inspector

The cube and all other interactable objects have this script:

public class PhysicsObjects : MonoBehaviour
{
    public float waitOnPickup = 0.2f;
    public float breakForce = 35f;
    [HideInInspector] public bool pickedUp = false;
    [HideInInspector] public PlayerInteractions playerInteractions;


    private void OnCollisionEnter(Collision collision)
    {
        if (pickedUp)
        {
            if (collision.relativeVelocity.magnitude > breakForce)
            {
                playerInteractions.BreakConnection();
            }

        }
    }

    //this is used to prevent the connection from breaking when you just picked up the object as it sometimes fires a collision with the ground or whatever it is touching
    public IEnumerator PickUp()
    {
        yield return new WaitForSecondsRealtime(waitOnPickup);
        pickedUp = true;

    }
}

You are doing

if (Physics.SphereCast(raycastPos, sphereCastRadius, mainCamera.transform.forward, out hit, maxDistance, 1 << interactableLayerIndex))
{
    lookObject = hit.collider.transform.gameObject; 
}

with a radius of 0.5 meters!

Then next you do

 Physics.Raycast(mainCamera.transform.position, mainCamera.transform.forward, out hit, maxDistance);

without any layer filter so you hit eg the floor.

So the check

if (hit.transform)
{
    interactiveObjects = hit.transform.GetComponent<InteractiveObjects>();
}
else
{
    lookObject = null;
    interactiveObjects = null;
}

enters the if case, even then when the hit.transform of the second cast is not he same as the first one.


I don't really understand why you do two casts in the first place. You probably rather want to simply stick with

if (Physics.SphereCast(mainCamera.ViewportPointToRay(Vector2.one * 0.5f), sphereCastRadius, mainCamera.transform.forward, out var hit, maxDistance, 1 << interactableLayerIndex))
// or if you don't want to use a sphere cast anyway
//if(Physics.Raycast(mainCamera.ViewportPointToRay(Vector2.one * 0.5f), maxDistance, 1 << interactableLayerIndex))
{
    var lookObject = hit.collider.gameObject;
       
    if(lookObject.TryGetComponent<InteractiveObjects>(out var interactiveObject))
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            //and we're not holding anything
            if (!currentlyPickedUpObject)
            {
                // whatever happens in here
                PickUpObject(interactiveObject);     
            }
        }
    }
}

and simply use a smaller radius for the sphere cast or as commented use the simple RayCast directly if you don't want to use a radius anyway.


in general I would also recommend to rather use a

public LayerMask interactableLayers;

and then directly use interactableLayers instead of 1 << interactableLayerIndex .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM