Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // --- Component Definitions ---
- // Represents an entity's ability to equip items.
- public struct EquipmentComponent
- {
- // Stores equipped items (Entity ID) per slot.
- // Assuming EquipmentSlot is an enum or similar identifier.
- public Dictionary<EquipmentSlot, int> EquippedItems;
- // Flag to indicate equipment changed and stats might need recalculation.
- public bool NeedsRecalculation;
- }
- // Indicates an item can be equipped into a specific slot.
- public struct EquippableComponent
- {
- public EquipmentSlot Slot;
- }
- // Data specific to entities that can bark.
- public struct BarkVolumeComponent
- {
- public int BaseVolume; // The inherent bark volume without items.
- public int CurrentVolume; // The final calculated volume including bonuses.
- }
- // Data for items that modify bark volume.
- public struct BarkBonusComponent
- {
- public int Bonus;
- }
- // Example: Tag component to identify an entity as a 'Dog'
- // (though often the presence of BarkVolumeComponent might be enough).
- public struct DogTag { }
- // Example: Tag component to identify an entity as an 'Item'.
- public struct ItemTag { }
- // --- System Definitions ---
- // Handles the logic of equipping items.
- public class EquipSystem
- {
- // Hypothetical EntityManager to access component data.
- private readonly EntityManager entityManager;
- public EquipSystem(EntityManager manager)
- {
- this.entityManager = manager;
- }
- // Attempts to equip an itemEntity onto a unitEntity.
- public void EquipItem(int unitEntityId, int itemEntityId)
- {
- // Check if both entities exist and have the necessary components.
- if (!entityManager.HasComponent<EquipmentComponent>(unitEntityId) ||
- !entityManager.HasComponent<EquippableComponent>(itemEntityId))
- {
- Console.WriteLine($"ECS Equip: Entity missing required components for equip.");
- return;
- }
- // Get references to the components (often involves pointers or specific ECS API calls).
- ref var equipment = ref entityManager.GetComponent<EquipmentComponent>(unitEntityId);
- var equippable = entityManager.GetComponent<EquippableComponent>(itemEntityId); // Read-only is fine.
- EquipmentSlot slot = equippable.Slot;
- // --- Unequip Existing Item (If Any) ---
- // In a full system, you'd likely call an UnequipSystem or use an event.
- if (equipment.EquippedItems.ContainsKey(slot))
- {
- int currentlyEquippedItemId = equipment.EquippedItems[slot];
- Console.WriteLine($"ECS Equip: Unequipping item {currentlyEquippedItemId} from slot {slot} first.");
- // UnequipSystem.UnequipItem(unitEntityId, currentlyEquippedItemId); // Ideal approach
- // Simplified for this example: just remove it here.
- equipment.EquippedItems.Remove(slot);
- // Mark for recalculation because an item was removed.
- equipment.NeedsRecalculation = true;
- }
- // --- Equip New Item ---
- equipment.EquippedItems[slot] = itemEntityId;
- Console.WriteLine($"ECS Equip: Equipped item {itemEntityId} to unit {unitEntityId} in slot {slot}.");
- // Mark the unit's equipment as changed, signalling stat recalculation might be needed.
- equipment.NeedsRecalculation = true;
- }
- }
- // Handles recalculating BarkVolume based on equipped items.
- public class BarkVolumeUpdateSystem
- {
- private readonly EntityManager entityManager;
- public BarkVolumeUpdateSystem(EntityManager manager)
- {
- this.entityManager = manager;
- }
- public void Update()
- {
- // Query for all entities that have BarkVolume AND Equipment,
- // AND whose equipment needs recalculation.
- var query = entityManager.QueryEntitiesWithComponents<BarkVolumeComponent, EquipmentComponent>();
- foreach (int entityId in query)
- {
- // Get components for the entity that needs updating.
- ref var equipment = ref entityManager.GetComponent<EquipmentComponent>(entityId);
- // Only update if flagged. Reset the flag after processing.
- if (!equipment.NeedsRecalculation) continue;
- ref var barkVolume = ref entityManager.GetComponent<BarkVolumeComponent>(entityId);
- Console.WriteLine($"ECS Update: Recalculating BarkVolume for entity {entityId}.");
- int totalBonus = 0;
- // Iterate through equipped items.
- foreach (var kvp in equipment.EquippedItems)
- {
- int itemId = kvp.Value;
- // Check if the equipped item provides a bark bonus.
- if (entityManager.HasComponent<BarkBonusComponent>(itemId))
- {
- var itemBonus = entityManager.GetComponent<BarkBonusComponent>(itemId); // Read-only.
- totalBonus += itemBonus.Bonus;
- Console.WriteLine($" - Item {itemId} provides +{itemBonus.Bonus} bark bonus.");
- }
- }
- // Calculate the final volume.
- barkVolume.CurrentVolume = barkVolume.BaseVolume + totalBonus;
- Console.WriteLine($" - BaseVolume: {barkVolume.BaseVolume}, TotalBonus: {totalBonus}, New CurrentVolume: {barkVolume.CurrentVolume}");
- // Reset the flag now that recalculation is done.
- equipment.NeedsRecalculation = false;
- }
- }
- }
- // --- Helper/Setup Code (Simplified) ---
- public enum EquipmentSlot { Head, Paws, Collar }
- // VERY basic stand-in for a real ECS EntityManager/World.
- public class EntityManager
- {
- private Dictionary<int, Dictionary<Type, object>> entities = new Dictionary<int, Dictionary<Type, object>>();
- private int nextEntityId = 1;
- public int CreateEntity() {
- int id = nextEntityId++;
- entities[id] = new Dictionary<Type, object>();
- return id;
- }
- public void AddComponent<T>(int entityId, T component) where T : struct
- {
- if (entities.ContainsKey(entityId)) {
- entities[entityId][typeof(T)] = component;
- }
- }
- public bool HasComponent<T>(int entityId) where T : struct
- {
- return entities.ContainsKey(entityId) && entities[entityId].ContainsKey(typeof(T));
- }
- // NOTE: Real ECS uses optimized ways to get component references (ref returns).
- // This boxing/unboxing version is for demonstration only and is inefficient.
- public ref T GetComponent<T>(int entityId) where T : struct
- {
- if (!HasComponent<T>(entityId)) {
- throw new KeyNotFoundException($"Entity {entityId} does not have component {typeof(T).Name}");
- }
- // This is NOT how you'd return a 'ref struct' in a real ECS.
- // It requires more complex unsafe code or specific library features.
- // We'll simulate the modification effect by re-assigning after potential changes.
- // This limitation makes the 'ref' keyword in the Systems illustrative rather than functional here.
- object compObj = entities[entityId][typeof(T)];
- T comp = (T)compObj;
- // **PROBLEM**: This returns a *copy*. We can't directly return a ref to the dictionary value easily here.
- // For the example, we'll rely on systems *re-adding* the modified component if needed (less ideal).
- // A better stub would manage component pools directly.
- // Workaround for stub: We can't return ref T easily. Systems will need to AddComponent again if they modify.
- // Let's make GetComponent just return a copy for this stub.
- return ref GetActualComponentRef<T>(entityId); // Cheating for demo
- }
- // Helper to bypass limitation for demo (Still not real ECS performance/safety)
- private ref T GetActualComponentRef<T>(int entityId) where T : struct {
- // This requires storing components differently, maybe arrays per type.
- // Too complex for this example stub. Systems will just get copies.
- // **Let's adjust the systems to work with copies for this simple demo.**
- throw new NotSupportedException("Returning 'ref T' is complex without a full ECS framework. Adjusting example systems.");
- }
- // Adjusted GetComponent (returns copy)
- public T GetCopyGetComponent<T>(int entityId) where T : struct
- {
- if (!HasComponent<T>(entityId)) {
- throw new KeyNotFoundException($"Entity {entityId} does not have component {typeof(T).Name}");
- }
- return (T)entities[entityId][typeof(T)];
- }
- public List<int> QueryEntitiesWithComponents<T1, T2>() where T1 : struct where T2 : struct
- {
- List<int> result = new List<int>();
- foreach (var kvp in entities) {
- if (kvp.Value.ContainsKey(typeof(T1)) && kvp.Value.ContainsKey(typeof(T2))) {
- result.Add(kvp.Key);
- }
- }
- return result;
- }
- public List<int> QueryEntitiesWithComponents<T1, T2, T3>()
- where T1 : struct where T2 : struct where T3 : struct
- {
- List<int> result = new List<int>();
- foreach (var kvp in entities) {
- if (kvp.Value.ContainsKey(typeof(T1)) &&
- kvp.Value.ContainsKey(typeof(T2)) &&
- kvp.Value.ContainsKey(typeof(T3))) {
- result.Add(kvp.Key);
- }
- }
- return result;
- }
- }
- // --- Main Execution Logic ---
- public class EcsExampleRunner
- {
- public static void Run()
- {
- // --- Setup ---
- var entityManager = new EntityManager(); // Use adjusted EntityManager
- var equipSystem = new EquipSystem(entityManager); // Pass adjusted manager
- var barkSystem = new BarkVolumeUpdateSystem(entityManager); // Pass adjusted manager
- // Adjust Systems to use GetCopyGetComponent and AddComponent for updates
- // (This modification would be needed inside the system classes shown above)
- // For brevity, imagine the System classes now call:
- // var componentCopy = entityManager.GetCopyGetComponent<T>(id);
- // componentCopy.Value = newValue;
- // entityManager.AddComponent(id, componentCopy); // Re-add modified copy
- // --- Create Entities ---
- int dogEntity = entityManager.CreateEntity();
- entityManager.AddComponent(dogEntity, new DogTag()); // Optional tag
- entityManager.AddComponent(dogEntity, new EquipmentComponent {
- EquippedItems = new Dictionary<EquipmentSlot, int>(),
- NeedsRecalculation = true // Start dirty to calculate initial volume
- });
- entityManager.AddComponent(dogEntity, new BarkVolumeComponent { BaseVolume = 10, CurrentVolume = 10 });
- int loudCollarItem = entityManager.CreateEntity();
- entityManager.AddComponent(loudCollarItem, new ItemTag()); // Optional tag
- entityManager.AddComponent(loudCollarItem, new EquippableComponent { Slot = EquipmentSlot.Collar });
- entityManager.AddComponent(loudCollarItem, new BarkBonusComponent { Bonus = 5 });
- int squeakyToyItem = entityManager.CreateEntity();
- entityManager.AddComponent(squeakyToyItem, new ItemTag());
- entityManager.AddComponent(squeakyToyItem, new EquippableComponent { Slot = EquipmentSlot.Paws });
- // No BarkBonusComponent on this item
- Console.WriteLine("--- Initial State ---");
- // Initial bark volume calculation
- barkSystem.Update(); // Needs adjustment to use GetCopy/AddComponent
- // Show initial volume (requires adjusted GetCopyGetComponent)
- // var initialBark = entityManager.GetCopyGetComponent<BarkVolumeComponent>(dogEntity);
- // Console.WriteLine($"Dog {dogEntity} initial BarkVolume: {initialBark.CurrentVolume}");
- Console.WriteLine("\n--- Equipping Loud Collar ---");
- equipSystem.EquipItem(dogEntity, loudCollarItem); // Needs adjustment to use GetCopy/AddComponent
- // Recalculate stats after equipping
- barkSystem.Update(); // Needs adjustment to use GetCopy/AddComponent
- // Show updated volume
- // var updatedBark = entityManager.GetCopyGetComponent<BarkVolumeComponent>(dogEntity);
- // Console.WriteLine($"Dog {dogEntity} BarkVolume after collar: {updatedBark.CurrentVolume}");
- Console.WriteLine("\n--- Equipping Squeaky Toy ---");
- equipSystem.EquipItem(dogEntity, squeakyToyItem); // Needs adjustment to use GetCopy/AddComponent
- // Recalculate (BarkVolume shouldn't change as toy has no bonus)
- barkSystem.Update(); // Needs adjustment to use GetCopy/AddComponent
- // Show volume again
- // var finalBark = entityManager.GetCopyGetComponent<BarkVolumeComponent>(dogEntity);
- // Console.WriteLine($"Dog {dogEntity} BarkVolume after toy: {finalBark.CurrentVolume}");
- // --- Add Unequip Example ---
- // Console.WriteLine("\n--- Unequipping Loud Collar ---");
- // UnequipSystem would be needed here...
- // E.g., UnequipSystem.Unequip(dogEntity, loudCollarItem);
- // barkSystem.Update();
- // ... show volume ...
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement