资讯详情

原神抽卡模拟器开发指南:概率模型、代码实现与验证

📅 2026/10/25 13:30:57 | 华诺云谱 👁 阅读
原神抽卡模拟器开发指南:概率模型、代码实现与验证
简介这是一份面向C初学者与原神玩家的抽卡模拟器源码工程用C复刻游戏内祈愿逻辑帮助学习者在无需消耗原石的前提下理解抽卡概率与保底机制。资源以Visual Studio解决方案形式组织包含main.cpp、wish_function.cpp、wish.h、cmd_login.hpp、Md5.hpp等核心源码与头文件并附带数据文本、登录日志及编译产物可直接打开工程编译运行。压缩包共38个文件以txt与tlog日志、cpp与hpp源码、pdb与obj编译中间文件为主另有sln、vcxproj工程配置及exe可执行文件整体约4.04MB。目前已有1592人学习下载。读者可从中掌握用random库生成随机数、设计祈愿池与保底计数器、统计出货率等实现思路并参考登录与MD5模块了解工程结构适合作为C面向对象与概率统计的练手项目。1. 原神抽卡模拟器.zip一个压缩包背后到底藏着什么你下载了一个叫「原神抽卡模拟器.zip」的压缩包解压后可能是一堆 HTML、JS 文件也可能是一个 Python 脚本加几张角色立绘。不管哪种形态它本质上都在做同一件事用代码复现《原神》里「祈愿」系统的概率逻辑和交互反馈。很多人第一次搜「原神抽卡模拟器」动机很朴素——想看看自己到底是欧皇还是非酋又不想真金白银往里砸。这个需求非常合理因为原神抽卡的概率模型本身就是一个值得拆解的工程问题基础概率 0.6%74 抽后概率线性递增90 抽硬保底大保底机制、四星保底、十连保底……这些规则用代码写出来并不复杂但要把「模拟器」做得可信、可玩、可验证里面有不少细节值得聊。这篇文章面向两类人一是想自己动手写一个抽卡模拟器的开发者二是拿到别人写的模拟器但不确定它靠不靠谱的玩家。我会从概率模型怎么建、代码怎么写、参数怎么调、哪里容易翻车一路讲到怎么验证你的模拟器是不是真的「像那么回事」。2. 抽卡概率模型从官方规则到可执行代码2.1 原神祈愿的概率规则到底怎么描述在动手写代码之前必须先把规则用自然语言精确描述出来否则代码一定写歪。原神角色活动祈愿限定池的核心规则可以归纳为以下几点五星角色基础概率为 0.6%从第 74 抽开始每抽概率增加 6%直到第 90 抽达到 100%。五星物品有保底机制如果上一次五星不是当期 UP 角色则下一次五星必定是 UP 角色这就是俗称的「大保底」。四星物品基础概率为 5.1%每 10 抽至少出一个四星或以上物品。四星同样有 UP 机制三个四星 UP 物品共享 50% 的概率如果上一次四星不是 UP则下一次四星必定是 UP 之一。这些规则看起来清晰但落到代码里有一个关键分歧概率递增是从第 74 抽开始算还是从第 75 抽开始算不同版本的模拟器在这点上处理不一样。常见做法是定义一个soft_pity_start 74然后第 n 抽的五星概率为0.006 max(0, n - 74) * 0.06当 n 达到 90 时直接返回 1.0。这个公式在 n74 时概率仍是 0.6%n75 时变成 6.6%以此类推到 n90 时正好是 0.006 16 * 0.06 0.966所以第 90 抽必须强制为 1.0。这个细节如果不处理好模拟出来的出货分布会和真实体感有明显偏差。2.2 用 Python 写一个最小可用的抽卡核心下面是一个抽卡核心逻辑的最小实现不依赖任何第三方库直接跑就能用import random class GachaSimulator: def __init__(self): self.pity_5star 0 # 距离上次五星的抽数 self.pity_4star 0 # 距离上次四星的抽数 self.guaranteed_5star False # 是否处于大保底 self.guaranteed_4star False # 是否处于四星大保底 def _five_star_rate(self): 计算当前抽的五星概率 n self.pity_5star 1 if n 90: return 1.0 if n 74: return 0.006 return min(0.006 (n - 74) * 0.06, 1.0) def _four_star_rate(self): 计算当前抽的四星概率 n self.pity_4star 1 if n 10: return 1.0 return 0.051 def pull_one(self): 单抽返回 (5star/4star/3star, 是否为UP) # 先判定五星 if random.random() self._five_star_rate(): self.pity_5star 0 self.pity_4star 1 is_up self.guaranteed_5star or random.random() 0.5 self.guaranteed_5star not is_up return (5star, is_up) # 再判定四星 if random.random() self._four_star_rate(): self.pity_4star 0 self.pity_5star 1 is_up self.guaranteed_4star or random.random() 0.5 self.guaranteed_4star not is_up return (4star, is_up) # 三星 self.pity_5star 1 self.pity_4star 1 return (3star, False) def pull_ten(self): 十连返回结果列表 return [self.pull_one() for _ in range(10)]这段代码的逻辑说明pity_5star记录的是「距离上一次五星已经过了多少抽」所以在计算当前抽概率时要用pity_5star 1作为当前是第几抽。五星判定优先于四星这是符合官方规则的——如果一抽同时满足五星和四星的条件它算五星。guaranteed_5star标记大保底状态当上一次五星不是 UP 时置为 True下次五星必定 UP。四星逻辑同理但四星没有「软保底递增」只有 10 抽硬保底。参数方面0.006和0.06这两个数字是核心改任何一个都会显著影响出货分布。0.051是四星基础概率10是四星保底抽数。如果你想让模拟器更接近真实体感不要动这些值而是去调整随机数生成器的种子或者跑更多次模拟来观察分布。2.3 十连保底和单抽的差异在哪很多人以为十连就是「抽十次单抽」从概率上讲确实如此但有一个细节十连保底确保的是「十抽内至少有一个四星或以上」而不是「第十抽必定四星」。上面的pull_ten实现是逐抽调用pull_one四星保底逻辑在_four_star_rate里已经处理了——当pity_4star 1 10时概率为 1.0。这意味着如果你前九抽都没出四星第十抽必定是四星或五星。这个逻辑和单抽完全一致所以十连和单抽在概率上没有区别只是展示形式不同。但有一个容易翻车的点如果你在十连过程中出了五星pity_4star会加 1 而不是清零这会导致四星保底计数继续累积。这是正确的因为五星不重置四星保底。但有些模拟器实现里会把五星也当作「重置四星保底」来处理这就错了会导致四星出货率偏低。3. 把模拟器做成能看的界面HTML 单文件方案3.1 为什么推荐单文件 HTML 而不是框架如果你只是想做一个自己能玩、能分享给朋友的抽卡模拟器单文件 HTML 是最务实的选择。不需要 Node.js、不需要构建工具、不需要服务器双击就能跑发给别人也能直接打开。用 React 或 Vue 当然可以但你会花大量时间在环境配置和打包上而抽卡模拟器的核心价值在概率逻辑和交互反馈不在技术栈。常见做法是一个 HTML 文件内联 CSS 和 JavaScript用 Canvas 或 DOM 做抽卡动画用 localStorage 存保底计数。3.2 用 Canvas 做十连翻牌动画的关键参数十连抽的视觉反馈是模拟器「像不像」的关键。原神的十连是先显示一张一张卡片背面然后依次翻开。用 Canvas 实现时核心参数包括参数建议值说明卡片宽度120px十连时一行五张两行卡片高度200px宽高比接近原神卡面翻牌间隔80ms太快没感觉太慢烦躁五星特效时长1200ms金光闪烁加粒子四星特效时长600ms紫光一闪三星过渡200ms快速翻过这些值不是绝对的但如果你想让模拟器有「那味」翻牌间隔不要低于 50ms五星特效不要短于 800ms。很多模拟器翻车就翻在动画太快十连一秒翻完完全没有抽卡的感觉。3.3 用 localStorage 存保底状态的最小实现抽卡模拟器如果不存保底状态每次刷新页面都重置那就失去了「模拟」的意义。用 localStorage 存状态是最简单的方案// 保存状态 function saveState(state) { localStorage.setItem(gacha_state, JSON.stringify({ pity5: state.pity5, pity4: state.pity4, guaranteed5: state.guaranteed5, guaranteed4: state.guaranteed4, totalPulls: state.totalPulls })); } // 读取状态 function loadState() { const raw localStorage.getItem(gacha_state); if (!raw) return { pity5: 0, pity4: 0, guaranteed5: false, guaranteed4: false, totalPulls: 0 }; try { return JSON.parse(raw); } catch (e) { // 数据损坏时重置避免白屏 return { pity5: 0, pity4: 0, guaranteed5: false, guaranteed4: false, totalPulls: 0 }; } }逻辑说明saveState在每次抽卡后调用把当前保底计数和总抽数写入 localStorage。loadState在页面加载时调用如果解析失败就返回默认状态。这里有一个坑如果你在loadState里不做 try-catch一旦 localStorage 里的数据被手动改坏或者版本不兼容整个页面会直接报错白屏。加上异常处理是血泪经验。参数方面totalPulls用来统计总抽数可以用来算平均出货率。guaranteed5和guaranteed4是布尔值存的时候用 JSON 序列化没问题。注意不要存函数或循环引用否则JSON.stringify会报错。4. 避坑与排查抽卡模拟器最容易翻车的 5 个地方4.1 概率递增公式写错导致出货分布异常现象模拟器跑一万次平均五星出货抽数在 55 左右而真实体感应该在 62 到 65 之间。原因概率递增的起始抽数写成了 70 或者递增步长写成了 0.05。原神的软保底是从第 74 抽开始每抽增加 6%这两个数字任何一个写错都会让出货偏早。解决把soft_pity_start固定为 74increment固定为 0.06然后在第 90 抽强制概率为 1.0。跑 10000 次模拟统计五星出货抽数的分布如果中位数在 75 到 80 之间说明公式对了。4.2 大保底状态在十连中丢失现象十连抽到五星后下一次五星仍然可能歪大保底没有生效。原因十连实现里每次pull_one都重新读取状态但状态没有在十连结束后正确写回。或者guaranteed_5star在十连过程中被覆盖了。解决确保pull_one修改的是同一个状态对象十连结束后统一保存。不要在十连里创建新的模拟器实例。4.3 四星保底被五星重置现象四星出货率明显偏低十连里经常只有一个四星甚至没有。原因代码里在判定五星后把pity_4star清零了。正确做法是五星不重置四星保底只有四星才重置。解决检查pull_one里五星分支确保pity_4star是加 1 而不是清零。4.4 localStorage 数据损坏导致页面白屏现象刷新页面后模拟器直接白屏控制台报 JSON 解析错误。原因localStorage 里的数据被手动修改过或者旧版本的数据结构和新版本不兼容。解决在loadState里加 try-catch解析失败时返回默认状态并清除损坏数据。同时给存储的 key 加版本号比如gacha_state_v2版本升级时旧数据自动失效。4.5 动画卡顿导致抽卡体验断裂现象十连翻牌时动画掉帧五星特效卡住不动。原因用 DOM 操作逐张卡片添加类名每次添加都触发重排重绘。或者用setTimeout控制动画时序但时间间隔太短导致回调堆积。解决用 Canvas 绘制动画或者用 CSStransform和opacity做硬件加速。翻牌间隔不要低于 50ms五星特效可以用requestAnimationFrame驱动粒子效果避免用setInterval。5. 验证模拟器可信度的三个硬指标5.1 跑一万次模拟看五星出货抽数的分布一个可信的抽卡模拟器跑 10000 次单抽或 1000 次十连五星出货抽数的分布应该满足中位数在 75 到 80 之间平均出货抽数在 62 到 65 之间90 抽保底触发的比例在 5% 到 8% 之间。如果你模拟出来的平均出货抽数低于 55说明概率递增太早如果高于 70说明递增太晚或者保底没生效。import statistics def run_simulation(n10000): sim GachaSimulator() pulls [] current 0 for _ in range(n): current 1 result, _ sim.pull_one() if result 5star: pulls.append(current) current 0 return pulls pulls run_simulation(10000) print(f平均出货抽数: {statistics.mean(pulls):.2f}) print(f中位数: {statistics.median(pulls):.2f}) print(f90抽保底触发次数: {pulls.count(90)})这段代码跑完后如果平均出货抽数在 62 到 65 之间中位数在 75 到 80 之间说明你的概率模型基本可信。如果偏差超过 5%回去检查_five_star_rate的公式。5.2 用卡方检验验证四星出货率四星基础概率 5.1%加上 10 抽保底实际四星出货率应该在 13% 左右。你可以跑 10000 次单抽统计四星出货次数然后用卡方检验判断是否显著偏离预期。如果四星出货率低于 10%大概率是四星保底逻辑写错了。5.3 大保底触发率的验证方法大保底的理论触发率是 50%——每次五星有 50% 概率是 UP如果不是 UP下次必定 UP。所以长期来看UP 五星占总五星的比例应该接近 75%第一次 50% UP剩下 50% 里第二次必定 UP所以总 UP 率 50% 50% × 100% 75%。如果你模拟出来的 UP 比例在 70% 到 80% 之间说明大保底逻辑正确。低于 70% 说明大保底没生效高于 80% 说明 UP 概率写成了 100%。我自己的习惯是每次改完概率逻辑先跑 10000 次模拟看三个指标指标不对就不往下做界面。这个习惯帮我省了很多后悔药——界面做得再漂亮概率错了就是自欺欺人。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑