Koda spel i Ruby

Lektion 6: Stoppa spelet vid krock

Att vårt rymdskepp kan fortsätta att flyga även om det träffas av en asteroid, det är ju inte särskilt realistiskt. Så i den här lektionen ska vi se till så att spelet stoppas om skeppet och asteroiden krockar. I Window-klassens metod initialize ska vi lägga till den nya variabeln @running:


def initialize
  super(640, 480, false)
  @hero = Hero.new(self)
  @asteroid = Asteroid.new(self)
  @running = true 
end	  
	  

Ordet "running" betyder ungefär "körs". När vi startar spelet vill vi ju att det ska köras, så från början får @running värdet true, som betyder "sant".

Vi ska också "rama in" allting som står i Window-klassens metod update i en if-sats, som börjar med if @running och slutar med end. Så här ska det se ut:


def update
  if @running
    if button_down? Gosu::Button::KbRight
      @hero.move_forward
    end
  
    if button_down? Gosu::Button::KbLeft
      @hero.move_back
    end
  
    if button_down? Gosu::Button::KbUp
      @hero.move_up
    end
  
    if button_down? Gosu::Button::KbDown
      @hero.move_down
    end
  
    @asteroid.update
  end  
end	  
	  

If-satsen gör så att koden i update – som ju sköter uppdateringen av alla bilder i spelet – bara körs så länge värdet på @running är true. Det ska vi utnyttja senare, genom att sätta @running till false ("falskt") när vi vill stoppa spelet.

Men först ska vi ordna så att rymdskeppet kan känna av om det träffas av en asteroid. Vi går till Hero-klassen, och lägger till den nya metoden hit_by? ("hit by" betyder "träffad av") allra sist i klassen:


def hit_by?(asteroid)
  Gosu::distance(@x, @y, asteroid.x, asteroid.y) < 50
end  
	  

Den här metoden mäter avståndet, distansen, mellan skeppets position och asteroidens position. Om avståndet är mindre än 50 pixlar så är det en krock!

I Asteroid-klassen måste vi fixa så att asteroidens position går att läsa av "utifrån", till exempel i rymdskeppets hit_by?-metod. Det gör vi genom att lägga till denna lilla rad direkt efter class Asteroid:


attr_reader :x, :y
      

Okej. Till sist går vi tillbaka till Window-klassens metod update. Det är där vi ska känna av om skeppet och asteroiden har krockat. Om så är fallet ska @running få värdet false, och spelet stoppas. Vi lägger till ännu en if-sats, direkt efter raden @asteroid.update:


if @hero.hit_by?(@asteroid)
  @running = false
end
	  

Man kan se det som att if-satsen ställer en fråga till rymdskeppet: Är du träffad? (Det är därför som det är ett frågetecken i hit_by?-metodens namn.)

Det här var en lite rörig lektion, med hoppande mellan olika klasser. Titta igenom programmet här nedanför, och kontrollera så att du har skrivit rätt.


require 'gosu'

class Window < Gosu::Window
  def initialize
    super(640, 480, false)
    @hero = Hero.new(self)
    @asteroid = Asteroid.new(self)
    @running = true
  end
  
  def update
    if @running
      if button_down? Gosu::Button::KbRight
        @hero.move_forward
      end
  
      if button_down? Gosu::Button::KbLeft
        @hero.move_back
      end
  
      if button_down? Gosu::Button::KbUp
        @hero.move_up
      end
  
      if button_down? Gosu::Button::KbDown
        @hero.move_down
      end
  
      @asteroid.update
         
      if @hero.hit_by?(@asteroid)
        @running = false
      end
    end  
  end
  
  def draw
    @hero.draw
    @asteroid.draw
  end
end 

class Hero
  def initialize(window)
    @window = window
    @icon = Gosu::Image.new(@window, "spaceship.png", true)
    @x = 100
    @y = 215
  end
  
  def move_forward
    @x = @x + 5
    if @x > @window.width - @icon.width
      @x = @window.width - @icon.width
    end
  end
  
  def move_back
    @x = @x - 5
    if @x < 0
      @x = 0
    end
  end

  def move_up
    @y = @y - 5
    if @y < 0
      @y = 0
    end
  end

  def move_down
    @y = @y + 5
    if @y > @window.height - @icon.height
      @y = @window.height - @icon.height
    end
  end 
  
  def draw
    @icon.draw(@x, @y, 2)
  end
  
  def hit_by?(asteroid)
    Gosu::distance(@x, @y, asteroid.x, asteroid.y) < 50
  end  
end

class Asteroid
  attr_reader :x, :y
  
  def initialize(window)
    @window = window
    @icon = Gosu::Image.new(@window, "asteroid.png", true)
    @x = @window.width
    @y = rand(@window.height - @icon.height)
  end
  
  def update
    @x = @x - 5
    if @x < -@icon.width
      @x = @window.width
      @y = rand(@window.height - @icon.height)
    end
  end

  def draw
    @icon.draw(@x, @y, 2)
  end  
end    

window = Window.new
window.show	  
	  

Om du nu sparar och kör spelet så märker du att bilden mycket riktigt "fryser" om rymdskeppet och asteroiden krockar.

Men det är fortfarande inte särskilt svårt att undvika den ensamma asteroiden. Vi ska nu skruva upp svårighetsgraden rejält – genom att skapa en hel svärm av asteroider!