• Create Account

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.

11 replies to this topic

#1MoBaT  Members   -  Reputation: 115

Like
0Likes
Like

Posted 07 February 2013 - 10:29 PM

I cannot grasp the concept of the map loading all around the player. For example if the map loads 10 to the left of the player, 10 right, 10 up, and 10 down, I cannot think of a way to incorporate that into my code. I'm having a brain fart.

         int xcoord = player->xcoord; // Players x coord will be the original coord * (TILE_SIZE * zoom)
int ycoord = player->ycoord; // Players x coord will be the original coord * (TILE_SIZE * zoom)
//Rectangles
SDL_Rect offset;
SDL_Rect offset2;

// Loop through laters
for (int i = 0; i < map->GetNumLayers(); ++i) {
const Tmx::Layer *layer = map->GetLayer(i);

// TILE_SIZE = 32, ZOOM = 2.0;
// Loop through tile number in layers. Tile #(2, 1) on the map would be 2 * (TILE_SIZE * ZOOMSCALE) down the x and 1 * (TILE_SIZE * ZOOMSCALE) down the y;
// 0 1 2 3 4
// 1
// 2
// 3
// 4

// Loop through tiles from tile (0, 0) to (20, 20);
for (int y = 0; y < 20; ++y)
{
for (int x = 0; x < 20; ++x)
{
// Get tileID
int TileID = layer->GetTileId(x, y);

if (TileID != 0) {

int tilesetID = map->GetLayer(i)->GetTileTilesetIndex(x, y);

int margin = map->GetTileset(tilesetID)->GetMargin() * ZOOMSCALE;
int spacing = map->GetTileset(tilesetID)->GetSpacing() * ZOOMSCALE;

int TileZoom = (TILE_SIZE * ZOOMSCALE);
int TilesWidth = tileSet[tilesetID]->w / TileZoom;

int tileRow = TileID % TilesWidth;
int  tileColumn = ((TileID - tileRow) / TilesWidth);

// Set tileset tile X, Y
offset.x = ((tileRow * TileZoom) + tileRow * margin) + spacing;
offset.y = ((tileColumn * TileZoom) + tileColumn * margin) + spacing;
offset.w = TileZoom;
offset.h = TileZoom;

//Where to draw on gamemap
offset2.x = x * TileZoom;
offset2.y = y * TileZoom;
offset2.w = TileZoom;
offset2.h = TileZoom;

//Blitz.
SDL_BlitSurface(tileSet[tilesetID], &offset, Surf_Display, &offset2);

}
}
}
}


#2Servant of the Lord  Crossbones+   -  Reputation: 18482

Like
2Likes
Like

Posted 07 February 2013 - 10:52 PM

In my game, I have the map broken into 'chunks' of 20x20 tiles.

If the player is on Chunk(0,0), then the game loads chunks (0,0), (0,1), (1,1), and etc... It loads the current chunk the player is on, and each chunk around the player.

Every time a player crosses a chunk boundry, all the current unneeded chunks are the discarded, and the new chunks are loaded, but I hold onto the existing chunks that were already loaded and don't discard them if they are still within one chunk of the player. (For simplicity, just discard every chunk and reload every new chunk around the player first, then rewrite it to try and preserve the chunks that don't need to be discarded and reloaded)

But the map has to already be broken up into seperate files to stream it this way.

Edited by Servant of the Lord, 07 February 2013 - 10:54 PM.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames -

[Need web hosting? I personally like A Small Orange]

#3MoBaT  Members   -  Reputation: 115

Like
0Likes
Like

Posted 07 February 2013 - 11:09 PM

So basically you have map files from like 1 - 100 let's say and each file is 20 x 20 tiles. Then you load the maps depending on the area they're at.

What I tried doing was something like:

for (int y = playerYTILE - 10; y < playerYTILE + 20; ++y)
{
for (int x = playerYTILE - 10; x < playerYTILE + 10; ++x)
{
//CODE
}
}



But when I was walking, my character was moving faster than the map loading itself, which does not make sense to me since it uses the tile it's on. I could implement a chunk system, but spent some time trying to get this method working.

Edited by MoBaT, 07 February 2013 - 11:10 PM.

#4Servant of the Lord  Crossbones+   -  Reputation: 18482

Like
6Likes
Like

Posted 08 February 2013 - 12:36 AM

I'm not sure I fully understand what method you're using. You're talking about loading maps, but it looks like you are showing code for drawing maps.

You said, "so basically you have map files from like 1-100". Well, that might work for a linear side-scrolling game, but my game is a 2D RPG, so my chunks have to have 2D numbers, like chunk (0,0), and chunk (0,1).

Let's imagine for a second that there are who knows how many areas in the game, and each 'Area' is identified by a string, like "Great Forest".

Now let's imagine that each area (for simplicity) is broken into 10 by 10 chunks, and each chunk is 20 by 20 (or 10x10 or whatever) tiles.

So if the player is at "Green Forest" in chunk (-4, 3), on tile (15, 12), then that means chunk (-4, 3) is loaded and each chunk around that:

If the player moves over a chunk, then some chunks need to be streamed out, and other chunks need to be streamed in.

If you could explain the method you're currently using in more detail, perhaps I could find the problem. All I currently see is two for() loops with nothing in them that could be doing almost anything. Your previous code post looked more like drawing the map and not loading the map. I'm not exactly sure what part of your code you are having difficulty with? Perhaps you have your drawing code too tightly integrated into your loading code and it's hard for you to separate them? The loading of the map should not be in the same functions as the drawing of the map.

Edited by Servant of the Lord, 08 February 2013 - 12:37 AM.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames -

[Need web hosting? I personally like A Small Orange]

#5MoBaT  Members   -  Reputation: 115

Like
0Likes
Like

Posted 08 February 2013 - 10:51 PM

I'm not sure I fully understand what method you're using. You're talking about loading maps, but it looks like you are showing code for drawing maps.
You said, "so basically you have map files from like 1-100". Well, that might work for a linear side-scrolling game, but my game is a 2D RPG, so my chunks have to have 2D numbers, like chunk (0,0), and chunk (0,1).
Let's imagine for a second that there are who knows how many areas in the game, and each 'Area' is identified by a string, like "Great Forest".
Now let's imagine that each area (for simplicity) is broken into 10 by 10 chunks, and each chunk is 20 by 20 (or 10x10 or whatever) tiles.
So if the player is at "Green Forest" in chunk (-4, 3), on tile (15, 12), then that means chunk (-4, 3) is loaded and each chunk around that:

If you could explain the method you're currently using in more detail, perhaps I could find the problem. All I currently see is two for() loops with nothing in them that could be doing almost anything. Your previous code post looked more like drawing the map and not loading the map. I'm not exactly sure what part of your code you are having difficulty with? Perhaps you have your drawing code too tightly integrated into your loading code and it's hard for you to separate them? The loading of the map should not be in the same functions as the drawing of the map.

Sorry I'm not being that clear. I mean I want to draw the map around my player similar to what you showed above in the images but using the code I have provided above. That's what my two for loops were above in my second post. I tried getting the characters position and then loading 10 tiles before him and 10 tiles after him. Same thing for the y axis.

#6Servant of the Lord  Crossbones+   -  Reputation: 18482

Like
2Likes
Like

Posted 09 February 2013 - 02:00 AM

Something like this?

const int minX = 0;
const int maxX = ENTIRE_MAP_WIDTH;

const int minY = 0;
const int maxY = ENTIRE_MAP_HEIGHT;

const int showWidth = 20;
const int showHeight = 20;

//...

//Starting point of tiles.
int startX = player->xcoord - (showWidth / 2);
int startY = player->ycoord - (showHeight / 2);

//Keep camera in bounds of the map.
if(startX < minX) startX = minX;
if((startX + showWidth) > maxX) startX = (maxX - showWidth);

if(startY < minY) startX = minY;
if((startY + showWidth) > maxY) startY = (maxY - showWidth);

//Ending point of tiles.
int endX = (startX + ENTIRE_MAP_WIDTH);
int endY = (startY + ENTIRE_MAP_HEIGHT);

for(int y = startY; y < endY; ++y)
{
for(int x = startX; x < endX; ++x)
{

}
}
I added more variables than necessary, to illustrate how it works.
It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames -

[Need web hosting? I personally like A Small Orange]

#7MoBaT  Members   -  Reputation: 115

Like
0Likes
Like

Posted 09 February 2013 - 12:46 PM

Finally, I tried doing something similar but probably failed at it. I got it working with this code. Thanks for your hard work, I appreciate it. I have one more questions involving the camera. Since the code now works and draws the map around the character, how would I got by implementing the camera so the player stays on the middle of the screen at all times in my 800 x 600 windows. I thought of just going to my player render code and subtracting the offset the character was at it can stay in the middle, but that sounds like a dirty way.


const int minX = 0;
const int maxX = map->GetHeight();

const int minY = 0;
const int maxY = map->GetHeight();

const int showWidth = 15;
const int showHeight = 13;

//Starting point of tiles.
int startX = (MapX / (TILE_SIZE * ZOOMSCALE) - (showWidth / 2));
if (startX <= 0) startX = 0;
int startY = (MapY / (TILE_SIZE * ZOOMSCALE))- (showHeight / 2);
if (startY <= 0) startY = 0;

//Keep camera in bounds of the map.
if(startX < minX) startX = minX;
if((startX + showWidth) > maxX) startX = (maxX - showWidth);

if(startY < minY) startX = minY;
if((startY + showWidth) > maxY) startY = (maxY - showWidth);

//Ending point of tiles.
int endX = (startX + showWidth);
int endY = (startY + showHeight); 

I can keep the map on my screen by replacing the offset in my loop from:

    offset2.x = x * TileZoom;
offset2.y =y * TileZoom;
offset2.w = TileZoom;
offset2.h = TileZoom;

to

    offset2.x = (x - startX) * TileZoom;
offset2.y =(y - startY) * TileZoom;
offset2.w = TileZoom;
offset2.h = TileZoom;

Edited by MoBaT, 09 February 2013 - 01:21 PM.

#8MoBaT  Members   -  Reputation: 115

Like
0Likes
Like

Posted 09 February 2013 - 01:18 PM

Accidently posted twice.

Edited by MoBaT, 09 February 2013 - 01:19 PM.

#9Servant of the Lord  Crossbones+   -  Reputation: 18482

Like
1Likes
Like

Posted 10 February 2013 - 12:22 PM

In the code I posted above, this:
//Starting point of tiles.
int startX = player->xcoord - (showWidth / 2);
int startY = player->ycoord - (showHeight / 2);

//<snipped>

//Ending point of tiles.
int endX = (startX + showWidth); //Whoops, meant to do 'showWidth' not 'ENTIRE_MAP_WIDTH' in my previous post.
int endY = (startY + showHeight);
Gets the 20x20 (or whatever) visible portion of the map centered around the player.

And this:
//Keep camera in bounds of the map.
if(startX < minX) startX = minX;
if((startX + showWidth) > maxX) startX = (maxX - showWidth);

if(startY < minY) startX = minY;
if((startY + showWidth) > maxY) startY = (maxY - showWidth);
Uncenters the 20x20 portion, if the player is at the edge of the map.

You do the exact same thing with the camera as you do with the visible map tiles.

const int minX = 0;
const int maxX = map->GetWidthInPixels();

const int minY = 0;
const int maxY = map->GetHeightInPixels();

//Starting point of camera.
int startX = ConvertFromTilesToPixels(player->xcoord) - (cameraWidth / 2);
int startY = ConvertFromTilesToPixels(player->ycoord) - (cameraHeight / 2);

//Keep camera in bounds of the map.
if(startX < minX) startX = minX;
if((startX + showWidth) > maxX) startX = (maxX - showWidth);

if(startY < minY) startX = minY;
if((startY + showWidth) > maxY) startY = (maxY - showWidth);

//Ending point of camera.
int endX = (startX + cameraWidth);
int endY = (startY + cameraHeight);
If you want the camera *always* centered, even when it means the camera shows beyond the edge of the map (into blackness), then just remove the boundary checks.
It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames -

[Need web hosting? I personally like A Small Orange]

#10MoBaT  Members   -  Reputation: 115

Like
0Likes
Like

Posted 10 February 2013 - 04:24 PM

I understood the code you gave me and it worked fine with displaying the map around the character but what I meant was this:

Now the code you gave me displays the map around the character but the character dosent stay on my screen and can move out of the bounds becuase of the map loading around the character.

    offset2.x = (x - startX) * TileZoom;
offset2.y =(y - startY) * TileZoom;
offset2.w = TileZoom;
offset2.h = TileZoom;


The map stays within my 800 x 600 screen but the character can venture off and it's not in the middle.

Will I have to do some calculations for the player so it can stay in the middle of my screen? Let me just show my whole code instead of a partial bit.


void Map::OnRender(SDL_Surface* Surf_Display, int MapX, int MapY)
{
//Rectangles
SDL_Rect offset;
SDL_Rect offset2;

// Loop through laters
for (int i = 0; i < map->GetNumLayers(); ++i) {
const Tmx::Layer *layer = map->GetLayer(i);

const int maxX = map->GetHeight();
const int maxY = map->GetHeight();
const int showWidth = 15;
const int showHeight = 13;

//Starting point of tiles.
int startX = (MapX / (TILE_SIZE * ZOOMSCALE) - (showWidth / 2));
if (startX <= 0) startX = 0;
int startY = (MapY / (TILE_SIZE * ZOOMSCALE))- (showHeight / 2);
if (startY <= 0) startY = 0;

//Keep camera in bounds of the map.
if(startX < 0) startX = 0;
if((startX + showWidth) > maxX) startX = (maxX - showWidth);

if(startY < 0) startX = 0;
if((startY + showWidth) > maxY) startY = (maxY - showWidth);

//Ending point of tiles.
int endX = (startX + showWidth);
int endY = (startY + showHeight);

for (int y = startY; y < endY; ++y)
{
for (int x = startX; x < endX; ++x)
{
// Get tileID
int TileID = layer->GetTileId(x, y);

if (TileID != 0) {

int tilesetID = map->GetLayer(i)->GetTileTilesetIndex(x, y);

int margin = map->GetTileset(tilesetID)->GetMargin() * ZOOMSCALE;
int spacing = map->GetTileset(tilesetID)->GetSpacing() * ZOOMSCALE;

int TileZoom = (TILE_SIZE * ZOOMSCALE);
int TilesWidth = tileSet[tilesetID]->w / TileZoom;

int tileRow = TileID % TilesWidth;
int  tileColumn = ((TileID - tileRow) / TilesWidth);

// Set tileset tile X, Y
offset.x = ((tileRow * TileZoom) + tileRow * margin) + spacing;
offset.y = ((tileColumn * TileZoom) + tileColumn * margin) + spacing;
offset.w = TileZoom;
offset.h = TileZoom;

//Where to draw on gamemap
offset2.x = (x - startX) * TileZoom;
offset2.y =(y - startY) * TileZoom;
offset2.w = TileZoom;
offset2.h = TileZoom;

//Blitz.
SDL_BlitSurface(tileSet[tilesetID], &offset, Surf_Display, &offset2);

}
}
}
}
}


Edited by MoBaT, 10 February 2013 - 04:25 PM.

#11Servant of the Lord  Crossbones+   -  Reputation: 18482

Like
0Likes
Like

Posted 10 February 2013 - 11:11 PM

Let's separate some mental concepts here:
The map and player data should be separate from the view.
Which means, the collision data of the player should never depend upon the view.

Regardless of how big your screen is, it shouldn't effect the logic of the game data.

Logically speaking, you have four things:
- The map
- The player's avatar
- The camera

This gives you two possibilities of appearance (at least that we care about):
A) The camera is always centered on the player, even if it means showing the black background.
B) The camera is always centered on the player, except when the player gets near the edge of the map, then the camera stays as centered as possible without showing any blackness.

If you want A, and not B, just comment out these lines:
//Keep camera in bounds of the map.
//if(startX < 0) startX = 0;
//if((startX + showWidth) > maxX) startX = (maxX - showWidth);

//if(startY < 0) startX = 0;
//if((startY + showWidth) > maxY) startY = (maxY - showWidth);

To make sure the player doesn't walk outside the map boundries, you have to check that when you receive player input:
on-event:
If-left-key:
move-player-left
If-player-is-outside-map
keep-player-inside-map-boundries


Maybe to handle that, after getting input each frame, you might call something like this:
player->KeepWithin(map->GetWidth(), map->GetHeight);

Player::KeepWithin(unsigned int width, unsigned int height)
{
if(playerX < 0) playerX = 0;
if((playerX + playerWidth) > width) playerX = (width - playerWidth);
if(playerY < 0) playerY = 0;
if((playerX + playerHeight) > height)  playerY = (height - playerHeight);
}

Also, your code has an unrelated mistake here: (unless your map is square)
const int maxX = map->GetHeight(); //<--This should be map->GetWidth()
const int maxY = map->GetHeight();
That's probably what's causing the blackness to be visible in your first screenshot.
Ofcourse, if you commented out the lines mentioned above, then you'll get blackness anyway, anytime the player walks near the edge of the map.

Also, these shouldn't have "magic numbers" typed directly into the code.
const int showWidth = 15;
const int showHeight = 13;
What if you change the screen size later? It should be something like:
const int showWidth = (screen->GetWidth() / TILE_WIDTH) + 1;
const int showHeight = (screen->GetHeight() / TILE_WIDTH) + 1;
That way, if you later change the screen width, this part of the code automatically works. Also, if you let users choose their own screen size, it'll work for most of their screen sizes automatically.

Edited by Servant of the Lord, 10 February 2013 - 11:20 PM.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames -

[Need web hosting? I personally like A Small Orange]

#12Servant of the Lord  Crossbones+   -  Reputation: 18482

Like
1Likes
Like

Posted 10 February 2013 - 11:37 PM

Ah, I see where you're handling the camera. I'd suggest renaming 'offset1' to 'tilesheetOffset', and 'offset2' to 'cameraOffset'.

Try separating out the map drawing information from the camera information, and calculate the entire map's offset outside of the function itself.

Map::OnRender(SDL_Surface* Surf_Display, int drawX, int drawY);

map->OnRender(screen, (0 - camera.x), (0 - camera.y));

(Assuming you want to start drawing the map at pixel 0,0 normally)

If you actually have zooming, then inside the map function you'll need to apply your zoom to the 'drawX' and 'drawY' before using those variables to position your tiles.

Then you can go like this:

//Center the camera on the player.
camera.x = player.x + (player.width / 2);
camera.y = player.y + (player.height / 2);

//Draw the map.
map->OnRender(screen, (0 - camera.x), (0 - camera.y));

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames -

[Need web hosting? I personally like A Small Orange]

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