Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

"Sloppily" distributing encounters

Discussion in 'Scripting' started by Sendatsu_Yoshimitsu, May 25, 2017.

  1. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    I'm working on a game where an "encounter" is the generic class that encapsulates all random events which occur during a session- monsters spawning, loot appearing, all that stuff is accomplished through a child of Encounter that has its own logic. The length of the level determines how many encounters need to be generated, and each category of encounter knows what percentage of the total number of encounters it should occupy, so a horizontal level that's 100m long might end up with 10 encounters: 3 enemies, 2 treasure chests, 2 "blank" encounters that don't do a thing, and a wildcard that randomly selects an encounter type when it spawns.

    Where I'm running into trouble is figuring out how to shuffle all of these encounters together- putting them in a random order will result in weird clumps where the player fights five guys in a row, or spends ten minutes running into treasure after treasure, but shuffling them together evenly will result in an obvious, jarring consistency that the player will notice.

    So is there any simple way to effect a sort of sloppy shuffle, where given n encounters of t type, they are mixed together in a list in such a fashion that they are generally evenly distributed, but not rigidly so?
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    Can you define "generally" vs "rigidly"?

    Like say you want it so that encounters of type treasure chest never occur side by side. Sure, you can set up that algorithm.

    An algorithm is just a set of rules, so define your rules, and your algorithm will come out of that.

    ...

    oh and a nitpick, your example counts to 8, not 10
     
  3. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    Sorry for not being more precise! By "rigidly" I meant what I'm doing now: after having generated some arbitrary number of encounters per the logic I described above, I stick them into a bunch of List<EncounterType>, one per category of encounter, then iterate over them in order, removing the top element of each list and adding it to some master List<BaseEncounter> that represents every encounter which will occur, in the order it will occur in. Since I'm hitting each list once, in order, then repeating, I end up with a mixed but regular encounter order- if there are three lists, blank/monster/treasure, the master list will fill the level with a blank encounter, then a monster, then a treasure, then another blank encounter and so forth until the entire list's contents have spawned.

    By sloppy, what I was trying to articulate was following the same general logic of spacing things out, but fudging the order enough that the player can't consistently predict "Oh, I see a monster, that means I'll get a treasure right after it, then have some breathing room".
     
  4. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    I can think of one way to do it, though it's probably not the most optimized. But that should be fine unless you've got thousands of encounters in one map. First you need "weighted random" selection. Easiest way to do that is something like this:
    Code (csharp):
    1. public class WeightedEncounter {
    2.   public int weight;
    3.   public Encounter encounter;
    4. }
    5.  
    6. public List<WeightedEncounter> encounters;
    At the beginning, you would make a list of all the encounters you want in the level with all the weights to 1, so they're all equally likely to be chosen. To do a weighted random selection you do this:
    Code (csharp):
    1. public Encounter GetRandomWeightedEncounter() {
    2.   var totalWeight = encounters.Sum(e => e.weight);
    3.   var random = Random.Range(0, totalWeight);
    4.   foreach(var encounter in encounters) {
    5.     if(random < encounter.weight) return encounter;
    6.     random -= encounter.weight;
    7.   }
    8. }
    That's a normal weighted random selection, but in your case, you'll want to update the weights each time you select something, so as to increase the weight of everything that isn't whatever you just selected. For example:
    Code (csharp):
    1. public GenerateNextEncounter() {
    2.   var thisEncounter = GetRandomWeightedEncounter();
    3.   encounters.Remove(thisEncounter);
    4.   foreach(var e in encounters) {
    5.     //everything not what you just chose is now three times more likely to be chosen
    6.     if(e.encounterType != thisEncounter.encounterType) e.weight *= 3;
    7.   }
    8.   DoWhateverToSetup(thisEncounter);
    9. }
    Instead of "3" you can tweak that to be whatever you like. Basically the more you pick the same thing, the more likely it will be that you'll pick something different the next time.
     
  5. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    You could make it so whenever something is chosen randomly, it defines a random delay before it can be picked again. This generally is "sloppy round robin".
     
  6. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    Weighted average was the term I was missing, thank you for the extremely thorough explanation! I was going to ask a daft follow-up, but figured it out while writing this, so thank you again!! :)
     
    makeshiftwings likes this.