W tym temacie zamierzam was w kilkunastu, kilkudziesięciu lekcjach nauczyć programowania w Ruby Gosu. Wiem, że z początku zamierzeniem były videotutoriale, ale ze względu na kilka spraw, musiałbym odłożyć je na dużo później (około miesiąca), więc zdecydowałem zmienić na tutoriale tekstowe. Przynajmniej dostaliście je szybciej :p
W tym poście będzie tylko lista z odnośnikami do kolejnych tutoriali. Jako, że temat jest otwarty, pewnie pomiędzy kolejnymi moimi postami będą również i wasze komentarze, czasami wręcz będą one wymagane (np. w przypadku sond "co wolicie w następnych tutorialach") itp.
Dzięki tym tutorialom stworzymy grę platformową. Tylko tyle jestem w stanie wam powiedzieć, ponieważ gra będzie powstawała na bierząco, razem z wami - wasze uwagi, komentarze są brane pod uwagę i będą kształtowały wynik ;)
Dobrze, teraz bez zbędnego gadania, oto lista poradników:
Lekcje specjalne: 4.5: Parsowanie danych z programu Tiled
Dodatkowe rzeczy:
Grafiki których używam są autorstwa Marca Russella. Są na całkowicie otwartej licencji: autor zezwala na ich używanie nawet w projektach komercyjnych, do tego nie wymaga podania swojego nazwiska. Jedynym warunkiem jest to, żeby nie przywłaszczać ich sobie, więc jeśli zamierzasz ich użyć, to albo podaj go jako autora grafik, albo nie podawaj takowego w ogóle ;)
Ostatnio zmieniony 14 cze 2014, 15:05 przez Ekhart, łącznie zmieniany 12 razy.
No matter how tender, how exquisite… A lie will remain a lie.
Lekcja 1: Instalacja Ruby, Gems, Gosu. Okno gry.
Witajcie w pierwszej lekcji. Zaczniemy od całkowitych podstaw. Poza tym co w tytule podałem, poruszę jeszcze małą sprawę dotyczącą ruby, a dokładnie formatowania kodu, ale na to przyjdzie czas za chwilę ;)
W tutorialach postaram się również tłumaczyć wszystko, żeby osoby które dopiero zaczynają programowanie nie miały problemów. Dla osób, które już potrafią programować w ruby i chcą się nauczyć robić gry (albo przeglądają to z ciekawości) - wybaczcie, ale sami kiedyś zaczynaliście!
Dobra, żeby nie przedłużać. Zacznijmy od ściągnięcia i zainstalowania wszystkiego co potrzebne. Wchodzimy na poniższe adresy i ściągamy: RubyInstaller - ściągamy Ruby 1.9.3-p484. Koniecznie wersję 1.9.3, a nie 2.0.0, ponieważ Gosu nie działa pod 2.0 ;) RubyGems - ściągamy Gems w najnowszej wersji (na dzień dzisiejszy: v2.1.11). Ściągnijcie w formacie "zip" dla windowsa.
Dodatkowo przyda nam się program w którym będziemy pisać kod. Jest ich całkiem sporo, zwykły notatnik nawet da radę. Przykładowe programy; Notepad++, chyba najbardziej znany, otwarty edytor. Prosty, szybki, zdecydowanie wystarczy. Aptana Studio Radrails. Coś dla ludzi, którzy programują w javie za pomocą eclipse. Aptana jest niemalże portem Eclipsa do web developmentu, a Radrails jest dostosowany do pracy z Ruby Rails. Uwaga: nie używałem programu na tyle, żeby określić, czy dokładnie się nadaje do tego, co robimy. Sublime Text. Sam używam Sublime Text 2, i jestem bardzo zadowolony z jego możliwości, jest całkiem rozbudowany. Wadą ejst to, że neistety nie jest darmowy. SciTE jest również dość dobrym programem, i jest darmowym. Pracowałem na nim do pewnego momentu i byłem bardzo zadowolony. Geany, polecany przez Rave'a, wydaje się działać na wszystkich systemach, więc dla osób pracujących na linuksie/OSX może być najlepszym wyjściem.
Gdy już mamy wszystko ściągnięte, czas na instalację. Instalowanie Ruby jest banalnie proste, ogranicza się do kliknięcia w "Next" kilka razy. Za to zainstalowanie Gemsów, oraz samego Gosu, jest ciut trudniejsze.
Gdy już zainstalujemy ruby, wypakujcie RubyGems do osobnego folderu. Powinniście mieć takie pliki:
Na pewno rzucił wam się w oczy plik "setup.rb". Jeśli macie ruby zainstalowane, powinien mieć ikonkę taką jak u mnie. Podwójne kliknięcie na nim uruchomi wiersz poleceń, ale ruby ma niestety jedną wadę - wszystkie błędy momentalnie zamykają program, więc zauważenie ewentualnych błędów jest niemalże niemożliwe. Jedyna możliwość obejścia tego, to otwierać program w już otwartym wierszu poleceń. Dlatego trzymając klawisz shift kliknijcie prawym przyciskiem myszy gdziekolwiek wewnątrz folderu (nie na pliku!) i wybierzcie opcję "Otwórz wiersz polecenia tutaj".
Zapamiętajcie to, bo będziemy tego używać cały czas :)
Gdy już mamy okienko cmd.exe otwarte, wpisujemy "setup.rb", i klikamy enter. Chwila czekania i pojawi nam się informacja, że zostało pomyślnie zainstalowane. Cóż, pozostała trzecia część - instalacja samego gosu. Nie opuszczając wierszu polecenia wpiszcie
Chwila czekania i powinna nam się wyświetlić wiadomość "1 gem installed". Brawo, jesteś gotowy do programowania!
Zanim zaczniemy pisać kod gry, przygotujmy folder w którym ów gra będzie. To jak będziecie nazywać foldery i subfoldery pozostawiam wam. Ja jednak zazwyczaj obieram taką strukturę:
Dodatkowo potrzebujemy kilku plików gosu w naszym folderze. Otwórzcie więc poniższą lokację:
I skopiujcie wszystkie pliki (poza folderem gosu) do folderu naszej gry. Stwórzcie w nim przy okazji plik "Run.rb", który będzie służył do otwierania naszej gry. Otwórzcie go w programie którego używacie, oraz otwórzcie wiersz polecenia w folderze (Shift + PPM) i jesteśmy gotowi do programowania. Żeby nasz plik Run nie był taki pusty, dodajcie małe informacje o projekcie, np:
=begin
Projekt: Gra Platformowa "FuZeD"
Data Rozpoczecia: 06/01/14
Autorzy: Piotr Blaut ("Ekhart")
=end
Zauważcie, że komendy =begin i =end określają wielolinijkowe komentarze. Komentarze w jednej linijce zaznaczamy znakiem #.
Zanim zaczniemy tworzyć nasze okienko chciałem jeszcze poruszyć jedną rzecz. Ruby jest na tyle elastycznym językiem, że pozwala nam pisać oraz wywoływać funkcje na kilka sposóbów, w zależności od tego kto co woli. Tak więc funcje możemy zapisywać na takie sposoby:
Obydwa sposoby działają. Ruby również nie potrzebuje słowa "return" by zwrócić daną wartość. Krótkie funkcje można również umieścić w jednej linijce oddzielając kolejne fragmenty średnikiem ;. Tak więc taka funkcja:
I również będzie działała. To w jaki sposób piszecie kod pozostawiam wam.
Dobrze, przejdźmy teraz do poprawnej części, czyli tworzenie okienka. Najpierw jednak zaimportujmy co jest potrzebne. W pliku "Run.rb" wpiszcie poniższy kod:
$: << File.dirname(__FILE__)
require 'gosu'
require 'rubygems'
include Gosu
Po kolei: pierwsza linijka ($: << File.dirname(__FILE__)) ustala położenie tego pliku jako lokację ładowania skryptów. Bez tego moglibyśmy mieć problemy z wczytywaniem plików potrzebnych przez grę (na pewno był z tym problem w 1.8, nie wiem czy naprawiono to w 1.9, ale jedna linijka kodu nie zaszkodzi). Następnie mamy require 'gosu' i 'rubygems', które wczytuje obie biblioteki. Ostatnia linijka, include Gosu, informuje system, że używamy biblioteki Gosu. Dzięki temu używając klas Gosu (np. Window, które to zaraz będziemy używać) wystarczy wpisać:
To stworzy nową klasę "GameWindow", która jest podklasą klasy "(Gosu::)Window". Dzięki umieszczeniu "include Gosu", mogliśmy pominąć "Gosu::" w importowaniu okna.
Każdy z naszych skryptów potrzebuje metody initialize, która jest wywoływana przy każdym utworzeniu instancji klasy. Do tego w naszym okienku potrzebujemy jeszcze kilku innych metod. Dlatego tworzymy poniższe metody:
def initialize
end
def update
end
def draw
end
def button_down(id)
end
def button_up(id)
end
Te metody służą do:
=> update - odświeżana jest co każdą kratkę (ok. 16 milisekund), zawiera całą "fizykę" danej klasy.
=> draw - wyświetla na ekranie to, co jest w niej umieszczone. Komputer czasami może wywołać tę metodę dwa razy jednocześnie, także zalecane jest, żeby tutaj mieć tylko wyświetlanie (ew. małe obliczenia nie związane z fizyką gry).
I dodatkowo dwie metody klasy Gosu::Window, które biorą parametr id:
=> button_down - wykrywa naciśnięcie przycisku
=> button_up - wykrywa puszczenie przycisku.
Gdy mamy już te metody, zajmijmy się metodą initialize. Potrzebujemy w niej dwóch rzeczy:
Pierwsza tworzy okienko. Przyjmuje trzy parametry: szerokość, wysokość oraz fullscreen. Myślę, że okienko rozmiaru 640x480, bez fullscreena, będzie wystarczające. Wpisujemy więc:
W metodę initialize. Druga rzecz, to komenda "caption", która jest po prostu nazwą naszego okienka, (czyli naszej gry). Musimy się jednak odwołać do tego, konkretnego okna. Wpisujemy więc:
self informuje system, że celem zmiany jest miejsce w którym została wywołana (dana klasa). W cudzysłowiu umieszczamy tytuł gry.
Spoiler:
Jeśli chcecie mieć tytuł gry po polsku (albo w innym języku używającym specjalnych znaków), musicie na początku każdego pliku w którym znajduje się taki tekst dodać na samym początku linijkę:
Oraz zapisać plik z kodowaniem utf-8. Można to zrobić w notatniku:
Niezapisanie pliku w ten sposób może powodować trochę problemów przy obcojęzycznych systemach!
I to wszystko, podstawowe okienko jest gotowe! Teraz wystarczy w wierszu polecenia wpisać "Run.rb", odpalić i...
...nic się nie dzieje. Ale dlaczego?!
Ponieważ nic nie zrobiliśmy ;) to, że okno jest zrobione, nie znaczy, że zostało użyte. Dlatego wracamy do pliku Run.rb i na końcu dodajemy te dwie linijki:
Po kolei: $window tworzy zmienną globalną, czyli taką, którą możemy użyć w każdej chwili, niezależnie w której części programu jesteśmy. Do niej przypisujemy klasę GameWindow. .new na końcu informuje nas, że owszem, to jest klasa i tworzymy jej nową instancję. W drugiej linijce odwołujemy się do metody "show" danej zmiennej, czyli do metody show klasy GameWindow. Nasza klasa nie posiada takiej metody, ale klasa Gosu::Window (której podklasą jest nasze okienko) ją posiada, więc skrypt odwoła się właśnie do niej. No, teraz jak już mamy wyświetlenie okienka, możemy spokojnie odpalić i podziwiać...
...błąd. Dlatego mamy ciągle otwarty wiersz polecenia - gdybyśmy uruchomili program przez podwójne kliknięcie, nie wiedzielibyśmy dlaczego nie działa ;). Na razie treść błędu pewnie jest dla was całkowicie niezrozumiała, więc tłumaczę: Run.rb nie ma pojęcia czym jest "GameWindow". Ale dlaczego?
Otóż spójrzcie kilka linijek wyżej, na
Jak wspomniałem, komenda require importuje klasy. Więc pewnie już się domyślacie czego potrzebujemy - poniżej, najlepiej pod "include Gosu', wpisujemy:
Jak to wrzucić do folderu? Skomplikowana ta instalacja. Mam stworzyć osobny folder w folderze "Ruby 193" czy na dysku? Czy mam wszystko z "RubyGems" wrzucić do "Ruby 193", ale wtedy muszę podmieniać, a zgaduję że tak być nie może. EDIT// Już nie trzeba, dałem radę lol
Ja używam Geany. Bardzo dobre IDE "do wszystkiego", można go spiąć w zasadzie z każdym kompilerem/interpreterem, jest multiplatformowy czyli zarówno Linux jak i rozbite okna i zgniłe jabłka ;). Nie pamiętam czy pokazuje logi (ale raczej tak) bo dawno go używałem i chyba go przez przypadek odinstalowałem.
//edit: Dwa pytania:
- Czemu masz angielskiego Windowsa?
- Czy ruby rozróżnia wielkość znaków w identyfikatorach (czyli czy RPG_Maker=rpg_maker)
A o SciTe całkowicie zapomniałem (bo mi magicznie przestał działać xd), dodaję już do posta.
Ja używam Geany. Bardzo dobre IDE "do wszystkiego", można go spiąć w zasadzie z każdym kompilerem/interpreterem, jest multiplatformowy czyli zarówno Linux jak i rozbite okna i zgniłe jabłka ;). Nie pamiętam czy pokazuje logi (ale raczej tak) bo dawno go używałem i chyba go przez przypadek odinstalowałem.
//edit: Dwa pytania:
- Czemu masz angielskiego Windowsa?
- Czy ruby rozróżnia wielkość znaków w identyfikatorach (czyli czy RPG_Maker=rpg_maker)
W sumie jeśli ktoś zna IDE dla Linuxa czy OSX, to proszę o napisanie tutaj/wysłanie PW, chętnie dorzucę do posta. Nie pracuję w tych środowiskach, więc nie wiem co tam się używa :p
A co do pytań:
1 - mieszkając w Irlandii od ponad 6 lat przyzwyczaiłem się do angielskiego we wszystkim. No i dorwanie polskiej wersji tutaj jest nieco trudne :p
2 - Ruby, jak chyba każdy język programowania (nie markupowy!) traktuje zmienne zapisane inaczej jako odrębne zmienne. Więc rpg_maker != Rpg_Maker != RPG_MAKER. Musisz dokładnie pamiętać jak nazywasz zmienne, i trzymać się ich nazewnictwa ;)
No matter how tender, how exquisite… A lie will remain a lie.
inaczej będziecie mieć dziwne błędy. Musiałem nieco za tym pogooglać, bo zdecydowałem użyć polskich znaków w tytule gry (będę się słuchał w kwestii kodu, ale od grafiki, designu poziomów i muzyki mi wara! ;) ).
//edit: A co do IDE to ja gorąco polecam Geany. Nie dość że działa na wszystkich systemach (a przynajmniej tych liczących się, choć słyszałem że ktoś portuje to na Haiku o ile jeszcze nie skończył) to można go zintegrować go z praktycznie każdym kompilatorem/interpreterem.
Mam nadzieję że jeszcze poruszysz temat dystrybucji gry - nie chcę aby każdy gracz musiał instalować ruby i gosu.
inaczej będziecie mieć dziwne błędy. Musiałem nieco za tym pogooglać, bo zdecydowałem użyć polskich znaków w tytule gry (będę się słuchał w kwestii kodu, ale od grafiki, designu poziomów i muzyki mi wara! ;) ).
Teoretycznie mógłbym zmienić na język polski, ale nic bym nie potrafił znaleźć, za bardzo się do angielskiego przyzwyczaiłem ;)
A kwestii kodowania utf-8 nie poruszyłem jeszcze, bo nie wydawało mi się to potrzebne, ale jak widać, myliłem się ;) już dodaję, dodatkowo plik często trzeba zapisać z tym samym kodowaniem.
//Edit:
Już dodaję IDE, a kwestię wydawania gry poruszę w odpowiednim momencie.
@Hubertov - odpisałem na PW, ale jeszcze tutaj powiem:
Hubertov zamknął klasę GameWindow (end w linijce 3), a potem próbował do niej dodać metody (def... w 4 linijce itd), czego Ruby nie rozumiało (mniej więcej można to porównać do otwarcia zeszytu, zamknięcia go i pisania na biurku z nadzieją, że znajdziemy to potem w w/w zeszycie ;) ).
@down - jeśli tylko podasz mnie jako autora, to nie mam nic przeciwko. Oszczędzi mi to robienia tego samego :p
Ostatnio zmieniony 06 sty 2014, 20:53 przez Ekhart, łącznie zmieniany 1 raz.
No matter how tender, how exquisite… A lie will remain a lie.
Akurat nie wielkimi literami, ale z pierwszą literą wielką z tego co zgooglałem.
//edit: Ekhart, nie wiem czy wiesz (pewnie tak, skoro jest na głównej stronie Gosu), ale jest dobry gem zwany Chingu który bardzo ułatwi robienie gry (obsługa stanów, obiekty gry) niż jechanie na czystym Gosu. Jedyną wadą jest dość kiepska dokumentacja, jednak gem instaluje się z wieloma przykładami, które pomogą się zorientować o co biega.
Faktycznie, masz rację, ale tylko częściowo, bo przyjętym standardem jest, że stałe w Ruby piszemy w całości wielkimi literami, nawet w makerowych skryptach można to zauważyć. =)
Lexically, the names of constants look like the names of local variables, except that they begin with a capital letter. By convention, most constants are written in all uppercase with underscores to separate words, LIKE_THIS. Ruby class and module names are also constants, but they are conventionally written using initial capital letters and camel case, LikeThis.
W skrócie - w całości wielkimi literami nazwy stałych zawierających wartość, pierwsza wielka litera dla nazw klas/modułów, które też są stałymi.
Oczywiście to tylko umowna forma, ale po co komplikować życie osobom trzecim (np. jak robisz z kimś program, albo piszesz tutorial)?
//edit: Ekhart, nie wiem czy wiesz (pewnie tak, skoro jest na głównej stronie Gosu), ale jest dobry gem zwany Chingu który bardzo ułatwi robienie gry (obsługa stanów, obiekty gry) niż jechanie na czystym Gosu. Jedyną wadą jest dość kiepska dokumentacja, jednak gem instaluje się z wieloma przykładami, które pomogą się zorientować o co biega.
Owszem, znam Chingu, jest też rubygame. Ale w obu nigdy nie siedziałem na tyle, żeby dobrze je znać (a przynajmniej tak dobrze, jak Gosu) - głównie przez kiepską dokumentację - więc niezbyt mogę o nich uczyć :p
No matter how tender, how exquisite… A lie will remain a lie.
OK, rób jak uważasz. Tylko powodzenia z bardziej skomplikowanymi rzeczami jak stany, animowanie spritów, itd. Ja jeszcze dam radę, głównie dlatego że w przeszłości napisałem framework oparty o Allegro na potrzeby swojej gry (Super Heli Land, googlnij sobie, projekt anulowany), ale noobki już nie.
//edit: I dodaj Geany do listy IDE, bardzo porządne, nosz już chyba trzeci raz tutaj piszę o tym. A do chingu są przykłady instalowane w katalogu gema, nawet dobrze komentowane.
class Animation
def initialize(window, file, x, y)
@anim = Image.load_tiles(window, "Graphics/"+file+".png", x, y, true)
@counter = 0
end
def update
increase_counter()
end
def increase_counter()
@counter += 1
end
def reset_counter()
@counter = 0
end
def draw_animated(x, y, z, speed, moving = true)
case moving
when true then x_mod = $window.camera_x; y_mod = $window.camera_y # !!!!!!!!!
when false then x_mod, y_mod = 0
end
for i in 0..@anim.size-1
if @counter == speed
@anim[i].draw(x + x_mod, y + y_mod, z)
reset_counter()
end
end
end
end
Jedną metodą wczytujesz plik, który będzie non-stop się animował. Można by jeszcze dopisać tu prostą metodę (3 linijki kodu) pozwalającą na wyświetlanie konkretnych klatek i tryb, w którym animacja skończy się po iluś klatkach.