När vi nu ska lägga till ytterligare en sak i vårt spel, ett nytt objekt, så kommer mycket att kännas bekant. Vi börjar med att skapa en ny klass, som vi kallar för Asteroid
:
class Asteroid
def initialize(window)
@window = window
end
def update
end
def draw
end
end
I metoden initialize
talar vi om för asteroiden att den hör hemma i vårt spelfönster. Metoderna update
och draw
får vara tomma ett tag till.
Precis som när det gällde rymdskeppet så behöver vi en bild, en ikon, för att asteroiden ska synas på skärmen. Här har du en lämplig bild:
Det är en kille som heter Jonas Wagner som har skapat asteroidbilden. Jag hämtade den här:
http://opengameart.org/content/asteroid-explosions-rocket-mine-and-laser
Högerklicka på bilden, spara den på din dator och flytta den till rätt mapp. Gör sedan följande tillägg i Asteroid
-klassens initialize
-metod, så att datorn vet vilken bild som ska användas:
def initialize(window)
@window = window
@icon = Gosu::Image.new(@window, "asteroid.png", true)
end
Sedan ska vi ge asteroiden en startposition, och då måste vi tänka till ordentligt. Asteroiden ska komma in i spelfönstret från högerkanten, röra sig genom fönstret och försvinna ut på den vänstra sidan. Därför måste x-värdet – positionen i sidled – från början vara samma som fönstrets bredd, det vill säga 640 pixlar.
Och y-värdet, som bestämmer positionen i höjdled? För att få fram det ska vi ta hjälp av slumpen. Tänk dig att du har en hatt, full med ihopvikta papperslappar. På lapparna står olika nummer. För att få ett slumptal kan du helt enkelt låta någon dra en lapp ur hatten.
Men vi behöver inte använda någon hatt. I Ruby finns det en färdig metod som heter rand
, och som levererar ett slumptal när man ber den om det. Vi ska ta och använda rand
i vår initialize
-metod:
def initialize(window)
@window = window
@icon = Gosu::Image.new(@window, "asteroid.png", true)
@x = @window.width
@y = rand(@window.height - @icon.height)
end
Här får @x
inledningsvis värdet 640, vilket betyder att asteroiden ligger och lurar precis utanför spelfönstrets högerkant. Värdet på @y
blir ett slumptal mellan 0 och spelfönstrets höjd, som ju är 480, minus asteroidbildens höjd, som är 77 pixlar. Vi drar bort asteroidens höjd eftersom vi inte vill att den ska hamna utanför fönstret.
Det var mycket siffror på en gång! Vi ska strax se hur det här fungerar i praktiken. Men först ska vi lägga till lite kod i Asteroid
-klassens båda metoder update
och draw
:
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
Koden i draw
-metoden ritar helt enkelt bilden av asteroiden. I update
minskas @x
med 5 varje gång metoden anropas. Det betyder att asteroiden rör sig åt vänster, fem pixlar åt gången. När den har svävat ut genom fönstrets vänsterkant så startar den om från högerkanten igen, nu med en ny y-position.
Titta gärna en extra gång på if-satsen, där vi kontrollerar om @x
har blivit mindre än -81 (asteroidbilden är 81 pixlar bred, så -@icon.width
är här samma sak som -81). Ett minusvärde? Ja, vid fönstrets vänstra kant är ju x-värdet 0. Så när asteroiden precis har försvunnit ut ur fönstret så har @x
faktiskt värdet -81.
Vi ska också lägga till lite asteroid-kod på några ställen i Window
-klassen. Först i initialize
-metoden, där vi nu även vill skapa en ny asteroid:
def initialize
super(640, 480, false)
@hero = Hero.new(self)
@asteroid = Asteroid.new(self)
end
I update
och draw
ska vi bara anropa de metoder i Asteroid
-klassen som har samma namn. Så i slutet av update
-metoden skriver vi @asteroid.update
och i draw
-metoden ska det stå @asteroid.draw
(nedan ser du hur det ska se ut).
Här följer, återigen, programmet i sin helhet:
require 'gosu'
class Window < Gosu::Window
def initialize
super(640, 480, false)
@hero = Hero.new(self)
@asteroid = Asteroid.new(self)
end
def update
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
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
end
class Asteroid
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
Du kanske minns att spelfönstrets båda metoder update
och draw
körs automatiskt, 60 gånger i sekunden, tack vare en loop. Nu har vi gjort så att även asteroidens update
- och draw
-metoder körs automatiskt. Därmed kommer asteroiden att flyttas "av sig själv". Provkör så får du se!
Men att undvika en ensam asteroid är inte särskilt svårt. Och om rymdskeppet mot förmodan krockar med asteroiden så händer ingenting alls! Det ska vi ändra på i nästa lektion.