Rezolvarea frustrării jucătorilor tehnici pentru generarea de numere aleatorii

Dacă faceți o conversație cu un fan RPG, nu va dura mult timp pentru a auzi o răsfățare despre rezultatele randomizate și pradă - și cât de frustrante pot fi acestea. Mulți jucători au făcut cunoscut această iritare și, în timp ce unii dezvoltatori au creat soluții inovatoare, mulți ne-au forțat încă prin încercări de infirmare a perseverenței.

Există o cale mai bună. Prin modificarea modului în care dezvoltatorii folosesc numerele aleatoare și generatoarele lor, suntem capabili să creăm experiențe captivante care împing această dificultate "perfectă" fără a împinge jucătorii peste margine. Dar, înainte de a intra în acest lucru, să trecem peste câteva elemente de bază ale generatoarelor de numere aleatoare (sau RNG-uri pe scurt).

Generatorul de numere aleatoare și utilizarea acestuia

Numerele aleatoare sunt în jurul nostru, fiind folosite pentru a adăuga variații software-ului nostru. În general, utilizările majore ale RNG-urilor sunt de a reprezenta evenimente haotice, de a manifesta volatilitate sau de a se comporta ca un limitator artificial.

Probabil interacționați cu numerele aleatoare sau cu rezultatele acțiunilor lor, în fiecare zi. Ele sunt folosite în studii științifice, jocuri video, animații, artă și aproape orice aplicație de pe computer. De exemplu, este posibil ca un RNG să fie implementat în animațiile de bază de pe telefon.

Acum, că am vorbit despre ceea ce este un RNG, să analizăm implementarea sa și cum poate îmbunătăți jocurile noastre.

Generatorul standard de numere aleatoare

Aproape toate limbile de programare folosesc un standard RNG în funcțiile de bază. Funcționează returnând o valoare aleatorie între două numere. RNG-urile standard pot fi implementate în zeci de moduri diferite în cadrul diferitelor sisteme, dar toate au în general același efect: returnați un număr randomizat în care fiecare valoare din interval are aceeași șansă de a fi returnată.

Pentru jocuri, acestea sunt utilizate în mod obișnuit pentru a simula zarurile rulante. În mod ideal, acestea ar trebui folosite numai în situațiile în care fiecare rezultat este dorit să apară în număr egal de ori.

Dacă doriți să experimentați cu raritate sau rate diferite de randomizare, această metodă următoare este mai potrivită pentru dvs..

Numere aleatoare aleatorii și sloturi de raritate

Acest tip de RNG este baza pentru orice RPG cu raritate de element. Mai exact, atunci când aveți nevoie de un rezultat randomizat, dar doriți ca unele să apară cu o frecvență mai mică decât altele. În cele mai multe clase de probabilitate, acest lucru este reprezentat de obicei cu o pungă de marmură. Cu RNG-uri ponderate, sacul dvs. ar putea avea trei marmură albastră și una roșie. Din moment ce dorim doar o marmură, fie ea una roșie, fie una albastră, dar este mult mai probabil ca ea să fie albastră.

De ce ar fi importantă o randomizare ponderată? Hai să folosim evenimentele SimCity în jocuri ca exemplu. Dacă fiecare eveniment a fost selectat utilizând metode ne-ponderate, atunci potențialul de apariție a fiecărui eveniment este statistic același. Acest lucru vă va face la fel de probabil să primiți o propunere pentru un nou cazinou ca să experimentați un cutremur în timpul jocului. Prin adăugarea ponderii, putem asigura că aceste evenimente se întâmplă într-o sumă proporțională care păstrează modul de joc.

Formele și folosințele sale

Gruparea acelorași elemente

În multe cursuri de informatică sau în cărți, această metodă este adesea menționată ca o "pungă". Numele este destul de nas, folosind clase sau obiecte pentru a crea o reprezentare virtuala a unui sac literal. 

Funcționează practic în felul următor: există un container în care obiectele pot fi plasate în locația în care sunt stocate, o funcție pentru plasarea unui obiect în "sac" și o funcție pentru selectarea aleatorie a unui element din "sac". Pentru a vă referi înapoi la exemplul nostru de marmură, înseamnă că vă veți trata punga ca având o marmură albastră, o marmură albastră, o marmură albastră și o marmură roșie.

Folosind această metodă de randomizare, putem determina aproximativ rata la care are loc un rezultat pentru a ajuta la omogenizarea experienței fiecărui jucător. Dacă am fi simplificat rezultatele pe o scară de la "Foarte rău" la "Foarte bine", acum am reușit mult mai viabil ca un jucător să experimenteze un șir inutil de rezultate nedorite (cum ar fi obținerea rezultatului "Foarte prost" De 20 de ori la rând). 

Cu toate acestea, este încă posibil din punct de vedere statistic să se primească o serie de rezultate proaste, doar din ce în ce mai puțin. Vom analiza o metodă care merge puțin mai departe pentru a reduce în scurt timp rezultatele nedorite.

Iată un exemplu rapid de pseudocode despre cum ar putea arăta o clasă de pungi:

Bag de clasă // Păstrați o gamă de elemente care se găsesc în elementele de arhivă ale saculuiInBag; // Umpleți sacul cu obiecte atunci când constructorul său creat (Array startItems) itemsInBag = startItems;  // adăugați un element la pungă trecând obiectul (apoi apăsați-l pe array) Funcția addItem (Object item) itemsInBag.push (item);  // Pentru a obține un return random item, utilizați o funcție aleatorie încorporată pentru a apuca un element din matrice Funcția getRandomItem () return (itemsInBag [random (0, itemsInBag.length-1)]);  

Raritatea implementării sloturilor

Similar cu implementarea de grupare din trecut, slotul de raritate este o metodă de standardizare pentru a determina rate (de obicei pentru a facilita menținerea procesului de design al jocului și a recompensei jucătorului). 

În loc de a determina individual rata fiecărui element dintr-un joc, veți crea o raritate reprezentativă - unde rata unui "comun" ar putea reprezenta o șansă de 20 în X pentru un anumit rezultat, în timp ce "Rare" ar putea reprezenta 1 X șansă.

Această metodă nu modifică prea mult funcția actuală a pungii în sine, ci mai degrabă poate fi utilizată pentru a crește eficiența la sfârșitul dezvoltatorului, permițând unui număr exponențial de elemente să li se acorde rapid o șansă statistică. 

În plus, sloturile de raritate sunt utile în modelarea percepției unui jucător, permițându-le cu ușurință să înțeleagă cât de des ar putea să apară un eveniment fără a elimina scufundarea lor prin numărătoarea numerelor.

Iată un exemplu simplu despre modul în care ar trebui să adăugăm o raritate în cutia noastră:

Bag de clasă // Păstrați o gamă de elemente care se găsesc în elementele de arhivă ale saculuiInBag; // adăugați un element în geantă trecând obiectul Funcție addItem (Object item) // ține evidența looping-ului în legătură cu sloturile de raritate Int timesToAdd; // Verificați variabila de raritate a elementului // (dar mai întâi creați acea variabilă de raritate în clasa elementului, // preferabil cu un tip enumerat) Comutator (item.rarity) Case 'common': timesToAdd = 5; Cazul "neobișnuit": timesToAdd = 3; Cauza "rare": timesToAdd = 1;  // Adăugați instanțe ale elementului în pungă în funcție de raritate În timp ce (timesToAdd> 0) itemsInBag.push (element); timesToAdd--;  

Numere aleatoare cu rate variabile

Am discutat acum despre unele dintre cele mai comune metode de a face față randomizării în jocuri, așa că hai să mergem într-una mai avansată. Conceptul de utilizare a ratelor variabile începe în mod similar cu cel din trecut: avem un număr determinat de rezultate și știm cât de des dorim ca acestea să aibă loc. Diferența față de această implementare este că dorim să ajustăm potențialul de rezultate, așa cum se întâmplă.

De ce am vrea să facem asta? Luați, de exemplu, jocuri cu un aspect colectiv. Dacă aveți zece rezultate posibile pentru elementul pe care îl primiți, dintre care nouă sunt "comune" și unul este "rar", atunci șansele dumneavoastră sunt destul de simple: 90% din timp, un jucător va obține un joc comun, iar 10% timpul vor primi rar. Problema vine atunci când luăm în considerare mai multe remizări.

Să ne uităm la șansele de a obține o serie de rezultate comune:

  • La prima remiză, există o șansă de 90% de a desena un obișnuit.
  • La două egaluri, există o șansă de 81% de a fi tras toate commons.
  • La 10 remize, există încă o șansă de 35% pentru toate commons.
  • La 20 remize, există o șansă de 12% pentru toate commons.

În timp ce raportul inițial de 9: 1 părea a fi o rată ideală la început, a ajuns doar să reprezinte rezultatul mediu și a lăsat 1 din 10 jucători petrecând de două ori mai mult decât intenționau să obțină acel rar. În plus, 4% dintre jucători ar petrece de trei ori mai mult pentru a obține cei rari, iar un nefericit de 1,5% ar petrece de patru ori mai mult.

Cum ratele variabile rezolvă această problemă

Soluția implementează o gamă de rarități pe obiectele noastre. Efectuați acest lucru prin definirea unei rarități maxime și minime pentru fiecare obiect (sau slot de raritate, dacă doriți să îl combinați cu exemplul anterior). De exemplu, permiteți elementului nostru comun o valoare minimă de raritate de 1, cu un maxim de 9. Raritatea va avea o valoare minimă și maximă de 1.

Acum, cu scenariul de dinainte, vom avea zece elemente, iar nouă dintre ele sunt un exemplu comun, în timp ce unul dintre ele este unul rar. La prima remiză, există o șansă de 90% de a obține un joc comun. Acum, cu ratele variabile, după ce este desenată obișnuită, vom scădea valoarea rarității cu 1.

Acest lucru face ca următoarea noastră remiză să aibă un total de nouă articole, dintre care opt sunt comune, oferind o șansă de 89% de a desena o comună. După fiecare rezultat comun, raritatea acestui element scade, făcând mai multe șanse de a trage rar până când vom elimina cu două elemente în sac, una comună și una rare.

În timp ce înainte de a exista o șansă de 35% de a desena 10 comuni la rând, acum există doar o șansă de 5%. Pentru rezultatele extraterestre, cum ar fi desenarea a 20 de comenzi la rând, șansele sunt acum reduse la 0,5%, și chiar mai mult în jos pe linie. Acest lucru creează un rezultat mai consistent pentru jucătorii noștri și împiedică acele cazuri în care un jucător are în mod repetat un rezultat rău.

Construirea unei clase de rată variabilă

Implementarea cea mai de bază a ratei variabile ar fi să scoatem un element din sac, mai degrabă decât să îl întoarcem, cum ar fi:

Bag de clasă // Păstrați o gamă de elemente care se găsesc în elementele de arhivă ale saculuiInBag; // Umpleți sacul cu obiecte atunci când constructorul său creat (Array startItems) itemsInBag = startItems;  // adăugați un element la pungă trecând obiectul (apoi apăsați-l pe array) Funcția addItem (Object item) itemsInBag.push (item);  Funcția getRandomItem () // alege un element aleatoriu din sac Var currentItem = itemsInBag [random (0, itemsInBag.length-1)]; // reduce numărul de instanțe ale acelui element dacă depășește valoarea minimă Dacă (instanțeOf (currentItem, itemsInBag)> currentItem.minimumRarity) itemsInBag.remove (currentItem);  return (actualItem);  

În timp ce o astfel de versiune simplă aduce cu ea însăși câteva probleme (cum ar fi punga care ajunge în cele din urmă la o stare de randomizare standard), ea reprezintă modificările minore care pot ajuta la stabilizarea rezultatelor randomizării.

Extinderea ideii

În timp ce aceasta acoperă ideea de bază a ratelor variabile, există încă câteva lucruri pe care trebuie să le luați în considerare pentru propriile implementări:

  • Eliminarea articolelor din sac vă ajută să creați rezultate consecvente, însă în cele din urmă revine la problemele de randomizare standard. Cum am putea modela funcții pentru a permite atât creșterea, cât și scăderea elementelor pentru a preveni acest lucru?

  • Ce se întâmplă când avem de-a face cu mii sau milioane de articole? Utilizarea unui sac plin cu pungi ar putea fi o soluție pentru acest lucru. De exemplu, crearea unui sac pentru fiecare raritate (toate articolele comune într-o pungă, razele în altul) și plasarea fiecăruia în sloturi într-o pungă mare poate oferi un număr mare de noi posibilități de manipulare.

Cazul pentru numere aleatoare mai puțin dure

Multe jocuri încă utilizează generarea de numere aleatorii standard pentru a crea dificultăți. Procedând astfel, se creează un sistem în care jumătate din experiențele playerului se încadrează pe ambele părți ale planificării. Dacă este lăsat necontrolat, acest lucru creează posibilitatea de a se întâmpla la un nivel neintenționat de cazuri limită de repetare a unor experiențe proaste.

Prin limitarea intervalului în care rezultatele se pot rătăci, este asigurată o experiență mai coerentă a utilizatorului, permițând un număr mai mare de jucători să se bucure de jocul dvs.,.

Înfășurarea în sus

Generarea de numere aleatoare este o bază de proiectare a jocului bun. Asigurați-vă că dvs. Verificați statisticile și implementați cele mai bune generații pentru fiecare scenariu pentru a îmbunătăți experiența playerului.

Îți place o altă metodă pe care nu am acoperit-o? Aveți întrebări cu privire la generarea de numere aleatoare în design-ul propriului dvs. joc? Lasă-mă un comentariu de mai jos, și voi face tot posibilul pentru a te întoarce la tine.