Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: C++: Tilemapengine

Sivun loppuun

Atte [27.12.2006 13:25:00]

#

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

crafn [05.01.2007 16:17:28]

#

Hyvält näyttää, monelle varmasti tulee tarpeeseen.
Mistä muute ton tilesetin sait? Teitkö ite? ._.

Atte [05.01.2007 21:52:18]

#

Ääh, tilesetti on vain oma modifikaatio CoolBasicin karttaeditorin mukana tulleesta. Sitä saa halutessaan käyttää täysin vapaasti :)

T.M. [12.01.2007 22:47:54]

#

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.

Atte [15.01.2007 15:57:52]

#

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ä.

T.M. [15.01.2007 20:04:23]

#

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...

jones7 [29.01.2007 17:04:01]

#

Voi kauhia paikka mitä wnb-englishmiä täälläkin. Ei se ole mikään tilemapengine, se on kaakelikarttakone. Tämä on suomenkielinen foorumi.

jannepelaa [22.03.2007 15:48:18]

#

Öö...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.

teksturi [26.03.2008 11:47:06]

#

Missään vaiheessa ei lopeteta SDL:ää eli lopussa pitäisi kutsua SDL_Quit() tai laittaa SDL:n alustuksen jälkeen atexit(SDL_Quit)


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta