Koda spel i Ruby

Lektion 3: Rymdskeppet som försvann

Efter två lektioner har du redan lärt dig en hel del om spelprogrammering. Och nu börjar det börjar bli riktigt spännande – vi ska börja sätta saker och ting i rörelse! Men först ska jag berätta en hemlighet om vårt spelfönster.

Än så länge ser det kanske inte så märkvärdigt ut, spelfönstret. Men under ytan händer det intressanta saker. När vi kör programmet, och fönstret öppnas, startar samtidigt en loop. En loop använder man i datorprogram när man vill få något att upprepas. I vårt fall är det koden i Window-klassens metoder update och draw som körs, om och om igen. Det här sker i en svindlande hastighet. Metoderna körs, eller anropas, som det heter, hela 60 gånger i sekunden!

Okej, men vad ska det här vara bra för? Jo, tack vare loopen går det smidigt att skapa animationer – bilder som rör sig i fönstret. Det ska vi göra nu.

Först lägger vi till några rader i Window-klassens update-metod, som än så länge är tom. Vi skriver:


def update
  if button_down? Gosu::Button::KbRight
    @hero.move_forward
  end
end  
	  

Det här är en så kallad if-sats och sådana är mycket vanliga i datorprogram. Kan du klura ut vad den gör? Här får du en ledtråd: Översatt till svenska så betyder if button_down? "om knapp nere". Och koden kontrollerar just om en viss tangent, i det här fallet höger piltangent, är nedtryckt. Om så är fallet anropas en metod i Hero-klassen som heter move_forward, vilket betyder typ "flytta framåt".

Men någon sådan metod finns ju inte! Det måste åtgärdas. Vi går till Hero-klassen och gör några ändringar:


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
  end
  
  def draw
    @icon.draw(@x, @y, 2)
  end
end
	  

Först ändrar vi metoden initialize så att rymdskeppets startposition sparas i två variabler som heter @x och @y. Det gör vi för att vi senare ska kunna ändra positionen.

För att förstå vad en variabel är så kan du tänka dig datorns minne som en massa lådor där man kan spara olika saker, till exempel tal eller ord. När vi skapar en variabel så sätter vi en etikett på en sådan låda. På så vis kan vi hitta den när vi vill lägga något i den, eller ändra eller ta bort det som ligger där.

Nu har vi alltså lagt talen 100 och 215 i två "lådor" som heter @x och @y. De heter så eftersom man brukar kalla positionen i sidled för x-värde och positionen i höjdled för y-värde. (Varför namnen på variablerna börjar med "@", snabel-a, ska jag inte gå närmare in på här. Men det är för att @x och @y är en speciell sorts variabler som kallas instansvariabler.)

Den andra ändringen i Hero-klassen är den nya metoden move_forward. När den anropas så ökas x-värdet med 5, det vill säga skeppets position flyttas fem pixlar framåt.

Till sist har vi ändrat i draw-metoden så att det står @x och @y i stället för 100 och 215.

Spara nu programmet, och kör det sedan. När du nu trycker på högerpilen ska skeppet röra sig framåt, fem pixlar i taget. Prova gärna att ändra i metoden move_forward så att @x ökas med till exempel 3, eller 10. Vad händer då?

Här är nu vårt spelprogram i sin helhet:


require 'gosu'

class Window < Gosu::Window
  def initialize
    super(640, 480, false)
    @hero = Hero.new(self)
  end
  
  def update
    if button_down? Gosu::Button::KbRight
      @hero.move_forward
    end
  end
  
  def draw
    @hero.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
  end
  
  def draw
    @icon.draw(@x, @y, 2)
  end
end

window = Window.new
window.show	  
	  

Nu har vi alltså ett rymdskepp som kan flyga. Tjusigt! Men vi har ett par problem att lösa: Dels kan det bara flyga framåt, inte bakåt, uppåt eller nedåt. Dels så stannar inte skeppet om det råkar flyga för långt framåt – utan det flyger ut från spelfönstret och försvinner i tomma intet. Prova själv så får du se.

I nästa lektion ska vi lägga till fler move-metoder – och sätta gränser för vårt skepp så att det inte kan rymma.