Module: AdventureRL::Modifiers::Solid

Defined in:
lib/AdventureRL/Modifiers/Solid.rb

Overview

This module is supposed to be included in Mask child classes. It will tag that Mask instance as 'solid', and check collision with other solid Masks when calling #move_by. You can give it a specific solid_tag, which can be passed as the :solid_tag key’s value upon initialization. Multiple solid tags may be passed as an array. Solid Masks will only collide with other Solid Masks that have a mutual solid tag. The default solid tag is :default.

Constant Summary collapse

DEFAULT_SOLID_SETTINGS =

NOTE: possible :precision_over_performance values:

:low (or anything other than the higher values)

Lowest precision, highest performance. Never check every pixel between previous and new positions. If there is collision at new position, jump to previous position and return. The larger the movement steps, the more distance there will be to the colliding object.

  • – __CANNOT__ fully close gaps to Solids.

  • – __CAN__ phase through Solids at high speeds (especially when it lags).

  • – __CAN__ get stuck in place temporarily when moving on both axes but only colliding on one of them.

  • + Only __one collision check__ per call to #move_by, highest performance.

:medium

Medium precision, medium (varying) performance. Only checks every pixel in path if the expected destination collides. Even then, collision checking is used somewhat sparingly.

  • – __CAN__ phase through Solids at high speeds (especially when it lags).

  • – __CAN__ get stuck in place temporarily when moving on both axes but only colliding on one of them.

  • + __CAN__ almost fully close gaps to Solids (no sub-pixel collision checks).

:high

High precision, low to medium (varying) performance. Only checks every pixel in path if the expected destination collides. When checking every pixel in path, check both axes separately, to improve precision.

  • – __CAN__ phase through Solids at high speeds (especially when it lags).

  • + __CANNOT__ get stuck in place temporarily when moving on both axes but only colliding on one of them.

  • + __CAN__ fully close gaps to Solids.

:highest

Highest precision, least performance. Always check every pixel between previous and new positions. Depending on the amount of (moving) Solid objects on screen,

  • – Depending on the amount of (moving) Solids, this can get very laggy at high speeds => lag produces larger steps (usually), because of Deltatime => larger steps produce more collision checks and more lag.

  • + __CANNOT__ phase through Solids, no matter what the speed is.

  • + __CANNOT__ get stuck in place temporarily when moving on both axes but only colliding on one of them.

  • + __CAN__ fully close gaps to Solids.

Settings.new(
  solid_tag:                  SolidsManager::DEFAULT_SOLID_TAG,
  solid_tag_collides_with:    nil,
  precision_over_performance: :medium,
  static:                     false,
  auto_update:                false
)

Instance Method Summary collapse

Instance Method Details

#add_to_solids_manager(solids_manager) ⇒ Object



73
74
75
76
77
78
79
80
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 73

def add_to_solids_manager solids_manager
  Helpers::Error.error(
    "Expected argument to be a SolidsManager, but got",
    "'#{solids_manager.inspect}:#{solids_manager.class.name}`."
  )  unless (solids_manager.is_a? SolidsManager)
  @solids_manager = solids_manager
  @solids_manager.add_object self, get_solid_tags
end

#get_colliding_objectsObject

Returns all currently colliding objects (if any). TODO: Write documentation for callback method.



166
167
168
169
170
171
172
173
174
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 166

def get_colliding_objects
  if (@solids_manager)
    colliding_objects = @solids_manager.get_colliding_objects(self, get_solid_tags_collides_with)
  else
    colliding_objects = []
  end
  is_colliding_with_objects colliding_objects  if (colliding_objects.any? && methods.include?(:is_colliding_with_objects))
  return colliding_objects
end

#get_solid_tagsObject

Returns this Mask’s solid tags, which other Masks use to check collision against this Mask.



199
200
201
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 199

def get_solid_tags
  return @solid_tags
end

#get_solid_tags_collides_withObject

Returns the solid tags, which this Mask uses to check collision against other Masks.



205
206
207
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 205

def get_solid_tags_collides_with
  return @solid_tags_collides_with || @solid_tags
end

#in_collision?Boolean

Returns true if this Mask is currently in collision with another solid Mask which has a mutual solid tag. TODO: Write documentation for callback method.

Returns:

  • (Boolean)


154
155
156
157
158
159
160
161
162
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 154

def in_collision?
  if (@solids_manager)
    is_colliding = @solids_manager.collides?(self, get_solid_tags_collides_with)
  else
    is_colliding = false
  end
  is_colliding  if (is_colliding && methods.include?(:is_colliding))
  return is_colliding
end

#initialize(settings = {}) ⇒ Object

Additionally to the Mask’s settings Hash or Settings instance, you may pass the extra key :solid_tag, to define a custom solid tag (or multiple solid tags) upon initialization. They are used for collision checking with other Solid Mask objects that have a mutual solid tag.



63
64
65
66
67
68
69
70
71
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 63

def initialize settings = {}
  @settings                   = DEFAULT_SOLID_SETTINGS.merge settings
  @solid_tags                 = [@settings.get(:solid_tag)].flatten.sort
  @solid_tags_collides_with   = [@settings.get(:solid_tag_collides_with) || @solid_tags].flatten.sort
  @solid_static               = @settings.get :static  # Basically disables #move_by
  @precision_over_performance = @settings.get :precision_over_performance
  assign_to_solids_manager  if (@settings.get :auto_update)
  super @settings
end

#is_static?Boolean

Returns true if this is a static solid Mask, which means it cannot be moved with #move_by.

Returns:

  • (Boolean)


183
184
185
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 183

def is_static?
  return !!@solid_static
end

#make_staticObject

Makes this Solid Mask static.



177
178
179
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 177

def make_static
  @solid_static = true
end

#move_by(*args) ⇒ Object

Overwrite #move_by method, so that collision checking with other objects with a mutual solid tag is done, and movement prevented if necessary.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 103

def move_by *args
  return false  if     (is_static?)
  return super  unless (@solids_manager)

  @real_point = nil
  previous_position = get_position.dup
  incremental_position = parse_position(*args)
  expected_position = {
    x: (previous_position[:x] + (incremental_position.key?(:x) ? incremental_position[:x] : 0)),
    y: (previous_position[:y] + (incremental_position.key?(:y) ? incremental_position[:y] : 0))
  }

  # NOTE:
  # This is a bit of a hacky workaround for some
  # weird Pusher behavior with Velocity and Gravity.
  previous_precision_over_performance = @precision_over_performance.dup
  opts = args.last.is_a?(Hash) ? args.last : nil

  @precision_over_performance = opts[:precision_over_performance]  if (opts && opts.key?(:precision_over_performance))

  if ([:highest].include? @precision_over_performance)
    move_by_steps incremental_position
  else
    @position[:x] += incremental_position[:x]  if (incremental_position.key? :x)
    @position[:y] += incremental_position[:y]  if (incremental_position.key? :y)

    # TODO
    #puts 'PUSHING'  if (is_a?(Player) && opts && opts[:pushed_by_pusher])

    unless (move_by_handle_collision_with_previous_position previous_position)
      move_by_steps incremental_position  if ([:medium, :high].include? @precision_over_performance)
    end
  end

  @precision_over_performance = previous_precision_over_performance
  @solids_manager.reset_object self, get_solid_tags  unless (@position == previous_position)
  return @position == expected_position
end

#move_to(*args) ⇒ Object

Overwrite the #move_to method, so we can reset the object for the solids_manager if necessary.



144
145
146
147
148
149
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 144

def move_to *args
  previous_position = get_position.dup
  super
  return  unless (@solids_manager)
  @solids_manager.reset_object self, get_solid_tags  if (@position != previous_position)
end

#remove_from_solids_managerObject

When it is removed, also remove it from the SolidsManager. TODO: Do this properly.



96
97
98
99
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 96

def remove_from_solids_manager
  #@solids_manager.remove_object self, [get_solid_tags, get_solid_tags_collides_with].flatten  if (@solids_manager)
  @solids_manager.remove_object_from_all_quadtrees self  if (@solids_manager)
end

#removedObject

This method is called when this object is removed from an Inventory.



90
91
92
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 90

def removed
  remove_from_solids_manager
end

#set_layer(layer) ⇒ Object

Overwrite #set_layer method, so we can get the SolidsManager from the Layer, if it has one.



84
85
86
87
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 84

def set_layer layer
  super
  assign_to_solids_manager  if (@settings.get :auto_update)
end

#set_solid_tags(*new_solid_tags) ⇒ Object



187
188
189
190
191
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 187

def set_solid_tags *new_solid_tags
  @solids_manager.remove_object self, get_solid_tags  if (@solids_manager)
  @solid_tags = [new_solid_tags].flatten.compact
  @solids_manager.add_object self,    get_solid_tags  if (@solids_manager)
end

#set_solid_tags_collides_with(*new_solid_tags_collides_with) ⇒ Object



193
194
195
# File 'lib/AdventureRL/Modifiers/Solid.rb', line 193

def set_solid_tags_collides_with *new_solid_tags_collides_with
  @solid_tags_collides_with = [new_solid_tags_collides_with].flatten.compact
end