我在测试碰撞检测系统时遇到了一个问题.为了便于可视化,您可以将代码的整体 struct 想象为Main Class=>Class(船,shell ,敌人),shell 对象在Ship中初始化(获取Ship坐标),敌人对象在属于Main类的更新方法中初始化,碰撞测试在Enemy类(获取敌人对象的坐标)中进行. 我已经确保其他类可以通过attr_accessor访问shell 对象,但出现以下错误: 指向冲突检测方法的nil:nilClass`的undefined method Shell_Hit_Box‘仍然出现:

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

这应该是所有的方法,彼此之间有有机的联系,并与错误相关,我希望这能得到解决.

如果您认为错误与我所想和所写的不同,以下是Sample的完整代码:

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 




推荐答案

我甚至不打算try 调试您的应用程序,但是如果您想知道shell是否等于nil,只要在发生时引发异常即可.仅作为调试目的的一个示例:

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

你甚至不能叫#火炮或#穿透?除非您传递的是shell的参数,但该参数仍然可以是nil.因为有很多地方您需要判断shell是否可以respond_to? :shell_hit_box,理想情况下,您应该在存储您的Shell对象集合的任何地方修复核心问题.例如,除非您可以用不会触发的DUDS填充@magazine,否则最好将实例变量转换为确保弹夹中没有nil个对象的方法.

如果您的目标只是调试它,那么您可以使用Ruby的标准调试gem来跟踪您的shell变量并在每次shell == nil时中断,或者明智地使用raise来探索回溯并找出调用者是谁,以及他们为什么调用以nil作为shell参数的方法.

Ruby相关问答推荐

Ruby中链表大小调用的时间复杂度

在Ruby中将嵌套哈希键从CamelCase转换为snake_case

each_with_object 应该如何工作?

Ruby 局部变量未定义

Ruby 连接字符串并添加空格

Rails 控制台中没有数据库连接

模块中的实例变量?

如何判断 Capistrano 中是否存在文件(在远程服务器上)?

Ruby 中的 Object 和 BasicObject 有什么区别?

在文件中搜索字符串的最佳方法是什么?

C# ?? Ruby 中的运算符?

加载 RubyGems 插件时出错,openssl.bundle (LoadError)

hash['key'] 到 Ruby 中的 hash.key

如何从 SystemStackError 中获取回溯:堆栈级别太深?

很好地格式化输出到控制台,指定选项卡的数量

纯 Ruby 并发哈希

如何使用 link_to Ruby on rails 添加确认消息

如何在没有 RVM 的 Ubuntu 上安装 Ruby 2

class << self vs self.method with Ruby:什么更好?

Ruby中的字符串和符号有什么区别?