Skip to content

Instantly share code, notes, and snippets.

@meredoth
Last active September 13, 2024 15:31
Show Gist options
  • Save meredoth/8f9560e1f3dceba52b15b0e2b3be8c52 to your computer and use it in GitHub Desktop.
Save meredoth/8f9560e1f3dceba52b15b0e2b3be8c52 to your computer and use it in GitHub Desktop.
An example of a loot probabilities table based on Unity's Animation and Min Max Curves. Check the explanation post (https://giannisakritidis.com/blog/Animation-And-Min-Max-Curves-In-Unity/)
public class LootProbabilities : MonoBehaviour
{
private const int MAX_LEVEL = 20;
private const float MAX_RARITY = 10;
[SerializeField] private ParticleSystem.MinMaxCurve lootRarityPerLevel;
[SerializeField] private AnimationCurve probabilitiesDistribution;
[ReadOnly, SerializeField] private LevelProbabilities[] probabilitiesPerLevel;
private void OnValidate()
{
lootRarityPerLevel.curveMultiplier = 1f;
CalculateChances();
}
private void Reset()
{
var upperAnimationCurve = AnimationCurve.EaseInOut(0, MAX_RARITY/2, MAX_LEVEL, MAX_RARITY);
var lowerAnimationCurve = AnimationCurve.EaseInOut(0, 0, MAX_LEVEL, MAX_RARITY/2);
probabilitiesDistribution = new AnimationCurve(new Keyframe(0.01f, 0.01f), new Keyframe(1, 1));
lootRarityPerLevel = new ParticleSystem.MinMaxCurve(1, lowerAnimationCurve, upperAnimationCurve)
{ mode = ParticleSystemCurveMode.TwoCurves };
CalculateChances();
}
public int GetRarity(int level, float random) => probabilitiesPerLevel[level].GetRarity(random);
private void CalculateChances()
{
var (minRarityAtLevel,maxRarityAtLevel) = RaritySpreadPerLevel();
probabilitiesPerLevel = new LevelProbabilities[MAX_LEVEL + 1];
for (int level = 0; level <= MAX_LEVEL; level++)
{
probabilitiesPerLevel[level] = new LevelProbabilities(minRarityAtLevel[level], maxRarityAtLevel[level]);
var chancesSum = CalculateChancesSum(minRarityAtLevel[level], maxRarityAtLevel[level]);
for (int rarity = minRarityAtLevel[level]; rarity <= maxRarityAtLevel[level]; rarity++)
{
var chance = probabilitiesDistribution.Evaluate((float)(rarity - minRarityAtLevel[level]) /
(maxRarityAtLevel[level] - minRarityAtLevel[level]));
probabilitiesPerLevel[level].AddChance(rarity, chance / chancesSum);
}
}
return;
(int[],int[]) RaritySpreadPerLevel()
{
var minRarity = new int[MAX_LEVEL + 1];
var maxRarity = new int[MAX_LEVEL + 1];
for (var i = 0; i < MAX_LEVEL + 1; i++)
{
minRarity[i] = (int)lootRarityPerLevel.Evaluate(i, 0f);
maxRarity[i] = (int)lootRarityPerLevel.Evaluate(i, 1f);
}
return (minRarity, maxRarity);
}
float CalculateChancesSum(int minRarity, int maxRarity)
{
float chancesSum = 0;
for (int j = minRarity; j <= maxRarity; j++)
{
var chance = probabilitiesDistribution.Evaluate((float)(j - minRarity) / (maxRarity - minRarity));
chancesSum += chance;
}
return chancesSum;
}
}
[Serializable]
private class LevelProbabilities
{
[ReadOnly, SerializeField] private List<RarityPercentages> rarityPercentages;
internal LevelProbabilities(int minRarity, int maxRarity) => rarityPercentages = new List<RarityPercentages>(maxRarity - minRarity + 1);
internal void AddChance(int rarity, float chance) =>
rarityPercentages.Add(new RarityPercentages
{
rarity = rarity,
chance = (float)Math.Round(Mathf.Clamp(chance * 100f, 0, 100f),2)
});
internal int GetRarity(float percentageChance)
{
var totalChance = 0f;
foreach (var chance in rarityPercentages)
{
totalChance += chance.chance;
if (percentageChance * 100f <= totalChance)
return chance.rarity;
}
Debug.LogException(new ArgumentOutOfRangeException(nameof(percentageChance), " greater than sum of percentages"));
return -1;
}
[Serializable]
private struct RarityPercentages
{
public int rarity;
public float chance;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment