Ruby/Gosu getting a bouncing image - ruby

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.

Related

How can I detect if mouse position is over the circle position using Gosu Ruby?

I have a circle that is drawn on the window, how can I define its boundaries ? So far, I can place it in an invisible rectangle and so I detect the mouse, but this is not what I want
CIRCLE_STEP = 10
def draw_circle(cx,cy,r,color)
0.step(360, CIRCLE_STEP) do |a1|
a2 = a1 + CIRCLE_STEP
$window.draw_line cx + Gosu.offset_x(a1, r), cy + Gosu.offset_y(a1, r), color, cx + Gosu.offset_x(a2, r), cy + Gosu.offset_y(a2, r), color, 10
end
end
def update
if mouse_over_button($window.mouse_x, $window.mouse_y, 180)
#color = Gosu::Color::GREEN
else
#color = Gosu::Color.argb(255, 240, 232, 196)
end
end
def mouse_over_button(mouse_x, mouse_y, shift)
mouse_x.between?(get_rect_width, get_rect_width + shift) && mouse_y.between?(get_rect_height, get_rect_height + shift)
end
def get_rect_width()
$window.width / 2
end
def get_rect_height()
$window.height / 2 + 200
end
Is there any other more efficient way ?
Use the Center of the circle's screen position, then take the mouse's screen position. Then offset by the circles radius.
Gosu::Window#mouse_
my = Gosu::Window#mouse_x
mx = Gosu::Window#mouse_y
if (my - circle_y).abs + (mx - circle_x).abs < circle_radius
# mouse is over circle
end

Is there a way to use the cursor to scroll through a map indefinitely using Gosu

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

Looping background image in Ruby side-scrolling game

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'm using kivy and I don't know how to create the event for the block to break

I don't know how to set up a event so that when my pong ball hits block it will be disabled or be gone off screen. Can someone help me. I'm very new and I look on there API but it just confused me a lot. Help would be much appreciated.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty
from kivy.vector import Vector
from kivy.clock import Clock
class Block(Widget):
score = NumericProperty(0)
def bounce_ball(self, ball):
if self.collide_widget(ball):
vx, vy = ball.velocity
offset = (ball.center_y - self.center_y) / (self.height / 2)
bounced = Vector(-1 * vx, vy)
vel = bounced * 1.0
ball.velocity = vel.y, vel.x + offset
self.dispatch
class PongPaddle(Widget):
score = NumericProperty(0)
def bounce_ball(self, ball):
if self.collide_widget(ball):
vx, vy = ball.velocity
offset = (ball.center_y - self.center_y) / (self.height / 2)
bounced = Vector(-1 * vx, vy)
vel = bounced * 1.0
ball.velocity = vel.y, vel.x + offset
class PongBall(Widget):
ball = image
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def move(self):
self.pos = Vector(*self.velocity) + self.pos
class PongGame(Widget):
brick = ObjectProperty(None)
ball = ObjectProperty(None)
player1 = ObjectProperty(None)
block = ObjectProperty(None)
def serve_ball(self, vel=(0, 4)):
self.ball.center = self.center
self.ball.velocity = vel
def update(self, dt):
self.ball.move()
#bounce of paddles
self.player1.bounce_ball(self.ball)
if self.block.bounce_ball(self.ball): self.dispatch
#bounce ball off bottom or top
if (self.ball.top > self.top):
self.ball.velocity_y *= -1
#bounce ball off bottom or top
if (self.ball.x < 0) or (self.ball.right > self.width):
self.ball.velocity_x *= -1
def on_touch_move(self, touch):
if touch.x > self.width / 12:
self.player1.center_x = touch.x
class PongApp(App):
def build(self):
game = PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0 / 30.0)
return game
if __name__ == '__main__':
PongApp().run()
It is not elegant solution - I don't use event.
I add visible to class Block:
class Block(Widget):
visible = BooleanProperty(True)
# the rest of the code
and then I remove block when it collide with ball
def update(self, dt):
self.ball.move()
#bounce of paddles
self.player1.bounce_ball(self.ball)
#bounce ball off bottom or top
if (self.ball.top > self.top):
self.ball.velocity_y *= -1
#bounce ball off bottom or top
if (self.ball.x < 0) or (self.ball.right > self.width):
self.ball.velocity_x *= -1
# remove block when collide with ball
if self.block.visible and self.block.collide_widget(self.ball):
self.block.bounce_ball(self.ball)
self.block.visible = False
self.remove_widget(self.block)
Set either the x or y coordinate of your block so that it will be drawn offscreen (i.e., not actually drawn at all). First, if applicable, save your y coordinate so you can retrieve it later, to restore your block:
# Save old y setting for later retrieval.
root.saved_y = self.block.y
# Now set y so the block is moved offscreen.
self.block.y = 5000
(I've tested this solution; it works.)

High CPU Usage in Gosu Drawing Program with Ruby

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.

Resources