2017-09-22 15 views
0

我正在構建一個調用方法時遇到問題的遊戲。一個怪物可以出現並獲得一個隨機武器,如果該武器射程,該怪物獲得一個回合挫折,給玩家一個戰鬥機會。當monsterRangedTurnSetback方法被調用時,我得到它試圖在nil:NilClass中查找屬性的錯誤。我最終追溯到genWeapon函數,並且該函數不能被調用。她的一些代碼是attr_reader不調用變量

def monsterRangedTurnSetback(weapon) 
    attribs = weapon.attributes() 
    attribs.each do |attrib| 
    if attrib == "Ranged" 
     return 1 
    else 
     return 0 
    end 
    end 
end 

def genWeapon 
    weaponGen = rand(1..80) 
    if weaponGen == 1 or weaponGen == 2 
    weapon = GreatSword.new 
    hasTwoHandedWeapon = true 
    elsif weaponGen == (3..23) 
    weapon = ShortSword.new 
    hasTwoHandedWeapon = false 
    elsif weaponGen == (24..34) 
    weapon = ShortBow.new 
    hasTwoHandedWeapon = true 
    elsif weaponGen == (35..48) 
    weapon = LongBow.new 
    hasTwoHandedWeapon = true 
    elsif weaponGen == (49..64) 
    weapon = Dagger.new 
    hasTwoHandedWeapon = false 
    elsif weaponGen == (65..78) 
    weapon = HandCrossbow.new 
    hasTwoHandedWeapon = false 
    elsif weaponGen == 79 or weaponGen == 80 
    weapon = HeavyCrossbow.new 
    hasTwoHandedWeapon = true 
    end 
    return weapon 
    puts weapon.name 
    sleep 2 
end 

class Orc 
    attr_accessor :totalDamage, :totalHealth, :armorClass, :attackText, :name, 
    :turnSetback 
    def initialize 
    @wep = genWeapon() 
    @baseDamage = 7 
    @weapon = @wep 
    @turnSetback = monsterRangedTurnSetback(@weapon) 
    @wep = nil 
    @health = 5 
    @hasShield = shield(@weapon) 
    @armorClass = 6 
    if @hasShield == true 
     @armorClass += 2 
    end 
    @challengeLevel = 1 
    @attackText = ["Orc stabs you", "Orc slashes at you", "Orc intimidates you"] 
    @name = "Orc" 
    end 
end 

class ShortSword 
    attr_reader :attributes, :name, :attackBonus 
    def initialize 
    @attributes = ["Melee", "1Hand"] 
    attackBonus = 3 
    name = "Short Sword" 
    end 
end 

是的代碼是按照這個順序進行的,是的,我知道怪物類允許讀取不存在的變量。任何幫助表示讚賞。

+0

武器是唯一當'weaponGen'爲1,2,79或80時,因爲'weaponGen'永遠不會等於('==')'Range',而不是'weaponGen ==(3..23)'我想你是什麼意思是'(3..23).cover?(weaponGen)'。這個代碼肯定還有很多其他問題(邏輯和語法上都有),但是這應該會讓你超過當前的代碼 – engineersmnky

+0

這非常需要'case'語句或ev更好,查找表。另外,在Ruby方法中,沒有參數的方法定義和調用都省略了相應的parens,所以你會寫'attributes'而不是'attributes'。這使得鏈接方法在語法上更不復雜。同樣,變量和方法名都是小寫,大寫字母在Ruby中有重要意義,所以使用'weapon_gen'優先於'weaponGen'。 – tadman

+0

這裏還有一些代碼在'return'語句之後,這意味着它永遠不會運行,所以它很好奇爲什麼它在那裏。希望有一些早期的調試工作的人爲因素。 – tadman

回答

1

這裏的錯誤可能是attr_reader不能綁定到像x這樣的局部變量,而只能用於像@x這樣的實例變量。在您的代碼中:

attr_reader :attackBonus 

def initialize 
    # This is a local variable that will fall out of scope once the method 
    # finishes. It is not saved anywhere, simply thrown away. 
    attackBonus = 3 
end 

添加一個@前綴可以使其保持可讀性。

同樣的事情在genWeapon中發生,其中局部變量被設置和丟棄。如果你需要那些堅持不知何故,你需要包括他們在回報。這些屬性無論如何都應該是某種基本武器類的一部分,你可以在這裏調用Dagger.new.two_handed?Dagger.new.hands_required

由於@engineersmnky指出,有一個在genWeapon方法,其中x == (1..2)絕不會爲任何x值不是字面上(1..2)返回true一個沉重的缺陷。 工作的是(1..2).include?(x)(1..2) === x。由於case使用===在內部它可以很容易地寫:

case (rand(1..80)) 
when 1..2 
    GreatSword.new 
when 3..23 
    ShortSword.new 
# ... 
end 

這仍然是很乏味。而是寫一個查詢表:

WEAPON_PROBABILITY = { 
    (1..2) => GreatSword, 
    (3..23) => ShortSword, 
    (24..34) => ShortBow, 
    (35..48) => LongBow, 
    (49..64) => Dagger, 
    (65..78) => HandCrossbow, 
    (79..80) => HeavyCrossbow 
}.flat_map do |range, type| 
    range.to_a.map do |roll| 
    [ roll, type ] 
    end 
end.to_h 

這將卷映射到類。然後你發生器功能變得微不足道:

def gen_weapon 
    WEAPON_PROBABILITY[rand(1..80)].new 
end 

利用Ruby的的「一切都是對象」的原則,使類查表大大簡化了的事情,可以使代碼更直接的理解。總是儘量引導您的程序,以便根據數據而不是程序來定義事物。

+0

@engineersmnky這段代碼真的很讓人頭暈目眩,我盡我所能! – tadman

+0

既然你發佈了一個答案,不妨重複我的評論,這似乎是當前的問題,因爲'genWeapon'將返回'nil'大多數時間,然後'monsterRangedTurnSetback'將出錯 – engineersmnky

+0

這是一個非常好的觀察。我已經爲此做了部分修復,但認爲這是過度複雜的事情。 – tadman

1

您可能想重溫一下您如何定義其中一些類。也許甚至包括turn_delay作爲武器類的方法。以下是我可能會重構這個從武器父類繼承的專門武器:

class Weapon 
    attr_reader :attributes, :name, :attack_bonus 

    def initialize 
    @attributes = [] 
    end 

    def turn_delay? 
    @attributes.include? :ranged 
    end 

    def two_handed? 
    @attributes.include? :two_hand 
    end 
end 

class ShortSword < Weapon 
    def initialize 
    @attributes = %i(melee one_hand) 
    @attack_bonus = 3 
    @name = 'Short Sword' 
    end 
end 

class LongBow < Weapon 
    def initialize 
    @attributes = %i(ranged) 
    @attack_bonus = 10 
    @name = 'Long Bow' 
    end 
end 

bow = LongBow.new 
puts bow.name 
puts bow.turn_delay? 

sword = ShortSword.new 
puts sword.name 
puts sword.turn_delay? 

輸出:

Long Bow 
true 
Short Sword 
false 

我這個有太多的樂趣,大量的武器有可能成爲累贅寫類的定義。既然你選擇了紅寶石,你可以擁抱一些元編程和快速通過以下(掀起新的武器需要你定義的基礎Weapon類:

[ 
    { klass: 'BroadSword', attributes: [:melee, :two_hand], attack_bonus: 20, name: 'Broad Sword' }, 
    { klass: 'Dagger', attributes: [:melee, :one_hand], attack_bonus: 1, name: 'Dagger' }, 
    { klass: 'ShortBow', attributes: [:ranged], attack_bonus: 5, name: 'Short Bow' }, 
].each do |obj| 
    eval <<WEAPON 
    class #{obj[:klass]} < Weapon 
    def initialize 
     @attributes = #{obj[:attributes]} 
     @name = '#{obj[:name]}' 
     @attack_bonus = #{obj[:attack_bonus]} 
    end 
    end 
WEAPON 
end 

然後:

bs = BroadSword.new 
puts bs.name 
puts bs.two_handed? 

Broad Sword 
true 
+0

在第二種情況下,您總是可以使用Json或yml文件來存儲您的所有武器,以製作自定義武器並進行簡單的修改。很好地完成了重構 – engineersmnky

+0

因爲我是相對較新的,你會介意解釋什麼是eval << WEAPON和WEAPON嗎?感謝您的答案,我現在要試一試? –

+0

當然eval基本上是評估(執行)分隔符之間的代碼並將其注入到當前上下文中。 「WEAPON」術語基本上只是文本分隔符,它們表示從何處開始和停止評估。 https://ruby-doc.org/core/Kernel.html#method-i-eval – ddubs