module AdventureRL
class SolidsManager
DEFAULT_SOLID_TAG = :default
DEFAULT_SETTINGS = Settings.new(
use_cache: false
)
def initialize settings = {}
@settings = DEFAULT_SETTINGS.merge settings
@quadtrees = {}
@reset_queue = {}
@cache = {}
@use_cache = @settings.get :use_cache
end
# Add one (or multiple) object(s)
# with one (or multiple) solid_tag(s).
def add_object object, solid_tag = DEFAULT_SOLID_TAG
objects = [object].flatten
solid_tags = [solid_tag].flatten
solid_tags.each do |tag|
if (@quadtrees[tag])
@quadtrees[tag].add_object objects
else
@quadtrees[tag] = Quadtree.new objects: objects
end
end
end
alias_method :add, :add_object
def remove_object object, solid_tag = DEFAULT_SOLID_TAG
objects = [object].flatten
solid_tags = [solid_tag].flatten
objects.each do |obj|
@cache.delete obj
end
get_quadtrees_for(solid_tags).each do |quadtree|
quadtree.remove_object objects
end
end
alias_method :remove, :remove_object
def remove_object_from_all_quadtrees object
objects = [object].flatten
objects.each do |obj|
@cache.delete obj
end
@quadtrees.values.flatten.each do |quadtree|
quadtree.remove_object objects
end
end
alias_method :remove_from_all_quadtrees, :remove_object_from_all_quadtrees
# Returns true if the given object (or multiple objects),
# collide with any other objects with a mutual solid_tag.
def collides? object, solid_tag = DEFAULT_SOLID_TAG
objects = [object].flatten
solid_tags = [solid_tag].flatten
return objects.any? do |obj|
handle_collides_cache_for obj, solid_tags
next @cache[obj][:collides?]
end if (@use_cache)
return objects.any? do |obj|
next get_quadtrees_for(solid_tags).any? do |quadtree|
next quadtree.collides?(obj)
end
end
end
# Returns all objects colliding with object(s).
def get_colliding_objects object, solid_tag = DEFAULT_SOLID_TAG
objects = [object].flatten
solid_tags = [solid_tag].flatten
return objects.map do |obj|
handle_colliding_objects_cache_for obj, solid_tags
next @cache[obj][:colliding_objects]
end .flatten if (@use_cache)
return objects.map do |obj|
next get_quadtrees_for(solid_tags).map do |quadtree|
next quadtree.get_colliding_objects(obj)
end
end .flatten
end
# Pass an object (or multiple), to queue it/them for
# resetting next update.
def reset_object object, solid_tag = DEFAULT_SOLID_TAG
objects = [object].flatten
solid_tags = [solid_tag].flatten
solid_tags.each do |tag|
@reset_queue[tag] ||= []
@reset_queue[tag].concat objects
end
end
# Resets for every object in @reset_queue.
def reset
@reset_queue.each do |solid_tag, objects|
@quadtrees.map do |quadtree_tag, quadtree|
next quadtree if (solid_tag == quadtree_tag)
next nil
end .compact.each do |quadtree|
quadtree.reset_object objects
end
@reset_queue[solid_tag] = []
end
end
def reset_cache_for object
@collides_cache.delete object
@colliding_objects_cache.delete object
end
# Called once every frame by Window.
def update
reset
end
private
def get_quadtrees_for solid_tag = DEFAULT_SOLID_TAG
solid_tags = [solid_tag].flatten
return @quadtrees.map do |quadtree_tag, quadtree|
next quadtree if (solid_tags.include?(quadtree_tag))
next nil
end .compact
end
def handle_collides_cache_for object, solid_tags
cached = @cache[object]
update_collides_cache_for object, solid_tags unless (
# TODO: Remove cache or improve, it can break stuff
@use_cache &&
cached &&
(cached[:position] == object.get_position) &&
(cached[:size] == object.get_size)
)
end
def update_collides_cache_for object, solid_tags
@cache[object] ||= {}
@cache[object][:position] = object.get_position.dup
@cache[object][:size] = object.get_size.dup
@cache[object][:collides?] = get_quadtrees_for(solid_tags).any? do |quadtree|
next quadtree.collides?(object)
end
end
def handle_colliding_objects_cache_for object, solid_tags
cached = @cache[object]
update_colliding_objects_cache_for object, solid_tags unless (
# TODO: Remove cache or improve, it can break stuff
@use_cache &&
cached &&
(cached[:position] == object.get_position) &&
(cached[:size] == object.get_size)
)
end
def update_colliding_objects_cache_for object, solid_tags
@cache[object] ||= {}
@cache[object][:position] = object.get_position.dup
@cache[object][:size] = object.get_size.dup
@cache[object][:colliding_objects] = get_quadtrees_for(solid_tags).map do |quadtree|
next quadtree.get_colliding_objects(object)
end .flatten
end
end
end