diff --git a/shard.lock b/shard.lock index 57a8e6b..1031c8b 100644 --- a/shard.lock +++ b/shard.lock @@ -6,5 +6,5 @@ shards: sdl: git: https://github.com/ysbaddaden/sdl.cr.git - version: 0.1.0+git.commit.d2aa0fb2ee30e42fc8d046d2d00c03c76879cb17 + version: 0.1.0+git.commit.0ded44711246feb3aa3ba28fea249c9b03be061b diff --git a/src/asteroid.cr b/src/asteroid.cr index 52e38b7..3739e79 100644 --- a/src/asteroid.cr +++ b/src/asteroid.cr @@ -6,6 +6,7 @@ class Asteroid < Sprite include LxGame::VectorSprite property size : Float64 = 1.0 + property color : Pixel = Pixel.new(128, 128, 128, 255) def initialize super @@ -16,11 +17,8 @@ class Asteroid < Sprite update_position(dt) end - def draw(renderer) - # renderer.draw_color = SDL::Color[0, 100, 100, 255] - # draw_radius(renderer) + def draw(engine) frame = project_points(points: @frame, scale: Vector2.new(@size, @size)) - renderer.draw_color = SDL::Color[128, 128, 128, 255] - draw_frame(renderer, frame) + draw_frame(engine, frame, color) end end diff --git a/src/asteroids.cr b/src/asteroids.cr index 5648d7c..38efe67 100644 --- a/src/asteroids.cr +++ b/src/asteroids.cr @@ -8,22 +8,6 @@ require "./asteroid" require "./bullet" require "./explosion" -WIDTH = 600 -HEIGHT = 400 -SCALE = 2 - -module LxGame - def draw_point(renderer, x, y) - x = x % WIDTH - y = y % HEIGHT - - x = WIDTH + x if x < 0 - y = HEIGHT + y if y < 0 - - renderer.draw_point(x, y) - end -end - class Asteroids < Game @ship : Ship @asteroids = [] of Asteroid @@ -50,6 +34,17 @@ class Asteroids < Game }) end + # override to wrap the coordinates + def draw_point(x : Int32, y : Int32, pixel : Pixel, surface = @screen) + x = x % @width + y = y % @height + + x = @width + x if x < 0 + y = @height + y if y < 0 + + super(x, y, pixel, surface) + end + def generate_asteroids center_size = Vector2.new(50.0, 50.0) center_pos = Vector2.new((width.to_f / 2.0) - (center_size.x / 2.0), (height.to_f / 2.0) - (center_size.y / 2.0)) @@ -192,15 +187,14 @@ class Asteroids < Game end end - def draw - @renderer.draw_color = SDL::Color[0, 0, 0, 255] - @renderer.clear - @ship.draw(@renderer) - @bullets.each { |b| b.draw(@renderer) } - @asteroids.each { |a| a.draw(@renderer) } - @explosions.each { |e| e.draw(@renderer) } + def draw(engine) + engine.clear + @ship.draw(engine) + @bullets.each { |b| b.draw(engine) } + @asteroids.each { |a| a.draw(engine) } + @explosions.each { |e| e.draw(engine) } end end -game = Asteroids.new(WIDTH, HEIGHT, SCALE) +game = Asteroids.new(500, 400, 2) game.run! diff --git a/src/bullet.cr b/src/bullet.cr index 16d3240..384cb5c 100644 --- a/src/bullet.cr +++ b/src/bullet.cr @@ -11,10 +11,10 @@ class Bullet < Sprite update_position(dt) end - def draw(renderer) - brightness = ((4.0 - self.age) / 4.0) * 255 - renderer.draw_color = SDL::Color[brightness, brightness, 0] - renderer.draw_point(@position.x.to_i, @position.y.to_i) + def draw(engine) + brightness = (((4.0 - self.age) / 4.0) * 255).to_u8 + color = Pixel.new(r: brightness, g: brightness, b: 0_u8) + engine.draw_point(@position.x.to_i, @position.y.to_i, color) end def collides_with?(other : VectorSprite) diff --git a/src/lx_game/emitter.cr b/src/lx_game/emitter.cr index 974aabe..5c6d055 100644 --- a/src/lx_game/emitter.cr +++ b/src/lx_game/emitter.cr @@ -12,6 +12,8 @@ module LxGame property emit_angle : Float64 = 2 * Math::PI property size : Float64 = 0.0 + # property color : Pixel = Pixel.new + def generate_particle Particle.build do |particle| particle.position = @position @@ -44,11 +46,9 @@ module LxGame @particles.reject!(&.dead?) end - def draw(renderer : SDL::Renderer) - renderer.draw_color = SDL::Color[255, 255, 0, 255] - + def draw(engine) @particles.each do |particle| - particle.draw(renderer) + particle.draw(engine) end end end diff --git a/src/lx_game/game.cr b/src/lx_game/game.cr index 333c9c8..8f09e9e 100644 --- a/src/lx_game/game.cr +++ b/src/lx_game/game.cr @@ -1,3 +1,6 @@ +require "./lib_sdl" +require "./pixel" + module LxGame abstract class Game FPS_INTERVAL = 1.0 @@ -15,18 +18,63 @@ module LxGame def initialize(@width, @height, @scale = 1, @title = self.class.name) SDL.init(SDL::Init::VIDEO) @window = SDL::Window.new(@title, @width * @scale, @height * @scale) - @renderer = SDL::Renderer.new(@window, flags: SDL::Renderer::Flags::SOFTWARE) + @renderer = SDL::Renderer.new(@window, flags: SDL::Renderer::Flags::PRESENTVSYNC) # , flags: SDL::Renderer::Flags::SOFTWARE) @renderer.scale = {@scale, @scale} + @screen = SDL::Surface.new(LibSDL.create_rgb_surface( + flags: 0, width: @width, height: @height, depth: 32, + r_mask: 0xFF000000, g_mask: 0x00FF0000, b_mask: 0x0000FF00, a_mask: 0x000000FF + )) end abstract def update(dt : Float64) - abstract def draw + abstract def draw(engine : Game) def elapsed_time Time.monotonic.total_milliseconds end - def engine_update + def clear(r = 0, g = 0, b = 0) + @screen.fill(0, 0, 0) + end + + def draw_point(x : Int32, y : Int32, pixel : Pixel, surface = @screen) + target = surface.pixels + (y * surface.pitch) + (x * 4) + target.as(Pointer(UInt32)).value = pixel.format(surface.format) + end + + # Draw a line using Bresenham’s Algorithm + def draw_line(p1 : Vector2, p2 : Vector2, pixel : Pixel, surface = @screen) + return draw_line(p2, p1, pixel, surface) if p1.x > p2.x + x1, y1, x2, y2 = p1.x.to_i, p1.y.to_i, p2.x.to_i, p2.y.to_i + + dx = (x2 - x1).abs + dy = -(y2 - y1).abs + + sx = x1 < x2 ? 1 : -1 + sy = y1 < y2 ? 1 : -1 + + d = dx + dy + x, y = x1, y1 + + loop do + draw_point(x, y, pixel, surface) + break if x == x2 && y == y2 + + d2 = d + d + + if d2 >= dy + d += dy + x += sx + end + + if d2 <= dx + d += dx + y += sy + end + end + end + + private def engine_update @fps_frames += 1 et = elapsed_time @@ -41,8 +89,12 @@ module LxGame @last_time = et end - def engine_draw - draw + private def engine_draw + @screen.lock do + draw(self) + end + + @renderer.copy(SDL::Texture.from(@screen, @renderer)) @renderer.present end diff --git a/src/lx_game/lib_sdl.cr b/src/lx_game/lib_sdl.cr new file mode 100644 index 0000000..35156b3 --- /dev/null +++ b/src/lx_game/lib_sdl.cr @@ -0,0 +1,7 @@ +module SDL + class Surface + def pixels + surface.pixels + end + end +end diff --git a/src/lx_game/particle.cr b/src/lx_game/particle.cr index 2ef6a9f..1008cd4 100644 --- a/src/lx_game/particle.cr +++ b/src/lx_game/particle.cr @@ -11,11 +11,11 @@ module LxGame update_position(dt) end - def draw(renderer : SDL::Renderer) + def draw(engine) return if dead? - brightness = ((@lifespan - @age) / @lifespan) * 255 - renderer.draw_color = SDL::Color[brightness / 2, brightness / 2, brightness / 2] - renderer.draw_point(@position.x.to_i, @position.y.to_i) + brightness = ((((@lifespan - @age) / @lifespan) * 255) / 2).to_u8 + color = Pixel.new(r: brightness, g: brightness, b: brightness) + engine.draw_point(@position.x.to_i, @position.y.to_i, color) end end end diff --git a/src/lx_game/pixel.cr b/src/lx_game/pixel.cr new file mode 100644 index 0000000..ed2b3c1 --- /dev/null +++ b/src/lx_game/pixel.cr @@ -0,0 +1,16 @@ +module LxGame + struct Pixel + def self.random + new(rand(0_u8..0xFF_u8), rand(0_u8..0xFF_u8), rand(0_u8..0xFF_u8), 0xFF_u8) + end + + property r, g, b, a + + def initialize(@r : UInt8 = 255, @g : UInt8 = 255, @b : UInt8 = 255, @a : UInt8 = 255) + end + + def format(format) + LibSDL.map_rgba(format, @r, @g, @b, @a) + end + end +end diff --git a/src/lx_game/sprite.cr b/src/lx_game/sprite.cr index c897b9f..9c47009 100644 --- a/src/lx_game/sprite.cr +++ b/src/lx_game/sprite.cr @@ -31,6 +31,6 @@ module LxGame end abstract def update(dt : Float64) - abstract def draw(renderer : SDL::Renderer) + abstract def draw(engine : Game) end end diff --git a/src/lx_game/sprite/vector_sprite.cr b/src/lx_game/sprite/vector_sprite.cr index f79f89f..3add4f8 100644 --- a/src/lx_game/sprite/vector_sprite.cr +++ b/src/lx_game/sprite/vector_sprite.cr @@ -55,17 +55,17 @@ module LxGame end end - def draw_frame(renderer : SDL::Renderer, frame = @frame) + def draw_frame(engine : Game, frame = @frame, color : Pixel = Pixel.new) 0.upto(frame.size - 1) do |n| - draw_line(renderer, frame[n], frame[(n + 1) % frame.size]) + engine.draw_line(frame[n], frame[(n + 1) % frame.size], color) end end - def draw_radius(renderer : SDL::Renderer, points = 30) + def draw_radius(engine : Game, points = 30, color : Pixel = Pixel.new) circle = self.class.generate_circle(points, average_radius).map do |point| point + @position end - draw_frame(renderer, frame: circle) + draw_frame(engine, frame: circle, color: color) end end end diff --git a/src/ship.cr b/src/ship.cr index 6b5a0c5..c071c75 100644 --- a/src/ship.cr +++ b/src/ship.cr @@ -10,6 +10,7 @@ class Ship < Sprite @r_emitter : Emitter @projected_points : Array(Vector2)? = nil property blew_up : Bool = false + @color = Pixel.new def initialize super @@ -112,16 +113,16 @@ class Ship < Sprite @r_emitter.rotation = @rotation + 1.5 end - def draw(renderer) + def draw(engine) return if @blew_up - @emitter.draw(renderer) + @emitter.draw(engine) @emitter.emitting = false - @l_emitter.draw(renderer) + @l_emitter.draw(engine) @l_emitter.emitting = false - @r_emitter.draw(renderer) + @r_emitter.draw(engine) @r_emitter.emitting = false - renderer.draw_color = SDL::Color[255, 255, 255, 255] - draw_frame(renderer, projected_points) + # renderer.draw_color = SDL::Color[255, 255, 255, 255] + draw_frame(engine, projected_points, @color) end end