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.