diff options
| -rw-r--r-- | component_manager.rb | 128 | ||||
| -rw-r--r-- | entity_manager.rb | 136 | ||||
| -rw-r--r-- | felflame.rb | 72 | ||||
| -rw-r--r-- | helpers/01_component.rb | 59 |
4 files changed, 233 insertions, 162 deletions
diff --git a/component_manager.rb b/component_manager.rb index 33cb931..16d6300 100644 --- a/component_manager.rb +++ b/component_manager.rb @@ -6,24 +6,14 @@ class FelFlame class Components @component_map = [] class <<self - def entity_destroyed(entity_id) - #TODO: make it 'search by entity' to remove - @component_map.delete(entity_id) - constants.each do |component| #TODO: change delete to remove - component.delete(entity_id) unless (component.signature & FelFlame::Entity.signatures[entity_id]).zero? - end - end - - #def entity_created(entity_id) - # #TODO: probably delete this, I dont think its needed in the new system - # constants.each do |component| - # const_get(component.to_s).add(entity_id) unless (const_get(component.to_s).signature & FelFlame::Entity.signatures[entity_id]).zero? - # end - # @component_map[entity_id] = [] - #end - + include Enumerable + # Creates a new component manager. + # + # @param component_name [String] Name of your new component manager. Must be stylized in the format of constants in Ruby + # @param attrs [:Symbols] New components made with this manager will include these symbols as accessors, the values of these accessors will default to nil + # @param attrs_with_defaults [Keywords] New components made with this manager will include these keywords as accessors, their defaults set to the values given to the keywords def new(component_name, *attrs, **attrs_with_defaults) - const_set(component_name, Class.new(FelFlame::Helper::BaseComponent) {}) + const_set(component_name, Class.new(FelFlame::Helper::ComponentManagerTemplate) {}) attrs.each do |attr| FelFlame::Components.const_get(component_name).attr_accessor attr end @@ -36,6 +26,110 @@ class FelFlame end end end + + # Iterate over all existing component managers. You also call other enumerable methods instead of each, such as `each_with_index` or `select` + # @return [Enumerator] + def each(&block) + constants.each(&block) + end + end + end + # Namespace for helper functions and inheritance classes + class Helper + # Component Managers are what is used to create individual components which can be attached to entities. + # When a Component is created from a Component Manager that has accessors given to it, you can set or get the values of those accessors using standard ruby message sending (e.g `@component.var = 5`), or by using the {#attrs} and {#update_attrs} methods instead. + class ComponentManagerTemplate + # Holds the {id unique ID} of a component. The {id ID} is only unique within the scope of the component manager it was created from. + attr_accessor :id + + class <<self + # @return [Array] Array of all Components that belong to a given component manager + # @!visibility private + def data + @data ||= [] + end + + # Gets a Component from the given {id unique ID}. Usage is simular to how an Array lookup works. + # @param component_id [Integer] + # @return [Component] Returns the Component that uses the given unique {id ID}, nil if there is no Component associated with the given {id ID} + def [](component_id) + data[component_id] + end + + # Creates a new component and sets the values of the attributes given to it. If an attritbute is not passed then it will remain as the default. + def new(**attrs) + new_component = super + + # Generate ID + new_id = self.data.find_index { |i| i.nil? } + new_id = self.data.size if new_id.nil? + new_component.id = new_id + + # Fill params + attrs.each do |key, value| + new_component.send "#{key}=", value + end + + # Save Component + data[new_id] = new_component + end + + # Iterates over all components within the component manager + # @return [Enumerator] + def each + data.each do |component| + yield component + end + end + end + + # An alias for the ID reader + # @return [Integer] + def to_i + id + end + + # A list of components that are linked to the component + # @return [Array] + def linked_entities + @linked_entities ||= [] + end + + # Update attribute values using a hash or keywords. + # @return Hash of updated attributes + def update_attrs(**opts) + opts.each do |key, value| + send "#{key}=", value + end + end + + # 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 + linked_entities.each do |entity_id| + FelFlame::Entities[entity_id].remove self + end + self.class.data[id] = nil + @linked_entities = nil + instance_variables.each do |var| + instance_variable_set(var, nil) + end + true + end + + # @return [Hash] Returns a hash, where all the keys are attributes linked to their respective values. + def attrs + instance_variables.each_with_object({}) do |key, final| + final[key.to_s.delete_prefix('@').to_sym] = instance_variable_get(key) + end + end + + # Export all data into a JSON String, which could then later be loaded or saved to a file + # TODO: This function is not yet complete + # @return [String] a JSON formatted String + def to_json + # should return a json or hash of all data in this component + end end end end diff --git a/entity_manager.rb b/entity_manager.rb index 177ce1a..f699c95 100644 --- a/entity_manager.rb +++ b/entity_manager.rb @@ -1,64 +1,108 @@ class FelFlame class Entities + # Holds the unique ID of this entity + # @return [Integer] attr_accessor :id - def initialize(*signature) - final_signature = 0 - signature.each do |sig| - final_signature += sig + # Creating a new component + # @param components [Component] Can be any number of components, identical duplicated will be automatically purged however different components from the same component manager are allowed. + def initialize(*components) + # Assign new unique ID + new_id = self.class.data.find_index { |i| i.nil? } + new_id = self.class.data.size if new_id.nil? + self.id = new_id + + # Add each component + components.uniq.each do |component| + add component end - @id = Entities.generate_new_id - self.class.all.push self - self.class.signatures.push final_signature - Components.entity_created(@id) + self.class.data[id] = self end - class <<self - # All entities that exist - def all - @all ||= [] + # A hash that uses component manager constant names as keys, and where the values of those keys are arrays that contain the IDs of components attached to this entity. + # @return [Hash] + def components + @components ||= {} + end + + # An alias for the {#id ID reader} + # @return [Integer] + def to_i + id + end + + # Removes this Entity from the list and purges all references to this Entity from other Components, as well as its {id ID} and data. + # @return [Boolean] true. + def delete + components.each do |component_manager, component_array| + component_array.each do |component_id| + FelFlame.const_get( + component_manager.to_s.delete_prefix('FelFlame::') + )[component_id].linked_entities.delete(id) + end end + FelFlame::Entities.data[id] = nil + @id = nil + @components = nil + true + end - def id_queue - @id_queue ||= [] + # Returns true when added, or false if it already belongs to the Entity + # Add a component to the Entity + # @param component [Component] A component created from any component manager + # @return [Boolean] true if component is added, false if it already is attached + def add component + if components[component.class.to_s.to_sym].nil? + components[component.class.to_s.to_sym] = [component.id] + component.linked_entities.push id + true + elsif !components[component.class.to_s.to_sym].include? component.id + components[component.class.to_s.to_sym].push component.id + component.linked_entities.push id + true + else + false end + end - def generate_new_id - if id_queue.empty? - all.size - else - id_queue.shift - end + # Remove a component from the Entity + # @param component [Component] A component created from any component manager + # @return [Boolean] true if component is removed, false if it wasnt attached to component + def remove component + components[component.class.to_s.to_sym].delete component.id + if component.linked_entities.delete id + true + else + false + end + end + + # Export all data into a JSON String which can then be saved into a file + # TODO: This function is not yet complete + # @return [String] A JSON formatted String + def to_json + end + + class <<self + include Enumerable + # @return [Array] Array of all Entities that exist + # @!visibility private + def data + @data ||= [] end - # What components a given entity uses - def signatures - @signatures ||= [] + # Gets an Entity from the given unique ID. Usage is simular to how an Array lookup works + # @param entity_id [Integer] + # @return [Entity] returns the Entity that uses the given unique ID, nil if there is no Entity associated with the given ID + def [](entity_id) + data[entity_id] end - def destroy_entity(entity_id) - if all[entity_id].nil? - puts 'Entity can not be destroyed, id out of bounds' - elsif entity_id < all.size - 1 - Components.constants.each do |constant| - unless (signatures[entity_id] & Components::const_get(constant).id).zero? - Components::const_get(constant).delete(entity_id) - end - end - all[entity_id] = nil - signatures[entity_id] = nil - id_queue.push entity_id - elsif entity_id == all.size - 1 - Components.constants.each do |constant| - unless (signatures[entity_id] & Components::const_get(constant).id).zero? - Components::const_get(constant).delete(entity_id) - end - end - all.pop - signatures.pop - else - puts 'Unknown error with destroy_entity, entity not destroyed' - 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. + # You also call other enumerable methods instead of each, such as `each_with_index` or `select` + # @return [Enumerator] + def each(&block) + data.each(&block) end end end diff --git a/felflame.rb b/felflame.rb index 89c7f70..981211d 100644 --- a/felflame.rb +++ b/felflame.rb @@ -4,48 +4,38 @@ require_relative './system_manager.rb' require_relative './scene_manager.rb' require_relative './stage_manager.rb' -class FelFlame - class <<self - def dump - end - - def load - end - - def Ent - FelFlame::Entities - end - - def Cmp - FelFlame::Components - end - - def Sys - FelFlame::Systems - end - - def Scn - FelFlame::Scene - end +require_relative './helpers/00_tileset.rb' +require_relative './helpers/01_component.rb' - def Stg - FelFlame::Stage - end - - def const_missing(name) - FelFlame.send name.to_s - end - - def method_missing(name) - if name[0] == name[0].upcase - # TODO throw NameError - super - else - super - end - end +class FelFlame + # Creates and manages Entities. Allows accessing Entities using their {FelFlame::Entities#id ID} + # + # TODO: Improve Entity overview + class Entities + end + # Creates component managers and allows accessing them them under the {FelFlame::Components} namespace as Constants + # + # To see how component managers are used please look at the {FelFlame::Helper::ComponentManagerTemplate} documentation. + # + # TODO: Improve Component overview + class Components + end + # Creates an manages Systems. + # + # TODO: Improve System overview + class Systems end end -FF = FelFlame # TODO Maybe find better solution? - +# An alias for {FelFlame} +FF = FelFlame +# An alias for {FelFlame::Entities} +FF::Ent = FelFlame::Entities +# An alias for {FelFlame::Components} +FF::Cmp = FelFlame::Components +# An alias for {FelFlame::Systems} +FF::Sys = FelFlame::Systems +# An alias for {FelFlame::Scenes} +#FF::Sce = FelFlame::Scenes +# An alias for {FelFlame::Stage} +#FF::Stg = FelFlame::Stage diff --git a/helpers/01_component.rb b/helpers/01_component.rb index 580b5d8..f68a233 100644 --- a/helpers/01_component.rb +++ b/helpers/01_component.rb @@ -1,64 +1,7 @@ class FelFlame class Helper - class BaseComponent - class <<self - def signature - @signature ||= FelFlame::Signature.create_new_signature\ - FelFlame::Helper::ComponentHelper.underscore(ancestors[0].name.split('::').last) - end - - def data - @data ||= [] - end - - def new(**opts) - new_component = super - - # Generate ID - new_id = self.data.find_index { |i| i.nil? } - new_id = self.data.size if new_id.nil? - new_component.id = new_id - - # Fill params - opts.each do |key, value| - new_component.send "#{key}=", value - end - - # Save Component - data[new_id] = new_component - end - - #def add(entity_id) - # data[entity_id] = new - #end - - #def delete(entity_id) - # data.delete entity_id - #end - end - - def set(**opts) - opts.each do |key, value| - send "#{key}=", value - end - end - - #def create_data(name, default = nil) - # #TODO: fill this out - #end - - def get #TODO: maybe optimize removing the @ symbol - instance_variables.each_with_object({}) do |key, final| - final[key.to_s.delete_prefix('@').to_sym] = instance_variable_get(key) - end - end - - def dump #TODO: Needs to get id and stuff? - # should return a json or hash of all data in this component - end - end - class Level < FelFlame::Helper::BaseComponent + class Level < FelFlame::Helper::ComponentManagerTemplate class <<self def data @data ||= { add: [], remove: [], grid: FelFlame::Helper::Array2D.new } |
