PROGRAMMING RETRO PLATFORMER Piccola storia sulla programmazione dei platformer 2D Dagli 8bit a Unity GAME OVER - Milano - 19/09/2015 Autore: Paolo Cattaneo (Raven TravelStudios) Perché il platformer? • Saltare = AZIONE ! • Gravità + piattaforma + salto = sensazione immediata di mondo definito • Non è mai passato veramente di moda (ricorrenze di questi giorni) • Target trasversale • Base fortemente estendibile Le radici • 1979: Frogger - introduce il concetto di "saltare su una piattaforma". • 1980: Space Panic - vista laterale, scale, gravità. (Anticipa Lode Runner come gameplay). • 1981: Donkey Kong - Primo gioco dove si può saltare. Codifica lo stereotipo classico. (Anticipa Jump Bug di 6 mesi). • 1982: Q*Bert - Prima estensione isometrica. (Space Panic) Jump Bug (Alpha Denshi 1981) Primo platform con scrolling multidirezionale Definire il mondo di gioco: Tecniche • Tile Map (Statisticamente la maggioranza dei platform) • Vettoriale (Braid) • BitMask (Turrican, Worms ...) *gli esempi sono verosimili TILEMAP • E' come SUPER MARIO MAKER ! • Lo schermo di gioco è composto da caselline quadrate della stessa dimensione. • I dati del livello sono contenuti in una mappa di byte "in scala". • Con delle proprietà offset possiamo simulare inclinazioni e pendenze ("slopes"). • La tile non solo come terreno ma anche oggetto (nel senso di OOP) Super Mario Bros. Rockman I dati del livello sono contenuti in una mappa di byte "in scala". • CCCCCCCC CCCCCCCC CCCCCCCC CCCPPPCC CCCCCCCC GGGGGGGG GGGGGGGG Vantaggi • Relazione diretta tra coordinate sullo schermo e coordinate sulla mappa (pointMappa = pointScreen / TileSize) • Editing dei livelli facile e veloce • Facile da implementare, molto flessibile ed estendibile • Fornisce molte scorciatoie per velocizzare i calcoli Con degli offset di disegno possiamo simulare inclinazioni e pendenze. La scenetta iniziale di Donkey Kong mostra il funzionamento degli offset: il livello viene caricato con il classico meccanismo delle tile, ma alcune di esse sono posizionate con un allineamento verticale diverso per creare le pendenze. Notare che le tile con l'offset non sono fisicamente inclinate ma mantengono lo stesso piano orizzontale. La tile non solo come terreno ma anche oggetto (nel senso di OOP) Gli RPG a caselle come la saga di Ultima furono tra i primissimi giochi ad utilizzare il concetto di tile come oggetto con proprietà, metodi ed eventi. Due Grandi Applicazioni. I muretti di SMB possono essere usati per uccidere mostri, essere distrutti e fare spazio, prendere bonus nuovi, ecc. Nel capolavoro di Fukio Mitsuji ogni tile del livello "dirige" la bolla con cui collide. L'uso delle bolle è polivalente. TileMap e telecamera VETTORIALE • Molta più libertà nel creare forme irregolari del terreno • Calcoli basati sulla trigonometria • Dati del livello salvati come insieme di punti definiti e asset da caricare • Insidie da evitare (salto oltre le pendenze) Pac Land (namco 1984) Inaugura lo standard della decade successiva collisioni vettoriali..? Calcoli basati su trigonometria • La hitbox andrà a intersecare il quadrato delimitato dai vertici D ed E. • Inclinazione = (E.y - D.y) / (E.x D.x)); • P = D.y + (Inclinazione * dX); • P è il punto y sulla pendenza in relazione a dX Gestire il salto in modo da non eccedere la linea di collisione in fase ascendente. Willow (Capcom 1989) BITMASK • Ogni sprite terreno ha una controparte in b/n che ne definisce i bounds • Bad performance !!!!!!!1!1!!!!!!!111one!1one!!!!!!!!! • Molto precisa ma noiosa da implementare • Permette features più spettacolari, tipo overlay e distruzione delle texture stesse Routine iterazione per pixel perfect collision Due Grandi Applicazioni Creare Il Motore Fisico • Imho in questo genere programmarlo da zero è la cosa migliore (no engine precotti). Box2D è vostro nemico. • Gravità e movimenti del player. • Il salto: analogico, direzionabile, air drag, fisso. • Collisioni col mondo di gioco. • Applicare tolleranze per il giocatore. GRAVITA' E MOVIMENTI • Gravità = velocita.Y + accelerazione.Y dove accelerazione.Y aumenta ad ogni tick fino al suo massimo valore. • Viene spesso usato un booleano (IsOnGround) per determinare se applicare la gravità o meno. • Movimento.X = velocita.X + accelerazione.X dove accelerazione.X aumenta ad ogni tick fino al suo massimo valore. GRAVITA' E MOVIMENTI • Il modo in cui viene utilizzata accelerazione.X determina il rapporto di frizione con il terreno. • Se accelerazione.X segue la direzione dello stick, il player subirà uno slittamento quando invertirà la direzione (alla Super Mario Bros.) • Se accelerazione.X può essere solo positiva, si utilizza una variabile di direzione -1/+1 da moltiplicare al calcolo del movimento. • E' utile tenere una seconda variabile per memorizzare la direzione in cui si è rivolti per motivi grafici (es. Implementare un moonwalking, Salto ad arco fisso dove ci si può girare e sparare) IL SALTO DIREZIONABILE • ANALOGICO: viene commisurato alla pressione del tasto • Movimento.Y = (velocita.Y + accelerazione.Y) + jumpPushed; sotto una certa soglia di accelerazione.Y, jumpPushed è un fattore > 0 se il tasto salto è premuto, se no vale 0. ESEMPIO : SONIC CD • Massima accelerazione.X = 0.09375f • Massima velocita.X = 6f • Massima accelerazione.Y = 0.21873f • Massima velocita.Y = 16f AIR DRAG • E' responsabile del controllo "in aria" del player. • Di solito interviene quando il salto è in prossimità del suo apice. • Di solito tende a rallentare il player. • Esempio da Sonic: if (speed.Y < 0 && speed.Y is > -4) if (ABS(speed.X ) >= 0.125) speed.X = speed.X * 0.96875; IL SALTO NON DIREZIONABILE • Non è il default, va programmato appositamente in quanto la gestione dei tre movimenti base (dx, sx, salto) configura in automatico il salto direzionabile. IL SALTO NON DIREZIONABILE • Mentre nel salto direzionabile lo stato vale fino all'apice e poi il player torna in balia della gravità, nel salto non direzionabile lo stato è valido (e bloccato) finchè il player non riceve uno stimolo esterno all'input (tocca terra, viene colpito, ecc.) • Ha il grande vantaggio di facilitare le azioni coadiuvate al salto senza che il player debba più preoccuparsi di tenere lo stick premuto nella direzione desiderata. • Il valore di spinta del salto parte negativo (fase ascensionale) per terminare positivo (fase discendente) - Quindi su Unity è il tendenzialmente il contrario. Dragon Buster (namco 1984) Primo gioco con doppio salto COLLISIONI COL MONDO DI GIOCO • TileMap: Di solito si definisce una serie di punti attorno al perimetro dell'hitbox e si confrontano con le tile adiacenti (facili da trovare grazie al rapporto screen/mappa) (foto: McKids) COLLISIONI COL MONDO DI GIOCO • TileMap: Ogni tipo di tile è codificato con le sue proprietà definite nel programma. Nell'esempio: H = Right Slope Tile = sottotabella di 16x16 offset. (vedere slide 9) (foto: McKids) COLLISIONI COL MONDO DI GIOCO • Vettoriale : slide 18 • BitMask : slide 22 TOLLERANZE PER IL GIOCATORE • Effetto "trasporto" sulla piattaforma superiore. • In fase di atterraggio, extra collisione anticipata sul terreno per un nuovo eventuale salto immediato. • Extra time quando si cade per accettare l'input di salto. PIATTAFORMA MOBILE • Se si muove orizzontalmente, la position.X del player è sincronizzata a quella della piattaforma, più l'aggiunta del movimento.X del player. • Se si muove verticalmente, la position.Y del player è sincronizzata a quella della piattaforma. ANIMAZIONI RESIDUE • Esempio tipico: esplosioni delle hit. • Classe Manager con pool di classi "animazione". • Quando c'è bisogno di un'animazione residua, viene passata l'animazione al Manager che la include nel pool, la renderizza e a fotogrammi finiti la disattiva. BEST PRATICES • In questo genere, la risposta all'input è più importante dell'animazione. • The way: Splatterhouse inverte l'animazione in tempo reale se il player cambia direzione con lo stick. • Prince Of Persia e Dark Souls seguono la regola opposta. BEST PRATICES • Regolate bene durata, altezza e gravità del salto in modo che siano temporizzate con gli ostacoli mobili da saltare. Basarsi su quelli fissi non è sufficiente. • Sincronizzate bene le piattaforme mobili in modo che si incontrino spesso. Viceversa è facile snervare il player. • Evitate i "salti della fede". • Allo stesso modo, lasciate sempre sufficiente margine di schermo per far sì che i nemici siano visibili in tempo dal player. BEST PRATICES • Usare sempre le ENUM per gli stati a numero chiuso. • Legare sempre il tick come fattore moltiplicativo dei calcoli. • Usare sempre i pool di oggetti, non istanziare mai nel game cycle. • Evitare LINQ e seek di ogni genere, il Binary Search è il metodo di ricerca più veloce per un elenco di oggetti. BEST PRATICES • Se si usa un linguaggio con garbage collector, attenzione a non generare spazzatura involontariamente. Profilare sempre il codice perché alcune operazioni tipo il boxing generano garbage in automatico. • Preferire dove possibile i reference type e le struct (vengono allocate nello stack) ai strong type e alle classi. E in ogni caso invocare sempre i distruttori (sono problemi che poi vengono fuori quando fate i porting su device meno indulgenti dei PC). BREVE CENNI SU UNITY • Non adatto di suo ai 2D retro platformer ma ormai "ce tocca". • NON usare rigidbody o altro tipo di fisica rigida. • Sfruttare i collider (si trovano 1000 esempi) come nuovo sistema di collisione che sostituisce i precedenti. • Camera ortografica, coordinate OpenGL (0,0 = Bottom Left). • Pixel per Unit : 1 LINK UTILI http://games.greggman.com/game/programming_m_c__kids/ http://gamasutra.com/blogs/YoannPignole/20140103/207987/Plat former_controls_how_to_avoid_limpness_and_rigidity_feelings.p hp http://higherorderfun.com/blog/2012/05/20/the-guide-toimplementing-2d-platformers/ http://info.sonicretro.org/SPG:Jumping