ruby 碰撞检测测试中对象的Nil类

new9mtju  于 2022-11-29  发布在  Ruby
关注(0)|答案(1)|浏览(158)

我在测试碰撞检测系统时遇到了一个问题。(船、壳、敌人),壳对象在船中初始化(要取船坐标)、在属于Main类的update方法中初始化敌方对象,在Enemy类中进行碰撞测试(获取敌人对象的坐标)。我已经确保shell对象可以通过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

给予炮弹动量的方法:

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
thtygnil

thtygnil1#

我甚至不打算调试你的应用程序,但是如果你想知道 shell 是否是nil,只要在发生这种情况时引发一个异常。

def penetration? shell
  raise if shell.nil?
  # ... whatever
end

你甚至不能调用#fire_artillary或#penetration?,除非你为 shell 传递了一个参数,但这个参数仍然可以是nil。因为有很多地方你需要评估 shell 是否可以respond_to? :shell_hit_box,理想的情况是,你应该在存储Shell对象集合的地方修复核心问题。例如,除非您可以用不发射的哑弹填充 *@杂志 *,否则最好将示例变量转换为一个方法,以确保弹匣中没有nil对象。
如果您的目标只是调试它,那么您可以使用Ruby的标准调试gem来跟踪您的 shell 变量,并在shell == nil出现时中断,或者明智地使用raise来探索反向跟踪,找出调用者是谁,以及他们为什么调用以nil作为 shell 参数的方法。

相关问题