How can I jump on Gosu gem from Ruby? - ruby

I'm trying to recreate a simple game but I'm stuck at the moment that I'm trying to make jump my character( or player).
I've been checking the example from gosu but it's not really clear for me.
Can someone help me?
Here is my window code:
class Game < Gosu::Window
def initialize(background,player)
super 640,480
self.caption = "My First Game!"
#background = background
#player = player
#player.warp(170,352)
#x_back = #y_back = 0
#allow_to_press = true
end
# ...
def update
#...
if Gosu.button_down? Gosu::KbSpace and #allow_to_press
#allow_to_press = false
#player.jump
end
end
end
Then my player class:
class Player
def initialize(image)
#image = image
#x = #y = #vel_x = #vel_y = #angle = 0.0
#score = 0
end
def warp(x,y)
#x,#y = x,y
end
def draw
#image.draw_rot(#x,#y,1,#angle,0.5,0.5,0.3,0.3)
end
def jump
##y -= 15 # THIS IS NOT CORRECT
end
end
Basically, the idea is when u press Space, then the jump method is invoked and here i should be able to recreate an animation to move the "y" position up ( for example 15 px) and then go down again.
Just i have on my mind change the value of #y variable but i don't know how to do it with an animation and then come back at the original point.
Thanks!

Finally I got it!
What I did is split the jump and an update method inside player:
class Player
def initialize(image)
#image = image
#x = #y = #angle = 0.0
#score = #vel_y = #down_y = 0
#allow = true
end
def warp(x,y)
#x,#y = x,y
end
def update(jump_max)
#Jump UP
if #vel_y > 0
#allow = false
#vel_y.times do
#y-=0.5
end
#vel_y-=1
end
#Jump DOWN
if #vel_y < 0
(#vel_y*-1).times do
#y+=0.5
end
#vel_y+=1
end
check_jump
end
def draw
#image.draw_rot(#x,#y,1,#angle,0.5,0.5,0.3,0.3)
end
def jump(original)
#vel_y = original
#down_y = original * -1
end
def check_jump
if #vel_y == 0 and #allow == false
#vel_y = #down_y
#down_y = 0
#allow = true
end
end
end
And then on my Game class
...
def initialize(background,player,enemy_picture)
#size of the game
super 640,480
#original_y = 352
#original_x = 170
self.caption = "Le Wagon Game!"
#background = background
#player = player
#player.warp(#original_x,#original_y)
#enemy_anim = enemy_picture
#enemy = Array.new
#Scrolling effect
#x_back = #y_back = 0
#jump = true
#jumpy = 25
end
def update
if Gosu.button_down? Gosu::KbSpace and #jump
#jump = false
#player.jump(#jumpy)
end
#player.update(#jumpy)
end
def button_up(id)
if id == Gosu::KbSpace
#jump = true
end
super
end
end
So now I can press Space and it goes up, after finish the transition, it goes down

Related

I'm trying to incorporate scenes into my mini space shooter game but Private Method Error keeps occuring

Whenever the game reaches the draw_end method it crashes. The draw_start and draw_game methods work great but the game crashes when it reaches the draw_end and therefore the ending credits cannot be displayed. The error is are these :
Traceback (most recent call last):
9: from sector_five_scenes.rb:275:in `<main>'
8: from /Users/skynet/.rvm/rubies/ruby-2.6.3/lib/ruby/gems/2.6.0/gems/gosu-0.14.5/lib/gosu/patches.rb:72:in `tick'
7: from /Users/skynet/.rvm/rubies/ruby-2.6.3/lib/ruby/gems/2.6.0/gems/gosu-0.14.5/lib/gosu/patches.rb:72:in `tick'
6: from sector_five_scenes.rb:29:in `draw'
5: from sector_five_scenes.rb:237:in `draw_end'
4: from /Users/skynet/.rvm/rubies/ruby-2.6.3/lib/ruby/gems/2.6.0/gems/gosu-0.14.5/lib/gosu/compat.rb:165:in `block (2 levels) in <class:Window>'
3: from /Users/skynet/.rvm/rubies/ruby-2.6.3/lib/ruby/gems/2.6.0/gems/gosu-0.14.5/lib/gosu/compat.rb:165:in `clip_to'
2: from sector_five_scenes.rb:238:in `block in draw_end'
1: from sector_five_scenes.rb:238:in `each'
sector_five_scenes.rb:239:in `block (2 levels) in draw_end': private method `draw' called for #<Credit:0x00007ff1b48ad448> (NoMethodError)
I have tried renaming the methods in the credits file. I have tried adding and removing the require_relative 'credit'. I removed the #credits array from the initialize_end method and placed it into the initialize method
Aside from that I'm really out of ideas of where to go from here and would really appreciate your help.
This portion connects all the classes to the main file
require 'gosu'
require_relative 'credit'
require_relative 'player'
require_relative 'enemy'
require_relative 'bullet'
require_relative 'explosion'
This part opens the window and sets the first scene.
class SectorFive < Gosu::Window
WIDTH = 800
HEIGHT = 600
ENEMY_FREQUENCY = 0.05
MAX_ENEMIES = 100
def initialize
super(WIDTH, HEIGHT)
self.caption = "Sector Five"
#background_image = Gosu::Image.new('ima/start.png')
#scene = :start
end
#The draw method below is the guide for this program.
def draw
case #scene
when:start
draw_start
when:game
draw_game
when:end
draw_end
end
end
This sets the background image
def draw_start
#background_image.draw(0,0,0)
end
This draws all the enemy, player, bullets, and explosions
def draw_game
#player.draw
#background_image.draw(0,0,0)
#enemies.each do |enemy|
enemy.draw
end
#enemybullets.each do |bullet|
bullet.draw
end
#bullets.each do |bullet|
bullet.draw_two
end
#explosions.each do |explosion|
explosion.draw
end
#playerexplosions.each do |explosion|
explosion.draw_two
end
end
This allows the game to update every frame
def update
case #scene
when :game
update_game
when :end
update_end
end
end
This executes the start of the game after the first scene
def button_down(id)
case #scene
when :start
button_down_start(id)
when :game
button_down_game(id)
when :end
button_down_end(id)
end
end
This method calls the initialize_game method
def button_down_start(id)
initialize_game
end
This method defines the initialize_game method by defining the variables
def initialize_game
#background_image = Gosu::Image.new('ima/space.png')
#player = Player.new(self)
#enemies = []
#bullets = []
#enemybullets = []
#random_enemy_bullets = []
#explosions = []
#playerexplosions = []
#framecounter = 0
#scene = :game
#enemies_appeared = 0
#enemies_destroyed = 0
#credits = []
end
This is what allows the game to function. It tells the game what to do!
def update_game
#framecounter += 1
#player.turn_left if button_down?(Gosu::KbLeft)
#player.turn_right if button_down?(Gosu::KbRight)
#player.accelerate if button_down?(Gosu::KbUp)
#player.backward if button_down?(Gosu::KbDown)
#player.move
if rand < ENEMY_FREQUENCY
#enemies.push Enemy.new(self)
#enemies_appeared += 1
end
#enemies.each do |enemy|
enemy.move
end
#Fires a bullet
#enemies.select do |enemy|
if #framecounter % 600 == 0
#enemybullets.push Bullet.new(self, #enemies[-1].x, #enemies[-1].y, #enemies[-1].angle)
if #enemies.length > 3
#enemybullets.push Bullet.new(self, enemy.x, enemy.y, enemy.angle)
end
end
end
# Detects whether there is a collision between a player bullet and an enemy.
#enemies.dup.each do |enemy|
#bullets.dup.each do |bullet|
distance = Gosu.distance(enemy.x, enemy.y, bullet.x, bullet.y)
if distance < enemy.radius + bullet.radius
#enemies.delete enemy
#bullets.delete bullet
#explosions.push Explosion.new(self, enemy.x, enemy.y)
#enemies_destroyed += 1
end
end
end
#explosions.dup.each do |explosion|
if #explosions.length > 15
#explosions.delete explosion
#explosions.delete explosion
end
end
#explosions.dup.each do |explosion|
#enemies.dup.each do |enemy|
distance = Gosu.distance(explosion.x, explosion.y, enemy.x, enemy.y)
if distance < explosion.radius + enemy.radius
#enemies.delete enemy
#explosions.push Explosion.new(self, enemy.x, enemy.y)
#enemies_destroyed += 1
end
end
end
#Explosions on the player
#enemybullets.each do |bullet|
distance = Gosu.distance(bullet.x, bullet.y, #player.x, #player.y)
if distance < bullet.radius + #player.radius
#enemybullets.delete bullet
#playerexplosions.push Explosion.new(self, #player.x, #player.y)
end
end
#playerexplosions.dup.each do |explosion|
#explosions.delete explosion unless explosion.finishedtwo
end
#playerexplosions.dup.each do |explosion|
#enemies.dup.each do |enemy|
distance = Gosu.distance(explosion.x, explosion.y, enemy.x, enemy.y)
if distance < explosion.radius + enemy.radius
#enemies.delete enemy
#playerexplosions.delete explosion
#explosions.push Explosion.new(self, enemy.x, enemy.y)
end
end
end
#enemies.dup.each do |enemy|
if enemy.y > HEIGHT + enemy.radius
#enemies.delete enemy
end
end
#bullets.dup.each do |bullet|
#bullets.delete bullet unless bullet.onscreen?
end
#enemybullets.each do |bullet|
bullet.enemy_move
end
#bullets.each do |bullet|
bullet.move
end
#explosions.each do |explosion|
explosion.move
end
#playerexplosions.each do |explosion|
explosion.move
end
This part of the update method defines when the game ends.
initialize_end(:count_reached) if #enemies_appeared > MAX_ENEMIES
#enemies.each do |enemy|
distance = Gosu::distance(enemy.x, enemy.y, #player.x, #player.y)
initialize_end(:hit_by_enemy) if distance < #player.radius + enemy.radius
end
initialize_end(:off_top) if #player.y < -#player.radius
end
This method allows the player to fire bullets
def button_down_game(id)
if id == Gosu::KbSpace
#bullets.push Bullet.new(self, #player.x, #player.y, #player.angle)
end
end
This method sets the end scene("The scene that is not working") and displays messages on the bottom of the screen and top of the screen depending on the players fate and also is suppose to display the ending credits in the center of the screen produced by the credits.txt file. The credits should slowly move -y out of the window.
def initialize_end(fate)
case fate
when :count_reached
#message = "You Made it! You destroyed #{#enemies_destroyed} ships"
#message2 = "and #{100 - #enemies_destroyed} reached the base."
when :hit_by_enemy
#message = "You were struck by an enemy ship"
#message2 = "Before your ship was destroyed, "
#message2 += "you took out #{#enemies_destroyed} enemy ships!"
when :off_top
#message = "You got too close to the enemy mothership."
#message2 = "Before your ship was destroyed, "
#message2 += "you took out #{#enemies_destroyed} enemy ships!"
end
#bottom_message = "Press P to play again, or Q to quit."
#message_font = Gosu::Font.new(28)
y = 700
File.open('credits.txt').each do |line|
#credits.push(Credit.new(self,line.chomp,100,y))
y+=30
end
#scene = :end
end
This is the method that is producing the Private Method error. I can't figure out why.
def draw_end
clip_to(50,140,700,360) do
#credits.each do |credit|
credit.draw
end
end
draw_line(0,140,Gosu::Color::RED,WIDTH,140,Gosu::Color::RED)
#message_font.draw(#message,40,40,1,1,1,Gosu::Color::FUCHSIA)
#message_font.draw(#message2,40,75,1,1,1,Gosu::Color::FUCHSIA)
draw_line(0,500,Gosu::Color::RED,WIDTH,500,Gosu::Color::RED)
#message_font.draw(#bottom_message,180,540,1,1,1,Gosu::Color::AQUA)
end
The methods below do not work either. Produces the same Private method error. I don't understand why this doesn't work because I have used require_relative on the credit ruby file.
def update_end
#credits.each do |credit|
credit.move
end
if #credits.last.y < 150
#credits.each do |credit|
credit.reset
end
end
end
# This method gives the player the option to play again or quit the game and the player will select the button based on his or her preference.
def button_down_end(id)
if id == Gosu::KbP
initialize_game
elsif id == Gosu::KbQ
close
end
end
end
window = SectorFive.new
window.show
----------------------------------------------------------------------
"This is the Credit class"
class Credit
SPEED = 1
attr_reader :y, :x, :text
def initialize(window,text, x, y)
#x = x
#y = #initial_y = y
#text = text
#font = Gosu::Font.new(24)
end
end
def move
#y -= SPEED
end
def draw
#font.draw(#text, #x, #y, 1)
end
def reset
#y = #initial_y
end
-----------------------------------------------------------------------
"""The credits.txt file"""
"This is a part of the File.open method used to push the line into the credits array."
SectorFive
By: John Jordan Shelley
Based on a Tutorial from
Learn Game Programming with Ruby
By Mark Sobkowicz
—Images——
Game art from OpenGameArt.org
Licensed under Creative Commons: Domain CC0
The game should smoothly execute the initialize end method and give the player options depending on the player's fate and play the ending credits.

Possible moves on a chess board, using instance from one class in another.

I'm working on making a chess game. I want to keep everything that has to do with possible moves inside each type of pieces class. In order to do this I need to refer back to #board[][]. The commented section of code in the possible_moves method is what I would like to do, except it does not work and throws an error.
class Board
attr_accessor :board, :choice
def initialize
#board = Array.new(8){Array.new(8," ")}
#choice = choice
end
def set_board
#board[0][2] = Bishop.new([0,2],false)
end
end
class Bishop
attr_accessor :x_position, :y_position, :piece, :color, :moves
def initialize(position,is_white)
#x_position = position[0]
#y_position = position[1]
#piece = is_white ? "♝" : "♗"
#color = is_white ? "white" : "black"
#moves = [[+1,-1],
[+1,+1],
[-1,+1],
[-1,-1]]
end
def possible_moves
move_list = Array.new
#moves.each do |moves|
x = #x_position
y = #y_position
loop do
x += moves[0]
y += moves[1]
break if x.between?(0,7) == false
break if y.between?(0,7) == false
# This is where I want to refer back to #board from the Board class.
# if #board[x][y].color.....
move_list << [x,y]
#end
end
end
p move_list
end
end
You can pass the board into the Bishop constructor:
class Bishop
attr_reader :board
# etc.
def initialize(position,is_white,board)
#board = board.board # or just #board = board and you can fetch the matrix later
# etc.
end
end
class Board
# etc.
def set_board
#board[0][2] = Bishop.new([0,2],false, self)
end
end

attr_accessor - Accessing an objects attributes from another class

I want to access the ogre's object's swings attribute from the Human's class. However, all I am getting is:
NameError: undefined local variable or method ogre for
**<Human:0x007fdb452fb4f8 #encounters=3, #saw_ogre=true>
Most likely a simple solution, and my brain is just not operating this morning. I am running tests with minitest. The test and classes are below:
ogre_test.rb
def test_it_swings_the_club_when_the_human_notices_it
ogre = Ogre.new('Brak')
human = Human.new
ogre.encounter(human)
assert_equal 0, ogre.swings
refute human.notices_ogre?
ogre.encounter(human)
ogre.encounter(human)
assert_equal 1, ogre.swings
assert human.notices_ogre?
end
ogre.rb
class Ogre
attr_accessor :swings
def initialize(name, home='Swamp')
#name = name
#home = home
#encounters = 0
#swings = 0
end
def name
#name
end
def home
#home
end
def encounter(human)
human.encounters
end
def encounter_counter
#encounters
end
def swing_at(human)
#swings += 1
end
def swings
#swings
end
end
class Human
def initialize(encounters=0)
#encounters = encounters
#saw_ogre = false
end
def name
"Jane"
end
def encounters
#encounters += 1
if #encounters % 3 == 0 and #encounters != 0
#saw_ogre = true
else
#saw_ogre = false
end
if #saw_ogre == true
ogre.swings += 1 # <----issue
end
end
def encounter_counter
#encounters
end
def notices_ogre?
#saw_ogre
end
end
The easy fix would be to pass the ogre object as an argument to encounters - assuming encounters isn't used anywhere else without the argument.
class Ogre
...
def encounter(human)
human.encounters(self)
end
...
end
class Human
...
def encounters(ogre)
#encounters += 1
if #encounters % 3 == 0 and #encounters != 0
#saw_ogre = true
else
#saw_ogre = false
end
if #saw_ogre == true
ogre.swings += 1 # <----issue
end
end
...
end

/usr/local/lib/ruby/gems/2.2.0/gems/gosu-0.10.4/lib/gosu/patches.rb:37:in `initialize': Cannot open file ./media/image.jpg (RuntimeError) in gosu

I have this code which is not giving the desired output and shows the above error.
require 'gosu'
def media_path(file)
File.join(File.dirname(File.dirname(
__FILE__)),'media', file)
end
#def media_path(file); File.expand_path "media/#{file}", File.dirname(__FILE__)
# end
class Explosion
FRAME_DELAY = 10 # ms
SPRITE = media_path('space.png')
def self.load_animation(window)
Gosu::Image.load_tiles(
window, SPRITE, 128, 128, false)
end
def initialize(animation, x, y)
#animation = animation
#x, #y = x, y
#current_frame = 0
end
def update
#current_frame += 1 if frame_expired?
end
def draw
return if done?
image = current_frame
image.draw(
#x - image.width / 2.0,
#y - image.height / 2.0,
0)
end
def done?
#done ||= #current_frame == #animation.size
end
private
def current_frame
#animation[#current_frame % #animation.size]
end
def frame_expired?
now = Gosu.milliseconds
#last_frame ||= now
if (now - #last_frame) > FRAME_DELAY
#last_frame = now
end
end
end
class GameWindow < Gosu::Window
BACKGROUND = media_path('image.jpg')
def initialize(width=800, height=600, fullscreen=false)
super
self.caption = 'Hello Animation'
#background = Gosu::Image.new(
self, BACKGROUND, false)
#animation = Explosion.load_animation(self)
#explosions = []
end
def update
#explosions.reject!(&:done?)
#explosions.map(&:update)
end
def button_down(id)
close if id == Gosu::KbEscape
if id == Gosu::MsLeft
#explosions.push(
Explosion.new(
#animation, mouse_x, mouse_y))
end
end
def needs_cursor?
true
end
def needs_redraw?
!#scene_ready || #explosions.any?
end
def draw
#scene_ready ||= true
#background.draw(0, 0, 0)
#explosions.map(&:draw)
end
end
window = GameWindow.new
window.show
It can't find ./media/image.jpg are you sure it is there, and can be opened, and is the current directory "." what you think it is?

How to update Ruby GTK window label in real time and communicate with other threads

I am building a Ruby application which consists of the code responsible for the logic of the program and the one for GUI. Both parts of the code are split in classes and run in separate threads.
Ruby Gtk library is very poorly documented. I want to know how to update specific Gtk elements in real time (for instance, text in a label, which is in a window). I want to update a specific element every second.
I also want to find out how can threads exchange data. I have tried using Queue library. When I use it, I run into an error in the console:
undefined local variable or method `queue' for #<TimerWindow:0xa36d634 ptr=0xb4201178>
Program code:
require_relative 'notifications'
require_relative 'settings_reader'
require 'gtk3'
require "thread"
q = Queue.new
class TimerWindow < Gtk::Window
def initialize
#label = ""
super
init_ui
end
def init_ui
fixed = Gtk::Fixed.new
add fixed
button = Gtk::Button.new :label => "Quit"
button.set_size_request 80, 35
button.signal_connect "clicked" do
#label.set_text q.pop
end
fixed.put button, 50, 50
set_title "Tomatono"
signal_connect "destroy" do
Gtk.main_quit
end
set_border_width 10
#label = Gtk::Label.new "HEY"
fixed.put #label, 20, 20
set_default_size 250, 200
set_window_position :center
show_all
end
end
class Timer
def initialize
# Current time in seconds
#time = 0
settings = Settings_Reader.new
#work_time = Integer(settings.work_time) * 60
#break_time = Integer(settings.break_time) * 60
#work_text = settings.work_text
#return_text = settings.return_text
#break_text = settings.break_text
#work_notif_header = settings.work_notif_header
#break_notif_header = settings.break_notif_header
#status_notif_header = settings.status_notif_header
#work_status = settings.work_status
#break_status = settings.break_status
end
def launch
while true
work_time()
break_time()
end
end
def work_time()
puts #work_text
notification = Notif.new(#work_notif_header, #work_text)
notification.post
#time = 0
sleep(1)
while #time < #work_time
#time += 1
puts "#{min_remaining()} minutes remaining" if (#time % 60) == 0
if (#time % 60) == 0
notification = Notif.new(#work_notif_header, "#{#work_status} #{#time / 60} minutes.")
notification.post
end
q << #time
sleep(1)
end
end
def break_time
puts #break_text
#time = 0
sleep(1)
while #time < #break_time
#time += 1
puts "#{min_remaining()} minutes remaining" if (#time % 60) == 0
notification = Notif.new(#break_notif_header, "#{#break_status} #{#time / 60} minutes.")
notification.post
q << #time
sleep(1)
end
end
def reset
end
def stop_time
end
def min_remaining()
(1500 - #time) / 60
end
end
app = Thread.new {
timer = Timer.new
timer.launch
}
gui = Thread.new {
Gtk.init
window = TimerWindow.new
#window.update
Gtk.main
}
app.join
gui.join
Whenever I press the "Quit" button, I want the label text to change to the value set in the q variable, set in the Timer class (in the while loop). But it throws out an error that variable does not exist. Should it not be global?
No It is a local variable:
myglobal = "toto"
class Myclass
def initialize
#myvar = myglobal
end
def print
puts #myvar
end
end
an_instance = Myclass.new
an_instance.print
throw this error:
global_and_class.rb:5:in `initialize': undefined local variable or method `myglobal' for #<Myclass:0x00000000a74ce8> (NameError)
But it works if you specify myglobal as a global variable:
$myglobal = "toto"
class Myclass
def initialize
#myvar = $myglobal
end
def print
puts #myvar
end
end
an_instance = Myclass.new
an_instance.print
But you should be carefull with the use of global variable. Why not use the Queue instance as an argument for the initialize method ?
** Edit **
First of all here is a simple example that works with just a local variable:
#!/usr/bin/env ruby
require "gtk3"
label = Gtk::Label.new("test")
othert = Thread.new {
loop {
puts 'thread running';
label.text = Time.now.to_s; sleep 1 }
}
maint = Thread.new {
win = Gtk::Window.new
win.set_default_size 100, 30
win.add(label)
win.show_all
win.signal_connect("destroy") {othert.kill;Gtk.main_quit}
Gtk.main
}
maint.join
othert.join
Maybe you should start from this and see how to create you classes.
Edit 2
class TimerWindow < Gtk::Window
def initialize(label)
super()
add(label)
end
end
alabel = Gtk::Label.enw("test")
othert = Thread.new {
loop {
puts 'thread running';
label.text = Time.now.to_s; sleep 1 }
}
maint = Thread.new {
win = TimerWindow.new(alabel)
win.set_default_size 100, 30
win.show_all
win.signal_connect("destroy") {othert.kill;Gtk.main_quit}
Gtk.main
}
maint.join
othert.join

Resources