我在测试碰撞检测系统时遇到了一个问题。为了可视化,你可以将代码的整体结构想象为Main Class =>Class(船,炮弹,敌人),炮弹对象在ship中初始化(获取船坐标),敌人对象在属于Main类的更新方法中初始化,碰撞测试在enemy类中(获取敌人对象坐标)。我已经确保外壳对象可以通过attr_accessor被其他类访问,但错误:undefined method
shell_hit_box' for nil:NilClass '指向碰撞检测方法仍然发生:
def penetration?(shell)
shell.shell_hit_box && [[shell.shell_hit_box, shell.shell_hit_box], [shell.shell_hit_box, shell.shell_hit_box],
[shell.shell_hit_box.x3, shell.shell_hit_box], [shell.shell_hit_box, shell.shell_hit_box]].any? do |coordinates|
@box.contains?(coordinates[0], coordinates[1])
puts"Shikkikan, penetration on enemy confirmed!"
end
end
我的假设是shell对象没有从基本初始化点正确地传递到方法中:
def fire_attilery(shell) #this is where the shell is created and pushed into an array to store
x = @sprite.x + (@sprite.width)*0.5 + @x_velocity
y =@sprite.y + (@sprite.height)*0.5 + @y_velocity
shell=Shell.new(x, y, @sprite.rotate)
@magazine << shell
end
为shell提供动量的方法:
def move
@sprite.x += @x_velocity
@sprite.y += @y_velocity
@magazine.each do |shell| # shell is push out of array and ordered to fly foward
shell.shell_fired
end
从主类中检测更新方法中的冲突
def update #updating player movement and and spawning enemy
@bismark.move
@bismark.deaccelare
@enemy= Enemy.new
@fleet << @enemy
@enemy.moving_slowly
@fleet.each { |enemy| enemy.move }
if @enemy.penetration?(shell) #calling the colision test method in Enemy Class
print "Working"
end
end
这应该是所有的方法,有一个有机的关系,彼此和相关的错误,我希望这可以解决
这是完整的代码样本,如果你认为错误不是我想的和写的:
require 'ruby2d'
######################################################################################
class Ship
attr_accessor :skin, :x , :y, :shell #making the shell object accessible?
def initialize(skin, x, y)
@skin = skin
@x = x
@y = y
@magazine = []
@fire_rate = 0
@remaning_ammo = 7
@x_velocity= 0
@y_velocity = 0
@sprite = Sprite.new(skin,
width: 78,
height: 99,
clip_width: 220,
time: 150,
rotate: 180,
animations: {
floating: 0..1,
open_fire:2..3 ,
}
)
end
def quarter_speed
@sprite.play(animation: :floating, loop:true )
end
def fire
@sprite.play(animation: :open_fire, loop:false)
end
def rotation(angle)
case angle
when :port
@sprite.rotate -=2
when :starboard
@sprite.rotate +=2
end
end
def acceleration(angle)
case angle
when :forwards
@x_velocity+= Math.sin(@sprite.rotate*Math::PI/180)
@y_velocity -= Math.cos(@sprite.rotate*Math::PI/180)
when :backwards
@x_velocity-= Math.sin(@sprite.rotate*Math::PI/180)
@y_velocity += Math.cos(@sprite.rotate*Math::PI/180)
end
end
def deaccelare
@x_velocity*= 0.99
@y_velocity*= 0.99
end
def move
@sprite.x += @x_velocity
@sprite.y += @y_velocity
@magazine.each do |shell| # shell is push out of array and ordered to fly foward
shell.shell_fired
end
end
def fire_attilery(shell) #this is where the shell in created and push into an array to store
if @fire_rate+40 < Window.frames
x = @sprite.x + (@sprite.width)*0.5 + @x_velocity
y =@sprite.y + (@sprite.height)*0.5 + @y_velocity
shell=Shell.new(x, y, @sprite.rotate)
@magazine << shell
@fire_rate = Window.frames
end
end
end
class Shell
attr_reader :shell_hit_box #making the hit box accessible?
def initialize(x, y, rotation)
@shell = Sprite.new('images/pistol_shell.png',
x: x,
y: y,
width:13,
height:20,
rotate: rotation)
@shell_count = 7
@x_velocity= Math.sin(@shell.rotate*Math::PI/180)
@y_velocity = -Math.cos(@shell.rotate*Math::PI/180)
@shell_hit_box = Square.new(x: x, y: y, size: 10, color:[1,1,1,0.001])
end
def shell_fired
@shell.x += @x_velocity*22
@shell.y += @y_velocity*22
@shell_hit_box.x += @x_velocity*22
@shell_hit_box.y += @y_velocity*22
end
end
class Enemy
attr_accessor
def initialize
@x_velocity= 0
@y_velocity =0
@baseship = Sprite.new('images/enemy.png',
x: rand(Window.width),
y:rand(Window.height),
width: 80,
height: 100,
clip_width: 250,
time: 300,
rotate: rand(360),
animations: {
shipenemy: 0..3,})
@x_velocity+= Math.sin(@baseship.rotate*Math::PI/180)
@y_velocity -= Math.cos(@baseship.rotate*Math::PI/180)
@a = @baseship.x + (@baseship.width)*0.5
@b = @baseship.y + (@baseship.height)*0.5
@box = Square.new(x: @a, y: @b, size:30, color:[1, 0, 1, 0.001] )
end
def moving_slowly
@baseship.play(animation: :shipenemy, loop:true )
end
def move
@box.x += @x_velocity
@box.y += @y_velocity
@baseship.x += @x_velocity
@baseship.y += @y_velocity
end
def penetration?(shell) #this is detecting the collision , source of error i think
shell.shell_hit_box && [[shell.shell_hit_box, shell.shell_hit_box], [shell.shell_hit_box, shell.shell_hit_box],
[shell.shell_hit_box.x3, shell.shell_hit_box], [shell.shell_hit_box, shell.shell_hit_box]].any? do |coordinates|
@box.contains?(coordinates[0], coordinates[1])
puts"Sir, penetration on enemy confirmed!"
end
end
end
class Mainscreen
def initialize
@bismark = Ship.new('images/bismark',230, 230)
@bismark.quarter_speed
@fleet = []
end
def ship_turn(angle)
@bismark.rotation(angle)
end
def bismark_acceleration(angle)
@bismark.acceleration(angle)
end
def update #updating player movement and and spawning enemy
@bismark.move
@bismark.deaccelare
@enemy= Enemy.new
@fleet << @enemy
@enemy.moving_slowly
@fleet.each { |enemy| enemy.move }
if @enemy.penetration?(shell) #calling the colision test method in Enemy Class
print "Working"
end
end
def bismark_fire_shell
@bismark.fire_attilery
@bismark.fire
end
end
mainscreen = Mainscreen.new
update do
mainscreen.update
end
################################The code below doesnt matter much, just user input for movement##########################
on :key_held do |event|
case event.key
when 'up'
mainscreen.bismark_acceleration(:forwards)
when 'down'
mainscreen.bismark_acceleration(:backwards)
when 'left'
mainscreen.ship_turn(:port)
when 'right'
mainscreen.ship_turn(:starboard)
end
end
on :key_down do |event|
if event.key == 'f'
mainscreen.bismark_fire_shell
artilery_sound.play
end
end
show
我甚至不打算尝试调试您的应用程序,但是如果您想知道shell是否为nil
,只需在发生这种情况时引发异常。作为调试目的的一个示例:
def penetration? shell
raise if shell.nil?
# ... whatever
end
你甚至不能叫#fire_artillery或#penetration?除非你传递了shell的参数,但是这个参数仍然可以是nil
。由于有很多地方需要评估shell是否可以respond_to? :shell_hit_box
,理想情况下,您应该在存储shell对象集合的任何地方修复核心问题。例如,除非您可以用不会触发的duds填充@magazine,否则您最好将实例变量转换为确保杂志中没有nil
对象的方法。
如果您的目标只是调试它,那么您可以使用Ruby的标准调试gem来跟踪shell变量并在shell == nil
时中断,或者明智地使用raise
来探索反向跟踪并找出调用者是谁以及为什么他们以nil
作为shell的参数调用方法。