I have figured out how to randomly spawn enemies in different locations, but I cant figure out to make some of the enemies randomly fire bullets.
I created a Bullet class and a Enemy class. I use attr_reader to locate the enemies and call the x and y locations in the new Bullet method, but it fails to find where the enemy is located.
require 'gosu'
require_relative 'player'
require_relative 'enemy'
require_relative 'bullet'
class Proto < Gosu::Window
WIDTH = 1000
HEIGHT = 800
ENEMY_FREQUENCY = 0.03
attr_reader :x, :y, :radius, :angle
def initialize
super(WIDTH,HEIGHT)
self.caption = "Proto"
#player = Player.new(self)
#enemies = []
#bullets = []
#framecounter = 0
end
def update
#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)
end
#enemies.each do |enemy|
enemy.move
if #framecounter % 60 == 0 && #enemies[3]
#bullets.push Bullet.new(self, #enemy.x, #enemy.y, #enemy.angle)
end
end
#bullets.each do |bullet|
bullet.move
end
end
def draw
#player.draw
#enemies.each do |enemy|
enemy.draw
end
#bullets.each do |bullet|
bullet.draw
end
end
end
window = Proto.new
window.show
class Enemy
SPEED = 1
attr_reader :x, :y, :radius, :angle
def initialize(window)
#radius = 20
#x = rand(window.width - 2 * #radius) + #radius
#y = 0
#image = Gosu::Image.new('ima/tile000.png')
end
def move
#y += SPEED
end
def draw
#image.draw(#x - #radius, #y - #radius, 2)
end
end
require_relative 'enemy'
class Bullet
SPEED = 5
def initialize(window, x, y, angle)
#x = x
#y = y
#direction = angle
#image = Gosu::Image.new('ima/tile000.png')
#imaget = Gosu::Image.new('ima/tile000.png')
#radius = 3
#window = window
end
def move
#y += SPEED
end
def draw
#image.draw(#x - #radius, #y - #radius, 1)
#imaget.draw(#enemy.x - radius, #enemy.y - #radius, 1)
end
end
I expect random enemies to fire bullets.
As discussed in the comments, the issue was that you had
#enemies.each do |enemy|
#enemy.draw
end
instead of
#enemies.each do |enemy|
enemy.draw
end
Related
I'm creating a game with ruby and gosu, and I get an error:
C:/Ruby26-x64/destroy!/bullet.rb:14:in draw_rot': There is no rendering queue for this operation (RuntimeError)
from C:/Ruby26-x64/destroy!/bullet.rb:14:indraw'
from C:/Ruby26-x64/destroy!/player.rb:45:in fire'
from C:/Ruby26-x64/destroy!/destroy.rb:36:inupdate'
from C:/Ruby26-x64/lib/ruby/gems/2.6.0/gems/gosu-0.14.5-x64-mingw32/lib/gosu/patches.rb:72:in tick'
from C:/Ruby26-x64/lib/ruby/gems/2.6.0/gems/gosu-0.14.5-x64-mingw32/lib/gosu/patches.rb:72:intick'
from C:/Ruby26-x64/destroy!/destroy.rb:40:in `'
[Finished in 0.517s]
I've tried to change draw in the bullet class to draw_rot, but no avail.
My Classes
Destroy
require 'gosu'
require 'cmath'
require_relative 'player.rb'
require_relative 'enemy.rb'
require_relative 'bullet.rb'
class Destroy < Gosu::Window
def initialize
super(800, 600)
self.caption = 'Destroy!'
#player = Player.new(self)
#enemies = []
#enemies.push(Enemy.new(self))
end
def draw
#player.draw
#enemies.each do |enemy|
enemy.draw
end
end
def update
#player.righturn if button_down?(Gosu::KbRight)
#player.lefturn if button_down?(Gosu::KbLeft)
#player.startmove
#player.move if button_down?(Gosu::KbUp)
#freq = 0.0025
if rand < #freq
#enemies.push(Enemy.new(self))
end
#enemies.each do |enemy|
enemy.move
enemy.update(#player.x, #player.y)
end
if #freq < 0.5
#freq += 0.0002
end
#player.fire(self)
end
end
window = Destroy.new
window.show
Bullet
class Bullet
attr_accessor :x
attr_accessor :y
attr_accessor :fired
def initialize(window)
#image = Gosu::Image.new('C:\Ruby26-x64\destroy!\images\bullet.png')
#fired = 0
#x = 0
#y = 0
end
def draw(x, y)
#x = x
#y = y
#image.draw_rot(x, y, 1, 0)
#fired = 1
end
def update(angle)
#xspeed = Gosu.offset_x(angle, 2)
#yspeed = Gosu.offset_y(angle, 2)
if (#x > 800 || #x < 0 || #y > 600 || #y < 0)
#fired = 0
end
end
end
Player
require_relative 'bullet.rb'
require 'gosu'
class Player
attr_accessor :x
attr_accessor :y
def initialize(window)
#x = 200
#y = 200
#xspeed = 0
#yspeed = 0
#angle = 0
#image = Gosu::Image.new('C:/Ruby26-x64/destroy!/images/man.png')
end
def draw
#image.draw_rot(#x, #y, 1, #angle)
end
def righturn
#angle += 3
end
def lefturn
#angle -= 3
end
def startmove
#xspeed = Gosu.offset_x(#angle, 2)
#yspeed = Gosu.offset_y(#angle, 2)
end
def move
if #x > 767
#xpeed = 0
#x = 767
end
if #x < 33
#xpeed = 0
#x = 33
end
if #y > 567
#yspeed = 0
#y = 567
end
#x += #xspeed
#y += #yspeed
end
def fire(window)
#bullet = Bullet.new(window)
#bullet.draw(#x, #y)
while #bullet.fired = 1
#bullet.update(#angle)
end
end
end
Enemy
require 'cmath'
class Enemy
def initialize(window)
#flipped = 0
#x = rand(800 - 2 * 30) + 30
#y = rand(600 - 2 * 30) + 30
#firefreq = 1 / 60
#health = 5
#numkilled = 0
#dead = 0
#xspeed = 0
#yspeed = 0
#angle = 0
#image = Gosu::Image.new('C:/Ruby26-x64/destroy!/images/enemy.png')
#imageflipped = Gosu::Image.new('C:/Ruby26-x64/destroy!/images/enemyflip.png')
end
def gethit
#health -= 1
end
def die
#numkilled += 1
#dead = 1
end
def move
if rand < 0.1
#xspeed = rand(-3..3)
#yspeed = rand(-3..3)
end
if #x + #xspeed > 800
#x = 800
end
if #x + #xspeed < 0
#x = 0
end
if #y + #yspeed > 600
#y = 600
end
if #y + #yspeed < 0
#y = 0
end
if !(#y + #yspeed < 0 && #y + #yspeed > 600 && #x + #xspeed < 0 && #x + #xspeed > 800)
#y += #yspeed
#x += #xspeed
end
end
def draw
if #dead == 0
if #flipped == 1
#imageflipped.draw_rot(#x, #y, 1, #angle)
end
if #flipped == 0
#image.draw_rot(#x, #y, 1, #angle)
end
end
end
def update(xdist, ydist)
if #x < xdist
(#angle = CMath.atan((ydist - #y) / (xdist - #x)) * 180 / 3.14159265358979323846264338327950289)
#flipped = 0
end
if #x > xdist
(#angle = CMath.atan(-(ydist - #y) / (#x - xdist)) * 180 / 3.14159265358979323846264338327950289)
#flipped = 1
end
end
end
I do not get this at all.
Your Destroy#update method calls Player#fire which calls Bullet#draw which calls Gosu::Image#draw_rot. You can't call draw methods from within the main update method.
You will have to move your #bullet.draw method call out from Player#fire (which gets called during main update) and into Player#draw (which gets called during main draw)
So far my shortest path method will stop when it reaches the goal position, printing out everything it did along the way. I would like to know how I could go about implementing parent positions so I can print the path along with the goal. This is an assignment.
class Knight
attr_accessor :x, :y, :prev_position, :moves
def initialize(position)
#x = position[0]
#y = position[1]
#prev_position = nil
#moves = [
[-1,-2],
[-2,-1],
[-2,+1],
[-1,+2],
[+1,-2],
[+2,-1],
[+2,+1],
[+1,+2]]
end
def possible
move_list = Array.new
#moves.each do |moves|
x = #x + moves[0]
y = #y + moves[1]
if x.between?(0,7)
if y.between?(0,7)
move_list << [x,y]
end
end
end
move_list
end
end
def shortest_path(position,goal)
paths = Array.new
#start_knight = Knight.new(position)
until #start_knight.x == goal[0] and
#start_knight.y == goal[1]
#start_knight.possible.each do |p| paths << p end
shifted = paths.shift
#start_knight.x = shifted[0]
#start_knight.y = shifted[1]
puts "[#{#start_knight.x},#{#start_knight.y}]"
end
end
shortest_path([0,0],[7,7])
In the code below, a cluster has many points.
class Cluster
attr_accessor :centroid, :points
def initialize(centroid, *points)
#centroid = centroid
#points = points
end
end
class Point
attr_accessor :x, :y
def initialize(x = 0, y = 0)
#x = x
#y = y
end
end
An example of a Cluster object (lets us call this c):
#<Cluster:0x007ff5c123c210
#centroid=#<Point:0x007ff5c123c288 #x=25, #y=125>,
#points=
[#<Point:0x007ff5c123c238 #x=25, #y=125>,
#<Point:0x007ff5c1020120 #x=28, #y=145>]>
I am trying to calculate the mean of the points, and update #centroid without changing #points.
Let's say I have:
class Point
def +(point)
#x = #x + point.x
#y = #y + point.y
self
end
def /(num)
#x = #x/num
#y = #y/num
self
end
end
and to calculate the mean of all the points, I run:
c.centroid = c.points.reduce(&:+)/c.points.length
Then, c changes to:
#<Cluster:0x007ff5c123c210
#centroid=#<Point:0x007ff5c1515ec8 #x=26, #y=135>,
#points=
[#<Point:0x007ff5c1515ec8 #x=26, #y=135>,
#<Point:0x007ff5c1020120 #x=28, #y=145>]>
Note that first element of the #points is changed. Any suggestions?
Your + method in Point modifies the point's members #x and #y. You need to return a new point with the calculated values instead:
def +(point)
Point.new(#x + point.x, #y + point.y)
end
def /(num)
Point.new(#x/num, #y/num)
end
Since you did not pass an initial value to reduce, the first point became modified. You can pass a new point as the initial value to reduce, which will be modified and returned.
c.centroid = c.points.reduce(Point.new, &:+)/c.points.length
I think the problem is caused by the + method modifying the point #x and #y values.
Try changing the + method to:
def +(point)
x = #x + point.x
y = #y + point.y
self.class.new(x, y)
end
I've been trying out the Gosu tutorial so I can learn to make games. I'm getting errors with my code. Could someone point out to me what I'm doing wrong? Here's the tutorial link:
https://github.com/jlnr/gosu/wiki/Ruby-Tutorial
here's my code:
require 'Gosu'
class GameWindow < Gosu::Window
def initialize
super 640, 480, false
self.caption = "Gosu Tutorial Game"
#background_image = Gosu::Image.new(self, "Usable Images/final_fantasy_background.png", true)
#player = Player.new(self)
#player.warp(320, 240)
#Star_anim = Gosu::Image::load_tiles(self, "Usable Images/donut.png", 25, 25, false)
#Star = Array.new
end
def update
if button_down? Gosu::KbLeft or button_down? Gosu::GpLeft then
#player.turn_left
end
if button_down? Gosu::KbRight or button_down? Gosu::GpRight then
#player.turn_left
end
if button_down? Gosu::KbUp or button_down? Gosu::GpButton0 then
#player.accelerate
end
#player.move
#player.collect_stars(#stars)
if rand(100) < 4 and #Star.size < 25 then
#Star.push(Star.new(#Star_anim))
end
end
def draw
#background_image.draw(0,0,ZOrder::Background)
#player.draw
stars = Star.new()
#stars.each{ |star| star.draw}
end
def button_down(id)
if id == Gosu::KbEscape
close
end
end
end
class Player
def initialize(window)
#image = Gosu::Image.new(window, "Usable Images/eric_cartman.png", false)
#x = #y = #vel_x = #vel_y = #angle = 0.0
#score = 0
end
def warp(x, y)
#x, #y = x, y
end
def turn_left
#angle -= 4.5
end
def turn_right
#angle += 4.5
end
def accelerate
#vel_x += Gosu::offset_x(#angle, 0.5)
#vel_y += Gosu::offset_x(#angle, 0.5)
end
def move
#x += #vel_x
#y += #vel_y
#x %= 640
#y %= 480
#vel_x *= 0.95
#vel_y *= 0.95
end
def draw
#image.draw_rot(#x, #y, 1, #angle)
end
def score
#score
end
def collect_Star(star)
if Star.reject! {|star| Gosu::distance(#x, #y, Star.x, star.y) < 35} then
#score += 1
end
end
end
module ZOrder
Background, Star, Player, UI = *0..3
end
class Star
attr_reader :x, :y
def initialize(animation)
#animation = animation
#color = Gosu::Color.new(0xff000000)
#color.red = rand(256 - 40) + 40
#color.green = rand(256 - 40) + 40
#color.blue = rand(256 - 40) + 40
#x = rand * 640
#y = rand * 480
end
def draw
img = #animation[Gosu::milliseconds / 100 % #animation.size];
img.draw(#x - image.width / 2.0, #y - img.height / 2.0, ZOrder::Star, 1, 1, #color, :add)
end
end
window = GameWindow.new
window.show
You have Star.new(), but you define it as initialize(animation) (with one mandatory argument).
Hi I am building a clone pong program with ruby and rubygame. My ain problem at the moment is the collision of the leftside. The collision happends backwards. The right works perfectly fine. I need help. Can anyone fix this?
Heres my code
require 'rubygems'
require 'rubygame'
Rubygame::TTF.setup
class Game
def initialize
#screen = Rubygame::Screen.new [640,480], 0,
[Rubygame::HWSURFACE, Rubygame::DOUBLEBUF]
#screen.title = "Pong"
#queue = Rubygame::EventQueue.new
#clock = Rubygame::Clock.new
#clock.target_framerate = 60
limit = #screen.height - 10
#player = Paddle.new 50, 10, Rubygame::K_W, Rubygame::K_S, 10, limit
#enemy = Paddle.new #screen.width-50-#player.width, 10,
Rubygame::K_UP, Rubygame::K_DOWN, 10, limit
#player.center_y #screen.height
#enemy.center_y #screen.height
#ball = Ball.new #screen.width/2, #screen.height/2
#background = Background.new #screen.width, #screen.height
end
def run!
loop do
update
draw
#clock.tick
end
end
def update
#player.update
#enemy.update
#ball.update #screen
#queue.each do |ev|
#player.handle_event ev
#enemy.handle_event ev
case ev
when Rubygame::QuitEvent
Rubygame.quit
exit
when Rubygame::KeyDownEvent
if ev.key==Rubygame::K_ESCAPE
#queue.push Rubygame::QuitEvent.new
end
end
end
if collision? #ball, #player
#ball.collision #player, #ball
elsif collision? #ball, #enemy
#ball.collision #enemy, #ball
end
end
def draw
#screen.fill [0,0,0]
#background.draw #screen
#player.draw #screen
#enemy.draw #screen
#ball.draw #screen
#screen.flip
end
def collision? obj1, obj2
if obj1.y + obj1.height < obj2.y ; return false ; end
if obj1.y > obj2.y + obj2.height ; return false ; end
if obj1.x + obj1.width < obj2.x ; return false ; end
if obj1.x > obj2.x + obj2.width ; return false ; end
return true
end
end
class GameObject
attr_accessor :x, :y, :width, :height, :surface
def initialize x, y, surface
#x = x
#y = y
#surface = surface
#width = surface.width
#height = surface.height
end
def update
end
def draw screen
#surface.blit screen, [#x, #y]
end
def handle_event event
end
end
class Paddle < GameObject
def initialize x,y,up_key,down_key,top_limit,bottom_limit
surface = Rubygame::Surface.new [20, 100]
surface.fill [255, 255, 255]
#up_key = up_key
#down_key = down_key
#moving_up = false
#moving_down = false
#top_limit = top_limit
#bottom_limit = bottom_limit
super x, y, surface
end
def center_y h
#y = h/2-#height/2
end
def handle_event event
case event
when Rubygame::KeyDownEvent
if event.key==#up_key
#moving_up = true
elsif event.key==#down_key
#moving_down = true
end
when Rubygame::KeyUpEvent
if event.key==#up_key
#moving_up = false
elsif event.key==#down_key
#moving_down = false
end
end
end
def update
if #moving_up and #y > #top_limit
#y -= 5
end
if #moving_down and #y+#height < #bottom_limit
#y += 5
end
end
end
class Background < GameObject
def initialize width, height
surface = Rubygame::Surface.new [width, height]
# Draw Background
white = [255, 255, 255]
#Top
surface.draw_box_s [0, 0], [surface.width, 10], white
#Left
surface.draw_box_s [0, 0], [10, surface.height], white
#Bottom
surface.draw_box_s [0, surface.height-10, 10],
[surface.width, surface.height], white
#Right
surface.draw_box_s [surface.width-10, 0],
[surface.width, surface.height], white
#Middle Divide
surface.draw_box_s [surface.width/2-5, 0],
[surface.width/2+5, surface.height], white
super 0, 0, surface
end
end
class Ball < GameObject
def initialize x, y
surface = Rubygame::Surface.load('Ball.png')
#vx = #vy = 5
super x, y, surface
end
def update screen
#x += #vx
#y += #vy
if #x <= 10 or #x+#width >= screen.width-10
#vx *= -1
end
if #y <= 10 or #y+#height >= screen.height-10
#vy *= -1
end
end
def collision paddle, screen
if paddle.x < screen.width/2
unless #x < paddle.x-5
#x = paddle.x+paddle.width+1
#vx *= -1
end
else
unless #x > paddle.x+5
#x = paddle.x-#width-1
#vx *= -1
end
end
end
end
class Text < GameObject
def initialize x=0, y=0, text="Hello, World!", size=40
#font = Rubygame::TTF.new "font.ttf", size
#text = textssssw
super x, y, #font.render(#text, true, [255,255,255])
end
end
g = Game.new
g.run! `