» Najnowsze projekty

Strona Główna Tutoriale Rozbudowany HUD.
Rozbudowany HUD.
Redaktor: Dan    Dodano: 01/07/2009 - 15:34
Poradnik:
Jak zrobić rozbudowanego HUDa w Ruby (jednoosobowego)


Opis:
Zauważyłem niedawno, że w dziale 'Poradniki' nie ma żadnego tematu poświęconego robieniu HUDa w Ruby. Początkującym rzecz ta może wydawać się trudna (szczególnie w RGSS) jednak jest prawie że tak prosta jak na eventach =] Postanowiłem więc napisać poradnik, w którym umieszczę masę rzeczy dzięki którym każdy będzie mógł stworzyć swój własny HUD, bez podstawowych ograniczeń spowodowanych niewiedzą ;3

Wymagane:
-Grafiki do naszego HUDa
-RPG Maker XP
-Umiejętność czytania i pisania (na klawiaturze ofc, nie ma tak łatwo)
-TroszeczkÄ™ wolnego czasu
-Podstawowa znajomość Ruby (tworzenie klas etc.)

1. Organizacja
Pierwszym krokiem jest zaplanowanie wyglądu naszego HUDa oraz znalezienie grafik (zrobić też można, polecam). W tym celu otwieramy dowolny program graficzny (Paint rox), ustawiamy wymiar obrazu na 640x480 i rysujemy wszystko co będziemy chcieli mieć. Dzięki obrazowi o wymiarach 640x480 wiemy, ile miejsca będzie zajmował HUD (żeby się czasami nie zdarzyło tak, że obrazki będą zawalały połowę ekranu :>) Gdy już narysujemy zarys, standardowo chwalimy się kolegom. Mój szkic wygląda tak:


Po co tak dużo? Po to by pokazać jak najwięcej możliwych opcji w HUDzie. Ok, skończyliśmy już względny plan, tak więc tworzymy (lololol) grafiki do HUDa. W moim przypadku będą to:
-Background HUDa
-Twarz bohatera
-Pasek HP
-Masek MP
-Pasek Exp

Po jakimś czasie spędzonym w GIMPie powstały proste graficzki (możecie brać, ale twarz nie moja) które wrzucamy w osobne pliki:


No dobra, posiadamy już wszystkie graficzne rzeczy. Czas na modyfikację kodu :d

2. Podstawowe wyposażenie
Na początek wrzucamy wszystkie grafiki do folderów gry (Tylko nie wrzucajcie do 'Audio/' XD, polecam dać wszystko do folderu 'Graphic/Pictures'). Następnie otwieramy projekt i wchodzimy do Edytora Skryptów, gdzie wykonujemy następujące czynności:
-Tworzymy nowÄ… klasÄ™ nad 'Main' i nazywamy jÄ… sobie jak chcemy (Np. 'HUD Script')
-Odnajdujemy klasę 'Scene_Map', kopiujemy jej zawartość i wklejamy do stworzonej wcześniej klasy (od teraz wszystkie operacje ze 'Scene_Map' wykonujemy na tej klasie)
-Odnajdujemy klasę 'Window_Base', kopiujemy jej zawartość i wklejamy do stworzonej wcześniej klasy (pod wklejoną zawartość 'Scene_Map')
-Usuwamy z wklejonego 'Window_Base' wszystkie warunki (def cośtamy) tak by powstało coś takiego:

 
#==============================================================================
# ** Window_Base
#------------------------------------------------------------------------------
#  This class is for all in-game windows.
#==============================================================================
class Window_Base < Window
end
 


Ok, najtrudniejsze za nami xd Teraz po kolei robimy naszego HUDa.

3. Background HUDa
Na początek zajmiemy się wyświetlaniem głównego obrazka HUDa, w miom przypadku tego:


Wspinamy się na górę naszego HUDa, czyli do Scene_Map. Gdy zobaczymy komendę:
 
# Make sprite set
 


Długo się w nią wpatrujemy ;d Ok, teraz zajmiemy się wszystkim co jest między tym komentarzem a kolejnym. Wymyślamy sobie jakąś nazwę dla naszego obrazka, powiedzmy że hud_back. Gdy to zrobimy, pod znanym nam komentarzem wpisujemy:
 
@hud_back = Sprite.new
 


Zrobione? Ok, dzięki temu będziemy mogli wyświetlić obrazek. Pod tą komendą wpisujemy:
 
@hud_back.bitmap = RPG::Cache.picture("Nazwa_obrazka")
 


Zamiast 'nazwa_obrazka' wpisujemy nazwę naszego pliku z backgroundem HUDa. Ma sie on znajdować w folderze 'Pictures' ofc ;s Gdy to napiszemy, wklejamy takiego cosia:
 
@hud_back.x = X
@hud_back.y = Y
@hud_back.z = Z
@hud_back.opacity = O
 


Czyli:
X - położenie X na ekranie (poziom)
Y - położenie Y na ekranie (pionowo)
Z - położenie 'warstwowe' (możemy np. ustawić że obrazek będzie przykrywał niektóre rzeczy a niektóre nie).
O - przezroczystość obrazka (0 - przezroczysty, 1-254 półprzezroczysty i 255 - brak przezroczystości).

Ustawiamy sobie według własnych potrzeb, ja dam tak:

 
@hud_back.x = 0
@hud_back.y = 0
@hud_back.z = 100
@hud_back.opacity = 255
 


Położyłem więc obrazek w lewym-górnym rogu ekranu, nieprzezroczysty. Jeżeli nei chcecie kombinować z warstwą obrazka, polecam ustawić ją na 100. Teraz zjeżdżamy na dół aż odnajdziemy:
 
@spriteset.dispose
 


I dajemy pod tym:
 
@hud_back.dispose
 


Dzięki temu nasz HUD zostanie usunięty przy włączaniu menusów itp. Jeżeli chcemy mieć swój HUD w menusach nie wpiczujemy nigdzie tego kodu Xc Na końcu znajdujemy:
 
@spriteset.update
 


I dajemy pod:
 
@hud_back.update
 


4. Okienko HUDa
Dodawanie napisów oraz obrazków na HUDzie ułatwi nam przezroczyste okienko nad nim. W tym celu robimy następującą rzecz:
-Znajdujemy 'Window_Gold' i wklejamy jego zawartość na koniec naszego skrypciku
-Zamieniamy:

 
class Window_Gold < Window_Base
 


Na nazwe okienka HUDa, dajmy na to:

 
class Window_HUD < Window_Base
 


Ok, teraz odnajdujemy w tym samym okienku:
 
cx = contents.text_size($data_system.words.gold).width
self.contents.font.color = normal_color
self.contents.draw_text(4, 0, 120-cx-2, 32, $game_party.gold.to_s, 2)
self.contents.font.color = system_color
self.contents.draw_text(124-cx, 0, cx, 32, $data_system.words.gold, 2)
 


I usuwamy to. Dzięki temu mamy czyste okienko. Została nam jeszcze jedna rzecz. Troszeczkę wyżej widzimy:
 
super(0, 0, 160, 64)
 


Nas interesują tylko dwie ostatnie wartości, początkowi dajemy spokój. Tak więc 160 oznacza szerokość okienka a 64 jego wysokość. Zamieniamy te wartości tak, by odpowiadały naszemui HUDowi. Pamiętajcie o tym, że im większe okienko i więcej będzie w nim obrazków i tekstów, tym bardziej zmniejszy się liczna FPS. Zazwyczaj daje się tam szerokość oraz wysokość taką jak Background, chyba że informacje w HUDzie mają być poza nim. Ja zamieniłem to tak:
 
super(0, 0, 370, 160)
 


Tak więc nadałem okienku szerokość taką jak background HUDa.

Teraz musimy sprawić, by okienko pojawiło się na mapie. W tym celu znajdujemy:

 
@hud_back = Sprite.new
 


Wymyślamy sobie jakąś nazwę dla okienka, dajmy na to 'window_hud'. Pod komendami 'hud_back' dajemy coś takiego:
 
@window_hud = Window_HUD.new
@window_hud.opacity = O
@window_hud.x = X
@window_hud.y = Y
@window_hud.z = Z
 


Czyli:
O - przezroczystość całego okienka
X - położenie X na ekranie (poziom)
Y - położenie Y na ekranie (pionowo)
Z - warstwa

Myślę, że X i Y najlepiej ustawić takie same jak X i Y obrazka. Przezroczystość okienka ustawiamy na 0, chyba że chcemy by było widoczne :d Co do warstwy to musi ona być wyższa niż warstwa backgrounda. U mnie wygląda to tak:

 
@window_hud.opacity = 0
@window_hud.x = 0
@window_hud.y = 0
@window_hud.z = 101
 


Teraz pozostaje jeszcze wkminić

 
@window_hud.dispose
 


Pod disposem backgrounda oraz
 
@window_hud.refresh
 


Pod jego updatem ;p Teraz już możemy zapełniać naszego HUDzika obrazkami, tekstami, zmiennymi oraz wszystkim czym chcemy :D

5. Wypełnianie HUDa tekstem
Przyszedł czas na wrzucenie do HUDa tekstu ;> Jest wiele możliwości jego wyświetlania. Może on przybrać wartości zmiennych oraz być pokazywanym w zależności od nich (warunki). Mój HUD chcę wypełnić:
-Ilością kasy
-Imieniem bohatera
-Poziomem postaci
-Statusem postaci
-Atakiem oraz obronÄ… bohtera
-Zwykłą zmienną z gry (przykładem będzie Karma czyli wartość dobrej lub złej energii ;x)
-HP oraz MP

Hmm, trochę tego jest. Prawie każdy typ tekstu się od siebie różni, ale dosyć teorii - przejdźmy do praktyki. Zacznijmy od wyświetlenia kasy drużyny. Lecimy na sam dół skryptu, czyli do 'Window_HUDa'. Miejsce na tekst znajduje się pod takim ładnym napisem

 
self.contents.clear
 


Ok, wybieramy sobie jaki kolor ma mieć tekst. Załóżmy że normalny. Pod tekstem na górze wklejamy coś takiego:

 
self.contents.font.color = normal_color
 


Pozwala to zmienić kolor wyświetlanego tekstu. Domyślne kolory to:
-normal_color (biały)
-disable_color (szary)
-system_color (błękitny)
-crisis_color (żółty)
-knockout_color (czerwony)

Jeżeli chcemy uzyskać jakiś nowy kolorek, wspinamy się trochę wyżej (do naszej pustej klasy 'Window_Base' i dajemy tam coś takiego:

 
def new_color
return Color.new(R, G, B, O)
end
 


Zamiast 'new_color' dajemy nazwę koloru jaka będzie oznaczała nasz własny. Końcówka nie musi kończyć się na '_color'. Mogą to być też inne nazwy :o Nie używajcie tylko nazw wymienionych wcześniej, bo popsuje to wam inne kolory ;x Oznakowania:
R - ilość koloru czerwonego w kolorze
G - ilość koloru zielonego w kolorze
B - ilość koloru niebieskiego w kolorze
O - przezroczystość koloru

Ja stworzyłem sobie dwa kolory poprzez dodanie ich do 'Window_Base', które później wykorzystam w HUDzie:

 
def new_color
return Color.new(255, 0, 0, 255)
end
def new_color
return Color.new(0, 255, 0, 255)
end
 


Wróćmy do wyświetlania tekstu, bo trochę odbiegliśmy od tematu Xd Kod na wyświetlenie tekstu wygląda tak:
 
self.contents.draw_text(X, Y, W, H, Text, T)
 


Czyli:
X - poziom X wyświetlonego tekstu w okienku
Y - poziom Y wyświetlonego tekstu w okienku
W - szerokość w której ma mieścić się tekst (jeżeli przekroczy tą długość zaczyna się zwężać)
H - wysokość w której mieści się tekst (domyślnie 32)
Text - chyba wiadomo o co chodzi xd Tekst który ma się wyświetlić zawsze wpisujemy w cudzysłowiu (tym podwójnym, np. "Tekst"), natomiast jeżeli ma się wyświetlić wartość zmiennej, wpisujemy ją bez cudzysłowia, dodając na jej końcu '.to_s' (np. $game_variables[51].to_s). Ta końcówka zamienia naszą zmienna w string, czyli ciąg znaków. Bez tego zmienna będzie miała wartość ale nie zostanie wyświetlona)
T - sposób wyświetlania tekstu przyjmujący trzy wartości (0, 1 lub 2). 0 - do lewej, 1 - wyśrodkowanie, 2 - do prawej.

Przed pokazywaniem powinniśmy też ustawić czcionkę tekstu oraz jego wielkość. W tym celu wracamy do 'Window_HUD' i znajdujemy napis

 
self.contents = Bitmap.new(width - 32, height - 32)
 


Następnie wklejamy pod nim takie cacko:

 
self.contents.font.name = F
self.contents.font.size = S
 


F - nazwa czcionki jaką sobie wybierzemy (musi być w cudzysłowiu, np. "Arial"
S - wielkość czcionki

Przykład (domyślny):

 
self.contents.font.name = "Arial"
self.contents.font.size = 22
 


Wracając do kasy XD~ Zmienna odpowiadająca za ilość pieniędzy to
 
$game_party.gold
 


A zmienna zachowujÄ…ca nazwÄ™ waluty podanej w bazie danych to
 
$data_system.words.gold
 


Tak więc pod

 
self.contents.font.color = normal_color
 


Wpisujemy poprzednie schematy wyświetlania tekstu oraz zmieniania kolorów (rozpis zmiennych globalnych znajduje się na końcu tego punktu). Ja sobie zrobiłem coś takiego
 
self.contents.font.color = normal_color
self.contents.draw_text(-766, 85, 999, 32, $game_party.gold.to_s, 2)
self.contents.font.color = system_color
self.contents.draw_text(235, 85, 999, 32, $data_system.words.gold)
 


W tym przypadku wyświetla nam się ilość złota, a troszeczkę dalej waluta podana w bazie danych ^^" Im niżej komenda na tekst (zresztą nie tylko na tekst, również na obrazki itp.) tym wyższa jest jej warstwa. Z przykładu na górze wynika więc, że nazwa waluty przykrywałaby ilość pieniędzy.

W grze często mamy rzeczy które zapisujemy do zwykłych zmiennych gry (np. uczciwość, czas w grze, punkty nauki, przejście gry w procentach, ilość obecnych questów). Zmienne możemy pokazać w HUDzie równie dobrze jak tekst. By pokazać zmienną w tekście, wystarczy wklepać coś takiego:

 
self.contents.draw_text(X, Y, W, H, $game_variables[ID].to_s, T)
 


Czyli:
X - poziom X wyświetlonego tekstu w okienku
Y - poziom Y wyświetlonego tekstu w okienku
W - szerokość w której ma mieścić się tekst
H - wysokość w której mieści się tekst
ID - numer zmiennej jaka ma zostać wyświetlona.
T - sposób wyświetlania tekstu przyjmujący trzy wartości, 0 - do lewej, 1 - wyśrodkowanie, 2 - do prawej.

Dla przykładu posłużę się karmą (uczciwością, dobrą/złą energią - nazywajcie to jak chcecie). Gdy karma będzie ujemna czcionka będzie miała kolor czerwony, a gdy dodatni - czcionka będzie miała kolor zielony. Zapiszę ją do zmiennej o ID 1, oraz stworzę drugą zmienną o ID 2 która będzie dodatnia (gdy mniejsza od 0 mnożona przez -1 by odwrócić znak). Następnie robię bardzo przydatną rzecz czyli warunek na zmiennych gry, pod komendami do kasy. Warunek wygląda tak:

 
if $game_variables[ID] == N
1st Action
else
2nd Action
end
 


Pamiętajcie o podwójnym znaku równości, inaczej nic z tego nie będzie :p Tak więc za pomocą warunków robię wyświetlanie karmy. Wykombinowałem to sobie tak (posłużyłem się stworzonymi wcześniej kolorami):

 
if $game_variables[1] > 0
self.contents.font.color = green_color
else
if $game_variables[1] < 0
self.contents.font.color = red_color
else
self.contents.font.color = normal_color
end
end
self.contents.draw_text(-453, 68, 999, 32, "Karma: " + $game_variables[2].to_s , 1)
 


Po polsku oznacza to:
Jeżeli karma jest większa od 0, daj jej kolor zielony. Gdy karma jest mniejsza od 0 (else), daj jej kolor czerwony. Gdy żaden z tych warunków nie został spełniony (Karma wynosi 0) zmień kolor na biały. Prawda, że fajne ^^? Wystarczy tylko troszeczkę pokombinować. Ok, mamy już dwie rzeczy. Teraz pójdzie łatwo.

Imię postaci, jej poziom, status, klasę itp. wyświetlamy za pomocą komend 'draw' (np. draw_actor_name). Tak właśnie zrobimy. Jednak by to podziałało, musimy znaleźć w 'Window_HUD'

 
def initialize
 


Zamienić na
 
def initialize(actor)
 


Potem dodać nad refreshem
 
@actor = actor
 


Potem znaleźć
 
@window_hud = Window_HUD.new
 


I zamienić na
 
@window_hud = Window_HUD.new(@actor)
 


Pod 'Main' wkleić
 
@actor = $game_party.actors[@actor_index]
 


A pod 'Scene_Map' wkleić
 
def initialize(actor_index = 0, equip_index = 0)
@actor_index = actor_index
end
 


Uff... Zrobione :D? To lecimy. Na dole tekstu wstawiamy odpowiednio to co nam się podoba (rozpis komend znajduje się na końcu tego punktu).
Ja wybrałem sobię imię, poziom, status, HP, MP Attack oraz Total Defense postaci. Następnie zmieniłem to na kod i wbiłem w okienko:

 
draw_actor_name(@actor, 92, -3)
draw_actor_level(@actor, 180, -3)
draw_actor_state(@actor, 215, -3)
 


A potem jeszcze
 
self.contents.font.color = normal_color
attack = @actor.atk
self.contents.draw_text(84, 87, 999, 32, attack.to_s)
defend = @actor.pdef + @actor.mdef
self.contents.draw_text(154, 87, 999, 32, defend.to_s)
 


To ostatnie wyświetla w jednym atak a w drugim wyniku sumę obrony fizycznej i magicznej (Total Defense).

Tak więc umiemy już dodawać tekst do naszego HUDa :DDD! Tutaj jest lista przydatnych komend 'draw':

 
draw_actor_graphic - pokazuje character jaki posiada postać
draw_actor_name - pokazuje imiÄ™ bohatera
draw_actor_class - pokazujÄ™ klasÄ™ postaci
draw_actor_level - pokazuje level bohatera
draw_actor_state - pokazuje postaci
draw_actor_exp - pokazuje doświadczenie bohatera
draw_actor_hp - pokazuje hit points postaci
draw_actor_sp - pokazuje skill points bohatera
draw_actor_parameter (actor, x, y, type) 
# pokazuje parametry postaci 
#(dla type: 0 - atak, 1 - obrona fizyczna, 2 - obrona magiczna, 
#3 - siła, 4 - zręczność, 5 - zwinność, 6 - inteligencja).
 


Lista przydatnych zmiennych globalnych:
 
$game_variables[N] - pokazuje wartość zmiennej nr N.
$game_switches[N] - pokazuje wartość przełącznika nr N (True lub False)
$game_system.save_count - pokazuje ilość zapisów gry
$data_system.words.hp - pokazuje słowo 'HP' zapisane w bazie danych
$data_system.words.sp - pokazuje słowo 'SP' zapisane w bazie danych
$data_system.words.atk - pokazuje słowo "Atk" zapisane w bazie danych
$data_system.words.pdef - pokazuje słowo "Pdef" zapisane w bazie danych
$data_system.words.mdef - pokazuje słowo "Mdef" zapisane w bazie danych
$data_system.words.str - pokazuje słowo "Str" zapisane w bazie danych
$data_system.words.dex - pokazuje słowo "Dex" zapisane w bazie danych
$data_system.words.agi - pokazuje słowo "Agi" zapisane w bazie danych
$data_system.words.int - pokazuje słowo "Int" zapisane w bazie danych
$data_actors[N].final_level - ostateczny poziom postaci o numerze N
$data_actors[N].name - imiÄ™ bohatera o numerze N
$data_actors[N].level - poziom postaci o numerze N
$data_actors[N].class_name - nazwa klasy bohatera o numerze N
$data_actors[N].exp_s - doświadczenie postaci o numerze N
$data_actors[N].next_exp_s - doświadczenie potrzebne do następnego poziomu dla postaci o numerze N
$data_actors[N].hp - pokazuje ilość HP postaci o numerze N
$data_actors[N].maxhp - pokazuje MaxHP postaci o numerze N
$data_actors[N].sp - pokazuje ilość HP postaci o numerze N
$data_actors[N].maxsp - pokazuje MaxSP postaci o numerze N
$data_actors[N].skills.size - ilość umiejętności jakie posiada postać o numerze N
$data_actors[N].item.size - ilość przedmiotów jakie posiada postać o numerze N
$data_actors[N].str  - ilość str postaci o numerze N
$data_actors[N].dex - ilość dex postaci o numerze N
$data_actors[N].agi - ilość agi postaci o numerze N
$data_actors[N].int - ilość int postaci o numerze N
$data_actors[N].atk - ilość atk postaci o numerze N
$data_actors[N].pdef - ilość pdef postaci o numerze N
$data_actors[N].mdef - ilość mdef postaci o numerze N
$data_actors[N].eva - ilość eva postaci o numerze N
 


6. Paski HP, MP i Exp
Hmm... Wreszcie to, co najważniejsze jest w HUDzie, czyli paski. Niestety i tu będzie trochę roboty ;p Na początek lecimy do 'Window_Base' i wklejamy taki szablonik:

 
def draw_hp_meter(actor, x, y)
hpbar = RPG::Cache.picture("File")
cw = hpbar.width  * actor.hp / actor.maxhp
ch = hpbar.height
src_rect = Rect.new(0, 0, cw, ch)
self.contents.blt(x, y, hpbar, src_rect)
end
 


Zamiast 'File' wstawiamy tam nazwę pliku, który posiada pasek HP. Jeżeli chcemy, by nasz pasek zwęrzał się ku górze, to
 
 * actor.hp / actor.maxhp
 


Usuwamy i dajemy po
 
ch = hpbar.height
 


Gdy już to zrobimy, idziemy do dobrze nam znanego 'Window_HUD' (tam gdzie są nasze napisy :d) i wklejamy na samej górze

 
draw_hp_meter(@actor, X, Y)
 


A zamiast X i Y wpisujemy współrzędne. Czemu komenda ta ma być najwyżej? Żeby w HUDzie nie przykrywała żadnych napisów, szczególnie tych z HP ;> I jak, zrobione? Teraz znowu lecimy do 'Window_Base', kopiujemy dopiero co wbity szablonik i zmieniamy w nim każde 'hp' na 'sp'. Potem już tylko dostosowujemy go do naszych potrzeb. Mój szablonik wygląda tak:
 
def draw_mp_meter(actor, x, y)
mpbar = RPG::Cache.picture("MP_Meter")
cw = mpbar.width  * actor.sp / actor.maxsp
ch = mpbar.height
src_rect = Rect.new(0, 0, cw, ch)
self.contents.blt(x, y, mpbar, src_rect)
end
 


Teraz wystarczy wbić pod
 
draw_hp_meter(@actor, X, Y)
 


ten kod:
 
def draw_mp_meter(@actor, X, Y)
 


I poustawiać parametry ;d

Te dwie rzeczy już zrobione, by zrobić pasek expa, wystarczy tylko skopiowaćjeden z szablonów, każde 'hp'/'sp' zamienić na 'exp' a w linijce trzeciej dać:

 
actor.now_exp.to_f / actor.next_exp
 


zamiast
 
cw = hp(sp)bar.width  * actor.hp(sp) / actor.maxhp(sp)
 


Na koniec w wklejamy w klasie 'Game_Actor' (gdzieÅ› pod 'def index'):
 
def now_exp
return @exp - @exp_list[@level]
end
def next_exp
return @exp_list[@level+1] > 0 ? @exp_list[@level+1] - @exp_list[@level] : 0
end
 


7. Twarz zależna od HP bohatera
Teraz trik który nieczęsto pojawia się w innych grach, czyli faceset postaci zależny od HP. Na początek upewnijmy się, że mamy w folderze z obrazkami twarze bohatera. Są? To fajnie. Nazwijmy je sobie 'Face[Liczba]', czyli np. Face01, Face07, Face666 itd. Teraz idziemy do dobrze nam już znanego 'Window_Base'. Piszemy tam nowy warunek:

 
def draw_actor_faces(actor, x, y)
end
 


Ok, co teraz? Skoro chcemy by wyświetlanie twarzy było zależne od posiadanego HP z pewnością musimy porobić warunki. Zmienna odpowiadająca za obecne HP postaci to
 
actor.hp
 


a ta mówiąca ile bohater ma max.hp to
 
actor.maxhp
 


Tak więc między 'def' a 'end' wstukujemy warunki które pozwolą nam pokazać twarze w zależności od HP. Ja zrobiłem coś takiego:
 
if actor.hp <= actor.maxhp * 0.25
else
if actor.hp <= actor.maxhp * 0.5
else
if actor.hp <= actor.maxhp * 0.75
else
end
end
end
end
 


Co oznacza: Jeżeli bohater ma 25%HP lub mniej to coś, jeżeli inaczej - ma 50% lób mniej to coś, jeżeli jeszcze inaczej - ma mniej niż 75% to coś, a jeżeli mniej niż 100% to coś. Teraz do każdego else'a wbijamy
 
face = RPG::Cache.picture("FaceFile")
cw = face.width
ch = face.height
src_rect = Rect.new(0, 0, cw, ch)
self.contents.blt(x, y, face, src_rect)
 


A zamiast 'FaceFile' pliki z twarzÄ… (np. Face01, Face07, Face666)

Gdy już to zbudujemy, zjeżdżamy do okienka 'Window_Hud' i standardowo wklejamy komendę, w tym przypadku

 
draw_actor_faces(@actor, X, Y)
 


I ustawiamy parametry. Teraz możemy się cieszyć mordeczką naszego herosa :p

8. Wyświetlanie obecnie trzymanej broni oraz tarczy
Teraz rzecz trochę łatwiejsza, czyli pokzywanie obecnie trzymanej tarczy oraz broni :o Pierwsze co, to powrót do 'Window_Base'. Wpisujemy tam kolejny warunek, for example:

 
def draw_equip_items(item, x, y)
end
 


Teraz wklejamy w nim taki prosty szablonik:
 
if item == nil
return
end
itemicon = RPG::Cache.icon(item.icon_name)
self.contents.blt(x, y, itemicon, Rect.new(0, 0, 24, 24))
 


Jeżeli chcemy zmienić wielkość ikony, to w
 
Rect.new(0, 0, 24, 24)
 


Zmieniamy 24 na inną wielkość =] Co potem? Jedziemy do 'Window_HUD' do naszego ukochanego 'def refresh' ;> Następnie wbijamy tam:
 
@data = []
@data.push($data_weapons[@actor.weapon_id])
@data.push($data_armors[@actor.armor1_id])
draw_equip_items(@data[0], x, y)
draw_equip_items(@data[1], x, y)
 


Teraz tylko ustawiamy parametry. Lista innych armorów:
 
actor.armor2_id - Głowa
actor.armor3_id - Ciało
actor.armor4_id - Akcesoria
 


9. Animowane obrazki
Hmm... Mój HUD już zapchany, ale można jeszcze coś do niego dodać. Teraz będzie to mała animowana ikonka, wyświetlana tylko wtedy, gdy bohater ma full HP. Do roboty xd! Na początek wchodzimy w Common Eventy (Woot, coś nowego). Nazywamy nowe zdarzenie 'Animacja', czy jak kto sobie chce i dajemy na Parallel Proces. Potem tworzymy nową zmienną, do której dodajemy co kilka Frames wartość "1". Gdy dojdzie do maximum (tyle ile obrazków mamy, w moim przypadku 3) zostawiamy ją w spokoju. U mnie wygląda to tak:

 
Control Variables: [0003: Animation] = 0
Wait: 5 frame(s)
Control Variables: [0003: Animation] = 1
Wait: 5 frame(s)
Control Variables: [0003: Animation] = 2
Wait: 5 frame(s)
Control Variables: [0003: Animation] = 3
Wait: 5 frame(s)
 


Dobra, zrobione. Pamiętajcie, by dać do folderu z obrazkami cztery obrazki. Nazwałem je sobie Animation 00, Animation01, Animation02 i Animation 03.

K, teraz wracamy do skryptów ;s Idziemy do 'Window_Base' i tworzymy znów to nowy warunek, np. 'def draw_animated_icon(x, y)'. Wklejamy tam takie cuś:
 
animicon = RPG::Cache.picture(Nazwa)
self.contents.blt(x, y, itemicon, Rect.new(0, 0, 24, 24)
 


Zamiast 'nazwa' wklejamy tam początek nazwy pliku (w moim przypadku "Animation0" a potem dodajemy do tego zwykła zmienną która zapisuje liczbę animacji. Będzie to wyglądało więc tak:
 
animicon = RPG::Cache.picture("Animation0" + $game_variables[3].to_s)
self.contents.blt(x, y, animicon, Rect.new(0, 0, 24, 24))
 


Pozostaje nam już tylko iść do 'Window_HUD' i wkleić warunek:
 
if @actor.hp / @actor.maxhp == 1
end
 


A w nim naszÄ… animkÄ™, czyli:
 
draw_animated_icon(x, y)
 


10. Ukrywanie HUDa
Przyszedł czas na ostatnią, jedną z najważniejszych rzeczy =) W tym celu posłużymy się Common Eventem (tworzymy nowy, ustawiony na Parralel) a następnie wklepujemy tam takie warunki (ID to nr przełącznika odpowiadającego za ukrywanie HUD'a):

 
Canditional Branch: Switch [ID] == ON
Canditional Branch: Script: Input.trigger?(Input::L)
Control Switches: [ID] = OFF
Branch end
else
Canditional Branch: Script: Input.trigger?(Input::L)
Control Switches: [ID] = ON
Branch end
Brach end
 


L możemy zamienić na dowolny klawisz który ma służyć do chowania i pokazywania HUDa :D Aha, warunek na wciskanie klawisza wstawiłem taki a nie inny, ponieważ w przypadku eventowego przy trzymaniu klawisza HUD chował by się i pokazywał (efekt zapętlania).

Mamy to? Fajnie, teraz tylko okładamy idzemy do naszego starego 'Window_HUD' i wklejamy tam (w def refresh):

 
if $game_switches[ID] == true
self.x = 0
else
self.x = -500
end
 


Ostatecznie wklejamy ten kod w naszym 'Scene_Map' w updatach:
 
if $game_switches[ID] == true
@hud_back.x = 0
else
@hud_back.x = 0
end
 


Mam nadzieję, że wiecie o co czoklet.

11. Zakończenie
Wreszcie koniec, teraz możecie od podstaw zrobić własnego HUDa ;> Oto mój ostateczny kod:

 
#==============================================================================
# ** Scene_Map
#------------------------------------------------------------------------------
#  This class performs map screen processing.
#==============================================================================
class Scene_Map
 #--------------------------------------------------------------------------
 # * Object Initialization
 #     actor : actor
 #--------------------------------------------------------------------------
 def initialize(actor_index = 0, equip_index = 0)
 @actor_index = actor_index
 end
 #--------------------------------------------------------------------------
 # * Main Processing
 #--------------------------------------------------------------------------
 def main
 # Make sprite set
 @actor = $game_party.actors[@actor_index]
 @spriteset = Spriteset_Map.new
 @hud_back = Sprite.new
 @hud_back.bitmap = RPG::Cache.picture("Hud_Background")
 @hud_back.x = 0
 @hud_back.y = 0
 @hud_back.z = 100
 @hud_back.opacity = 255
 @window_hud = Window_HUD.new(@actor)
 @window_hud.opacity = 0
 @window_hud.x = 0
 @window_hud.y = 0
 @window_hud.z = 101
 # Make message window
 @message_window = Window_Message.new
 # Transition run
 Graphics.transition
 # Main loop
 loop do
 # Update game screen
 Graphics.update
 # Update input information
 Input.update
 # Frame update
 update
 # Abort loop if screen is changed
 if $scene != self
 break
 end
 end
 # Prepare for transition
 Graphics.freeze
 # Dispose of sprite set
 @spriteset.dispose
 @hud_back.dispose
 @window_hud.dispose
 # Dispose of message window
 @message_window.dispose
 # If switching to title screen
 if $scene.is_a?(Scene_Title)
 # Fade out screen
 Graphics.transition
 Graphics.freeze
 end
 end
 #--------------------------------------------------------------------------
 # * Frame Update
 #--------------------------------------------------------------------------
 def update
 # Loop
 loop do
 # Update map, interpreter, and player order
 # (this update order is important for when conditions are fulfilled 
 # to run any event, and the player isn't provided the opportunity to
 # move in an instant)
 $game_map.update
 $game_system.map_interpreter.update
 $game_player.update
 # Update system (timer), screen
 $game_system.update
 $game_screen.update
 # Abort loop if player isn't place moving
 unless $game_temp.player_transferring
 break
 end
 # Run place move
 transfer_player
 # Abort loop if transition processing
 if $game_temp.transition_processing
 break
 end
 end
 # Update sprite set
 @spriteset.update
 @hud_back.update
 if $game_switches[2] == true
 @hud_back.x = 0
 else
 @hud_back.x = -500
 end
 @window_hud.refresh
 # Update message window
 @message_window.update
 # If game over
 if $game_temp.gameover
 # Switch to game over screen
 $scene = Scene_Gameover.new
 return
 end
 # If returning to title screen
 if $game_temp.to_title
 # Change to title screen
 $scene = Scene_Title.new
 return
 end
 # If transition processing
 if $game_temp.transition_processing
 # Clear transition processing flag
 $game_temp.transition_processing = false
 # Execute transition
 if $game_temp.transition_name == ""
 Graphics.transition(20)
 else
 Graphics.transition(40, "Graphics/Transitions/" +
 $game_temp.transition_name)
 end
 end
 # If showing message window
 if $game_temp.message_window_showing
 return
 end
 # If encounter list isn't empty, and encounter count is 0
 if $game_player.encounter_count == 0 and $game_map.encounter_list != []
 # If event is running or encounter is not forbidden
 unless $game_system.map_interpreter.running? or
 $game_system.encounter_disabled
 # Confirm troop
 n = rand($game_map.encounter_list.size)
 troop_id = $game_map.encounter_list[n]
 # If troop is valid
 if $data_troops[troop_id] != nil
 # Set battle calling flag
 $game_temp.battle_calling = true
 $game_temp.battle_troop_id = troop_id
 $game_temp.battle_can_escape = true
 $game_temp.battle_can_lose = false
 $game_temp.battle_proc = nil
 end
 end
 end
 # If B button was pressed
 if Input.trigger?(Input::B)
 # If event is running, or menu is not forbidden
 unless $game_system.map_interpreter.running? or
 $game_system.menu_disabled
 # Set menu calling flag or beep flag
 $game_temp.menu_calling = true
 $game_temp.menu_beep = true
 end
 end
 # If debug mode is ON and F9 key was pressed
 if $DEBUG and Input.press?(Input::F9)
 # Set debug calling flag
 $game_temp.debug_calling = true
 end
 # If player is not moving
 unless $game_player.moving?
 # Run calling of each screen
 if $game_temp.battle_calling
 call_battle
 elsif $game_temp.shop_calling
 call_shop
 elsif $game_temp.name_calling
 call_name
 elsif $game_temp.menu_calling
 call_menu
 elsif $game_temp.save_calling
 call_save
 elsif $game_temp.debug_calling
 call_debug
 end
 end
 end
 #--------------------------------------------------------------------------
 # * Battle Call
 #--------------------------------------------------------------------------
 def call_battle
 # Clear battle calling flag
 $game_temp.battle_calling = false
 # Clear menu calling flag
 $game_temp.menu_calling = false
 $game_temp.menu_beep = false
 # Make encounter count
 $game_player.make_encounter_count
 # Memorize map BGM and stop BGM
 $game_temp.map_bgm = $game_system.playing_bgm
 $game_system.bgm_stop
 # Play battle start SE
 $game_system.se_play($data_system.battle_start_se)
 # Play battle BGM
 $game_system.bgm_play($game_system.battle_bgm)
 # Straighten player position
 $game_player.straighten
 # Switch to battle screen
 $scene = Scene_Battle.new
 end
 #--------------------------------------------------------------------------
 # * Shop Call
 #--------------------------------------------------------------------------
 def call_shop
 # Clear shop call flag
 $game_temp.shop_calling = false
 # Straighten player position
 $game_player.straighten
 # Switch to shop screen
 $scene = Scene_Shop.new
 end
 #--------------------------------------------------------------------------
 # * Name Input Call
 #--------------------------------------------------------------------------
 def call_name
 # Clear name input call flag
 $game_temp.name_calling = false
 # Straighten player position
 $game_player.straighten
 # Switch to name input screen
 $scene = Scene_Name.new
 end
 #--------------------------------------------------------------------------
 # * Menu Call
 #--------------------------------------------------------------------------
 def call_menu
 # Clear menu call flag
 $game_temp.menu_calling = false
 # If menu beep flag is set
 if $game_temp.menu_beep
 # Play decision SE
 $game_system.se_play($data_system.decision_se)
 # Clear menu beep flag
 $game_temp.menu_beep = false
 end
 # Straighten player position
 $game_player.straighten
 # Switch to menu screen
 $scene = Scene_Menu.new
 end
 #--------------------------------------------------------------------------
 # * Save Call
 #--------------------------------------------------------------------------
 def call_save
 # Straighten player position
 $game_player.straighten
 # Switch to save screen
 $scene = Scene_Save.new
 end
 #--------------------------------------------------------------------------
 # * Debug Call
 #--------------------------------------------------------------------------
 def call_debug
 # Clear debug call flag
 $game_temp.debug_calling = false
 # Play decision SE
 $game_system.se_play($data_system.decision_se)
 # Straighten player position
 $game_player.straighten
 # Switch to debug screen
 $scene = Scene_Debug.new
 end
 #--------------------------------------------------------------------------
 # * Player Place Move
 #--------------------------------------------------------------------------
 def transfer_player
 # Clear player place move call flag
 $game_temp.player_transferring = false
 # If move destination is different than current map
 if $game_map.map_id != $game_temp.player_new_map_id
 # Set up a new map
 $game_map.setup($game_temp.player_new_map_id)
 end
 # Set up player position
 $game_player.moveto($game_temp.player_new_x, $game_temp.player_new_y)
 # Set player direction
 case $game_temp.player_new_direction
 when 2  # down
 $game_player.turn_down
 when 4  # left
 $game_player.turn_left
 when 6  # right
 $game_player.turn_right
 when 8  # up
 $game_player.turn_up
 end
 # Straighten player position
 $game_player.straighten
 # Update map (run parallel process event)
 $game_map.update
 # Remake sprite set
 @spriteset.dispose
 @spriteset = Spriteset_Map.new
 # If processing transition
 if $game_temp.transition_processing
 # Clear transition processing flag
 $game_temp.transition_processing = false
 # Execute transition
 Graphics.transition(20)
 end
 # Run automatic change for BGM and BGS set on the map
 $game_map.autoplay
 # Frame reset
 Graphics.frame_reset
 # Update input information
 Input.update
 end
end
#==============================================================================
# ** Window_Base
#------------------------------------------------------------------------------
#  This class is for all in-game windows.
#==============================================================================
 
class Window_Base < Window
 
 def red_color
 return Color.new(255, 0, 0, 255)
 end
 def green_color
 return Color.new(0, 255, 0, 255)
 end
 def draw_hp_meter(actor, x, y)
 hpbar = RPG::Cache.picture("HP_Meter")
 cw = hpbar.width  * actor.hp / actor.maxhp
 ch = hpbar.height
 src_rect = Rect.new(0, 0, cw, ch)
 self.contents.blt(x, y, hpbar, src_rect)
 end
 def draw_mp_meter(actor, x, y)
 mpbar = RPG::Cache.picture("MP_Meter")
 cw = mpbar.width  * actor.sp / actor.maxsp
 ch = mpbar.height
 src_rect = Rect.new(0, 0, cw, ch)
 self.contents.blt(x, y, mpbar, src_rect)
 end
 def draw_exp_meter(actor, x, y)
 expbar = RPG::Cache.picture("Exp_Meter")
 cw = expbar.width  * actor.now_exp.to_f / actor.next_exp
 ch = expbar.height
 src_rect = Rect.new(0, 0, cw, ch)
 self.contents.blt(x, y, expbar, src_rect)
 end
 def draw_actor_faces(actor, x, y)
 if actor.hp <= actor.maxhp * 0.25
 face = RPG::Cache.picture("Face04")
 cw = face.width
 ch = face.height
 src_rect = Rect.new(0, 0, cw, ch)
 self.contents.blt(x, y, face, src_rect)
 else
 if actor.hp <= actor.maxhp * 0.5
 face = RPG::Cache.picture("Face03")
 cw = face.width
 ch = face.height
 src_rect = Rect.new(0, 0, cw, ch)
 self.contents.blt(x, y, face, src_rect)
 else
 if actor.hp <= actor.maxhp * 0.75
 face = RPG::Cache.picture("Face02")
 cw = face.width
 ch = face.height
 src_rect = Rect.new(0, 0, cw, ch)
 self.contents.blt(x, y, face, src_rect)
 else
 face = RPG::Cache.picture("Face01")
 cw = face.width
 ch = face.height
 src_rect = Rect.new(0, 0, cw, ch)
 self.contents.blt(x, y, face, src_rect)
 end
 end
end
 def draw_equip_items(item, x, y)
 if item == nil
 return
 end
 itemicon = RPG::Cache.icon(item.icon_name)
 self.contents.blt(x, y, itemicon, Rect.new(0, 0, 24, 24))
 end
end
end
 def draw_animated_icon(x, y)
 animicon = RPG::Cache.picture("Animation0" + $game_variables[3].to_s)
 self.contents.blt(x, y, animicon, Rect.new(0, 0, 24, 24))
 end
 
#==============================================================================
# ** Window_HUD
#------------------------------------------------------------------------------
#  This window displays HUD meters etc.
#==============================================================================
 
class Window_HUD < Window_Base
 #--------------------------------------------------------------------------
 # * Object Initialization
 #--------------------------------------------------------------------------
 def initialize(actor)
 super(0, 0, 370, 160)
 self.contents = Bitmap.new(width - 32, height - 32)
 self.contents.font.name = "Arial"
 self.contents.font.size = 22
 @actor = actor
 refresh
 end
 #--------------------------------------------------------------------------
 # * Refresh
 #--------------------------------------------------------------------------
 def refresh
 self.contents.clear
 draw_hp_meter(@actor, 87, 19)
 draw_mp_meter(@actor, 85, 41)
 draw_exp_meter(@actor, 62, 59)
 draw_actor_faces(@actor, -1, 3)
 self.contents.font.color = normal_color
 self.contents.draw_text(-766, 87, 999, 32, $game_party.gold.to_s, 2)
 self.contents.font.color = system_color
 self.contents.draw_text(235, 87, 999, 32, $data_system.words.gold)
 if $game_variables[1] > 0
 self.contents.font.color = green_color
 else
 if $game_variables[1] < 0
 self.contents.font.color = red_color
 else
 self.contents.font.color = normal_color
 end
 end
 self.contents.draw_text(-453, 67, 999, 32, "Karma: " + $game_variables[2].to_s , 1)
 draw_actor_name(@actor, 92, -3)
 draw_actor_level(@actor, 180, -3)
 draw_actor_state(@actor, 215, -3)
 draw_actor_hp(@actor, 96, 19, width = 230)
 draw_actor_sp(@actor, 96, 42, width = 205)
 self.contents.font.color = normal_color
 attack = @actor.atk
 self.contents.draw_text(84, 87, 999, 32, attack.to_s)
 defend = @actor.pdef + @actor.mdef
 self.contents.draw_text(154, 87, 999, 32, defend.to_s)
 @data = []
 @data.push($data_weapons[@actor.weapon_id])
 @data.push($data_armors[@actor.armor1_id])
 draw_equip_items(@data[0], 56, 92)
 draw_equip_items(@data[1], 126, 92)
 if @actor.hp / @actor.maxhp == 1
 draw_animated_icon(25, 92)
 end
 if $game_switches[2] == true
 self.x = 0
 else
 self.x = -500
 end
 end
end
 


Ostateczny efekt:



Gra demo:
http://www.box.net/shared/os8t32y5i0

Jeżeli macie jakieś pytania, piszcie na PW - w miarę możliwości postaram się pomóc.

Pozdrawiam, Dan.


PS.

Przeniesienie tego poradnika na forum było najbardziej monotonną rzeczą w moim życiu, SI.