ruby gosu moving image to the direction is pointed at - ruby

i have been trying to make an image move to the direction it is pointed at.
however it just keeps moving in random directions :(
cant figure out why...
here's the code:
require 'gosu'
class Game < Gosu::Window
def initialize
super(1280, 720, true)
#image = Gosu::Image.from_text ':', 100
#ang = 1
#x = 640 - #image.width
#y = 360 - #image.height
end
def update
#sngX = (10.0/Math.cos(#ang.to_f)).to_i
#sngY = (10.0/Math.sin(#ang.to_f)).to_i
#n = Gosu::Image.from_text #ang.to_s, 50
end
def draw
#image.draw_rot #x, #y, 0, #ang
#n.draw 0, 0, 0
end
def button_down id
close if id == Gosu::KbEscape
if id == Gosu::KbE
#ang += 10
end
if id == Gosu::KbQ
#ang -= 10
end
if id == Gosu::KbW
#x += #sngX
#y += #sngY
end
end
end
Game.new.show

It definitely does not behave random.
I'd bet the issue is with Math trigonometric functions expect values in radians not grads, so #sngX and #sngY are assigned extreme results.
From the Ruby docs to Math::cos:
Computes the cosine of x (expressed in radians). Returns a Float in the range -1.0..1.0
Hope you know how to convert grads to radians.

Related

ruby packing variables and monkey patching

Okay so this is the code:
class Board
attr_reader :size
def initialize n
#grid = Array.new(n) {Array.new(n,:N)}
#size = n * n
end
def [] position
row, col = position
#grid[row][col]
end
def []= position, val
row, col = position
#grid[row][col] = val
end
def num_ships
#grid.flatten.count(:S)
end
def attack position
if self[position] == :S
self[position] = :H
puts "you sunk my battleship"
return true
else
self[position] = :X
return false
end
end
def place_random_ships
max_ships = #size * 0.25
while self.num_ships < max_ships
row = rand(0...#grid.length)
col = rand(0...#grid.length)
position = [row,col]
self[position] = :S
end
end
end
But,
def place_random_ships
max_ships = #size * 0.25
while self.num_ships < max_ships
row = rand(0...#grid.length)
col = rand(0...#grid.length)
position = [row,col]
self[position] = :S
end
end
this works, and does what it's suppose to, but when I avoid packing [row,col] and add it directly it does not work.
def place_random_ships
max_ships = #size * 0.25
while self.num_ships < max_ships
row = rand(0...#grid.length)
col = rand(0...#grid.length)
self[row,col] = :S
end
end
I'm still new to programming, so please try to explain the issue to where I can understand it, or tell me the problem, so I can google it to get a better understanding please.
The issue is that you defined []= to take 2 argument, and array and a value.
def []= position, val
row, col = position
#grid[row][col] = val
end
With your current implementation you would need to call it like this
foo[[row,col]] = :S
What you might want to to is define []= like this:
def []= row, col, val
#grid[row][col] = val
end
then when you want to pass the array position you can use the array spread operator. With this implementation both of these calls will work.
position = [1,2]
foo[1,2] = :S
foo[*position] = :S
if you do that you probably would want to define [] the same way.
def [] row,col
#grid[row][col]
end

Trouble making random spawning enemies shoot bullets

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

What is the correct way to write this in Ruby?

I am needing to write few methods: value(x), zero(a,b,e), area(a,b), derivative(x)
class Funkcja
def initialize(funkcja)
#funkcja = funkcja
end
def value(x)
#funkcja.call(x)
end
end
This class will have to work over block which is an object from Proc
This is how I create that new object
f = Funkcja.new (Proc.new{|x| x*x*Math.sin(x)})
What is the correct way and in Ruby style (if not please show me that i
newbie in Ruby) to do this right Funkcja.new (Proc.new x) and
initialize #funkcja = funkcja
def zero(a, b, eps)
x = (a+b)/2.0
val = value(x)
if val >= -eps and val <= eps
x
else
left = value(a)
rigth = value(b)
if left < 0 and val > 0
zero(a,x,eps)
elsif left > 0 and val < 0
zero(a,x,eps)
elsif rigth > 0 and val < 0
zero(x,b,eps)
elsif rigth < 0 and val > 0
zero(x,b,eps)
elsif value == 0
x
else
nil
end
end
end
def area(a,b)
pole = 0
while a < b
if (self.value(a) > self.value( a + 0.00001))
pole = pole + (self.value( a) * 0.00001)
else
pole = pole + (self.value( a + 0.00001) * 0.00001 )
end
a += 0.00001
end
pole
end
def derivative(x)
eps = 0.00000001
return (self.value(x) - self.value(x - eps))/eps
end
Area is calculated area between a and b and OX, zero is find where
F (x)=0 derivative is calculated as derivative in point.
The main thing that's non-idiomatic is this:
f = Funkcja.new (Proc.new{|x| x*x*Math.sin(x)})
What would be more normal is to do this:
f = Funkcja.new { |x| x*x*Math.sin(x) }
This is a normal block, and you could split it up among multiple lines as usual:
f = Funkcja.new do |x|
x*x*Math.sin(x)
end
However, this wouldn't work with your initialize definition and that's because of one minor detail. You'd just need to change def initialize(funkcja) to def initialize(&funkja) - this converts the passed block into a proc that you can assign to a variable, use call with, etc:
def initialize(&funjka)
#funkja = funkja
end
Another way to do the same thing would be this:
def initialize
#funkja = yield
end
Other than that, your code seems fine with one other glaring non-idiomatic thing, which that you use self.value. The self is unnecessary unless you're using a setter method (i.e. self.value =), which you're not here.
Here's an idea of adapting one of your methods to a more Ruby style:
def area(a,b)
pole = 0
while a < b
if (yield(a) > yield(a + 0.00001))
pole = pole + (yield(a) * 0.00001)
else
pole = pole + (yield(a + 0.00001) * 0.00001)
end
a += 0.00001
end
pole
end
You'd use it like this:
area(a, b) do |a|
a ** 2
end
Now it's one thing to be handling Proc objects, that's fine, the do ... end method of appending blocks to method calls generates those by default. It's very not Ruby to put extra methods on Proc itself. I can't see anything here that can't be covered by defining these inside a simple module:
module CalculationModule
def area(a, b)
# ... Implementation
end
extend self
end
That way you can use those methods like CalculationModule.area or you can always include it into another class or module and use them like area.

Ruby instance variable changes unexpectedly

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 need advice on how to do collision in a clone of Pong

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! `

Resources