Simppeli tilemapengine C++:lla ja SDL:llä toteutettuna. Ei liene hirveän optimoitu, mutta hoitaa kuitenkin hyvin tehtävänsä.
Käyttää yksinkertaista karttaformaattia, jossa tilesetin tiedostossa on ensimmäisellä rivillä välilyönnillä erotettuna leveys, korkeus ja tilekoko. Seuraavat riveillä ovat tilemapin sisältämät tilet, 0 merkitsee tyhjää ja > 0 kartassa olevaa tileä.
Tilemap -luokka ja käyttöesimerkki ovat samassa listauksessa. Toisessa listauksessa on esimerkkitilemap. Lataa esimerkki tilesetteineen ja binääreineen tästä.
Esimerkkikoodissa karttaa voi liikutella nuolinäppäimistä.
// Simppeli tilemap -engine á la Attenk #include <SDL/SDL.h> #include <iostream> #include <fstream> #include <string> #include <sstream> //Vakiot #define SCREEN_WIDTH 500 #define SCREEN_HEIGHT 500 using namespace std; //Kuvienpiirtofunkkari, suoraan SDL -oppaasta void PiirraKuva(SDL_Surface *kuva, SDL_Surface *naytto, int kuvax, int kuvay, int leveys, int korkeus, int nayttox, int nayttoy) { SDL_Rect kuvaalue; // alue, mikä kuvasta piirretään kuvaalue.x = kuvax; kuvaalue.y = kuvay; kuvaalue.h = korkeus; kuvaalue.w = leveys; SDL_Rect nayttoalue; // alue näytöllä, jolle kuva piirretään nayttoalue.x = nayttox; nayttoalue.y = nayttoy; SDL_BlitSurface(kuva, &kuvaalue, naytto, &nayttoalue); } //TILEMAP -luokka, jonka avulla tilemappeja käsitellään class TILEMAP { int *MapArray; //Osoitin tiletaulukkon (yksiulotteinen) SDL_Surface **MapImages; //Osoitin tilekuvataulukkoon int MapWidth, MapHeight, TileSize, TileCount; //Kaikenlaista tarpeellista tietoa public: int LoadMap(string FileName,string TileSet); //Tilesetin ja -mapin latausfunkkari ~TILEMAP(); int DrawMap(SDL_Surface *screen, int MapX,int MapY,int ScreenX, int ScreenY, int Width, int Height); //Kartan piirtofunktio int GetMap(int TileX, int TileY) {return MapArray[TileY * MapWidth + TileX];} int PutMap(int TileX, int TileY, int Tile) {MapArray[TileY * MapWidth + TileX] = Tile;} }; TILEMAP::~TILEMAP() //Hajoitin, vapautetaan kaikki surfacet ja poistetaan osoittimien sisältö { for (int i = 0; i < TileCount;i++) SDL_FreeSurface(MapImages[i]); delete [] MapImages; delete [] MapArray; } int TILEMAP::LoadMap(string FileName,string TileSet) //Tilekartan latausfunkkari { ifstream MapFile(FileName.c_str()); //Tiedostovirta string Line; //Muuttuja tiedoston riveille string::size_type loc, loc2; //Välimerkin sijainti if (MapFile.is_open()) { //Jos tiedosto on olemassa int width,height,tSize; //ENSIMMÄINEN RIVI: leveys, korkeus ja tilekoko getline(MapFile,Line); //Luetaan rivi line -muuttujaan istringstream lineStream; lineStream.str(Line); lineStream >> width >> height >> tSize; if(width != 0 && height != 0 && tSize != 0) { //Jos kaikkien arvo > 0, jatketaan MapWidth = width; MapHeight = height; TileSize = tSize; } else { //Muussa tapauksessa heitetään erroria fprintf(stderr, "Ensimmäinen rivi on virheellinen!"); MapFile.close(); return 0; } //Luodaan tiletaulukko tietojen perusteella MapArray = new int[MapWidth * MapHeight]; int len = 1, tileluku; bool ready; for (int i = 0;i < MapHeight;i++) { //Käydään jokainen rivi läpi //LUETAAN RIVIDATA getline(MapFile,Line); //Jos rivillä on oikea määrä dataa... if (Line != "" && Line.length() >= (MapWidth * 2 - 1)) { lineStream.str(Line); lineStream.clear(); //Otetaan jokainen numero for (int a = 0;a < MapWidth;a++) { lineStream >> tileluku; //Sijoitetaan tiledata taulukkoon MapArray[i * MapWidth + a] = tileluku; } } else { fprintf(stderr, "Kartan määrityksissä on virheitä!"); MapFile.close(); return 0; } } MapFile.close(); //Suljetaan tiedosto //Lataillaan tilesetti SDL_Surface *tmpMap; //Väliaikainen surface tilesetille if (tmpMap = SDL_LoadBMP(TileSet.c_str())) { SDL_SetColorKey(tmpMap, SDL_SRCCOLORKEY, SDL_MapRGB(tmpMap->format,0,0,0)); //Asetetaan läpinäkyvyys mustaksi MapImages = new SDL_Surface*[(tmpMap->h / TileSize) * (tmpMap->w / TileSize)]; //Luodaan tilekuvataulukko for (int y = 0;y < tmpMap->h / TileSize;y++) { for (int x = 0;x < tmpMap->w / TileSize;x++) { loc = y * (tmpMap->w / TileSize) + x; //Laitetaan tilekuvat taulukkoon MapImages[loc] = SDL_CreateRGBSurface(SDL_HWSURFACE, TileSize, TileSize, 32, 0, 0, 0, 0); PiirraKuva(tmpMap, MapImages[loc], x * TileSize, y * TileSize, TileSize, TileSize,0,0); } } TileCount = (tmpMap->h / TileSize) * (tmpMap->w / TileSize); SDL_FreeSurface(tmpMap); //Poistetaan surface } else { fprintf(stderr, "Tilesettiä ei saatu ladattua!"); return 0; } } else { fprintf(stderr, "Tilekarttaa ei löytynyt!"); return 0; } } int TILEMAP::DrawMap(SDL_Surface *screen, int MapX,int MapY,int ScreenX, int ScreenY, int Width, int Height) { //Jos leveys tai korkeus turhan isoja, pienennetään if (MapWidth * TileSize < MapX + Width) Width = MapWidth * TileSize - MapX ; if (MapHeight * TileSize < MapY + Height) Height = MapWidth * TileSize - MapY; if (Width > 0 && Height > 0 && MapX + Width > 0 && MapY + Height > 0) { for(int x = (MapX / TileSize); x < (MapX + Width) / TileSize + 1; x++) { if (x < 0) x = 0; for(int y = (MapY / TileSize); y < (MapY + Height) / TileSize + 1; y++) { if (y < 0) y = 0; //Piirretään näytölle MapArrayn osoittama tile if (y < MapHeight && x < MapWidth && MapArray[y * MapWidth + x] != 0) PiirraKuva(MapImages[MapArray[y * MapWidth + x] - 1],screen,0,0,TileSize,TileSize, x * TileSize - MapX + ScreenX,y * TileSize - MapY + ScreenY); } } } } int main(int argc, char *argv[]) { //Alustukset if(SDL_Init(SDL_INIT_VIDEO) == -1){ fprintf(stderr, "SDL:n alustus epäonnistui: %s\n", SDL_GetError()); return 0; } SDL_Surface *screen; if (!(screen = SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT, 32, SDL_HWSURFACE|SDL_DOUBLEBUF))) { fprintf (stderr, "Näyttötilan asettaminen epäonnistui: %s\n", SDL_GetError()); return 0; } TILEMAP map; map.LoadMap("taso1.txt","tileset.bmp"); bool isRunning = true; //Boolean -muuttuja, joka kertoo onko suoritus käynnissä //Näppisinputtia varten SDL_Event event; Uint8* nappi; //Kartan piirtokohta int mapX = 0,mapY = 0; while(isRunning){ //Päälooppi SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format,255,255,255)); //Tyhjennetään screeni valkoiseksi map.DrawMap(screen,mapX,mapY,0,0,SCREEN_WIDTH,SCREEN_HEIGHT); //Piirretään kartta SDL_Flip(screen); //Päivitetään näytölle //Lopetustarkistukset while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_QUIT: isRunning = false; break; case SDL_KEYDOWN: if(event.key.keysym.sym == SDLK_ESCAPE) isRunning = false; break; } } //Karttaa siirrellään nuolinäppäimistä nappi = SDL_GetKeyState(NULL); if ( nappi[SDLK_UP] ) mapY -= 4; if ( nappi[SDLK_DOWN] ) mapY += 4; if ( nappi[SDLK_LEFT] ) mapX-= 4; if ( nappi[SDLK_RIGHT] ) mapX+= 4; //Jotta ei toimi liian nopeasti SDL_Delay(4); } return 0; }
Tilemaptiedosto
25 20 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 4 4 4 4 4 4 18 18 18 4 4 4 4 4 4 4 0 0 0 0 0 0 0 0 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 4 4 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 155 155 4 155 155 0 0 0 0 4 0 0 0 0 0 0 0 0 0 4 4 0 0 0 0 0 0 4 0 0 0 0 0 0 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 4 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 4 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 4 155 155 4 0 0 0 0 0 4 155 155 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 4 0 0 0 0 0 4 0 0 0 4 0 0 0 0 0 0 0 0 0 0 4 0 0 0 4 0 0 0 0 0 4 0 0 0 4 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 149 102 102 102 102 102 102 102 102 102 102 102 102 102 102 102 152 0 0 0 0
Hyvält näyttää, monelle varmasti tulee tarpeeseen.
Mistä muute ton tilesetin sait? Teitkö ite? ._.
Ääh, tilesetti on vain oma modifikaatio CoolBasicin karttaeditorin mukana tulleesta. Sitä saa halutessaan käyttää täysin vapaasti :)
Joo hieman hidas on, 45 FPS, 640x480 resol. 1400mhz koneella. screenin flippausta en laskenu FPS arvoon mukaan.
Jotenkin tuntuu että tuossa on turhan paljon ylimääräistä säätöä tuossa koodissa... vaikuttaa turhan pitkältä. esim MapImages[MapArray[y * MapWidth + x] - 1] ei oikein vaikuta järkevältä ratkaisulta näin äkkiseltään katottuna.
Ja tuohon kannattaisi laittaa screenin leveys ja korkeus omiin muuttujiinsa, ja käyttää niitä ohjelman eri kohdissa, kun käytät tuossa suoraan "500,500" monessa kohdassa koodia.
Ja tuo mappiformaatti kannattais tallentaa suoraan binääridatana, jolloin sen voi lukea muistiinkin todella nopeasti + vie vähemmän tilaa.
Jooh, toteutus on paikoin hieman tuollaista 'mitä ekana tuli mieleen' -tyyppistä ja koodiin oli jäänyt tyhmiä hidasteita. Muuttelin koodia hiukan, nyt FPS pitäisi olla n. kaksinkertainen (!). Saatan vielä tämän jälkeenkin muutella koodia hieman järkevämmäksi, tuo MapImages -taulukko on todellakin vähän turha.
Binäärimuodossa lataus ei ole puhtaasti helpomman kartanmuokkauksen takia. Myönnetään, että koodi olisi huomattavasti selkeämpää sitä käytettäessä.
No, eikös niitä karttoja yleensä muokata editoreilla kuitenkin :)
Tosin, kyseessä ei ollut mapin latausesimerkki, joten ihan sama mitä tässä nyt käyttääkään...
Voi kauhia paikka mitä wnb-englishmiä täälläkin. Ei se ole mikään tilemapengine, se on kaakelikarttakone. Tämä on suomenkielinen foorumi.
Öö...Miten tuohon saataisiin silleen, että olisi x ja y, ja ne eivät voisi mennä "seinän" päälle? Mielellään liukuva törmäys.
Missään vaiheessa ei lopeteta SDL:ää eli lopussa pitäisi kutsua SDL_Quit() tai laittaa SDL:n alustuksen jälkeen atexit(SDL_Quit)
Aihe on jo aika vanha, joten et voi enää vastata siihen.