on-game | vs-web | vs-online | AAA WEBY
Hlavni panel

MFR 4 – A na závěr trochu designu…


MFR 4 – A na závěr trochu designu…

Dnes píšu poslední díl miniseriálu věnovanému programování PHP. Během tohoto seriálu jsme vytvořili jednoduchou hru, ukázali si na ní některá úskalí, která na vývojáře čekají. Ukázali jsme si některá jejich řešení, ovšem nutno podotknout, že ne jediná. Dnes na nás čeká úskalí asi největší. A to grafika…

Ať si to chceme nebo nechceme přiznat, ve světě prodává grafika, a vidíme to všude kolem nás. V obchodech se prodávají balení plná obrázků, reklamy v televizích hýří barvami, celý kinematografický průmysle není postaven prakticky na ničem jiném, auta mají různý tvar a barvu, nikoho vlastně ani nezajímá, že dvě auta mají stejné výkonové parametry, prodá se to se zajímavějšími tvary a šílenější barvou. Dvě krabice od mléka obsahující totéž mléko a prodá se ta pěkně vybarvená. Bohužel, obdobné zákonitosti platí i ve světě webových aplikací.

Hned na začátku se přiznávám, že nejsem přítelem přemrštěné grafiky. Asi jako hodně ostatních i já bych dokázal postavit grafiku na obrázcích a pěkném podkladovém obrázku. Ale ti, kteří ke svým webovým prezentacím přistupují tímto směrem, dle mého názoru nepochopili jeden z aspektů webu. Tedy ne přesně webových aplikací ale jejich spotřebitelů. Klienti přistupující na vaše stránky mají rozličné druhy připojení o rozličných rychlostech, a pokud vy patříte mezi ty šťastlivce, kteří sedí přímo na páteřní síti, neznamená to, že nějaký z vašich uživatelů nesedí na 56k modemu. Rychlost připojení je přitom jedním z kritických parametrů pro každou webovou aplikaci. Platí totiž jeden zákon a to ten, že většina uživatelů je ochotna čekat na stránku přibližně 10 vteřin. Slabší polovina 20 vteřin a pouhá čtvrtina až 30 vteřin. Jen ve výjimečných případech uživatelé čekají déle. Pravidlem tedy může být že naše stránka se musí dotáhnout za 10 vteřin.
Pravidlo se zdá být jednoduché, ovšem jeho splnění je už horší. Pokud vyvíjíte na lokálním serveru, pak je vše v pořádku. I na rychlém připojení je vše jak má být. Ovšem stačí omezit šířku použitelného pásma na 56k a leckterá stránka začne mít problém se splněním těchto kritérií. A důvodem paradoxně ve většině případů bývají obrázky. Naštěstí existuje alternativní možnost jak uvést web do koukatelného stavu a to použití tzv. CSS stylů.

CSS styly popisují vlastnosti jednotlivých skupin tagů. Nejčastěji nastavované vlastnosti jsou barva, řez a styl písma, barva podkladu, barva a tloušťka rámečků, neposlední řadě vnější a vnitřní okraje. Skupiny tagů jsou pak specifikovány pomocí tzv. selektorů. Selektor se skládá z hodnot názvu tagu, id nebo class atributu tagu. Jejich seřazení pak určuje vzájemné souvislosti. Zdá se to složité? Pro zjednodušení a upřesnění uveďme několik příkladů.

body { color: black; } /* text uvnitř tagu body má mít černou barvu */
H1 { background-color: gray; } /* tagy H1 mají tmavě šedý podklad */
H1.title { color: white; } /* tagy H1 s attributem class=”title” mají bilý text */
H1#pgTitle { font-weight: bold; } /* tagy H1 s atributem id=“pgTitle“ mají tlustépísmo*/
div.block div.title a { text-decoration: none; }
/* hyperlinky v divu s atributem class=”title” uvnitř divu s atributem class=“block“ nejsou podtržené */
div.page #copyright a { color: black; }
/* hyperlinky v tagu s id=”copyright” ať už je to jakýkoliv tag a je zároveň uvnitř divu s class=“page“ mají černý text */

Věřím že nyní je už mnohem jasnější jak se selektory vytváří. Problematiku kolizí skupin specifikovaných různými selektory (tedy situace, kdy existují dvě různé skupiny popisující stejnou vlastnost) a jejich řešení (tedy rozhodnutí jak skutečně bude výstup vypadat) si troufám zařadit mimo rozsah tohoto článku, nicméně pravidlo říká, že vítězí ten selektor, který specifikuje svou skupinu jednoznačnějším způsobem. Tedy nejpřesnějším je selektor přes íd tagu, protože id by mělo být jen jedno, volnějším jsou selektory pomocí tagu a třídy a ještě volnějším pomocí trídy anebo tagu. Osobně doporučuji provést řadu experimentů, protože zkušenost mi říká, že právě tyto experimenty ukáží správné chování.

Často se pro přehlednost a snadnou údržbu ukládají styly do zvláštního souboru, který je pak linkován do stránky pomocí tagu <meta> v záhlaví stránky.

Podíváme-li se na dnešní kód naší hry, zjistíme, že nedošlo k žádným převratným změnám, pouze přibylo několik řádek vypisující tagy obalují kusy html, které naše aplikace vypisovala již v předchozí verzi. Tyto tagy, ačkoliv jsou funkčně absolutně zbytečné, jsou důležité pro grafiku stránky, obalují totiž jiné kusy HTML kódu a umožňují je správně neadresovat a nastylovat. Z hlediska funkce aplikace se ovšem nic nezměnilo. Kromě těchto úprav jsme dále přidali atributy class do některých již existujících tagů tak, abychom je opět dokázali neadresovat ze stylů. Dále je vidět že používáme oddělení stylů od HTML kódu.

V samotných stylech jsme nadefinovali šířku stránky aplikace na 1024 pixelů, zde se dopouštíme lehké chyby, protože uživatelé používající rozlišení 1024x768 neuvidí naší stránku celou, ale s posuvníkem, je to způsobeno tím, že 1024 rozlišení obrazovky je i při maximalizovaném okně zkráceno o posuvník a několik režijních pixelů okna samotného. Efektivně bychom měli použít tedy hodnotu pohybující se kolem 990 pixelů. Na tuto skutečnost se zpočátku často zapomíná. Dále vidíme nastavování různých zarovnání uvnitř tagů a vnější okraje tagů, toto je technika kterou lze (stejně jako mnoho jiných ) použít k centrování bloků uvnitř jiných HTML bloků. Poslední zajímavou technikou je použití neúplných okrajů u tagů H1 a H2, dodávající našemu designu lehce odlišný nádech.

Celkově byl design postaven tak, aby co nejméně zatěžoval komunikaci mezi serverem a klientem.

Dobrým zvykem bývá podívat se na tu samou stránku v různých prohlížečích a někdy i v různých verzích prohlížeče. Já měl k dispozici starší verzi Mozilla FireFox a IE7.

V této chvíli tedy máme k dispozici jednoduše nedesignovanou velmi jednoduchou hru s nízkými nároky na na přenos a na klienta. Z druhé strany je nutno přiznat, že při návrhu designu bylo vzato v úvahu pouze funkční hledisko a z hlediska prodeje zde máme přesně tu krabici mléka, která se bude hůř prodávat, protože nemá nijak úchvatný obal.

V této fázi je možné předat aplikaci grafikům, ať pustí uzdu své zvrhlé fantazii a rozsápou naší aplikaci na kousky ve snaze navléct ji do jistě zajímavějšího kabátku.

V posledních letech se objevuje trend oddělit design aplikace od její funkční části, v tomto směru pak vznikají různé šablonovaní enginy, na platformě .NET je dokonce toto oddělení nativní součástí vývoje a ještě dále jde MPF, kde existuje vývojové studio pro vývojáře a designovací nástroj pro grafiky, přičemž oba znepřátelené tábory mohou v klidu pracovat na jednom a tom samém projektu ušetřeni následků práce druhé strany.

Myslím, že zde je vhodné místo naši poslední kapitolu a zároveň miniseriál ukončit. Cílem našeho seriálu bylo v rychlosti a velmi povrchně představit jak vzniká vývoj extrémně jednoduché minihry. Prošli jsme si sestavením myšlenky hry, návrhem jejího chování a postupné implementace až po design. Mnozí jistě budou namítat, že design aplikace startuje mnohem dřív, než jsme předvedli a do určité míry budou mít pravdu. Mnoho témat, jsme jen nastínili, protože věnovat se třeba jen jedinému tématu do větší hloubky bylo nad rámec tohoto seriálu, oproti tomu na knižním trhu je dostatek publikací, ve kterých lze najít více informací. A pro nás línější existuje největší knihovna hned za hranicemi vašich počítačů.

Doufám, že se vám náš seriál líbil a třeba se setkáme u dalšího. Jménem celého redaktorského teamu vám děkuji za pozornost a přeji hodně štěstí, nervů a trpělivosti při implementaci vašich vlastních projektů.


Stahovat tyto soubory můžete zde

Zdrojové kódy:
style.css
body{
color: #343434;
font-family: Verdana, Arial;
font-size: 12px;
text-align: center;
}
.page{
width: 1024px;
_height: 768px;
min-height: 768px;
}
.pagecontent{
text-align: justify;
width:1024;
background-color: #EAEAEA;
}


H1{
font-size: 20px;
padding: 5px 20px 5px 20px;
border-left: #343434 solid 2px;
border-bottom: #343434 solid 2px;
background-color: #D0D0D0;
}

H2{
font-size: 16px;
padding: 2px 20px 2px 20px;
border-bottom: #A0A0A0 double 3px;
width: 60%;
white-space: nowrap;
}

a, a:visited
{
color: #666666;
font-weight: bold;
text-decoration: underline;
white-space: nowrap;
}
a:hover
{
color: #000000;
background-color: #D0D0D0;
text-decoration: none;
}
div.field
{
text-align: center;
white-space: nowrap;
width: 200px;
margin: 0px 390px 10px 390px;
}
div.controls{
text-align: center;
clear: both;
}
div.content{
text-align: center;
padding: 20px;
}
div.innercontent{
text-align: center;
}
table.score{
border: 1px solid #666666;
width: 300px;
margin: 10px 340px 10px 340px;


}
table.score tr td{
padding: 1px 5px;

}
table td.name {
text-align: left;
}
table td.value {
text-align: right;
}


game.php
<?php
// konfigurace
$dbserver = "127.0.0.1";
$dbuser = "mfr";
$dbpwd = "mfr";
$dbname = "mfr";
$tableNameResults = "results";
/*
CREATE DATABASE `mfr`

CREATE TABLE `results` (
`id` int(11) NOT NULL auto_increment,
`date` datetime NOT NULL default '0000-00-00 00:00:00',
`username` varchar(100) NOT NULL default '',
`score` bigint(20) NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `IDX_Score` (`score`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

*/

session_start();

// zpracovani vstupnich parametru
$action = $_POST["action"];
if (!isset($action)){
$action = "G_".$_GET["action"];
}

// *** centralni zpracovani ***
if ( ( GetSession("username","") == "" ) && ($action != "LOGIN" ) )
{
$action = "LOGINFORM";
} else {
$action = StrToUpper( $action );
}
switch( $action ){
case "STEP" : {
OnStep( $_POST["fieldcode"] );
break;
}
case "LOGINFORM" : {
OnLoginForm();
break;
}
case "LOGIN" : {
OnLogin( $_POST["username"] );
break;
}
case "G_SCORE" :
case "SCORE" : {
OnScoreTable();
break;
}
default :{
OnDefault( GetEmptyRow() );
break;
}
}
// *** /centralni zpracovani ***

// *** podpora pro praci s DB ***
function OpenDB()
{
global $dbserver, $dbuser, $dbpwd, $dbname;
$db = mysql_connect($dbserver, $dbuser, $dbpwd );
if (!$db )
{
echo MySQL_Error();
return false;
}
else
{
@mysql_select_db($dbname);
echo MySQL_Error();
return $db;
}
}

function CloseDB( $db )
{
mysql_close($db);
}

function ExecuteSQL( $db, $sql )
{
@MySQL_Query( $sql, $db );
return @mysql_affected_rows($db);
}

function OpenSQL( $db, $sql, $callback )
{
$Q = MySQL_Query( $sql, $db );
if ($Q)
{
while( $row = MySQL_fetch_array( $Q ) )
{
$callback( $row );
}
MySQL_Free_result( $Q );
}
}

function SQLEncode( $str )
{
return mysql_escape_string( $str );
}

function SQLNumber( $str )
{
return 0+$str+0; // :o)
}
// *** /podpora pro praci s DB ***

// *** podpurne funkce ***
function SetSession( $varname, $value ){
$_SESSION[$varname] = $value;
}

function GetSession( $varname, $default ){
if (isset($_SESSION[$varname])) {
return $_SESSION[$varname];
} else {
return $default;
}
}

function WriteHeader(){
echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>Mine Field Runner 1.0</title>
<meta name="Copyright" content="genesis, genesis@cardici.com" />
<link REL="stylesheet" TYPE="text/css" HREF="style.css?x1"/>
</head>
<body>
<div class="page"><div class="pagecontent">
<H1>Mine Field Runner</H1>
';
}

function WriteFooter(){
echo ' </div></div></body>
</html>';
}

function GetEmptyRow(){
return array( $false, $false, $false, $false, $false, $false, $false, $false, $false, $false );
}

function LogScore( $username, $score )
{
global $dbname, $tableNameResults;
if ( $score < 1 ) return;
$score = SQLNumber( $score );
$username = SQLEncode( $username );

$sql = "INSERT INTO $dbname.$tableNameResults (`date`, `username`, `score`) VALUES ( NOW(), '$username', $score )";
$db = OpenDB();
if ( $db != false ){
ExecuteSQL( $db, $sql );
CloseDB( $db );
}
}

function WriteScoreRow( $row )
{
echo "<tr><td class='name'>".$row["username"]."</td><td class='value'>".$row["score"]."</td></tr>";
}

function WriteScoreTable()
{
global $dbname, $tableNameResults;

echo "<table class='score'>";
$sql = "SELECT username, score FROM $dbname.$tableNameResults ORDER BY `score` DESC limit 10";
$db = OpenDB();
if ( $db != false )
{
OpenSQL( $db, $sql, WriteScoreRow );
CloseDB( $db );
}
else
{
echo "<tr><td>Nebyly nalezeny žádné záznamy</td></tr>";
}
echo "</table>";
}


// *** /podpurne funkce ***


// *** sekce jednotlivych akci ***
function OnLoginForm()
{
WriteHeader();
echo "<H2>Přihlášení</H2>";
echo "<div class=\"content\"><div class=\"innercontent\">";
echo "<form action=\"?\" method=\"POST\">";
echo "Uživatelské jméno: ";
echo "<input type=\"text\" name=\"username\"/>";
echo "<input type=\"submit\" value=\"Přihlásit\"/>";
echo "<input type=\"hidden\" name=\"action\" value=\"LOGIN\"/>";
echo "</form>";
echo "<a href=\"?action=score\">Výsledková tabulka</a> ";
echo "</div></div>";
WriteFooter();
}
function OnLogin( $username )
{
if ( $username != "" )
{
SetSession('username', $username );
SetSession('score', 0);
OnDefault( GetEmptyRow() );
}
else
{
OnLoginForm();
}
}

function OnDefault( $originalRow ){
WriteHeader();
// score
$score = 0+GetSession('score', 0);
echo "<H2>Score $score bodu</H2>";

echo "<div class=\"content\"><div class=\"innercontent\">";
// herni pole
echo "<div class=\"field\" style=\"width:" . (20*10 ). "px\">";
echo "<form action=\"?\" method=\"POST\">";
// vykresli 10 buttonu
for ( $f = 1; $f < 11; $f++ ){
echo "<input type=\"submit\" name=\"fieldcode\" value=\"$f\" style=\"width:20px;\"/>";
}
// vykresli puvodni radku
echo "<div>";
if ((!isset( $originalRow ) || (!is_array( $originalRow ) ))){
$originalRow = GetEmptyRow();
}
for ( $f = 1; $f < 11; $f++ ){
$color = "#55FF55";
if( $originalRow[$f]==true) $color = "#FF0000";
echo "<div style=\"float:left;width:20px;height:20px;background-color:$color;\"> </div>";
}

echo "<input type=\"hidden\" name=\"action\" value=\"STEP\"/>";
echo "</form>";
echo "</div></div>";

echo "<div class=\"controls\">";
echo "<form action=\"?\" method=\"POST\">";
echo "<input type=\"hidden\" name=\"action\" value=\"LOGIN\"/>";
echo "<input type=\"submit\" value=\"Odhlásit\"/>";
echo "</form><br/>";

echo "</div></div>";

WriteFooter();
}

function OnStep( $code ){
$score = 0+GetSession('score',0);
$code = 0+$code;

// nasazime miny


$minecount = round($score / 5);
srand( time() );
$row = GetEmptyRow();
if ( $minecount < 10 ) {
for( $f = 0; $f < $minecount; $f++ )
{
$index = rand(1,10);
$row[$index] = true;
}
} else {
for( $f = 1; $f <= 10; $f++ )
{
$row[$f] = true;
}
}
if ( $row[$code] ){
// mina
WriteHeader();

// score
echo "<H2>Score $score bodu</H2>";

// herni pole nahradime hlaskou, ze leti vzduchem
echo "<div class=\"content\"><div class=\"innercontent\">";
echo "<p>Lituji, ale právě jste šlápl na minu. Snad příště.</p>";
echo "<a href=\"?\">Hrát znovu</a> ";
echo "<a href=\"?action=score\">Výsledková tabulka</a> ";
echo "</div></div>";
WriteFooter();

// zapiseme score do db
$username = GetSession('username', $username );
LogScore( $username, $score );

SetSession('score',0);
}
else
{
// volne pole
SetSession('score', $score + 1 );
OnDefault( $row );
}
}

function OnScoreTable()
{
WriteHeader();
echo "<div class=\"content\"><div class=\"innercontent\">";
echo "<div><a href='?'>Hrát znovu</a></div>";
WriteScoreTable();
echo "</div></div>";
WriteFooter();
}


// *** /sekce jednotlivych akci ***
?>




Známka 1,00 | Přečteno 2886 x | Smaž | 21/07/2007 | Autor: genesis |
Komentařů: 0 | Vstup do diskuse |
Sdílet

Abyste mohl hodnotit tento článek, tak musíte být přihlášen. Nemáte ještě zřízený účet ? Registrujte se zde.
Všechna práva vyhrazena, © 2006 Team on-game

Optimalizováno pro 1024x768 a prohlížeče IE, Mozilla Firefox a Operu

ip:38.107.179.216, čas:0.00266909599304, sql:10