How to account for spacing between tiles in a tile sheet - c++

My tile-sheet has tiles that are 64x64, however between each tile there is a 10px gap and i need to account for that gap when setting the texture rectangle in the image in order to draw that tile
I tried simply adding the space upon setting the texture rectangle but the image still looks distorted
for (auto y = 0u; y < map.getTileCount().y; ++y)
{
for (auto x = 0u; x < map.getTileCount().x; ++x)
{
auto posX = static_cast<float>(x * map.getTileSize().x);
auto posY = static_cast<float>(y * map.getTileSize().y);
sf::Vector2f position(posX, posY);
tileSprite.setPosition(position);
auto tileID = tiles[y * map.getTileCount().x + x].ID; //the id of the current tile
if (tileID == 0)
{
continue; //empty tile
}
auto i = 0;
while (tileID < tileSets[i].getFirstGID())
{
++i;
}
auto relativeID = tileID - tileSets[i].getFirstGID();
auto tileX = relativeID % tileSets[i].getColumnCount();
auto tileY = relativeID / tileSets[i].getColumnCount();
textureRect.left = tileX * tileSets[i].getTileSize().x; //i am guessing this is where
// i should account for the spacing
textureRect.top = tileY * tileSets[i].getTileSize().y;
tileSprite.setTexture(mTextureHolder.get(Textures::SpriteSheet));
tileSprite.setTextureRect(textureRect);
mMapTexture.draw(tileSprite);
}
}
The code itself is working and its drawing the tiles in the correct sizes, if i use a normal 64x64 tileset without any spacing the final image looks right however with spacing included the tiles are cut out.
How do i add the gap between the tiles when setting the texture rectangle?
this is how it looks:
this is how it should look:
(NOTE: The "how it should look" image is from the Tiled editor )

Removing the spaces with a python script i found and gimp fixed the problem, however if anyone knows how to account for the spacing feel free to answer as i might need it someday

Related

Zooming towards center of Camera on 2d Plane

Once again, camera zooming on a 2D-Plane. I searched a lot and know that there are similar questions, but I am obviously way too stupid to apply what I was able to find.
Basically I multiply the distance of all elements to the origin by mouseDelta, which is a double between 0.5 and 1. works fine for all elements, but since the anchor of the camera (camX, camY) are the upper left corner of the camera, the objects in the focus of the cam change their position in relation to the focus. I want to scroll "towards" the focus. Here is what I got, but it behaves really weird:
camX and camY, as mentioned, are the coordinates for the upper left of the cam.
mouseDelta is the zoom-level thats stored globally and is changed by each wheel-event.
screenX is the width of the screen/window (fullscreen anyways)
screenY is the height of the screen/window
if (newEvent.type == sf::Event::MouseWheelMoved) //zoom
{
mouseDelta += ((double)newEvent.mouseWheel.delta)/20;
if (mouseDelta > 1) { mouseDelta = 1; }
else if (mouseDelta < 0.5) { mouseDelta = 0.5; }
//resize graphics
for (int i = 0; i < core->universe->world->nodes.size(); i++) {
core->universe->world->nodes.at(i).pic->setSize(mouseDelta);
}
for (int i = 0; i < core->universe->world->links.size(); i++) {
core->universe->world->links.at(i).pic->setSize(mouseDelta);
}
camX = (camX + screenX/2) - (camX + screenX/2)*mouseDelta;
camY = (camY + screenY/2) - (camY + screenY/2)*mouseDelta;
}

SDL Infinite Tile Background

I'm trying to create a tiled background in SDL, one that scrolls and continues to scroll indefinitely.
So, I came up with some code and tested it out. It works well enough, but only can travel 1920 pixels along the x axis and 1080 along the Y.
Here's my code:
void Background::render(SDL_Renderer *renderer){
int Xoffset = 0;
int Yoffset = 0;
for(int y = 0; (y * 411) < 1080; y++){
for(int x = 0; (x * 405) < 1920; x++){
Xoffset = 0;
Yoffset = 0;
if(GameManager::GetInstance().getGlobalX() + (405 * x) + 405 < 0){
Xoffset = 1920;
}
if(GameManager::GetInstance().getGlobalY() + (411 * y) + 411 < 0){
Yoffset = 1080;
}
SDL_Rect backRect = {GameManager::GetInstance().getGlobalX() + (405 * x) + Xoffset, GameManager::GetInstance().getGlobalY() + (411 * y) + Yoffset, 405, 411};
SDL_RenderCopy(renderer, ResourceManager::GetInstance().getTexture("background"), 0, &backRect);
}
}
}
The getGlobalX() and getGlobalY() are where the object should be relative to the player.
You should be able to draw the 1920x1080 background more than once.
The algorithm would look something like this.
Draw a background starting at (-1920,0) (completely out of the screen)
Draw another copy of the background, this time starting at (0,0).
Every frame, draw both backgrounds one pixel to the right, so you'll have a scrolling illusion, the end of the background exiting the right will come out from the left.
Once your background at step 1 has come to (0,0), draw another background at (-1920,0) and keep scrolling.
So basically, you push two backgrounds to the right and keep putting one on the left every time you need to. This should be simple to code.

Tileset crop calculation C++

So I have this problem with my tileset rendering. It's a little tricky to explain but i'll do my best.
Basicly what i do is i load these "tile numbers" from TMX files and they only represent the tileset number in the tileset. So if i use a tile that is on a row below the first one i only get the number. But i want both X and Y position to calculate where i would crop the image when i display it in my game.
So my array would look something like this,
1,1,1,2,1,1,1,1,9,1,6,1,1
1,1,1,2,2,2,1,1,1,1,1,1,1
1,1,1,1,1,2,1,1,1,1,1,1,1
1,1,1,1,1,2,1,1,1,3,1,1,1
1,6,1,1,1,2,1,1,1,1,1,1,1
1,1,5,5,1,2,2,2,1,1,1,1,1
1,1,1,1,1,1,1,2,1,1,1,1,1
Tileset:
http://puu.sh/9Tv2X/7938b6abf4.png
so when i render all the tiles in the first row in this tileset, numbers 1-4 (since the tileset is 4 tiles long) it works fine but anything past that would get cropped outside the image.
So for that i need the Y position to increase and to reset the X position everytime it goes outside the tileset width which is (32 * (tileNumber - 1)) but my brain cant figure out how to do this.
tile width and height is 32.
my code for drawing the tilset:
void TileMap::Draw()
{
for (unsigned int i = 0; i < mapVector.size(); i++)
{
for (unsigned int j = 0; j < mapVector[i].size(); j++)
{
int tileY = 0;
int tileX = mapVector[i][j] - 1;
if (tileWidth * mapVector[i][j] > tilesetWidth)
{
//now what
}
if (mapVector[i][j] > 0)
{
tileSprite->SetPosition(j * tileWidth, i * tileHeight);
tileSprite->SetTextureRect(tileX * tileWidth, tileY * tileHeight, 32, 32);
tileSprite->Draw();
}
}
}
}

Determine which tile is clicked in a window

I am drawing a tilemap on a SFML renderwindow. I want to determine which tile is clicked by the user, but I just cant seem to find a solution. First of all, each tile has 32 width and height.
What i try at the moment : Get the position of the click. Loop trough the tilemap until a tile is found which position is between 100. So if I click on (100,100) the tile should begin at (96,96) but this does not seem to work.
Here is my code snippet from the function getTile(mousepos x,mousepos y)
Tile* TileMap::getTile(int x, int y)
{
Tile *t = NULL;
for(int i = 0; i < tilemap.size(); i++)
{
for(int j = 0; j < tilemap[i].size(); j++)
{
if(x > tilemap[i][j].sprite.getPosition().x
&& x < (tilemap[i][j].sprite.getPosition().x+32))
{
if(y > tilemap[i][j].sprite.getPosition().y
&& y < (tilemap[i][j].sprite.getPosition().y+32))
{
t = &tilemap[i][j];
break;
}
}
}
}
return t;
}
Based on your code, I am going to assume that you are basing your tilemap on a 2d array of Tiles: tilemap[x][y]. I am also going to assume that tilemap[0][0] is the top left tile.
There should be a much easier way to find out which tile is being clicked on instead of testing every single tile.
If you are at 100,100 and tiles are 32x32, then we can get the x and y of the tile within the tilemap by doing something as simple as:
x = 100 / 32 = 3
y = 100 / 32 = 3
Therefor the tile in your tilemap that corresponds to a mouse position of (100,100) is tilemap[3][3].

SDL - drawing 'negative' circles (Fog of War)

I have this 800x600square I want to draw to the screen. I want to 'cut' circles in it (where alpha would be 0). Basically I'm drawing this whole rectangle over a map so in these 'circles' I drew, you can see the map, otherwise you see the grey square
So, I assume you're trying to add fog of war to one of you game?
I had a small demo I made for a local University a few weeks ago to show A* pathfinding, so I thought I could add fog of war to it for you. Here's the results:
Initial map
First, you start with a complete map, totally visible
Fog
Then, I added a surface to cover the entire screen (take note that my map is smaller than the screen, so for this case I just added fog of war on the screen, but if you have scrolling, make sure it covers each map pixel 1:1)
mFogOfWar = SDL_CreateRGBSurface(SDL_HWSURFACE, in_Width, in_Height, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
SDL_Rect screenRect = {0, 0, in_Width, in_Height};
SDL_FillRect(mFogOfWar, &screenRect, 0xFF202020);
Then, you need to draw it... I added this call after drawing the game objects and before drawing the UI
DrawSurface(mFogOfWar, 0, 0);
Where
void RenderingManager::DrawSurface(SDL_Surface* in_Surface, int in_X, int in_Y)
{
SDL_Rect Dest = { in_X, in_Y, 0, 0 };
SDL_BlitSurface(in_Surface, NULL, mScreen, &Dest);
}
Which should give you the following result:
"Punch Surface"
I then created a 32 bits .png that looks like this (checkerboard shows alpha)
When rendering my main character, I added this call:
gRenderingManager.RemoveFogOfWar(int(mX) + SPRITE_X_OFFSET, int(mY) + SPRITE_Y_OFFSET);
The offset is only there to center the punch with the sprite, basically, what I'm passing to RemoveFogOfWar is the center of my sprite.
Remove Fog Of War
Now the meat of the fog of war. I did two versions, one where Fog of War is removed permanently and one where the fog of war is reset. My fog of war reset relies on my punch surface to have a contour where the alpha is reset to 0 and the fact that my character moves of less pixels than the contour contains per frame, otherwise I would keep the Rect where my punch was applied and I would refill it before drawing again the new punch.
Since I couldn't find a "multiply" blend with SDL, I decided to write a simple function that iterates on the punch surface and updates the alpha on the fog of war surface. The most important part is to make sure you stay within the bounds of your surfaces, so it takes up most of the code... there might be some crop functions but I didn't bother checking:
void RenderingManager::RemoveFogOfWar(int in_X, int in_Y)
{
const int halfWidth = mFogOfWarPunch->w / 2;
const int halfHeight = mFogOfWarPunch->h / 2;
SDL_Rect sourceRect = { 0, 0, mFogOfWarPunch->w, mFogOfWarPunch->h };
SDL_Rect destRect = { in_X - halfWidth, in_Y - halfHeight, mFogOfWarPunch->w, mFogOfWarPunch->h };
// Make sure our rects stays within bounds
if(destRect.x < 0)
{
sourceRect.x -= destRect.x; // remove the pixels outside of the surface
sourceRect.w -= sourceRect.x; // shrink to the surface, not to offset fog
destRect.x = 0;
destRect.w -= sourceRect.x; // shrink the width to stay within bounds
}
if(destRect.y < 0)
{
sourceRect.y -= destRect.y; // remove the pixels outside
sourceRect.h -= sourceRect.y; // shrink to the surface, not to offset fog
destRect.y = 0;
destRect.h -= sourceRect.y; // shrink the height to stay within bounds
}
int xDistanceFromEdge = (destRect.x + destRect.w) - mFogOfWar->w;
if(xDistanceFromEdge > 0) // we're busting
{
sourceRect.w -= xDistanceFromEdge;
destRect.w -= xDistanceFromEdge;
}
int yDistanceFromEdge = (destRect.y + destRect.h) - mFogOfWar->h;
if(yDistanceFromEdge > 0) // we're busting
{
sourceRect.h -= yDistanceFromEdge;
destRect.h -= yDistanceFromEdge;
}
SDL_LockSurface(mFogOfWar);
Uint32* destPixels = (Uint32*)mFogOfWar->pixels;
Uint32* srcPixels = (Uint32*)mFogOfWarPunch->pixels;
static bool keepFogRemoved = false;
for(int x = 0; x < destRect.w; ++x)
{
for(int y = 0; y < destRect.h; ++y)
{
Uint32* destPixel = destPixels + (y + destRect.y) * mFogOfWar->w + destRect.x + x;
Uint32* srcPixel = srcPixels + (y + sourceRect.y) * mFogOfWarPunch->w + sourceRect.x + x;
unsigned char* destAlpha = (unsigned char*)destPixel + 3; // fetch alpha channel
unsigned char* srcAlpha = (unsigned char*)srcPixel + 3; // fetch alpha channel
if(keepFogRemoved == true && *srcAlpha > 0)
{
continue; // skip this pixel
}
*destAlpha = *srcAlpha;
}
}
SDL_UnlockSurface(mFogOfWar);
}
Which then gave me this with keepFogRemoved = false even after the character had moved around
And this with keepFogRemoved = true
Validation
The important part is really to make sure you don't write outside of your pixel buffer, so watch out with negative offsets or offsets that would bring you out of the width or height. To validate my code, I added a simple call to RemoveFogOfWar when the mouse is clicked and tried corners and edges to make sure I didn't have a "off by one" problem
case SDL_MOUSEBUTTONDOWN:
{
if(Event.button.button == SDL_BUTTON_LEFT)
{
gRenderingManager.RemoveFogOfWar(Event.button.x, Event.button.y);
}
break;
}
Notes
Obviously, you don't need a 32 bits texture for the "punch", but it was the clearest way I could think of to show you how to do it. It could be done using as little as 1 bit per pixel (on / off). You can also add some gradient, and change the
if(keepFogRemoved == true && *srcAlpha > 0)
{
continue; // skip this pixel
}
To something like
if(*srcAlpha > *destAlpha)
{
continue;
}
To keep a smooth blend like this:
3 State Fog of War
I thought I should add this... I added a way to create a 3 state fog of war: visible, seen and fogged.
To do this, I simply keep the SDL_Rect of where I last "punched" the fog of war, and if the alpha is lower than a certain value, I clamp it at that value.
So, by simply adding
for(int x = 0; x < mLastFogOfWarPunchPosition.w; ++x)
{
for(int y = 0; y < mLastFogOfWarPunchPosition.h; ++y)
{
Uint32* destPixel = destPixels + (y + mLastFogOfWarPunchPosition.y) * mFogOfWar->w + mLastFogOfWarPunchPosition.x + x;
unsigned char* destAlpha = (unsigned char*)destPixel + 3;
if(*destAlpha < 0x60)
{
*destAlpha = 0x60;
}
}
}
mLastFogOfWarPunchPosition = destRect;
right before the loop where the fog of war is "punched", I get a fog of war similar to what you could have in games like StarCraft:
Now, since the "seen" fog of war is semi transparent, you will need to tweak your rendering method to properly clip "enemies" that would be in the fog, so you don't see them but you still see the terrain.
Hope this helps!