Refactor character and enemy AI movement logic; implement delta time for smoother updates and replace object (a reserved keyword) with prop.
This commit is contained in:
+12
-9
@@ -1,7 +1,7 @@
|
|||||||
class Character
|
class Character
|
||||||
attr_reader :world_x, :world_y
|
attr_reader :world_x, :world_y
|
||||||
|
|
||||||
SPEED = 3
|
SPEED = 180.0
|
||||||
SIZE = [25, 30]
|
SIZE = [25, 30]
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@@ -56,7 +56,7 @@ class Character
|
|||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update(dt)
|
||||||
dx = 0.0
|
dx = 0.0
|
||||||
dy = 0.0
|
dy = 0.0
|
||||||
|
|
||||||
@@ -89,23 +89,26 @@ class Character
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Apply speed
|
# Apply speed
|
||||||
dx *= SPEED
|
dx *= SPEED * dt
|
||||||
dy *= SPEED
|
dy *= SPEED * dt
|
||||||
|
|
||||||
steps = SPEED.ceil
|
distance = Math.sqrt(dx * dx + dy * dy)
|
||||||
step_dx = dx / steps.to_f
|
steps = (distance / 3.0).ceil
|
||||||
step_dy = dy / steps.to_f
|
steps = 1 if steps < 1
|
||||||
|
|
||||||
|
step_dx = dx / steps
|
||||||
|
step_dy = dy / steps
|
||||||
|
|
||||||
steps.times do
|
steps.times do
|
||||||
# X axis
|
# X axis
|
||||||
@world_x += step_dx
|
@world_x += step_dx
|
||||||
if ($bus.get_all(:collides?, rect) & [:wall, :object])&.any?
|
if ($bus.get_all(:collides?, rect) & [:wall, :prop])&.any?
|
||||||
@world_x -= step_dx
|
@world_x -= step_dx
|
||||||
end
|
end
|
||||||
|
|
||||||
# Y axis
|
# Y axis
|
||||||
@world_y += step_dy
|
@world_y += step_dy
|
||||||
if ($bus.get_all(:collides?, rect) & [:wall, :object])&.any?
|
if ($bus.get_all(:collides?, rect) & [:wall, :prop])&.any?
|
||||||
@world_y -= step_dy
|
@world_y -= step_dy
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+15
-15
@@ -1,6 +1,6 @@
|
|||||||
class EnemyAI
|
class EnemyAI
|
||||||
AGGRO_RADIUS = 2000
|
AGGRO_RADIUS = 2000
|
||||||
SPEED = 2
|
SPEED = 120.0
|
||||||
CORNER_OFFSET = 35 # how far from tile center to place corner waypoint
|
CORNER_OFFSET = 35 # how far from tile center to place corner waypoint
|
||||||
|
|
||||||
def initialize(enemy, type)
|
def initialize(enemy, type)
|
||||||
@@ -12,7 +12,7 @@ class EnemyAI
|
|||||||
@avoiding_obstacle = false
|
@avoiding_obstacle = false
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(player_x, player_y)
|
def update(player_x, player_y, dt)
|
||||||
distance = Gosu.distance(@enemy.x, @enemy.y, player_x, player_y)
|
distance = Gosu.distance(@enemy.x, @enemy.y, player_x, player_y)
|
||||||
return unless distance < AGGRO_RADIUS
|
return unless distance < AGGRO_RADIUS
|
||||||
|
|
||||||
@@ -21,14 +21,14 @@ class EnemyAI
|
|||||||
|
|
||||||
return if $bus.get_all(:collides?, @enemy.rect)&.include?(:character)
|
return if $bus.get_all(:collides?, @enemy.rect)&.include?(:character)
|
||||||
|
|
||||||
object_in_tile = $bus.get(:object_at, enemy_tile[0], enemy_tile[1])
|
prop_in_tile = $bus.get(:prop_at, enemy_tile[0], enemy_tile[1])
|
||||||
|
|
||||||
if player_tile == enemy_tile
|
if player_tile == enemy_tile
|
||||||
los_blocked = !object_in_tile.nil? && !(
|
los_blocked = !prop_in_tile.nil? && !(
|
||||||
line_of_sight?(@enemy.x - @enemy.w / 2, @enemy.y - @enemy.h / 2, player_x, player_y, object_in_tile.rect) &&
|
line_of_sight?(@enemy.x - @enemy.w / 2, @enemy.y - @enemy.h / 2, player_x, player_y, prop_in_tile.rect) &&
|
||||||
line_of_sight?(@enemy.x + @enemy.w / 2, @enemy.y + @enemy.h / 2, player_x, player_y, object_in_tile.rect) &&
|
line_of_sight?(@enemy.x + @enemy.w / 2, @enemy.y + @enemy.h / 2, player_x, player_y, prop_in_tile.rect) &&
|
||||||
line_of_sight?(@enemy.x - @enemy.w / 2, @enemy.y + @enemy.h / 2, player_x, player_y, object_in_tile.rect) &&
|
line_of_sight?(@enemy.x - @enemy.w / 2, @enemy.y + @enemy.h / 2, player_x, player_y, prop_in_tile.rect) &&
|
||||||
line_of_sight?(@enemy.x + @enemy.w / 2, @enemy.y - @enemy.h / 2, player_x, player_y, object_in_tile.rect)
|
line_of_sight?(@enemy.x + @enemy.w / 2, @enemy.y - @enemy.h / 2, player_x, player_y, prop_in_tile.rect)
|
||||||
)
|
)
|
||||||
|
|
||||||
if los_blocked
|
if los_blocked
|
||||||
@@ -48,7 +48,7 @@ class EnemyAI
|
|||||||
@avoiding_obstacle = true
|
@avoiding_obstacle = true
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
move_towards(player_x, player_y)
|
move_towards(player_x, player_y, dt)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -56,7 +56,7 @@ class EnemyAI
|
|||||||
if @waypoints.empty? || @last_player_tile != player_tile
|
if @waypoints.empty? || @last_player_tile != player_tile
|
||||||
@last_player_tile = player_tile
|
@last_player_tile = player_tile
|
||||||
@tile_path = $bus.get(:maze_solve, enemy_tile[0], enemy_tile[1], player_tile[0], player_tile[1]) || []
|
@tile_path = $bus.get(:maze_solve, enemy_tile[0], enemy_tile[1], player_tile[0], player_tile[1]) || []
|
||||||
if $bus.get(:object_at, enemy_tile[0], enemy_tile[1]).nil?
|
if $bus.get(:prop_at, enemy_tile[0], enemy_tile[1]).nil?
|
||||||
@tile_path.shift
|
@tile_path.shift
|
||||||
end
|
end
|
||||||
@waypoints = build_waypoints(@tile_path)
|
@waypoints = build_waypoints(@tile_path)
|
||||||
@@ -72,7 +72,7 @@ class EnemyAI
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
move_towards(target_x, target_y)
|
move_towards(target_x, target_y, dt)
|
||||||
end
|
end
|
||||||
|
|
||||||
def draw
|
def draw
|
||||||
@@ -92,7 +92,7 @@ class EnemyAI
|
|||||||
center_x = tx * 120 + 90
|
center_x = tx * 120 + 90
|
||||||
center_y = ty * 120 + 90
|
center_y = ty * 120 + 90
|
||||||
|
|
||||||
unless $bus.get(:object_at, tx, ty).nil?
|
unless $bus.get(:prop_at, tx, ty).nil?
|
||||||
next_tile = tile_path[i + 1]
|
next_tile = tile_path[i + 1]
|
||||||
prev_tile = i > 0 ? tile_path[i - 1] : nil
|
prev_tile = i > 0 ? tile_path[i - 1] : nil
|
||||||
|
|
||||||
@@ -126,10 +126,10 @@ class EnemyAI
|
|||||||
waypoints
|
waypoints
|
||||||
end
|
end
|
||||||
|
|
||||||
def move_towards(target_x, target_y)
|
def move_towards(target_x, target_y, dt)
|
||||||
angle = Gosu.angle(@enemy.x, @enemy.y, target_x, target_y)
|
angle = Gosu.angle(@enemy.x, @enemy.y, target_x, target_y)
|
||||||
dx = Gosu.offset_x(angle, SPEED)
|
dx = Gosu.offset_x(angle, SPEED * dt)
|
||||||
dy = Gosu.offset_y(angle, SPEED)
|
dy = Gosu.offset_y(angle, SPEED * dt)
|
||||||
|
|
||||||
@enemy.x += dx
|
@enemy.x += dx
|
||||||
@enemy.y += dy
|
@enemy.y += dy
|
||||||
|
|||||||
+2
-2
@@ -25,9 +25,9 @@ class Enemy
|
|||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update(dt)
|
||||||
player_pos = $bus.get(:player_position)
|
player_pos = $bus.get(:player_position)
|
||||||
@ai.update(*player_pos) if player_pos
|
@ai.update(*player_pos, dt) if player_pos
|
||||||
end
|
end
|
||||||
|
|
||||||
def draw
|
def draw
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ class EnemyHandler
|
|||||||
@enemies = []
|
@enemies = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update(dt)
|
||||||
spawn!
|
spawn!
|
||||||
@enemies.each(&:update)
|
@enemies.each { |enemy| enemy.update(dt) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def spawn!
|
def spawn!
|
||||||
|
|||||||
@@ -1,159 +0,0 @@
|
|||||||
class Placeable
|
|
||||||
SIZE = [25, 15]
|
|
||||||
|
|
||||||
# The base class for all placeables in the game. Placeables are things that can be interacted with, but do not move (unlike characters).
|
|
||||||
# They cannot be walked through, but can be interacted with (e.g. a chest can be opened, a torch can be lit, a radar can be used).
|
|
||||||
# They are placed at the center of a tile, and their position is represented by the tile coordinate
|
|
||||||
# An object is one that is placed at the center of a tile (radar, torch & chest)
|
|
||||||
attr_reader :x, :y
|
|
||||||
|
|
||||||
def initialize(x, y)
|
|
||||||
@x = x
|
|
||||||
@y = y
|
|
||||||
@frames_count = 0
|
|
||||||
@spritesheet = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def collides?(rect)
|
|
||||||
return true if intersects?(self.rect, rect)
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def rect
|
|
||||||
[@x * 120 + 90 - SIZE[0] / 2, @y * 120 + 90 - SIZE[1], *SIZE]
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
# For now, placeables don't have any behavior
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw
|
|
||||||
return unless @spritesheet
|
|
||||||
|
|
||||||
cam_x, cam_y = $bus.get(:camera_pos) || [0, 0]
|
|
||||||
screen_x = @x * 120 + 90 - cam_x
|
|
||||||
screen_y = @y * 120 + 90 - cam_y
|
|
||||||
|
|
||||||
elapsed = Gosu.milliseconds / 1000.0
|
|
||||||
frame_index = ((elapsed * @frames_count).floor) % @frames_count
|
|
||||||
|
|
||||||
@spritesheet[frame_index].draw(screen_x - 20, screen_y - 20, screen_y, 2, 2)
|
|
||||||
|
|
||||||
return unless DEBUG
|
|
||||||
|
|
||||||
# collision box centered on same point
|
|
||||||
Gosu.draw_rect(screen_x - SIZE[0] / 2, screen_y - SIZE[1], *SIZE, Gosu::Color.new(0x88ff0000), Float::INFINITY)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Radar < Placeable
|
|
||||||
def initialize(x, y)
|
|
||||||
super(x, y)
|
|
||||||
@frames_count = 4
|
|
||||||
@spritesheet = Gosu::Image.load_tiles("assets/images/radar.png", 20, 20, retro: true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Torch < Placeable
|
|
||||||
def initialize(x, y)
|
|
||||||
super(x, y)
|
|
||||||
@frames_count = 4
|
|
||||||
@spritesheet = Gosu::Image.load_tiles("assets/images/torch.png", 20, 20, retro: true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Chest < Placeable
|
|
||||||
def initialize(x, y)
|
|
||||||
super(x, y)
|
|
||||||
@wood = rand(0..64)
|
|
||||||
@metal = rand(0..16)
|
|
||||||
@science = rand(0..4)
|
|
||||||
@frames_count = 1
|
|
||||||
@spritesheet = Gosu::Image.load_tiles("assets/images/chest.png", 20, 20, retro: true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class ObjectHandler
|
|
||||||
attr_reader :objects
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@objects = []
|
|
||||||
@grid = {}
|
|
||||||
|
|
||||||
spawn_chests!
|
|
||||||
|
|
||||||
$bus.on(:collides?) do |rect|
|
|
||||||
nearby_objects(rect).any? { |obj| obj.collides?(rect) } ? :object : nil
|
|
||||||
end
|
|
||||||
|
|
||||||
$bus.on(:object_at) do |x, y|
|
|
||||||
@grid[[x, y]]
|
|
||||||
end
|
|
||||||
|
|
||||||
$bus.on(:nearby_torches) do |rect|
|
|
||||||
nearby_objects(rect).select { |obj| obj.is_a?(Torch) }.map { |t| t.rect }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def spawn_chests!
|
|
||||||
world_size = $bus.get(:maze_size) || [0, 0]
|
|
||||||
|
|
||||||
cell_size = 10
|
|
||||||
|
|
||||||
(0...world_size[0]).step(cell_size) do |cx|
|
|
||||||
(0...world_size[1]).step(cell_size) do |cy|
|
|
||||||
|
|
||||||
# Random position inside this cell
|
|
||||||
x = cx + rand(cell_size)
|
|
||||||
y = cy + rand(cell_size)
|
|
||||||
|
|
||||||
next if x >= world_size[0] || y >= world_size[1]
|
|
||||||
|
|
||||||
# Keep your spawn exclusion
|
|
||||||
next if (x - 2).abs <= 1 && (y - 2).abs <= 1
|
|
||||||
|
|
||||||
next if $bus.get(:room?, x, y)
|
|
||||||
|
|
||||||
chest = Chest.new(x, y)
|
|
||||||
@objects << chest
|
|
||||||
@grid[[x, y]] = chest
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def nearby_objects(rect)
|
|
||||||
x, y, w, h = rect
|
|
||||||
|
|
||||||
min_tx = ((x) / 120).floor
|
|
||||||
max_tx = ((x + w) / 120).floor
|
|
||||||
min_ty = ((y) / 120).floor
|
|
||||||
max_ty = ((y + h) / 120).floor
|
|
||||||
|
|
||||||
results = []
|
|
||||||
|
|
||||||
(min_tx..max_tx).each do |tx|
|
|
||||||
(min_ty..max_ty).each do |ty|
|
|
||||||
obj = @grid[[tx, ty]]
|
|
||||||
results << obj if obj
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
results
|
|
||||||
end
|
|
||||||
|
|
||||||
def add(object)
|
|
||||||
@objects << object
|
|
||||||
|
|
||||||
key = [object.x, object.y]
|
|
||||||
@grid[key] = object
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
@objects.each(&:update)
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw
|
|
||||||
cam = $bus.get(:camera_pos) || [0, 0]
|
|
||||||
nearby_objects([*cam, *SCREEN_SIZE]).each(&:draw)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
class Prop
|
||||||
|
SIZE = [25, 15]
|
||||||
|
|
||||||
|
# The base class for all props in the game. Props are things that can be interacted with, but do not move (unlike characters).
|
||||||
|
# They cannot be walked through, but can be interacted with (e.g. a chest can be opened, a torch can be lit, a radar can be used).
|
||||||
|
# They are placed at the center of a tile, and their position is represented by the tile coordinate
|
||||||
|
# A prop is one that is placed at the center of a tile (radar, torch & chest)
|
||||||
|
attr_reader :x, :y
|
||||||
|
|
||||||
|
def initialize(x, y)
|
||||||
|
@x = x
|
||||||
|
@y = y
|
||||||
|
@frames_count = 0
|
||||||
|
@spritesheet = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def collides?(rect)
|
||||||
|
return true if intersects?(self.rect, rect)
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def rect
|
||||||
|
[@x * 120 + 90 - SIZE[0] / 2, @y * 120 + 90 - SIZE[1], *SIZE]
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
# For now, placeables don't have any behavior
|
||||||
|
end
|
||||||
|
|
||||||
|
def draw
|
||||||
|
return unless @spritesheet
|
||||||
|
|
||||||
|
cam_x, cam_y = $bus.get(:camera_pos) || [0, 0]
|
||||||
|
screen_x = @x * 120 + 90 - cam_x
|
||||||
|
screen_y = @y * 120 + 90 - cam_y
|
||||||
|
|
||||||
|
elapsed = Gosu.milliseconds / 1000.0
|
||||||
|
frame_index = ((elapsed * @frames_count).floor) % @frames_count
|
||||||
|
|
||||||
|
@spritesheet[frame_index].draw(screen_x - 20, screen_y - 20, screen_y, 2, 2)
|
||||||
|
|
||||||
|
return unless DEBUG
|
||||||
|
|
||||||
|
# collision box centered on same point
|
||||||
|
Gosu.draw_rect(screen_x - SIZE[0] / 2, screen_y - SIZE[1], *SIZE, Gosu::Color.new(0x88ff0000), Float::INFINITY)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Radar < Prop
|
||||||
|
def initialize(x, y)
|
||||||
|
super(x, y)
|
||||||
|
@frames_count = 4
|
||||||
|
@spritesheet = Gosu::Image.load_tiles("assets/images/radar.png", 20, 20, retro: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Torch < Prop
|
||||||
|
def initialize(x, y)
|
||||||
|
super(x, y)
|
||||||
|
@frames_count = 4
|
||||||
|
@spritesheet = Gosu::Image.load_tiles("assets/images/torch.png", 20, 20, retro: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Chest < Prop
|
||||||
|
def initialize(x, y)
|
||||||
|
super(x, y)
|
||||||
|
@wood = rand(0..64)
|
||||||
|
@metal = rand(0..16)
|
||||||
|
@science = rand(0..4)
|
||||||
|
@frames_count = 1
|
||||||
|
@spritesheet = Gosu::Image.load_tiles("assets/images/chest.png", 20, 20, retro: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
require_relative "base"
|
||||||
|
|
||||||
|
class PropsHandler
|
||||||
|
attr_reader :props
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@props = []
|
||||||
|
@grid = {}
|
||||||
|
|
||||||
|
spawn_chests!
|
||||||
|
|
||||||
|
$bus.on(:collides?) do |rect|
|
||||||
|
nearby_props(rect).any? { |p| p.collides?(rect) } ? :prop : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
$bus.on(:prop_at) do |x, y|
|
||||||
|
@grid[[x, y]]
|
||||||
|
end
|
||||||
|
|
||||||
|
$bus.on(:nearby_torches) do |rect|
|
||||||
|
nearby_props(rect).select { |p| p.is_a?(Torch) }.map { |t| t.rect }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def spawn_chests!
|
||||||
|
world_size = $bus.get(:maze_size) || [0, 0]
|
||||||
|
|
||||||
|
cell_size = 10
|
||||||
|
|
||||||
|
(0...world_size[0]).step(cell_size) do |cx|
|
||||||
|
(0...world_size[1]).step(cell_size) do |cy|
|
||||||
|
|
||||||
|
# Random position inside this cell
|
||||||
|
x = cx + rand(cell_size)
|
||||||
|
y = cy + rand(cell_size)
|
||||||
|
|
||||||
|
next if x >= world_size[0] || y >= world_size[1]
|
||||||
|
|
||||||
|
# Keep your spawn exclusion
|
||||||
|
next if (x - 2).abs <= 1 && (y - 2).abs <= 1
|
||||||
|
|
||||||
|
next if $bus.get(:room?, x, y)
|
||||||
|
|
||||||
|
chest = Chest.new(x, y)
|
||||||
|
@props << chest
|
||||||
|
@grid[[x, y]] = chest
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def nearby_props(rect)
|
||||||
|
x, y, w, h = rect
|
||||||
|
|
||||||
|
min_tx = ((x) / 120).floor
|
||||||
|
max_tx = ((x + w) / 120).floor
|
||||||
|
min_ty = ((y) / 120).floor
|
||||||
|
max_ty = ((y + h) / 120).floor
|
||||||
|
|
||||||
|
results = []
|
||||||
|
|
||||||
|
(min_tx..max_tx).each do |tx|
|
||||||
|
(min_ty..max_ty).each do |ty|
|
||||||
|
p = @grid[[tx, ty]]
|
||||||
|
results << p if p
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
results
|
||||||
|
end
|
||||||
|
|
||||||
|
def add(prop)
|
||||||
|
@props << prop
|
||||||
|
|
||||||
|
key = [prop.x, prop.y]
|
||||||
|
@grid[key] = prop
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@props.each(&:update)
|
||||||
|
end
|
||||||
|
|
||||||
|
def draw
|
||||||
|
cam = $bus.get(:camera_pos) || [0, 0]
|
||||||
|
nearby_props([*cam, *SCREEN_SIZE]).each(&:draw)
|
||||||
|
end
|
||||||
|
end
|
||||||
+16
-15
@@ -1,7 +1,7 @@
|
|||||||
require_relative 'state'
|
require_relative 'state'
|
||||||
require_relative 'maze'
|
require_relative 'maze'
|
||||||
require_relative 'hud'
|
require_relative 'hud'
|
||||||
require_relative 'placeables/base'
|
require_relative 'props/handler'
|
||||||
require_relative 'enemy/handler'
|
require_relative 'enemy/handler'
|
||||||
require_relative 'character'
|
require_relative 'character'
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ class Game < Scene
|
|||||||
@maze = Maze.new
|
@maze = Maze.new
|
||||||
@character = Character.new
|
@character = Character.new
|
||||||
@enemies = EnemyHandler.new
|
@enemies = EnemyHandler.new
|
||||||
@objects = ObjectHandler.new
|
@props = PropsHandler.new
|
||||||
|
|
||||||
@hud = HUD.new
|
@hud = HUD.new
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ class Game < Scene
|
|||||||
@maze.draw
|
@maze.draw
|
||||||
@character.draw
|
@character.draw
|
||||||
@enemies.draw
|
@enemies.draw
|
||||||
@objects.draw
|
@props.draw
|
||||||
@hud.draw
|
@hud.draw
|
||||||
draw_fog!
|
draw_fog!
|
||||||
draw_debug! if DEBUG
|
draw_debug! if DEBUG
|
||||||
@@ -50,11 +50,6 @@ class Game < Scene
|
|||||||
end
|
end
|
||||||
|
|
||||||
def draw_fog!
|
def draw_fog!
|
||||||
# This part is a shader but due to time constraints, I'm doing it on the CPU.
|
|
||||||
# If done on the GPU, we could do a single pass and have much better performance.
|
|
||||||
# and also make it more fine-grained and smoother gradients.
|
|
||||||
# doing it on the GPU would require better knowledge of the ruby openGL bindings and shader programming, which I don't have right now.
|
|
||||||
|
|
||||||
cam_x, cam_y = @camera
|
cam_x, cam_y = @camera
|
||||||
cell = 10
|
cell = 10
|
||||||
p_i_radius = 80.0
|
p_i_radius = 80.0
|
||||||
@@ -65,16 +60,22 @@ class Game < Scene
|
|||||||
# Player screen position
|
# Player screen position
|
||||||
px = SCREEN_SIZE[0] / 2.0
|
px = SCREEN_SIZE[0] / 2.0
|
||||||
py = SCREEN_SIZE[1] / 2.0
|
py = SCREEN_SIZE[1] / 2.0
|
||||||
|
|
||||||
# Collect torch positions relative to camera
|
# Collect torch positions relative to camera
|
||||||
torches = $bus.get(:nearby_torches,
|
torches = $bus.get(:nearby_torches,
|
||||||
[cam_x - SCREEN_SIZE[0] / 2, cam_y - SCREEN_SIZE[1] / 2, SCREEN_SIZE[0] * 2, SCREEN_SIZE[1] * 2]
|
[cam_x - SCREEN_SIZE[0] / 2, cam_y - SCREEN_SIZE[1] / 2, SCREEN_SIZE[0] * 2, SCREEN_SIZE[1] * 2]
|
||||||
) || []
|
) || []
|
||||||
torch_lights = torches.map { |t| [t[0] - cam_x, t[1] - cam_y] }
|
torch_lights = torches.map { |t| [t[0] - cam_x, t[1] - cam_y] }
|
||||||
|
|
||||||
tiles_x = (SCREEN_SIZE[0] / cell) + 2
|
tiles_x = (SCREEN_SIZE[0] / cell) + 2
|
||||||
tiles_y = (SCREEN_SIZE[1] / cell) + 2
|
tiles_y = (SCREEN_SIZE[1] / cell) + 2
|
||||||
|
|
||||||
|
# This part is a shader but due to time constraints, I'm doing it on the CPU.
|
||||||
|
# If done on the GPU, we could have much better performance.
|
||||||
|
# and also make it more fine-grained and smoother gradients.
|
||||||
|
# doing it on the GPU would require better knowledge of the ruby openGL bindings and shader programming,
|
||||||
|
# which I don't have right now.
|
||||||
|
|
||||||
tiles_y.times do |j|
|
tiles_y.times do |j|
|
||||||
tiles_x.times do |i|
|
tiles_x.times do |i|
|
||||||
sx = i * cell
|
sx = i * cell
|
||||||
@@ -143,10 +144,10 @@ class Game < Scene
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update(dt)
|
||||||
@character.update
|
@character.update(dt)
|
||||||
@enemies.update
|
@enemies.update(dt)
|
||||||
@objects.update
|
@props.update
|
||||||
end
|
end
|
||||||
|
|
||||||
def button_down(id, _pos)
|
def button_down(id, _pos)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class Window < Gosu::Window
|
|||||||
self.caption = "Mist"
|
self.caption = "Mist"
|
||||||
|
|
||||||
@scene = Menu.new
|
@scene = Menu.new
|
||||||
|
@last_time = Gosu.milliseconds
|
||||||
|
|
||||||
$bus.on(:quit_game) { close! }
|
$bus.on(:quit_game) { close! }
|
||||||
|
|
||||||
@@ -35,7 +36,11 @@ class Window < Gosu::Window
|
|||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@scene.update
|
now = Gosu.milliseconds
|
||||||
|
dt = (now - @last_time) / 1000.0
|
||||||
|
@last_time = now
|
||||||
|
|
||||||
|
@scene.update(dt)
|
||||||
end
|
end
|
||||||
|
|
||||||
def compute_transform
|
def compute_transform
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ class Menu < Scene
|
|||||||
@last_mouse_pos = nil
|
@last_mouse_pos = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update(_dt)
|
||||||
pos = $bus.get(:mouse_pos)
|
pos = $bus.get(:mouse_pos)
|
||||||
return unless pos
|
return unless pos
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ class Scene
|
|||||||
def initialize
|
def initialize
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update(_dt)
|
||||||
end
|
end
|
||||||
|
|
||||||
def draw
|
def draw
|
||||||
|
|||||||
Reference in New Issue
Block a user