From ba707eebb995eb46141d3c5e1701cd7252ba81c8 Mon Sep 17 00:00:00 2001 From: realtradam Date: Wed, 16 Jun 2021 07:09:43 -0400 Subject: entities, components, and systems improved --- codeclimate/export-coverage.rb | 2 + component_manager.rb | 36 +- docs/FelFlame.html | 4 +- docs/FelFlame/Components.html | 2 +- docs/FelFlame/Entities.html | 82 +- docs/FelFlame/Helper.html | 2 +- docs/FelFlame/Helper/ComponentManager.html | 378 +++++++-- docs/FelFlame/Systems.html | 1174 +++++++++++++++++++++++++++- docs/_index.html | 2 +- docs/file.README.html | 37 +- docs/index.html | 37 +- docs/method_list.html | 152 +++- docs/top-level-namespace.html | 2 +- entity_manager.rb | 37 +- spec/entity_manager_spec.rb | 31 +- spec/system_manager_spec.rb | 185 +++++ system_manager.rb | 110 ++- 17 files changed, 2107 insertions(+), 166 deletions(-) create mode 100644 spec/system_manager_spec.rb diff --git a/codeclimate/export-coverage.rb b/codeclimate/export-coverage.rb index a7a2c12..7a63acd 100644 --- a/codeclimate/export-coverage.rb +++ b/codeclimate/export-coverage.rb @@ -4,6 +4,8 @@ require_relative 'env' class ReportCoverage class <] Array of all Components that belong to a given component manager # @!visibility private def data @@ -128,11 +152,17 @@ class FelFlame # Removes this component from the list and purges all references to this Component from other Entities, as well as its {id ID} and data. # @return [Boolean] true. def delete - entities.each do |entity_id| - FelFlame::Entities[entity_id].remove self + addition_triggers.each do |system| + system.clear_triggers component_or_manager: self + end + # This needs to be cloned because indices get deleted as + # the remove command is called, breaking the loop if it + # wasn't referencing a clone(will get Nil errors) + iter = entities.map(&:clone) + iter.each do |entity_id| + FelFlame::Entities[entity_id].remove self #unless FelFlame::Entities[entity_id].nil? end self.class.data[id] = nil - @entities = nil instance_variables.each do |var| instance_variable_set(var, nil) end diff --git a/docs/FelFlame.html b/docs/FelFlame.html index 1dba2dc..13eb3f1 100644 --- a/docs/FelFlame.html +++ b/docs/FelFlame.html @@ -95,7 +95,7 @@
Defined in:
component_manager.rb,
- entity_manager.rb,
felflame.rb
+ system_manager.rb,
entity_manager.rb,
felflame.rb
@@ -193,7 +193,7 @@ diff --git a/docs/FelFlame/Components.html b/docs/FelFlame/Components.html index 54946ae..3472ea0 100644 --- a/docs/FelFlame/Components.html +++ b/docs/FelFlame/Components.html @@ -403,7 +403,7 @@ diff --git a/docs/FelFlame/Entities.html b/docs/FelFlame/Entities.html index 7669d53..e91c2c0 100644 --- a/docs/FelFlame/Entities.html +++ b/docs/FelFlame/Entities.html @@ -643,12 +643,12 @@
 
 
-104
-105
-106
+113 +114 +115 -
# File 'entity_manager.rb', line 104
+      
# File 'entity_manager.rb', line 113
 
 def [](entity_id)
   data[entity_id]
@@ -670,7 +670,7 @@
 
-

Iterates over all entities. In general when using ECS the use of this method should never be neccassary unless you are doing something very wrong, however I will not stop you. You also call other enumerable methods instead of each, such as each_with_index or select

+

Iterates over all entities. The data is compacted so that means index does not correlate to ID. You also call other enumerable methods instead of each, such as each_with_index or select

@@ -697,15 +697,15 @@
 
 
-111
-112
-113
+120 +121 +122
-
# File 'entity_manager.rb', line 111
+      
# File 'entity_manager.rb', line 120
 
 def each(&block)
-  data.each(&block)
+  data.compact.each(&block)
 end
@@ -774,10 +774,10 @@
 
 
-119
+128
-
# File 'entity_manager.rb', line 119
+      
# File 'entity_manager.rb', line 128
 
 def from_json(json_string, **opts) end
@@ -814,7 +814,7 @@
  • - component + components_to_add (Component) @@ -855,7 +855,6 @@
     
     
    -54
     55
     56
     57
    @@ -868,26 +867,23 @@
     64
     65
     66
    -67
    -68
    +67
  • -
    # File 'entity_manager.rb', line 54
    +      
    # File 'entity_manager.rb', line 55
     
     def add(*components_to_add)
    -  added = false
       components_to_add.each do |component|
         if components[component.class].nil?
           components[component.class] = [component.id]
           component.entities.push id
    -      added =true
    +      check_systems component, :addition_triggers
         elsif !components[component.class].include? component.id
           components[component.class].push component.id
           component.entities.push id
    -      added = true
    +      check_systems component, :addition_triggers
         end
       end
    -  added
     end
    @@ -1002,7 +998,8 @@ 46 47 48 -49
    +49 +50
    # File 'entity_manager.rb', line 39
    @@ -1011,11 +1008,12 @@
       components.each do |component_manager, component_array|
         component_array.each do |component_id|
           component_manager[component_id].entities.delete(id)
    -    end
    +      #self.remove FelFlame::Components.const_get(component_manager.name)[component_id]
    +    end
       end
       FelFlame::Entities.data[id] = nil
    +  @components = {}
       @id = nil
    -  @components = nil
       true
     end
    @@ -1046,7 +1044,7 @@
  • - component_to_remove + components_to_remove (Component) @@ -1087,29 +1085,25 @@
     
     
    -73
    -74
    -75
    -76
    -77
    -78
    -79
    -80
    -81
    -82
    +84 +85 +86 +87 +88 +89 +90 +91 -
    # File 'entity_manager.rb', line 73
    +      
    # File 'entity_manager.rb', line 84
     
     def remove(*components_to_remove)
    -  removed = false
       components_to_remove.each do |component|
    +    check_systems component, :removal_triggers if component.entities.include? id
    +    component.entities.delete id
         components[component.class].delete component.id
    -    if component.entities.delete id
    -      removed = true
    -    end
       end
    -  removed
    +  true
     end
    @@ -1214,10 +1208,10 @@
     
     
    -87
    +96
    -
    # File 'entity_manager.rb', line 87
    +      
    # File 'entity_manager.rb', line 96
     
     def to_json() end
    @@ -1230,7 +1224,7 @@ diff --git a/docs/FelFlame/Helper.html b/docs/FelFlame/Helper.html index 497de3a..19f40b6 100644 --- a/docs/FelFlame/Helper.html +++ b/docs/FelFlame/Helper.html @@ -132,7 +132,7 @@ diff --git a/docs/FelFlame/Helper/ComponentManager.html b/docs/FelFlame/Helper/ComponentManager.html index 7f6e857..425d91b 100644 --- a/docs/FelFlame/Helper/ComponentManager.html +++ b/docs/FelFlame/Helper/ComponentManager.html @@ -114,9 +114,89 @@ +

    Class Attribute Summary collapse

    + +

    Instance Attribute Summary collapse

      +
    • + + + #addition_triggers ⇒ Object + + + + + + + + + + + + + + + + +
      + +
    • + +
    • @@ -141,6 +221,31 @@

      Holds the unique ID of a component.

      +
    • + + +
    • + + + #removal_triggers ⇒ Object + + + + + + + + + + + + + + + + +
      +
    • @@ -436,28 +541,28 @@
       
       
      -62
      -63
      -64
      -65
      -66
      -67
      -68
      -69
      -70
      -71
      -72
      -73
       74
       75
       76
       77
       78
       79
      -80
      +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92
    -
    # File 'component_manager.rb', line 62
    +      
    # File 'component_manager.rb', line 74
     
     def initialize(**attrs)
       # Prepare the object
    @@ -485,13 +590,114 @@
       
     
     
    +  
    +

    Class Attribute Details

    + + + +
    +

    + + .addition_triggersObject + + + + + +

    + + + + +
    +
    +
    +
    +97
    +98
    +99
    +
    +
    # File 'component_manager.rb', line 97
    +
    +def addition_triggers
    +  @addition_triggers ||= []
    +end
    +
    +
    + + + +
    +

    + + .removal_triggersObject + + + + + +

    + + + + +
    +
    +
    +
    +103
    +104
    +105
    +
    +
    # File 'component_manager.rb', line 103
    +
    +def removal_triggers
    +  @removal_triggers ||= []
    +end
    +
    +
    + +
    +

    Instance Attribute Details

    - +
    -

    +

    + + #addition_triggersObject + + + + + +

    + + + + +
    +
    +
    +
    +61
    +62
    +63
    +
    +
    # File 'component_manager.rb', line 61
    +
    +def addition_triggers
    +  @addition_triggers ||= []
    +end
    +
    +
    + + + +
    +

    #idInteger @@ -544,6 +750,38 @@

    + + +
    +

    + + #removal_triggersObject + + + + + +

    + + + + +
    +
    +
    +
    +67
    +68
    +69
    +
    +
    # File 'component_manager.rb', line 67
    +
    +def removal_triggers
    +  @removal_triggers ||= []
    +end
    +
    +
    +
    @@ -619,12 +857,12 @@
     
     
    -96
    -97
    -98
    +120 +121 +122
    -
    # File 'component_manager.rb', line 96
    +      
    # File 'component_manager.rb', line 120
     
     def [](component_id)
       data[component_id]
    @@ -673,12 +911,12 @@
           
     
     
    -103
    -104
    -105
    +127 +128 +129
    -
    # File 'component_manager.rb', line 103
    +      
    # File 'component_manager.rb', line 127
     
     def each(&block)
       data.compact.each(&block)
    @@ -738,14 +976,14 @@
           
     
     
    -143
    -144
    -145
    -146
    -147
    +173 +174 +175 +176 +177
    -
    # File 'component_manager.rb', line 143
    +      
    # File 'component_manager.rb', line 173
     
     def attrs
       instance_variables.each_with_object({}) do |key, final|
    @@ -801,27 +1039,39 @@
           
     
     
    -130
    -131
    -132
    -133
    -134
    -135
    -136
    -137
    -138
    -139
    -140
    +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170
    -
    # File 'component_manager.rb', line 130
    +      
    # File 'component_manager.rb', line 154
     
     def delete
    -  entities.each do |entity_id|
    -    FelFlame::Entities[entity_id].remove self
    +  addition_triggers.each do |system|
    +    system.clear_triggers component_or_manager: self
       end
    +  # This needs to be cloned because indices get deleted as
    +  # the remove command is called, breaking the loop if it
    +  # wasn't referencing a clone(will get Nil errors)
    +  iter = entities.map(&:clone)
    +  iter.each do |entity_id|
    +    FelFlame::Entities[entity_id].remove self #unless FelFlame::Entities[entity_id].nil?
    +  end
       self.class.data[id] = nil
    -  @entities = nil
       instance_variables.each do |var|
         instance_variable_set(var, nil)
       end
    @@ -871,12 +1121,12 @@
           
     
     
    -116
    -117
    -118
    +140 +141 +142
    -
    # File 'component_manager.rb', line 116
    +      
    # File 'component_manager.rb', line 140
     
     def entities
       @entities ||= []
    @@ -925,12 +1175,12 @@
           
     
     
    -110
    -111
    -112
    +134 +135 +136
    -
    # File 'component_manager.rb', line 110
    +      
    # File 'component_manager.rb', line 134
     
     def to_i
       id
    @@ -984,12 +1234,12 @@
           
     
     
    -152
    -153
    -154
    +182 +183 +184
    -
    # File 'component_manager.rb', line 152
    +      
    # File 'component_manager.rb', line 182
     
     def to_json
       # should return a json or hash of all data in this component
    @@ -1043,14 +1293,14 @@
           
     
     
    -122
    -123
    -124
    -125
    -126
    +146 +147 +148 +149 +150
    -
    # File 'component_manager.rb', line 122
    +      
    # File 'component_manager.rb', line 146
     
     def update_attrs(**opts)
       opts.each do |key, value|
    @@ -1067,7 +1317,7 @@
     
     
           
    diff --git a/docs/FelFlame/Systems.html b/docs/FelFlame/Systems.html
    index cfb3a95..65d463b 100644
    --- a/docs/FelFlame/Systems.html
    +++ b/docs/FelFlame/Systems.html
    @@ -85,6 +85,11 @@
     
       
       
    +  
    +
    Extended by:
    +
    Enumerable
    +
    + @@ -94,7 +99,9 @@
    Defined in:
    -
    felflame.rb
    +
    felflame.rb,
    + system_manager.rb
    +
    @@ -116,15 +123,1178 @@ +

    Instance Attribute Summary collapse

    + + + + + + +

    + Class Method Summary + collapse +

    + + + +

    + Instance Method Summary + collapse +

    + + + + + +
    +

    Constructor Details

    + +
    +

    + + #initialize(name, priority: 0, &block) ⇒ Systems + + + +

    +
    + +

    Creates a new System which can be accessed as a constant under the namespace FelFlame::Systems. The name given is what constant the system is assigned to

    +
    +
    +
    + +
    +

    Examples:

    + + +
    FelFlame::Systems.new('PassiveHeal', priority: -2, frame: 2) do
    +  FelFlame::Components::Health.each do |component|
    +    component.hp += 5
    +  end
    +end
    +# Only heal all characters with health every other frame.
    +# Give it a low priority so other systems such as a
    +#   Poison system would kill the player first
    + +
    +

    Parameters:

    +
      + +
    • + + name + + + (String) + + + + — +
      +

      The name this system will use. Needs to to be in the Ruby Constant format.

      +
      + +
    • + +
    • + + priority + + + (Integer) + + + (defaults to: 0) + + + — +
      +

      Which priority order this system should be executed in relative to other systems. Higher means executed earlier.

      +
      + +
    • + +
    • + + block + + + (Proc) + + + + — +
      +

      The code you wish to be executed when the system is triggered. Can be defined by using a do end block or using { } braces.

      +
      + +
    • + +
    + + +
    + + + + +
    +
    +
    +
    +50
    +51
    +52
    +53
    +54
    +55
    +
    +
    # File 'system_manager.rb', line 50
    +
    +def initialize(name, priority: 0, &block)
    +  FelFlame::Systems.const_set(name, self)
    +  @const_name = name
    +  @priority = priority
    +  @block = block
    +end
    +
    +
    + +
    + +
    +

    Instance Attribute Details

    + + + +
    +

    + + #addition_triggersObject + + + + + +

    + + + + +
    +
    +
    +
    +14
    +15
    +16
    +
    +
    # File 'system_manager.rb', line 14
    +
    +def addition_triggers
    +  @addition_triggers ||= []
    +end
    +
    +
    + + + +
    +

    + + #const_nameObject + + + + + +

    +
    + +

    The Constant name assigned to this System

    + + +
    +
    +
    + + +
    + + + + +
    +
    +
    +
    +7
    +8
    +9
    +
    +
    # File 'system_manager.rb', line 7
    +
    +def const_name
    +  @const_name
    +end
    +
    +
    + + + +
    +

    + + #frameObject + + + + + +

    +
    + +

    How many frames need to pass before this System is executed when controlled by FelFlame::Stage

    + + +
    +
    +
    + + +
    + + + + +
    +
    +
    +
    +10
    +11
    +12
    +
    +
    # File 'system_manager.rb', line 10
    +
    +def frame
    +  @frame
    +end
    +
    +
    + + + +
    +

    + + #priorityObject + + + + + +

    +
    + +

    How early this System should be executed in a list of Systems

    + + +
    +
    +
    + + +
    + + + + +
    +
    +
    +
    +4
    +5
    +6
    +
    +
    # File 'system_manager.rb', line 4
    +
    +def priority
    +  @priority
    +end
    +
    +
    + + + +
    +

    + + #removal_triggersObject + + + + + +

    + + + + +
    +
    +
    +
    +20
    +21
    +22
    +
    +
    # File 'system_manager.rb', line 20
    +
    +def removal_triggers
    +  @removal_triggers ||= []
    +end
    +
    +
    + +
    + + +
    +

    Class Method Details

    + + +
    +

    + + .each(&block) ⇒ Enumerator + + + + + +

    +
    + +

    Iterate over all Systems, sorted by their priority. You also call other enumerable methods instead of each, such as each_with_index or select

    + + +
    +
    +
    + +

    Returns:

    +
      + +
    • + + + (Enumerator) + + + +
    • + +
    + +
    + + + + +
    +
    +
    +
    +29
    +30
    +31
    +
    +
    # File 'system_manager.rb', line 29
    +
    +def each(&block)
    +  constants.map { |sym| const_get(sym) }.sort_by(&:priority).reverse.each(&block)
    +end
    +
    +
    + +
    + +
    +

    Instance Method Details

    + + +
    +

    + + #callObject + + + + + +

    +
    + +

    Manually execute the system a single time

    + + +
    +
    +
    + + +
    + + + + +
    +
    +
    +
    +58
    +59
    +60
    +
    +
    # File 'system_manager.rb', line 58
    +
    +def call
    +  @block.call
    +end
    +
    +
    + +
    +

    + + #clear_triggers(*trigger_types, component_or_manager: nil) ⇒ Boolean + + + + + +

    +
    + +

    Removes triggers from this system

    + + +
    +
    +
    +

    Parameters:

    +
      + +
    • + + component_or_manager + + + (Component or ComponentManager) + + + (defaults to: nil) + + + — +
      +

      The object to clear triggers from. Use Nil to clear triggers from all components associated with this system.

      +
      + +
    • + +
    + +

    Returns:

    +
      + +
    • + + + (Boolean) + + + + — +
      +

      true

      +
      + +
    • + +
    + +
    + + + + +
    +
    +
    +
    +77
    +78
    +79
    +80
    +81
    +82
    +83
    +84
    +85
    +86
    +87
    +88
    +89
    +90
    +91
    +
    +
    # File 'system_manager.rb', line 77
    +
    +def clear_triggers(*trigger_types, component_or_manager: nil)
    +  trigger_types = [:addition_triggers, :removal_triggers] if trigger_types.empty?
    +  trigger_types.each do |trigger_type|
    +    if component_or_manager.nil?
    +      send(trigger_type).each do |trigger|
    +        trigger.send(trigger_type).delete self
    +      end
    +      self.addition_triggers = []
    +    else
    +      send(trigger_type).delete component_or_manager
    +      component_or_manager.send(trigger_type).delete self
    +    end
    +  end
    +  true
    +end
    +
    +
    + +
    +

    + + #redefine(&block) ⇒ Object + + + + + +

    +
    + +

    Redefine what code is executed by this System when it is called upon.

    + + +
    +
    +
    +

    Parameters:

    +
      + +
    • + + block + + + (Proc) + + + + — +
      +

      The code you wish to be executed when the system is triggered. Can be defined by using a do end block or using { } braces.

      +
      + +
    • + +
    + + +
    + + + + +
    +
    +
    +
    +70
    +71
    +72
    +
    +
    # File 'system_manager.rb', line 70
    +
    +def redefine(&block)
    +  @block = block
    +end
    +
    +
    + +
    +

    + + #stepBoolean + + + + + +

    +
    + +

    Attempt to execute the system following the frame parameter set on this system. For example if a System has its frame set to 3, it will only execute once every third frame that is called in FelFlame::Stage

    + + +
    +
    +
    + +

    Returns:

    +
      + +
    • + + + (Boolean) + + + + — +
      +

      true if the frame of the FelFlame::Stage is a multiple of this System's frame setting and this system has executed, false otherwise where the system has not executed.

      +
      + +
    • + +
    + +
    + + + + +
    +
    +
    +
    +66
    +
    +
    # File 'system_manager.rb', line 66
    +
    +def step; end
    +
    +
    + +
    +

    + + #trigger_when_added(component_or_manager) ⇒ Boolean + + + + + +

    +
    + +

    Add a component or component manager so that it triggers this system when the component or a component from the component manager is added to an entity

    + + +
    +
    +
    +

    Parameters:

    +
      + +
    • + + component_or_manager + + + (Component or ComponentManager) + + + + — +
      +

      The component or component manager to trigger this system when added

      +
      + +
    • + +
    + +

    Returns:

    +
      + +
    • + + + (Boolean) + + + + — +
      +

      true

      +
      + +
    • + +
    + +
    + + + + +
    +
    +
    +
    +96
    +97
    +98
    +99
    +100
    +
    +
    # File 'system_manager.rb', line 96
    +
    +def trigger_when_added(component_or_manager)
    +  self.addition_triggers |= [component_or_manager]
    +  component_or_manager.addition_triggers |= [self]
    +  true
    +end
    +
    +
    + +
    +

    + + #trigger_when_removed(component_or_manager) ⇒ Boolean + + + + + +

    +
    + +

    Add a component or component manager so that it triggers this system when the component or a component from the component manager is removed from an entity

    + + +
    +
    +
    +

    Parameters:

    +
      + +
    • + + component_or_manager + + + (Component or ComponentManager) + + + + — +
      +

      The component or component manager to trigger this system when removed

      +
      + +
    • + +
    + +

    Returns:

    +
      + +
    • + + + (Boolean) + + + + — +
      +

      true

      +
      + +
    • + +
    + +
    + + + + +
    +
    +
    +
    +105
    +106
    +107
    +108
    +109
    +
    +
    # File 'system_manager.rb', line 105
    +
    +def trigger_when_removed(component_or_manager)
    +  self.removal_triggers |= [component_or_manager]
    +  component_or_manager.removal_triggers |= [self]
    +  true
    +end
    +
    +
    + +
    diff --git a/docs/_index.html b/docs/_index.html index 2c11d39..cc26e86 100644 --- a/docs/_index.html +++ b/docs/_index.html @@ -164,7 +164,7 @@ diff --git a/docs/file.README.html b/docs/file.README.html index 98047e1..2de097c 100644 --- a/docs/file.README.html +++ b/docs/file.README.html @@ -98,6 +98,8 @@ I plan to eventually add functionality outside of just ECS such as loading tiles

    FF::Entities

    +

    WARNING: POTENTIALLY OUT OF DATE

    +
    FF::Ent.new(@component1, @component2)
     @entity = FF::Ent.get(entity_id)
     FF::Ent.delete(entity_id)
    @@ -111,6 +113,8 @@ I plan to eventually add functionality outside of just ECS such as loading tiles
     
     

    FF::Components

    +

    WARNING: POTENTIALLY OUT OF DATE

    +
    FF::Cmp.new('Name', 'param1', param2: 'default')
     FF::Cmp::Name.new(param1: value1)
     @component = FF::Cmp::Name.get(component_id)
    @@ -130,14 +134,29 @@ I plan to eventually add functionality outside of just ECS such as loading tiles
     
     

    FF::Systems

    -
    FF::Sys.new(name: 'Render', position: 5, frame: 1) do
    -    @component.each do
    -        # functionality
    -    end
    -end
    -FF::Sys::Render.trigger_when FF::Cmp::Health.added
    -FF::Sys::Render.trigger_when FF::Cmp::Health.removed
    -FF::Sys::Render.trigger_when FF::Cmp::Health.is_set('var')
    +
    FF::Sys.new(name: 'Render', position: 5, frame: 1) do
    +  @component.each do
    +    # functionality
    +  end
    +end
    +
    +# for all components belonging to a certain component manager
    +FF::Sys::Render.trigger_when_added FF::Cmp::Health
    +FF::Sys::Render.trigger_when_removed FF::Cmp::Health
    +FF::Sys::Render.trigger_when_is_set(FF::Cmp::Health, 'var')
    +FF::Sys::Render.clear_triggers FF::Cmp::Health # clears all triggers
    +FF::Sys::Render.clear_triggers (FF::Cmp::Health, :added)
    +FF::Sys::Render.clear_triggers (FF::Cmp::Health, :removed)
    +FF::Sys::Render.clear_triggers (FF::Cmp::Health, :is_set, 'var')
    +
    +# for specific components
    +FF::Sys::Render.trigger_when_added FF::Cmp::Health[3]
    +FF::Sys::Render.trigger_when_removed FF::Cmp::Health[3]
    +FF::Sys::Render.trigger_when_is_set(FF::Cmp::Health[3], 'var')
    +FF::Sys::Render.clear_triggers FF::Cmp::Health[3] # clears all triggers
    +FF::Sys::Render.clear_triggers (FF::Cmp::Health[3], :added)
    +FF::Sys::Render.clear_triggers (FF::Cmp::Health[3], :removed)
    +FF::Sys::Render.clear_triggers (FF::Cmp::Health[3], :is_set, 'var')
     

    FF::Scenes

    @@ -354,7 +373,7 @@ complete I will use a more verbose explanation as below to help users of the fra diff --git a/docs/index.html b/docs/index.html index 44132ea..ce3390b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -98,6 +98,8 @@ I plan to eventually add functionality outside of just ECS such as loading tiles

    FF::Entities

    +

    WARNING: POTENTIALLY OUT OF DATE

    +
    FF::Ent.new(@component1, @component2)
     @entity = FF::Ent.get(entity_id)
     FF::Ent.delete(entity_id)
    @@ -111,6 +113,8 @@ I plan to eventually add functionality outside of just ECS such as loading tiles
     
     

    FF::Components

    +

    WARNING: POTENTIALLY OUT OF DATE

    +
    FF::Cmp.new('Name', 'param1', param2: 'default')
     FF::Cmp::Name.new(param1: value1)
     @component = FF::Cmp::Name.get(component_id)
    @@ -130,14 +134,29 @@ I plan to eventually add functionality outside of just ECS such as loading tiles
     
     

    FF::Systems

    -
    FF::Sys.new(name: 'Render', position: 5, frame: 1) do
    -    @component.each do
    -        # functionality
    -    end
    -end
    -FF::Sys::Render.trigger_when FF::Cmp::Health.added
    -FF::Sys::Render.trigger_when FF::Cmp::Health.removed
    -FF::Sys::Render.trigger_when FF::Cmp::Health.is_set('var')
    +
    FF::Sys.new(name: 'Render', position: 5, frame: 1) do
    +  @component.each do
    +    # functionality
    +  end
    +end
    +
    +# for all components belonging to a certain component manager
    +FF::Sys::Render.trigger_when_added FF::Cmp::Health
    +FF::Sys::Render.trigger_when_removed FF::Cmp::Health
    +FF::Sys::Render.trigger_when_is_set(FF::Cmp::Health, 'var')
    +FF::Sys::Render.clear_triggers FF::Cmp::Health # clears all triggers
    +FF::Sys::Render.clear_triggers (FF::Cmp::Health, :added)
    +FF::Sys::Render.clear_triggers (FF::Cmp::Health, :removed)
    +FF::Sys::Render.clear_triggers (FF::Cmp::Health, :is_set, 'var')
    +
    +# for specific components
    +FF::Sys::Render.trigger_when_added FF::Cmp::Health[3]
    +FF::Sys::Render.trigger_when_removed FF::Cmp::Health[3]
    +FF::Sys::Render.trigger_when_is_set(FF::Cmp::Health[3], 'var')
    +FF::Sys::Render.clear_triggers FF::Cmp::Health[3] # clears all triggers
    +FF::Sys::Render.clear_triggers (FF::Cmp::Health[3], :added)
    +FF::Sys::Render.clear_triggers (FF::Cmp::Health[3], :removed)
    +FF::Sys::Render.clear_triggers (FF::Cmp::Health[3], :is_set, 'var')
     

    FF::Scenes

    @@ -354,7 +373,7 @@ complete I will use a more verbose explanation as below to help users of the fra diff --git a/docs/method_list.html b/docs/method_list.html index cdf6aae..6d634b1 100644 --- a/docs/method_list.html +++ b/docs/method_list.html @@ -69,6 +69,30 @@
  • +
    + #addition_triggers + FelFlame::Systems +
    +
  • + + +
  • +
    + #addition_triggers + FelFlame::Helper::ComponentManager +
    +
  • + + +
  • +
    + addition_triggers + FelFlame::Helper::ComponentManager +
    +
  • + + +
  • #attrs FelFlame::Helper::ComponentManager @@ -76,7 +100,23 @@
  • +
  • +
    + #call + FelFlame::Systems +
    +
  • + +
  • +
    + #clear_triggers + FelFlame::Systems +
    +
  • + + +
  • #components FelFlame::Entities @@ -84,6 +124,14 @@
  • +
  • +
    + #const_name + FelFlame::Systems +
    +
  • + +
  • #delete @@ -101,6 +149,14 @@
  • +
    + each + FelFlame::Systems +
    +
  • + + +
  • each FelFlame::Components @@ -108,7 +164,7 @@
  • -
  • +
  • each FelFlame::Helper::ComponentManager @@ -116,7 +172,7 @@
  • -
  • +
  • each FelFlame::Entities @@ -124,7 +180,7 @@
  • -
  • +
  • #entities FelFlame::Helper::ComponentManager @@ -132,6 +188,14 @@
  • +
  • +
    + #frame + FelFlame::Systems +
    +
  • + +
  • from_json @@ -157,6 +221,14 @@
  • +
    + #initialize + FelFlame::Systems +
    +
  • + + +
  • #initialize FelFlame::Helper::ComponentManager @@ -164,7 +236,7 @@
  • -
  • +
  • #initialize FelFlame::Entities @@ -172,7 +244,7 @@
  • -
  • +
  • new FelFlame::Components @@ -180,6 +252,46 @@
  • +
  • +
    + #priority + FelFlame::Systems +
    +
  • + + +
  • +
    + #redefine + FelFlame::Systems +
    +
  • + + +
  • +
    + #removal_triggers + FelFlame::Systems +
    +
  • + + +
  • +
    + #removal_triggers + FelFlame::Helper::ComponentManager +
    +
  • + + +
  • +
    + removal_triggers + FelFlame::Helper::ComponentManager +
    +
  • + +
  • #remove @@ -189,6 +301,14 @@
  • +
    + #step + FelFlame::Systems +
    +
  • + + +
  • #to_i FelFlame::Helper::ComponentManager @@ -196,7 +316,7 @@
  • -
  • +
  • #to_i FelFlame::Entities @@ -204,7 +324,7 @@
  • -
  • +
  • #to_json FelFlame::Helper::ComponentManager @@ -212,7 +332,7 @@
  • -
  • +
  • #to_json FelFlame::Entities @@ -220,7 +340,23 @@
  • +
  • +
    + #trigger_when_added + FelFlame::Systems +
    +
  • + +
  • +
    + #trigger_when_removed + FelFlame::Systems +
    +
  • + + +
  • #update_attrs FelFlame::Helper::ComponentManager diff --git a/docs/top-level-namespace.html b/docs/top-level-namespace.html index 804d156..d08d816 100644 --- a/docs/top-level-namespace.html +++ b/docs/top-level-namespace.html @@ -127,7 +127,7 @@
    diff --git a/entity_manager.rb b/entity_manager.rb index abbf1d0..2e1b845 100644 --- a/entity_manager.rb +++ b/entity_manager.rb @@ -40,45 +40,54 @@ class FelFlame components.each do |component_manager, component_array| component_array.each do |component_id| component_manager[component_id].entities.delete(id) + #self.remove FelFlame::Components.const_get(component_manager.name)[component_id] end end FelFlame::Entities.data[id] = nil + @components = {} @id = nil - @components = nil true end # Add any number components to the Entity. - # @param component [Component] Any number of components created from any component manager + # @param components_to_add [Component] Any number of components created from any component manager # @return [Boolean] true if component is added, false if it already is attached or no components given def add(*components_to_add) - added = false components_to_add.each do |component| if components[component.class].nil? components[component.class] = [component.id] component.entities.push id - added =true + check_systems component, :addition_triggers elsif !components[component.class].include? component.id components[component.class].push component.id component.entities.push id - added = true + check_systems component, :addition_triggers end end - added + end + + # triggers every system associated with this component's trigger + # @return [Boolean] true + # @!visibility private + def check_systems(component, trigger_type) + component_calls = component.class.send(trigger_type) + component.send(trigger_type).each do |system| + component_calls |= [system] + end + component_calls.sort_by(&:priority).reverse.each(&:call) + true end # Remove a component from the Entity - # @param component_to_remove [Component] A component created from any component manager + # @param components_to_remove [Component] A component created from any component manager # @return [Boolean] true if at least one component is removed, false if none of them were attached to the component def remove(*components_to_remove) - removed = false components_to_remove.each do |component| + check_systems component, :removal_triggers if component.entities.include? id + component.entities.delete id components[component.class].delete component.id - if component.entities.delete id - removed = true - end end - removed + true end # Export all data into a JSON String which can then be saved into a file @@ -105,11 +114,11 @@ class FelFlame data[entity_id] end - # Iterates over all entities. In general when using ECS the use of this method should never be neccassary unless you are doing something very wrong, however I will not stop you. + # Iterates over all entities. The data is compacted so that means index does not correlate to ID. # You also call other enumerable methods instead of each, such as +each_with_index+ or +select+ # @return [Enumerator] def each(&block) - data.each(&block) + data.compact.each(&block) end # Creates a new entity using the data from a JSON string diff --git a/spec/entity_manager_spec.rb b/spec/entity_manager_spec.rb index 532e583..2db4787 100644 --- a/spec/entity_manager_spec.rb +++ b/spec/entity_manager_spec.rb @@ -54,15 +54,38 @@ describe 'Entities' do expect(@ent1.components[@component_manager].include?(@cmp2.id)).to be true end + it 'can get id from to_i' do + expect(@ent0.id).to eq(@ent0.to_i) + expect(@ent1.id).to eq(@ent1.to_i) + expect(@ent2.id).to eq(@ent2.to_i) + end + it 'can have components removed' do @ent0.add @cmp0 expect(@ent0.remove @cmp0).to be true + expect(@cmp0.entities.empty?).to be true expect(@ent0.components[@component_manager].empty?).to be true end - it 'can get id from to_i' do - expect(@ent0.id).to eq(@ent0.to_i) - expect(@ent1.id).to eq(@ent1.to_i) - expect(@ent2.id).to eq(@ent2.to_i) + it 'can have many components added then removed' do + @ent0.add @cmp0, @cmp1, @cmp2 + @ent1.add @cmp0, @cmp1 + @ent2.add @cmp1, @cmp2 + expect(@ent0.components).to eq({@component_manager => [0,1,2]}) + expect(@cmp0.entities).to eq([0,1]) + expect(@cmp1.entities).to eq([0,1,2]) + expect(@cmp2.entities).to eq([0,2]) + @ent1.delete + expect(@cmp0.entities).to eq([0]) + expect(@cmp1.entities).to eq([0,2]) + expect(@cmp2.entities).to eq([0,2]) + @cmp1.delete + expect(@ent0.components).to eq({@component_manager => [0,2]}) + @component_manager.each(&:delete) + expect(@component_manager.each.to_a).to eq([]) + expect(@ent0.components).to eq({@component_manager => []}) + expect(@ent2.components).to eq({@component_manager => []}) + FelFlame::Entities.each(&:delete) + expect(FelFlame::Entities.each.to_a).to eq([]) end end diff --git a/spec/system_manager_spec.rb b/spec/system_manager_spec.rb new file mode 100644 index 0000000..c2b8cf6 --- /dev/null +++ b/spec/system_manager_spec.rb @@ -0,0 +1,185 @@ +require_relative '../felflame.rb' + +describe 'Components' do + + before :all do + @component_manager ||= FelFlame::Components.new('TestSystems', health: 10, name: 'imp') + end + + before :each do + end + + after :each do + #TODO: order breaks it + @component_manager.each(&:delete) + FelFlame::Entities.each(&:delete) + FelFlame::Systems.each(&:clear_triggers) + end + + it 'can create a system' do + FelFlame::Systems.new('Test100') do + 'Works' + end + expect(FelFlame::Systems::Test100.call).to eq('Works') + end + + it 'can be redefined' do + FelFlame::Systems.new('Test101') do + 'neat' + end + FelFlame::Systems::Test101.redefine do + 'very neat' + end + expect(FelFlame::Systems::Test101.call).to eq('very neat') + end + + it 'can iterate over the sorted systems by priority' do + FelFlame::Systems.new('Test102', priority: 1) {} + FelFlame::Systems.new('Test103', priority: 50) {} + FelFlame::Systems.new('Test104', priority: 7) {} + answer_key = ['Test103', 'Test104', 'Test102'] + test = FelFlame::Systems.each.to_a + # converts the system name to the constant, compares their positions making sure they are sorted + # higher priority should be placed first + expect(test.map(&:const_name).find_index(answer_key[0])).to be <= test.map(&:const_name).find_index(answer_key[1]) + expect(test.map(&:const_name).find_index(answer_key[0])).to be <= test.map(&:const_name).find_index(answer_key[2]) + expect(test.map(&:const_name).find_index(answer_key[1])).to be >= test.map(&:const_name).find_index(answer_key[0]) + expect(test.map(&:const_name).find_index(answer_key[1])).to be <= test.map(&:const_name).find_index(answer_key[2]) + expect(test.map(&:const_name).find_index(answer_key[2])).to be >= test.map(&:const_name).find_index(answer_key[0]) + expect(test.map(&:const_name).find_index(answer_key[2])).to be >= test.map(&:const_name).find_index(answer_key[1]) + end + + it 'can manipulate components' do + init1 = 27 + init2 = 130 + multiple = 3 + first = @component_manager.new(health: init1) + second = @component_manager.new(health: init2) + FelFlame::Systems.new('Test105') do + @component_manager.each do |component| + component.health -= multiple + end + end + FelFlame::Systems::Test105.call + expect(first.health).to eq(init1 -= multiple) + expect(second.health).to eq(init2 -= multiple) + 10.times do + FelFlame::Systems::Test105.call + end + expect(first.health).to eq(init1 - (multiple * 10)) + expect(second.health).to eq(init2 - (multiple * 10)) + end + it 'can trigger when a single Component is added' do + FelFlame::Systems.new 'Test107' do + @component_manager.each do |component| + component.health += 5 + end + end + @cmp0 = @component_manager.new + @cmp1 = @component_manager.new health: 20 + FelFlame::Systems::Test107.trigger_when_added @cmp0 + expect(@cmp0.health).to eq(10) + expect(@cmp1.health).to eq(20) + @entity0 = FelFlame::Entities.new + @entity1 = FelFlame::Entities.new @cmp0 + expect(@cmp0.health).to eq(15) + expect(@cmp1.health).to eq(25) + @entity0.add @cmp0 + expect(@cmp0.health).to eq(20) + expect(@cmp1.health).to eq(30) + end + + it 'can trigger when a Component from a manager is added' do + FelFlame::Systems.new 'Test106' do + @component_manager.each do |component| + component.health += 5 + end + end + @cmp1 = @component_manager.new + @cmp2 = @component_manager.new health: 20 + FelFlame::Systems::Test106.trigger_when_added @component_manager + expect(@cmp1.health).to eq(10) + expect(@cmp2.health).to eq(20) + @entity1 = FelFlame::Entities.new + @entity2 = FelFlame::Entities.new @cmp2 + expect(@cmp1.health).to eq(15) + expect(@cmp2.health).to eq(25) + FelFlame::Systems::Test106.trigger_when_added @cmp1 + @entity1.add @cmp1 + expect(@cmp1.health).to eq(20) + expect(@cmp2.health).to eq(30) + end + + it 'can trigger when a single Component is removed' do + FelFlame::Systems.new 'Test108' do + @component_manager.each do |component| + component.health += 5 + end + end + @cmp0 = @component_manager.new + @cmp1 = @component_manager.new health: 20 + FelFlame::Systems::Test108.trigger_when_removed @cmp0 + expect(@cmp0.health).to eq(10) + expect(@cmp1.health).to eq(20) + @entity0 = FelFlame::Entities.new + @entity1 = FelFlame::Entities.new @cmp0 + expect(@cmp0.health).to eq(10) + expect(@cmp1.health).to eq(20) + @entity1.remove @cmp0 + expect(@cmp0.health).to eq(15) + expect(@cmp1.health).to eq(25) + @entity0.add @cmp1, @cmp0 + @entity0.remove @cmp1 + expect(@cmp0.health).to eq(15) + expect(@cmp1.health).to eq(25) + end + + it 'can trigger when a Component from a manager is removed' do + FelFlame::Systems.new 'Test109' do + @component_manager.each do |component| + component.health += 5 + end + end + @cmp0 = @component_manager.new + @cmp1 = @component_manager.new health: 20 + FelFlame::Systems::Test109.trigger_when_removed @component_manager + expect(@cmp0.health).to eq(10) + expect(@cmp1.health).to eq(20) + @entity0 = FelFlame::Entities.new + @entity1 = FelFlame::Entities.new @cmp0 + expect(@cmp0.health).to eq(10) + expect(@cmp1.health).to eq(20) + @entity1.remove @cmp0 + expect(@cmp0.health).to eq(15) + expect(@cmp1.health).to eq(25) + FelFlame::Systems::Test109.trigger_when_removed @cmp1 + @entity0.add @cmp1, @cmp0 + @entity0.remove @cmp1 + expect(@cmp0.health).to eq(20) + expect(@cmp1.health).to eq(30) + end +=begin + it 'can trigger when a single Component is added' do + FelFlame::Systems.new 'Test110' do + @component_manager.each do |component| + component.health += 5 + end + end + @cmp0 = @component_manager.new + @cmp1 = @component_manager.new health: 20 + FelFlame::Systems::Test110.trigger_when_is_changed @cmp0, :name + expect(@cmp0.health).to eq(10) + expect(@cmp1.health).to eq(20) + @entity0 = FelFlame::Entities.new + @entity1 = FelFlame::Entities.new @cmp0 + expect(@cmp0.health).to eq(10) + expect(@cmp1.health).to eq(20) + @cmp0.name = 'different' + expect(@cmp0.health).to eq(15) + expect(@cmp1.health).to eq(25) + @cmp1.name = 'different' + expect(@cmp0.health).to eq(15) + expect(@cmp1.health).to eq(25) + end +=end +end diff --git a/system_manager.rb b/system_manager.rb index 14f37d4..4bd37b2 100644 --- a/system_manager.rb +++ b/system_manager.rb @@ -1,7 +1,111 @@ -#require 'app/ECS/systems/00_movement.rb' -#require 'app/ECS/systems/01_flying.rb' - class FelFlame class Systems + # How early this System should be executed in a list of Systems + attr_accessor :priority + + # The Constant name assigned to this System + attr_accessor :const_name + + # How many frames need to pass before this System is executed when controlled by {FelFlame::Stage} + attr_accessor :frame + + attr_writer :addition_triggers + + def addition_triggers + @addition_triggers ||= [] + end + + attr_writer :removal_triggers + + def removal_triggers + @removal_triggers ||= [] + end + + class <