How make a Rect for tiles from a Tiled Map - cocos2d-iphone

For my collision detection I need to check of the ball rect intersects any of the wall rects. Right now I have it working but its checking to see if the ball's position is at one of the tile's GID, using this.
-(void) checkHits:(CGPoint)position {
CGPoint tileCoord = [self tileCoordForPosition:position];
int tileGid = [levelLayer tileGIDAt:tileCoord];
//NSLog(#"%g",tileRect.origin);
if (tileGid) {
NSDictionary *properties = [level propertiesForGID:tileGid];
if (properties) {
NSString *collision = [properties valueForKey:#"break"];
if (collision && [collision compare:#"True"] == NSOrderedSame) {
//for blocks
//[[SimpleAudioEngine sharedEngine] playEffect:#"hit.caf"];
[levelLayer removeTileAt:tileCoord];
velocity.y *= -1;
}
if (collision && [collision compare:#"False"] == NSOrderedSame) {
//for edges
//[[SimpleAudioEngine sharedEngine] playEffect:#"hit.caf"];
velocity.y *= -1;
}
}
}
}
I need to know how to change this to checking to see if the balls rect/boundingbox intersects with any of the tiles rects/boundingboxex(and how to get the tiles rect/boundingbox in the first place) that have the property break.
P.S. I'm using the Tiled map editor.

Figured this out a little while ago:
CCSprite *tile;
for (int i = 0; i < level.contentSize.width/level.tileSize.width; i ++)
for (int j = 0; j < level.contentSize.height/level.tileSize.height; j ++){
tile = [levelLayer tileAt:ccp(i,j)];
if (CGRectIntersectsRect(ball.boundingBox, tile.boundingBox)) {
//Do whatever...
}
}
}
I recommend having a collide layer to make map making easier.

Related

Outline of pixels after detecting object (without convex hull)

The idea is to use grabcut (OpenCV) to detect the image inside a rectangle and create a geometry with Direct2D.
My test image is this:
After performing the grab cut, resulting in this image:
the idea is to outline it. I can use an opacity brush to exclude it from the background but I want to use a geometric brush in order to be able to append/widen/combine geometries on it like all other selections in my editor (polygon, lasso, rectangle, etc).
If I apply the convex hull algorithm to the points, I get this:
Which of course is not desired for my case. How do I outline the image?
After getting the image from the grabcut, I keep the points based on luminance:
DWORD* pixels = ...
for (UINT y = 0; y < he; y++)
{
for (UINT x = 0; x < wi; x++)
{
DWORD& col = pixels[y * wi + x];
auto lumthis = lum(col);
if (lumthis > Lum_Threshold)
{
points.push_back({x,y});
}
}
}
Then I sort the points on Y and X:
std::sort(points.begin(), points.end(), [](D2D1_POINT_2F p1, D2D1_POINT_2F p2) -> bool
{
if (p1.y < p2.y)
return true;
if ((int)p1.y == (int)p2.y && p1.x < p2.x)
return true;
return false;
});
Then, for each line (traversing the above point array from top Y to bototm Y) I create "groups" for each line:
struct SECTION
{
float left = 0, right = 0;
};
auto findgaps = [](D2D1_POINT_2F* p,size_t n) -> std::vector<SECTION>
{
std::vector<SECTION> j;
SECTION* jj = 0;
for (size_t i = 0; i < n; i++)
{
if (i == 0)
{
SECTION jp;
jp.left = p[i].x;
jp.right = p[i].x;
j.push_back(jp);
jj = &j[j.size() - 1];
continue;
}
if ((p[i].x - jj->right) < 1.5f)
{
jj->right = p[i].x;
}
else
{
SECTION jp;
jp.left = p[i].x;
jp.right = p[i].x;
j.push_back(jp);
jj = &j[j.size() - 1];
}
}
return j;
};
I'm stuck at this point. I know that from an arbitrary set of points many polygons are possible, but in my case the points have defined what's "left" and what's "right". How would I proceed from here?
For anyone interested, the solution is OpenCV contours. Working example here.

spritekit nodeAtPoint intersection

I am trying to detect the coordinates where two sprites (shipLaser and baddie) intersect and then make a new sprite (speedPowerUp) in that position. Below is what I have attempted to do with nodeAtPoint but it is just running and then crashing when the two sprites intersect.
for (SKSpriteNode *baddie in _baddies)
{
baddie.name = #"baddie1";
if (baddie.hidden) {
continue;
}
for (SKSpriteNode *shipLaser in _shipLasers){
if (shipLaser.hidden) {
continue;
}
if ([shipLaser intersectsNode:baddie]) {
shipLaser.hidden = YES;
baddie.hidden = YES;
[shipLaser nodeAtPoint:*(_itemDrop)];
NSLog(#"You just destroyed a baddie");
_score = _score + 10;
_scoreLabel.text = [NSString stringWithFormat:#"%i", _score];
if (_score == 10)
{
_speedPowerUp.hidden = NO;
_speedPowerUp.position = *(_itemDrop);
[self addChild:_speedPowerUp];
}

Testing Rectagles Intersection on CGRect's

I am trying to detect collision between sprites from the same NSMutableArray. I am running a loop to compare one sprite with all of them, inclusing itself.
The thing is that the "CGRectEqualToRect" method is returning me a TRUE boolean, even though it is not!
- (BOOL)detectCollision: (CCSprite*)sprite{
BOOL result = FALSE;
CCSprite *toCollide;
CGRect rect1 = [GameScene positionRect:sprite];
for (int i = 0; i < [movableSprites count]; i++){
toCollide = [movableSprites objectAtIndex:i];
CGRect rect2 = [GameScene positionRect:toCollide];
if (!CGRectIsNull(CGRectIntersection(rect1, rect2))) {
if(!CGRectEqualToRect(rect1, rect2)){
//handle collision
NSLog(#"Collides");
result = TRUE;
}
}
}
return result;
}
For instance, when debugging, rect1 is returning the same sizes as rect2, but its x and y are completely different.
Any ideas on this one?
Thanks!
Try this..
- (BOOL)detectCollision: (CCSprite*)sprite
{
BOOL result = FALSE;
for (int i = 0; i < [movableSprites count]; i++)
{
CCSprite *toCollide; = [movableSprites objectAtIndex:i];
if(sprite==toCollide){
continue;
}
if (CGRectIntersectsRect(sprite.boundingBox, toCollide.boundingBox)) {
//handle collision
NSLog(#"Collides");
result = TRUE;
return result;
}
}
return result;
}
Make sure that all sprites are added to same object(Parent),otherwise you same to do some changes..

Xcode Collision Detection Error Cocos2D Assertion Error

I am making a ball game in cocos2D, where you can slide the ball in a direction, and it will travel until it collides with a wall. My collision detection is giving me an "assertion error" when I swipe the ball in a direction.
I am using tiledmap to make the levels.
The way I've coded it is that I find the tilesize that my ball is in, then, in a for loop, I check if there are any wall sprites around the ball.
Any ideas where its going wrong?
-(void)callEveryFrame:(ccTime)dt{
//Error Here vvv (Assertion Failure in -[CCTMXLayer tileAt:])
NSLog(#"First Touch x: /%f Last Touch x: /%f First Touch y: /%f Last Touch y: /%f", firstTouch.x, lastTouch.x, firstTouch.y, lastTouch.y);
if(swipeLeft || swipeRight || swipeDown || swipeUp)
{
bool collision = false;
int ballTileX = (player.position.x / level1.tileSize.width);
int ballTileY = (player.position.y / level1.tileSize.width);
int collisionAreaX = ballTileX-1;
int collisionAreaY = ballTileY-1;
for(int i = 0; i < 9; i++)
{
bool tilePresent = [background tileAt:CGPointMake(collisionAreaX, collisionAreaY)] != NULL;
if(tilePresent && CGRectIntersectsRect(CGRectMake(collisionAreaX*level1.tileSize.width, collisionAreaY*level1.tileSize.height, level1.tileSize.width, level1.tileSize.height), CGRectMake(ballTileX*level1.tileSize.width, ballTileY*level1.tileSize.height, level1.tileSize.width, level1.tileSize.height)))
{
collision = true;
break;
}
if(i % 3 == 0)
{
collisionAreaX -= 2;
collisionAreaY++;
}
else
{
collisionAreaX++;
}
}
if(collision) {
swipeLeft = false;
swipeRight = false;
swipeDown = false;
swipeUp = false;
}
}
}

Adding CCSprite on Tiled tile properties

I'm trying to add a Sprite beside the player, but only when the Tile beside the player is NOT a Wall. I know that the Tiled tiles are working properly, as they do their job in this method:
CGPoint p = CGPointMake(tileCoord.x, tileCoord.y - 1);
if ([self isValidTileCoord:p] && ![self isWallAtTileCoord:p]) {
[tmp addObject:[NSValue valueWithCGPoint:p]];
t = YES;
I am checking for Tiled on coordinate with these two methods:
-(BOOL)isProp:(NSString*)prop atTileCoord:(CGPoint)tileCoord forLayer:(CCTMXLayer *)layer {
if (![self isValidTileCoord:tileCoord]) return NO;
int gid = [layer tileGIDAt:tileCoord];
NSDictionary * properties = [_tileMap propertiesForGID:gid];
if (properties == nil) return NO;
return [properties objectForKey:prop] != nil;
}
-(BOOL)isWallAtTileCoord:(CGPoint)tileCoord {
return [self isProp:#"Wall" atTileCoord:tileCoord forLayer:_bgLayer];
}
(Thanks RayWenderlich)
And my code for adding the sprite is
CGPoint tileCoord = ccp(_player.position.x - 24 +60, player.position.y);
CGPoint cTileCoord = [self tileCoordForPosition:tileCoord];
NSLog(#" t: %#, c: %#",
CGPointCreateDictionaryRepresentation(tileCoord),
CGPointCreateDictionaryRepresentation(cTileCoord)
);
if (![self isWallAtTileCoord:cTileCoord])
{
NSLog(#"False");
circle1.position = ccp(_player.position.x - 24 +60, _player.position.y);
[self addChild:circle1];
}
else
{
NSLog(#"True");
}
What I want this to do is to only add the Circle1 sprite to the left of the player when there isn't a Wall tile there. The problem is that this code always detects false, no matter if there is a wall there or not. Do any of you guys understand why it does not detect the walls properly and how I can fix it?
Last year me also observed same problem in retina mode. It worked in non retina but not detected in retina mode.
So finally used custom dictionary to check edge tile. Integer comparison takes less CPU time than string comparison. So replaced it with enum.
Go for this approach only if none other simple way worked.
I used this code to find edge of the tile that set in meta property.
typedef enum GRID_TYPE{
kGrideType_Normal = 4001,
kGrideType_Collidable,
kGrideType_Collectable,
kGrideType_Sea,
kGrideType_Edge,
kGrideType_enemy,
kGrideType_gold,
}GridType;
//Init tilemap and grid type
tileMap.meta = [tileMap layerNamed:PP_TILE_META_LAYER];
[tileMap initTileAnimation];
//Somewhere in code
CGPoint point = [self getTileCoordForPosition:position];
GridType type = [self getTileType:point];
if(type == kGrideType_Edge)
{
//touched edge tile....
}
Functions:
-(GridType)getTileType:(CGPoint)pos
{
// not defined USE_COCOS2D_FOR_TILE_IDENTIFICATION
#ifdef USE_COCOS2D_FOR_TILE_IDENTIFICATION
GridType type = kGrideType_Normal;
CGPoint tileCoord = position;//[self tileCoordForPosition:position];
unsigned int tileGid = [self.meta tileGIDAt:tileCoord];
if (tileGid) {
NSDictionary *properties = [self propertiesForGID:tileGid];
if (properties)
{
NSString *collision = [properties valueForKey:TILE_PROPERTY_COLLIDABLE];
if (collision && [collision caseInsensitiveCompare:#"true"] == NSOrderedSame)
{
type = kGrideType_Collidable ;
}
NSString *collectable = [properties valueForKey:TILE_PROPERTY_COLLECTABLE];
if (collectable && [collectable caseInsensitiveCompare:#"true"] == NSOrderedSame) {
type = kGrideType_Coin ;
}
NSString *collectable = [properties valueForKey:TILE_PROPERTY_CHEST];
if (collectable && [collectable caseInsensitiveCompare:#"true"] == NSOrderedSame) {
type = kGrideType_Chest ;
}
}
}
return type;
#else
int type = [[mTileInfoDict objectForKey:[NSString stringWithFormat:#"TILE(%d,%d)", (int)pos.x,(int)pos.y]] intValue];
GridType gType = kGrideType_Normal;
if(type!=0)
gType = (GridType)type;
return (GridType)gType;
#endif
}
-(void)initTileAnimation
{
mTileInfoDict = [[NSMutableDictionary alloc] init];
//Parse all the tile in map
mBgLayer = [self layerNamed:PP_TILE_MAP_BG_LAYER];
int rowSize = self.mapSize.width ;
int colSize = self.mapSize.height ;
for(int x=0; x<rowSize; x++)
{
for (int y=0; y<colSize; y++)
{
CGPoint tileCord = ccp(x,y) ;
NSString *key = [NSString stringWithFormat:#"TILE(%d,%d)", (int)tileCord.x,(int)tileCord.y];
GridType tileType = kGrideType_Normal;
unsigned int tileGid = [self.meta tileGIDAt:tileCord];
if (tileGid)
{
NSDictionary *properties = [self propertiesForGID:tileGid];
if (properties)
{
/* Check Tile : IS SEA - TILE */
NSString *sea = [properties valueForKey:TILE_PROPERTY_SEA];
if (sea && [sea isEqualToString:#"true"])
{
tileType = kGrideType_Sea;
[mTileInfoDict setObject:[NSNumber numberWithInt:tileType] forKey:key];
continue;
}
/* Check Tile : IS Inky - TILE */
/* Check Tile : IS COLLECTABLE - COIN */
NSString *collectable = [properties valueForKey:TILE_PROPERTY_COLLECTABLE];
if (collectable && [collectable isEqualToString:#"true"])
{
tileType = kGrideType_Collectable;
[mTileInfoDict setObject:[NSNumber numberWithInt:tileType] forKey:key];
PPCoins *coinSprite = [PPCoins spriteWithSpriteFrameName:#"coin_0000.png"];
coinSprite.tag = kTagCoinSprite;
coinSprite.anchorPoint = ccp(0.5f,0.5f);
CCSprite *sprite = [mBgLayer tileAt:tileCord];
coinSprite.position = ccp(sprite.position.x+coinSprite.contentSize.width*0.5f, sprite.position.y+coinSprite.contentSize.height*0.5f) ;
[self addChild:coinSprite z:3 tag:kTagCoinSprite];
[coinSprite runAnimation];
{
coinSprite.key = key;
[mCoinDict setObject:coinSprite forKey:key];
}
continue;
}
/* Check Tile : IS COLLIDABLE - TILE */
NSString *collidable = [properties valueForKey:TILE_PROPERTY_COLLIDABLE];
if (collidable && [collidable isEqualToString:#"true"])
{
tileType = kGrideType_Collidable;
[mTileInfoDict setObject:[NSNumber numberWithInt:tileType] forKey:key];
continue;
}
/* Check Tile : IS Edge - TILE */
NSString *edge = [properties valueForKey:TILE_PROPERTY_EDGE];
if (edge && [edge isEqualToString:#"true"])
{
tileType = kGrideType_Edge;
[mTileInfoDict setObject:[NSNumber numberWithInt:tileType] forKey:key];
continue;
}
NSString *redTargetCoin = [properties valueForKey:TILE_PROPERTY_TARGET_COIN];
if (redTargetCoin && [redTargetCoin isEqualToString:#"true"])
{
tileType = kGrideType_TargetCoins;
[mTileInfoDict setObject:[NSNumber numberWithInt:tileType] forKey:key];
}
}
else
{
[mTileInfoDict setObject:[NSNumber numberWithInt:kGrideType_Normal] forKey:key];
}
}
}
}
}
- (CGPoint)getTileCoordForPosition:(CGPoint)position
{
// CGPoint nodeSpace1 = [self convertToNodeSpace:position];
// float x = floor(nodeSpace1.x / self.tileSize.width);
// float y = floor(self.mapSize.height - (nodeSpace1.y / self.tileSize.height));
//
// if( x >= TILE_IN_ROW)
// x = TILE_IN_ROW - 1;
//
// if( y >= TILE_IN_COL)
// y = TILE_IN_COL - 1;
//
// return ccp(x, y);
int maxTileCol = self.mapSize.height;// (_tileMap.contentSize.height)/TILE_SIZE;
int x = ( (position.x-self.position.x)/TILE_SIZE);
int y = maxTileCol - ( ((position.y)-self.position.y)/TILE_SIZE);
if( x >= TILE_IN_ROW)
x = TILE_IN_ROW - 1;
if( y >= TILE_IN_COL)
y = TILE_IN_COL - 1;
return ccp(x, y);
}
You should check if your tile map has the correct layer settings. I did a mistake with this once, it is easy to forget