Another 2D sample:

how to build a

Platform Game!

Un modo evoluto di uso dei Texture


Texture Atlas

kenney.nl


(vedi anche opengameart.org)

AtlasMaker


Scegliere e gestire le nostre risorse


Immagini e descrittori

<atlas numImages="47">
  <texture file="ken.tga" trans="true">
    <image name="alienBlue" x="323" y="71" width="66" height="92"/>
    <image name="alienBlue_duck" x="125" y="374" width="67" height="72"/>
    <image name="alienBlue_jump" x="191" y="71" width="66" height="93"/>
    <image name="alienBlue_stand" x="257" y="71" width="66" height="92"/>
    <image name="alienBlue_walk1" x="125" y="71" width="66" height="93"/>
    ...
    

Elenco e coordinate / offset

Mappa di gioco

Semplice file di testo
Remap dei tile con elementi nel Texture Atlas

vs

Map Makers: Layer, gestione tile, mask di collisione


vs

Animazioni

Meccanismo dinamico (usando naming)


    <image name="alienBlue_walk1" x="125" y="71" width="66" height="93"/>
    <image name="alienBlue_walk2" x="0" y="272" width="67" height="96"/>

    <image name="worm_walk1" x="389" y="141" width="63" height="23"/>
    <image name="worm_walk2" x="0" y="488" width="63" height="23"/>        
    

Oltre la _drawSPRITE

typedef struct TEXATLI{
 float x,y,w,h;
 float bx,by,rw,rh;
 char  nm[64];
}TEXATLI;

typedef struct TEXATL{
 Texture texID;
 int     cnt;
 TEXATLI*tai;
}TEXATL; 

int   TEXATL_findanim(TEXATL*t,const char*name,int*nframes);

int   gfx_drawSPRITETEXATL(float x,float y,TEXATL*t,int id,float ratio,float alpha,int dir);

Area di gioco vs Schermo

Il nostro mondo è più grande di quello che vediamo

Disegnare la mappa

algoritmo simile al clone di Pac-Man
void drawWORLD(WORLD*w,INGAMESCENE*s) 

Concetto e gestione di Viewport

schermo = porzione della mappa intorno al player

Disegnare la mappa


usando un offset
typedef struct tagMAP{
 char      *map;        // elementi mappa 
 int        w,rw,h;     // dimensione (in elementi)
 float      sx,sy;      // dimensione (in pixel)
 float      ox,oy;      // offset
 float      tx,ty;      // dimensione in pixel degli elementi
}MAP; 
void resyncRESIZE(INGAMESCENE*s)
{
 int   id;
 os_gfxratio=os_video_h/320.0f;
 id=TEXATL_find(&s->textID,"sandCenter");
 s->map.tx=s->map.ty=s->textID.tai[id].w*os_gfxratio;
 s->map.sx=s->map.w*s->map.tx;
 s->map.sy=s->map.h*s->map.ty;
} 

Cambiare la Viewport


in modo che il player sia al centro
(quando possibile)
void adjustVIEWPORT(MAP*g_map,ACTOR*a)
{
 g_map->ox=max(a->x,os_video_w/2);
 g_map->oy=max(a->y,os_video_h/2);
 g_map->ox=min(g_map->ox,g_map->sx-os_video_w/2);
 g_map->oy=min(g_map->oy,g_map->sy-os_video_h/2);
 g_map->ox-=os_video_w/2;
 g_map->oy-=os_video_h*2/3;
 g_map->ox=(float)floor(g_map->ox);
 g_map->oy=(float)floor(g_map->oy);
} 

Interagire con il Mondo

Gestione collisioni


usando tiles vs bitmask

Strutture e funzioni

typedef struct tagINGAMESCENE{
 MAP       map;
 int       maptiles[256];
 int       maptilesmask[256];
 TEXATL    textID;
 Texture   backID;
}INGAMESCENE;

int checkGROUND(INGAMESCENE*s,ACTOR*a,float dx)
int checkCOLLISIONS(INGAMESCENE*s,ACTOR*a,float*dx,float*dy) 

Non sono destra-sinistra


Saltare (ovvero spinta e gravità)

int PLAYER_action(WORLD*w,INGAMESCENE*s,ACTOR*a)
 ...
 if((w->keys&dirJUMP)||(a->anim=='j'))
  {
   if(a->anim!='j')
    {
     a->anim='j';
     a->curFrame=a->baseFrame=TEXATL_findanim(&s->textID,"alienBlue_jump",&a->nFrames);
     a->vy=-15*os_gfxratio;
    }
   dy+=a->vy;
   a->vy+=0.75f*os_gfxratio;
   if(a->vy>15*os_gfxratio)
    a->vy=15*os_gfxratio;
   if(w->keys&dirLEFT)
    {
     dx=-15.0f*0.33f*os_gfxratio;
     a->dir=1;
    }
   else
   if(w->keys&dirRIGHT)
    {
     dx=15.0f*0.33f*os_gfxratio;
     a->dir=0;
    }
  } 

Actors vs Tiles

I diamanti non sono per sempre

(e neppure le monete)

Callback e collisione


int DIAMOND_action(WORLD*w,INGAMESCENE*s,ACTOR*a)
int COIN_action(WORLD*w,INGAMESCENE*s,ACTOR*a) 

Diamanti

int DIAMOND_action(WORLD*w,INGAMESCENE*s,ACTOR*a)
{
 AREA2D bD,bP;

 getACTORBOUND(w,s,a,&bD);
 getACTORBOUND(w,s,g_player,&bP);

 if(AREA2D_intersect(&bD,&bP))
  {
   w->tempscore+=20;
   return 0;
  }
 else
  return 1;
} 

Aggiungiamo attori

Animazione e movimento degli antagonisti
Altri elementi animati

Screenshot


Ciclo di gioco

Il concetto di "perdere una vita"

non è necessariamente univoco

(ricominciare vs continuare)
(necessità di momento di invulnerabilità)


Collisione
tra antagonisti 
e attore principale

int WORM_action(WORLD*w,INGAMESCENE*s,ACTOR*a)
   ...   
   getACTORBOUND(w,s,a,&bW);
   getACTORBOUND(w,s,g_player,&bP);

   if(AREA2D_intersect(&bW,&bP))
    if((g_player->status&2)==0)
     {
      g_player->statustimer=os_getMilliseconds()+2000;
      g_player->status|=2;
      w->lifes--;
      if(w->lifes==0)
       EVENT_set(w,event_GAMEOVER);
      else
       {
        g_player->anim='j';
        g_player->curFrame=g_player->baseFrame=TEXATL_findanim(&s->textID,"alienBlue_jump",&g_player->nFrames);
        g_player->vy=-10*os_gfxratio;
       }
     }

Stabilire come si finisce
un livello

(e cosa capita dopo)

HUD ed eventi

Grafica ad hoc

Gestione del passaggio di stati

Eventi


Punti, vite e v-Pad


Home

(sweet home)


Cosa manca ancora

nella fase di gioco

Sconfiggere gli antagonisti

Effetti (particles / scene animate)

Cosa manca ancora

nella fase pre-gioco

selezione dei mondi 

audio

help/tutorial

(credits/leaderboard/ADMob)


Altre considerazioni

(sempre sui giochi 2D)

Quando l'area di gioco

è più grande dello schermo

(e non solo più larga)

(Disegno con doppio offset)


Ordinare gli elementi da disegnare


Altre tipologie di mappe


(isometriche)

Mappe "Irregolari"

bitmask / slopes / vectorial

(vedere qui)

Another 2D Game sample

By Marco Giorgini

Loading comments...

More from Marco Giorgini