Hi there,
I’m in the process of writing what will eventually be a card game using
ruby. However, first things first I’m trying to set up the graphics
functions. Currently I’m using a Gtk window with a sigle widget, a
DrawingArea inside which I use cairo to draw the graphics.
However I’ve run into a few problems. First of all, if I don’t manually
call the garbage collector frequently, the program grinds to a halt –
it seems like when i grab a new Cairo context, the old one isn’t deleted
and hangs around in memory? Am I doing something wrong?
Secondly, even after adding the garbage collector, the frame rate is
depressingly low. With no background and drawing a minimal number of
objects per frame, it only gets ~20 FPS.
I feel like I must be doing something wrong.
My question is twofold:
- Is there a better way to do this (other than cairo/GTK) that I don’t
know about? - If not, is there any way to speed up cairo’s drawing?
I’ve included a stripped-down version of my code below.
Thanks so much!
P.S. I’m somewhat new to this forum, so if this post is in the wrong
place, please tell me.
###############################################################################
require ‘gtk2’
require ‘cairo’
def min(x, y)
if x<y
return x
else
return y
end
end
def max(x, y)
if x>y
return x
else
return y
end
end
class Entity
attr_accessor :image, :x, :y, :scale
def initialize(img, x, y)
@x, @y = x, y
@image = Gdk::Pixbuf.new(img)
@scale = 0
end
def draw(cr, scale)
cr.save
if scale != @scale
@pixbuf = @image.scale(@image.width*scale,
@image.heightscale)
@scale = scale
end
cr.set_source_pixbuf(@pixbuf, @xscale, @yscale)
cr.rectangle(@xscale, @y*scale, @pixbuf.width,
@pixbuf.height)
cr.clip
cr.paint
cr.restore
end
end
class Field
attr_accessor :entities, :scale, :height, :width
def initialize(w, h)
@width, @height = w, h
@entities = []
end
def draw(cr, x, y, scale)
return nil if scale <= 0
cr.save
cr.rectangle(x, y, scale*@width, scale*@height)
cr.clip
cr.translate(x, y)
cr.scale(scale, scale)
entities.each do |e|
e.draw(cr, 1)
e.x += rand()*10-5
e.y += rand()*10-5
end
cr.restore
end
end
class JCanvas < Gtk::DrawingArea
attr_accessor :myfield, :yourfield, :frames, :sc
def initialize(w, h)
@myfield = Field.new(1280, 800)
@yourfield = Field.new(1280, 800)
@frames = 0
@sc = 0.5
super()
end
def get_cr()
#puts "Getting new CR"
cr = window.create_cairo_context()
end
def redraw
cr = get_cr
cr.push_group()
black = [0.0,0.0,0.0]
cr.set_source_rgb(black)
cr.paint
sc = @sc
myfield.draw(cr, 640-640*min(sc,1), 0, min(sc, 1))
yourfield.draw(cr, 640-640*max(1-sc, 0), 800*sc,
max(1-sc, 0))
cr.pop_group_to_source()
cr.paint
@frames += 1
return 1
end
end
def main
win = Gtk::Window.new;
win.set_title("Magickz!");
win.fullscreen
w, h = win.default_size()
canvas = JCanvas.new(w, h);
10.times { canvas.myfield.entities.push(Entity.new("test.jpg",
rand()*500, rand()*500)) }
10.times {
canvas.yourfield.entities.push(Entity.new(“skull.jpg”, rand()*500,
rand()*500)) }
canvas.add_events(Gdk::Event::BUTTON_PRESS_MASK)
canvas.signal_connect("button_press_event") { puts "got MOUSE!!"
}
canvas.add_events(Gdk::Event::KEY_PRESS_MASK)
canvas.signal_connect("key_press_event") { |ev, k| puts "got
KYBD (#{k.keyval})"; if k.keyval==113; exit(); end; if k.keyval==65362;
canvas.sc +=0.05; else; canvas.sc -= 0
.05; end }
canvas.set_flags(Gtk::Widget::CAN_FOCUS)
win.add(canvas);
canvas.show();
win.show()
Thread.new("gameloop") {
while true
canvas.redraw
sleep(0.0100)
end
true
}
Gtk.timeout_add(2000) {puts "starting GC"; GC.start; true}
Gtk.timeout_add(1000) {puts "FPS = #{canvas.frames}";
canvas.frames = 0; true}
Gtk.main
return 0;
end
main
#############################################################################