Loot code Modification

Hi guys,

I’m an artist making a demo level with some of my assets. I’ve been “programming” (following tutorials) for like two weeks now so I only know enough to make slight modifications to code. The following works, but I’d like to add functionality, which is a little beyond what I’m capable of right now… I don’t expect anyone to write anything for me, but some solid guidance would be greatly appreciated!

First, where would I stick a Random.Range in here? I’d like to enter my entire loot table into the array and have the script choose from the items.

Second, how would I tell it to only drop a specific number (or even better, a random range) of items.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class ItemDropper_Death : MonoBehaviour
{
	public List<GameObject> objectsToDropOnDeath;
	public int offSetWidth = 10;
	
	public void AddItemToDrop(GameObject newItem)
	{
		if (newItem != null)
		{
			if (!objectsToDropOnDeath.Contains(newItem))
			{
				objectsToDropOnDeath.Add(newItem);
			}
		}
	}
	
	public void OnDeath()
	{
		if(objectsToDropOnDeath.Count > 0)
			DropItems();
	}
	
	private void DropItems()
	{
		foreach (GameObject myObject in objectsToDropOnDeath)
		{
			if(myObject != null)
				InstantiateNewObject(myObject);
		}
	}
	
	private void InstantiateNewObject(GameObject objectToDrop)
	{
		Instantiate(objectToDrop, GetPosition(), GetRotation());
	}
	
	private bool offsetLeft = true;
	private Vector3 GetPosition()
	{
		Vector3 newPosition = new Vector3();
		
		int randX = (int)Random.Range((offSetWidth / 1.5f), offSetWidth);
		int negRandX = (int)Random.Range(-offSetWidth, -(offSetWidth / 1.5f));
		
		int xOffset = 0;
		
		if (offsetLeft)
			xOffset = negRandX;
		else
			xOffset = randX;
		
		offsetLeft = !offsetLeft;
		
		newPosition = new Vector3(this.transform.position.x + xOffset, this.transform.position.y, this.transform.position.z);
		
		return newPosition;
	}
	
	private Quaternion GetRotation()
	{
		Quaternion rot = new Quaternion(0.0f, 0.0f, 0.0f, 0.0f);    //rotation of 0, needed for instantiation
		
		return rot;
	}
	
}

I would replace lines 29-33 with something like this:

// Random.Range, when passed two integers, returns a number >= start and < last
// That's confusing, because when you pass two floats, you get >= start and <= last.
// The reason they did that is because most of the time when you pass ints, you want between 0 
// and an object's count as an index, but the max index has to always be object count - 1
int index = Random.Range(0, objectsToDropOnDeath.Count);
GameObject obj = objectsToDropOnDeath[index];
// then instantiate it

To do something like limit the drops, it depends on if you are okay with a specific drop being repeated, i.e. the code I just wrote could be called twice but would potentially give the same game object twice. To avoid that, the pattern I always use is to create a temprary list of potential drops, then run the code above but remove that drop from the list. Then run it again with the smaller list. For instance, to get a random number of drops (between minDrops and maxDrops):

int minDrops = 1;
int maxDrops = 5;
int numDrops = Random.Range(minDrops, maxDrops + 1);  // Remember, Range() with ints is always < last, so add 1 to make it return maxDrops occasionally

// This list will always contain indices into the objectsToDropOnDeath array that have not been used
List<int> dropsLeft = new List<int>();
for (int i=0; i < objectsToDropOnDeath.Count; ++i)
    dropsLeft.Add(i);

// Create drops the random numDrops times
for (int i=0; i < numDrops; ++i) {
    int index = Random.Range(0, dropsLeft.Count);
    GameObject obj = objectsToDropOnDeath[dropsLeft[index]];
    // instantiate etc

    // We used this drop object already, so remove it from the list for next iteration
    dropsLeft.Remove(index);
}

You could go even further and limit just the number of drops for a single game object, like allow up to 10 arrows but never more than 1 bow. I would also implement a probability scale so some items are more likely to drop. There are two ways I know of to do this. One is to add multiple items to the temp list, like you would add the index for an arrow 10 times and index for a bow only once, then you have 1 in 11 chance to drop a bow, etc.

The List class may not support this because it’s Remove() method works by passing in the value that is being stored (what I mean is you aren’t removing index 0, index 1, etc of the temp list. You’re telling it remove object that equals 5 or equals 10, where 5 or 10 is the index you pushed into the array from objectsToDropOnDeath initially.

The other approach is to pick a random num between 1 and 100 floating point and establish buckets for the likelihood of an object to land in a given range, I.e. 1 to 89 is an arrow and 90 to 100 is a bow.

At some point, it becomes such a complex thing it makes more sense to just create a LootableItem class which provides the min loot, max loot times, the probability of looting it, etc.