Last active
September 13, 2024 15:31
-
-
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/)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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