You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby2D碰撞检测遇nil:NilClass错误,Shell对象传入异常排查

Ruby2D碰撞检测报错:undefined method 'shell_hit_box' for nil:NilClass

问题情况

测试Ruby2D碰撞检测系统时出现上述错误,代码结构为Mainscreen主类包含Ship、Shell、Enemy三个子类:

  • Shell对象在Ship类中初始化,用于获取舰船坐标
  • Enemy对象在主类的update方法中创建
  • 碰撞检测逻辑在Enemy类的penetration?方法实现

已通过attr_accessor确保Shell对象可被其他类访问,但错误仍触发,指向penetration?方法中对shell.shell_hit_box的调用。推测问题源于Shell对象未从初始化位置正确传入方法。

相关代码片段

Enemy类的penetration?方法

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

Ship类的fire_attilery方法(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

Ship类的move方法(处理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

Mainscreen主类的update方法

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

问题根源及修复方案

1. 主类中shell变量未定义(核心错误)

主类update方法中调用@enemy.penetration?(shell)时,shell变量从未定义过,导致传入penetration?的是nil,触发undefined method 'shell_hit_box' for nil:NilClass错误。

需要从Ship实例(@bismark)的@magazine数组中获取已生成的Shell对象,再逐个与Enemy做碰撞检测:

修正后的主类update方法:

def update
  @bismark.move
  @bismark.deaccelare
  
  # 避免每帧生成Enemy,添加概率控制(示例:每100帧有2%概率生成)
  if rand(100) < 2
    @enemy = Enemy.new
    @fleet << @enemy
    @enemy.moving_slowly
  end
  
  @fleet.each { |enemy| enemy.move }
  
  # 遍历所有炮弹与所有敌人做碰撞检测
  @bismark.magazine.each do |shell|
    @fleet.each do |enemy|
      if enemy.penetration?(shell)
        print "Working"
        # 可在此添加命中后的逻辑:如移除炮弹、敌人血量减少等
      end
    end
  end
end

2. fire_attilery方法参数冗余

方法参数中的shell未被使用,内部重新定义了同名变量,容易造成混淆,直接移除参数即可:

修正后的fire_attilery方法:

def fire_attilery
  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

3. 碰撞检测逻辑优化

原penetration?方法中坐标数组存在重复和错误(如shell.shell_hit_box.x3搭配单个shell.shell_hit_box),且puts语句会在每次循环执行,应只在命中时输出:

修正后的penetration?方法:

def penetration?(shell)
  # 先判断shell及shell_hit_box是否存在,避免nil调用
  return false unless shell&.shell_hit_box
  
  # 假设shell_hit_box包含四个顶点坐标,替换为实际的顶点属性名
  [
    [shell.shell_hit_box.x1, shell.shell_hit_box.y1],
    [shell.shell_hit_box.x2, shell.shell_hit_box.y2],
    [shell.shell_hit_box.x3, shell.shell_hit_box.y3],
    [shell.shell_hit_box.x4, shell.shell_hit_box.y4]
  ].any? do |x, y|
    hit = @box.contains?(x, y)
    puts "Shikkikan, penetration on enemy confirmed!" if hit
    hit # 返回hit状态给any?判断
  end
end

内容的提问来源于stack exchange,提问作者Simplylmk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 22:10:45