【发布时间】:2020-07-13 14:30:18
【问题描述】:
我正在尝试做的事情:我正在尝试制作基于组件的对象,这些对象可以使用每个组件值的自定义设置类型的规则轻松创建。
我是怎么做的:我创建了每个组件实现的IComponent 接口。所有组件都需要是结构体,例如:
public struct Weight : IComponent
{
int weight;
}
每个对象仅由具有其值的组件列表定义。然后为了使其自定义规则集,我制作了 ObjectSettings,其中包含泛型类ComponentSetup<T> where T : IComponent 的列表。 ComponentSetup 是一个类,它通过反射获取 IComponent 中的字段列表,并在字典中将它们作为 FieldName 和 GenerationType 配对。例如:对于对象“汽车”:
Car:
Weight:
weight:
GenerationType: RandomRange
Min: 1200
Max: 1700
对于对象“人类”:
Human:
Weight:
weight:
GenerationType: NormalDistribution
Expected Value: 70
Variance: 4
对于对象“1kg 哑铃”:
1kgDumbbell:
Weight:
weight:
GenerationType: Fixed
Value: 1
为了获得生成的对象,我使用反射来设置组成列表的组件的值并作为对象返回。
这种方法的问题:当我想生成 5k-10k 的这些对象时,它需要太多时间。
到目前为止我的解决方案:我生成半填充对象(在启动时)并将它们作为预制件存储在 PrefabManager 中。它们是仅当其 GenerationType 为“Fixed”时才设置组件值的对象,然后仅使用其他类型的 Generation 填充值。
我的问题:如何通过反射更快地设置值,如果不可能,那么我怎样才能获得相同的结果但更快?我还想在启动时继续生成预制件,因为它们可以帮助我实例化对象,因为我不需要创建全新的对象,只需复制预制件并填充它,这在我的情况下更快。
编辑:添加示例代码。我没有对其进行测试,但是应该很容易理解我要做什么:
namespace Example
{
//ProceduralObject Component intreface
public interface IComponent
{
}
//Example component for procedural object
public struct Weight : IComponent
{
public int weight;
}
//object with procedurally generated components
public class ProceduralObject
{
public List<IComponent> components = new List<IComponent>();
}
public class ProceduralObjectSettings
{
public Dictionary<string,ComponentSetup> ComponentSetups = new Dictionary<string,ComponentSetup>();
public ProceduralObjectSettings()
{
}
public void AddComponent(Type t)
{
//check if added component is assignable from correct interface
if (t.IsAssignableFrom(typeof(IComponent))) ComponentSetups.Add(t.Name,new ComponentSetup(t));
}
//getting ProceduralObject with generated components
public ProceduralObject getGeneratedObject()
{
ProceduralObject newObject = new ProceduralObject();
foreach (var componentSetup in ComponentSetups)
{
newObject.components.Add(componentSetup.Value.getGeneratedComponent());
}
return newObject;
}
}
public class ComponentSetup
{
// Collection of properties of IComponent it represents
public Dictionary<string, IGenerationType> propertyGenerationSettings = new Dictionary<string, IGenerationType>();
// Type of IComponent it represents
public Type t;
public ComponentSetup(Type t)
{
this.t = t;
//Getting all fields of represented IComponent and adding them to propertyGenerationSettings with default GenerationType
var fields = t.GetFields();
for (int i = 0; i < fields.Length; i++)
{
propertyGenerationSettings.Add(fields[i].Name,new EmptyGenerationType());
}
}
//Generating new component with settings
public IComponent getGeneratedComponent()
{
IComponent toReturn = (IComponent)Activator.CreateInstance(t);
var fields = toReturn.GetType().GetFields();
foreach (var property in propertyGenerationSettings)
{
var fieldInfo = fields.First(field => field.Name == property.Key);
toReturn.GetType().SetMemberValue(fieldInfo, property.Value.GetGeneratedValue());
}
return toReturn;
}
}
public interface IGenerationType
{
System.Object GetGeneratedValue();
}
public class EmptyGenerationType : IGenerationType
{
public object GetGeneratedValue()
{
throw new Exception("You can't use EmptyGenerationType");
}
}
public class RandomRangeGenerationType : IGenerationType
{
private double min, max;
public RandomRangeGenerationType(double min, double max)
{
this.min = min;
this.max = max;
}
public object GetGeneratedValue()
{
return null; /* return */
}
}
public class NormalDistributionGenerationType : IGenerationType
{
private double expectedValue, variance;
public NormalDistributionGenerationType(double expectedValue, double variance)
{
this.expectedValue = expectedValue;
this.variance = variance;
}
public object GetGeneratedValue()
{
return null; /* return */
}
}
public class FixedGenerationType : IGenerationType
{
public double value;
public FixedGenerationType(double value)
{
this.value = value;
}
public object GetGeneratedValue()
{
return null;
}
}
public class Example
{
public void Main()
{
Dictionary<string,ProceduralObjectSettings> proceduralObjectsCollection = new Dictionary<string,ProceduralObjectSettings>();
proceduralObjectsCollection.Add("Car",new ProceduralObjectSettings());
proceduralObjectsCollection["Car"].AddComponent(typeof(Weight));
proceduralObjectsCollection["Car"].ComponentSetups["Weight"].propertyGenerationSettings["weight"] = new RandomRangeGenerationType(1200,1700);
proceduralObjectsCollection.Add("Human",new ProceduralObjectSettings());
proceduralObjectsCollection["Human"].AddComponent(typeof(Weight));
proceduralObjectsCollection["Human"].ComponentSetups["Weight"].propertyGenerationSettings["weight"] = new NormalDistributionGenerationType(70,4);
proceduralObjectsCollection.Add("1kgDumbbell",new ProceduralObjectSettings());
proceduralObjectsCollection["1kgDumbbell"].AddComponent(typeof(Weight));
proceduralObjectsCollection["1kgDumbbell"].ComponentSetups["Weight"].propertyGenerationSettings["weight"] = new FixedGenerationType(1);
}
}
}
【问题讨论】:
-
您会发现 FastMember 有帮助吗? github.com/mgravell/fast-member
-
@mjwills 我添加了示例。我不确定它是否会编译,但它解释了很多我想要做的事情。
标签: c# random reflection procedural