Skip to content

Instantly share code, notes, and snippets.

@rakkarage
Created August 2, 2012 17:51
Show Gist options
  • Save rakkarage/3239101 to your computer and use it in GitHub Desktop.
Save rakkarage/3239101 to your computer and use it in GitHub Desktop.
/*
* Kobold2D™ --- http://www.kobold2d.org
*
* Copyright (c) 2010-2011 Steffen Itterheim.
* Released under MIT License in Germany (LICENSE-Kobold2D.txt).
*/
#import "cocos2d.h"
enum
{
TileMapNode = 0,
};
@interface TileMapLayer : CCLayer <UIGestureRecognizerDelegate>
{
float tileMapHeightInPixels;
}
+(id) node;
@end
/*
* Kobold2D™ --- http://www.kobold2d.org
*
* Copyright (c) 2010-2011 Steffen Itterheim.
* Released under MIT License in Germany (LICENSE-Kobold2D.txt).
*/
#import "TileMapLayer.h"
#import "SimpleAudioEngine.h"
@implementation TileMapLayer
+(id) node
{
CCScene *scene = [CCScene node];
TileMapLayer *layer = [TileMapLayer node];
[scene addChild: layer];
return scene;
}
- (void)setViewpoint:(CGPoint)p at:(CGPoint)center
{
CGPoint offset = ccpSub(center, p);
CGPoint scaled = ccpMult(offset, self.scale);
self.position = scaled;
}
- (void)setViewpoint:(CGPoint)p
{
[self setViewpoint:p at:[CCDirector sharedDirector].screenCenter];
}
static CGPoint startPan = {0.0f, 0.0f};
static CGPoint touchCenter = {0.0f, 0.0f};
static CGFloat startScale = 0.0f;
static const CGFloat minScale = 0.5f;
static const CGFloat minScaleSnap = 1.0f;
static const CGFloat maxScaleSnap = 32.0f;
static const CGFloat maxScale = 32.5f;
- (void)update:(ccTime)dt
{
CCNode* node = [self getChildByTag:TileMapNode];
NSAssert([node isKindOfClass:[CCTMXTiledMap class]], @"not a CCTMXTiledMap");
CCTMXTiledMap* tileMap = (CCTMXTiledMap*)node;
KKInput *input = [KKInput sharedInput];
CCDirector *director = [CCDirector sharedDirector];
CGSize size = director.screenSize;
CGPoint center = director.screenCenter;
if (input.gesturePanBegan)
{
if (CGPointEqualToPoint(startPan, CGPointZero))
startPan = self.position;
CGPoint newPosition = ccpAdd(startPan, input.gesturePanTranslation);
// pan bounds
CGFloat maxX = size.width / 2 * self.scale;
CGFloat minX = tileMap.contentSize.width * self.scale - maxX;
CGFloat maxY = size.height / 2 * self.scale;
CGFloat minY = tileMap.contentSize.height * self.scale - maxY;
if (newPosition.x > maxX)
newPosition = ccp(maxX, newPosition.y);
if (newPosition.x < -minX)
newPosition = ccp(-minX, newPosition.y);
if (newPosition.y > maxY)
newPosition = ccp(newPosition.x, maxY);
if (newPosition.y < -minY)
newPosition = ccp(newPosition.x, -minY);
// prevent tearing
NSInteger x = newPosition.x;
NSInteger y = newPosition.y;
self.position = ccp(x, y);
}
else
{
if (!CGPointEqualToPoint(startPan, CGPointZero))
startPan = CGPointZero;
}
// the center of all touches
NSInteger touchCount = input.touches.count;
if (touchCount >= 2)
{
CGFloat x = 0.0f;
CGFloat y = 0.0f;
for (KKTouch *touch in input.touches)
{
x += touch.location.x;
y += touch.location.y;
}
touchCenter = ccp(x / touchCount, y /touchCount); // center of touches on screen
CGPoint delta = ccpSub(center, touchCenter); // is how far from actual screen center
touchCenter = ccpAdd(touchCenter, delta); // add delta to original point to center on that instead
touchCenter = [tileMap convertToNodeSpace:touchCenter]; // what is that in map coords
}
if (input.gesturePinchBegan)
{
if (startScale == 0.0f)
startScale = self.scale;
CGFloat tempScale = startScale * input.gesturePinchScale;
if (tempScale > maxScale)
{
tempScale = maxScale;
input.gesturePinchScale = maxScale / startScale;
}
else if (tempScale < minScale)
{
tempScale = minScale;
input.gesturePinchScale = minScale / startScale;
}
self.scale = tempScale;
[self setViewpoint:touchCenter];
}
else
{
if (startScale != 0.0f)
{
CGFloat newScale = (NSUInteger)(self.scale + 0.5f);
if (newScale < minScaleSnap) newScale = minScaleSnap;
else if (newScale > maxScaleSnap) newScale = maxScaleSnap;
self.scale = newScale;
[self setViewpoint:touchCenter];
startScale = 0.0f;
}
}
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
-(id) init
{
if ((self = [super init]))
{
CCTMXTiledMap *tileMap = [CCTMXTiledMap tiledMapWithTMXFile:@"orthogonal.tmx"];
tileMapHeightInPixels = tileMap.mapSize.height * tileMap.tileSize.height / CC_CONTENT_SCALE_FACTOR();
[self addChild:tileMap z:-1 tag:TileMapNode];
// Use a negative offset to set the tilemap's start position
//tileMap.position = CGPointMake(-160, -120);
// hide the event layer, we only need this information for code, not to display it
CCTMXLayer* eventLayer = [tileMap layerNamed:@"GameEventLayer"];
eventLayer.visible = NO;
CCTMXLayer* winterLayer = [tileMap layerNamed:@"WinterLayer"];
winterLayer.visible = NO;
#if KK_PLATFORM_IOS
self.isTouchEnabled = YES;
#elif KK_PLATFORM_MAC
self.isMouseEnabled = YES;
#endif
[[SimpleAudioEngine sharedEngine] preloadEffect:@"alien-sfx.caf"];
KKInput *input = [KKInput sharedInput];
input.multipleTouchEnabled = YES;
input.gesturePinchEnabled = YES;
input.pinchGestureRecognizer.cancelsTouchesInView = NO;
input.pinchGestureRecognizer.delegate = self;
input.gesturePanEnabled = YES;
input.panGestureRecognizer.cancelsTouchesInView = NO;
input.panGestureRecognizer.delegate = self;
[self scheduleUpdate];
}
return self;
}
-(CGPoint) tilePosFromLocation:(CGPoint)location tileMap:(CCTMXTiledMap*)tileMap
{
// Tilemap position must be added as an offset, in case the tilemap position is not at 0,0 due to scrolling
CGPoint pos = ccpSub(location, tileMap.position);
// scaling tileSize to Retina display size if necessary
float scaledWidth = tileMap.tileSize.width / CC_CONTENT_SCALE_FACTOR();
float scaledHeight = tileMap.tileSize.height / CC_CONTENT_SCALE_FACTOR();
// Cast to int makes sure that result is in whole numbers, tile coordinates will be used as array indices
pos.x = (int)(pos.x / scaledWidth);
pos.y = (int)((tileMap.mapSize.height * tileMap.tileSize.height - pos.y) / scaledHeight);
CCLOG(@"touch at (%.0f, %.0f) is at tileCoord (%i, %i)", location.x, location.y, (int)pos.x, (int)pos.y);
// make sure coordinates are within bounds
pos.x = fminf(fmaxf(pos.x, 0), tileMap.mapSize.width - 1);
pos.y = fminf(fmaxf(pos.y, 0), tileMap.mapSize.height - 1);
return pos;
}
-(void) centerTileMapOnTileCoord:(CGPoint)tilePos tileMap:(CCTMXTiledMap*)tileMap
{
// center tilemap on the given tile pos
CGSize screenSize = [[CCDirector sharedDirector] winSize];
CGPoint screenCenter = CGPointMake(screenSize.width * 0.5f, screenSize.height * 0.5f);
// tile coordinates are counted from upper left corner, this maps coordinates to lower left corner
tilePos.y = (tileMap.mapSize.height - 1) - tilePos.y;
// point is now at lower left corner of the screen
CGPoint scrollPosition = CGPointMake(-(tilePos.x * tileMap.tileSize.width),
-(tilePos.y * tileMap.tileSize.height));
// offset point to center of screen and center of tile
scrollPosition.x += screenCenter.x - tileMap.tileSize.width * 0.5f;
scrollPosition.y += screenCenter.y - tileMap.tileSize.height * 0.5f;
// make sure tilemap scrolling stops at the tilemap borders
scrollPosition.x = MIN(scrollPosition.x, 0);
scrollPosition.x = MAX(scrollPosition.x, -screenSize.width);
scrollPosition.y = MIN(scrollPosition.y, 0);
scrollPosition.y = MAX(scrollPosition.y, -screenSize.height);
CCLOG(@"tilePos: (%i, %i) moveTo: (%.0f, %.0f)", (int)tilePos.x, (int)tilePos.y, scrollPosition.x, scrollPosition.y);
CCAction* move = [CCMoveTo actionWithDuration:0.2f position:scrollPosition];
[tileMap stopAllActions];
[tileMap runAction:move];
}
-(CGRect) getRectFromObjectProperties:(NSDictionary*)dict tileMap:(CCTMXTiledMap*)tileMap
{
float x, y, width, height;
x = [[dict valueForKey:@"x"] floatValue] + tileMap.position.x;
y = [[dict valueForKey:@"y"] floatValue] + tileMap.position.y;
width = [[dict valueForKey:@"width"] floatValue];
height = [[dict valueForKey:@"height"] floatValue];
return CGRectMake(x, y, width, height);
}
//-(void) tilemapTouchedAt:(CGPoint)location
//{
// CCNode* node = [self getChildByTag:TileMapNode];
// NSAssert([node isKindOfClass:[CCTMXTiledMap class]], @"not a CCTMXTiledMap");
// CCTMXTiledMap* tileMap = (CCTMXTiledMap*)node;
//
// // get the position in tile coordinates from the touch location
// CGPoint tilePos = [self tilePosFromLocation:location tileMap:tileMap];
//
// // move tilemap so that touched tiles is at center of screen
// [self centerTileMapOnTileCoord:tilePos tileMap:tileMap];
//
// // Check if the touch was on water (eg. tiles with isWater property drawn in GameEventLayer)
// bool isTouchOnWater = NO;
// CCTMXLayer* eventLayer = [tileMap layerNamed:@"GameEventLayer"];
// int tileGID = [eventLayer tileGIDAt:tilePos];
//
// if (tileGID != 0)
// {
// NSDictionary* properties = [tileMap propertiesForGID:tileGID];
// if (properties)
// {
// NSString* isWaterProperty = [properties valueForKey:@"isWater"];
// isTouchOnWater = ([isWaterProperty boolValue] == YES);
// }
// }
//
// // Check if the touch was within one of the rectangle objects
// CCTMXObjectGroup* objectLayer = [tileMap objectGroupNamed:@"ObjectLayer"];
// NSAssert([objectLayer isKindOfClass:[CCTMXObjectGroup class]], @"ObjectLayer not found or not a CCTMXObjectGroup");
//
// bool isTouchInRectangle = NO;
// NSUInteger numObjects = [[objectLayer objects] count];
// for (NSUInteger i = 0; i < numObjects; i++)
// {
// NSDictionary* properties = [[objectLayer objects] objectAtIndex:i];
// CGRect rect = [self getRectFromObjectProperties:properties tileMap:tileMap];
//
// if (CGRectContainsPoint(rect, location))
// {
// isTouchInRectangle = YES;
// break;
// }
// }
//
// // decide what to do depending on where the touch was ...
// if (isTouchOnWater)
// {
// [[SimpleAudioEngine sharedEngine] playEffect:@"alien-sfx.caf"];
// }
// else if (isTouchInRectangle)
// {
// CCParticleSystem* system = [CCParticleSystemQuad particleWithFile:@"fx-explosion.plist"];
// system.autoRemoveOnFinish = YES;
// system.position = location;
// [self addChild:system z:1];
// }
// else
// {
// /*
// // get the winter layer and toggle its visibility
// CCTMXLayer* winterLayer = [tileMap layerNamed:@"WinterLayer"];
// winterLayer.visible = !winterLayer.visible;
//
// // remove the touched tile
// [winterLayer removeTileAt:tilePos];
//
// // adds a given tile
// tileGID = [winterLayer tileGIDAt:CGPointMake(0, 19)];
// [winterLayer setTileGID:tileGID at:tilePos];
// */
// }
//}
//#if KK_PLATFORM_IOS
//
//-(CGPoint) locationFromTouch:(UITouch*)touch
//{
// CGPoint touchLocation = [touch locationInView:[touch view]];
// return [[CCDirector sharedDirector] convertToGL:touchLocation];
//}
//
//-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
//{
// // get the position in tile coordinates from the touch location
// CGPoint location = [self locationFromTouch:[touches anyObject]];
// [self tilemapTouchedAt:location];
//}
//
//#elif KK_PLATFORM_MAC
//
//-(BOOL) ccMouseDown:(NSEvent*)event
//{
// CGPoint location = [[CCDirector sharedDirector] convertEventToGL:event];
// [self tilemapTouchedAt:location];
// return YES;
//}
//
//#endif
#ifdef DEBUG
-(void) drawRect:(CGRect)rect
{
// Because there is no specialized rect drawing method the rect is drawn using 4 lines
CGPoint pos1, pos2, pos3, pos4;
pos1 = CGPointMake(rect.origin.x, rect.origin.y);
pos2 = CGPointMake(rect.origin.x, rect.origin.y + rect.size.height);
pos3 = CGPointMake(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height);
pos4 = CGPointMake(rect.origin.x + rect.size.width, rect.origin.y);
ccDrawLine(pos1, pos2);
ccDrawLine(pos2, pos3);
ccDrawLine(pos3, pos4);
ccDrawLine(pos4, pos1);
}
// Draw the object rectangles for debugging and illustration purposes.
-(void) draw
{
CCNode* node = [self getChildByTag:TileMapNode];
NSAssert([node isKindOfClass:[CCTMXTiledMap class]], @"not a CCTMXTiledMap");
CCTMXTiledMap* tileMap = (CCTMXTiledMap*)node;
// get the object layer
CCTMXObjectGroup* objectLayer = [tileMap objectGroupNamed:@"ObjectLayer"];
NSAssert([objectLayer isKindOfClass:[CCTMXObjectGroup class]], @"ObjectLayer not found or not a CCTMXObjectGroup");
// make the line 3 pixels thick
glLineWidth(3.0f);
glColor4f(1, 0, 1, 1);
NSUInteger numObjects = [[objectLayer objects] count];
for (NSUInteger i = 0; i < numObjects; i++)
{
NSDictionary* properties = [[objectLayer objects] objectAtIndex:i];
CGRect rect = [self getRectFromObjectProperties:properties tileMap:tileMap];
[self drawRect:rect];
}
// show center screen position
CGSize screenSize = [[CCDirector sharedDirector] winSize];
CGPoint center = CGPointMake(screenSize.width * 0.5f, screenSize.height * 0.5f);
ccDrawCircle(center, 10, 0, 8, NO);
// reset line width & color as to not interfere with draw code in other nodes that draws lines
glLineWidth(1.0f);
glColor4f(1, 1, 1, 1);
}
#endif
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment