Jump to content

  • Log In with Google      Sign In   
  • Create Account

Tile Map Editor/Collision Detection


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
11 replies to this topic

#1 SharkBaitHooHaHa   Members   -  Reputation: 271

Like
0Likes
Like

Posted 11 September 2013 - 10:11 PM

Hey everybody!

I'm continuing in my SFML endeavors and I just recently finished a tile map editor. Given a tilesheet and the size of each tile, the program is able to take a "map" where one specific color corresponds to a tile. At the moment you have to hard code the values of each color-tile pair, but I will make it so that it can read in a text file that has the color pairings(if anyone has a better way of doing this I'd be interested).

Now my issue lies here. At the moment my map editor only takes one image and creates another one out of it. I can display this image as the background and move it accordingly when my character moves yadda yadda. However I do not really understand where the collision detection is going to fit in here.Is it possible to have this information handled within the tile map editor itself? I remember ages ago when I first started Java I created a Tile class and each Tile said whether or not it was "walkable". However I have foregone that step in this case so I am at a bit of a loss at what to do.
 

While reading the forums I found some people talking about using "layers". So I would draw my map in the background as usual, but at the same time have an invisible layer on top of that with which the sprites/entities interacted with. But I would like this to be automatically coupled with the tiles(e.g. grass is walkable tile, but stone is not). Also on a similar note, I am curious as to the best way of storing this "walkable" information. As some objects could become walkable later on should the user use a certain item, for example water if the user gets into a boat. I don't think this should change water's walkable property, but instead override it somehow. Again, I am lost here. 

I will include the code as an attachment as well as in the post itself for the map editor in case it is of use to the discussion.

Thanks again for the help!
-Adrian

Map.h and Map.cpp

#ifndef MAP_H
#define MAP_H

//SFML Includes
#include <SFML/Graphics.hpp>

//C++ Includes
#include <iostream>
#include <string>

using namespace std;

class Map
{
public:
	//Ctors
	Map();//Don't use this lol
	Map(string rawMapFile,string tileFile,int tSize);//Accepts map of pixels, tile spritesheet, and the size of each tile

	//Methods
	void process();//Takes map of pixels and turns it into map of tiles
	void initTileColors();//Initializes the color values for each tile
private:
	sf::Image rawMap; //The map generated by the user where each pixel represents a tile
	sf::Image tileSpriteSheet; //The map generated by the program and saved to disk for user

	int numOfTiles;//How many tiles the user has in their spriteSheet
	int tileSize; //The size of each tile in the spriteSheet, MUST BE SQUARE

	sf::Color tileColors[];
};

#endif

//SFML Includes
#include <SFML/Graphics.hpp>

//Personal Includes
#include "Map.h"

//C++ Includes
#include <iostream>
#include <string>

using namespace std;

//Ctors
Map::Map()
{

}

Map::Map(string rawMapFile,string tileFile,int tSize)
{
	//Loads the rawMap image
	if(!rawMap.loadFromFile(rawMapFile))
	{
		cout<<"Could not load file: "<<rawMapFile<<endl;
	}

	//Loads the map's tile spritesheet
	if(!tileSpriteSheet.loadFromFile(tileFile))
	{
		cout<<"Could not load file: "<<tileFile<<endl;
	}

	//Sets the size of each tile in the spritesheet and how may tiles there are
	tileSize = tSize;
}

//Methods
void Map::initTileColors()
{
	//********************************
	//REMINDER!!! Make this user customizable through text file or something
	//********************************

	tileColors[0] = sf::Color(0,0,0,255);//Ground
	tileColors[1] = sf::Color(100,100,100,255);//Stone
	tileColors[2] = sf::Color(255,255,255,255);//Sky
}
void Map::process()
{
	//Creates a blank image the size of rawMap times the size of each tile in tileSpriteSheet
	sf::Image processedMap;
	processedMap.create(rawMap.getSize().x * tileSize,rawMap.getSize().y * tileSize);

	//Takes map tile spritesheet and turns all magic purple pixels into transparent pixels
	tileSpriteSheet.createMaskFromColor(sf::Color(255,0,255,255),0);

	//Loops through every pixel on the raw map
	//When a pixel matches a color in the tileColors array it copies over the appropriate tile from spritesheet
	//To the processed image
	for(int x = 0; x < rawMap.getSize().x; x++)
	{
		for(int y = 0; y < rawMap.getSize().y; y++)
		{
			//Ground
			if(rawMap.getPixel(x,y) == tileColors[0])
			{
				processedMap.copy(tileSpriteSheet,x * tileSize,y * tileSize,sf::IntRect(0,0,tileSize,tileSize),true);
			}
			//Stone
			if(rawMap.getPixel(x,y) == tileColors[1])
			{
				processedMap.copy(tileSpriteSheet,x * tileSize,y * tileSize,sf::IntRect(8,0,tileSize,tileSize),true);			
			}
			//Sky
			if(rawMap.getPixel(x,y) == tileColors[2])
			{
				processedMap.copy(tileSpriteSheet,x * tileSize,y * tileSize,sf::IntRect(16,0,tileSize,tileSize),true);			
			}
		}
	}

	processedMap.saveToFile("C:/Users/Adrian/Desktop/NewMap.png");
	cout<<"Success"<<endl;
}

Attached Files



Sponsor:

#2 Crusable   Members   -  Reputation: 594

Like
2Likes
Like

Posted 11 September 2013 - 11:36 PM

The way I do my tiles is to have a struct with a few values: position, size, texture, and solidity. Solidity is a bool and is set when I read in an xml file. When I check for collision, I check to see if the tile the entity is about to go onto is solid, if so, then I set the entities position to the edge of the tile and offset it by one pixel. I don`t know why people do layers I have never used them. When I render all the textures, I make sure I render the map and then entities, and then any UI. I see that you are reading in an image and then iterating through each pixel and based on the pixel you set the tiles. A way you could do this when you find a pixel, for example that matches stone, set that tile to a stone tile and then it gets all the properties. The way you are doing it with the one image doesn't look like you can do collision detection because you need the positions of the tiles and such. I hope this helped.



#3 SharkBaitHooHaHa   Members   -  Reputation: 271

Like
0Likes
Like

Posted 12 September 2013 - 01:38 AM

Thanks for the reply Crusable! So are you then saying that I can use my existing method, but then instead of drawing one whole image that I should draw struct by struct?

My concern with that then is how the map would move. The movement I usually use is the one found in Pokemon games where the player remains in the center and the map moves in the background around him until he reaches the edge of the map at which point the player actually moves along the map. If I were to do this method and had a map of say 64x64 tiles would I hold all these tile structs inside of an array, and then upon prompt for movement loop through the array and change all the positions accordingly?

Wouldn't there be massive performance issues with this? How big of a map could I do without having to section areas and have the player go through loading screens?

Sorry if I have a lot of questions >_< I do not know enough about this subject as I didn't get far in my previous project but am now really starting to get into this one. Thanks for your help again!



#4 DekuTree64   Members   -  Reputation: 986

Like
2Likes
Like

Posted 12 September 2013 - 02:35 AM

You've pretty much got it. Either store collision data with the tileset, or have an invisible map layer that you can edit, or both. Each choice has its advantages and disadvantages.

 

In my map editor, I do both. The tile selection window doubles as the tileset collision editor, and the map window doubles as the map collision editor (there's a button to switch between normal and collision mode). The collision layer is initialized to all "default", which means to pull from the tileset collision data. But you can draw on it with walkable or solid to override any tiles where the collision doesn't match the visuals (actually I have more types than just solid and walkable... e.g. slopes in a side scroller).

 

Then when exporting from editor format to game format, leave all the non-detault collision tiles alone, and replace all the default ones with their corresponding values from the tileset collision data. So the game itself doesn't need the tileset collision data, because there are no more "default" tiles in any of the collision maps smile.png

 

For dynamic collision, the game code can just modify the collision map data in RAM. But you'll probably want to include some stuff in the map editor to script which tiles to modify. But that's a whole other can of worms. Does your map editor have any functionality for placing NPCs and other entities?

 

 

EDIT: Oops, I left this window open for a long time and you made another post since then... so this was a response to the original post.

 

For the movement thing (keeping the player in the center of the screen), it's just part of the map drawing code, no modifying the map data. You have a "camera position", which generally refers to the position of the top left corner of the screen in the world. Set it to (player.x - screenwidth/2, player.y - screenheight/2), to get the player in the center of the screen, and then clamp to 0 if negative, or to mapwidth - wcreenwidth if you go over that so you don't try to draw anything outside the map boundaries.

 

Then your map drawing code needs to take the camera position into account, which will depend on exactly how that map drawing code works :) But generally it's just a matter of subtracting the camera position from the tile or sprite's position.


Edited by DekuTree64, 12 September 2013 - 02:44 AM.


#5 farmdve   Members   -  Reputation: 194

Like
0Likes
Like

Posted 12 September 2013 - 05:38 AM

So wait, you guys made your own tile map editors? Doesn't that take the same amount of time as just making a 2D game? Or am I missing something, cause a tile map editor like Tiled but specifically for you would take a long time to make.

 

The way I store my map is like this

struct Tile {
    bool solid;
    int type; // what tile to use
    int y, x;
    int h, w;
 
    int sY, sX; // location on sprite sheet
};
 
typedef struct _Map {
    struct Tile **tile;
 
    int tile_num;
} _Map;

Edited by farmdve, 12 September 2013 - 05:39 AM.


#6 Karsten_   Members   -  Reputation: 1612

Like
1Likes
Like

Posted 12 September 2013 - 09:02 AM

If it is necessary, don't be afraid of creating your own tools. We have an internal tile editor but it certainly isn't in the same state as Tiled and is a long way off commercial tools. It is however more fit for purpose for our game than any of the others we had found. In other words, it would take longer to understand how the plugin system works for a 3rd party tool than it did to just knock together our own.
 
Here are a few of our tools. You can see how rough they are ;)

(One day we do aim to clean them up to release to allow modding for the actual game but polishing is pretty time consuming lol)

 

cdt.png

 

This map editor isn't actually tiling. Our days or tiling map editors ended once we realized none of us were very good at pixel art ;)

 

tdat.png

 

Both were coded in C++ and wxWidgets and it works on all our development platforms (something proprietary tools fail to do). All in all, it probably took about a day or two for each one but features were added as we needed them so I am not sure of total coding hours. Well worth it though because now we aren't relying on companies owning tools like Unity or Spine to stay in business or to keep up support of a feature smile.png


Edited by Karsten_, 12 September 2013 - 09:28 AM.

Mutiny - Open-source C++ Unity re-implementation.
Defile of Eden 2 - FreeBSD and OpenBSD binaries of our latest game.


#7 SharkBaitHooHaHa   Members   -  Reputation: 271

Like
0Likes
Like

Posted 12 September 2013 - 09:34 AM

DekuTree64:

Nice name haha :]


For dynamic collision, the game code can just modify the collision map data in RAM. But you'll probably want to include some stuff in the map editor to script which tiles to modify. But that's a whole other can of worms. Does your map editor have any functionality for placing NPCs and other entities?


No, unfortunately at the moment my tile map editor is pretty bare bones. It's not even an editor in the truest sense of the word as I don't have a GUI and whatnot for it. All I have is a console application which generates a map of tiles from a map of pixels. What do you mean here by scripting? I've been fuzzy on that term for that longest time. Are we talking about the same scripting that you can use to create cutscenes within your own engine? If so I wouldn't mind some clarifcation on how that works haha. Like do I just write a text file with a set of commands and write something to parse those commands and move game objects with it? But again, that's a different story.

 


Then when exporting from editor format to game format,

 

How would you store this map information to make it easily modifiable to changes such as camera movement, changing of tiles(my game revolves around "combat gardening" haha)?

Farmdve:


So wait, you guys made your own tile map editors? Doesn't that take the same amount of time as just making a 2D game? Or am I missing something, cause a tile map editor like Tiled but specifically for you would take a long time to make.


Not exactly, like I said, my "editor" is pretty bare bones, and pales in comparison with Tiled. At the moment I'm just having it read an image file of pixels and make a bigger image file of tiles. But that's purely for viewing, I then have to repurpose it to actually create an array or some kind of data structure to hold all the tiles in a map and display them, allow modification, etc.

I'm not too familar to structs as I've largely just used classes when I needed some kind of structure to hold various types of data. I came to C++ from Java, so I didn't reaally see much benefits to using structs over classes. May I ask then how you go about drawing your map and moving it? Are you iterating through your Map struct and displaying each Tile individually on the screen and just moving them all together that way?

Karsten_:

Those are some pretty neat graphics that you have for your environment! Were they hand drawn and scanned in? I really like the feel they have to them. Yeah your tile editor is leaps and bounds beyond mine. How did you go about developing the GUI for the editor? And I'm guessing those lines and points are for some kind of enemy/entity AI? Do you include initial placement of entities such as the player, enemies, items, and the such in your map editor? And if so how do you do this? Are you using some kind of layering system to place them on top of the tiles?


Thanks everyone for your replies!

#8 Karsten_   Members   -  Reputation: 1612

Like
0Likes
Like

Posted 12 September 2013 - 09:57 AM

SharkBaitHooHaHa,

 

Thanks smile.png. Yes, they were drawn and scanned in. The graphics started out as an experiment since in our day jobs we are all game programmers rather than artists so we wanted a quick way to be able to make "acceptable" artwork. (the coloring pencils used were also borrowed from a local restaurants "kid activity" area lol.)

We also came to this solution after seeing a large tilesheet (http://www.vgmaps.com/Atlas/SuperNES/LegendOfZelda-ALinkToThePast-LightWorld.png) and thinking to ourselves that it would just be easier to draw directly and overlay collision areas rather than place individual tiles.

 

I edited my original post so you may not have read it since. The editor itself is not a tile editor since it didn't really work well with this art style. Instead it allows us to place objects (and as layers) and then draw collision lines and blocks around impassable areas.

The lines around the trees are an example of some of the collision lines.

 

All those horrid mspaint images dotted around are indeed entity pickups and spawn points. The triangle is an example of some bot waypoints. These needed to be placed while the bots were still stupid. They are a little bit smarter now but it can still help navigate over tricky things like bridges and through door portals.

 

We made a small youtube video of the tools a while back. There is also one of an early state of the game.

 

 

If you are interested in using this kind of artwork, it is extremely simple but if you have questions, feel free to ask me about how we did certain things.

 

I would be really interested in seeing what tools others have created for their games. Much like ours is held together with wire and sellotape it would be great to see what others have hacked together ;)

So if anyone has any, please upload some screenshots / vids! (though perhaps in another thread so not to hijack this one from SharkBaitHooHaHa like I seem to have just done :/)


Edited by Karsten_, 12 September 2013 - 10:36 AM.

Mutiny - Open-source C++ Unity re-implementation.
Defile of Eden 2 - FreeBSD and OpenBSD binaries of our latest game.


#9 DekuTree64   Members   -  Reputation: 986

Like
0Likes
Like

Posted 12 September 2013 - 12:52 PM

No, unfortunately at the moment my tile map editor is pretty bare bones. It's not even an editor in the truest sense of the word as I don't have a GUI and whatnot for it. All I have is a console application which generates a map of tiles from a map of pixels. What do you mean here by scripting? I've been fuzzy on that term for that longest time. Are we talking about the same scripting that you can use to create cutscenes within your own engine? If so I wouldn't mind some clarifcation on how that works haha. Like do I just write a text file with a set of commands and write something to parse those commands and move game objects with it? But again, that's a different story.

laugh.png Sorry, scripting is one of those buzz words people toss around all the time, that's not particularly meaningful because it encompasses pretty much everything that lies in the space between artwork and programming.
 
Toward the programming end, people often write simplified programming languages (can be compiled or interpreted) with game-specific instructions, which are used for cutscenes and all sorts of gameplay stuff. It's actually easier to just write everything in C/C++ or whatever your programming language is, but scripting languages have some major advantages, particularly in large projects. It keeps compile/link times down, plus reduces the executable size and RAM usage, because you can have a separate script file for each map. You can also make it so you can edit script files while the game is running, and have the changes take effect immediately without having to restart. I prefer to work alone, and therefore don't typically get to the scale where those advantages really shine. In my current game (SNES style RPG), event functions are written in C/C++, and then each map has a function pointer table to make them referenceable by number.
 
Toward the art end, you have what I was talking about in my previous post, which is placing entities on the map, and defining some properties for them. This is actually the primary purpose of a map editor, IMO. Otherwise you can create tiled maps in a regular art program just by copy/pasting tiles, and then write a simple converter program to generate a tileset and map data from the image (of course optimizing the tileset so there are no duplicate tiles).

 

How would you store this map information to make it easily modifiable to changes such as camera movement, changing of tiles(my game revolves around "combat gardening" haha)?

In my game, each layer of the map is just a big array of 16-bit values (tile numbers). And the collision layer is an array of 8-bit values (collision types... 0 = walkable, 1 = solid, other numbers for other types). So in the game, do like

map->collision[y * map->width + x] = 1;

And voila, no more walking on the tile at position (x,y). Modifying graphical tiles is the same, but slightly more difficult because you need to figure out what tile number you want to change it to. For combat gardening, I assume you'll be planting vegetables. So say tile 13 of your tileset is a tomato plant. Then you'll probably want a table in the code somewhere that has data about each vegetable type, including what tile number to set in the map data when you plant one (13 in the case of tomatoes here). This actually won't need any form of scripting, so you should be fine with your simple map editor for now. Player tries to plant a tomato, check if the tile in front of him is dirt, if so, set it to the number from the vegetable data table.



#10 Crusable   Members   -  Reputation: 594

Like
0Likes
Like

Posted 12 September 2013 - 04:38 PM

Hello,  I am not sure if someone already said this and I am too lazy to read all the other peoples responses. Anyways, Sharkbait, the way that I, and many other people do it, is we have a camera (sf::View) and we have a massive map, and as the player moves around center the camera to the players position and then prevent it from going off the map. I have never done it the way you explained, however you could use an sf::VertexArray of sf::Quads and have the image that way, and then an array that keeps track of the tiles to see if they are solid or not. I have not tested this method, and it may not be that efficient. 

 

Camera code:

//center the camera to the players position
	m_sfCamera.setCenter(sf::Vector2f(m_Player->getPosition().x + (m_Player->getSize().x / 2), 
					 (m_Player->getPosition().y + (m_Player->getSize().y / 2))));

	//make sure the camera stays in bounds
	//left and right
	if(m_sfCamera.getCenter().x - (m_sfCamera.getSize().x / 2) <= 0) 
		m_sfCamera.setCenter(0 + (m_sfCamera.getSize().x / 2), m_sfCamera.getCenter().y);
	else if(m_sfCamera.getCenter().x + (m_sfCamera.getSize().x / 2) >=  m_Level->getSize().x) 
		m_sfCamera.setCenter(1280 - (m_sfCamera.getSize().x / 2), m_sfCamera.getCenter().y);
	//top and bottom
	if(m_sfCamera.getCenter().y - (m_sfCamera.getSize().y / 2) <= 0) 
		m_sfCamera.setCenter(m_sfCamera.getCenter().x, 0 + (m_sfCamera.getSize().y / 2));
	else if(m_sfCamera.getCenter().y + (m_sfCamera.getSize().y / 2) >=  m_Level->getSize().y) 
		m_sfCamera.setCenter(m_sfCamera.getCenter().x, 640 - (m_sfCamera.getSize().y / 2));

Edited by Crusable, 12 September 2013 - 04:43 PM.


#11 SharkBaitHooHaHa   Members   -  Reputation: 271

Like
0Likes
Like

Posted 12 September 2013 - 05:44 PM

Crusable:

Ah thanks Crusable! Yes this is what I was thinking of but in reverse. I did not know SFML had a VIew class that took care of these things, that is very nice. The method I was thinking of using was one I used back when I was developing my game in Java. I was keeping my player on the center of the screen but  moving the background around him, but I guess this is a lot more intuitive! 

DekuTree64: 

Oh okay that clarifies things a lot more. I will be experimenting with the method you suggested as well others in order to see what works best for me. Thanks a lot! :]



#12 menyo   Members   -  Reputation: 465

Like
0Likes
Like

Posted 13 September 2013 - 08:30 AM

I recently came to the conclusion that it's very handy to have a separate array for each feature of a tile. When you need to iterate over some or a lot of tiles for a specific parameter you'll just use the corresponding array. This is very efficient since you won't ever need to itterate over data you don't want. Let's say you only need to know if tiles are walk-able in an area of 100x100. If you just have to itterate over a boolean array it's a lot cheaper then itterating over a array containing stucts of several int's,shorts, bool's, etc.

 

It's just a bit more expensive if you have to go over a lot of tiles and need all the data for some reason.

 

So instead of:

struct tile {

int tileType;

bool walkable;

short layer1;

byte moveCost;

}

 

Just:

int[,] tileType;

bool[,] walkable;

short[,] layer1;

byte[,] moveCost;


Current Project: TechnoFlux read all about it on my

DEV BLOG





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS