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
相关产品推荐
相关产品推荐

