Paletele de imagini au fost folosite de la început în grafica computerizată și, chiar dacă acestea sunt rar întâlnite în jocurile moderne, o anumită clasă de probleme ar fi practic imposibilă fără ele. În acest tutorial vom construi un designer de caractere MMO pentru un joc 2D folosind palete, multi-texturi și shadere.
Notă: Deși acest tutorial este scris folosind AS3 și Flash, ar trebui să puteți utiliza aceleași tehnici și concepte în aproape orice mediu de dezvoltare a jocului.
Modelul de caractere folosit pentru demo a fost realizat de Dennis Ricardo Díaz Samaniego și poate fi găsit aici: Leviathan. Platforma a fost realizată de Daren Davoux și poate fi găsită aici: Leviathan Rigged.
Faceți clic pe o secțiune a modelului de caractere, apoi faceți clic oriunde în selectorul de culori. Nu puteți vedea SWF de mai sus? Consultați în schimb acest videoclip:
Fișierele sursă completă sunt disponibile în descărcarea sursei.
Implementarea demo-ului utilizează AS3 și Flash, cu biblioteca Starling pentru redarea accelerată a GPU și cu biblioteca Feathers pentru UI. Scena noastră inițială conține o imagine a personajului și a selectorului de culori, care va fi utilizată pentru a schimba culorile paletei de caractere.
Reprezentarea culorilor folosind palete a fost obișnuită în jocurile timpurii din cauza cerințelor hardware. Această tehnică ar mapa o valoare dintr-o imagine la o altă valoare într-o paletă. De obicei, imaginea ar avea un set mai mic de valori, pentru a salva memoria și a fi folosit pentru a căuta valoarea reală din paletă.
Sprite-ul de mai jos nu este alcătuit din roșu, portocaliu și maro - este alcătuit din 1, 2 și 3. Când Mario preia o floare de foc, paleta se schimbă astfel încât 1 reprezintă alb și 3 reprezintă roșu. Mario arată diferit, dar sprite nu se schimbă de fapt.
Deoarece utilizarea reprezentării imaginilor pe 24 de biți este adevărată de mai bine de zece ani, s-ar putea să te întrebi cum ar putea fi utilă această tehnică astăzi. Primul caz evident de utilizare este acela de a face jocuri retro, însă este rareori necesar să utilizați palete acolo. Un artist poate să se limiteze la un anumit set de culori, dar să folosească în continuare spectrul complet de 24 de biți, deoarece aceasta este o modalitate ușoară de a manipula texturile în hardware-ul modern.
O tehnică utilizată în zilele paletelor a fost schimbarea paletei: aceasta a fost utilizată pentru a schimba imaginea existentă într-o schemă de culori diferită. Și mulți dintre voi vă amintiți cum au fost colorate diferitele niveluri ale monștrilor din vechile RPG-uri; acest lucru a salvat timp pentru artiști și a folosit mai puțină memorie, permițând o mai mare varietate de modele de monstri. (Desigur, acest lucru ar putea face jocul să pară repetitiv, totuși.)
Acest lucru ne aduce la obiectivul acestui tutorial, care vă permite să aveți mai multă varietate în ceea ce privește jocul. Vom simula un creator de caractere MMO, cu culori personalizabile pentru piese de caractere, folosind o paleta.
Efectuarea de imagini palete este puțin mai dificilă decât realizarea de imagini obișnuite. Există câteva limitări și detalii tehnice pe care să le urmăriți.
În primul rând: paletele au un domeniu de aplicare limitat; în acest tutorial, fiecare sprite de caractere are 8 biți - adică 256 valori posibile - dintre care valoarea "255" va fi utilizată pentru a desemna "transparent".
Pentru arta pixelului aceasta nu este o problemă prea mare, deoarece se bazează, de obicei, pe o paletă limitată aleasă de un artist. În timp ce desenați, culorile paletei sunt definite și aplicate imaginii.
În exemplu, folosesc un model 3D; Am făcut acest lucru în straturi separate, apoi am mapat fiecare strat la spectrul paletei. Acest lucru se poate face manual prin editarea nivelelor imaginii pentru a se potrivi în partea dorită a paletei. Acele părți ale paletei pe care o vom reprezenta ca gradienți mici, pentru a permite cartografierea de la umbre la cele mai importante.
Acest lucru va reduce adâncimea totală a imaginii unei imagini. Dacă faci un joc de cartonare (cel umbrit), acest lucru poate fi bine, dar s-ar putea să îi lipsești pentru un stil mai realist. Putem remedia oarecum acest lucru sacrificând memoria în timp ce mărim adâncimea unei imagini la 16 biți - paleta ar putea să rămână la aceeași dimensiune și am fi folosit interpolarea pentru a ne oferi mai multă varietate.
Implementarea noastră folosește o singură textură de canal pe 8 biți pentru caracter. Pentru simplificare, vom face acest lucru folosind o imagine PNG obișnuită, ale cărei canale verzi și albastre sunt setate la 0 și totul este stocat în canalul roșu (de aceea imaginea de exemplu este în roșu). În funcție de platforma dvs., puteți să o salvați într-un format unic adecvat. O paletă inițială este salvată ca o imagine 1D, cu o lățime de 256 pentru a reprezenta toate valorile pe care le vom mapa.
Partea principală nu este atât de complicată. Vom căuta anumite valori într-o textura bazată pe o altă textura. Pentru aceasta vom folosi multi-textură și un fragment simplu de shader.
(Multi-texturarea inseamna, in esenta, folosirea texturilor multiple in timp ce desenati o singura bucata de geometrie si este sustinuta de GPU-uri).
// setarea mai multor texturi, contextul este context context Stage3D.setTextureAt (0, _texture.base); // fs0 în contextul shader.setTextureAt (1, _palette.base); // fs1 în shader
Un alt lucru pe care trebuie să-l căutăm este că avem nevoie de o modalitate de a face ca anumite părți ale imaginilor să fie transparente. Am menționat mai devreme că acest lucru se va face folosind o valoare specială (255), ceea ce înseamnă transparență. Fragmentele shaders au o comandă care va elimina fragmentul, făcându-l invizibil. Vom face acest lucru detectând când valoarea este 255.
Acest exemplu folosește limbajul de shader AGAL. Poate fi un pic greu de înțeles, deoarece este un limbaj asemănător cu ansamblul. Puteți afla mai multe despre aceasta în Conexiunea pentru dezvoltatori Adobe.
// citiți valoarea din textura obișnuită (la ft1) tex ft1, v1, fs0 <2d, linear, mipnone, repeat> // scade valoarea texturii (ft1.x) de la pragul // alfa (fc0.x definit a fi 0.999 în codul principal) sub ft2.x, fc0.x, ft1.x // aruncă fragmentul dacă reprezintă masca, // 'kil' face că dacă valoarea este mai mică de 0 kil ft2.x // citește culoarea din paletă folosind valoarea din textură obișnuită (ft1) tex ft2, ft1, fs1 <2d, nearest, mipnone, repeat> // multiplicați culoarea vertexului cu culoarea paletei și păstrați-o la ieșirea mul oc, ft2, v0
Acest lucru poate fi acum încapsulat într-un Starling DisplayObject personalizat, care conține textura și imaginea paletei - așa este implementat în exemplul codului sursă.
Pentru a schimba realitatea colorate a caracterului, va trebui să schimbăm paleta. Dacă vrem să schimbăm culoarea pentru o anumită parte a personajului, luăm paleta originală de tonuri de gri și schimbăm colorarea segmentului care corespunde acelei părți.
Următorul cod se repetă prin paletă și aplică culoarea corespunzătoare fiecărei părți:
// treceți prin paleta pentru (var i: int = 0; i < _paletteVector.length; i++) // 42 is the length of a segment in the palette var color:uint = _baseColors[int(i / 42)]; // extract the RGB values from the segment color value and // multiply original grayscale palette var r:uint = Color.getRed(color) * _basePaletteVector[i]; var g:uint = Color.getGreen(color) * _basePaletteVector[i]; var b:uint = Color.getBlue(color) * _basePaletteVector[i]; // create a new palette color by joining color components _paletteVector[i] = Color.rgb(r, g, b);
Culorile noastre sunt salvate ca numere nesemnate care cuprind 8 biți de valoare roșie, verde și albastră. Pentru a le elimina ar trebui să facem niște operații bitwise, din fericire Starling oferă metode de ajutor pentru asta în Culoare
clasă.
Pentru a permite selectarea părților de caractere, păstrăm datele în memorie. Apoi, putem determina secțiunea caracterului pe care clicul unui mouse corespunde citirea valorii pixelilor în acel moment.
var caracterColor: uint = _chracterBitmapData.getPixel (touchPoint.x, touchPoint.y); // a lua valoarea roșie var caracterValue: uint = Color.getRed (characterColor); // 255 înseamnă transparent, așa că îl vom folosi ca deselecție dacă (characterValue == 255) _sectionSelection = SECTION_NONE; altfel // calculați secțiunea, fiecare secțiune durează 42 de pixeli din paleta _sectionSelection = int (characterValue / 42) + 1;
Această implementare are unele limitări, care pot fi rezolvate cu puțin mai multă muncă în funcție de nevoile dvs. Demo-ul nu arată animația foaie de sprite, dar aceasta poate fi adăugată fără modificări clasei principale paletă.
Este posibil să fi observat că transparența este tratată ca vizibil și nu este vizibil. Acest lucru cauzează marginile grosiere care ar putea să nu fie potrivite pentru toate jocurile. Acest lucru poate fi rezolvat folosind a masca - o imagine în tonuri de gri care reprezintă valoarea de transparență, de la negru (complet opac) la alb (complet transparent). Acest lucru va spori însă cerințele de memorie.
O tehnică alternativă care poate fi utilizată pentru a schimba culoarea părților obiectului este utilizarea unei texturi suplimentare sau a unui canal. Ele sunt folosite ca măști sau tabele de căutare (care sunt ele însele similare paletelor) pentru a găsi valori de culoare care vor fi înmulțite cu textura originală. Un exemplu poate fi văzut în acest videoclip:
Efectele foarte interesante pot fi realizate prin animarea paletei. În demonstrația de exemplu, aceasta este utilizată pentru a reprezenta partea de caractere în care se schimbă culoarea. Putem face acest lucru prin deplasarea segmentului paletei care reprezintă o secțiune a caracterului, creând o mișcare circulară:
// salvați valoarea de pornire a secțiunii paletei var tmp: int = _paletteVector [start]; // treceți prin segmentul de secțiune al paletei și deplasați valorile spre stânga pentru (var i: int = start; i < end; i++) _paletteVector[i] = _paletteVector[i + 1]; // use saved staring value, wrapping around _paletteVector[end] = tmp;
Un exemplu destul de uimitor poate fi văzut aici: Ciclul de pânză.
Un cuvânt de precauție: dacă vă bazați foarte mult pe această tehnică pentru animație, ar fi mai bine să o faceți pe CPU, deoarece încărcarea texturii pe GPU-ul pentru fiecare schimbare a paletei poate fi costisitoare.Pentru a obține o performanță, putem grupa mai multe palete de caractere (1D) într-o singură imagine (2D) - aceasta ne-ar permite să adăugăm o varietate de caractere cu modificări minime la starea de redare. Cazul perfect de utilizare pentru acest lucru este un joc MMO.
Tehnica descrisă aici poate fi foarte eficientă în mediile MMO 2D, în special în cazul jocurilor web unde dimensiunea descărcării contează foarte mult. Sper că am reușit să vă dau câteva idei despre ceea ce puteți face dacă vă gândiți la texturile dvs. într-un mod diferit. Vă mulțumim pentru lectură!