C# 获取给定泛型数组c的浮点变量#
我正在创建一个经常使用加权繁殖的RPG。许多职业,如怪物、战利品或地下城世代,都有一个叫做“繁殖几率”的变量,该值为0-100,决定了繁殖率 我以前一直使用这种选择方式从数组中获取随机加权成员C# 获取给定泛型数组c的浮点变量#,c#,unity3d,random,C#,Unity3d,Random,我正在创建一个经常使用加权繁殖的RPG。许多职业,如怪物、战利品或地下城世代,都有一个叫做“繁殖几率”的变量,该值为0-100,决定了繁殖率 我以前一直使用这种选择方式从数组中获取随机加权成员 var weights = enemy.Select(e => e.spawnChance); var index = weights.GetRandomWeightedIndex(); return enemy[index]; 这是分机 public static int GetRand
var weights = enemy.Select(e => e.spawnChance);
var index = weights.GetRandomWeightedIndex();
return enemy[index];
这是分机
public static int GetRandomWeightedIndex(this IEnumerable<float> weights)
{
int count = weights.Count();
if (weights == null || count == 0)
{
Debug.LogWarning($"Weights are invalid");
return -1;
}
else if (count == 1)
return 0;
float w;
float t = 0;
int i;
for (i = 0; i < count; i++)
{
w = weights.ElementAt(i);
if (float.IsPositiveInfinity(w))
{
return i;
}
else if (w >= 0f && !float.IsNaN(w))
{
t += weights.ElementAt(i); ;
}
}
float r = UnityEngine.Random.value;
float s = 0f;
for (i = 0; i < count; i++)
{
w = weights.ElementAt(i); ;
if (float.IsNaN(w) || w <= 0f) continue;
s += w / t;
if (s >= r) return i;
}
return -1;
}
public static int GetRandomWeightedIndex(此IEnumerable weights)
{
int count=weights.count();
如果(权重==null | |计数==0)
{
Debug.LogWarning($“权重无效”);
返回-1;
}
否则如果(计数=1)
返回0;
浮动w;
浮动t=0;
int i;
对于(i=0;i=0f&&!float.IsNaN(w))为else
{
t+=权重。元素at(i);
}
}
float r=UnityEngine.Random.value;
浮点数s=0f;
对于(i=0;i
但是,我想减少这段代码,因为通过linq选择所有浮点值,然后将其传递到扩展中,然后再返回的方法相当繁琐,并且使用了几十次
我曾想过使用反射在泛型类中查找一个名为“spawnChance”的变量,但由于所有内容都是在运行时生成的,因此速度相当慢
我是否可以将一个简单的浮点转换成一个类,然后以某种方式编写一个linq方法,该方法将能够有效地提取该类的值?由于项目设置的原因,这些类无法从spawnChance类继承。
思想?谢谢你的时间 您可以定义一个接口,然后让公开繁殖机会的类实现它。然后很容易测试对象是否实现了它
public interface ISpawnable
{
float SpawnChance { get; }
}
SpawnChance
必须是属性。没有必要为这个用例声明setter(因为您只需要阅读它);但是,实现仍然可以提供一个
然后你可以测试
if (obj is ISpawnable spawnable) {
float w = spawnable.SpawnChance;
...
}
如果有混合对象列表,也可以使用LINQ
var weights = mixedTypeObjects
.OfType<ISpawnable>() // Filters objects implementing it and casts to it.
.Select(x => x.SpawnChance);
看看你跟谁一起去
public class Spawner : MonoBehaviour
{
[System.Serializable]
public class WeightedItem
{
public GameObject Prefab; // Or whatever you're randomising
public float Weight;
}
public WeightedItem[] Items;
private float TotalWeight;
public void Start() {
// If your lists don't change, pre-calc TotalWeight here.
// If they do change, you'll have to recalculate it when it does.
TotalWeight = Items.Sum(m => m.Weight);
}
public object RandomItem()
{
// Get your 'random' selection.
var selectionWeight = Random.Range(0, TotalWeight);
// Parse over the array to find it
for (var i = 0; i < Items.Length; i++) {
selectionWeight -= Items[i].Weight;
if (selectionWeight <= 0)
return Items[i].Prefab;
}
// Should never reach here unless your TotalWeight is wrong
throw new System.ApplicationException("Randomised Weight exceeded Total Weight!");
}
}
公共类生成程序:单行为
{
[系统可序列化]
公共类WeightedItem
{
公共游戏对象预制;//或任何你正在随机化的东西
公众浮力;
}
公共物品【】项;
私人浮动总重量;
公开作废开始(){
//如果您的列表没有更改,请在此预先计算总重量。
//如果它们确实发生了变化,那么当它发生变化时,您必须重新计算它。
总重量=项目总重量(m=>m.Weight);
}
公共对象项()
{
//获取您的“随机”选择。
var selectionWeight=随机范围(0,总重量);
//对数组进行分析以找到它
对于(变量i=0;i if(selectionWeight如果我是你,我会实现生成概率,而不是浮点数,而是整数百分比。请记住,C#中的float
s不是小数,而是基2数(特别是二进制中的二进制32数,如0.010101)。将二进制数视为小数有时会导致令人惊讶的结果。或者,C#的System.Decimal
(Decimal
)可以使用,这是一种十进制数字格式。如果正确处理浮点数,也就是说,将其用作求和的“距离”而不是离散度量值,则浮点数是可以使用的。接口可能不理想,但泛型会很理想。谢谢!修改此方法是可行的。看起来接口可以工作,但它们不能很好地使用统一,因为它们不是在inspector中没有解决方法也不会暴露,也不会序列化。@SkylarStickley我一直推荐的“资产”实际上只有两种,DOTween和Odin inspector。Odin为inspector的显示和序列化添加了大量的生活质量改进。绝对值得一试。(但不会改变这个答案=)
public static int GetRandomWeightedIndex(this IEnumerable<GameObjects> gameObjects)
{
var weights = gameObjects
.OfType<ISpawnable>()
.Select(g => g.SpawnChance);
...
}
public class Spawner : MonoBehaviour
{
[System.Serializable]
public class WeightedItem
{
public GameObject Prefab; // Or whatever you're randomising
public float Weight;
}
public WeightedItem[] Items;
private float TotalWeight;
public void Start() {
// If your lists don't change, pre-calc TotalWeight here.
// If they do change, you'll have to recalculate it when it does.
TotalWeight = Items.Sum(m => m.Weight);
}
public object RandomItem()
{
// Get your 'random' selection.
var selectionWeight = Random.Range(0, TotalWeight);
// Parse over the array to find it
for (var i = 0; i < Items.Length; i++) {
selectionWeight -= Items[i].Weight;
if (selectionWeight <= 0)
return Items[i].Prefab;
}
// Should never reach here unless your TotalWeight is wrong
throw new System.ApplicationException("Randomised Weight exceeded Total Weight!");
}
}