public void TestMultipleComponents() { // Check that TransformComponent cannot be added multiple times Assert.False(EntityComponentAttributes.Get <TransformComponent>().AllowMultipleComponents); // Check that CustomEntityComponent can be added multiple times Assert.True(EntityComponentAttributes.Get <CustomEntityComponent>().AllowMultipleComponents); // Check that DerivedEntityComponentBase can be added multiple times Assert.True(EntityComponentAttributes.Get <DerivedEntityComponent>().AllowMultipleComponents); var entity = new Entity(); var transform = entity.Get <TransformComponent>(); Assert.NotNull(transform); Assert.Equal(entity.Transform, transform); var custom = entity.GetOrCreate <CustomEntityComponent>(); Assert.NotNull(custom); var custom2 = new CustomEntityComponent(); entity.Components.Add(custom2); Assert.Equal(custom, entity.Get <CustomEntityComponent>()); var allComponents = entity.GetAll <CustomEntityComponent>().ToList(); Assert.Equal(new List <EntityComponent>() { custom, custom2 }, allComponents); }
/// <inheritdoc/> protected override bool CanReplaceItem(IObjectNode collection, NodeIndex index, object newItem) { if (collection.Type != typeof(EntityComponentCollection)) { return(base.CanReplaceItem(collection, index, newItem)); } if (newItem == null) { return(false); } var componentType = newItem.GetType(); // Cannot replace the transform component by another type of component if (collection.IndexedTarget(index).Type == typeof(TransformComponent) && componentType != typeof(TransformComponent)) { return(false); } if (!EntityComponentAttributes.Get(componentType).AllowMultipleComponents) { // Cannot replace components that disallow multiple components, unless it is that specific component we're replacing var components = (EntityComponentCollection)collection.Retrieve(); if (components.Where((x, i) => x.GetType() == componentType && i != index.Int).Any()) { return(false); } } return(base.CanReplaceItem(collection, index, newItem)); }
/// <inheritdoc/> protected override bool CanInsertItem(IObjectNode collection, NodeIndex index, object newItem) { if (collection.Type != typeof(EntityComponentCollection)) { return(base.CanInsertItem(collection, index, newItem)); } if (newItem == null) { return(false); } var componentType = newItem.GetType(); if (!EntityComponentAttributes.Get(componentType).AllowMultipleComponents) { // Cannot insert components that disallow multiple components var components = (EntityComponentCollection)collection.Retrieve(); if (components.Any(x => x.GetType() == componentType)) { return(false); } } return(base.CanInsertItem(collection, index, newItem)); }
/// <inheritdoc/> protected override bool CanUpdate(IAssetNode node, ContentChangeType changeType, NodeIndex index, object value) { // Check if we are in the component collection of an entity (where we actually add new components) if (IsComponentForComponentCollection(node, value) && changeType == ContentChangeType.CollectionAdd) { var componentType = value.GetType(); var attributes = EntityComponentAttributes.Get(componentType); var onlySingleComponent = !attributes.AllowMultipleComponents; var collection = (EntityComponentCollection)node.Retrieve(); foreach (var existingItem in collection) { if (ReferenceEquals(existingItem, value)) { return(false); } if (onlySingleComponent && componentType == existingItem.GetType()) { return(false); } } } return(base.CanUpdate(node, changeType, index, value)); }
/// <inheritdoc/> void IAddChildrenPropertiesProviderViewModel.AddChildren(IReadOnlyCollection <object> children, AddChildModifiers modifiers) { foreach (var asset in children.OfType <AssetViewModel>()) { var component = CreateComponentFromAsset(asset); if (component != null) { // 3 cases: // - If AllowMultipleComponents is true, we add at the end // - If AllowMultipleComponents is true and the component doesn't exist yet, we replace // - If AllowMultipleComponents is false and the component already exists, we replace // Retrieve list of components to check if we need to add or replace var assetSideEntityNode = Editor.NodeContainer.GetNode(AssetSideEntity); var componentsNode = assetSideEntityNode[nameof(Entity.Components)].Target; var components = AssetSideEntity.Components; int replaceIndex = -1; // Only try to replace in case AllowMultipleComponents is not set if (!EntityComponentAttributes.Get(component.GetType()).AllowMultipleComponents) { // Find the replace index (if a component of same type already exists) replaceIndex = components.IndexOf(x => x.GetType() == component.GetType()); } // Add or replace the component using (var transaction = Editor.UndoRedoService.CreateTransaction()) { if (replaceIndex == -1) { componentsNode.Add(component); Editor.UndoRedoService.SetName(transaction, $"Add component {component.GetType().Name}"); } else { componentsNode.Update(component, new NodeIndex(replaceIndex)); Editor.UndoRedoService.SetName(transaction, $"Replace component {component.GetType().Name}"); } } } } }
protected override void UpdateNode(IAssetNodePresenter node) { if (!(node.Asset?.Asset is EntityHierarchyAssetBase)) { return; } if (node.Value is EntityComponent && node.Parent?.Type == typeof(EntityComponentCollection)) { // Apply the display name of the component var displayAttribute = TypeDescriptorFactory.Default.AttributeRegistry.GetAttribute <DisplayAttribute>(node.Value.GetType()); if (!string.IsNullOrEmpty(displayAttribute?.Name)) { node.DisplayName = displayAttribute.Name; } node.CombineKey = node.Value.GetType().Name; if (node.Value is TransformComponent) { // Always put the transformation component in first. node.Order = -1; var removeCommand = node.Commands.FirstOrDefault(x => x.Name == RemoveItemCommand.CommandName); node.Commands.Remove(removeCommand); // Remove the Children property of the transformation component (it should be accessible via the scene graph) node[nameof(TransformComponent.Children)].IsVisible = false; } } if (node.Type == typeof(EntityComponentCollection)) { var types = typeof(EntityComponent).GetInheritedInstantiableTypes() .Where(x => Attribute.GetCustomAttribute(x, typeof(NonInstantiableAttribute)) == null && (EntityComponentAttributes.Get(x).AllowMultipleComponents || ((EntityComponentCollection)node.Value).All(y => y.GetType() != x))) .OrderBy(DisplayAttribute.GetDisplayName) .Select(x => new AbstractNodeType(x)).ToArray(); node.AttachedProperties.Add(EntityHierarchyData.EntityComponentAvailableTypesKey, types); //TODO: Choose a better grouping method. var typeGroups = types.GroupBy(t => ComponentCategoryAttribute.GetCategory(t.Type)) .OrderBy(g => g.Key) .Select(g => new AbstractNodeTypeGroup(g.Key, g.ToArray())).ToArray(); node.AttachedProperties.Add(EntityHierarchyData.EntityComponentAvailableTypeGroupsKey, typeGroups); // Cannot replace entity component collection. var replaceCommandIndex = node.Commands.IndexOf(x => x.Name == ReplacePropertyCommand.CommandName); if (replaceCommandIndex >= 0) { node.Commands.RemoveAt(replaceCommandIndex); } // Combine components by type, but also append a index in case multiple components of the same types exist in the same collection. var componentCount = new Dictionary <Type, int>(); foreach (var componentNode in node.Children) { var type = componentNode.Value.GetType(); int count; componentCount.TryGetValue(type, out count); componentNode.CombineKey = $"{type.Name}@{count}"; componentCount[type] = ++count; } } if (typeof(EntityComponent).IsAssignableFrom(node.Type)) { node.AttachedProperties.Add(ReferenceData.Key, new ComponentReferenceViewModel()); } if (typeof(Entity) == node.Type) { node.AttachedProperties.Add(ReferenceData.Key, new EntityReferenceViewModel()); } }