Differences

This shows you the differences between two versions of the page.

Link to this comparison view

gp:laboratoare:06 [2025/03/30 16:21]
maria_anca.balutoiu
gp:laboratoare:06 [2026/04/08 09:07] (current)
maria_anca.balutoiu [Generarea procedurală de NPCs]
Line 1: Line 1:
-===== Laboratorul 06. Generare de NPCs și Item Stats =====+===== Laboratorul 06. Generare de NPCs, Items și Simulare de Lume ===== 
 +Generarea personajelor și a obiectelor este una dintre cele mai utilizate tehnici în jocurile moderne, de la RPG-uri care generează milioane de iteme unice până la simulări emergente, unde NPC-urile duc "​vieți"​ organice bazate pe reguli simple.
  
 +==== Generarea procedurală de NPCs ====
 + 
 +Un NPC este definit printr-o combinație de **atribute statice** (nume, clasă, aspect etc.) și **atribute dinamice** (viață, damage, personalitate). Cheia unui sistem bun nu este generarea de atribute pur aleatorie, ci generare **aleatorie controlată** (valori generate în intervale cu sens, combinate prin reguli care produc personaje coerente).
 + 
 +=== Clase și distribuție ponderată ===
 +În general, clasele de NPC nu sunt echiprobabile (de exemplu, războinicii sunt mai comuni decât magicienii legendari). Distribuția ponderată permite controlul frecvenței fiecărei clase.
 + 
  
 +=== Atribute derivate din clasă ===
 +Fiecare clasă definește **intervale de bază** pentru atributele NPC-urilor. Randomizarea se aplică în interiorul acestor intervale, nu global; un Mage va fi mereu mai slab fizic decât un Warrior, indiferent de roll. Exemplu:
 +
 +^ Class ^ HP ^ Damage ^ Armor ^
 +| Warrior | 80–120 | 15–25 | 10–20 |
 +| Mage | 40–65 | 30–50 | 2–5 |
 +| Rogue | 55–80 | 20–35 | 5–10 |
 +| Archer | 60–85 | 18–30 | 4–8 |
 +| Paladin | 90–130 | 12–20 | 15–25 |
 + 
 +=== Personalitate și modificatori comportamentali ===
 +Trăsătura de personalitate nu este doar cosmetică; aceasta modifică valorile de bază și va influența comportamentul în simulare. Fiecare trăsătură aplică un multiplicator pe atribute. Exemplu:
 +
 +^ Trăsătură ^ Efect pe atribute ^ Comportament în simulare ^
 +| Aggressive | Damage ×1.3, HP ×0.9 | Atacă primul, preferă duelul direct |
 +| Coward | HP ×0.8, Armor ×0.7 | Fuge când HP < 40% |
 +| Brave | HP ×1.1, Damage ×1.1 | Nu fuge niciodată, protejează aliații |
 +| Cunning | Damage ×1.2, Armor ×1.1 | Atacă NPC-ul cu HP cel mai scăzut |
 +| Peaceful | HP ×1.15, Damage ×0.7 | Nu inițiază atacuri, ripostează doar |
 +
 +==== Generarea procedurală de items ====
 +=== Sistemul de raritate ===
 +Raritatea controlează atât **puterea** unui item (stat budget), cât și **frecvența** cu care apare. Sistemul clasic cu culori comunică vizual calitatea instantaneu. Exemplu:
 +
 +^ Raritate ^ Culoare ^ Probabilitate ^ Stat multiplier ^ Abilitate specială ^
 +| Common | Alb | 55% | 1.0× | Nu |
 +| Uncommon | Verde | 25% | 1.3× | Nu |
 +| Rare | Albastru | 12% | 1.7× | Nu |
 +| Epic | Mov | 6% | 2.2× | Da |
 +| Legendary | Portocaliu | 2% | 3.0× | Da (2 abilități) |
 + 
 +=== Stat budget & balancing === 
 +Itemele de raritate mai mare nu au **toate** statisticile mai mari față de cele comune. În schimb, fiecare item primește un **buget de putere total** (calculat din raritate), iar acel buget este distribuit aleatoriu între atribute. Astfel, două iteme de aceeași raritate pot fi foarte diferite (de exemplu, unul poate fi axat pe damage, altul pe durabilitate).
 +
 +**Abilitățile speciale** se adaugă peste budget pentru Epic și Legendary. A doua abilitate a unui Legendary este aleasă explicit diferit de prima: dacă prima e ''​Poison'',​ a doua e aleasă din restul listei (''​Lifesteal'',​ ''​FireDamage'',​ ''​IceSlow'',​ ''​Thunder''​).
 +
 +<​code>​
 +GenerateItem(type,​ rarity):
 +    budget ​  = baseBudget * rarityMultiplier[rarity]
 +    damage ​  = Random(0.4, 0.7) * budget ​     // 40-70% din budget pe damage
 +    durabil ​ = budget - damage ​               // restul pe durabilitate
 +    ​
 +    if rarity >= Epic:
 +        ability = SelectRandom(specialAbilities)
 +    if rarity == Legendary:
 +        ability2 = SelectRandom(specialAbilities - {ability})
 +</​code>​
 +
 +<note tip>De ce se distribuie aleatoriu și nu este lăsat jucătorul să aleagă? Pentru că variabilitatea **creează motivație de a continua să caute loot**. Un Legendary cu distribuție nefavorabilă îl face pe player să mai joace o dată în speranța unui roll mai bun.</​note>​
 +
 +=== Abilități speciale ===
 +Abilitățile speciale sunt definite ca modificatori cu parametri proprii generați în intervale. Exemplu:
 +
 +^ Abilitate ^ Efect ^ Parametru generat ^
 +| Poison | Damage over time | 3–8 damage/​tick,​ 3–5 tick-uri |
 +| Lifesteal | Regenerare HP la hit | 15–30% din damage ca HP |
 +| Fire Damage | Bonus damage + burn | +5–15 damage, 2 tick-uri burn |
 +| Ice Slow | Reduce viteza țintei | 20–50% slow, 2–4 secunde |
 +| Thunder | Șansă de stun | 15–35% șansă, 1 secundă stun |
 +
 +==== Simularea unei Lumi ====
 +Simularea unei lumi este un exemplu de **emergent storytelling**. Pe baza unor reguli simple, se crează comportamente complexe și povești unice. Fiecare zi (**tick**) de simulare aplică regulile tuturor NPC-urilor în ordine, iar rezultatele se acumulează într-un jurnal narativ.
 +
 +La nivelul acestui laborator, simularea poate fi împărțită în patru componente independente:​
 +  * **World Simulator:​** Componenta centrală a aplicației,​ gestionează tick-urile, crează NPC și îi poziționează în lume la începutul simulării, gestionează sfârșitul simulării
 +  * **NPC Agent:** Conține toate informațiile despre fiecare NPC; gestionează logica de decizie pentru fiecare NPC (la fiecare tick primește contextul (vecini, relații, locație) și alege o acțiune)
 +  * **Sistemul de Relații:** Stochează relațiile dintre toate perechile de NPC-uri
 +  * **Jurnal Narativ:** Toate acțiunile alese de NPC-uri sunt transformate în text prin intermediul unor template-uri
 +
 +=== Logica de Decizie per Tick ===
 +La fiecare tick, fiecare NPC parcurge în ordine un arbore de decizie cu un anumit număr de priorități. Prima condiție adevărată câștigă, restul nemaifiind evaluate.
 +
 +^ Prioritate ^ Nume Acțiune ^ Condiție ^ Descriere Acțiune ^
 +| 1 | Supraviețuire | Personalitate ''​Coward''​ și HP < 30% | Fuge la o locație aleatoare |
 +| 2 | Atac | Personalitate ''​Aggressive''​ sau ''​Cunning''​ și există dușmani în locație | Atacă: Cunning alege ținta cu HP minim, Aggressive alege aleatoriu |
 +| 3 | Trade | Există aliați în locație și NPC-ul are iteme | 25% șansă de a da un item unui aliat |
 +| 4 | Explorare | - | 20% șansă de a se muta în altă locație |
 +| 5 | Idle | - | Nicio acțiune; relațiile cresc pasiv prin co-prezență |
 + 
 +<​note>​Ordinea priorităților determină caracterul lumii simulate. Dacă ''​Trade''​ se verifică înaintea ''​Attack'',​ NPC-urile vor prefera alianțele în fața conflictului și simularea va produce povești cu mai puțini morți și mai multă acumulare de relații pozitive.</​note>​
 +
 +=== Sistemul de Relații === 
 +Relațiile sunt stocate ca valori float într-un anumit interval predefinit (de exemplu, intervalul [−100, +100]), unde valoarea minimă înseamnă dușmănie absolută și valoarea maximă alianță puternică. Toate relațiile dintre NPC-uri pornesc de la 0 și se modifică prin interacțiuni:​
 +
 +^ Eveniment ^ Modificare relație ^
 +| Atac reușit | −20 pentru cel atacat față de atacant |
 +| Schimb de iteme | +10 pentru ambii participanți |
 +| Ucidere (martori prezenți) | −30 față de ucigaș pentru toți martorii |
 +| Fără conflict sau orice altă interacține | +2 per tick (familiaritate pasivă) |
 + 
 +<​note>​Relațiile sunt **asimetrice**. Când primul NPC modifică relația față de al doilea NPC cu un delta, relația inversă se modifică cu doar un anumit procent din acel delta.</​note>​
 +
 +=== Modificatorii de Locație ===
 +Locațiile, la rândul lor, nu sunt neutre. Ele modifică statistica de damage și probabilitățile de acțiune ale NPC-urilor prezente. De exemplu, modificatorul de agresivitate se aplică multiplicativ pe damage-ul de bază al NPC-ului pentru durata tick-ului curent, apoi se restaurează:​
 +
 +^ Locație ^ Modificator de agresivitate ^ Bonus trade ^ Bonus mișcare ^
 +| Dungeon | ×1.5 | — | — |
 +| Tavernă | ×0.6 | +40% | — |
 +| Piață | ×0.4 | +60% | — |
 +| Pădure | ×1.2 | — | +20% |
 +| Câmpie | ×1.0 | — | +10% |
 +
 +=== Jurnalul narativ procedural === 
 +Fiecare acțiune va genera o linie de text prin selectarea aleatoare a unui template și substituirea variabilelor contextuale. Template-urile sunt organizate pe tipuri de acțiuni, cu multiple variante pentru fiecare acțiune pentru a evita repetiția:
 +
 +^ Variabilă ^ Semnificație ^
 +| ''​{attacker}'',​ ''​{target}''​ | Numele NPC-urilor implicate |
 +| ''​{cls}''​ | Clasa NPC-ului care acționează |
 +| ''​{loc}''​ | Numele locației curente |
 +| ''​{dmg}''​ | Damage-ul aplicat (rotunjit) |
 +| ''​{style}''​ | Derivat din personalitate:​ ''​Aggressive''​ → "​brutală",​ ''​Cunning''​ → "​calculată"​ |
 +| ''​{hp}''​ | Procentul HP curent (pentru template-urile de fugă) |
 + 
 +Pentru acest laborator, jurnalul va reține și numărul zilei curente, prefixând fiecare intrare cu ''​[Ziua N]''​. Intrările de tip ''​Idle''​ se înregistrează rar (20% din cazuri) pentru a nu polua textul.
 + 
 +<​note>​Jurnalul narativ este ceea ce transformă o simulare abstractă de date într-o poveste. Calitatea și variabilitatea template-urilor determină dacă playerul simte că asistă la o lume vie sau citește un simplu log de debug.</​note>​
 +
 +==== Implementare în Unity ====
 +=== Setup proiect ===
 +  - **Structurile de date:** Definiți enum-urile și clasele de date pentru NPC-uri, iteme și locații (la nivelul acestui laborator sunt suficiente 5 locații și 3 modificatori de locație (agresivitate,​ trade, mișcare))
 +  - **Generatorul de NPC-uri:** Fiecare NPC se generează prin selecția ponderată a clasei și aplicarea multiplicatorilor de personalitate. Pentru început, se generează numele din două liste independente,​ apoi selectează clasa, apoi se selectează personalitatea uniform aleatoriu, iar în final se generează atributele de bază din intervalele clasei și se înmulțesc cu multiplicatorii de personalitate.
 +  - **ItemGenerator:​** Pentru calculul rarității se urmează același model de selecție ponderată. După ce raritatea e determinată,​ se calculează bugetul total (''​BASE_BUDGET × rarityMultiplier''​),​ se generează un număr aleator dintr-un interval predefinit (de exemplu, [0.4, 0.7]) pentru damage, iar restul bugetului merge la durabilitate. Numele itemului se construiește procedural dintr-un prefix aleatoriu și un sufix dependent de tip. Abilitățile speciale se adaugă pentru Epic (una) și Legendary (două distincte).
 +  - **Sistemul de Relații:** Relațiile sunt stocate ca valori float într-un anumit interval predefinit (de exemplu, intervalul [−100, +100]). Relația directă se actualizează cu delta, iar relația inversă cu ''​delta × 0.7'',​ ambele cu clamp la intervalul predefinit.
 +  - **NPC Agent:** Se implementează arborele de decizie din tabelul de relații, ținând cont și de lista vecinilor, modificatorul locației curente, în ordinea exactă a priorităților. Se calculează damage-ul primit și se verifică dacă NPC-ul a murit.
 +  - **Jurnal Narativ:** Se selectează aleatoriu din template-urile existente pentru a se afișa acțiunile NPC-urilor.
 +  - **Main World Simulator:​** Sistemul așteaptă ''​tickInterval''​ secunde între tick-uri. La fiecare ''​zi''​ nouă, se iterează printre toți agenții vii, aplică damge-ul agentului, aplică decizia luată de agent. Simularea se termină când rămâne cel mult un NPC în viață sau se epuizează zilele maxime.
 +==== Tasks ====
 +  - Creați un NPC generator simplist în care: 
 +    * Un NPC este definit printr-o combinație aleatoare de nume, clasă, viață, damage.
 +    * Clasa este selectată prin distribuție ponderată.
 +    * Fiecare NPC va avea și o trasătură aleatoare de personalitate,​ care modifică numeric atributele de bază.
 +    * Pe baza clasei asociate, fiecare NPC va avea un portret afișat pe ecran.
 +    * Se generează un nou NPC și se afișează pe ecran la apăsarea unui buton.
 +  - Creați un generator de armură și arme în care:
 +    * Un item este definit printr-un tip, o raritate, damage și durabilitate generate din **stat budget**.
 +    * Itemele Epic și Legendary au abilități speciale cu parametri proprii generați în intervale (exemple abilități:​ poison, lifesteal, fire damage, ice slow).
 +    * Se generează un nou item și se afișează pe ecran la apăsarea unui buton.
 +    * Numele itemului se generează procedural (prefix + tip).
 +    * În funcție de raritatea item-ului, scrisul de pe ecran va avea altă culoare.
 +  - Implementați simularea unei lumi procedurale:​
 +    * Generați 4–6 NPC-uri și distribuiți-le aleatoriu în 5 locații predefinite.
 +    * La apăsarea butonului ''​Advance Day'',​ fiecare NPC execută o acțiune bazată pe personalitate,​ HP curent și relațiile cu vecinii din aceeași locație.
 +    * Sistemul de relații [−100, +100] se actualizează după fiecare interacțiune și influențează deciziile viitoare.
 +    * Fiecare acțiune generează o linie de text narativ din template-uri cu minim 3 variante per tip.
 +    * Simularea se oprește când rămâne un singur NPC în viață sau după 10 zile și afișează un sumar procedural.
 +  - **Bonus 1.** Creați un meniu prin care să puteți scrola prin toate personajele și obiectele generate. Rezultatele generări se vor salva pentru a fi accesibile și la rulări ulterioare ale aplicației. Jucătorul va putea redenumi NPCs și item-urile generate.
 +  - **Bonus 2.** Pentru fiecare item afișați "​rarity stars" în dreptul numelui. Adăugați un efect de animație pentru cel mai rar tip de item.
 +  - **Bonus 3.** Adăugați o hartă vizuală simplă (grid sau icoane) care arată locația curentă a fiecărui NPC și se actualizează la fiecare tick.
gp/laboratoare/06.1743340908.txt.gz · Last modified: 2025/03/30 16:21 by maria_anca.balutoiu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0