Что я пытаюсь сделать: я пытаюсь создать объекты на основе компонентов, которые можно легко создать с помощью настраиваемого типа правил для каждого значения компонента.
Как я это делаю: я создал IComponent
интерфейс, который реализует каждый компонент. Все компоненты должны быть структурами, например:
public struct Weight : IComponent
{
int weight;
}
Каждый объект определяется просто списком компонентов с их значениями. Затем, чтобы сделать его собственным набором правил, я создал ObjectSettings, который содержит список общих c class ComponentSetup<T> where T : IComponent
. ComponentSetup
- это класс, который путем отражения получает список полей в IComponent и объединяет их в Dicionary как FieldName и GenerationType для поля. Например: для объекта «Автомобиль»:
Car:
Weight:
weight:
GenerationType: RandomRange
Min: 1200
Max: 1700
Для объекта «Человек»:
Human:
Weight:
weight:
GenerationType: NormalDistribution
Expected Value: 70
Variance: 4
Для объекта «Гантель 1 кг»:
1kgDumbbell:
Weight:
weight:
GenerationType: Fixed
Value: 1
В Чтобы получить сгенерированные объекты, я использовал отражение, чтобы установить значения компонентов, составляющих в списке и возвращаемых как объект.
Проблема с этим подходом: когда я хочу сгенерировать 5-10 тыс. этих объектов, это занимает слишком много времени .
Мое решение: я генерирую полузаполненные объекты (при запуске) и сохраняю их как префабы в PrefabManager. Это объекты, значения компонентов которых установлены только в том случае, если их GenerationType имеет значение «Fixed», а затем заполняются только значения с другими типами генерации.
Мой вопрос: как я могу быстрее установить значения путем отражения, если это невозможно тогда как я могу получить тот же результат, но быстрее? Я также хотел бы сохранить генерацию префаба при запуске, потому что они помогают мне создавать экземпляры объектов, потому что мне не нужно создавать полностью новый объект, просто скопируйте префаб и заполните его, что в моем случае быстрее.
EDIT: Добавляем пример кода. Я не тестировал это, но должно быть легко понять, что я пытаюсь сделать:
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);
}
}
}