Skip to content
This repository was archived by the owner on Jun 1, 2020. It is now read-only.

Source: TileMap

eXpl0it3r edited this page Oct 18, 2012 · 2 revisions

Basic Tiled Map using SFML's VertexArrays

This Tile Map example consists of two classes and one struct.
StaticTiledMap - class that holds chunksize and tilesize constants and will load vertices, store, display and texture them.
TileLoader - abstract base class, StaticTiledMap loads tiles by calling virtual method of class derieved from TileLoader that you pass to it.
MapData - struct that holds measurements of map(in tiles) and the name of texture that is to be used by StaticTiledMap, it's placed in TileLoader.h because MapData makes sense only when recieved/used by StaticTiledMap/TileLoader so putting it in it's own header would be redundant.

TileLoader.h:

	#ifndef TILE_LOADER_INCLUDED
	#define TILE_LOADER_INCLUDED

	#include <SFML/Graphics/VertexArray.hpp>
	#include <string>

	struct MapData
	{
		std::string TextureName;
		int MapX,MapY;
		MapData(void):MapX(0),MapY(0){}
	};
	class TileLoader//this is the class you must derieve from and complete following 2 tasks :
	{
	protected:
		MapData m_mapdata;//1.fill this struct with name of texture and map dimensions(in tiles)
	public:
		virtual ~TileLoader(void){};
		virtual void AppendTile(int gx,int gy,sf::VertexArray& garr)=0;
		/*
		2.implement this method to append(or skip appending) tiles(4 vertices) to given vertex array,
		you may assume tiles will be requested in row major order:((0,0),(1,0),..,(map_x-1,0),(0,1),..,(map_x-1,map_y-1)),
		this method will be called exactly MapX*MapY times,
		for correct displaying and culling it's assumed your appended tile will be square with side equal to tilesize
		and have it's top left point at (tilesize*gx,tilesize*gy),
		*/
		const MapData& GetData(void) const {return m_mapdata;}
	};

	#endif //TILE_LOADER_INCLUDED

StaticTiledMap.h:

	#ifndef STATIC_TILED_MAP_INCLUDED
	#define STATIC_TILED_MAP_INCLUDED
	#include <SFML/Graphics/Drawable.hpp>
	#include <SFML/Graphics/Texture.hpp>
	#include <SFML/Graphics/VertexArray.hpp>
	#include <vector>

	class TileLoader;//forward declaration for pointer
	class StaticTiledMap : public sf::Drawable
	{
	private:
		enum {tilesize=32,chunksize=32};//change values of these to match your needs and improve performance
		virtual void draw(sf::RenderTarget& target,sf::RenderStates states)const;
		sf::Texture m_texture;
		int map_x,map_y,chunks_x,chunks_y;//map x and y - dimensions of map in tiles, chunks x and y - amount of chunks
		std::vector<std::vector<sf::VertexArray> > m_chunks;
	public:
		StaticTiledMap(void);
		virtual ~StaticTiledMap(void){};
		void LoadFrom(TileLoader* gloader);
	};

	#endif //STATIC_TILED_MAP_INCLUDED

StaticTiledMap.cpp:

	#include "StaticTiledMap.h"
	#include "TileLoader.h"
	#include <SFML/Graphics/RenderTarget.hpp>
	#include <SFML/System/Vector2.hpp>

	void StaticTiledMap::draw(sf::RenderTarget& target,sf::RenderStates states)const
	{

		int left=0,right=0,top=0,bottom=0;
		//get chunk indices into which top left and bottom right points of view fall:
		sf::Vector2f temp=target.getView().getCenter()-(target.getView().getSize()/2.f);//get top left point of view
		left=static_cast<int>(temp.x/(chunksize*tilesize));
		top=static_cast<int>(temp.y/(chunksize*tilesize));
		temp+=target.getView().getSize();//get bottom right point of view
		right=1+static_cast<int>(temp.x/(chunksize*tilesize));
		bottom=1+static_cast<int>(temp.y/(chunksize*tilesize));
		//clamp these to fit into array bounds:
		left=std::max(0,std::min(left,chunks_x));
		top=std::max(0,std::min(top,chunks_y));
		right=std::max(0,std::min(right,chunks_x));
		bottom=std::max(0,std::min(bottom,chunks_y));
		//set texture and draw visible chunks:
		states.texture=&m_texture;
		for(int ix=left;ix<right;++ix)
		{
			for(int iy=top;iy<bottom;++iy)
			{
				target.draw(m_chunks[ix][iy],states);
			}
		}
	}
	StaticTiledMap::StaticTiledMap(void):
	map_x(0),map_y(0),chunks_x(0),chunks_y(0)
	{

	}
	void StaticTiledMap::LoadFrom(TileLoader* gloader)
	{
		m_texture.loadFromFile(gloader->GetData().TextureName);
		map_x=gloader->GetData().MapX;
		map_y=gloader->GetData().MapY;
		if((map_x*map_y)==0)//empty map - possibly forgotten to fill data struct
		{
			//to stop displaying at all after failed loading:
			chunks_x=0;
			chunks_y=0;
			m_chunks.clear();
			return;
		}
		chunks_x=(map_x/chunksize)+1;
		chunks_y=(map_y/chunksize)+1;
		m_chunks.assign(chunks_x,std::vector<sf::VertexArray>(chunks_y,sf::VertexArray(sf::Quads)));//ready up empty 2d arrays
		for(int iy=0;iy<map_y;++iy)
		{
			for(int ix=0;ix<map_x;++ix)
			{
				gloader->AppendTile(ix,iy,m_chunks[ix/chunksize][iy/chunksize]);
			}
		}
	}

Example implementation of TileLoader:

	#include "TileLoader.h"
	#include "StaticTiledMap.h"

	#include <SFML/System/Vector2.hpp>
	#include <SFML/Graphics/RenderWindow.hpp>
	#include <SFML/Graphics/View.hpp>
	#include <SFML/Graphics/Vertex.hpp>
	#include <SFML/Window/Event.hpp>
	#include <SFML/Window/Keyboard.hpp>

	class ExampleLoader : public TileLoader
	{
	public:
		ExampleLoader(void)
		{
			m_mapdata.MapX=100;
			m_mapdata.MapY=100;
			m_mapdata.TextureName="JustBrick.tga";//a simple 32x32 seamless image of brick 
		}
		virtual void AppendTile(int gx,int gy,sf::VertexArray& garr)
		{
			sf::Vertex ver;
			ver.position=sf::Vector2f(gx*32.f,gy*32.f);
			ver.texCoords=sf::Vector2f(0.f,0.f);
			garr.append(ver);

			ver.position=sf::Vector2f(gx*32.f+32.f,gy*32.f);
			ver.texCoords=sf::Vector2f(32.f,0.f);
			garr.append(ver);

			ver.position=sf::Vector2f(gx*32.f+32.f,gy*32.f+32.f);
			ver.texCoords=sf::Vector2f(32.f,32.f);
			garr.append(ver);

			ver.position=sf::Vector2f(gx*32.f,gy*32.f+32.f);
			ver.texCoords=sf::Vector2f(0.f,32.f);
			garr.append(ver);
		}
	};

	int main(int argc, char* argv[])
	{	
		sf::RenderWindow app(sf::VideoMode(600,600),"Tilemap Example");
		app.setVerticalSyncEnabled(true);
		sf::View cam=app.getDefaultView();

		StaticTiledMap testmap;
		ExampleLoader example;
		testmap.LoadFrom(&example);
		while(app.isOpen())
		{
			sf::Event eve;
			while(app.pollEvent(eve))
				if(eve.type==sf::Event::Closed)
					app.close();

			if(sf::Keyboard::isKeyPressed(sf::Keyboard::Q))
				cam.zoom(1.05f);

			if(sf::Keyboard::isKeyPressed(sf::Keyboard::W))
				cam.move(0.f,-10.f);

			if(sf::Keyboard::isKeyPressed(sf::Keyboard::E))
				cam.zoom(0.95f);

			if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
				cam.move(-10.f,0.f);

			if(sf::Keyboard::isKeyPressed(sf::Keyboard::S))
				cam.move(0.f,10.f);

			if(sf::Keyboard::isKeyPressed(sf::Keyboard::D))
				cam.move(10.f,0.f);

			app.setView(cam);
			app.clear();
			app.draw(testmap);
			app.display();
		}
	}

Clone this wiki locally