前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >了解加权随机化算法,在游戏中进行概率计算,抽取SSR五星卡牌指日可待

了解加权随机化算法,在游戏中进行概率计算,抽取SSR五星卡牌指日可待

作者头像
呆呆敲代码的小Y
发布于 2023-02-16 06:22:25
发布于 2023-02-16 06:22:25
1.1K00
代码可运行
举报
运行总次数:0
代码可运行


📢 前言
  • 概率 在游戏中可以说是最玄学的东西了,只要涉及到游戏,基本上就跟概率是离不开关系的。
  • 例如游戏中抽卡、开宝箱、抽奖等等玩法,说到底就是使用 概率 在操控。
  • 比如原神中的祈愿,十连出4星,90发小保底,180发大保底都是在原有概率的基础上增加了一些可控的因素让玩家欲罢不能。
  • 游戏中对概率的定义方法也有很多种,主要看开发者想怎样操控这个概率从而使用不同的方法。
  • 本文就来介绍一个插件WeightedRandomization,功能比较单一,非常轻量,专门用来求概率的时候使用。

对于游戏概率的介绍,之前有一篇文章讲过 游戏概率常用模型算法整理,感兴趣的小伙伴可以去看看哦


🎬 Unity实用插件篇| 游戏中的求概率插件WeightedRandomization

一、概率插件介绍

WeightedRandomization:简单加权随机化算法的实现

加权随机化 是当您想要呈现多个值时,它们之间的几率不同。 例如,考虑值 A、B 和 C。如果您决定需要这 3 个值之一,但您希望 A 出现 20% 的时间,B 40% 和 C 60%,那将是加权随机化。 每个值的几率可能不同,并且增加到 100%。 这些类将为您提供定义和实现您自己的加权随机化的工具。 我自己使用它来为 RPG 中的敌人类型创建模板,并根据模板定义的权重为统计数据分配点数。

  简单地使用值类型作为通用参数初始化一个 WeightedRandomizer 实例。 使用您想要的值和您希望该值出现的几率调用 AddWeight。 对于您添加的每个值,几率可以是您想要的 0 到 1 之间的任何值,但在您尝试获取值之前,提供的所有权重的总和必须加起来为 1,以便保证有一个值出现背部。 添加所有权重后,使用 GetNext 方法获取下一个值。

插件本身由简单的几个脚本构成,在上面新加了一个可以配置权重的功能(初始版本只能配置概率,且概率和需为1)

核心方法如下:

  1. AddOrUpdateWeight():负责将概率及概率对象添加进概率池子中。
  2. AddOrUpdateWeightInt:负责将权重及权重对象添加进概率池子中。
  3. GetNext():从概率池子根据概率返回对应的对象。

二、使用方法介绍

将插件的 .unitypackage 包导入 项目中。

1.首先针对不同的泛型对象配置好对应的概率(使用列表或者字典配置),或者直接在代码中添加对象及概率都可以。

字典结构:

列表+结构体:

2.在程序运行时实例化插件,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
   //根据概率获取的泛型对象。可以是多种类型,如int/float/GameObject/Component/Script/等等
    private WeightedRandomizer<GameObject> CurrentObjRandomizer;
    private void Start()
    {
        //实例化插件
        CurrentObjRandomizer = new WeightedRandomizer<GameObject>();
    }

3.遍历配置的概率及概率对象,将其添加到WeightedRandomizer中。

此处也可以直接使用 AddOrUpdateWeight 代码添加概率及概率对象,省去第一步在Unity的监视器面板配置概率的步骤。 不过第一步的好处是可以在面板中可视化修改概率及概率对应的对象,体验更友好一些。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 //将配置的概率添加到WeightedRandomizer中
        foreach (var w in RandomizerList)
        {
            CurrentObjRandomizer.AddOrUpdateWeight(w.Go, w.Range);
        }

4.然后在代码中需要使用这个概率的时候调用APIWeightedRandomizer.GetNext()即可从配置的对象中根据概率抽取并返回该对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  GameObject item = CurrentObjRandomizer.GetNext();
  Debug.Log("根据概率获取的对象:"+item);

上述方法演示的为配置概率时的操作。

优点:可以直观明了的看到各个对象的概率,简单直观。 缺点:配置的各个概率对象 它们的概率和必须为1,也就是说我们想改动某个对象的获取概率时必须要同时改动另外的概率,否则概率和就不为1了。

若是想改为配置权重,则只需要将上述第三步的 AddOrUpdateWeight() 方法改为 AddOrUpdateWeightInt()方法就好啦。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 //将配置的权重添加到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
代码运行次数:0
运行
AI代码解释
复制
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<GameObjectRandomizerData> RandomizerList = new List<GameObjectRandomizerData>();
}
#endregion

public class WeightedRandom : MonoBehaviour
{
    #region 使用方法一:直接在脚本中配置相关概率
    [System.Serializable]
    public struct GameObjectRandomizerData
    {
        public GameObject Go;
        public float Range;
    }
    //根据概率获取的泛型对象。可以是多种类型,如int/float/GameObject/Component/Script/等等
    private WeightedRandomizer<GameObject> CurrentObjRandomizer;

    [Header("添加获取的对象及概率")]
    public List<GameObjectRandomizerData> RandomizerList = new List<GameObjectRandomizerData>();
    #endregion

    public Button ClickBtn;

    private void Start()
    {
        //1.实例化插件
        CurrentObjRandomizer = new WeightedRandomizer<GameObject>();
        //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
代码运行次数:0
运行
AI代码解释
复制
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;

namespace WeightedRandomization
{
    public class WeightedRandomizer<T>
    {
        private bool adjusted;
        private float tempWeightIntSum;
        private List<WeightedChance<T>> weights;
        private List<int> tempWeightIntRecordList;
        private List<float> tempWeightRecordList;
        public IRandomizationProvider Provider { get; set; }

        public WeightedRandomizer()
        {
            this.weights = new List<WeightedChance<T>>();
            tempWeightIntRecordList = new List<int>();
            tempWeightRecordList = new List<float>();
        }

        public void AddOrUpdateWeight(T value, float weight)
        {
            if(weight == 0)
                throw new ArgumentException("weighted value cannot have a 0% chance.");

            
            WeightedChance<T> existing = this.weights.FirstOrDefault(x => Object.Equals(x.Value, value));
            if (existing == null)            
                this.weights.Add(new WeightedChance<T> { 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<T> existing = this.weights.FirstOrDefault(x => Object.Equals(x.Value, value));
            if (existing == null)
                this.weights.Add(new WeightedChance<T> { 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<T> 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; 
        }
     
        /// <summary>
        /// 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. 
        /// </summary>
        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; 
        }

        /// <summary>
        /// Return a value based on the weights provided. 
        /// </summary>
        /// <returns></returns>
        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 <= x.AdjustedWeight);            
            return item.Value;
        }
    }
}

总结

  • 在游戏中求概率的方法有很多种,有些游戏甚至全靠概率抽卡维持生计。
  • 本文介绍的 概率插件WeightedRandomization 是一种最直接简单的方法从我们配置的概率中获取概率对象。
  • 插件非常轻量,只有几个脚本构成,在用到概率的时候直接将插件资源包拖入项目中即可一键调用。
  • 后面有机会也想尝试几种复杂的游戏概率模型用来学习使用。
  • 如果有小伙伴有概率模型这方面的独特见解和想法,也欢迎在评论区沟通交流哦~
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-02-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
dubbo源码分析1——负载均衡
  dubbo中涉及到的负载均衡算法只要有四种:Random LoadBalance(随机均衡算法)、RoundRobin LoadBalance(权重轮循均衡算法)、LeastAction LoadBalance(最少活跃调用数均衡算法)、ConsistentHash LoadBalance(一致性Hash均衡算法)。   在dubbo中,首先定义了一个LoadBalance的接口。 public interface LoadBalance { /** * select one in
Mister24
2018/05/14
6500
【Unity面试篇】Unity 面试题总结甄选 |Unity进阶篇 | ❤️持续更新❤️
Rigidbody具有完全真实物理的特性,⽽CharacterController可以说是受限的 Rigidbody,具有⼀定的物理效果但不是完全真实的。
呆呆敲代码的小Y
2023/07/24
3K0
【Unity面试篇】Unity 面试题总结甄选 |Unity进阶篇 | ❤️持续更新❤️
源码分析Dubbo负载算法
Dubbo支持在服务调用方对服务提供者采用负载均衡算法,LoadBalance接口定义如下:
丁威
2019/06/10
5530
项目优化之优化技巧进阶(Unity3D)
做游戏经验比较丰富的人都知道,优化的好坏一直是一个游戏的评判标准之一,它直接影响着玩家们的游戏体验,优化一直是项目中开发周期比较长的一个点,也是开发者头疼的一个问题,要求掌握的知识点比较全面,经验也要求比较丰富。 这篇文章参考很多文章的知识点,加以总结与学习,从最基础的概念讲起,配合讲解各种优化技巧,希望大家可以在我的文章中学到一些东西。
恬静的小魔龙
2022/08/07
2.1K0
项目优化之优化技巧进阶(Unity3D)
Unity3D | 经典小游戏Pacman
在学习的过程中,几个比较重要的知识点就是:预制体、克隆体、精灵渲染器、渲染层级、脚本、碰撞检测、触发检测、AI设计、UI设计、固定物理帧…
全栈程序员站长
2022/08/30
8470
Unity3D | 经典小游戏Pacman
网络游戏简单化!PUN插件了解一下?(Unity3D)
Photon Unity Networking (PUN)是一种用于多人游戏的Unity软件包。 灵活的匹配可以让玩家进入房间,可以通过网络同步对象。 快速和可靠的通信是通过专用的Photon 服务器完成的,因此客户端连接不需要1对1。
恬静的小魔龙
2022/08/07
3.2K0
网络游戏简单化!PUN插件了解一下?(Unity3D)
Dubbo负载均衡策略实现[通俗易懂]
1.根据@SPI(RandomLoadBalance.NAME)注解可以知道dubbo的默认负载均衡策略是RandomLoadBalance。
全栈程序员站长
2022/09/08
6770
Dubbo负载均衡策略实现[通俗易懂]
从源码分析dubbo四种负载均衡
LoadBalance 中文意思为负载均衡,它的职责是将网络请求,或者其他形式的负载“均摊”到不同的机器上。避免集群中部分服务器压力过大,而另一些服务器比较空闲的情况。通过负载均衡,可以让每台服务器获取到适合自己处理能力的负载。在为高负载服务器分流的同时,还可以避免资源浪费,一举两得。负载均衡可分为软件负载均衡和硬件负载均衡。在我们日常开发中,一般很难接触到硬件负载均衡。但软件负载均衡还是可以接触到的,比如 Nginx。在 Dubbo 中,也有负载均衡的概念和相应的实现。Dubbo 需要对服务消费者的调用请求进行分配,避免少数服务提供者负载过大。服务提供者负载过大,会导致部分请求超时。因此将负载均衡到每个服务提供者上,是非常必要的。Dubbo 提供了4种负载均衡实现,分别是基于权重随机算法的 RandomLoadBalance、基于最少活跃调用数算法的 LeastActiveLoadBalance、基于 hash 一致性的 ConsistentHashLoadBalance,以及基于加权轮询算法的 RoundRobinLoadBalance。这几个负载均衡算法代码不是很长,但是想看懂也不是很容易,需要大家对这几个算法的原理有一定了解才行。如果不是很了解,也没不用太担心。我们会在分析每个算法的源码之前,对算法原理进行简单的讲解,帮助大家建立初步的印象。
叔牙
2020/11/19
4110
从源码分析dubbo四种负载均衡
(34) 随机 / 计算机程序的思维逻辑
随机 本节,我们来讨论随机,随机是计算机程序中一个非常常见的需求,比如说: 各种游戏中有大量的随机,比如扑克游戏洗牌 微信抢红包,抢的红包金额是随机的 北京购车摇号,谁能摇到是随机的 给用户生成随机密码 我们首先来介绍Java中对随机的支持,同时介绍其实现原理,然后我们针对一些实际场景,包括洗牌、抢红包、摇号、随机高强度密码、带权重的随机选择等,讨论如何应用随机。 先来看如何使用最基本的随机。 Math.random Java中,对随机最基本的支持是Math类中的静态方法random,它生成一个0到1
swiftma
2018/01/31
1.2K0
(34) 随机 / 计算机程序的思维逻辑
实现TensorRT自定义插件(plugin)自由!
本系列为新TensorRT的第一篇,为什么叫新,因为之前已经写了两篇关于TensorRT的文章,是关于TensorRT-5.0版本的。好久没写关于TensorRT的文章了,所幸就以新来开头吧~
老潘
2023/10/19
1.6K0
实现TensorRT自定义插件(plugin)自由!
Dubbo 负载均衡的实现
负载均衡是指在集群中,将多个数据请求分散在不同单元上进行执行,主要为了提高系统容错能力和加强系统对数据的处理能力。
Bug开发工程师
2020/05/26
5580
Dubbo 负载均衡的实现
unity麻将开发视频教程_一屏双人单机手机游戏
因为公司都用比较稳定的版本,所以我是从比较老的版本unity 4.7上手学习的 刚开始学什么都不懂,参考了一个别人写的斗地主demo,所以代码比较累赘…你们可以改良一下
全栈程序员站长
2022/09/27
3K0
unity麻将开发视频教程_一屏双人单机手机游戏
【Unity游戏开发】跟着马三一起魔改LitJson
  在游戏开发中,我们少不了和数据打交道,数据的存储格式可谓是百花齐放,xml、json、csv、bin等等应有尽有。在这其中Json以其小巧轻便、可读性强、兼容性好等优点受到广大程序员的喜爱。目前市面上有许多针对Json类型数据的序列化与反序列化库,比如Newtonsoft.Json、LitJson、SimpleJson、MiniJson等等,在这之中马三比较钟意于LitJson,其源码规模适中、代码规范可读性好、跨平台能力强、解析速度快,但是美中不足的是LitJson对float(官方最新Release已经支持float)、以及Unity的Vector2、Vector3、Rect、AnimationCurve等类型不支持,譬如在解析float的时候会报 Max allowed object depth reached while trying to export from type System.Single 的错误,这就比较蛋疼了。
马三小伙儿
2020/04/01
4.2K1
【Unity游戏开发】跟着马三一起魔改LitJson
借助CodeBuddy打造《部落争霸》:移动战略游戏开发全流程优化
我正在参加CodeBuddy「首席试玩官」内容创作大赛,本文所使用的 CodeBuddy 免费下载链接:腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴
Lethehong
2025/05/18
1040
借助CodeBuddy打造《部落争霸》:移动战略游戏开发全流程优化
Dubbo 源码分析 - 集群容错之 LoadBalance
LoadBalance 中文意思为负载均衡,它的职责是将网络请求,或者其他形式的负载“均摊”到不同的机器上。避免集群中部分服务器压力过大,而另一些服务器比较空闲的情况。通过负载均衡,可以让每台服务器获取到适合自己处理能力的负载。在为高负载的服务器分流的同时,还可以避免资源浪费,一举两得。负载均衡可分为软件负载均衡和硬件负载均衡。在我们日常开发中,一般很难接触到硬件负载均衡。但软件负载均衡还是能够接触到一些的,比如 Nginx。在 Dubbo 中,也有负载均衡的概念和相应的实现。Dubbo 需要对服务消费者的调用请求进行分配,避免少数服务提供者负载过大。服务提供者负载过大,会导致部分服务调用超时。因此将负载均衡到每个服务提供者上,是非常必要的。Dubbo 提供了4种负载均衡实现,分别是基于权重随机算法的 RandomLoadBalance、基于最少活跃调用数算法的 LeastActiveLoadBalance、基于 hash 一致性的 ConsistentHashLoadBalance,以及基于加权轮询算法的 RoundRobinLoadBalance。这几个负载均衡算法代码不是很长,但是想看懂也不是很容易,需要大家对这几个算法的原理有一定了解才行。如果不是很了解,也没不用太担心。我会在分析每个算法的源码之前,对算法原理进行简单的讲解,帮助大家建立初步的印象。
田小波
2018/12/19
6460
游戏开发者使用code buddy能做成什么样? C# Unity
其实,腾讯的code buddy (下文简称为buddy) 和现在大多数的AI辅助编程一样,都是帮助各位IT行业的小伙伴更加高效的编辑代码(make your life better ),目前是免费的阶段(**个人版免费** 企业版与专业版收费详见官网).使用方法与cursor类似.在代码编辑器扩展中安装,然后登陆账号.即可开始使用.使用是很简单的,但是如何去用,也是一门学问.
张曙光
2025/05/13
1260
游戏开发者使用code buddy能做成什么样? C# Unity
NumPyML 源码解析(五)
The preprocessing module implements common data preprocessing routines.
ApacheCN_飞龙
2024/02/17
2230
Unity3d EasyAR开发案例系列教程
这篇文章介绍如何使用EasyAR.unitypackage配置EasyAR ---------- 参考资料 1、EasyAR 初学者入门指南 http://forum.easyar.cn/portal
恬静的小魔龙
2020/03/09
3.1K0
Unity3d EasyAR开发案例系列教程
Unity性能调优手册2基础:硬件,渲染,数据,Unity如何工作,C#基础,算法和计算复杂度
翻译自https://github.com/CyberAgentGameEntertainment/UnityPerformanceTuningBible/ 性能调优需要对整个应用程序进行检查和修改。因此,有效的性能调整需要广泛的知识,从硬件到3D渲染再到Unity机制。因此,本章总结了执行性能调优所需的基本知识
立羽
2023/11/19
9730
Unity性能调优手册2基础:硬件,渲染,数据,Unity如何工作,C#基础,算法和计算复杂度
dubbo源码解析之负载均衡
GitHub地址:https://github.com/abel-max/Java-Study-Note/tree/master
用户5546570
2020/07/01
6190
dubbo源码解析之负载均衡
推荐阅读
相关推荐
dubbo源码分析1——负载均衡
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验