Jako, że chwilowo mam czasu więcej, postanowiłem szybko zabrać się za drugą część. Dzisiaj zrobimy podstawową SceneMap oraz naszego małego gracza ;) zaczynamy!
Otwórzcie edytor i oba nasze pliki. Stwórzcie kolejny plik pod nazwą "SceneMap.rb", który umieście w "/scripts", i importujcie go w naszym Run.rb
Kod: Zaznacz cały
require 'scripts/SceneMap.rb'
Kod: Zaznacz cały
class SceneMap
def initialize
end
def update
end
def draw
end
def button_down(id)
end
def button_up(id)
end
end
W metodzie initialize, zaraz pod 'self.caption' umieśćcie ten oto kod:
Kod: Zaznacz cały
$scene = SceneMap.new
Kod: Zaznacz cały
$scene.[nazwa_metody]
Kod: Zaznacz cały
# Main game window
class GameWindow < Window
def initialize
super(640,480,false)
self.caption = "Fuzed"
$scene = SceneMap.new
end
def update
$scene.update
end
def draw
$scene.draw
end
def button_down(id)
$scene.button_down(id)
end
def button_up(id)
$scene.button_up(id)
end
end
Jeśli teraz uruchomicie grę, nie powinno się nic zmienić (tj. powinniśmy mieć cały czas czarne okienko gry). Powód tego jest taki, że jeszcze nic nie wyświetliliśmy. Zróbmy więc próbę, czy wszystko działa, i używając wewnętrzną metodę Gosu::Window#draw_line narysujemy białą linię!
Przełączmy na nasze SceneMap i w metodzie draw wstawmy ten kod:
Kod: Zaznacz cały
$window.draw_line(0,0,Color.new(255,255,255),640,480,Color.new(255,255,255),0)
Metoda Gosu::Window#draw_line pobiera 7 argumentów: x1, y2, color1, x2, y2, color2, z. x i y to są współrzędne, a color potrzebuje wykorzystania klasy Color. z jest pozycją na ekranie, na której "warstwie" jest wyświetlane (rzeczy o wyższym z są wyświetlane ponad tymi, które mają mniejszą tę wartość). Z Wartościami jakie podaliśmy powinna nam się wyświetlić biała linia idąca po przekątnej przez nasze okienko. I rzeczywiście, jeśli wszystko zrobiliście okej, powinniście ujrzeć to:
Wiemy już, że nasz Scene jest wyświetlany prawidłowo, wykasujcie więc kod odpowiadający za rysowanie linii. Nie jest nam już potrzebny.
Znów stwórzcie nowy plik i nazwijcie go 'Player.rb'. Również musicie go importować ;) Nasza klasa Player potrzebuje tylko trzech podstawowych metod: initialize, update i draw. Jednak jest mała różnica w porównaniu do poprzednich skryptów: tym razem zarówno initialize jak i draw potrzebują parametrów. Dla 'initialize' będą to parametry x i y, bez żadnej wartości, a dla 'draw' będzie to parametr 'z' przyjmujący wartość 5.
Kod: Zaznacz cały
class Player
def initialize(x,y)
end
def update
end
def draw(z=5)
end
end
@x, @y, @sprite, @real_x, @real_y. Pierwsze dwie są bez problemu do zrozumienia. @sprite też nie powinien sprawić problemu, ale co z ostatnimi dwoma?
O tym za chwilę. Zacznijmy od przypisania pierwszym dwóm zmiennym wartości które przesyłamy poprzez 'initialize':
Kod: Zaznacz cały
def initialize(x,y)
@x = x
@y = y
end
I w naszym initialize wpiszcie:
Kod: Zaznacz cały
@sprite = Image.new($window, "graphics/sprites/player_1_stand_right.png", false)
Kod: Zaznacz cały
@sprite.draw(@x, @y, z)
W SceneMap#initialize stwórzcie taką oto zmienną:
Kod: Zaznacz cały
@player = Player.new(128,128)
Jeśli pojawi wam się taki błąd:
(który niestety czasem się pojawia, jak np. mi teraz) musicie do GameWindow, pod 'self.caption' a nad '$scene' wkleić:
Kod: Zaznacz cały
$window = self
Jeśli błąd nie wyskoczył (albo poprawiliście go w ten sposób) powinniście zobaczyć obrazek naszego gracza unoszący się blisko lewego górnego rogu.
Teraz zajmijmy się ostatnimi dwiema zmiennymi, których nasz gracz potrzebuje. Jak widzicie, mamy zarówno @x, @y, jak i @real_x i @real_y. Powodem tego jest to, że postanowiłem rozbić postać na dwa rodzaje współrzędnych, @x i @y będą wskazywały nam środkowy dół postaci ("środek ciężkości"), a @real_x/y będą punktem {0,0} grafiki. Tak więc musimy wprowadzić nieco zmian w naszym initialize:
Kod: Zaznacz cały
def initialize(x,y)
@real_x = x
@real_y = y
@sprite = Image.new($window, "graphics/sprites/player_1_stand_right.png", false)
@x = @real_x + (@sprite.width / 2)
@y = @real_y + @sprite.height
end
Kod: Zaznacz cały
@real_x = @x - (@spirte.width / 2)
@real_y = @y - @sprite.height
Ale statyczna postać jest nieco nudna. Czas pozwolić jej się poruszać!
Do tego potrzebujemy dwóch zmiennych w initialize:
Kod: Zaznacz cały
@move_x = 0
@moving = false
Kod: Zaznacz cały
if @moving then
if @move_x > 0 then
@move_x -= 1
@x += 1
elsif @move_x < 0 then
@move_x += 1
@x -= 1
elsif @move_x == 0 then
@moving = false
end
end
Kod: Zaznacz cały
def move_left
@move_x = -3
@moving = true
end
def move_right
@move_x = 3
@moving = true
end
Ponieważ metoda button_down jest wywoływana tylko raz, przez co gracz musiałby bez przerwy klikać strzałkami w lewo i prawo, żeby postać się ruszała. Tak więc w update umieśćmy ten kod:
Kod: Zaznacz cały
@player.move_left if $window.button_down?(KbLeft)
@player.move_right if $window.button_down?(KbRight)
Kod: Zaznacz cały
if $window.button_down?(KbRight) then
@player.move_right
end
To za chwilkę. Najpierw jednak zrobimy, żeby się odwracał do kierunku w którym idziemy. Wracamy do klasy Player. Ściągnijcie kolejną graficzkę:
Zacznijmy od stworzenia dwóch zmiennych z Image (@stand_left i @stand_right, zawierających odpowiednie grafiki) oraz przypisania @sprite wartości @stand_right. Dajmy również zmienną @dir, odpowiadającą za kierunek postaci. W ten sposób:
Kod: Zaznacz cały
@stand_right = Image.new($window, "graphics/sprites/player_1_stand_right.png", false)
@stand_left = Image.new($window, "graphics/sprites/player_1_stand_left.png", false)
@sprite = @stand_right
@dir = :right
Musimy tylko dodać obracanie postaci i zmianę sprita. Najpierw w naszych metodach move_left i move_right przypisujemy odpowiednią wartość dla @dir. Następnie w update, ponad warunkiem 'if @moving then' zamieszczamy ten kod:
Kod: Zaznacz cały
if @dir == :left then
@sprite = @stand_left
elsif @dir == :right then
@sprite = @stand_right
end
i umieśćcie wiecie gdzie ;). Musimy je dodać oraz dać naszemu programowi do zrozumienia, że to tak naprawdę nie jest jedna graficzka, a zbiór grafik w jednym pliku. Usuńcie wartości zmiennych @stand_right i @stand_left. Dodajcie również chwilowo puste zmienne @walk_left i @walk_right zaraz pod nimi. Żeby wczytało nam grafikę jako spritesheet, musimy użyć nieco innej metody: Image#load_tiles. Parametry jakie przyjmuje ta zmienna to: (Okno, ścieżka_dostępu, szerokość, wysokość, tileable?). Pierwsza dwa oraz ostatni znamy już, a szerokość i wysokość ustalają rozmiary jednej kratki. Można w nie przesłać wartości dwojako: w przypadku wartości dodatnich, program podzieli obrazek na kratki wielkości którą podamy (np. dając 32, 32 będziemy mieli x kratek o wielkości 32x32 piksele), a podając wartości ujemne, dzieli ten obrazek na tyle kratek (dając -4,-4 otrzymamy 16 kratek o rozmiarze zależnym od naszego spritesheeta). Pojedyncza kratka naszej postaci ma 32x32 piksele, więc podajemy te właśnie wartości. Wczytajcie wszystkie cztery grafiki:
Kod: Zaznacz cały
@stand_right = Image.load_tiles($window, "graphics/sprites/player_1_standby_right.png", 32, 32, false)
@stand_left = Image.load_tiles($window, "graphics/sprites/player_1_standby_left.png", 32, 32, false)
@walk_left = Image.load_tiles($window, "graphics/sprites/player_1_run_left.png", 32, 32, false)
@walk_right = Image.load_tiles($window, "graphics/sprites/player_1_run_right.png", 32, 32, false)
Kod: Zaznacz cały
@sprite[0].draw(@real_x, @real_y, z)
Kod: Zaznacz cały
frame = milliseconds / 100 % @sprite.size
@sprite[frame].draw(@real_x, @real_y, z)
Uruchomcie grę. Nasza postać do was mruga ;) możecie zmieniać szybkość tego mrugania zmieniając liczbę 100 w obliczeniach frame. Im większa jest to wartość, tym wolniejsza animacja, ale pamiętajcie! Ta szybkość jest również do pozostałych animacji!
Dobrze, teraz czas na ostatnią animację: poruszanie się. Wbrew pozorom, nie będzie to trudne. Najpierw w metodach move_left i move_right musimy zmienić sprite postaci na odpowiedni:
Kod: Zaznacz cały
def move_left
@dir = :left
@move_x = -3
@sprite = @walk_left
@moving = true
end
def move_right
@dir = :right
@move_x = 3
@sprite = @walk_right
@moving = true
end
Kod: Zaznacz cały
def update
@real_x = @x - (@sprite[0].width / 2)
@real_y = @y - @sprite[0].height
if @moving then
if @move_x > 0 then
@move_x -= 1
@x += 1
elsif @move_x < 0 then
@move_x += 1
@x -= 1
elsif @move_x == 0 then
@moving = false
end
else
if @dir == :left then
@sprite = @stand_left
elsif @dir == :right then
@sprite = @stand_right
end
end
end
Ostatnią rzeczą jaką dzisiaj zrobimy będzie spadanie i skakanie postaci. Zaczniemy od spadania. Najpierw musimy pozwolić SceneMap odczytać położenie postaci (ponieważ to ta klasa będzie sprawdzała, czy mamy spadać). Żeby to uczynić musimy w Player dać tzw. readery. Są dwa tego sposoby, pierwszy to funkcja
Kod: Zaznacz cały
attr_reader
Kod: Zaznacz cały
def get_x
return @x
end
def get_y
return @y
end
Kod: Zaznacz cały
@player.fall if no_ground?(@player.get_x, @player.get_y)
Jak widać, potrzebuje ona dwóch parametrów, x i y. Jako, że nasza mapa w tej chwili nie istnieje, metoda będzie tylko sprawdzała czy postać nie wypadnie poza okienko. Tak więc jeśli przesłane y jest mniejsze niż wielkość okna (480) zwracamy true, jeśli równe lub większe - zwracamy false.
Kod: Zaznacz cały
def no_ground?(x,y)
return y < 480
end
Kod: Zaznacz cały
def fall
@y += 2
if @dir == :left then
@sprite = @jump_left
elsif @dir == :right then
@sprite = @jump_right
end
end
I wczytajcie tak samo jak poprzednie, jako @jump_left i @jump_right. Użyjcie metody Image#load_tiles mimo, że są to pojedyncze grafiki - to pozwoli nam uniknąć błędów ;) Szybkie odpalenie gry i:
Czyli wszystko działa. Zostało nam skaknie. Wróćmy do SceneMap, i tym razem dla odmiany udajcie się do metody 'button_down(id)'. Dlaczego tutaj a nie tak jak w przypadku ruchu, w update?
Otóż w tej metodzie, żeby gracz powtórzył skok, będzie musiał puścić przycisk i nacisnąć go jeszcze raz (jak wcześniej wspomniałem, ta metoda jest wywoływana tylko raz przy naciśnięciu!). Musimy sprawdzić więc, czy gracz naciśnie odpowiedni przycisk (strzałka do góry), oraz czy jest na ziemi (żeby nie mógł skakać odbijając się od powietrza). Najpierw tylko to sprawdzimy (pierwszy raz użyjemy printa). Nasz kod powinien wyglądać tak:
Kod: Zaznacz cały
if id == KbUp then
if !no_ground?(@player.get_x, @player.get_y) then
p "skok!"
end
end
Kod: Zaznacz cały
if no_ground?(@player.get_x, @player.get_y) == false then
Czyli wszystko działa. Zastąpmy więc naszego printa wywołaniem metody skoku u gracza:
Kod: Zaznacz cały
@player.jump
Kod: Zaznacz cały
@jump = 0
Kod: Zaznacz cały
if @jump == 0 then
@y += 2
if @dir == :left then
@sprite = @jump_left
elsif @dir == :right then
@sprite = @jump_right
end
end
Kod: Zaznacz cały
@jump = 15 if @jump == 0
Dobrze, mamy więc zmienianie @jump oraz powstrzymywanie upadania postaci jeśli tylko @jump jest różne od zera. Teraz w update musimy dać ten skok.
Kod: Zaznacz cały
if @jump > 0 then
@y -= 5
if @dir == :left then
@sprite = @jump_left
elsif @dir == :right then
@sprite = @jump_right
end
@jump -= 1
end
W metodach move_left i move_right musicie dać prosty warunek, żeby @sprite było zmieniane tylko gdy @jump równy jest 0: 'if @jump == 0'.
Kod: Zaznacz cały
def move_left
@dir = :left
@move_x = -3
@sprite = @walk_left if @jump == 0
@moving = true
end
def move_right
@dir = :right
@move_x = 3
@sprite = @walk_right if @jump == 0
@moving = true
end
Na dzisiaj to wszystko. Całkiem sporo udało nam się zrobić :) Do następnego!
Archiwum winrara z efektem dzisiejszej pracy.