iPhone Map Kit cluster pinpoints - iphone

Regarding iPhone Map Kit cluster pinpoints:
I have 1000's of marks that I want to show on the map but it's just too many to handle so I want to cluster them.
Are there frameworks available or proof of concepts? That this is possible or is already been done?

You can use REVClusterMap to cluster

Note: This is a commercial product I'm affiliated with, but it solves this very problem.
I solved this problem in few of my apps and decided to extract it into a reusable framework. It's called Superpin and it is an (commercial, license costs $149) iOS Framework that internally uses quadtrees for annotation storage and performs grid-based clustering. The algorithm is quite fast, the included sample app is showing airports of the world (more than 30k+ annotations) and it's running quite smooth on an 3G iPhone.

This might be a bit like using a chainsaw to mow the lawn, but here is an excerpt from Algorithms in a Nutshell
Creating a KD-Tree...
public class KDFactory {
// Known comparators for partitioning points along dimensional axes.
private static Comparator<IMultiPoint> comparators[ ] ;
// Recursively construct KDTree using median method on input points.
public static KDTree generate (IMultiPoint [ ] points) {
if (points. length == 0) { return null; }
// median will be the root.
int maxD = points[ 0] . dimensionality( );
KDTree tree = new KDTree(maxD) ;
// Make dimensional comparators that compare points by ith dimension
comparators = new Comparator[ maxD+1] ;
for (int i = 1; i <= maxD; i++) {
comparators[ i] = new DimensionalComparator(i) ;
}
tree. setRoot(generate (1, maxD, points, 0, points. length-1) ) ;
return tree;
}
// generate the node for the d-th dimension (1 <= d <= maxD)
// for points[ left, right]
private static DimensionalNode generate (int d, int maxD,
IMultiPoint points[ ] ,
int left, int right) {
// Handle the easy cases first
if (right < left) { return null; }
if (right == left) { return new DimensionalNode (d, points[ left] ) ; }
// Order the array[ left, right] so the mth element will be the median
// and the elements prior to it will all be <=, though they won' t
// necessarily be sorted; similarly, the elements after will all be >=
int m = 1+(right-left) /2;
Selection. select(points, m, left, right, comparators[ d] ) ;
// Median point on this dimension becomes the parent
DimensionalNode dm = new DimensionalNode (d, points[ left+m-1] ) ;
// update to the next dimension, or reset back to 1
if (++d > maxD) { d = 1; }
// recursively compute left and right sub-trees, which translate
// into ' below' and ' above' for n-dimensions.
dm. setBelow(maxD, generate (d, maxD, points, left, left+m-2) ) ;
dm. setAbove(maxD, generate (d, maxD, points, left+m, right) ) ;
return dm;
}
}
Finding nearest neighbors best: O(log n) worst O(n)
// method in KDTree
public IMultiPoint nearest (IMultiPoint target) {
if (root == null) return null;
// find parent node to which target would have been inserted. This is our
// best shot at locating closest point; compute best distance guess so far
DimensionalNode parent = parent(target) ;
IMultiPoint result = parent. point;
double smallest = target. distance(result) ;
// now start back at the root, and check all rectangles that potentially
// overlap this smallest distance. If better one is found, return it.
double best[ ] = new double[ ] { smallest };
double raw[ ] = target. raw( );
IMultiPoint betterOne = root. nearest (raw, best) ;
if (betterOne ! = null) { return betterOne; }
return result;
}
// method in DimensionalNode. min[ 0] contains best computed shortest distance.
IMultiPoint nearest (double[ ] rawTarget, double min[ ] ) {
// Update minimum if we are closer.
IMultiPoint result = null;
// If shorter, update minimum
double d = shorter(rawTarget, min[ 0] ) ;
if (d >= 0 && d < min[ 0] ) {
min[ 0] = d;
result = point;
}
// determine if we must dive into the subtrees by computing direct
// perpendicular distance to the axis along which node separates
// the plane. If d is smaller than the current smallest distance,
// we could "bleed" over the plane so we must check both.
double dp = Math. abs(coord - rawTarget[ dimension-1] ) ;
IMultiPoint newResult = null;
if (dp < min[ 0] ) {
// must dive into both. Return closest one.
if (above ! = null) {
newResult = above. nearest (rawTarget, min) ;
if (newResult ! = null) { result = newResult; }
}
if (below ! = null) {
newResult = below. nearest(rawTarget, min) ;
if (newResult ! = null) { result = newResult; }
}
} else {
// only need to go in one! Determine which one now.
if (rawTarget[ dimension-1] < coord) {
if (below ! = null) {
newResult = below. nearest (rawTarget, min) ;
}
} else {
if (above ! = null) {
newResult = above. nearest (rawTarget, min) ;
}
}
// Use smaller result, if found.
if (newResult ! = null) { return newResult; }
}
return result;
}
More on KD-Trees at Wikipedia

I tried the others suggested here, and I also found OCMapView which has worked the best.
Its free and allows for easy grouping of annotations, which is what I needed. Its a bit newer & more updated than Revolver and to me is easier to implement.

A proof of concept is the Offline Maps app "OffMaps" ;)
http://itunes.apple.com/us/app/offmaps/id313854422?mt=8

I recently had to implement annotation clustering with MapKit. The solutions mentioned above are good, depending on your use case. I ended up going with FBAnnotationClustering (Objective-C) because it was free, and had lots of stars and few issues on github:
https://github.com/infinum/FBAnnotationClustering
The app I was working on was very map-centric, so it made sense to translate FBAnnotationClustering into Swift. Here's a blog post on the approach, which includes a link to the sample project on github.
http://ribl.co/blog/2015/05/28/map-clustering-with-swift-how-we-implemented-it-into-the-ribl-ios-app/

Inspired by WWDC 2011 video, this code works very well for me. Maybe not the fastest of all proposed here but it's for free and it's definitely the simplest.
It basically use 2 maps. One is hidden and hold every single annotation (allAnnotationMapView in my code). One is visible and show only the clusters or the annotations if single (mapView in my code).
- (void)didZoom:(UIGestureRecognizer*)gestureRecognizer {
if (gestureRecognizer.state == UIGestureRecognizerStateEnded){
[self updateVisibleAnnotations];
}
}
- (void)updateVisibleAnnotations {
static float marginFactor = 2.0f;
static float bucketSize = 50.0f;
MKMapRect visibleMapRect = [self.mapView visibleMapRect];
MKMapRect adjustedVisibleMapRect = MKMapRectInset(visibleMapRect, -marginFactor * visibleMapRect.size.width, -marginFactor * visibleMapRect.size.height);
CLLocationCoordinate2D leftCoordinate = [self.mapView convertPoint:CGPointZero toCoordinateFromView:self.view];
CLLocationCoordinate2D rightCoordinate = [self.mapView convertPoint:CGPointMake(bucketSize, 0) toCoordinateFromView:self.view];
double gridSize = MKMapPointForCoordinate(rightCoordinate).x - MKMapPointForCoordinate(leftCoordinate).x;
MKMapRect gridMapRect = MKMapRectMake(0, 0, gridSize, gridSize);
double startX = floor(MKMapRectGetMinX(adjustedVisibleMapRect) / gridSize) * gridSize;
double startY = floor(MKMapRectGetMinY(adjustedVisibleMapRect) / gridSize) * gridSize;
double endX = floor(MKMapRectGetMaxX(adjustedVisibleMapRect) / gridSize) * gridSize;
double endY = floor(MKMapRectGetMaxY(adjustedVisibleMapRect) / gridSize) * gridSize;
gridMapRect.origin.y = startY;
while(MKMapRectGetMinY(gridMapRect) <= endY) {
gridMapRect.origin.x = startX;
while (MKMapRectGetMinX(gridMapRect) <= endX) {
NSSet *allAnnotationsInBucket = [self.allAnnotationMapView annotationsInMapRect:gridMapRect];
NSSet *visibleAnnotationsInBucket = [self.mapView annotationsInMapRect:gridMapRect];
NSMutableSet *filteredAnnotationsInBucket = [[allAnnotationsInBucket objectsPassingTest:^BOOL(id obj, BOOL *stop) {
BOOL isPointMapItem = [obj isKindOfClass:[PointMapItem class]];
BOOL shouldBeMerged = NO;
if (isPointMapItem) {
PointMapItem *pointItem = (PointMapItem *)obj;
shouldBeMerged = pointItem.shouldBeMerged;
}
return shouldBeMerged;
}] mutableCopy];
NSSet *notMergedAnnotationsInBucket = [allAnnotationsInBucket objectsPassingTest:^BOOL(id obj, BOOL *stop) {
BOOL isPointMapItem = [obj isKindOfClass:[PointMapItem class]];
BOOL shouldBeMerged = NO;
if (isPointMapItem) {
PointMapItem *pointItem = (PointMapItem *)obj;
shouldBeMerged = pointItem.shouldBeMerged;
}
return isPointMapItem && !shouldBeMerged;
}];
for (PointMapItem *item in notMergedAnnotationsInBucket) {
[self.mapView addAnnotation:item];
}
if(filteredAnnotationsInBucket.count > 0) {
PointMapItem *annotationForGrid = (PointMapItem *)[self annotationInGrid:gridMapRect usingAnnotations:filteredAnnotationsInBucket];
[filteredAnnotationsInBucket removeObject:annotationForGrid];
annotationForGrid.containedAnnotations = [filteredAnnotationsInBucket allObjects];
[self.mapView addAnnotation:annotationForGrid];
//force reload of the image because it's not done if annotationForGrid is already present in the bucket!!
MKAnnotationView* annotationView = [self.mapView viewForAnnotation:annotationForGrid];
NSString *imageName = [AnnotationsViewUtils imageNameForItem:annotationForGrid selected:NO];
UILabel *countLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 2, 8, 8)];
[countLabel setFont:[UIFont fontWithName:POINT_FONT_NAME size:10]];
[countLabel setTextColor:[UIColor whiteColor]];
[annotationView addSubview:countLabel];
imageName = [AnnotationsViewUtils imageNameForItem:annotationForGrid selected:NO];
annotationView.image = [UIImage imageNamed:imageName];
if (filteredAnnotationsInBucket.count > 0){
[self.mapView deselectAnnotation:annotationForGrid animated:NO];
}
for (PointMapItem *annotation in filteredAnnotationsInBucket) {
[self.mapView deselectAnnotation:annotation animated:NO];
annotation.clusterAnnotation = annotationForGrid;
annotation.containedAnnotations = nil;
if ([visibleAnnotationsInBucket containsObject:annotation]) {
CLLocationCoordinate2D actualCoordinate = annotation.coordinate;
[UIView animateWithDuration:0.3 animations:^{
annotation.coordinate = annotation.clusterAnnotation.coordinate;
} completion:^(BOOL finished) {
annotation.coordinate = actualCoordinate;
[self.mapView removeAnnotation:annotation];
}];
}
}
}
gridMapRect.origin.x += gridSize;
}
gridMapRect.origin.y += gridSize;
}
}
- (id<MKAnnotation>)annotationInGrid:(MKMapRect)gridMapRect usingAnnotations:(NSSet *)annotations {
NSSet *visibleAnnotationsInBucket = [self.mapView annotationsInMapRect:gridMapRect];
NSSet *annotationsForGridSet = [annotations objectsPassingTest:^BOOL(id obj, BOOL *stop) {
BOOL returnValue = ([visibleAnnotationsInBucket containsObject:obj]);
if (returnValue) {
*stop = YES;
}
return returnValue;
}];
if (annotationsForGridSet.count != 0) {
return [annotationsForGridSet anyObject];
}
MKMapPoint centerMapPoint = MKMapPointMake(MKMapRectGetMinX(gridMapRect), MKMapRectGetMidY(gridMapRect));
NSArray *sortedAnnotations = [[annotations allObjects] sortedArrayUsingComparator:^(id obj1, id obj2) {
MKMapPoint mapPoint1 = MKMapPointForCoordinate(((id<MKAnnotation>)obj1).coordinate);
MKMapPoint mapPoint2 = MKMapPointForCoordinate(((id<MKAnnotation>)obj2).coordinate);
CLLocationDistance distance1 = MKMetersBetweenMapPoints(mapPoint1, centerMapPoint);
CLLocationDistance distance2 = MKMetersBetweenMapPoints(mapPoint2, centerMapPoint);
if (distance1 < distance2) {
return NSOrderedAscending;
}
else if (distance1 > distance2) {
return NSOrderedDescending;
}
return NSOrderedSame;
}];
return [sortedAnnotations objectAtIndex:0];
}

I think Foto Brisko (iTunes link) does this.
I do not think there is a Cocoa Touch framework for it.

Related

moving several balls around a board sprite kit Xcode

I have a small app that I have several sprite "balls" that move around the board using MotionManager.gravity.
I have code that when one of any of the balls meets an edge the stop at the edge. also if one of any of the balls gets to the corner it stops in the corner. I have also written code in which if two or three of the balls are on any one edge and touch each other (they have collision detection turned on) they stop in they maintain their relative position to the other ball.
Here is my code for that:
//this allocs the motionManager and set up the gravity values.
self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.deviceMotionUpdateInterval = 0.005f;
self.motionQueue = [[NSOperationQueue alloc] init];
self.motionQueue.name = [[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:#".motion"];
self.updatePosition = NO;
[self.motionManager startDeviceMotionUpdatesToQueue:self.motionQueue withHandler:^(CMDeviceMotion *motion, NSError *error) {
#synchronized(self) {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setMaximumFractionDigits:2];
[formatter setRoundingMode: NSNumberFormatterRoundUp];
{
_numberString = [formatter stringFromNumber:[NSNumber numberWithFloat:motion.gravity.x / 15.0 *200]];
_numberStringy = [formatter stringFromNumber:[NSNumber numberWithFloat:motion.gravity.y / 15.0 *200]];
n = [_numberString intValue];
y = [_numberStringy intValue];
}
self.gravity = motion.gravity;
self.updatePosition = YES;
}
}];
[self startDisplayLink];
//this shows how I stop the ball at the edge
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
#synchronized(self) {
if (!self.updatePosition)
return;
self.ball.position = CGPointMake(self.ball.position.x + (n*.99), self.ball.position.y + (y*.99));
self.ball2.position = CGPointMake(self.ball2.position.x + (n*.98), self.ball2.position.y +(y*.98));
self.ball3.position = CGPointMake(self.ball3.position.x + n, self.ball3.position.y + y);
if (ball.position.x <=340) {
ball.position = CGPointMake(340, ball.position.y);
}
if (ball.position.x >=684) {
ball.position = CGPointMake(684, ball.position.y);
}
if (ball.position.y <=148) {
ball.position = CGPointMake( ball.position.x,148);
}
if (ball.position.y >=620) {
ball.position = CGPointMake(ball.position.x,620);
}
//this stops it in the corner
if ( ball.position.x >=684 && ball.position.y <=148 ) {
ball.position = CGPointMake(684, 148);
}
if ( ball.position.x >=684 && ball.position.y >=620 ) {
ball.position = CGPointMake(684, 620);
}
if ( ball.position.x <=340 && ball.position.y <=148 ) {
ball.position = CGPointMake(340, 148);
}
if ( ball.position.x <=340 && ball.position.y >=620 ) {
ball.position = CGPointMake(340, 620);
}
//and this shows how I have set it up that if they are on one side and ball1 is in the corner and ball2 is next and ball3 is next they keep their relative positions. I thought if I set the corner ball and the rest had collision detection turned on they would just stop. They do but they push ball1 in the corner out of its position. So I wrote the code
if (ball2.position.y == ball3.position.y && ball.position.y == ball2.position.y && ball.position.x < ball2.position.x && ball2.position.x < ball3.position.x ) {
int s = (self.ball3.position.x - self.ball2.position.x);
int t = ((self.ball2.position.y - self.ball3.position.y));
k = t + s;
int f =(self.ball2.position.x - self.ball.position.x);
int g = ((self.ball2.position.y - self.ball.position.y));
j = f+g;
int h =(self.ball3.position.x - self.ball.position.x);
int i = ((self.ball3.position.y - self.ball.position.y));
l = h + i;
NSLog(#"ball2centerx:%f",ball2.position.x);
NSLog(#"ball2centery:%f",ball2.position.y);
NSLog(#"ballcenterx:%f",ball.position.x);
NSLog(#"ballcentery:%f",ball.position.y);
NSLog(#"ball3centerx:%f",ball3.position.x);
NSLog(#"ball3centery:%f",ball3.position.y);
CGPoint w = CGPointMake(340, 148);
CGPoint x = CGPointMake(340, 620);
CGPoint v = CGPointMake(684, 148);
CGPoint z = CGPointMake(684, 620);
CGPoint m = CGPointMake(370, 148);
CGPoint q = CGPointMake(370, 620);
CGPoint o = CGPointMake(654, 148);
CGPoint p = CGPointMake(654, 620);
if (CGPointEqualToPoint(ball.position,w) ) {
if (f < 30) {
ball2.position = CGPointMake(370, 148);
}
}
if (CGPointEqualToPoint(ball.position,x)) {
if (f<30) {
ball2.position = CGPointMake(370, 620);
}
}
if (CGPointEqualToPoint(ball3.position,v)) {
if (s <30){
ball2.position = CGPointMake(654, 148);
}
}
if (CGPointEqualToPoint(ball3.position,z)) {
if (s <30){
ball2.position = CGPointMake(654, 620);
}
}
if (CGPointEqualToPoint(ball2.position,m)) {
if (s <30){
ball3.position = CGPointMake(400, 148);
}
}
if (CGPointEqualToPoint(ball2.position,o)) {
if (f<30) {
ball.position = CGPointMake(624, 148);
}
}
if (CGPointEqualToPoint(ball2.position,q)) {
if (s <30){
ball3.position = CGPointMake(400, 620);
}
}
if (CGPointEqualToPoint(ball2.position,p)) {
if (f<30) {
ball.position = CGPointMake(624, 620);
}
}
}
The problem is that as I add more ball the code is going to become extremely complex.
is there a way to write it so that if one ball (any ball) is in the corner and another one is in position two it will stay there and so forth. Kind of like a generic code for all ball configuration. instead of writing it for every configuration?
If you are going to have a number of nodes in your scene, it becomes tedious and impractical to create a property for each one. A good solution is to create each node and add it into an array. The array will allow you to keep a reference to it. The important thing is to create each node with its own unique name. You can do this by creating a counter property.
#property (nonatomic) int myCounter;
Then when you create your node set the node's name property like this:
myCounter++;
myNode.name = [NSString stringWithFormat:#"myNode-%i",myCounter];
Once you have fully created the node, add it to a NSMutableArray like this:
[self.myArray addObject:myNode];
You can add as many nodes to the array as you need.
To enumerate through the array you can do this:
for (SKSpriteNode *object in myArray) {
// let's look for a node with the name of myNode-2
if([myNode.name isEqualToString:#"myNode-2"]) {
NSLog(#"Found it");
}
}
Here is an example to enumerate the array to check for each node's position and set its speed to zero dependent on the IF statement:
for (SKSpriteNode *object in myArray) {
if((object.position.x >= 300) && (object.position.y >= 300)) {
object.physicsBody.velocity = CGVectorMake(0, 0);
}
}
Remember that because you are storing the objects in your array as SKSpriteNodes and enumerating your array using SKSpriteNode, you have access to all the corresponding properties like speed, position, name, etc...

Show UIImageView in a random position on the screen

I am developing a game that uses the gyroscope to keep enemies in relatively the same place, with the following code:
if([motionManager isGyroAvailable])
{
[motionManager setGyroUpdateInterval:0.05];
[motionManager startGyroUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMGyroData *gyroData, NSError *error)
{
valueX3 = gyroData.rotationRate.y* 50;
valueY3 = gyroData.rotationRate.x* 50;
int newX3 = (int)(enemyufoG.center.x +valueY3);
int newY3 = (int)(enemyufoG.center.y -valueX3);
CGPoint newCenter2 = CGPointMake(newX3, newY3);
enemyufoG.center = newCenter2;
valueX2 = gyroData.rotationRate.y* 50;
valueY2 = gyroData.rotationRate.x* 50;
int newX2 = (int)(enemyufoR.center.x +valueY2);
int newY2 = (int)(enemyufoR.center.y -valueX2);
CGPoint newCenter = CGPointMake(newX2, newY2);
enemyufoR.center = newCenter;
valueX = gyroData.rotationRate.y* 50;
valueY = gyroData.rotationRate.x* 50;
int newX = (int)(enemyAlien.center.x +valueY);
int newY = (int)(enemyAlien.center.y -valueX);
CGPoint newCenter3 = CGPointMake(newX, newY);
enemyAlien.center = newCenter3;
}];
}
Once you shoot an enemy that is in the crosshairs of the gun, it hides the UIImageView, then uses NSTimer to call a different method that shows it again. I would like to have the enemies reappear in random positions on the screen.
CGPoint pos = enemyAlien.center;
if ((pos.x > 254) && (pos.x < 304) && (pos.y > 140) && (pos.y < 160 && _ammoCount != 0))
{
enemyAlien.hidden = YES;
[dangerBar setProgress:dangerBar.progress-0.10];
_killCount = _killCount+3;
[killCountField setText: [NSString stringWithFormat:#"%d", _killCount]];
timer = [NSTimer scheduledTimerWithTimeInterval: 4.0
target: self
selector: #selector(showAlien)
userInfo: nil
repeats: NO];
}
- (void) showAlien {
enemyAlien.hidden = NO;
}
When I try and use enemyAlien.center = enemyAlien.center + arc4random()%100; above enemyAlien.hidden = NO, I get the following error:
'Invalid operands to binary expression ('CGPoint (aka 'struct CGPoint') and 'unsigned int').
You're trying to add an integer to a cgpoint.
try this.
enemyAlien.center = CGPointMake(enemyAlien.center.x + (arc4random()%100),enemyAlien.center.y + (arc4random()%100));
although this will only move the alien in a diagonal direction. You should probably change it to the following for a better experience.
enemyAlien.center = CGPointMake((arc4random()%SCREEN_WIDTH),(arc4random()%SCREEN_HEIGHT));
where SCREEN_WIDTH and SCREEN_HEIGHT are the dimensions of your playing field.

Game crashes when I try to destroy a b2Body, what should I do?

"Assertion failed: (m_bodyCount < m_bodyCapacity), function Add, file libs/Box2D/Dynamics/b2Island.h, line 65."
That is what it the crash leaves in the console.
[self removeChild:(CCSprite*)body->GetUserData() cleanup:YES];
body->SetTransform(b2Vec2(30, 30), 0); //moving the body out of the scene so it doesnt collide anymore!
world->DestroyBody(body);
I think im doing the right stuff..
#property (nonatomic, assign) b2Body *body;
Here is how i "make it" a property
I dont understand why it doesnt work, "body" is a proper pointer because I can retrieve infromation from the bodys UserData like tags that are set in the creatin of the body, so that shouldnt be a problem.. Do anyone know whats wrong with my code?
Thank you.
Edited in:
-(void) tick: (ccTime) dt //Main loop
{
if (ballFired) {
Magnet *aMagnet = [magnetArray objectAtIndex:0];
world->DestroyBody(aMagnet.body); //It crashes here!
}
int32 velocityIterations = 8;
int32 positionIterations = 1;
// Instruct the world to perform a single step of simulation. It is
// generally best to keep the time step and iterations fixed.
world->Step(dt, velocityIterations, positionIterations);
//Iterate over the bodies in the physics world
for (b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL) {
//Synchronize the AtlasSprites position and rotation with the corresponding body
CCSprite *myActor = (CCSprite*)b->GetUserData();
myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b>GetPosition().y * PTM_RATIO);
myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
}
}
}
Isnt this outside the world step?
Edit 2:
ContactListener::ContactListener(){};
void ContactListener::BeginContact(b2Contact* contact)
{
// Box2d objects that collided
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
CCSprite* actorA = (CCSprite*) fixtureA->GetBody()->GetUserData();
CCSprite* actorB = (CCSprite*) fixtureB->GetBody()->GetUserData();
if(actorA == nil || actorB == nil) return;
b2WorldManifold* worldManifold = new b2WorldManifold();
contact->GetWorldManifold(worldManifold);
Kollisjon *kollisjon = [Kollisjon sharedKollisjon];
if (actorA.tag == 1) {
NSLog(#"OK1");
kollisjon.kollidertBody = fixtureB->GetBody();
kollisjon.world->DestroyBody(kollisjon.kollidertBody); //Isnt this ok?
}
else if (actorB.tag == 1) {
NSLog(#"OK2");
kollisjon.kollidertBody = fixtureA->GetBody();
kollisjon.world->DestroyBody(kollisjon.kollidertBody); //Isnt this ok?
}
}
Is it not outside the timestep? Please help me here...
Thank you
You must scan for contacts, store all contacts in an array, and then AFTER all contacts have been checked, you remove your bodies.
Checking for contacts:
std::vector<MyContact>::iterator pos;
for(pos = _contactListener->_contacts.begin();
pos != _contactListener->_contacts.end(); ++pos)
{
MyContact contact = *pos;
b2Body *bodyA = contact.fixtureA->GetBody();
b2Body *bodyB = contact.fixtureB->GetBody();
// Rocket explosion rect
if(bodyA->GetUserData() == NULL)
{
NSLog(#"NULL collision detected. (BODY A)");
hasDoneRocketCollisions = YES;
CCSprite *sprite = (CCSprite*) bodyB->GetUserData();
if(sprite.visible == NO) continue;
if(sprite.tag >= 200 && sprite.tag < 300)
{
index = sprite.tag - 200;
if([spriteTracker containsObject:sprite]) continue;
[spriteTracker addObject:sprite];
bodiesToKill[counter] = bodyB;
[enemyChargerIsAlive replaceObjectAtIndex:(int)(sprite.tag-200) withObject:[NSNumber numberWithInt:0]];
[ParticleController spawnExplosion:sprite.position inParent:currentDefaultNode];
}
else if(sprite.tag >= 300 && sprite.tag < 400)
{
index = sprite.tag - 300;
if([spriteTracker containsObject:sprite]) continue;
[spriteTracker addObject:sprite];
bodiesToKill[counter] = bodyB;
[enemyShooterIsAlive replaceObjectAtIndex:(int)(sprite.tag-300) withObject:[NSNumber numberWithInt:0]];
[ParticleController spawnExplosion:sprite.position inParent:currentDefaultNode];
counter++;
}
}
}
Later in your method after all contacts have been checked:
b2Body *dyingBody;
for(int i = 0; i < counter; i++)
{
CCSprite *dyingSprite;
dyingSprite = [spriteTracker objectAtIndex:i];
dyingSprite.visible = NO;
// Is player projectile
if(dyingSprite.tag >= 100 && dyingSprite.tag < 200)
{
CCParticleSystemQuad *dyingParticle;
dyingParticle = [particlesToKill objectAtIndex:particleIndex];
particleIndex++;
[dyingParticle stopSystem];
dyingBody = bodiesToKill[i];
dyingBody->SetActive(false);
[ParticleController spawnExplosion:dyingSprite.position inParent:currentDefaultNode];
[AudioController playExplosion];
dyingSprite.visible = NO;
if([_player currentShotType] == 1)
{
rocketHitBody->SetTransform(b2Vec2(dyingSprite.position.x/PTM_RATIO, dyingSprite.position.y/PTM_RATIO), 0.0);
rocketHitBody->SetActive(true);
}
}
}
Take note that these are just random chunks of code I have copy and pasted in. They are for example only and may only confuse you if you try to read them as exact instructions.
The point here is: You can not remove a body while it is being accessed by the step or contact listener. Finish using the contact listener and then remove your bodies.

How to implement powerUps and other game altering objects in objective-C or cocos2d

Ok, so I have these powerups that I want to slow/speed up the movement of the other objects in the game for a few seconds.
I have an array of objects that I have a variable called spawnInterval that gets faster and faster as the game progresses, making the ame get harder after a few mins.
But I can't really grasp how to make it so the character in the game will react differently to different objects as in when the fastPowerUp is hit by the character sprite, the spawn interval doesn't change.
And vice versa with the slowPowerUp.
the code I have at the moment is this in a move sequence method that gets called in an update method:
-
(void) updateObstacles:(ccTime)delta{
for (int i = 0; i < 20; i++) {
//int randomizer = CCRANDOM_0_1() * [obstacles count];
//NSLog(#"randomizer: %i",randomizer);
CCSprite* randomObject = [obstacles randomObject];
currentObject = [obstacles indexOfObject:randomObject];
if ([randomObject numberOfRunningActions] == 0) {
[self runObstacleMoveSequence:randomObject withTimer:delta];
break;
}
}
}
-(void) runObstacleMoveSequence:(CCSprite *)object withTimer:(ccTime)delta{
static int time;
//Slowly increase object speed
numObstaclesMoved++;
if (!slowPowerUp && !fastPowerUp) {
time += delta;
if (numObstaclesMoved % 17 == 0 && obstacleMoveDuration > 2.0f) {
obstacleMoveDuration -= 0.2f;
if (spawnInterval > 0.1f) {
[self unschedule:#selector(updateObstacles:)];
[self schedule:#selector(updateObstacles:) interval:spawnInterval];
spawnInterval-=0.1f;
NSLog(#"interval: %f",spawnInterval);
}
}
}else if (slowPowerUp && !fastPowerUp) {
if (numObstaclesMoved % 17 == 0 && obstacleMoveDuration > 2.0f) {
obstacleMoveDuration += 3.0f;
if (spawnInterval > 0.1f) {
[self unschedule:#selector(updateObstacles:)];
[self schedule:#selector(updateObstacles:) interval:spawnInterval];
spawnInterval-=0.1f;
NSLog(#"interval: %f",spawnInterval);
if (time >= (delta + 3)) {
slowPowerUp = NO;
obstacleMoveDuration -= 3.0f;
}
}
}
}else if (!slowPowerUp && fastPowerUp) {
if (numObstaclesMoved % 17 == 0 && obstacleMoveDuration > 2.0f) {
obstacleMoveDuration -= 3.0f;
if (spawnInterval > 0.1f) {
[self unschedule:#selector(updateObstacles:)];
[self schedule:#selector(updateObstacles:) interval:spawnInterval];
spawnInterval-=0.1f;
NSLog(#"interval: %f",spawnInterval);
if (time >= (delta + 3)) {
fastPowerUp = NO;
obstacleMoveDuration += 3.0f;
}
}
}
}
CGSize screenSize = [[CCDirector sharedDirector]winSize];
CGPoint aboveScreenPosition = CGPointMake(object.position.x, screenSize.height - object.position.y);
int rotations = (CCRANDOM_0_1()*3) * 360;
float duration = (CCRANDOM_0_1()*5.0f) + 8.0f;
CCMoveTo* move = [CCMoveTo actionWithDuration:obstacleMoveDuration position:aboveScreenPosition];
CCRotateTo* rotate = [CCRotateBy actionWithDuration:duration angle:rotations];
CCSpawn* moveRotate = [CCSpawn actions: move, rotate, nil];
CCCallFuncN* call = [CCCallFuncN actionWithTarget:self selector:#selector(objectAboveScreen:)];
CCSequence* sequence = [CCSequence actions:moveRotate, call, nil];
[object runAction:sequence];
if (time >= (delta + 3)) {
fastPowerUp = NO;
}
}
-(void) objectAboveScreen:(id) sender{
//make sure sender is actually of the right class
NSAssert([sender isKindOfClass:[CCSprite class]], #"sender is not a CCSprite!");
CCSprite* obstacle = (CCSprite*)sender;
//move the back to the bottom of the screen
CGPoint pos = obstacle.position;
CGSize screenSize = [[CCDirector sharedDirector]winSize];
pos.y = (-screenSize.height - [obstacle texture].contentSize.height);
pos.x = CCRANDOM_0_1() * screenSize.width;
obstacle.position = pos;
}
I really just don't know where to go from here... Should I make the powerUps a different class? If so, how would I implement something like this? I really hate trying to ask for someone to solve my question, but I really just can't rack my brain around this and I'm rather new... if it were explained to me, then I know I would be able to implement it in future games on my own...
Thanks in advance, and let me know if more information is needed...
I'd do something like
in the .h file
float speedModifier;
-(void)resetPowerUp;
in the .m
-(void)resetPowerUp
{
speedModifier = 1;
}
wherever you are initializing the level
[self resetPowerUp];
upon collision with powerup:
speedModifier = 2;
[self performSelector:#selector(resetPowerUp) withObject:nil afterDelay:5];
then wherever you are moving whatever it is which speed should be effected by the powerup mode, multiply the speed of the animation (or divide the duration it takes for it to get wherever it's going) by speedModified
hope that helps

xcode defining variable problem

Can someone please explain to me why my variable called activity is 0 when defined as a float, but displays the correct value when defined as an int? I have another float which is used for pretty much the same thing but works perfectly!
#implementation AppPrefsViewController
float age;
int activity;
...
-(void) updateAValues{
if (selectedActivityUnit == 0){
activity = 50;
NSLog(#"activity = %d",activity);
}
if (selectedActivityUnit == 1){
activity = 75;
NSLog(#"activity = %d",activity);
}
}
....
- (void)updateLabel {
if (selectedUnit == 0) {
subView.hidden = YES;
age = 1;
NSLog(#"age = %d",age);
}
if (selectedUnit == 1) {
subView.hidden = YES;
age = 2;
NSLog(#"age = %d",age);
}
if (selectedUnit == 2) {
subView.hidden = NO;
age = 3;
NSLog(#"age = %d",age);
}
}
You're using
NSLog(#"activity = %d", activity);
To display your values. This works if activity is an int type as %d is for displaying ints. If you want to display floats you need the float formater %f, as in:
NSLog(#"activity = %f", activity);