了解加权随机化算法,在游戏中进行概率计算,抽取SSR五星卡牌指日可待 |
您所在的位置:网站首页 › 游戏中的随机算法 › 了解加权随机化算法,在游戏中进行概率计算,抽取SSR五星卡牌指日可待 |
🎬 博客主页:https://xiaoy.blog.csdn.net
🎥 本文由 呆呆敲代码的小Y 原创 🙉
🎄 学习专栏推荐:Unity系统学习专栏
🌲 游戏制作专栏推荐:游戏制作
🌲Unity实战100例专栏推荐:Unity 实战100例 教程
📆 未来很长,值得我们全力奔赴更美好的生活✨
------------------❤️分割线❤️-------------------------
📢 前言🎬 Unity实用插件篇| 游戏中的求概率插件WeightedRandomization一、概率插件介绍二、使用方法介绍三、插件核心代码总结![]() 对于游戏概率的介绍,之前有一篇文章讲过 游戏概率常用模型算法整理,感兴趣的小伙伴可以去看看哦 🎬 Unity实用插件篇| 游戏中的求概率插件WeightedRandomization![]() WeightedRandomization:简单加权随机化算法的实现 加权随机化 是当您想要呈现多个值时,它们之间的几率不同。 例如,考虑值 A、B 和 C。如果您决定需要这 3 个值之一,但您希望 A 出现 20% 的时间,B 40% 和 C 60%,那将是加权随机化。 每个值的几率可能不同,并且增加到 100%。 这些类将为您提供定义和实现您自己的加权随机化的工具。 我自己使用它来为 RPG 中的敌人类型创建模板,并根据模板定义的权重为统计数据分配点数。 简单地使用值类型作为通用参数初始化一个 WeightedRandomizer 实例。 使用您想要的值和您希望该值出现的几率调用 AddWeight。 对于您添加的每个值,几率可以是您想要的 0 到 1 之间的任何值,但在您尝试获取值之前,提供的所有权重的总和必须加起来为 1,以便保证有一个值出现背部。 添加所有权重后,使用 GetNext 方法获取下一个值。 插件本身由简单的几个脚本构成,在上面新加了一个可以配置权重的功能(初始版本只能配置概率,且概率和需为1) ![]() 核心方法如下: AddOrUpdateWeight():负责将概率及概率对象添加进概率池子中。AddOrUpdateWeightInt:负责将权重及权重对象添加进概率池子中。GetNext():从概率池子根据概率返回对应的对象。二、使用方法介绍将插件的 .unitypackage 包导入 项目中。 1.首先针对不同的泛型对象配置好对应的概率(使用列表或者字典配置),或者直接在代码中添加对象及概率都可以。 字典结构: ![]() 列表+结构体: ![]() 2.在程序运行时实例化插件, 代码语言:javascript复制 //根据概率获取的泛型对象。可以是多种类型,如int/float/GameObject/Component/Script/等等 private WeightedRandomizer CurrentObjRandomizer; private void Start() { //实例化插件 CurrentObjRandomizer = new WeightedRandomizer(); }3.遍历配置的概率及概率对象,将其添加到WeightedRandomizer中。 此处也可以直接使用 AddOrUpdateWeight 代码添加概率及概率对象,省去第一步在Unity的监视器面板配置概率的步骤。 不过第一步的好处是可以在面板中可视化修改概率及概率对应的对象,体验更友好一些。 代码语言:javascript复制 //将配置的概率添加到WeightedRandomizer中 foreach (var w in RandomizerList) { CurrentObjRandomizer.AddOrUpdateWeight(w.Go, w.Range); }4.然后在代码中需要使用这个概率的时候调用API:WeightedRandomizer.GetNext()即可从配置的对象中根据概率抽取并返回该对象。 代码语言:javascript复制 GameObject item = CurrentObjRandomizer.GetNext(); Debug.Log("根据概率获取的对象:"+item);![]() 上述方法演示的为配置概率时的操作。 优点:可以直观明了的看到各个对象的概率,简单直观。 缺点:配置的各个概率对象 它们的概率和必须为1,也就是说我们想改动某个对象的获取概率时必须要同时改动另外的概率,否则概率和就不为1了。 若是想改为配置权重,则只需要将上述第三步的 AddOrUpdateWeight() 方法改为 AddOrUpdateWeightInt()方法就好啦。 代码语言:javascript复制 //将配置的权重添加到WeightedRandomizer中 foreach (var w in RandomizerList) { CurrentObjRandomizer.AddOrUpdateWeightInt(w.Go, w.Range); }然后在面板中配置格子对象的权重,记得将Value改为int属性即可。 ![]() 优点是不需要在考虑概率和是否为1的限制,配置权重时可以根据实际情况随心所欲,更改某个权重时,无需同步修改其他权重就可生效。 三、插件核心代码下面具体演示了插件的使用方法,一种是使用ScriptableObject保存我们的概率及概率对象。另一种是直接在类中配置,直接调用。 使用ScriptableObject的好处是我们可以在任何在有需要使用到此概率获取的时候拿到概率对应的SO,直接使用SO的数据获取即可,SO就相当于一个保存数据的载体。 插件下载地址:https://download.csdn.net/download/zhangay1998/87426511 插件使用示例脚本如下: 代码语言:javascript复制using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using WeightedRandomization; #region 使用方法二:用ScriptableObject配置概率相关信息,使用该概率时进行获取 [SerializeField, CreateAssetMenu(fileName = "Weighted", menuName = "Model/WeightedSO"),] public class WeightedSO : ScriptableObject { public enum WeihtedTestEnum { None, Good, Bad, General, } [System.Serializable] public struct GameObjectRandomizerData { public WeihtedTestEnum Go; public float Range; } [Header("添加获取的对象及概率")] public List RandomizerList = new List(); } #endregion public class WeightedRandom : MonoBehaviour { #region 使用方法一:直接在脚本中配置相关概率 [System.Serializable] public struct GameObjectRandomizerData { public GameObject Go; public float Range; } //根据概率获取的泛型对象。可以是多种类型,如int/float/GameObject/Component/Script/等等 private WeightedRandomizer CurrentObjRandomizer; [Header("添加获取的对象及概率")] public List RandomizerList = new List(); #endregion public Button ClickBtn; private void Start() { //1.实例化插件 CurrentObjRandomizer = new WeightedRandomizer(); //2.将配置的概率添加到WeightedRandomizer中 foreach (var w in RandomizerList) { CurrentObjRandomizer.AddOrUpdateWeight(w.Go, w.Range); } //Button按钮点击测试获取对象概率 ClickBtn.onClick.AddListener(()=> { //3.调用GetNext()从配置的概率中获取对象 GameObject item = CurrentObjRandomizer.GetNext(); Debug.Log("获取对象:" + item); }); } }插件核心脚本如下: 代码语言:javascript复制using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; namespace WeightedRandomization { public class WeightedRandomizer { private bool adjusted; private float tempWeightIntSum; private List weights; private List tempWeightIntRecordList; private List tempWeightRecordList; public IRandomizationProvider Provider { get; set; } public WeightedRandomizer() { this.weights = new List(); tempWeightIntRecordList = new List(); tempWeightRecordList = new List(); } public void AddOrUpdateWeight(T value, float weight) { if(weight == 0) throw new ArgumentException("weighted value cannot have a 0% chance."); WeightedChance existing = this.weights.FirstOrDefault(x => Object.Equals(x.Value, value)); if (existing == null) this.weights.Add(new WeightedChance { Value = value, Weight = weight }); else existing.Weight = weight; this.adjusted = false; } public void AddOrUpdateWeightInt(T value, int weightInt) { if (weightInt == 0) throw new ArgumentException("weighted value cannot have a 0% chance."); WeightedChance existing = this.weights.FirstOrDefault(x => Object.Equals(x.Value, value)); if (existing == null) this.weights.Add(new WeightedChance { Value = value, WeightInt = weightInt }); else existing.WeightInt = weightInt; this.adjusted = false; tempWeightIntSum = 0; tempWeightIntRecordList.Clear(); tempWeightRecordList.Clear(); for (int i = 0; i < this.weights.Count; i++) { tempWeightIntRecordList.Add(weights[i].WeightInt); tempWeightIntSum += weights[i].WeightInt; } for (int i = 0; i < tempWeightIntRecordList.Count; i++) { tempWeightRecordList.Add(tempWeightIntRecordList[i] / tempWeightIntSum); } for (int i = 0; i < this.weights.Count; i++) { weights[i].Weight = tempWeightRecordList[i]; } AddOrUpdateWeight(value, weights[weights.Count-1].Weight); } public void RemoveWeight(T value) { WeightedChance existing = this.weights.FirstOrDefault(x => Object.Equals(x.Value, value)); if (existing != null) { this.weights.Remove(existing); this.adjusted = false; } } public void ClearWeights() { this.weights.Clear(); this.adjusted = false; } /// /// Determines the adjusted weights for all items in the collection. This will be called automatically if GetNext is called after there are changes to the weights collection. /// public void CalculateAdjustedWeights() { var sorted = this.weights.OrderBy(x => x.Weight).ToList(); decimal weightSum = 0; for (int i = 0; i < sorted.Count(); i++) { weightSum += (decimal)sorted[i].Weight; if (i == 0) sorted[i].AdjustedWeight = sorted[i].Weight; else sorted[i].AdjustedWeight = sorted[i].Weight + sorted[i - 1].AdjustedWeight; } if (weightSum != 1.0m) throw new InvalidOperationException("The weights of all items must add up to 1.0 "); this.weights = this.weights.OrderBy(x => x.AdjustedWeight).ToList(); this.adjusted = true; } /// /// Return a value based on the weights provided. /// /// public T GetNext() { if (this.Provider == null) this.Provider = UnityRandomizationProvider.Default; if (!adjusted) this.CalculateAdjustedWeights(); double d = this.Provider.NextRandomValue(); var item = this.weights.FirstOrDefault(x => d |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |