Я пытаюсь создать простую пошаговую битву на Ruby, но все время застреваю, когда дело касается классов. Я попытался сделать это, начав с базового кода и построив его вокруг него. Мне удалось заставить его работать достаточно просто, используя обычные переменные и базовый код атаки:

player = "goodguy"
player_health = 15
player_damage = 5

enemy = "badguy"
enemy_health = 15
enemy_damage = 5

puts "#{player} attacks #{enemy} and does #{player_damage} damage."
enemy_health -= player_damage
puts "#{enemy} has #{enemy_health} remaining."

Затем я превратил атаку в функцию (мне пришлось сделать переменные глобальными, чтобы функция могла их видеть):

$player = "goodguy"
$player_health = 15
$player_damage = 5

$enemy = "badguy"
$enemy_health = 15
$enemy_damage = 5

def player_attack
  puts "#{$player} attacks #{$enemy} and does #{$player_damage} damage."
  $enemy_health -= $player_damage
  puts "#{$enemy} has #{$enemy_health} health remaining."
  if $enemy_health <= 0
    puts "#{$enemy} died!"
  end    
end    

player_attack()

Затем я превратил Player в класс:

class Player
  attr_accessor :name; :hp; :damage
  def initialize(name, hp, damage)
    @name = name
    @hp = hp
    @damage = damage
  end

  def attack
    puts "#{self.name} attacks #{$enemy}!"
    $enemy_health -= @damage
    puts $enemy_health
  end
end

$enemy = "badguy"
$enemy_health = 15
$enemy_damage = 5    

me = Player.new("goodguy", 15, 5)

me.attack

Вот где я застреваю. Когда я превращаю Enemy в класс (в основном смоделированный именно после класса Player), я не могу понять, как заставить два объекта взаимодействовать друг с другом. Этот код не работает, но вот последнее из того, что я пробовал. Переменные #{} больше показывают, что я пытаюсь сделать, чем что-либо еще:

class Player
  attr_accessor :name; :hp; :damage
  def initialize(name, hp, damage)
    @name = name
    @hp = hp
    @damage = damage
  end

  def attack
    puts "#{self.name} attacks #{badguy.name}!"
    badguy.hp -= @damage
    puts badguy.hp
  end
end

class Enemy
  attr_accessor :name; :hp; :damage
  def initialize(name, hp, damage)
    @name = name
    @hp = hp
    @damage = damage
  end

  def attack
    puts "#{self.name} attacks #{goodguy.name}!"
    player.hp -= @damage
    puts player.hp
  end
end  

goodguy = Player.new("Nicehero", 15, 5)
badguy = Enemy.new("Eviljerk", 15, 5)

me.attack 

По сути, я хочу сделать так, чтобы объект Player мог взаимодействовать с объектом Enemy. Я не могу заставить это работать, когда я пытаюсь заставить 2 класса взаимодействовать друг с другом; Кроме того, # {variable.name} - не единственное, что я пытался заставить функции сообщать об этих значениях, но я не могу найти, как на самом деле ссылаться на этот объект.

Очевидно, что мне не хватает того, как взаимодействуют объекты или что делает мой код по сравнению с тем, что я думаю, он должен делать. Я был бы признателен за любые предложения о взаимодействии этих двух классов или о том, как это следует переписать, чтобы оно функционировало по назначению.

0
levelonehuman 1 Май 2014 в 17:33

2 ответа

Лучший ответ

Как указано в @JacobM, проблема, с которой вы сталкиваетесь, связана с неспособностью ваших классов знать о других экземплярах друг друга без явной передачи их в качестве аргумента. Хотя ваш первоначальный обходной путь, заключающийся в использовании глобальных переменных для хранения ссылок на врага и игрока, будет работать, такая практика настоятельно не рекомендуется, поскольку она «просачивает» логику вашей программы по всей вашей игре. что обычно нежелательно (подробное объяснение того, почему их следует избегать, см. в Глобальные переменные).

При удалении $ из вашего кода player становится локальной переменной, если она определена в методе attack:

def attack
  puts "#{self.name} attacks #{goodguy.name}!"
  player.hp -= @damage
  puts player.hp
end

В этой конструкции переменная player, на которую вы хотите ссылаться как на экземпляр класса Player, на самом деле является неопределенной локальной переменной, которую вы объявили в теле метода. Поскольку код ваших классов Player и Enemy одинаков, я бы рекомендовал вам создать суперкласс для хранения этой логики:

class Piece
  attr_accessor :name, :hp, :damage
  def initialize(name, hp, damage)
    @name = name
    @hp = hp
    @damage = damage
  end

  def attack(opponent)
    opponent.hp -= @damage
    puts "#{@name} attacks #{opponent.name}!"
    puts "#{opponent.name}'s HP: #{opponent.hp}"
  end
end

А затем создайте подклассы для Player и Enemy:

class Player < Piece
end

class Enemy < Piece
end

С помощью этой конструкции вы можете создать любое количество врагов и частей и заставить их взаимодействовать друг с другом по отдельности:

> hero = Player.new("Zeus", 1000, 100)
=> #<Player:0x007fbd33958498 @name="Zeus", @hp=1000, @damage=100> 
> goul = Enemy.new("Pariah", 400, 50)
=> #<Enemy:0x007fbd33949b78 @name="Pariah", @hp=400, @damage=50>
> ghost = Enemy.new("Bane", 600, 75)
=> #<Enemy:0x007fbd33937680 @name="Bane", @hp=600, @damage=75> 


> hero.attack(goul)
Zeus attacks Pariah!
Pariah's HP: 300
=> nil
4
wvandaal 1 Май 2014 в 15:09

Поскольку весь код Player и Enemy одинаков, я могу смоделировать их в родительском классе (дав ему тупое имя Man, вы можете дать ему какое-нибудь причудливое имя: D) удаление всей дублированности кода и наследование от общего класса.

Между двумя объектами могут быть разные способы взаимодействия. Я взял самое простое, передав другой объект в функцию attack и начал с ним взаимодействовать.

Я изменю этот код следующим образом:

class Man
    attr_accessor :name, :hp, :damage
    def initialize(name, hp, damage)
        @name = name
        @hp = hp
        @damage = damage
    end

    def attack opposite_team_man
        puts "#{self.name} attacks #{opposite_team_man.name}!"
        opposite_team_man.hp -= @damage
        puts opposite_team_man.hp
    end

end
class Player < Man
end

class Enemy < Man
end  

goodguy = Player.new("Nicehero", 15, 5)
badguy = Enemy.new("Eviljerk", 15, 5)

goodguy.attack badguy
4
Saurabh 1 Май 2014 в 14:24