SFML/ruby/test/SideScroller.rb

241 lines
5.6 KiB
Ruby

require 'RubySFML'
include SFML
module Tiles
Grass = 0
Earth = 1
end
# Collectible item.
class Gem2 < Sprite
def initialize(image, x, y)
super(image)
self.x = x
self.y = y
end
def update
self.rotation = 25 * Math.sin($clock.to_f*6)
end
end
class Player < VSprite
attr_reader :score
def initialize(image, w, h)
super(image, w, h)
@sb = SoundBuffer.new("media/Beep.wav")
@sound = Sound.new(@sb)
@score = 0
end
def update(window, map)
time = window.frameTime
input = window.input
# If there's no ground beneath my feet, increase speed downward
if !map.solid?(self.x, self.bottom)
@vy += 1
# If there is ground beneath my feet, and the user presses up,
# boost speed upwards to jump
elsif input.isKeyDown(Key::Up)
@vy = -20
end
super(1) # Update position based on velocity
# If we just moved down, see it we landed
if @vy > 0
if map.solid?(self.x, self.bottom)
@vy = 0
self.top -= self.top % 50
end
# If we just moved up, see if we hit our head
elsif @vy < 0
if map.solid?(self.x, self.top)
@vy = 0
self.top += 50 - self.top % 50
end
end
moved = false
if input.isKeyDown(Key::Left)
moved = true
self.x -= 10
# If we just moved left, see if we hit a wall
if map.solid?(self.left, self.y)
self.left += 50 - self.left % 50
end
end
if input.isKeyDown(Key::Right)
moved = true
self.x += 10
# If we just moved right, see if we hit a wall
if map.solid?(self.right, self.y)
self.left -= self.left % 50
end
end
if @vy < 0
@tx = 3
elsif moved
@tx = (@tx + 1) % 3
else
@tx = 0
end
# Same as in the tutorial game.
map.gems.reject! do |c|
if (c.x - self.x).abs < 50 and (c.y - self.y).abs < 50
@sound.play
@score += 10
true
else
false
end
end
end
end
# Map class holds and draws tiles and gems.
class Map < Drawable
attr_reader :width, :height, :gems
def initialize(filename, player)
@start = Time.now
@tileset_image = Image.new("media/Tileset.png")
@tileset = TSprite.new(@tileset_image, 50, 50)
@gem_image = Image.new("media/Gem.png")
@gems = []
@exit_image = Image.new("media/Star.png")
@exit = TSprite.new(@exit_image, 25, 25)
lines = File.readlines(filename).map { |line| line.chop }
@height = lines.size
@width = lines[0].size
@tiles = Array.new(@width) do |x|
Array.new(@height) do |y|
case lines[y][x, 1]
when '"' then Tiles::Grass
when '#' then Tiles::Earth
when 'S' then player.x, player.y = x*50, y*50; nil
when 'E' then @exit.left, @exit.top = x*50+25, y*50+25; nil
when 'x' then @gems << Gem2.new(@gem_image, x * 50 + 25, y * 50 + 25); nil
else nil
end
end
end
end
def done?(player)
player.x.to_i/50 == @exit.x.to_i/50 and player.y.to_i/50 == @exit.y.to_i/50
end
def render(window)
# Very primitive drawing function:
# Draws all the tiles, some off-screen, some on-screen.
@height.times do |y|
@width.times do |x|
tile = @tiles[x][y]
if tile
@tileset.tx = tile
@tileset.left = x * 50
@tileset.top = y * 50
window.draw(@tileset) if
@tileset.right > window.view.left and
@tileset.left < window.view.right and
@tileset.bottom > window.view.top and
@tileset.top < window.view.bottom
end
end
end
@gems.each { |c|
c.update
window.draw(c) if
c.right > window.view.left and
c.left < window.view.right and
c.bottom > window.view.top and
c.top < window.view.bottom
}
window.draw(@exit)
end
# Solid at a given pixel position?
def solid?(x, y)
x < 0 or y < 0 or x >= @width*50 or y >= @height*50 or @tiles[x / 50][y / 50]
end
end
$clock = Clock.new
mode = VideoMode.new(640, 480, 32)
win = RenderWindow.new(mode, "RubySFML Test", 0)
view = View.new(FloatRect.new(0, 0, 640, 480))
win.showMouseCursor(false)
win.useVerticalSync(true)
sky_image = Image.new("media/Space.png")
sky = Sprite.new(sky_image)
player_image = Image.new("media/CptnRuby.png")
player = Player.new(player_image, 50, 50)
levels = Dir["media/Level*.txt"].sort
level = 1
level_start = Time.now
map = Map.new(levels.shift, player)
# Simple game loop
done = false
game_over = false
while !done
if map.done?(player)
if levels.empty?
game_over = true
else
level += 1
level_start = Time.now
map = Map.new(levels.shift, player)
end
end
while e = win.getEvent()
done = true if e.type == Event::Closed or
(e.type == Event::KeyReleased and e.code == Key::Escape)
end
player.update(win, map)
left = player.x - 640/2
left = 0 if left < 0
left = map.width*50-640 if left > map.width*50-640
top = player.y - 480/2
top = 0 if top < 0
top = map.height*50-480 if top > map.height*50-480
view.rect = FloatRect.new(left, top, left+640, top+480)
win.setView(view)
sky.left = view.rect.left
sky.top = view.rect.top
win.draw(sky)
win.draw(map)
win.draw(player)
score = Text.new("Score: #{player.score}", "", 20)
score.left, score.top = sky.left, sky.top
win.draw(score)
if game_over
text = Text.new("Game Over!", "", 60)
text.left, text.top = sky.left+200, sky.top+200
win.draw(text)
elsif Time.now - level_start < 3
text = Text.new("Level #{level}!", "", 60)
text.left, text.top = sky.left+200, sky.top+200
win.draw(text)
text = Text.new("(Collect the gems and find the exit)", "", 25)
text.left, text.top = sky.left+125, sky.top+260
win.draw(text)
end
win.display()
sleep(0.01)
end