Member Avatar for Dave_5

Hey guys I'm finishing off a project for an assignment and have run into an error I can't resolve.

Here is a link to a video showing the error...

https://www.dropbox.com/s/25ajb0mc5p4a3a9/Tilemap%20Error.mov

The class throwing the error has been working in other projects or so I thought, but has been modified a little from the original.

Sadly I dont have the time to develop my own pathfinder now so if anyone can tell me why this is happening I'd really appreciate it.

/*
 * AStarPathfinder https://github.com/sqlboy/tiled-games
 *
 * Copyright (c) 2011 Matt Chambers
 *
 * 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.
 *
 */

#import "AStar.h"
#import "cocos2d.h"
#import "SimpleAudioEngine.h"

@interface AStar (Private)
- (AStarNode *) lowCostNode;
- (BOOL) isCollision:(CGPoint)point;
- (AStarNode *) findPathFrom:(CGPoint)src to:(CGPoint)dst;
- (CGImageRef) newPathTile;
@end

@implementation AStar
@synthesize collideKey;
@synthesize collideValue;
@synthesize considerDiagonalMovement;
@synthesize groundLayer;

// Pre-define the neighboring tiles checked by the A* algorithm.
static const int numAdjacentTiles = 8;
static const int adjacentTiles[8][2] = { -1,1, 0,1, 1,1, -1,0,
    1,0, -1,-1, 0,-1, 1,-1 };
// The default path highlight color
static const float defaultPathFillColor[4] = {0.2, 0.5, 0.2, 0.3};

- (id) initWithTileMap:(CCTMXTiledMap*)aTileMap groundLayer:(NSString*)name
{
    if ((self=[super init]))
    {
        NSLog(@"init called");
        tileMap = aTileMap;
        groundLayer = [aTileMap layerNamed:name];
        openNodes = [NSMutableSet setWithCapacity:16];
        closedNodes = [NSMutableSet setWithCapacity:64];
        collideLayers = [NSMutableSet set];
        collideKey = ASTAR_COLLIDE_PROP_NAME;
        collideValue = ASTAR_COLLIDE_PROP_VALUE;
        considerDiagonalMovement = NO;
        memcpy(pathFillColor, defaultPathFillColor,
               sizeof(defaultPathFillColor));
        pathHighlightImage = [self newPathTile];
    }

    return self;
}

- (void) dealloc
{
    CFRelease(pathHighlightImage);
}

- (CGImageRef) newPathTile
{
    int width = [tileMap tileSize].width;
    int height = [tileMap tileSize].height;

    CGContextRef context = NULL;
    CGColorSpaceRef imageColorSpace = CGColorSpaceCreateDeviceRGB();

    context = CGBitmapContextCreate(NULL, width,
                                    height, 8, width * 4, imageColorSpace, kCGImageAlphaPremultipliedLast);

    CGContextSetRGBFillColor(context, pathFillColor[0],
                             pathFillColor[1], pathFillColor[2], pathFillColor[3]);
    CGContextFillRect(context, CGRectMake(0, 0, width, height));
    CFRelease(imageColorSpace);
    CGImageRef tile = CGBitmapContextCreateImage(context);
    CFRelease(context);
    return tile;
}
//#pragma mark - center of tile
//-(CGPoint)getCenterOfTile:(CGPoint)point{
//    CCSprite *tileAtPoint = [groundLayer tileAt:point];
//    CGPoint centerOfTile = ccp(tileAtPoint.position.x -16, tileAtPoint.position.y - 16);
//    return centerOfTile;
//}
//////
- (AStarNode *) findPathFrom:(CGPoint)src to:(CGPoint)dst
{
    [openNodes removeAllObjects];
    [closedNodes removeAllObjects];

    if ([self isCollision:dst]){
        NSLog(@"Collision at touch position");
    //[[SimpleAudioEngine sharedEngine]playEffect:@"CollisionSound.mp3"];
        return nil;
    }
    AStarNode *origin = [AStarNode nodeAtPoint:src];
    origin->parent = nil;
    [openNodes addObject:origin];

    AStarNode *closestNode = nil;
    while ([openNodes count])
    {
        closestNode = [self lowCostNode];
        if (closestNode->point.x == dst.x && closestNode->point.y == dst.y)
            return closestNode;

        [openNodes removeObject:closestNode];
        [closedNodes addObject:closestNode];

        for (int i=0; i<numAdjacentTiles; i++)
        {
            int x = adjacentTiles[i][0];
            int y = adjacentTiles[i][1];

            AStarNode *adjacentNode = [AStarNode
                                       nodeAtPoint:ccp(x + closestNode->point.x, y + closestNode->point.y)];
            adjacentNode->parent = closestNode;

            // Skip over this node if its already been closed.
            if ([closedNodes containsObject:adjacentNode])
                continue;

            // Skip over collide nodes, and add them to the closed set.
            if ([self isCollision:adjacentNode->point])
            {
                [closedNodes addObject:adjacentNode];
                NSLog(@"Collision at adjacent node position");

                continue;

            }

            // Calculate G
            // G cost is 10 for adjacent and 14 for a diagonal move.
            // We use these numbers because the distance to move diagonally
            // is the square root of 2, or 1.414 the cost of moving
            // horizontally or vertically.
            if (abs(x) == 1 && abs(y) == 1)
            {
                if (![self considerDiagonalMovement])
                    continue;
                adjacentNode->G = 14 + closestNode->G;
            }
            else
                adjacentNode->G = 10 + closestNode->G;

            // If the node is already in the open set, check and see if going
            // through the current node is a better path.
            if ([openNodes containsObject:adjacentNode])
            {
                AStarNode *otherNode = [openNodes member:adjacentNode];
                int newCost = otherNode->G - otherNode->parent->G + closestNode->G;
                if (newCost < otherNode->G)
                {
                    otherNode->G = newCost;
                    otherNode->parent = closestNode;
                }
            }
            else 
            {
                // Calculate H
                // Uses 'Mahhattan' method wich is just the number
                // of horizonal and vertical hops to the target.
                adjacentNode->H = (abs(adjacentNode->point.x - dst.x)
                                   + abs(adjacentNode->point.y - dst.y)) * 10;
                [openNodes addObject:adjacentNode];
            }
        }
    }
    return nil;
}


- (NSArray*) getPath:(CGPoint)src to:(CGPoint)dst
{
    paths = [NSMutableArray array];
    AStarNode *node = [self findPathFrom:src to:dst];


    if (node == nil)
    {
        return paths;
    }
    while(node != nil)
    {
        [paths addObject:node];
        node = node->parent;

    }
    return [[paths reverseObjectEnumerator] allObjects ];

}

- (void) clearHighlightPath
{
    [self removeAllChildrenWithCleanup:YES];
}

- (void) moveSprite:(CCSprite*)sprite
               from:(CGPoint)src to:(CGPoint)dst atSpeed:(float)speed
{

    NSArray *nodes = [self getPath:src to:dst];
    if ([nodes count] == 0)
    {
        return;
    }

    NSMutableArray *actionList = [NSMutableArray array];

//    int tileWidthOffset = [tileMap tileSize].width / 2;
//    int tileHeightOffset = [tileMap tileSize].height / 2;

    for(AStarNode *node in nodes)
    {       
        CGPoint p1 = node->point;

//        CGPoint p1 = [groundLayer
//                     positionAt:node->point];
//        p1.x = p1.x + tileWidthOffset;
//        p1.y = p1.y + tileHeightOffset;

        CCAction *move = [CCMoveTo actionWithDuration: speed position: p1];
        [actionList addObject:move];
    }
    [sprite runAction:[CCSequence actionWithArray:actionList]];
}

- (void) addCollideLayer:(NSString*)name
{
    CCTMXLayer *layer = [tileMap layerNamed:name];
    if (layer != nil)
    {
        [collideLayers addObject:layer];
    }
}

- (void) removeCollideLayer:(NSString*)name
{
    [collideLayers removeObject:[tileMap layerNamed:name]];
}

//-(CCSprite*) tileForPosition:(CGPoint)point {
//    int tilePositionX = point.x / groundLayer.mapTileSize.width;
//    int tilePositionY = point.y / groundLayer.mapTileSize.height;
//    
//    CCSprite* tile = [groundLayer tileAt:ccp(tilePositionX, tilePositionY)];
//    
//    return tile;
//}

- (BOOL) isCollision:(CGPoint)point
{
    for(AStarNode *node in openNodes){
    //CGPoint tileCenter = ccp(groundLayer.mapTileSize.width /2, groundLayer.mapTileSize.height /2);

        float width = groundLayer.layerSize.width * groundLayer.mapTileSize.width;
        float height = groundLayer.layerSize.height * groundLayer.mapTileSize.height;

    if (point.x >= width || point.x <= 0)
    {
        NSLog(@"isCollision: Off Map");
        return YES;
    }
    if (point.y >= height || point.y <= 0)
    {
        NSLog(@"isCollision: Off Map");
        return YES;
    }




    /////////////

    // Check for a tile in the collide layer.
            // Check for a tile in the collide layer.

            // ISSUE HERE - ASSERTION FAILURE WITH FLAGS. CCTMXLAYER TILEGIDAT:WITHFLAGS:]
        //'NSInternalInconsistencyException', reason: 'TMXLayer: invalid position'

        NSLog(@"point : x:%f y:%f ", point.x, point.y);        

        // Check for a tile in the collide layer.
        uint32_t tileGid = [groundLayer tileGIDAt:point];
/*--->*/if (tileGid)
        {
            // If a tile exists, see if collide is enabled on the entire layer.
            NSDictionary *ldict = [groundLayer propertyNamed:collideKey];
            if (ldict)
                return YES;

            // If not, then check the tile for the collide property.
            NSDictionary *dict = [tileMap propertiesForGID:tileGid];
            if (dict)
            {
                NSString *collide = [dict valueForKey:collideKey];
                if (collide && [collide compare:collideValue] == NSOrderedSame)
                    return YES;
            }
        }
    }
    return NO;
}

    /////////////


    - (AStarNode *) lowCostNode
{
    AStarNode *lowCostNode = [openNodes anyObject];
    for (AStarNode* otherNode in openNodes)
    {   
        if ([otherNode cost] < [lowCostNode cost])
        {

            lowCostNode = otherNode;
        }
        else if ([otherNode cost] == [lowCostNode cost])
        {
                        if (otherNode->H < lowCostNode->H){
                lowCostNode = otherNode;
            }
        }
    }
    return lowCostNode;
}

- (void) setPathRGBAFillColor:(float)red
                            g:(float)green
                            b:(float)blue
                            a:(float)alpha;
{
    pathFillColor[0] = red;
    pathFillColor[1] = green;
    pathFillColor[2] = blue;
    pathFillColor[3] = alpha;
    CFRelease(pathHighlightImage);
    pathHighlightImage = [self newPathTile];
}

@end

@implementation AStarNode

+ (id) nodeAtPoint:(CGPoint)point;
{
    return [[AStarNode alloc] initAtPoint:point];
}

- (id) initAtPoint:(CGPoint)pnt
{
    point = pnt;
    x = pnt.x;
    y = pnt.y;
    return self;
}

- (int) cost
{
    return G + H;
}

- (NSUInteger) hash
{
    return (x << 16) | (y & 0xFFFF);
}

- (BOOL)isEqual:(id)otherObject
{
    if (![otherObject isKindOfClass:[self class]])
    {
        return NO;
    }

    AStarNode *otherNode = (AStarNode*) otherObject;
    if (point.x == otherNode->point.x && point.y == otherNode->point.y)
    {
        return YES;
    }

    return NO;
}

@end

The section which defines the tilemap and passes in the needed variables from my HelloWorldLayer class are below...

//Pathfinding related code
        ////////////////////////////
        _tileMap = [CCTMXTiledMap tiledMapWithTMXFile:@"NewMap.tmx"];
        _pathFinder = [[AStar alloc] initWithTileMap:_tileMap groundLayer:@"Collision"]; //collidable Tiles
        [_pathFinder setCollideKey:@"Collidable"]; // defaults to COLLIDE
        [_pathFinder setCollideValue:@"True"]; // defaults to 1

        _pathFinder2 = [[AStar alloc] initWithTileMap:_tileMap groundLayer:@"Collision"]; //collidable Tiles
        [_pathFinder2 setCollideKey:@"Collidable"]; // defaults to COLLIDE
        [_pathFinder2 setCollideValue:@"True"]; // defaults to 1

        /////////////////////////////
        /////////////////////////////

Thanks in advance guys. I would really appreciate any assistance at this stage.

Member Avatar for Dave_5

Update on this... I managed to fix most of it but the algorithm is returning the wrong GID on the tiles at any given position. They are all returning as 1. Does anyone know why this may happen?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.