# Picking up items and references

Just a heads up, I'm sure there are much more efficient ways to do what I'm trying to do. I've even thought of some myself. However, at the moment, I'm mostly interested in getting this code to work. I think I understand exactly why it doesn't work, but I'm not sure how to fix it.

Firstly, there is an ItemPickupTrigger script attached to the drop prefab. The purpose of this script is simply to modify a variable's truth value depending on whether or not the player is in range. This script must be attached to the drop prefab because it uses OnTriggerEnter/OnTriggerExit. No problem here.

Secondly, there is a pretty standard EnemyDrops script.

public class EnemyDrops : MonoBehaviour {

private EnemyHealth enemyHealth;

public GameObject dropPrefab;

private float randomNumber;

public bool objectDropped;
private bool numberGenerated;

public GameObject drop;

// This is the correct offset for enemies' current y-position of 1.083333
private Vector3 dropPositionOffset = new Vector3(0f, -0.958333f, 0f);

void Start()
{
enemyHealth = GetComponent<EnemyHealth>();
}

void Update()
{
if (enemyHealth.isDead && !objectDropped && !numberGenerated) {

randomNumber = Random.value;
numberGenerated = true;

if (randomNumber <= 1.0)
{
DropObject();
}
}
}

private void DropObject()
{
objectDropped = true;

drop = GameObject.Instantiate(dropPrefab, transform.position + dropPositionOffset, Quaternion.identity);
}
}


It drops the object where the enemy has been killed. This script is attached to each enemy. As it's currently set up for testing purposes, an item will always drop. I'm sure there are better ways to do this, but it works, so no problem here.

Finally, there is an ItemPickup script attached to an ItemController object which is otherwise pretty empty. I'm not even really sure why I decided to do it this way, I just thought that it might be better for things I wanted to do in the foreseeable future to not have this script attached to each enemy. Now get ready because the code below is problematic.

public class ItemPickup : MonoBehaviour {

private GameObject pickupWindow;

private bool windowOpen;

private GameObject drop;
private ItemPickupTrigger itemPickupTrigger;

void Start()
{
pickupWindow = GameObject.Find("Pickup Window");
pickupWindow.SetActive(false);

drop = GameObject.Find("Drop");
itemPickupTrigger = drop.GetComponent<ItemPickupTrigger>();
}

void Update()
{
if (Input.GetKeyDown(KeyCode.R) && itemPickupTrigger.playerInRange && !windowOpen) {
pickupWindow.SetActive(true);

windowOpen = true;
}
else if (Input.GetKeyDown(KeyCode.R) && windowOpen)
{
pickupWindow.SetActive(false);

windowOpen = false;
}
}
}


For starters, it only works if there is a drop object already present when the script begins, but there shouldn't be a drop object then. Second, if I don't set the reference there, I'm not sure how to set itemPickupTrigger's reference, since I'm using it in update and need the reference set before then. Make a function called SetReferences() and call it early in Update()? Yeah, I'm pretty lost. Basically, I want to be able to open the "loot window" near any instantiated drop object in the game. Currently, that functionality only in a limited capacity (either only on a "preexisting" drop object in the scene or on one random instantiated drop object(?) depending on how I have been modifying the code). Help!

it only works if there is a drop object already present when the script begins, but there shouldn't be a drop object then

Well, that's because you find a drop(ped) object during Start() which is when (surprise) the script first starts running:

void Start() {
...
drop = GameObject.Find("Drop");
itemPickupTrigger = drop.GetComponent<ItemPickupTrigger>();
}


So if you want it to look for drops when the player presses a key, you need to move this into the area of code that handles the keypress.

Second, if I don't set the reference there, I'm not sure how to set itemPickupTrigger's reference, since I'm using it in update and need the reference set before then.

How about right before accessing the reference?

void Update()
{
drop = GameObject.Find("Drop");
itemPickupTrigger = drop.GetComponent<ItemPickupTrigger>();
if (Input.GetKeyDown(KeyCode.R) && itemPickupTrigger.playerInRange && !windowOpen) {
pickupWindow.SetActive(true);

windowOpen = true;
}


Of course, this is still going to fail if there are more than one of these in the scene and the player is standing near any of them other than the one that was first added to the scene.

Additionally, GameObject.Find() is slow and should be avoided. Given that you have a trigger for detecting if the player is in range, you should use that function to give a reference to the dropped object to the ItemPickup instance. Something like this:

void OnTriggerEnter(Collider other) {
// some method of determining that yes, the thing that entered
// the trigger volume is in fact the player
if(other.gameObject == ThePlayer) {
other.GetComponent<ItemPickup>().drop = this;
}
}