diff --git a/cocos2dx/cocos2dx-uphone.vcproj b/cocos2dx/cocos2dx-uphone.vcproj index bea617417d..3b87826d25 100644 --- a/cocos2dx/cocos2dx-uphone.vcproj +++ b/cocos2dx/cocos2dx-uphone.vcproj @@ -452,6 +452,10 @@ RelativePath=".\include\CCTileMapAtlas.h" > + + @@ -772,6 +776,10 @@ RelativePath=".\tileMap_parallax_nodes\CCTileMapAtlas.cpp" > + + diff --git a/cocos2dx/include/CCSpriteSheet.h b/cocos2dx/include/CCSpriteSheet.h index 3d526b380a..32f9ea2cda 100644 --- a/cocos2dx/include/CCSpriteSheet.h +++ b/cocos2dx/include/CCSpriteSheet.h @@ -169,6 +169,21 @@ protected: // all descendants: chlidren, gran children, etc... NSArray *m_pobDescendants; +protected: + /* IMPORTANT XXX IMPORTNAT: + * These 2 methods can't be part of CCTMXLayer since they call [super add...], and CCSpriteSheet#add SHALL not be called + */ + + /* Adds a quad into the texture atlas but it won't be added into the children array. + This method should be called only when you are dealing with very big AtlasSrite and when most of the CCSprite won't be updated. + For example: a tile map (CCTMXMap) or a label with lots of characgers (BitmapFontAtlas) + */ + void addQuadFromSprite(CCSprite *sprite, unsigned int index); + /* This is the opposite of "addQuadFromSprite. + It add the sprite to the children and descendants array, but it doesn't update add it to the texture atlas + */ + CCSpriteSheet * addSpriteWithoutQuad(CCSprite*child, unsigned int z, int aTag); + }; }//namespace cocos2d diff --git a/cocos2dx/include/CCTMXLayer.h b/cocos2dx/include/CCTMXLayer.h new file mode 100644 index 0000000000..a7f4f5e9df --- /dev/null +++ b/cocos2dx/include/CCTMXLayer.h @@ -0,0 +1,168 @@ +/**************************************************************************** +Copyright (c) 2010 cocos2d-x.org + +http://www.cocos2d-x.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +****************************************************************************/ +#ifndef __CCTMX_LAYER_H__ +#define __CCTMX_LAYER_H__ +#include "CCTMXObjectGroup.h" +#include "CCAtlasNode.h" +#include "CCSpriteSheet.h" +#include "support/data_support/ccArray.h" +namespace cocos2d { + + class CCTMXMapInfo; + class CCTMXLayerInfo; + class CCTMXTilesetInfo; + + /** CCTMXLayer represents the TMX layer. + + It is a subclass of CCSpriteSheet. By default the tiles are rendered using a CCTextureAtlas. + If you mofify a tile on runtime, then, that tile will become a CCSprite. + The benefits of using CCSprite objects as tiles are: + - tiles (CCSprite) can be rotated/scaled/moved with a nice API + + If the layer contains a property named "cc_vertexz" with an integer (in can be positive or negative), + then all the tiles belonging to the layer will use that value as their OpenGL vertex Z for depth. + + On the other hand, if the "cc_vertexz" property has the "automatic" value, then the tiles will use an automatic vertex Z value. + Also before drawing the tiles, GL_ALPHA_TEST will be enabled, and disabled after drawin them. The used alpha func will be: + + glAlphaFunc( GL_GREATER, value ) + + "value" by default is 0, but you can change it from Tiled by adding the "cc_alpha_func" property to the layer. + The value 0 should work for most cases, but if you have tiles that are semi-transparent, then you might want to use a differnt + value, like 0.5. + + For further information, please see the programming guide: + + http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:tiled_maps + + @since v0.8.1 + */ + class CCX_DLL CCTMXLayer : public CCSpriteSheet + { + /** name of the layer */ + CCX_SYNTHESIZE(std::string, m_sLayerName, LayerName); + /** size of the layer in tiles */ + CCX_SYNTHESIZE(CGSize, m_tLayerSize, LayerSize); + /** size of the map's tile (could be differnt from the tile's size) */ + CCX_SYNTHESIZE(CGSize, m_tMapTileSize, MapTileSize); + /** pointer to the map of tiles */ + CCX_SYNTHESIZE(unsigned int*, m_pTiles, Tiles); + /** Tilset information for the layer */ + CCX_SYNTHESIZE(CCTMXTilesetInfo*, m_pTileSet, TileSet); + /** Layer orientation, which is the same as the map orientation */ + CCX_SYNTHESIZE(int, m_nLayerOrientation, LayerOrientation); + /** properties from the layer. They can be added using Tiled */ + CCX_SYNTHESIZE(StringToStringDictionary*, m_pProperties, Properties); + public: + CCTMXLayer(); + virtual ~CCTMXLayer(); + /** creates a CCTMXLayer with an tileset info, a layer info and a map info */ + static CCTMXLayer * layerWithTilesetInfo(CCTMXTilesetInfo *tilesetInfo, CCTMXLayerInfo *layerInfo, CCTMXMapInfo *mapInfo); + /** initializes a CCTMXLayer with a tileset info, a layer info and a map info */ + bool initWithTilesetInfo(CCTMXTilesetInfo *tilesetInfo, CCTMXLayerInfo *layerInfo, CCTMXMapInfo *mapInfo); + + /** dealloc the map that contains the tile position from memory. + Unless you want to know at runtime the tiles positions, you can safely call this method. + If you are going to call [layer tileGIDAt:] then, don't release the map + */ + void releaseMap(); + + /** returns the tile (CCSprite) at a given a tile coordinate. + The returned CCSprite will be already added to the CCTMXLayer. Don't add it again. + The CCSprite can be treated like any other CCSprite: rotated, scaled, translated, opacity, color, etc. + You can remove either by calling: + - [layer removeChild:sprite cleanup:cleanup]; + - or [layer removeTileAt:ccp(x,y)]; + */ + CCSprite* tileAt(CGPoint tileCoordinate); + + /** returns the tile gid at a given tile coordinate. + if it returns 0, it means that the tile is empty. + This method requires the the tile map has not been previously released (eg. don't call [layer releaseMap]) + */ + unsigned int tileGIDAt(CGPoint tileCoordinate); + + /** sets the tile gid (gid = tile global id) at a given tile coordinate. + The Tile GID can be obtained by using the method "tileGIDAt" or by using the TMX editor -> Tileset Mgr +1. + If a tile is already placed at that position, then it will be removed. + */ + void setTileGID(unsigned int gid, CGPoint tileCoordinate); + + /** removes a tile at given tile coordinate */ + void removeTileAt(CGPoint tileCoordinate); + + /** returns the position in pixels of a given tile coordinate */ + CGPoint positionAt(CGPoint tileCoordinate); + + /** return the value for the specific property name */ + const char *propertyNamed(const char *propertyName); + + /** Creates the tiles */ + void setupTiles(); + + /** CCTMXLayer doesn't support adding a CCSprite manually. + @warning addchild:z:tag: is not supported on CCTMXLayer. Instead of setTileGID:at:/tileAt: + */ + CCNode * addChild(CCNode * child, int zOrder, int tag); + // super method + void removeChild(CCNode* child, bool cleanup); + void draw(); + private: + CGPoint positionForIsoAt(CGPoint pos); + CGPoint positionForOrthoAt(CGPoint pos); + CGPoint positionForHexAt(CGPoint pos); + + CGPoint calculateLayerOffset(CGPoint offset); + + /* optimization methos */ + CCSprite* appendTileForGID(unsigned int gid, CGPoint pos); + CCSprite* insertTileForGID(unsigned int gid, CGPoint pos); + CCSprite* updateTileForGID(unsigned int gid, CGPoint pos); + + /* The layer recognizes some special properties, like cc_vertez */ + void parseInternalProperties(); + int vertexZForPos(CGPoint pos); + + // index + unsigned int atlasIndexForExistantZ(unsigned int z); + unsigned int atlasIndexForNewZ(int z); + protected: + unsigned char m_cOpacity; // TMX Layer supports opacity + + unsigned int m_uMinGID; + unsigned int m_uMaxGID; + + // Only used when vertexZ is used + int m_nVertexZvalue; + bool m_bUseAutomaticVertexZ; + float m_fAlphaFuncValue; + + // used for optimization + CCSprite *m_pReusedTile; + ccCArray *m_pAtlasIndexArray; + }; + +}// namespace cocos2d +#endif //__CCTMX_LAYER_H__ + diff --git a/cocos2dx/include/CCTMXObjectGroup.h b/cocos2dx/include/CCTMXObjectGroup.h index 8cc5e6ac24..de7c0e21dd 100644 --- a/cocos2dx/include/CCTMXObjectGroup.h +++ b/cocos2dx/include/CCTMXObjectGroup.h @@ -34,6 +34,7 @@ namespace cocos2d { typedef std::map StringToStringDictionary; typedef std::pair StringToStringPair; + /** only used in StringToStringDictionary, return "" if not found*/ const char * valueForKey(std::string key, StringToStringDictionary *dict); /** CCTMXObjectGroup represents the TMX object group. diff --git a/cocos2dx/include/CCTileMapAtlas.h b/cocos2dx/include/CCTileMapAtlas.h index 57b805688e..8b9d0d7d2b 100644 --- a/cocos2dx/include/CCTileMapAtlas.h +++ b/cocos2dx/include/CCTileMapAtlas.h @@ -56,7 +56,7 @@ namespace cocos2d { /** creates a CCTileMap with a tile file (atlas) with a map file and the width and height of each tile. The tile file will be loaded using the TextureMgr. */ - CCTileMapAtlas * tileMapAtlasWithTileFile(const char *tile, const char *mapFile, int tileWidth, int tileHeight); + static CCTileMapAtlas * tileMapAtlasWithTileFile(const char *tile, const char *mapFile, int tileWidth, int tileHeight); /** initializes a CCTileMap with a tile file (atlas) with a map file and the width and height of each tile. The file will be loaded using the TextureMgr. */ diff --git a/cocos2dx/sprite_nodes/CCSpriteSheet.cpp b/cocos2dx/sprite_nodes/CCSpriteSheet.cpp index 358c860f19..70414297af 100644 --- a/cocos2dx/sprite_nodes/CCSpriteSheet.cpp +++ b/cocos2dx/sprite_nodes/CCSpriteSheet.cpp @@ -596,4 +596,60 @@ void CCSpriteSheet::setTexture(CCTexture2D *texture) { m_pobTextureAtlas->setTexture(texture); } + +// CCSpriteSheet Extension +//implementation CCSpriteSheet (TMXTiledMapExtension) + +void CCSpriteSheet::addQuadFromSprite(CCSprite *sprite, unsigned int index) +{ + NSAssert( sprite != NULL, "Argument must be non-nil"); + /// @todo NSAssert( [sprite isKindOfClass:[CCSprite class]], @"CCSpriteSheet only supports CCSprites as children"); + + while(index >= m_pobTextureAtlas->getCapacity() || m_pobTextureAtlas->getCapacity() == m_pobTextureAtlas->getTotalQuads()) + { + this->increaseAtlasCapacity(); + } + // + // update the quad directly. Don't add the sprite to the scene graph + // + sprite->useSpriteSheetRender(this); + sprite->setAtlasIndex(index); + + ccV3F_C4B_T2F_Quad quad = sprite->getQuad(); + m_pobTextureAtlas->insertQuad(&quad, index); + + // XXX: updateTransform will update the textureAtlas too using updateQuad. + // XXX: so, it should be AFTER the insertQuad + sprite->updateTransform(); +} + +CCSpriteSheet * CCSpriteSheet::addSpriteWithoutQuad(CCSprite*child, unsigned int z, int aTag) +{ + NSAssert( child != NULL, "Argument must be non-nil"); + /// @todo NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteSheet only supports CCSprites as children"); + + // quad index is Z + child->setAtlasIndex(z); + + // XXX: optimize with a binary search + int i=0; + if (m_pobDescendants && m_pobDescendants->count() > 0) + { + NSMutableArray::NSMutableArrayIterator iter; + for (iter = m_pobDescendants->begin(); iter != m_pobDescendants->end(); ++iter) + { + // fast dispatch + if (!(*iter) || (*iter)->getAtlasIndex() >=z) + { + break; + } + ++i; + } + } + m_pobDescendants->insertObjectAtIndex(child, i); + + // IMPORTANT: Call super, and not self. Avoid adding it to the texture atlas array + __super::addChild(child, z, aTag); + return this; +} }//namespace cocos2d diff --git a/cocos2dx/tileMap_parallax_nodes/CCTMXLayer.cpp b/cocos2dx/tileMap_parallax_nodes/CCTMXLayer.cpp new file mode 100644 index 0000000000..a15a3db8b7 --- /dev/null +++ b/cocos2dx/tileMap_parallax_nodes/CCTMXLayer.cpp @@ -0,0 +1,632 @@ +/**************************************************************************** +Copyright (c) 2010 cocos2d-x.org + +http://www.cocos2d-x.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +****************************************************************************/ +#include "CCLayer.h" +#include "CCTMXLayer.h" +#include "CCTMXXMLParser.h" +#include "CCSprite.h" +#include "CCSpriteSheet.h" +#include "CCTextureCache.h" +#include "CGPointExtension.h" + +namespace cocos2d { + + + // CCTMXLayer - init & alloc & dealloc + CCTMXLayer * CCTMXLayer::layerWithTilesetInfo(CCTMXTilesetInfo *tilesetInfo, CCTMXLayerInfo *layerInfo, CCTMXMapInfo *mapInfo) + { + CCTMXLayer *pRet = new CCTMXLayer(); + if (pRet->initWithTilesetInfo(tilesetInfo, layerInfo, mapInfo)) + { + pRet->autorelease(); + return pRet; + } + return NULL; + } + bool CCTMXLayer::initWithTilesetInfo(CCTMXTilesetInfo *tilesetInfo, CCTMXLayerInfo *layerInfo, CCTMXMapInfo *mapInfo) + { + // XXX: is 35% a good estimate ? + CGSize size = layerInfo->m_tLayerSize; + float totalNumberOfTiles = size.width * size.height; + float capacity = totalNumberOfTiles * 0.35f + 1; // 35 percent is occupied ? + + CCTexture2D *texture = NULL; + if( tilesetInfo ) + { + texture = CCTextureCache::sharedTextureCache()->addImage(tilesetInfo->m_sSourceImage.c_str()); + } + + if (__super::initWithTexture(texture, (unsigned int)capacity)) + { + // layerInfo + m_sLayerName = layerInfo->m_sName; + m_tLayerSize = layerInfo->m_tLayerSize; + m_pTiles = layerInfo->m_pTiles; + m_uMinGID = layerInfo->m_uMinGID; + m_uMaxGID = layerInfo->m_uMaxGID; + m_cOpacity = layerInfo->m_cOpacity; + if (!layerInfo->m_pProperties && layerInfo->m_pProperties->size()>0) + { + StringToStringDictionary::iterator it; + for (it = layerInfo->m_pProperties->begin(); it != layerInfo->m_pProperties->end(); ++it) + { + m_pProperties->insert(*it); + } + } + + // tilesetInfo + m_pTileSet = tilesetInfo; + + // mapInfo + m_tMapTileSize = mapInfo->getTileSize(); + m_nLayerOrientation = mapInfo->getOrientation(); + + // offset (after layer orientation is set); + CGPoint offset = this->calculateLayerOffset(layerInfo->m_tOffset); + this->setPosition(offset); + + m_pAtlasIndexArray = ccCArrayNew((unsigned int)totalNumberOfTiles); + + this->setContentSize(CGSizeMake(m_tLayerSize.width * m_tMapTileSize.width, m_tLayerSize.height * m_tMapTileSize.height)); + + m_bUseAutomaticVertexZ = false; + m_nVertexZvalue = 0; + m_fAlphaFuncValue = 0; + return true; + } + return false; + } + CCTMXLayer::CCTMXLayer() + :m_pTiles(NULL) + ,m_pTileSet(NULL) + ,m_pProperties(NULL) + ,m_pReusedTile(NULL) + ,m_pAtlasIndexArray(NULL) + {} + CCTMXLayer::~CCTMXLayer() + { + CCX_SAFE_RELEASE(m_pTileSet); + CCX_SAFE_RELEASE(m_pReusedTile); + if (m_pProperties) + { + m_pProperties->clear(); + delete m_pProperties; + } + + if( m_pAtlasIndexArray ) + { + ccCArrayFree(m_pAtlasIndexArray); + m_pAtlasIndexArray = NULL; + } + + if( m_pTiles ) + { + delete [] m_pTiles; + m_pTiles = NULL; + } + } + void CCTMXLayer::releaseMap() + { + if( m_pTiles ) + { + delete [] m_pTiles; + m_pTiles = NULL; + } + + if( m_pAtlasIndexArray ) + { + ccCArrayFree(m_pAtlasIndexArray); + m_pAtlasIndexArray = NULL; + } + } + + // CCTMXLayer - setup Tiles + void CCTMXLayer::setupTiles() + { + // Optimization: quick hack that sets the image size on the tileset + m_pTileSet->m_tImageSize = m_pobTextureAtlas->getTexture()->getContentSize(); + + // By default all the tiles are aliased + // pros: + // - easier to render + // cons: + // - difficult to scale / rotate / etc. + m_pobTextureAtlas->getTexture()->setAliasTexParameters(); + +// CFByteOrder o = CFByteOrderGetCurrent(); + + // Parse cocos2d properties + this->parseInternalProperties(); + + for( unsigned int y=0; y < m_tLayerSize.height; y++ ) + { + for( unsigned int x=0; x < m_tLayerSize.width; x++ ) + { + unsigned int pos = x + (unsigned int)m_tLayerSize.width * y; + unsigned int gid = m_pTiles[ pos ]; + + // gid are stored in little endian. + // if host is big endian, then swap + //if( o == CFByteOrderBigEndian ) + // gid = CFSwapInt32( gid ); + /* We support little endian.*/ + + // XXX: gid == 0 --> empty tile + if( gid != 0 ) + { + this->appendTileForGID(gid, ccp((float)x, (float)y)); + + // Optimization: update min and max GID rendered by the layer + m_uMinGID = MIN(gid, m_uMinGID); + m_uMaxGID = MAX(gid, m_uMaxGID); + } + } + } + + NSAssert( m_uMaxGID >= m_pTileSet->m_uFirstGid && + m_uMinGID >= m_pTileSet->m_uFirstGid, "TMX: Only 1 tilset per layer is supported"); + } + + // CCTMXLayer - Properties + const char *CCTMXLayer::propertyNamed(const char *propertyName) + { + return valueForKey(propertyName, m_pProperties); + } + void CCTMXLayer::parseInternalProperties() + { + // if cc_vertex=automatic, then tiles will be rendered using vertexz + + std::string vertexz = propertyNamed("cc_vertexz"); + if( vertexz != "" ) + { + if( vertexz == "automatic" ) + { + m_bUseAutomaticVertexZ = true; + } + else + { + m_nVertexZvalue = atoi(vertexz.c_str()); + } + } + + std::string alphaFuncVal = propertyNamed("cc_alpha_func"); + m_fAlphaFuncValue = (float)atof(alphaFuncVal.c_str()); + } + + // CCTMXLayer - obtaining tiles/gids + CCSprite * CCTMXLayer::tileAt(CGPoint pos) + { + NSAssert( pos.x < m_tLayerSize.width && pos.y < m_tLayerSize.height && pos.x >=0 && pos.y >=0, "TMXLayer: invalid position"); + NSAssert( m_pTiles && m_pAtlasIndexArray, "TMXLayer: the tiles map has been released"); + + CCSprite *tile = NULL; + unsigned int gid = this->tileGIDAt(pos); + + // if GID == 0, then no tile is present + if( gid ) + { + int z = (int)(pos.x + pos.y * m_tLayerSize.width); + tile = (CCSprite*) this->getChildByTag(z); + + // tile not created yet. create it + if( ! tile ) + { + CGRect rect = m_pTileSet->rectForGID(gid); + tile = new CCSprite(); + tile->initWithSpriteSheet(this, rect); + tile->setPosition(positionAt(pos)); + tile->setVertexZ((float)vertexZForPos(pos)); + tile->setAnchorPoint(CGPointZero); + tile->setOpacity(m_cOpacity); + + unsigned int indexForZ = atlasIndexForExistantZ(z); + this->addSpriteWithoutQuad(tile, indexForZ, z); + tile->release(); + } + } + return tile; + } + unsigned int CCTMXLayer::tileGIDAt(CGPoint pos) + { + NSAssert( pos.x < m_tLayerSize.width && pos.y < m_tLayerSize.height && pos.x >=0 && pos.y >=0, "TMXLayer: invalid position"); + NSAssert( m_pTiles && m_pAtlasIndexArray, "TMXLayer: the tiles map has been released"); + + int idx = (int)(pos.x + pos.y * m_tLayerSize.width); + return m_pTiles[ idx ]; + } + + // CCTMXLayer - adding helper methods + CCSprite * CCTMXLayer::insertTileForGID(unsigned int gid, CGPoint pos) + { + CGRect rect = m_pTileSet->rectForGID(gid); + + int z = (int)(pos.x + pos.y * m_tLayerSize.width); + + if( ! m_pReusedTile ) + { + m_pReusedTile = new CCSprite(); + m_pReusedTile->initWithSpriteSheet(this, rect); + } + else + { + m_pReusedTile->initWithSpriteSheet(this, rect); + } + m_pReusedTile->setPosition(positionAt(pos)); + m_pReusedTile->setVertexZ((float)vertexZForPos(pos)); + m_pReusedTile->setAnchorPoint(CGPointZero); + m_pReusedTile->setOpacity(m_cOpacity); + + // get atlas index + unsigned int indexForZ = atlasIndexForNewZ(z); + + // Optimization: add the quad without adding a child + this->addQuadFromSprite(m_pReusedTile, indexForZ); + + // insert it into the local atlasindex array + ccCArrayInsertValueAtIndex(m_pAtlasIndexArray, (void*)z, indexForZ); + + // update possible children + if (m_pChildren && m_pChildren->count()>0) + { + NSMutableArray::NSMutableArrayIterator it; + CCSprite *pSprite = NULL; + for (it = m_pChildren->begin(); it != m_pChildren->end(); ++it) + { + pSprite = (CCSprite*)(*it); + if (pSprite) + { + unsigned int ai = pSprite->getAtlasIndex(); + if ( ai >= indexForZ ) + { + pSprite->setAtlasIndex(ai+1); + } + } + } + } + m_pTiles[z] = gid; + return m_pReusedTile; + } + CCSprite * CCTMXLayer::updateTileForGID(unsigned int gid, CGPoint pos) + { + CGRect rect = m_pTileSet->rectForGID(gid); + int z = (int)(pos.x + pos.y * m_tLayerSize.width); + + if( ! m_pReusedTile ) + { + m_pReusedTile = new CCSprite(); + m_pReusedTile->initWithSpriteSheet(this, rect); + } + else + { + m_pReusedTile->initWithSpriteSheet(this, rect); + } + + m_pReusedTile->setPosition(positionAt(pos)); + m_pReusedTile->setVertexZ((float)vertexZForPos(pos)); + m_pReusedTile->setAnchorPoint(CGPointZero); + m_pReusedTile->setOpacity(m_cOpacity); + + // get atlas index + unsigned int indexForZ = atlasIndexForExistantZ(z); + m_pReusedTile->setAtlasIndex(indexForZ); + m_pReusedTile->updateTransform(); + m_pTiles[z] = gid; + + return m_pReusedTile; + } + + // used only when parsing the map. useless after the map was parsed + // since lot's of assumptions are no longer true + CCSprite * CCTMXLayer::appendTileForGID(unsigned int gid, CGPoint pos) + { + CGRect rect = m_pTileSet->rectForGID(gid); + + int z = (int)(pos.x + pos.y * m_tLayerSize.width); + + if( ! m_pReusedTile ) + { + m_pReusedTile = new CCSprite(); + m_pReusedTile->initWithSpriteSheet(this, rect); + } + else + { + m_pReusedTile->initWithSpriteSheet(this, rect); + } + + m_pReusedTile->setPosition(positionAt(pos)); + m_pReusedTile->setVertexZ((float)vertexZForPos(pos)); + m_pReusedTile->setAnchorPoint(CGPointZero); + m_pReusedTile->setOpacity(m_cOpacity); + + // optimization: + // The difference between appendTileForGID and insertTileforGID is that append is faster, since + // it appends the tile at the end of the texture atlas + unsigned int indexForZ = m_pAtlasIndexArray->num; + + // don't add it using the "standard" way. + addQuadFromSprite(m_pReusedTile, indexForZ); + + // append should be after addQuadFromSprite since it modifies the quantity values + ccCArrayInsertValueAtIndex(m_pAtlasIndexArray, (void*)z, indexForZ); + + return m_pReusedTile; + } + + // CCTMXLayer - atlasIndex and Z + int compareInts(const void * a, const void * b) + { + return ( *(int*)a - *(int*)b ); + } + unsigned int CCTMXLayer::atlasIndexForExistantZ(unsigned int z) + { + int key=z; + int *item = (int*)bsearch((void*)&key, (void*)&m_pAtlasIndexArray->arr[0], m_pAtlasIndexArray->num, sizeof(void*), compareInts); + + NSAssert( item, "TMX atlas index not found. Shall not happen"); + + int index = ((int)item - (int)m_pAtlasIndexArray->arr) / sizeof(void*); + return index; + } + unsigned int CCTMXLayer::atlasIndexForNewZ(int z) + { + // XXX: This can be improved with a sort of binary search + unsigned int i=0; + for( i=0; i< m_pAtlasIndexArray->num ; i++) + { + int val = (int) m_pAtlasIndexArray->arr[i]; + if( z < val ) + break; + } + return i; + } + + // CCTMXLayer - adding / remove tiles + void CCTMXLayer::setTileGID(unsigned int gid, CGPoint pos) + { + NSAssert( pos.x < m_tLayerSize.width && pos.y < m_tLayerSize.height && pos.x >=0 && pos.y >=0, "TMXLayer: invalid position"); + NSAssert( m_pTiles && m_pAtlasIndexArray, "TMXLayer: the tiles map has been released"); + + unsigned int currentGID = tileGIDAt(pos); + + if( currentGID != gid ) + { + // setting gid=0 is equal to remove the tile + if( gid == 0 ) + { + removeTileAt(pos); + } + + // empty tile. create a new one + else if( currentGID == 0 ) + { + insertTileForGID(gid, pos); + } + + // modifying an existing tile with a non-empty tile + else + { + unsigned int z = (unsigned int)(pos.x + pos.y * m_tLayerSize.width); + CCSprite *sprite = (CCSprite*)getChildByTag(z); + if( sprite ) + { + CGRect rect = m_pTileSet->rectForGID(gid); + sprite->setTextureRect(rect); + m_pTiles[z] = gid; + } + else + { + updateTileForGID(gid, pos); + } + } + } + } + CCNode * CCTMXLayer::addChild(CCNode * child, int zOrder, int tag) + { + NSAssert(0, "addChild: is not supported on CCTMXLayer. Instead use setTileGID:at:/tileAt:"); + return NULL; + } + void CCTMXLayer::removeChild(CCNode* node, bool cleanup) + { + CCSprite *sprite = (CCSprite*)node; + // allows removing nil objects + if( ! sprite ) + return; + + NSAssert( m_pChildren->containsObject(sprite), "Tile does not belong to TMXLayer"); + + unsigned int atlasIndex = sprite->getAtlasIndex(); + unsigned int zz = (unsigned int) m_pAtlasIndexArray->arr[atlasIndex]; + m_pTiles[zz] = 0; + ccCArrayRemoveValueAtIndex(m_pAtlasIndexArray, atlasIndex); + __super::removeChild(sprite, cleanup); + } + void CCTMXLayer::removeTileAt(CGPoint pos) + { + NSAssert( pos.x < m_tLayerSize.width && pos.y < m_tLayerSize.height && pos.x >=0 && pos.y >=0, "TMXLayer: invalid position"); + NSAssert( m_pTiles && m_pAtlasIndexArray, "TMXLayer: the tiles map has been released"); + + unsigned int gid = tileGIDAt(pos); + + if( gid ) + { + unsigned int z = (unsigned int)(pos.x + pos.y * m_tLayerSize.width); + unsigned atlasIndex = atlasIndexForExistantZ(z); + + // remove tile from GID map + m_pTiles[z] = 0; + + // remove tile from atlas position array + ccCArrayRemoveValueAtIndex(m_pAtlasIndexArray, atlasIndex); + + // remove it from sprites and/or texture atlas + CCSprite *sprite = (CCSprite*)getChildByTag(z); + if( sprite ) + { + __super::removeChild(sprite, true); + } + else + { + m_pobTextureAtlas->removeQuadAtIndex(atlasIndex); + + // update possible children + if (m_pChildren && m_pChildren->count()>0) + { + NSMutableArray::NSMutableArrayIterator it; + CCSprite *pSprite = NULL; + for (it = m_pChildren->begin(); it != m_pChildren->end(); ++it) + { + pSprite = (CCSprite*)(*it); + if (pSprite) + { + unsigned int ai = pSprite->getAtlasIndex(); + if ( ai >= atlasIndex ) + { + pSprite->setAtlasIndex(ai-1); + } + } + } + } + } + } + } + + /** Possible oritentations of the TMX map */ + enum + { + /** Orthogonal orientation */ + CCTMXOrientationOrtho, + + /** Hexagonal orientation */ + CCTMXOrientationHex, + + /** Isometric orientation */ + CCTMXOrientationIso, + };/// @todo to be deleted + //CCTMXLayer - obtaining positions, offset + CGPoint CCTMXLayer::calculateLayerOffset(CGPoint pos) + { + CGPoint ret = CGPointZero; + switch( m_nLayerOrientation ) + { + case CCTMXOrientationOrtho: + ret = ccp( pos.x * m_tMapTileSize.width, -pos.y *m_tMapTileSize.height); + break; + case CCTMXOrientationIso: + ret = ccp( (m_tMapTileSize.width /2) * (pos.x - pos.y), + (m_tMapTileSize.height /2 ) * (-pos.x - pos.y) ); + break; + case CCTMXOrientationHex: + NSAssert(CGPoint::CGPointEqualToPoint(pos, CGPointZero), "offset for hexagonal map not implemented yet"); + break; + } + return ret; + } + CGPoint CCTMXLayer::positionAt(CGPoint pos) + { + CGPoint ret = CGPointZero; + switch( m_nLayerOrientation ) + { + case CCTMXOrientationOrtho: + ret = positionForOrthoAt(pos); + break; + case CCTMXOrientationIso: + ret = positionForIsoAt(pos); + break; + case CCTMXOrientationHex: + ret = positionForHexAt(pos); + break; + } + return ret; + } + CGPoint CCTMXLayer::positionForOrthoAt(CGPoint pos) + { + float x = pos.x * m_tMapTileSize.width + 0.49f; + float y = (m_tLayerSize.height - pos.y - 1) * m_tMapTileSize.height + 0.49f; + return ccp(x,y); + } + CGPoint CCTMXLayer::positionForIsoAt(CGPoint pos) + { + float x = m_tMapTileSize.width /2 * ( m_tLayerSize.width + pos.x - pos.y - 1) + 0.49f; + float y = m_tMapTileSize.height /2 * (( m_tLayerSize.height * 2 - pos.x - pos.y) - 2) + 0.49f; + return ccp(x, y); + } + CGPoint CCTMXLayer::positionForHexAt(CGPoint pos) + { + float diffY = 0; + if( (int)pos.x % 2 == 1 ) + diffY = -m_tMapTileSize.height/2 ; + + float x = pos.x * m_tMapTileSize.width*3/4 + 0.49f; + float y = (m_tLayerSize.height - pos.y - 1) * m_tMapTileSize.height + diffY + 0.49f; + return ccp(x,y); + } + int CCTMXLayer::vertexZForPos(CGPoint pos) + { + int ret = 0; + unsigned int maxVal = 0; + if( m_bUseAutomaticVertexZ ) + { + switch( m_nLayerOrientation ) + { + case CCTMXOrientationIso: + maxVal = (unsigned int)(m_tLayerSize.width + m_tLayerSize.height); + ret = (int)(-(maxVal - (pos.x + pos.y))); + break; + case CCTMXOrientationOrtho: + ret = (int)(-(m_tLayerSize.height-pos.y)); + break; + case CCTMXOrientationHex: + NSAssert(0, "TMX Hexa zOrder not supported"); + break; + default: + NSAssert(0, "TMX invalid value"); + break; + } + } + else + { + ret = m_nVertexZvalue; + } + return ret; + } + + // CCTMXLayer - draw + void CCTMXLayer::draw() + { + if( m_bUseAutomaticVertexZ ) + { + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, m_fAlphaFuncValue); + } + + __super::draw(); + + if( m_bUseAutomaticVertexZ ) + { + glDisable(GL_ALPHA_TEST); + } + } + + +}// namespace cocos2d + diff --git a/cocos2dx/tileMap_parallax_nodes/CCTileMapAtlas.cpp b/cocos2dx/tileMap_parallax_nodes/CCTileMapAtlas.cpp index 5347c2a01a..8cee1f2bbc 100644 --- a/cocos2dx/tileMap_parallax_nodes/CCTileMapAtlas.cpp +++ b/cocos2dx/tileMap_parallax_nodes/CCTileMapAtlas.cpp @@ -33,7 +33,7 @@ namespace cocos2d { CCTileMapAtlas * CCTileMapAtlas::tileMapAtlasWithTileFile(const char *tile, const char *mapFile, int tileWidth, int tileHeight) { CCTileMapAtlas *pRet = new CCTileMapAtlas(); - if (initWithTileFile(tile, mapFile, tileWidth, tileHeight)) + if (pRet->initWithTileFile(tile, mapFile, tileWidth, tileHeight)) { pRet->autorelease(); return pRet;