I have been tinkering with making a drawing application in Gosu using Ruby, but am running into some high CPU usage. I know one possible cause.
First, I am simply adding a primitive to an hash every time the user clicks the mouse button (or if they hold down the mouse button, every time the screen refreshes, which I think happen around 60 times per second). Each time the screen redraws I have the program loop through the hash and redraw all the primitives. If someone holds down their mouse button it will just keep overlapping primitives on top of each other and in other situations the primitives just layer. I can see how that would eventually eat up a bunch of memory in the hash.
I feel that the best solution would be to use some sort of lower level function that simply colors pixels on the screen and doesn't have to store them in an hash. I'm not sure if Gosu supports that. Could someone either help me with this in Gosu or suggest a different method and/or another program to use? Thank you!
(Below is the complete code for my program.)
require 'Gosu'
require 'socket'
class GameWindow < Gosu::Window
def initialize
super 1280, 960, false
self.caption = "Drawz Server"
@background_image = Gosu::Image.new(self, "media/Space.png", true)
@player = Player.new(self)
# Platform is the blank image for the epicycle to be drawn on.
@earth_image = Gosu::Image.new(self, "media/earth.bmp", true)
@cursor_image = Gosu::Image.new(self, "media/cursor.bmp", true)
puts "Please enter port..."
port = gets.strip!
puts "Server is running."
# Initialized Instance variables for all methods.
@square_drawing_number = 0
@client_coordinates_array = []
@client_coordinates_array_array = []
@client_quad_hash = {}
server = TCPServer.open("10.64.8.2", port)
@client = server.accept
manage_client_data = Thread.new do
x = 0
loop do
@client_coordinates_array[x] = @client.gets
x += 1
end
end
@x = 0
@quad_hash = {}
# Start up value of things in draw method, because draw is called before update. Hence I put the start up values here so that when draw is called it has location of things in
# its arguments.
@cx = 0
@cy = 0
end
class QuadCoords
attr_reader :x1, :y1, :x2, :y2, :x3, :y3, :x4, :y4
def initialize(x1, y1, x2, y2, x3, y3, x4, y4)
@x1 = x1
@y1 = y1
@x2 = x2
@y2 = y2
@x3 = x3
@y3 = y3
@x4 = x4
@y4 = y4
end
end
# Mean't to change location (x and y) and such of things in draw method.
def update
@cx = mouse_x
@cy = mouse_y
@x += 1
# Adds square locations to hash which is then accessed by draw function to retrieve and draw squares.
if button_down? Gosu::MsLeft
@x += 1
quad_coords_obj = QuadCoords.new(@cx - 5, @cy - 5, @cx, @cy - 5, @cx - 5, @cy, @cx, @cy)
@quad_hash["quad#{@x}"] = quad_coords_obj
# Sends location of squares to client for it to draw.
@client.puts "#{quad_coords_obj.x1}---#{quad_coords_obj.y1}---#{quad_coords_obj.x2}---#{quad_coords_obj.y2}---#{quad_coords_obj.x3}---#{quad_coords_obj.y3}---#{quad_coords_obj.x4}---#{quad_coords_obj.y4}"
end
if @client_coordinates_array.length > @square_drawing_number
new_squares_to_add = @client_coordinates_array.length - @square_drawing_number
@client_coordinates_array[-1 * new_squares_to_add..-1].each do |value|
@client_coordinates_array_array << value.split(/\---/)
end
x = 1
new_squares_to_add.times do
@x += 1
@client_quad_coords_obj = QuadCoords.new(@client_coordinates_array_array[-1 * x][0].to_i,
@client_coordinates_array_array[-1 * x][1].to_i,
@client_coordinates_array_array[-1 * x][2].to_i,
@client_coordinates_array_array[-1 * x][3].to_i,
@client_coordinates_array_array[-1 * x][4].to_i,
@client_coordinates_array_array[-1 * x][5].to_i,
@client_coordinates_array_array[-1 * x][6].to_i,
@client_coordinates_array_array[-1 * x][7].to_i)
@client_quad_hash["quad#{@x}"] = @client_quad_coords_obj
x += 1
@square_drawing_number += 1
end
end
end
# Draw is called before update.
def draw
@background_image.draw(0,0,0)
@earth_image.draw(295,215,1)
# x1,y1 = Upper Left Hand Corner; x2,y2 = Lower Left Hand Corner; x3,y3 = Upper Right Hand Corner; x4,y4 = Lower Right Hand Corner
@quad_hash.each_value do |value|
draw_quad(value.x1, value.y1, 0xff00ffff, value.x2, value.y2, 0xff00ffff, value.x3, value.y3, 0xff00ffff, value.x4, value.y4, 0xff00ffff, z = 0, mode = :default)
end
@client_quad_hash.each_value do |value|
draw_quad(value.x1, value.y1, 0xff00ff00, value.x2, value.y2, 0xff00ff00, value.x3, value.y3, 0xff00ff00, value.x4, value.y4, 0xff00ff00, z = 0, mode = :default)
end
@cursor_image.draw(@cx, @cy, 1)
end
def button_down(id)
if id == Gosu::KbEscape
close
end
end
end
class Player
attr_accessor :x, :y, :update_called
def initialize(window)
@image = Gosu::Image.new(window, "media/Sun.bmp", false)
end
end
window = GameWindow.new
window.show
You can use TexPlay for drawing over Gosu::Image and then render this image. http://banisterfiend.wordpress.com/2008/08/23/texplay-an-image-manipulation-tool-for-ruby-and-gosu/ for example:
image1.paint {
circle 20,20,10, :color => :red
rect 40,40,50,50, :color => :green
pixel 60,60, :color => :blue
}
There are alternatives: RMagick (that's more powerful, but much more complex to install\distribute) and Ashton (it has methods of rendering to OpenGL texture, so it's faster, but requires some understanding of OpenGL).