游戏中的随机算法
1.从一个数组中随机取出一个元素
1 var element = myArray[Random.Range(0, myArray.Length)];
2.PRD伪随机算法, 通常用来计算暴击率
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using UnityEngine; 5 using UnityEditor; 6 using System.IO; 7 using System.Text; 8 using System.Threading; 9 10 public class PRDCalcC : EditorWindow 11 { 12 private static readonly string obj = "lock"; 13 private static Dictionary<int, int> prdDic = new Dictionary<int, int>(); 14 private string infoStr = ""; 15 private string dataStr = "数据运算中...."; 16 17 [MenuItem("Tools/PRD_C")] 18 static void ShowWindow() 19 { 20 GetWindow<PRDCalcC>(); 21 } 22 23 private void OnGUI() 24 { 25 EditorGUILayout.BeginVertical(); 26 if (GUILayout.Button("运算数据")) 27 { 28 // 计算 1% - 100% 暴击率范围所有的 PRD C值 29 for (int i = 0; i <= 100; ++i) 30 { 31 int j = i; 32 // 创建线程负责具体计算 C 值 33 Thread thread = new Thread(() => 34 { 35 double p = i * 1d / 100d; // 显示给玩家的暴击率 36 double c = CFromP(p); // PRD算法 暴击增量 37 int ic = (int)Math.Round(c * 100, 0); // 将百分数小数转换为整数 38 lock (obj) 39 { 40 prdDic[j] = ic; // 计算结果存放在字典中 41 } 42 }); 43 thread.Start(); 44 } 45 } 46 GUILayout.Label(dataStr); 47 if (prdDic.Count == 101) 48 { 49 dataStr = "数据运算完毕"; 50 if (GUILayout.Button("点击生成配置文件")) 51 { 52 try 53 { 54 CreateXml(); 55 infoStr = "配置文件生成成功!"; 56 } 57 catch (Exception e) 58 { 59 infoStr = "配置文件生成失败!错误为:" + e; 60 } 61 } 62 } 63 64 GUILayout.Label(infoStr); 65 66 EditorGUILayout.EndVertical(); 67 } 68 69 // 生成 XML 文件 70 private void CreateXml() 71 { 72 string path = EditorUtility.OpenFolderPanel("选择目标文件夹", "", "") + @"/prd.xml"; 73 StringBuilder sb = new StringBuilder(); 74 sb.Append(@"<?xml version=""1.0"" encoding=""UTF - 8"" standalone=""yes""?>"); 75 sb.Append('\n'); 76 sb.Append(@"<root xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">"); 77 sb.Append('\n'); 78 79 string xml = null; 80 lock (obj) 81 { 82 // 在主线程中 从字典中拿出多线程放入的数据,进行解析 83 foreach(var pair in prdDic) 84 { 85 sb.Append("<item>\n"); 86 sb.Append(" <p>" + pair.Key + "</p>\n"); 87 sb.Append(" <c>" + pair.Value + "</c>\n"); 88 sb.Append("</item>\n"); 89 } 90 xml = sb.ToString(); 91 sb.Clear(); 92 xml.Remove(xml.Length - 1); 93 } 94 using(FileStream fs = Directory.Exists(path) ? File.OpenWrite(path) : File.Create(path)) 95 { 96 byte[] bytes = Encoding.UTF8.GetBytes(xml); 97 fs.Write(bytes, 0, bytes.Length); 98 fs.Flush(); 99 fs.Close(); 100 } 101 lock (obj) 102 { 103 prdDic.Clear(); 104 } 105 } 106 107 // 根据 传入 C 值,计算该C值下,最小暴击范围的平均暴击率 108 private static double PFromC(double c) 109 { 110 double dCurP = 0d; 111 double dPreSuccessP = 0d; 112 double dPE = 0; 113 int nMaxFail = (int)Math.Ceiling(1d / c); 114 for (int i = 1; i <= nMaxFail; ++i) 115 { 116 dCurP = Math.Min(1d, i * c) * (1 - dPreSuccessP); 117 dPreSuccessP += dCurP; 118 dPE += i * dCurP; 119 } 120 return 1d / dPE; 121 } 122 123 // 根据传入的暴击率,计算 PRD 算法中的系数 C 124 private static double CFromP(double p) 125 { 126 double dUp = p; 127 double dLow = 0d; 128 double dMid = p; 129 double dPLast = 1d; 130 while (true) 131 { 132 dMid = (dUp + dLow) / 2d; 133 double dPtested = PFromC(dMid); 134 135 if (Math.Abs(dPtested - dPLast) <= 0.00005d) break; 136 137 if (dPtested > p) dUp = dMid; 138 else dLow = dMid; 139 140 dPLast = dPtested; 141 } 142 143 return dMid; 144 } 145 }
3.洗牌算法
1 /// <summary> 2 /// Knuth-Durstenfeld Shuffle算法,效率最高,会打乱原数组,时间复杂度O(n) 空间复杂度O(1) 3 /// </summary> 4 /// <typeparam name="T">数组类型</typeparam> 5 /// <param name="_array">目标数组</param> 6 public void KnuthDurstenfeldShuffle<T>(T[] _array) 7 { 8 int rand; 9 T tempValue; 10 for (int i = 0; i < _array.Length; i++) 11 { 12 rand = Random.Range(0, _array.Length - i); 13 tempValue = _array[rand]; 14 _array[rand] = _array[_array.Length - 1 - i]; 15 _array[_array.Length - 1 - i] = tempValue; 16 } 17 }
4.权重概率算法
1 //probs为权重数组, 且权重由大到小排序 2 float Choose (float[] probs) { 3 float total = 0; 4 foreach (float elem in probs) { 5 total += elem; 6 } 7 float randomPoint = Random.value * total; 8 for (int i= 0; i < probs.Length; i++) { 9 if (randomPoint < probs[i]) { 10 return i; 11 } 12 else { 13 randomPoint -= probs[i]; 14 } 15 } 16 return probs.Length - 1; 17 }
5.在一个空心圆范围内随机生成物体
1 using UnityEngine; 2 using System.Collections; 3 4 public class RandomRadius : MonoBehaviour { 5 public GameObject prefabs; 6 // Use this for initialization 7 void Start () { 8 for (int i = 0; i < 1000; i++) { 9 Vector2 p = Random.insideUnitCircle*3; 10 Vector2 pos = p.normalized*(2+p.magnitude); 11 Vector3 pos2 = new Vector3(pos.x,0,pos.y); 12 Instantiate(prefabs,pos2,Quaternion.identity); 13 } 14 } 15 }
6.从一个数组中随机选择指定个数且不重复的元素
1 int[] spawnPoints = {1, 5, 6, 8, 9, 20, 15, 10, 13}; 2 3 int[] ChooseSet (int numRequired) { 4 int[] result = new Transform[numRequired]; 5 int numToChoose = numRequired; 6 for (int numLeft = spawnPoints.Length; numLeft > 0; numLeft--) { 7 float prob = (float)numToChoose/(float)numLeft; 8 if (Random.value <= prob) { 9 numToChoose--; 10 result[numToChoose] = spawnPoints[numLeft - 1]; 11 if (numToChoose == 0) { 12 break; 13 } 14 } 15 } 16 return result; 17 }
7.在一个球体内生成随机点
1 var randWithinRadius = Random.insideUnitSphere * radius;
8.遵循高斯分布的随机算法(lua实现)
1 function randomNormalDistribution() 2 local u, v, w, c = 0, 0, 0, 0 3 while(w == 0 or w >= 1) 4 do 5 --//获得两个(-1,1)的独立随机变量 6 u = math.random() * 2 - 1 7 v = math.random() * 2 - 1 8 w = u * u + v * v 9 end 10 --//这里就是 Box-Muller转换 11 c = math.sqrt((-2 * math.log(w)) / w) 12 --//返回2个标准正态分布的随机数,封装进一个数组返回 13 --//当然,因为这个函数运行较快,也可以扔掉一个 14 --//return [u*c,v*c]; 15 return u * c 16 end 17 18 function getNumberInNormalDistribution(mean, std_dev) 19 return mean + (randomNormalDistribution() * std_dev) 20 end 21 --//参数1表示期望值, 参数二表示差值范围 22 getNumberInNormalDistribution(180, 10)
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步