I have used a segmented map approach in the past. This works for side scrolling maps where you simply add another piece onto the side. It also works for larger maps where side and bottom or top pieces are required.
An added benefit of using this approach is the ability, if desired, to randomize your map sections every time a new map is loaded. This eliminates the "same old map" for a specific level.
For the code example below I am using identical sized map sections with a 1280 width placed side by side. Here are the steps involved:
Create identical sized map sections in the Tiled app. Give each map section a unique name ending in a number. Something like this: MyMap-0, MyMap-1, MyMap-2 and so on.
Create a mutable array which will hold your map sections. Let's call it mapSectionsArray
.
Create a float ivar called mapOffsetX
and set its initial value to zero.
If you are looking to create a static map with 10 sections, use this code:
for (int i = 0; i < 10; i++) {
NSString *myString = [NSString stringWithFormat:@"MyMap-%d.tmx",i];
JSTileMap *tiledMap = [JSTileMap mapNamed:myString];
tiledMap.position = CGPointMake(mapOffsetX, 0);
tiledMap.zPosition = 100;
[worldNode addChild:tiledMap];
[mapSectionsArray addObject:tiledMap];
mapOffsetX += 1280;
}
If you are looking to create a random map with 10 sections, use this code:
// create an array with the total number of sections created.
// in this example I have created 30 sections in total
// I make sure not to load the same section twice
NSMutableArray *myArray = [[NSMutableArray alloc] initWithObjects:@"0", @"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"10", @"11", @"12", @"13", @"14",@"15", @"16", @"17", @"18", @"19", @"20", @"21", @"22", @"23", @"24", @"25", @"26", @"27", @"28", @"29", nil];
for (int i = 0; i < 10; i++) {
int r1 = arc4random() % [myArray count];
NSString *myString = [NSString stringWithFormat:@"MyMap-%@.tmx",[myArray objectAtIndex:r1]];
JSTileMap *tiledMap = [JSTileMap mapNamed:myString];
tiledMap.position = CGPointMake(mapOffsetX, 0);
tiledMap.zPosition = 100;
[worldNode addChild:tiledMap];
[mapSectionsArray addObject:tiledMap];
mapOffsetX += 1280;
[myArray removeObjectAtIndex:r1];
}
To load items in a map's object layer you can do this:
-(void)loadMapObjects {
float xOffset;
xOffset = 0;
for (int i = 0; i < [mapSectionsArray count]; i++) {
TMXObjectGroup *group = [[mapSectionsArray objectAtIndex:i] groupNamed:@"ObjectLayerName"];
NSArray *arrayObjects = [group objectsNamed:@"objectName"];
for (NSDictionary *dicObj in arrayObjects) {
SKNode *myNode = [SKNode node.....];
CGFloat x = [dicObj[@"x"] floatValue];
CGFloat y = [dicObj[@"y"] floatValue];
myNode.position = CGPointMake(x+xOffset, y);
[worldNode addChild:myNode];
}
xOffset += 1280;
}
In the update method you can add or remove selected map section based on your player's position like this:
// the exact values are determined by your view's size
// and map section's width
left = player.position.x - 1500;
right = player.position.x + 1500;
up = player.position.y + 1000;
down = player.position.y - 1000;
for(JSTileMap *object in mapSectionsArray) {
if((object.position.x > left) && (object.position.x < right) && (object.position.y > down) && (object.position.y < up)) {
if(object.parent == nil) {
[worldNode addChild:object];
}
} else {
if(object.parent != nil) {
[object removeFromParent];
}
}
}
Based on the player's position, a map section is either removed or added to the worldNode parent.
Another optimization is to also remove (from parent) objects with physical bodies when they are not in view. This might or might not be feasible depending on your game's design and logic.
You can handle this in the update method:
left = player.position.x - 1500;
right = player.position.x + 1500;
up = player.position.y + 1000;
down = player.position.y - 1000;
for(SKSpriteNode *object in mapItemsArray) {
if((object.position.x > left) && (object.position.x < right) && (object.position.y > down) && (object.position.y < up)) {
if(object.parent == nil) {
[worldNode addChild:object];
}
} else {
if(object.parent != nil) {
[object removeFromParent];
}
}
}