Tutorial
28 - Práce s dialogy |
Autor
: Ruprt |
Download = Tento tutorial + ukázka |
Co je to vlastně
dialog ?
Dialog je všem uživatelům PC dobře znám i přesto, že kolikrát vlastně ani
nevědí, že se o dialogové okno právě jedná. Dialogem jsou veškerá okna
aplikací sloužící k tomu, aby uživateli podaly nějakou zprávu nebo
informaci, nebo aby od uživatele daná aplikace získala nějaká data nebo
rozhodnutí. Typickým dialogovým oknem je například okno požadující po
uživateli zadání jména a hesla při spouštění operačního systému Windows,
nebo okénko zobrazené po provedení změny v souboru např. u aplikace MS
Word dotazující se uživatele, zda-li mají být provedené změny uloženy. U
OFP je typickým dialogovým oknem celý Editor misí nebo okénko spouštěné
pomocí příkazu HintC. Když se to vezme kolem a kolem OFP celé je vlastně
založeno na dialogových oknech -> brífingovým zápisníkem počínaje (zkuste
si jen tak pro zajímavost jednou napsat do pole PŘI AKTIVACI jakéhokoli
spínače vysílačky toto : check = CreateDialog "RscDisplayMainMap", a pak
ji spusťte) a zobrazením cinemaborderu při zabití hráče konče (check =
CreateDialog "RscDisplayMissionEnd").
K čemu v OFP potřebujeme dialogy ?
Dialog je velmi silný nástroj pomáhající tvůrci misí vytvářet mise mnohem
zajímavější, větvené, poskytující hráči možnosti volby, jak se bude hra
dále vyvíjet v závislosti na jeho rozhodnutí. Takto je jednoduše možné dát
hráči na výběr tím, že v určité fázi hry se mu objeví dialogové okno,
které mu nabídne např. takovéto možnosti : 1) bude pokračovat dál, sám a s
větším rizikem, 2) Přivolá na pomoc další vojáky, 3) Úkol ležící před ním
vynechá a bude pokračovat dál aniž by daný úkol splnil (a mise by dál
nemohla pokračovat), nebo 4) Misi prostě ukončí, přičemž každá z těchto
voleb bude nějak bodově ohodnocena (toto se jeví aplikovatelné především u
kampaní) a podle dosaženého počtu bodů se bude kampaň dále vyvíjet.
V neposlední řadě může být dialogové okno velmi silným pomocníkem tvůrce
při samotném vývoji a testování mise. Kdokoli si již alespoň jednou zkusil
misi vytvořit, bude se mnou souhlasit, že by práce byla mnohem jednodušší,
kdyby při při testovacím běhu mise věděl, na jakých souřadnicích se např.
určitá jednotka nachází, jak daleko je nejbližší nepřítel, jakou zbraň
právě používá raketometčík, který má odpálit BVPéčko (obvykle má zrovna v
ruce jenom klacek) a kde se (ksakru) zrovna nachází podpůrná skupina. Tak
tohle všechno lze jednoduše zjistit pomocí jednoho předem definovaného
dialogového debug okna, které si spustíte např. nabídkou v akčním menu
nebo prostě vysílačkou.
Popis logiky
definice tříd dialogů - description.ext
Základem jakéhokoli dialogového okna je definice jeho tříd (class), které
OFP vyhledává v souborech v pořadí :
1) description.ext - v adresáři mise
2) description.ext - v adresáři kampaně
3) resource.bin - v vdresáři \Res\Bin nebo\Bin hry OFP
Tyto třídy jsou vlastně stavební kameny jednotlivých prvků (objektů)
dialogu jako jsou např. tlačítka, rámečky, texty, obrázky, comboboxy apod.
(pozn. skládají se z vlastností, metod a funkcí - ale to není důležité
vědět. Pokud však máte nějaké zkušenosti s některými programovacími jazyky
jako jsou např. VisulaBasic, C++, Visual FoxPro, budete mít práci s
pochopením tříd velmi usnadněnou).
Příklad vytvoření
dialogu
Pro základní pochopení tvorby dialogu zde popíšu postup vytvoření a
spuštění velmi oblíbeného okna začátečníků ve všech programovacích
jazycích "Hello, World!", které doplním o combobox a funkci získání a
zobrazení souřadnic jednotlivých vojáků ve skupině hráče. Misi provedeme
ve dvojjazyčném zpracování.
Misi si umístíme na ostrov Desert Island (nejrychleji se načítá). Kamkoli
(na pevninu :o)) si umístíme skupinu vojáků Západu a hráče uděláme
velitelem. Poté si misi uložíme jako HelloWorld.
V adresáři HelloWorld, který nám hra sama vytvořila, najdeme pouze soubor
mission.sqm. V tomto adresáři vytvoříme soubor description.ext
(upozornění: pokaždé, když upravíte soubor desription.ext nebo stringtable.csv,
je nutné misi znovu načíst aby se změny projevily) do kterého vepíšeme
následující kód:
#define
FontMAINCZ |
"SteelfishB64CE" |
// definice typu písma |
#define
FontNOTES |
"AudreysHandI48" |
// definice typu písma |
#define
FontS |
"tahomaB24" |
// definice typu písma |
#define
FontM |
"tahomaB36" |
// definice typu písma |
|
|
|
// Control
types |
|
|
#define CT_STATIC
|
0 |
// statický typ (text, pozadí, obrázek apod.) |
#define CT_BUTTON |
1 |
// Tlačítka |
#define CT_COMBO |
4 |
// ComboBoxy |
|
|
|
// Static
styles |
|
|
#define
ST_LEFT |
0 |
// zarovnání textu doleva |
#define
ST_RIGHT |
1 |
// zarovnání textu doprava |
#define
ST_CENTER |
2 |
// zarovnání textu na střed |
#define
ST_UP |
3 |
// zarovnání textu nahoru |
#define
ST_DOWN |
4 |
// zarovnání textu dolu |
#define ST_VCENTER |
5 |
// zarovnání textu vertikálně - otočí
text o 90° po směru hodinových ručiček |
|
|
|
#define ST_SINGLE |
0 |
// do pole lze zadat pouze 1 řádek textu
- nelze použít \n pro přechod na další řádek |
#define ST_MULTI |
16 |
// do pole lze zapsat více řádek textu -
lze použít \n pro přechod na další řádek |
#define ST_TITLE |
32 |
// titulkový rámeček |
#define ST_PICTURE |
48 |
// obrázek |
#define
ST_3D_BORDER |
80 |
// rámeček je vyplněn a má 3D okraj |
#define ST_NO_BORDER |
96 |
// rámeček bez okraje |
|
|
|
#define
ST_HUD_BACKGROUND |
128 |
// typ podkladu - je průsvitný |
#define
ST_WITH_RECT |
160 |
// rámeček bude mít viditelný okraj |
#define
ST_SHADOW |
256 |
// písmo bude mít stín |
#define
ST_NO_RECT |
512 |
// rámeček nebude mít viditelný okraj |
(toto není tak moc důležité neboť se tím pouze přiřadily lépe
zapamatovatelné názvy typům a stylům objektů - důležitá jsou čísla a ta
jsou již předem nadefinována. Jinými slovy kód by fungoval i bez výše
uvedeného, ale musely by se používat pouze čísla a ta se hůře pamatují)
Nyní si nadefinujeme základní třídy objektů dialogu (base control classes).
Opět to není nutné, ale práce se tím podstatně zjednodušuje, neboť bez
základních tříd by se musely všechny objekty definovat zvlášť, což zabírá
moc času a mnohem víc řádek kódu. Takto vždy každý objekt odvodíme od již
definované základní třídy. Takže :
class RscBackground
{
type=CT_STATIC
idc=-1
style=ST_3D_BORDER
x=0.150000;
y=0.150000;
w=0.700000;
h=0.700000;
text="";
colorBackground[]={1,1,1,1};
colorText[]={0,0,0,0};
font="tahomaB24";
sizeEx=0
};
class RscTitle
{
type=CT_STATIC
idc=-1
style=ST_TITLE + ST_CENTER;
x=0.150000;
y=0.164000;
w=0.700000;
h=0.060000;
text="";
colorBackground[]={1,1,0,1};
colorText[]={1,1,1,1};
font="tahomaB36";
sizeEx="1.0714 * 0.03";
};
class RscGroupBox
{
type=CT_STATIC
idc=-1
style=ST_NO_BORDER
text="";
colorBackground[]={0,0,0,0};
colorText[]={0,0,0,0};
font="tahomaB24";
sizeEx=0.020000;
};
class RscText
{
type=CT_STATIC
idc=-1
style=ST_LEFT + ST_MULTI + ST_NO_RECT;
lineSpacing=1.000000;
h=0.040000;
colorBackground[]={0,0,0,0};
colorText[]={0.080000,0.080000,0.120000,0.750000};
font="tahomaB24";
sizeEx=0.020000;
};
class RscPicture
{
type=CT_STATIC
idc=-1
style=ST_PICTURE
colorBackground[]={0,0,0,0};
colorText[]={1,1,1,1};
font="tahomaB24";
sizeEx=0
};
class RscButton
{
type=CT_BUTTON
style=ST_CENTER
w=0.110000;
h=0.050000;
colorText[]={0.080000,0.080000,0.120000,1};
font="tahomaB24";
sizeEx=0.020000;
default=0
soundPush[]={"ui\ui_ok",0.200000,1};
soundClick[]={"",0.200000,1};
soundEscape[]={"ui\ui_cc",0.200000,1};
};
class RscCombo
{
type=CT_COMBO
style=ST_LEFT
h=0.040000;
wholeHeight=0.250000;
colorSelect[]={0.350000,0.380000,0.360000,1};
colorText[]={0.080000,0.080000,0.120000,0.750000};
colorBackground[]={0.350000,0.380000,0.360000,0.750000};
font="tahomaB24";
sizeEx=0.020000;
};
Vysvětlivky :
idc - index třídy objektu (vlastně takový číselný název), zde většinou -1,
neboť se jedná o base class
style - chování objektu (zarovnání apod.)
colorBackground - barva pozadí RGBA (1 = 255 = FF (HEX) a 0 = 0 = 00(HEX))
- A je průsvitnost
colorText - barva písma RGBA (1 = 255 = FF (HEX) a 0 = 0 = 00(HEX)) - A je
průsvitnost
colorSelect - barva podkladu myší vybrané položky ComboBoxu - RGBA
font - typ písma (lze použít i ony definice např. FontMAINCZ, ale tady je
lepší používat název písma)
lineSpacing - velikost mezery mezi řádky při stylu ST_MULTI
sizeEx - velikost písma
size - velikost písma
default - standardní/hlavní tlačítko (když se stiskne ENTER stiskne se
tlačítko s default=1)
soundPush/Escape/Click - zvuk, který se přehraje při stisku tlačítka OK/Cancel/ostatní
x - vzdálenost levého horního rohu objektu od levého okraje obrazovky,
0-úplně vlevo, 1-úplně vpravo
y - vzdálenost levého horního rohu objektu od horního okraje obrazovky,
0-úplně nahoře, 1-úplně dole
w - šířka objektu
h - výška objektu
wholeHeight - výška rozbaleného ComboBoxu
No a teď když máme definovány základní třídy objektů, můžeme se vrhnout na
samotnou tvorbu dialogového okna :
class RscHelloWorld
{
idd=1000
movingEnable=1
class Controls
{
// ----------------------------------------
// --------- Dialog Appearence ------------
// ----------------------------------------
class Background: RscBackground //vytvoří základní rámeček celého dialogu
{
x=0.250000;
y=0.250000;
w=0.500000;
h=0.500000;
};
class SubBackground: RscGroupBox // vytvoří druhý rámeček uvnitř základníh,
čímž se zvýrazní
{
x=0.270000;
y=0.270000;
w=0.460000;
h=0.460000;
};
// ----------------------------------------
// -------------- TextBoxes ---------------
// ----------------------------------------
class Title: RscTitle //Rámeček titulku dialogu s nadpisem "Hello, World
!"
{
idc=1001
x=0.350000;
y=0.270000;
w=0.300000;
h=0.060000;
text="$STR_DLG_Title";
};
class TextBox1: RscTitle //Rámeček pro výpis textů
{
idc=1002
x=0.290000;
y=0.350000;
w=0.420000;
h=0.180000;
text="";
sizeEx="0.022";
};
class TextBox2: RscText //Textové pole pro výpis textů uvnitř 1.rámečku
{
idc=1003
x=0.300000;
y=0.360000;
w=0.400000;
h=0.160000;
colorBackground[]={0,0,0,0};
colorText[]={1,1,0,1};
text="$STR_TextBox2_1";
sizeEx="0.024";
};
// ----------------------------------------
// -------------- Buttons -----------------
// ----------------------------------------
class GetData: RscButton // Tlačítko "Získej údaje"
{
idc=2001;
x=0.580000;
y=0.550000;
text=$STR_BTN_GetData;
action="Player Exec ""onClickGetData.sqs""";
};
class ButtonCancel: RscButton //Tlačítko "Cancel"
{
idc=2002
x=0.385000;
y=0.640000;
w=0.130000;
h=0.050000;
text="$STR_BTN_CANCEL";
action="CloseDialog 1000; sd = Nil";
};
// ----------------------------------------
// -------------- ComboBoxes --------------
// ----------------------------------------
class CBSoldiers: RscCombo //Vytvoří ComboBox
{
idc=3001
style = ST_TITLE + ST_CENTER;
x=0.315000;
y=0.565000;
w=0.250000;
h=0.025000;
colorSelect[]={1,1,1,1};
colorText[]={0,0,0,0.750000};
rowHeight=0.025000;
};
// ----------------------------------------
// --------------- Pictures ---------------
// ----------------------------------------
class RuprtLogo: RscPicture //Umístí do dialogu obrazek
{
idc=4001
text="ruprtslogo.jpg";
x=0.581900;
y=0.610000;
w=0.107500;
h=0.115000;
colorText[]={1,1,1,1};
};
};
};
A toť vše.
Údaj Action u tlačítek je vlastně metoda, která se spustí při stisku
daného tlačítka a provede veškeré příkazy uvedené v uvozovkách za
rovnítkem. Příkazů může být několik, musí však být odděleny středníkem.
Nastavení této metody lze za běhu programu změnit příkazem ButtonSetAction.
Teď je nutné si vytvořit soubor stringtable.csv, aby mohly být získávány
texty pro všechny objekty dialogu a do tohoto souboru zapsat následující :
Language, Czech, English, Comment
STR_DLG_Title, "Hello, World !", "Hello, World !",
STR_TextBox2_1, "\nVyberte z ComboBoxu níže vojáka, jehož\n\nsouřadnice se
vám zobrazí v tomto rámečku.", "\nChoose a soldier from ComboBox bellow\n\nto
display his coordinates.",
STR_TextBox2_2, "Voják ""%1"" je na souřadnicích :\nOsa X = %2\nOsa Y =
%3\nOsa Z = %4\núhel = %5°\nnázev jednotky = %6", "Soldier ""%1"" is on
coordinates :\nX axis = %2\nY axis = %3\nZ axis = %4\nangle = %5°\nname of
unit = %6",
STR_BTN_GetData, Získej údaje, Get Data,
STR_BTN_CANCEL, Ukončit, Cancel,
STR_CB_Txt_0, Vyberte vojáka, Choose a soldier,
STR_AddAction_0, Spustit dialog, Start dialog,
STR_Hint, "Nyní máte otevřené Akční menu.\nStiskem první
položky\n""Spustit dialog""\notevřete dialog ""Hello, World !""", "The
Action menu is opened now.\nPress first item to open\n""Hello, World !""
dialog.",
STR_Error, "Nevybral jste žádného vojáka !!!", "You havn't selected any
soldier !!!",
Teď již máme hotovo vše abychom mohli dialog spustit příkazem check =
CreateDialog "RscHelloWorld". Ale....... Tento dialog se nám sice otevře,
ale nebude zcela funkční, neboť je ještě zapotřebí vytvořit pár
jednoduchých scriptů, pomocí kterých budeme chování dialogu ovládat.
První script přidá položku do Akčního menu, kterou se aktivuje dialog a
napoví hráči co že to má vlastně udělat. Je to všem velmi dobře známý init.sqs,
který obsahuje pouze :
player AddAction [Localize"STR_AddAction_0", "activatedialog.sqs"]
~0.5
Hint Localize"STR_Hint"
Exit
Druhý script je o něco složitější neboť má za úkol aktivovat dialog a
iniciovat jeho impicitní nastavení. Je to script activatedialog.sqs, který
obsahuje tento kód :
_check = CreateDialog "RscHelloWorld"
; ******* Zde lze vytvořit chybovou rutinu pro případ, že by se dialog
neotevřel *****
;? _check: GoTo "OK"
;TitleText["Něco je špatně dialog nelze otevřít.\nZkuste restartovat misi,
nebo tak něco", "Plain"]
;ForceEnd
;Exit
;#OK
;******************************************************************
; ------------- Nastaví hodnoty položek ComboBoxu s IDC 3001
----------------------
_countgrp = Count (Units Group Player)
_x = 0
lbAdd [3001, Localize"STR_CB_Txt_0"]
#Loop1
_x = _x + 1
_index3001 = _index3001 + [lbAdd [3001, Name (Units Group Player Select
_x)]]
? _x < (_countgrp - 1): GoTo "Loop1"
; ------------ Nastaví implicitní položku ComboBoxu
---------------------------
lbSetCurSel [3001, 0]
Exit
No a na závěr vytvoříme poslední, bohužel taky nejsložitější script, který
má za úkol zjistit hráčem požadovaná data a vypsat je do textového pole
dialogu. Tento script si pojmenujeme třeba onClickGetData.sqs, a to proto,
že se spustí při kliknutí na tlačítko "Získej data". Soubor obsahuje
následující kód :
; ------- Získat hodnoty z vybrané položky
CombnoBoxu -------
_cbindex = lbCurSel 3001
; ******************** Chybová rutina ***************************
; ***** v případě, že hráč nevybere žádnou položku Combo boxu *****
? _cbindex == 0: Hint Localize"STR_Error"; Exit
; ******************************************************************
; ---------- Získat údaje o pozici vybraného vojáka ---------
_soldiername = lbText [3001, _cbindex]
_soldierpos = GetPos (Units Group Player Select _cbindex)
_soldierposx = _soldierpos Select 0
_soldierposy = _soldierpos Select 1
_soldierposz = _soldierpos Select 2
Goto "GetAngle"
#Return
_unitname = Units Group Player Select _cbindex
; ------------- Vypsat získané údaje do TextBoxu ------------
ctrlSetText [1003, Format[Localize"STR_TextBox2_2", _soldiername, _soldierposx,
_soldierposy, _soldierposz, _angle, _unitname]]
Exit
; --------- Subprocedura pro získání úhlu mezi vojákem a hráčem
-----------
; --------- Např. pro použití v příkazu SetDir, příkaz DoWatch
------------
; --------- totiž ne vždy spolehlivě pracuje
------------------------------
#GetAngle
_playerpos = GetPos player
_playerposx = _playerpos Select 0
_playerposy = _playerpos Select 1
_distancex = _soldierposx - _playerposx
_distancey = _soldierposy - _playerposy
_absgoniometry = Abs(_distancex) + Abs(_distancey)
_asinderived = _distancex / _absgoniometry
_acosderived = _distancey / _absgoniometry
? _asinderived >= 0 AND _acosderived >= 0: _angle = Asin(_asinderived)
? _asinderived >= 0 AND _acosderived < 0: _angle = 180 - Asin(_asinderived)
? _asinderived < 0 AND _acosderived < 0: _angle = 180 + Abs(Asin(_asinderived))
? _asinderived < 0 AND _acosderived >= 0: _angle = 360 - Abs(Asin(_asinderived))
Goto "Return"
No a teď je to již opravdu vše. Pokud jste postupovali přesně podle tohoto
návodu, měl by se Vám dialog "Hello, World !" bezproblémů otevřít a
správně pracovat. Pro jistotu si však vše co je výše uvedeno můžete
stáhnout
odstud.
|