I'm starting to code GUIs with Shoes. I tried the progress bar from the examples, but I found no way to exit the animation, break did not work...
animate do |frames|
unless frames > 100
#p.fraction = (frames % 100) / 100.0
else
break
end
end
Is there any possibility to stop a animation with Shoes? Thanks.
sure, stop does that
Shoes.app do
stack :margin => 0.1 do
title "Progress example"
#p = progress :width => 1.0
#animate = animate (24) do |i|
#p.fraction = (i % 100) / 100.0
#animate.stop if i > 99
end
end
end
Related
I'm using Gosu (with Ruby version 2.5.5) to create my first game. I created a map and I want to implement scrolling with the cursor. Using Gosu example "Cptn Ruby" as a guide, I was successful to a point.
Here is what I have so far.
def update
# map.width is the number of background tiles in each row and map.height is the number of tiles in
# each column. Each tile is 14x14 pixels.
#camera_x = [[self.mouse_x - (WIDTH / 2), 0].max, #map.width * 14 - WIDTH].min
#camera_y = [[self.mouse_y - (HEIGHT / 2), 0].max, #map.height * 14 - HEIGHT].min
end
def draw
#cursor.draw(self.mouse_x, self.mouse_y, 100, scale_x = 0.65, scale_y = 0.65)
Gosu.translate(-#camera_x, -#camera_y) {#map.draw}
end
This does scroll, but only to maximum point. Once the cursor reaches the bottom of the screen, the camera_y value will not get greater than 239 (and the same problem goes for camera_x). I can increase the scrolling distance by multiplying the value by 2 like so:
#camera_y = [[(self.mouse_y - (HEIGHT / 2) * 2, 0].max, #map.height * 14 - HEIGHT].min
However, while I scroll further with this approach, it still stops. I would like to continuously scroll while the mouse is at the bottom (or side) of the screen. I am confused as to why it is not doing this already since gosu::update runs 60 times per second. I would have thought that every time it runs it would add to my #camera_y and/or #camera_x variables if the cursor is in the right spot, but that is not happening.
I've also tried this:
if self.mouse_y > (HEIGHT * 0.67) # if mouse is in lower 3rd of screen
#camera_y += 10
end
This simply moves scrolls 10 pixels once instead of continuously.
I could do this easily with a loop, but I found that loops in Gosu update or draw cause the program to crash.
Any thoughts?
I figured out a way to do it.
def initialize
super WIDTH, HEIGHT, :fullscreen => false
self.caption = "Ecosystem Beta"
#map = Map.new("c:/users/12035/.atom/Ecosystem/eco_map.txt", :tileable => true)
#cursor = Gosu::Image.new("c:/users/12035/.atom/media/cursor.png")
#camera_x = 0
#camera_y = 0
end
def update
if self.mouse_y > (HEIGHT * 0.67) # if mouse is in lower 3rd of screen
#camera_y = [#camera_y += 10, 740].min # scrolls to a max of 740 pixels
elsif self.mouse_y < (HEIGHT * 0.33)
#camera_y = [#camera_y -= 10, 0].max # will not scroll past the begginining of the map
end
end
I'm following the book "Learn game programming with ruby"
one of the exercises is loading an image with gosu and making it bounce off the edges of the screen. I followed the exercise and the image bounces fine off the top and left corners but will sink past the edge of the screen for awhile before bouncing off the bottom and right sides.
require 'gosu'
class Window < Gosu::Window
def initialize
super(800, 600)
self.caption = 'First Game'
#blueheart = Gosu::Image.new('blueheart.png')
#x = 200
#y = 200
#width = 50
#height = 43
#velocity_x = 2
#velocity_y = 2
#direction = 1
end
def update
#x += #velocity_x
#y += #velocity_y
#velocity_x*= -1 if #x + #width /2 > 800 || #x - #width / 2 < 0
#velocity_y*= -1 if #y + #height /2 > 600 || #y - #height / 2 < 0
end
def draw
#blueheart.draw(#x - #width/2, #y - #height/2, 1)
end
end
window = Window.new
window.show
I think it has something to do with how ruby uses the top right corner of the image as the coordinates for an image but I thought
#blueheart.draw(#x - #width/2, #y - #height/2, 1)
was supposed to fix that, How can I make it work like I want?
Thanks in advance
The Problem came from me creating my own sprites and not realizing the height and width values would be different.
changing the code to #width = #blueheart.width gave me a crash
but I just changed the values to the proper width and height and fixed the problem. the values #width = 50 and #height = 43 were referring to different sprite sizes in the book.
I'm building this popup countdown timer for a game. The issue is I can't figure out how to update the the animate so that the display changes too. It works so far, but you can't see the numbers changing. It blends into the 00:00. I'm pretty positive it works so far just having trouble with this change. This is done with green shoes. Any ideas what I'm doing wrong here?
#Timer button used for creating a countdown timer for the game.
#Game ends when timer is zero.
flow width: 80, height: 0.2 do
button 'Timer', width: 1.0, height: 1.0 do
window do
def time_change!
#clock = '%02d:%02d' % [#second / 60, #second % 60]
if(#second == 0)
alert "game is over"
#clock = '00:00'
close()
para #clock, size: 50, stroke: black
end
end
background whitesmoke
#clock = '00:00'
para #clock, size: 50, stroke: black
#second = 10
animate(1) do
#second -= 1
time_change!
para #clock, size: 50, stroke: black
end
end
end
You can replace the text of the current para which displays the clock:
Shoes.app do
flow do
button 'Timer', width: 100, height: 50 do
window width: 200, height: 100 do #Open a child window when the button is clicked
seconds = 3
tenths = 0
clock = '%02d:%02d'
flow do
#p = para clock % [seconds, tenths], #Assign the clock para to an instance variable,
size: 50, #which will make the variable visible outside this block.
stroke: black
end
a = animate(10) do #Upate the clock every 1/10 of a second.
tenths -= 1
if tenths < 0
tenths = 9
seconds -= 1
end
#p.text = clock % [seconds, tenths] #Replace the clock text.
if seconds == 0 and tenths == 0
a.stop
alert("game over")
close #=>self.close where self is the window whose block contains this code
end
end #animate
end #window
end #button
end #flow
end #app
To verify that the clock is actualy showing each time increment, you can slow things down by changing animate(10) to animate(1).
I want to loop the background image in a Ruby gosu side scrolling game.I have problem with the counters #k and #p which are used to translate the background image and the duplicated background image.I can't think of a good way to plus them.Here's the code to make it more clear.
require 'gosu'
class GameWindow < Gosu::Window
attr_accessor :x, :y
SHIFT = 700
def initialize
super 640,440
#background = Gosu::Image.new("./images/bg.png")
#player = Gosu::Image.new("./images/000.png")
#x1, #y1 = 0, 0
#player_x, #player_y = 50, 50
#k = 0 #counter
#p = 1 #counter
end
def update
#x1 -= 3
#player_y += 1 if #player_y <= 375
#player_y -= 3 if button_down?(Gosu::KbSpace) and #player_y >= 0
#coordinates = Gosu::Image.from_text(
self, "#{#x1},#{#k}", Gosu.default_font_name, 30)
#here should be the code for #k and #p
end
def draw
#background.draw(#x1 + #k*SHIFT, #y1, 0)
#background.draw(#x1 + #p*SHIFT, #y1, 0)
#player.draw(#player_x, #player_y, 0)
#coordinates.draw(0, 0, 1)
end
def button_down(id)
$window.close if id == Gosu::KbEscape
end
end
window = GameWindow.new
window.show
So how do I plus the counters #k and #p.Tried this
if #x1 > -(SHIFT+5)*#p and #x1 < -SHIFT*#p #705 and 700
#k += 2
end
if #k > 0 and #x1 > -SHIFT*#k - 5 and #x1 < -SHIFT*#k - 3 #1405 and 1403
#p += 2
end
but it works only in the beginning(2-3 image shifts).
Also tried this
if #x1 == -SHIFT*#p
#k += 2
end
and it did not work.
Assumptions & Analysis
I'm assuming that #x1 and #y1 are the offset of the left-most background's origin relative to the screen. You also have #player_x and #player_y, which denote the player's position on the screen. Your player is kept horizontally in the center of the screen, moving vertically when jumping or falling, and your background only scrolls horizontally. Also, your window size is 640x440, and your background image is 700px wide and at least 440px tall.
Your update step handles things like jumping (but only to the top of the screen) and simple, constant-velocity gravity, both by modifying the player's screen coordinates. It also provides a constant horizontal scroll by subtracting 3 units per frame from #x1, the horizontal camera coordinate in world-space.
Your draw step then takes the background image and draws two of them, offsetting them by the camera offset #x1 plus the shift for which image is which. This shift, however, isn't important, because we can calculate it fairly simply, and we're left without having to manipulate the additional state of remembering which one is which.
Code Solution
Instead of remembering the counter values #k and #p, we're going to just modulo by the image width to eliminate the excess in #x1 and get it where we need it to be. #k and #p are useless, so delete those. SHIFT can be deleted as well.
Instead, we need to do the following:
Calculate the screen offset of the left-most image
Determine if two images need to be drawn instead of just one
Draw the image(s)
Our goal looks something like this:
Our new code:
require 'gosu'
class GameWindow < Gosu::Window
attr_accessor :x, :y # side note - are these used at all?
def initialize
super 640,440
#background = Gosu::Image.new("./images/bg.png")
#player = Gosu::Image.new("./images/000.png")
#x1, #y1 = 0, 0
#player_x, #player_y = 50, 50
end
def update
#x1 -= 3
#player_y += 1 if #player_y <= 375
#player_y -= 3 if button_down?(Gosu::KbSpace) and #player_y >= 0
#coordinates = Gosu::Image.from_text(
self, "#{#x1}", Gosu.default_font_name, 30)
end
def draw
# the important bits!
#local_x = #x1 % -#background.width
#background.draw(#local_x, #y1, 0)
#background.draw(#local_x + #background.width, #y1, 0) if #local_x < (#background.width - self.width)
#player.draw(#player_x, #player_y, 0)
#coordinates.draw(0, 0, 1)
end
def button_down(id) # Side note: Does this work correctly?
$window.close if id == Gosu::KbEscape
end
end
window = GameWindow.new
window.show
What this does is, it takes #x1 and modulos it with the (negative) width of the image. This means, it gives the remainder of an integer division of the two numbers. Modulo is very useful when trying to ensure that an integer value "wraps around" after exceeding a limit in either direction; it effectively maps it to between 0 (inclusive) and the given number (exclusive). The results of this are illustrated in the graphic above.
Keep in mind this solution only works as-is for your circumstances; vertical scrolling will require more code, and this will break pretty quickly if your window becomes wider than your background image.
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).
I suggest using my Ashton gem to render directly to textures - Unlike TexPlay or other software rendering, this is hardware accelerated, so is very fast! Using Ashton::Texture#render, you can cache drawing operations into a texture as quickly as you can draw them onto the screen.
def initialize(...)
# WindowBuffer is just an Ashton::Texture that is the same size as the window
#draw_buffer = Ashton::WindowBuffer.new
...
end
def update()
#draw_buffer.render do
# Perform new gosu drawing actions (e.g. draw_quad)
# here to draw directly into the texture.
# Both the local ones and the ones pulled from the network.
end
end
def draw()
# Since you are drawing things in the order they should appear,
# you don't need to worry about Z values.
#background_image.draw 0, 0, 0
#earth_image.draw 295, 215, 0
#draw_buffer.draw 0, 0, 0
#cursor_image.draw #cx, #cy, 0
end
Also note that printing text is actually quite slow, so will affect your FPS if performed in every update/draw.