Class: Entity
- Inherits:
-
Object
- Object
- Entity
- Extended by:
- ClassMethods
- Includes:
- ClassMethods, EntityConstants, Registerable, Serializable, Transparency
- Defined in:
- lib/game_2d/entity.rb,
lib/game_2d/entity/block.rb,
lib/game_2d/entity/pellet.rb,
lib/game_2d/entity/titanium.rb,
lib/game_2d/entity/teleporter.rb,
lib/game_2d/entity/destination.rb,
lib/game_2d/entity/owned_entity.rb
Direct Known Subclasses
Defined Under Namespace
Modules: ClassMethods Classes: Block, Destination, OwnedEntity, Pellet, Teleporter, Titanium
Constant Summary
Constants included from EntityConstants
EntityConstants::CELL_WIDTH_IN_PIXELS, EntityConstants::MAX_VELOCITY, EntityConstants::PIXEL_WIDTH, EntityConstants::WIDTH
Instance Attribute Summary collapse
-
#a ⇒ Object
Returns the value of attribute a.
-
#moving ⇒ Object
X and Y position of the top-left corner.
-
#space ⇒ Object
X and Y position of the top-left corner.
-
#x ⇒ Object
X and Y position of the top-left corner.
-
#x_vel ⇒ Object
Returns the value of attribute x_vel.
-
#y ⇒ Object
X and Y position of the top-left corner.
-
#y_vel ⇒ Object
Returns the value of attribute y_vel.
Instance Method Summary collapse
-
#accelerate(x_accel, y_accel) ⇒ Object
Apply acceleration.
- #all_state ⇒ Object
- #angle_to_vector(angle, amplitude = 1) ⇒ Object
- #as_json ⇒ Object
- #bottom_cell_y(y = @y) ⇒ Object
-
#destroy! ⇒ Object
Give this entity a chance to perform clean-up upon destruction.
-
#direction_to(other_x, other_y) ⇒ Object
Is the other entity basically above us, below us, or on the left or the right? Returns the angle we should face if we want to face that entity.
- #doomed? ⇒ Boolean
- #draw(window) ⇒ Object
-
#draw_angle ⇒ Object
0.5, 0.5, # rotate around the center 1, 1, # scaling factor @color, # modify color :add) # draw additively.
- #draw_zorder ⇒ Object
-
#drop_diagonal(x_vel, y_vel) ⇒ Object
Given a vector with a diagonal, drop the smaller component, returning a vector that is strictly either horizontal or vertical.
- #empty_above? ⇒ Boolean
- #empty_on_left? ⇒ Boolean
- #empty_on_right? ⇒ Boolean
- #empty_underneath? ⇒ Boolean
-
#entities_obstructing(new_x, new_y) ⇒ Object
Wrapper around @space.entities_overlapping Allows us to remove any entities that are transparent to us.
-
#going_past_entity(other_x, other_y) ⇒ Object
Given our current position and velocity (and only if our velocity is not on a diagonal), are we about to move past the entity at the specified coordinates? If so, returns:.
-
#grab! ⇒ Object
Entity is under direct control by a player This is transitory state (not persisted or copied).
- #grabbed? ⇒ Boolean
- #harmed_by(other) ⇒ Object
- #i_hit(other) ⇒ Object
- #image_filename ⇒ Object
-
#initialize(x = 0, y = 0, a = 0, x_vel = 0, y_vel = 0) ⇒ Entity
constructor
space: the game space x, y: position in sub-pixels of the upper-left corner a: angle, with 0 = up, 90 = right x_vel, y_vel: velocity in sub-pixels.
- #left_cell_x(x = @x) ⇒ Object
-
#move ⇒ Object
Process one tick of motion.
-
#move_x ⇒ Object
Process one tick of motion, horizontally only.
-
#move_y ⇒ Object
Process one tick of motion, vertically only.
-
#moving? ⇒ Boolean
True if we need to update this entity.
-
#next_to(angle, x = @x, y = @y) ⇒ Object
Return any entities adjacent to this one in the specified direction.
-
#occupied_cells(x = @x, y = @y) ⇒ Object
Returns an array of one, two, or four cell-coordinate tuples E.g.
- #opaque(others) ⇒ Object
-
#pixel_x ⇒ Object
X positions near this entity’s Position in pixels of the upper-left corner.
- #pixel_y ⇒ Object
- #release! ⇒ Object
- #right_cell_x(x = @x) ⇒ Object
- #should_fall? ⇒ Boolean
-
#sleep_now? ⇒ Boolean
True if this entity can go to sleep now Only called if update() fails to produce any motion Default: Sleep if we’re not moving and not falling.
- #to_s ⇒ Object
- #top_cell_y(y = @y) ⇒ Object
-
#update ⇒ Object
Handle any behavior specific to this entity Default: Accelerate downward if the subclass says we should fall.
- #update_from_json(json) ⇒ Object
-
#vector_to_angle(x_vel = @x_vel, y_vel = @y_vel) ⇒ Object
Convert x/y to an angle.
-
#wake! ⇒ Object
Notify this entity that it must take action.
-
#warp(x, y, x_vel = nil, y_vel = nil, angle = nil, moving = nil) ⇒ Object
Update position/velocity/angle data, and tell the space about it.
Methods included from ClassMethods
bottom_cell_y_at, constrain_velocity, left_cell_x_at, right_cell_x_at, top_cell_y_at
Methods included from Transparency
Methods included from Registerable
#nullsafe_registry_id, #registry_id, #registry_id=, #registry_id?, #registry_id_safe
Methods included from Serializable
#<=>, #==, as_json, #eql?, from_json, #hash, #to_json
Constructor Details
#initialize(x = 0, y = 0, a = 0, x_vel = 0, y_vel = 0) ⇒ Entity
space: the game space x, y: position in sub-pixels of the upper-left corner a: angle, with 0 = up, 90 = right x_vel, y_vel: velocity in sub-pixels
53 54 55 56 57 58 |
# File 'lib/game_2d/entity.rb', line 53 def initialize(x = 0, y = 0, a = 0, x_vel = 0, y_vel = 0) @x, @y, self.a = x, y, a self.x_vel, self.y_vel = x_vel, y_vel @moving = true @grabbed = false end |
Instance Attribute Details
#a ⇒ Object
Returns the value of attribute a.
47 48 49 |
# File 'lib/game_2d/entity.rb', line 47 def a @a end |
#moving ⇒ Object
X and Y position of the top-left corner
45 46 47 |
# File 'lib/game_2d/entity.rb', line 45 def moving @moving end |
#space ⇒ Object
X and Y position of the top-left corner
45 46 47 |
# File 'lib/game_2d/entity.rb', line 45 def space @space end |
#x ⇒ Object
X and Y position of the top-left corner
45 46 47 |
# File 'lib/game_2d/entity.rb', line 45 def x @x end |
#x_vel ⇒ Object
Returns the value of attribute x_vel.
47 48 49 |
# File 'lib/game_2d/entity.rb', line 47 def x_vel @x_vel end |
#y ⇒ Object
X and Y position of the top-left corner
45 46 47 |
# File 'lib/game_2d/entity.rb', line 45 def y @y end |
#y_vel ⇒ Object
Returns the value of attribute y_vel.
47 48 49 |
# File 'lib/game_2d/entity.rb', line 47 def y_vel @y_vel end |
Instance Method Details
#accelerate(x_accel, y_accel) ⇒ Object
Apply acceleration
118 119 120 121 |
# File 'lib/game_2d/entity.rb', line 118 def accelerate(x_accel, y_accel) self.x_vel = @x_vel + x_accel self.y_vel = @y_vel + y_accel end |
#all_state ⇒ Object
384 385 386 |
# File 'lib/game_2d/entity.rb', line 384 def all_state [registry_id_safe, @x, @y, @a, @x_vel, @y_vel, @moving] end |
#angle_to_vector(angle, amplitude = 1) ⇒ Object
251 252 253 254 255 256 257 258 259 |
# File 'lib/game_2d/entity.rb', line 251 def angle_to_vector(angle, amplitude=1) case angle % 360 when 0 then [0, -amplitude] when 90 then [amplitude, 0] when 180 then [0, amplitude] when 270 then [-amplitude, 0] else raise "Trig unimplemented" end end |
#as_json ⇒ Object
336 337 338 339 340 341 342 343 344 345 |
# File 'lib/game_2d/entity.rb', line 336 def as_json Serializable.as_json(self).merge!( :class => self.class.to_s, :registry_id => registry_id, :position => [ self.x, self.y ], :velocity => [ self.x_vel, self.y_vel ], :angle => self.a, :moving => self.moving? ) end |
#bottom_cell_y(y = @y) ⇒ Object
107 |
# File 'lib/game_2d/entity.rb', line 107 def bottom_cell_y(y = @y); bottom_cell_y_at(y); end |
#destroy! ⇒ Object
Give this entity a chance to perform clean-up upon destruction
97 |
# File 'lib/game_2d/entity.rb', line 97 def destroy!; end |
#direction_to(other_x, other_y) ⇒ Object
Is the other entity basically above us, below us, or on the left or the right? Returns the angle we should face if we want to face that entity.
285 286 287 |
# File 'lib/game_2d/entity.rb', line 285 def direction_to(other_x, other_y) vector_to_angle(*drop_diagonal(other_x - @x, other_y - @y)) end |
#doomed? ⇒ Boolean
72 |
# File 'lib/game_2d/entity.rb', line 72 def doomed?; @space.doomed?(self); end |
#draw(window) ⇒ Object
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
# File 'lib/game_2d/entity.rb', line 363 def draw(window) anim = window.animation[window.media(image_filename)] img = anim[Gosu::milliseconds / 100 % anim.size] # Entity's pixel_x/pixel_y is the location of the upper-left corner # draw_rot wants us to specify the point around which rotation occurs # That should be the center img.draw_rot( self.pixel_x + CELL_WIDTH_IN_PIXELS / 2, self.pixel_y + CELL_WIDTH_IN_PIXELS / 2, draw_zorder, draw_angle) # 0.5, 0.5, # rotate around the center # 1, 1, # scaling factor # @color, # modify color # :add) # draw additively end |
#draw_angle ⇒ Object
0.5, 0.5, # rotate around the center 1, 1, # scaling factor @color, # modify color :add) # draw additively
378 |
# File 'lib/game_2d/entity.rb', line 378 def draw_angle; a; end |
#draw_zorder ⇒ Object
361 |
# File 'lib/game_2d/entity.rb', line 361 def draw_zorder; ZOrder::Objects end |
#drop_diagonal(x_vel, y_vel) ⇒ Object
Given a vector with a diagonal, drop the smaller component, returning a vector that is strictly either horizontal or vertical.
279 280 281 |
# File 'lib/game_2d/entity.rb', line 279 def drop_diagonal(x_vel, y_vel) (y_vel.abs > x_vel.abs) ? [0, y_vel] : [x_vel, 0] end |
#empty_above? ⇒ Boolean
249 |
# File 'lib/game_2d/entity.rb', line 249 def empty_above?; opaque(next_to(0)).empty?; end |
#empty_on_left? ⇒ Boolean
247 |
# File 'lib/game_2d/entity.rb', line 247 def empty_on_left?; opaque(next_to(270)).empty?; end |
#empty_on_right? ⇒ Boolean
248 |
# File 'lib/game_2d/entity.rb', line 248 def empty_on_right?; opaque(next_to(90)).empty?; end |
#empty_underneath? ⇒ Boolean
246 |
# File 'lib/game_2d/entity.rb', line 246 def empty_underneath?; opaque(next_to(180)).empty?; end |
#entities_obstructing(new_x, new_y) ⇒ Object
Wrapper around @space.entities_overlapping Allows us to remove any entities that are transparent to us
130 131 132 133 |
# File 'lib/game_2d/entity.rb', line 130 def entities_obstructing(new_x, new_y) fail "No @space set!" unless @space opaque(@space.entities_overlapping(new_x, new_y)) end |
#going_past_entity(other_x, other_y) ⇒ Object
Given our current position and velocity (and only if our velocity is not on a diagonal), are we about to move past the entity at the specified coordinates? If so, returns:
1) The X/Y position of the empty space just past the entity. Assuming the other entity is adjacent to us, this spot touches corners with the other entity.
2) How far we’d go to reach that point.
3) How far past that spot we would go.
4) Which way we’d have to turn (delta angle) if moving around the other entity. Either +90 or -90.
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/game_2d/entity.rb', line 303 def going_past_entity(other_x, other_y) return if @x_vel == 0 && @y_vel == 0 return if @x_vel != 0 && @y_vel != 0 if @x_vel.zero? # Moving vertically. Find target height y_pos = (@y_vel > 0) ? other_y + HEIGHT : other_y - HEIGHT distance = (@y - y_pos).abs overshoot = @y_vel.abs - distance turn = if @y_vel > 0 # Going down: Turn left if it's on our right direction_to(other_x, other_y) == 90 ? -90 : 90 else # Going up: Turn right if it's on our right direction_to(other_x, other_y) == 90 ? 90 : -90 end return [[@x, y_pos], distance, overshoot, turn] if overshoot >= 0 else # Moving horizontally. Find target column x_pos = (@x_vel > 0) ? other_x + WIDTH : other_x - WIDTH distance = (@x - x_pos).abs overshoot = @x_vel.abs - distance turn = if @x_vel > 0 # Going right: Turn right if it's below us direction_to(other_x, other_y) == 180 ? 90 : -90 else # Going left: Turn left if it's below us direction_to(other_x, other_y) == 180 ? -90 : 90 end return [[x_pos, @y], distance, overshoot, turn] if overshoot >= 0 end end |
#grab! ⇒ Object
Entity is under direct control by a player This is transitory state (not persisted or copied)
92 |
# File 'lib/game_2d/entity.rb', line 92 def grab!; @grabbed = true; end |
#grabbed? ⇒ Boolean
94 |
# File 'lib/game_2d/entity.rb', line 94 def grabbed?; @grabbed; end |
#harmed_by(other) ⇒ Object
228 |
# File 'lib/game_2d/entity.rb', line 228 def harmed_by(other); end |
#i_hit(other) ⇒ Object
223 224 225 226 |
# File 'lib/game_2d/entity.rb', line 223 def i_hit(other) # TODO puts "#{self} hit #{other.inspect}" end |
#image_filename ⇒ Object
357 358 359 |
# File 'lib/game_2d/entity.rb', line 357 def image_filename raise "No image filename defined" end |
#left_cell_x(x = @x) ⇒ Object
104 |
# File 'lib/game_2d/entity.rb', line 104 def left_cell_x(x = @x); left_cell_x_at(x); end |
#move ⇒ Object
Process one tick of motion. Only called when moving? is true
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/game_2d/entity.rb', line 186 def move # Force evaluation of both update_x and update_y (no short-circuit) # If we're moving faster horizontally, do that first # Otherwise do the vertical move first moved = @space.process_moving_entity(self) do if @x_vel.abs > @y_vel.abs then move_x; move_y else move_y; move_x end end # Didn't move? Might be time to go to sleep @moving = false if !moved && sleep_now? moved end |
#move_x ⇒ Object
Process one tick of motion, horizontally only
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/game_2d/entity.rb', line 136 def move_x return if doomed? return if @x_vel.zero? new_x = @x + @x_vel impacts = entities_obstructing(new_x, @y) if impacts.empty? @x = new_x return end @x = if @x_vel > 0 # moving right # X position of leftmost candidate(s) impact_at_x = impacts.collect(&:x).min impacts.delete_if {|e| e.x > impact_at_x } impact_at_x - WIDTH else # moving left # X position of rightmost candidate(s) impact_at_x = impacts.collect(&:x).max impacts.delete_if {|e| e.x < impact_at_x } impact_at_x + WIDTH end self.x_vel = 0 i_hit(impacts) end |
#move_y ⇒ Object
Process one tick of motion, vertically only
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/game_2d/entity.rb', line 161 def move_y return if doomed? return if @y_vel.zero? new_y = @y + @y_vel impacts = entities_obstructing(@x, new_y) if impacts.empty? @y = new_y return end @y = if @y_vel > 0 # moving down # Y position of highest candidate(s) impact_at_y = impacts.collect(&:y).min impacts.delete_if {|e| e.y > impact_at_y } impact_at_y - HEIGHT else # moving up # Y position of lowest candidate(s) impact_at_y = impacts.collect(&:y).max impacts.delete_if {|e| e.y < impact_at_y } impact_at_y + HEIGHT end self.y_vel = 0 i_hit(impacts) end |
#moving? ⇒ Boolean
True if we need to update this entity
70 |
# File 'lib/game_2d/entity.rb', line 70 def moving?; @moving; end |
#next_to(angle, x = @x, y = @y) ⇒ Object
Return any entities adjacent to this one in the specified direction
231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/game_2d/entity.rb', line 231 def next_to(angle, x=@x, y=@y) points = case angle % 360 when 0 then [[x, y - 1], [x + WIDTH - 1, y - 1]] when 90 then [[x + WIDTH, y], [x + WIDTH, y + HEIGHT - 1]] when 180 then [[x, y + HEIGHT], [x + WIDTH - 1, y + HEIGHT]] when 270 then [[x - 1, y], [x - 1, y + HEIGHT - 1]] else puts "Trig unimplemented"; [] end @space.entities_at_points(points) end |
#occupied_cells(x = @x, y = @y) ⇒ Object
Returns an array of one, two, or four cell-coordinate tuples E.g. [[4, 5], [4, 6], [5, 5], [5, 6]]
111 112 113 114 115 |
# File 'lib/game_2d/entity.rb', line 111 def occupied_cells(x = @x, y = @y) x_array = (left_cell_x(x) .. right_cell_x(x)).to_a y_array = (top_cell_y(y) .. bottom_cell_y(y)).to_a x_array.product(y_array) end |
#opaque(others) ⇒ Object
123 124 125 |
# File 'lib/game_2d/entity.rb', line 123 def opaque(others) others.delete_if {|obj| obj.equal?(self) || transparent?(self, obj)} end |
#pixel_x ⇒ Object
X positions near this entity’s Position in pixels of the upper-left corner
101 |
# File 'lib/game_2d/entity.rb', line 101 def pixel_x; @x / PIXEL_WIDTH; end |
#pixel_y ⇒ Object
102 |
# File 'lib/game_2d/entity.rb', line 102 def pixel_y; @y / PIXEL_WIDTH; end |
#release! ⇒ Object
93 |
# File 'lib/game_2d/entity.rb', line 93 def release!; @grabbed = false; end |
#right_cell_x(x = @x) ⇒ Object
105 |
# File 'lib/game_2d/entity.rb', line 105 def right_cell_x(x = @x); right_cell_x_at(x); end |
#should_fall? ⇒ Boolean
81 82 83 |
# File 'lib/game_2d/entity.rb', line 81 def should_fall? raise "should_fall? undefined" end |
#sleep_now? ⇒ Boolean
True if this entity can go to sleep now Only called if update() fails to produce any motion Default: Sleep if we’re not moving and not falling
77 78 79 |
# File 'lib/game_2d/entity.rb', line 77 def sleep_now? self.x_vel == 0 && self.y_vel == 0 && !should_fall? end |
#to_s ⇒ Object
380 381 382 |
# File 'lib/game_2d/entity.rb', line 380 def to_s "#{self.class} (#{registry_id_safe}) at #{x}x#{y}" end |
#top_cell_y(y = @y) ⇒ Object
106 |
# File 'lib/game_2d/entity.rb', line 106 def top_cell_y(y = @y); top_cell_y_at(y); end |
#update ⇒ Object
Handle any behavior specific to this entity Default: Accelerate downward if the subclass says we should fall
204 205 206 207 |
# File 'lib/game_2d/entity.rb', line 204 def update accelerate(0, 1) if should_fall? move end |
#update_from_json(json) ⇒ Object
347 348 349 350 351 352 353 354 355 |
# File 'lib/game_2d/entity.rb', line 347 def update_from_json(json) new_x, new_y = json[:position] new_x_vel, new_y_vel = json[:velocity] new_angle = json[:angle] new_moving = json[:moving] warp(new_x, new_y, new_x_vel, new_y_vel, new_angle, new_moving) self end |
#vector_to_angle(x_vel = @x_vel, y_vel = @y_vel) ⇒ Object
Convert x/y to an angle
262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/game_2d/entity.rb', line 262 def vector_to_angle(x_vel=@x_vel, y_vel=@y_vel) if x_vel == 0 && y_vel == 0 return puts "Zero velocity, no angle" end if x_vel != 0 && y_vel != 0 return puts "Diagonal velocity (#{x_vel}x#{y_vel}), no angle" end if x_vel.zero? (y_vel > 0) ? 180 : 0 else (x_vel > 0) ? 90 : 270 end end |
#wake! ⇒ Object
Notify this entity that it must take action
86 87 88 |
# File 'lib/game_2d/entity.rb', line 86 def wake! @moving = true end |
#warp(x, y, x_vel = nil, y_vel = nil, angle = nil, moving = nil) ⇒ Object
Update position/velocity/angle data, and tell the space about it
210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/game_2d/entity.rb', line 210 def warp(x, y, x_vel=nil, y_vel=nil, angle=nil, moving=nil) blk = proc do @x, @y, self.x_vel, self.y_vel, self.a, @moving = (x || @x), (y || @y), (x_vel || @x_vel), (y_vel || @y_vel), (angle || @a), (moving.nil? ? @moving : moving) end if @space @space.process_moving_entity(self, &blk) else blk.call end end |