Dropping items on water

I followed the code created by Sam for getting a random drop location, with some modifications, that are explained here.

In my case, I changed the ClickablePickUp as follow:

        private void OnTriggerEnter(Collider other)
        {
            if (other.CompareTag("Player") && pickup.CanBePickedUp() && isAvailable)
                PickUpItem();
        }

        public bool HandleRaycast(PlayerController callingController)
        {
            if (Vector3.Distance(transform.position, callingController.transform.position) > maxDistanceToPickUp)
                return false;

            if (Input.GetMouseButtonDown(0))
                PickUpItem();
            return true;
        }

        private void PickUpItem()
        {
            isAvailable = false;
            pickup.PickupItem();
        }

This means that if the player is not on a maxDistanceToPickUp, I will not allow him to click on the pickup and get it automatically. If you want the pick up that is on the other side of the river, you need to go there! And I made all Clickable Pickups to work with the “run over” behaviour too, using the OnTriggerEnter.

Because of that, I needed to have a small change in the RandomDropper.GetDropLocation method: I can’t make it drop the item on the transform.position, as this would make the OnTriggerEnter fire and the player get the pickup again. I needed to add a check about the distance to see that.

Because of this check, most of the times it will not accept the attemps to create a random position.

So, my idea was: if none of the NavMesh.SamplePosition worked, I can “drop” the item in front of the player, using the scatter distance (that in my case is higher).

But this generates a case where the we would drop the item “on the water”. To avoid that, I made a new check: is the position “reachable”? In case it is not, I am dropping the item “behind” the player.

This made my GetDropLocation looks like this:

        protected override Vector3 GetDropLocation()
        {
            for (var i = 0; i < ATTEMPTS; i++)
            {
                var randomPoint = transform.position + Random.insideUnitSphere * scatterDistance;
                print($"{i}: {randomPoint}");
                if (!NavMesh.SamplePosition(randomPoint, out var hit, 0.1f, NavMesh.AllAreas) ||
                    Vector3.Distance(transform.position, randomPoint) < scatterDistance / 2)
                    continue;

                return hit.position;
            }

            var inFront = transform.position + transform.forward * scatterDistance;
            var path = new NavMeshPath();
            var isReachable = NavMesh.CalculatePath(transform.position, inFront, NavMesh.AllAreas, path)
                && path.status == NavMeshPathStatus.PathComplete;
            if (isReachable)
                return inFront;

            return transform.position - transform.forward * scatterDistance;
        }

I just would like to hear some opinions if there is any way better to do that. Any comment is highly appreciated :slight_smile:

The fact that it is not generating a “valid” random position is something that is making me annoyed…

And there is the problem that if the player is near a “mountain”, it can drop the item “inside” the mountain… :confounded:

This is the pitfall of the Runover Pickup with the RandomDropper.

An alternate consideration is to keep it as a ClickablePickup, but make picking it up an IAction with a new component on your Player

public class ItemCollector: MonoBehaviour, ISaveable
{
    [SerializeField] float pickupRange = 2.0f;

    Pickup target;

    public void Cancel() => target=null;

    public void CollectItem(Pickup pickup)
    {
         GetComponent<ActionScheduler>().StartMoveAction(this);
         target=pickup;
     }
     
     void Update()
     { 
            if(target==null) return;
            if(Vector3.Distance(transform.position, target.transform.position)>pickupRange) 
            {
                  GetComponent<Mover>().MoveTo(target.transform.position, 1);
            }  else
            {
                  target.PickupItem();
                  target=null;
            }
       }
}

That seems a good idea. So, instead of having the “run over” behaviour, players would still need to click on the pick up, but different from what is happening today, they will move to the location to get the item. Which means that I can drop the item on their position if the other cases does not work.

I am doing the Dialogue course now, but I will implement this, test and bring the results.

Thanks for the help!

I was checking here and my current code is doing exactly it in case I disable the trigger to the box colider.

        public bool HandleRaycast(PlayerController callingController)
        {
            if (Vector3.Distance(transform.position, callingController.transform.position) > maxDistanceToPickUp)
                return false;

            if (Input.GetMouseButtonDown(0))
                PickUpItem();
            return true;
        }

If the distance is between the player and the pickup is greater than the maxDistanceToPickUp, then it will return saying that it can’t handle the raycast. This means that the PlayerController will do the movement to it, until it is close enough to handle it as a pickup.

Anyway, thanks for the help. I will let without the run over behaviour.

Privacy & Terms